2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Mission/MissionParse.cpp $
15 * main upper level code for pasring stuff
18 * Revision 1.7 2005/10/01 22:04:58 taylor
19 * fix FS1 (de)briefing voices, the directory names are different in FS1
20 * hard code the table values so that the fs1.vp file isn't needed
21 * hard code a mission fix for sm2-08a since a have no idea how to fix it otherwise
22 * generally cleanup some FS1 code
23 * fix volume sliders in the options screen that never went all the way up
25 * Revision 1.6 2003/08/14 02:39:11 theoddone33
26 * Removed braces from initializers so GCC 3 will stop complaining.
28 * Revision 1.5 2003/05/25 02:30:43 taylor
31 * Revision 1.4 2002/06/17 06:33:09 relnev
32 * ryan's struct patch for gcc 2.95
34 * Revision 1.3 2002/06/09 04:41:22 relnev
35 * added copyright header
37 * Revision 1.2 2002/05/07 03:16:46 theoddone33
38 * The Great Newline Fix
40 * Revision 1.1.1.1 2002/05/03 03:28:09 root
44 * 63 9/12/99 8:09p Dave
45 * Fixed problem where skip-training button would cause mission messages
46 * not to get paged out for the current mission.
48 * 62 9/09/99 3:53a Andsager
49 * Only reposition HUGE ships to center of knossos device on warp in
51 * 61 8/27/99 9:07p Dave
52 * LOD explosions. Improved beam weapon accuracy.
54 * 60 8/26/99 8:51p Dave
55 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
57 * 59 8/25/99 10:06a Jefff
58 * vasudan pilots get a vasudan support ship.
60 * 58 8/24/99 5:27p Andsager
61 * Make subsystems with zero strength before mission blown off. Protect
62 * red alert pilot against dying between orders and jump.
64 * 57 8/18/99 10:07p Johnson
65 * Fix Fred bug in positioning of Knossos device (when trying to warp in
68 * 56 8/18/99 3:57p Andsager
69 * Add warning for invalid alt_name.
71 * 55 8/18/99 3:48p Andsager
72 * Make support ship take default name and not 0th alt_name.
74 * 54 8/16/99 3:53p Andsager
75 * Add special warp in interface in Fred and saving / reading.
77 * 53 8/16/99 2:01p Andsager
78 * Knossos warp-in warp-out.
80 * 52 8/03/99 5:35p Andsager
81 * Dont draw target dot for instructor in training mission
83 * 51 7/30/99 7:01p Dave
84 * Dogfight escort gauge. Fixed up laser rendering in Glide.
86 * 50 7/28/99 1:36p Andsager
87 * Modify cargo1 to include flag CARGO_NO_DEPLETE. Add sexp
88 * cargo-no-deplete (only for BIG / HUGE). Modify ship struct to pack
91 * 49 7/26/99 5:50p Dave
92 * Revised ingame join. Better? We'll see....
94 * 48 7/19/99 3:01p Dave
95 * Fixed icons. Added single transport icon.
97 * 47 7/15/99 9:20a Andsager
98 * FS2_DEMO initial checkin
100 * 46 7/13/99 5:03p Alanl
101 * make sure object sounds get assigned to ships
103 * 45 7/11/99 2:14p Dave
104 * Added Fred names for the new icon types.
106 * 44 7/02/99 4:\31p Dave
107 * Much more sophisticated lightning support.
109 * 43 7/01/99 4:23p Dave
110 * Full support for multiple linked ambient engine sounds. Added "big
113 * 42 6/28/99 4:51p Andsager
114 * Add ship-guardian sexp (does not allow ship to be killed)
116 * 41 6/21/99 1:34p Alanl
119 * 40 6/16/99 10:20a Dave
120 * Added send-message-list sexpression.
122 * 39 6/14/99 2:06p Andsager
123 * Default load brown asteroids
125 * 38 6/10/99 11:06a Andsager
126 * Mission designed selection of asteroid types.
128 * 37 6/03/99 6:37p Dave
129 * More TNT fun. Made perspective bitmaps more flexible.
131 * 36 5/20/99 7:00p Dave
132 * Added alternate type names for ships. Changed swarm missile table
135 * 35 4/26/99 8:49p Dave
136 * Made all pof based nebula stuff full customizable through fred.
138 * 34 4/26/99 12:49p Andsager
139 * Add protect object from beam support to Fred
141 * 33 4/16/99 2:34p Andsager
142 * Second pass on debris fields
144 * 32 4/15/99 5:00p Andsager
145 * Frist pass on Debris field
147 * 31 4/07/99 6:22p Dave
148 * Fred and Freespace support for multiple background bitmaps and suns.
149 * Fixed link errors on all subprojects. Moved encrypt_init() to
150 * cfile_init() and lcl_init(), since its safe to call twice.
152 * 30 3/31/99 9:52a Andsager
153 * generalization for debris field
155 * 29 3/30/99 5:40p Dave
156 * Fixed reinforcements for TvT in multiplayer.
158 * 28 3/29/99 6:17p Dave
159 * More work on demo system. Got just about everything in except for
160 * blowing ships up, secondary weapons and player death/warpout.
162 * 27 3/24/99 6:23p Dave
163 * Make sure we only apply squadron changes to the player in single-player
166 * 26 3/24/99 4:05p Dave
167 * Put in support for assigning the player to a specific squadron with a
168 * specific logo. Preliminary work for doing pos/orient checksumming in
169 * multiplayer to reduce bandwidth.
171 * 25 3/01/99 7:39p Dave
172 * Added prioritizing ship respawns. Also fixed respawns in TvT so teams
173 * don't mix respawn points.
175 * 24 2/26/99 6:01p Andsager
176 * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay
178 * 23 2/24/99 2:25p Dave
179 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
180 * bug for dogfight more.
182 * 22 2/23/99 8:11p Dave
183 * Tidied up dogfight mode. Fixed TvT ship type problems for alpha wing.
184 * Small pass over todolist items.
186 * 21 2/17/99 2:10p Dave
187 * First full run of squad war. All freespace and tracker side stuff
190 * 20 2/11/99 3:08p Dave
191 * PXO refresh button. Very preliminary squad war support.
193 * 19 2/11/99 2:15p Andsager
194 * Add ship explosion modification to FRED
196 * 18 2/07/99 8:51p Andsager
197 * Add inner bound to asteroid field. Inner bound tries to stay astroid
198 * free. Wrap when within and don't throw at ships inside.
200 * 17 2/05/99 10:38a Johnson
201 * Fixed improper object array reference.
203 * 16 2/04/99 1:23p Andsager
204 * Apply max spark limit to ships created in mission parse
206 * 15 2/03/99 12:42p Andsager
207 * Add escort priority. Modify ship_flags_dlg to include field. Save and
208 * Load. Add escort priority field to ship.
210 * 14 1/27/99 9:56a Dave
211 * Temporary checkin of beam weapons for Dan to make cool sounds.
213 * 13 1/25/99 5:03a Dave
214 * First run of stealth, AWACS and TAG missile support. New mission type
217 * 12 1/19/99 3:57p Andsager
218 * Round 2 of variables
220 * 11 1/07/99 1:52p Andsager
221 * Initial check in of Sexp_variables
223 * 10 11/14/98 5:32p Dave
224 * Lots of nebula work. Put in ship contrails.
226 * 9 11/12/98 12:13a Dave
227 * Tidied code up for multiplayer test. Put in network support for flak
230 * 8 11/05/98 5:55p Dave
231 * Big pass at reducing #includes
233 * 7 11/05/98 4:18p Dave
234 * First run nebula support. Beefed up localization a bit. Removed all
235 * conditional compiles for foreign versions. Modified mission file
238 * 6 10/29/98 9:23p Dave
239 * Removed minor bug concerning externalization of campaign files.
241 * 5 10/23/98 3:51p Dave
242 * Full support for tstrings.tbl and foreign languages. All that remains
243 * is to make it active in Fred.
245 * 4 10/20/98 10:44a Andsager
246 * Add comment for creating sparks before mission starts
248 * 3 10/07/98 6:27p Dave
249 * Globalized mission and campaign file extensions. Removed Silent Threat
250 * special code. Moved \cache \players and \multidata into the \data
253 * 2 10/07/98 10:53a Dave
256 * 1 10/07/98 10:49a Dave
258 * 503 9/21/98 8:46p Dave
259 * Put in special check in fred for identifying unknown ships.
261 * 502 9/14/98 5:09p Dave
262 * Massive hack to always ignore m-clash
264 * 501 9/14/98 3:40p Allender
265 * better error checking for invalid number of waves for player wings in a
266 * multiplayer game. Better popup message in FreeSpace side.
268 * 500 9/11/98 2:05p Allender
269 * make reinforcements work correctly in multiplayer games. There still
270 * may be a team vs team issue that I haven't thought of yet :-(
272 * 499 8/07/98 9:48a Allender
273 * changed how SF_FROM_PLAYER_WING is assigned for team vs. team games
275 * 498 7/16/98 2:22p Allender
276 * don't do wave check in single player
278 * 497 7/13/98 5:19p Dave
280 * 496 7/13/98 3:15p Allender
281 * don't allow multiple waves for any player wings
283 * 495 7/10/98 12:11a Allender
284 * fixed problem where missions files of 0 length would cause game to
287 * 494 5/21/98 3:31p Allender
288 * fix bug where Ship_obj_list was getting overwritten by the exited ships
291 * 493 5/20/98 1:04p Hoffoss
292 * Made credits screen use new artwork and removed rating field usage from
293 * Fred (a goal struct member).
295 * 492 5/18/98 4:41p Comet
296 * allender: fix problem where ships in wings were not deleted on clients
297 * when a new wing create packet came in. A serious hack of all hacks
299 * 491 5/18/98 3:37p Jasen
300 * move Int3() about too many ships in wing to before where ship actually
303 * 490 5/18/98 1:58a Mike
304 * Make Phoenix not be fired at fighters (but yes bombers).
305 * Improve positioning of ships in guard mode.
306 * Make turrets on player ship not fire near end of support ship docking.
308 * 489 5/15/98 9:52a Allender
309 * added code to give Warning when orders accepted don't match the default
312 * 488 5/13/98 4:41p Mike
313 * Make big ships try a tiny bit to avoid collision with each other when
314 * attacking another big ship. Make ships a little less likely to collide
315 * into player when in formation, drop off if player flying wacky.
317 * 487 5/13/98 3:07p Mitri
318 * fixed problem with checking current count of the mission against max
321 * 486 5/11/98 4:33p Allender
322 * fixed ingame join problems -- started to work on new object updating
323 * code (currently ifdef'ed out)
325 * 485 5/08/98 5:05p Dave
326 * Go to the join game screen when quitting multiplayer. Fixed mission
327 * text chat bugs. Put mission type symbols on the create game list.
328 * Started updating standalone gui controls.
339 #include "freespace.h"
341 #include "missionparse.h"
342 #include "missiongoals.h"
343 #include "missionlog.h"
344 #include "missionmessage.h"
346 #include "linklist.h"
352 #include "starfield.h"
354 #include "lighting.h"
355 #include "eventmusic.h"
356 #include "missionbriefcommon.h"
358 #include "multiutil.h"
359 #include "multimsgs.h"
363 #include "fireballs.h"
365 #include "gamesequence.h"
370 #include "missionhotkey.h"
371 #include "hudescort.h"
372 #include "asteroid.h"
374 #include "staticrand.h"
375 #include "missioncmdbrief.h"
376 #include "redalert.h"
377 #include "multi_respawn.h"
378 #include "hudwingmanstatus.h"
379 #include "jumpnode.h"
380 #include "multi_endgame.h"
381 #include "localize.h"
384 #include "neblightning.h"
389 char dockee[NAME_LENGTH];
390 char docker_point[NAME_LENGTH];
391 char dockee_point[NAME_LENGTH];
392 } Initially_docked[MAX_SHIPS];
394 int Total_initially_docked;
397 char Mission_filename[80];
399 int Mission_palette; // index into Nebula_palette_filenames[] of palette file to use for mission
400 int Nebula_index; // index into Nebula_filenames[] of nebula to use in mission.
401 int Num_iff = MAX_IFF;
402 int Num_ai_behaviors = MAX_AI_BEHAVIORS;
404 int Num_status_names = MAX_STATUS_NAMES;
405 int Num_arrival_names = MAX_ARRIVAL_NAMES;
406 int Num_goal_type_names = MAX_GOAL_TYPE_NAMES;
407 int Num_team_names = MAX_TEAM_NAMES;
409 int Player_starts = 1;
411 fix Entry_delay_time;
413 ushort Current_file_checksum = 0;
414 ushort Last_file_checksum = 0;
415 int Current_file_length = 0;
417 // alternate ship type names
418 char Mission_alt_types[MAX_ALT_TYPE_NAMES][NAME_LENGTH];
419 int Mission_alt_type_count = 0;
421 #define SHIP_WARP_TIME 5.0f // how many seconds it takes for ship to warp in
423 // the ship arrival list will contain a list of ships that are yet to arrive. This
424 // list could also include ships that are part of wings!
426 p_object ship_arrivals[MAX_SHIP_ARRIVALS], ship_arrival_list; // for linked list of ships to arrive later
427 int num_ship_arrivals;
429 #define MAX_SHIP_ORIGINAL 100
430 p_object ship_original[MAX_SHIP_ORIGINAL];
431 int num_ship_original;
433 // list for arriving support ship
434 p_object Support_ship_pobj;
435 p_object *Arriving_support_ship;
436 char Arriving_repair_targets[MAX_AI_GOALS][NAME_LENGTH];
437 int Num_arriving_repair_targets;
439 subsys_status Subsys_status[MAX_SUBSYS_STATUS];
442 char Mission_parse_storm_name[NAME_LENGTH] = "none";
444 team_data Team_data[MAX_TEAMS];
446 // variables for player start in single player
447 char Player_start_shipname[NAME_LENGTH];
448 int Player_start_shipnum;
449 p_object Player_start_pobject;
451 // name of all ships to use while parsing a mission (since a ship might be referenced by
452 // something before that ship has even been loaded yet)
453 char Parse_names[MAX_SHIPS + MAX_WINGS][NAME_LENGTH];
458 const char *Nebula_filenames[NUM_NEBULAS] = {
464 const char *Neb2_filenames[NUM_NEBULAS] = {
470 // Note: Nebula_colors[] and Nebula_palette_filenames are linked via index numbers
471 const char *Nebula_colors[NUM_NEBULA_COLORS] = {
483 const char *Iff_names[MAX_IFF] = { "IFF 1", "IFF 2", "IFF 3" };
485 const char *Ai_behavior_names[MAX_AI_BEHAVIORS] = {
509 char *Cargo_names[MAX_CARGO];
510 char Cargo_names_buf[MAX_CARGO][NAME_LENGTH];
512 char *Ship_class_names[MAX_SHIP_TYPES]; // to be filled in from Ship_info array
514 const char *Icon_names[MAX_BRIEF_ICONS] = {
515 "Fighter", "Fighter Wing", "Cargo", "Cargo Wing", "Largeship",
516 "Largeship Wing", "Capital", "Planet", "Asteroid Field", "Waypoint",
517 "Support Ship", "Freighter(no cargo)", "Freighter(has cargo)",
518 "Freighter Wing(no cargo)", "Freighter Wing(has cargo)", "Installation",
519 "Bomber", "Bomber Wing", "Cruiser", "Cruiser Wing", "Unknown", "Unknown Wing",
520 "Player Fighter", "Player Fighter Wing", "Player Bomber", "Player Bomber Wing",
524 "Knossos Device", "Transport Wing", "Corvette", "Gas Miner", "Awacs", "Supercap", "Sentry Gun", "Jump Node", "Transport"
528 // Translate team mask values like TEAM_FRIENDLY to indices in Team_names array.
529 // -1 means an illegal value.
530 int Team_names_index_xlate[MAX_TEAM_NAMES_INDEX+1] = {-1, 0, 1, -1, 2, -1, -1, -1, 3};
532 const char *Team_names[MAX_TEAM_NAMES] = {
533 "Hostile", "Friendly", "Neutral", "Unknown",
536 const char *Status_desc_names[MAX_STATUS_NAMES] = {
537 "Shields Critical", "Engines Damaged", "Fully Operational",
540 const char *Status_type_names[MAX_STATUS_NAMES] = {
541 "Damaged", "Disabled", "Corroded",
544 const char *Status_target_names[MAX_STATUS_NAMES] = {
545 "Weapons", "Engines", "Cable TV",
548 // definitions for arrival locations for ships/wings
549 const char *Arrival_location_names[MAX_ARRIVAL_NAMES] = {
550 "Hyperspace", "Near Ship", "In front of ship", "Docking Bay",
553 const char *Special_arrival_anchor_names[MAX_SPECIAL_ARRIVAL_ANCHORS] =
558 "<any friendly player>",
559 "<any hostile player>",
560 "<any neutral player>",
563 const char *Departure_location_names[MAX_ARRIVAL_NAMES] = {
564 "Hyperspace", "Docking Bay",
567 const char *Goal_type_names[MAX_GOAL_TYPE_NAMES] = {
568 "Primary", "Secondary", "Bonus",
571 const char *Species_names[MAX_SPECIES_NAMES] = {
572 "Terran", "Vasudan", "Shivan",
575 const char *Reinforcement_type_names[] = {
580 const char *Old_game_types[OLD_MAX_GAME_TYPES] = {
581 "Single Player Only",
583 "Single/Multi Player",
587 const char *Parse_object_flags[MAX_PARSE_OBJECT_FLAGS] = {
600 "hidden-from-sensors",
610 const char *Starting_wing_names[MAX_STARTING_WINGS+1] = {
619 int Num_reinforcement_type_names = sizeof(Reinforcement_type_names) / sizeof(char *);
621 vector Parse_viewer_pos;
622 matrix Parse_viewer_orient;
624 // definitions for timestamps for eval'ing arrival/departure cues
625 int Mission_arrival_timestamp;
626 int Mission_departure_timestamp;
627 fix Mission_end_time;
629 #define ARRIVAL_TIMESTAMP 2000 // every 2 seconds
630 #define DEPARTURE_TIMESTAMP 2200 // every 2.2 seconds -- just to be a little different
632 // calculates a "unique" file signature as a ushort (checksum) and an int (file length)
633 // the amount of The_mission we're going to checksum
634 // WARNING : do NOT call this function on the server - it will overwrite goals, etc
635 #define MISSION_CHECKSUM_SIZE (NAME_LENGTH + NAME_LENGTH + 4 + DATE_TIME_LENGTH + DATE_TIME_LENGTH)
637 // timers used to limit arrival messages and music
638 #define ARRIVAL_MUSIC_MIN_SEPARATION 60000
639 #define ARRIVAL_MESSAGE_MIN_SEPARATION 30000
641 #define ARRIVAL_MESSAGE_DELAY_MIN 2000
642 #define ARRIVAL_MESSAGE_DELAY_MAX 3000
644 static int Allow_arrival_music_timestamp;
645 static int Allow_arrival_message_timestamp;
646 static int Arrival_message_delay_timestamp;
649 static int Allow_arrival_music_timestamp_m[2];
650 static int Allow_arrival_message_timestamp_m[2];
651 static int Arrival_message_delay_timestamp_m[2];
654 void parse_player_info2(mission *pm);
655 void post_process_mission();
656 int allocate_subsys_status();
657 void parse_common_object_data(p_object *objp);
658 void parse_asteroid_fields(mission *pm);
659 int mission_set_arrival_location(int anchor, int location, int distance, int objnum, vector *new_pos, matrix *new_orient);
660 int get_parse_name_index(const char *name);
661 int get_anchor(char *name);
662 void mission_parse_do_initial_docks();
663 void mission_parse_set_arrival_locations();
664 void mission_set_wing_arrival_location( wing *wingp, int num_to_set );
665 int parse_lookup_alt_name(char *name);
667 void parse_mission_info(mission *pm)
670 char game_string[NAME_LENGTH];
672 required_string("#Mission Info");
674 required_string("$Version:");
675 stuff_float(&pm->version);
676 if (pm->version != MISSION_VERSION) {
677 mprintf(("Older mission, should update it (%.2f<-->%.2f)", pm->version, MISSION_VERSION));
680 required_string("$Name:");
681 stuff_string(pm->name, F_NAME, NULL);
683 required_string("$Author:");
684 stuff_string(pm->author, F_NAME, NULL);
686 required_string("$Created:");
687 stuff_string(pm->created, F_DATE, NULL);
689 required_string("$Modified:");
690 stuff_string(pm->modified, F_DATE, NULL);
692 required_string("$Notes:");
693 stuff_string(pm->notes, F_NOTES, NULL);
695 if (optional_string("$Mission Desc:"))
696 stuff_string(pm->mission_desc, F_MULTITEXT, NULL, MISSION_DESC_LENGTH);
698 SDL_strlcpy(pm->mission_desc, NOX("No description\n"), SDL_arraysize(pm->mission_desc));
700 pm->game_type = MISSION_TYPE_SINGLE; // default to single player only
701 if ( optional_string("+Game Type:")) {
702 // HACK HACK HACK -- stuff_string was changed to *not* ignore carriage returns. Since the
703 // old style missions may have carriage returns, deal with it here.
704 ignore_white_space();
705 stuff_string(game_string, F_NAME, NULL);
706 for ( i = 0; i < OLD_MAX_GAME_TYPES; i++ ) {
707 if ( !SDL_strcasecmp(game_string, Old_game_types[i]) ) {
709 // this block of code is now old mission compatibility code. We specify game
710 // type in a different manner than before.
711 if ( i == OLD_GAME_TYPE_SINGLE_ONLY )
712 pm->game_type = MISSION_TYPE_SINGLE;
713 else if ( i == OLD_GAME_TYPE_MULTI_ONLY )
714 pm->game_type = MISSION_TYPE_MULTI;
715 else if ( i == OLD_GAME_TYPE_SINGLE_MULTI )
716 pm->game_type = (MISSION_TYPE_SINGLE | MISSION_TYPE_MULTI );
717 else if ( i == OLD_GAME_TYPE_TRAINING )
718 pm->game_type = MISSION_TYPE_TRAINING;
722 if ( pm->game_type & MISSION_TYPE_MULTI )
723 pm->game_type |= MISSION_TYPE_MULTI_COOP;
730 if ( optional_string("+Game Type Flags:") ) {
731 stuff_int(&pm->game_type);
735 if (optional_string("+Flags:")){
736 stuff_int(&pm->flags);
739 // nebula mission stuff
741 if(optional_string("+NebAwacs:")){
742 if(pm->flags & MISSION_FLAG_FULLNEB){
743 stuff_float(&Neb2_awacs);
749 if(optional_string("+Storm:")){
750 stuff_string(Mission_parse_storm_name, F_NAME, NULL);
751 nebl_set_storm(Mission_parse_storm_name);
754 // get the number of players if in a multiplayer mission
756 if ( pm->game_type & MISSION_TYPE_MULTI ) {
757 if ( optional_string("+Num Players:") ) {
758 stuff_int( &(pm->num_players) );
762 // get the number of respawns
763 pm->num_respawns = 0;
764 if ( pm->game_type & MISSION_TYPE_MULTI ) {
765 if ( optional_string("+Num Respawns:") ){
766 stuff_int( (int*)&(pm->num_respawns) );
771 if ( optional_string("+Red Alert:")) {
772 stuff_int(&pm->red_alert);
774 red_alert_set_status(pm->red_alert);
777 if ( optional_string("+Scramble:")) {
778 stuff_int(&pm->scramble);
781 pm->disallow_support = 0;
782 if ( optional_string("+Disallow Support:")) {
783 stuff_int(&pm->disallow_support);
786 if (optional_string("+All Teams Attack")){
787 Mission_all_attack = 1;
789 Mission_all_attack = 0;
792 // Maybe delay the player's entry.
793 if (optional_string("+Player Entry Delay:")) {
797 SDL_assert(temp >= 0.0f);
798 Entry_delay_time = fl2f(temp);
801 if (optional_string("+Viewer pos:")){
802 stuff_vector(&Parse_viewer_pos);
805 if (optional_string("+Viewer orient:")){
806 stuff_matrix(&Parse_viewer_orient);
809 // possible squadron reassignment
810 SDL_strlcpy(The_mission.squad_name, "", SDL_arraysize(The_mission.squad_name));
811 SDL_strlcpy(The_mission.squad_filename, "", SDL_arraysize(The_mission.squad_filename));
812 if(optional_string("+SquadReassignName:")){
813 stuff_string(The_mission.squad_name, F_NAME, NULL);
814 if(optional_string("+SquadReassignLogo:")){
815 stuff_string(The_mission.squad_filename, F_NAME, NULL);
818 // always clear out squad reassignments if not single player
819 if(Game_mode & GM_MULTIPLAYER){
820 SDL_strlcpy(The_mission.squad_name, "", SDL_arraysize(The_mission.squad_name));
821 SDL_strlcpy(The_mission.squad_filename, "", SDL_arraysize(The_mission.squad_filename));
822 //mprintf(("Ignoring squadron reassignment\n"));
824 // reassign the player
826 if(!Fred_running && (Player != NULL) && (strlen(The_mission.squad_name) > 0) && (Game_mode & GM_CAMPAIGN_MODE)){
827 mprintf(("Reassigning player to squadron %s\n", The_mission.squad_name));
828 player_set_squad(Player, The_mission.squad_name);
829 player_set_squad_bitmap(Player, The_mission.squad_filename);
833 // set up the Num_teams variable accoriding to the game_type variable'
834 Num_teams = 1; // assume 1
836 // multiplayer team v. team games have two teams. If we have three teams, we need to use
837 // a new mission mode!
838 if ( (pm->game_type & MISSION_TYPE_MULTI) && (pm->game_type & MISSION_TYPE_MULTI_TEAMS) ){
843 void parse_player_info(mission *pm)
845 char alt[NAME_LENGTH + 2] = "";
846 SDL_assert(pm != NULL);
848 // alternate type names begin here
849 mission_parse_reset_alt();
850 if(optional_string("#Alternate Types:")){
852 while(!optional_string("#end")){
853 required_string("$Alt:");
854 stuff_string(alt, F_NAME, NULL, NAME_LENGTH);
857 mission_parse_add_alt(alt);
862 required_string("#Players");
864 while (required_string_either("#Objects", "$")){
865 parse_player_info2(pm);
869 void parse_player_info2(mission *pm)
871 char str[NAME_LENGTH];
872 int nt, i, total, list[MAX_SHIP_TYPES * 2], list2[MAX_WEAPON_TYPES * 2];
874 char starting_wings[MAX_PLAYER_WINGS][NAME_LENGTH];
876 // read in a ship/weapon pool for each team.
877 for ( nt = 0; nt < Num_teams; nt++ ) {
878 int num_ship_choices;
880 ptr = &Team_data[nt];
881 // get the shipname for single player missions
882 // MWA -- make this required later!!!!
883 if ( optional_string("$Starting Shipname:") )
884 stuff_string( Player_start_shipname, F_NAME, NULL );
886 required_string("$Ship Choices:");
887 total = stuff_int_list(list, MAX_SHIP_TYPES * 2, SHIP_INFO_TYPE);
889 SDL_assert(!(total & 0x01)); // make sure we have an even count
891 num_ship_choices = 0;
892 total /= 2; // there are only 1/2 the ships really on the list.
893 for (i=0; i<total; i++) {
894 // in a campaign, see if the player is allowed the ships or not. Remove them from the
895 // pool if they are not allowed
896 if (Game_mode & GM_CAMPAIGN_MODE || ((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER))) {
897 if ( !Campaign.ships_allowed[list[i*2]] )
901 ptr->ship_list[num_ship_choices] = list[i * 2];
902 ptr->ship_count[num_ship_choices] = list[i * 2 + 1];
905 ptr->number_choices = num_ship_choices;
907 // --- obsolete data, parsing remains for compatibility reasons ---
908 if (optional_string("+Starting Wings:"))
909 stuff_string_list(starting_wings, MAX_PLAYER_WINGS);
911 ptr->default_ship = -1;
912 if (optional_string("+Default_ship:")) {
913 stuff_string(str, F_NAME, NULL);
914 ptr->default_ship = ship_info_lookup(str);
915 // see if the player's default ship is an allowable ship (campaign only). If not, then what
916 // do we do? choose the first allowable one?
917 if (Game_mode & GM_CAMPAIGN_MODE || ((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER))) {
918 if ( !(Campaign.ships_allowed[ptr->default_ship]) ) {
919 for (i = 0; i < MAX_SHIP_TYPES; i++ ) {
920 if ( Campaign.ships_allowed[ptr->default_ship] ) {
921 ptr->default_ship = i;
925 SDL_assert( i < MAX_SHIP_TYPES );
930 if (ptr->default_ship == -1) // invalid or not specified, make first in list
931 ptr->default_ship = ptr->ship_list[0];
933 for (i=0; i<MAX_WEAPON_TYPES; i++)
934 ptr->weaponry_pool[i] = 0;
936 if (optional_string("+Weaponry Pool:")) {
937 total = stuff_int_list(list2, MAX_WEAPON_TYPES * 2, WEAPON_POOL_TYPE);
939 SDL_assert(!(total & 0x01)); // make sure we have an even count
941 for (i=0; i<total; i++) {
942 // in a campaign, see if the player is allowed the weapons or not. Remove them from the
943 // pool if they are not allowed
944 if (Game_mode & GM_CAMPAIGN_MODE || ((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER))) {
945 if ( !Campaign.weapons_allowed[list2[i*2]] )
949 if ((list2[i * 2] >= 0) && (list2[i * 2] < MAX_WEAPON_TYPES))
950 ptr->weaponry_pool[list2[i * 2]] = list2[i * 2 + 1];
955 if ( nt != Num_teams )
956 Error(LOCATION, "Not enough ship/weapon pools for mission. There are %d teams and only %d pools.", Num_teams, nt);
959 void parse_plot_info(mission *pm)
961 required_string("#Plot Info");
963 required_string("$Tour:");
964 stuff_string(pm->tour_name, F_NAME, NULL);
966 required_string("$Pre-Briefing Cutscene:");
967 stuff_string(pm->pre_briefing_cutscene, F_FILESPEC, NULL);
969 required_string("$Pre-Mission Cutscene:");
970 stuff_string(pm->pre_mission_cutscene, F_FILESPEC, NULL);
972 required_string("$Next Mission Success:");
973 stuff_string(pm->next_mission_success, F_NAME, NULL);
975 required_string("$Next Mission Partial:");
976 stuff_string(pm->next_mission_partial, F_NAME, NULL);
978 required_string("$Next Mission Failure:");
979 stuff_string(pm->next_mission_failure, F_NAME, NULL);
982 void parse_briefing_info(mission *pm)
986 if ( !optional_string("#Briefing Info") )
989 required_string("$Briefing Voice 1:");
990 stuff_string(junk, F_FILESPEC, NULL);
992 required_string("$Briefing Text 1:");
993 stuff_string(junk, F_MULTITEXTOLD, NULL);
995 required_string("$Briefing Voice 2:");
996 stuff_string(junk, F_FILESPEC, NULL);
998 required_string("$Briefing Text 2:");
999 stuff_string(junk, F_MULTITEXTOLD, NULL);
1001 required_string("$Briefing Voice 3:");
1002 stuff_string(junk, F_FILESPEC, NULL);
1004 required_string("$Briefing Text 3:");
1005 stuff_string(junk, F_MULTITEXTOLD, NULL);
1007 required_string("$Debriefing Voice 1:");
1008 stuff_string(junk, F_FILESPEC, NULL);
1010 required_string("$Debriefing Text 1:");
1011 stuff_string(junk, F_MULTITEXTOLD, NULL);
1013 required_string("$Debriefing Voice 2:");
1014 stuff_string(junk, F_FILESPEC, NULL);
1016 required_string("$Debriefing Text 2:");
1017 stuff_string(junk, F_MULTITEXTOLD, NULL);
1019 required_string("$Debriefing Voice 3:");
1020 stuff_string(junk, F_FILESPEC, NULL);
1022 required_string("$Debriefing Text 3:");
1023 stuff_string(junk, F_MULTITEXTOLD, NULL);
1026 // parse the event music and briefing music for the mission
1027 void parse_music(mission *pm)
1029 char name[NAME_LENGTH];
1031 event_music_reset_choices();
1033 if ( !optional_string("#Music") ) {
1037 required_string("$Event Music:");
1038 stuff_string(name, F_NAME, NULL);
1039 event_music_set_soundtrack(name);
1041 required_string("$Briefing Music:");
1042 stuff_string(name, F_NAME, NULL);
1043 event_music_set_score(SCORE_BRIEFING, name);
1045 if ( optional_string("$Debriefing Success Music:") ) {
1046 stuff_string(name, F_NAME, NULL);
1047 event_music_set_score(SCORE_DEBRIEF_SUCCESS, name);
1050 if ( optional_string("$Debriefing Fail Music:") ) {
1051 stuff_string(name, F_NAME, NULL);
1052 event_music_set_score(SCORE_DEBRIEF_FAIL, name);
1056 void parse_cmd_brief(mission *pm)
1060 SDL_assert(!Cur_cmd_brief->num_stages);
1063 required_string("#Command Briefing");
1064 while (optional_string("$Stage Text:")) {
1065 SDL_assert(stage < CMD_BRIEF_STAGES_MAX);
1066 Cur_cmd_brief->stage[stage].text = stuff_and_malloc_string(F_MULTITEXT, NULL, CMD_BRIEF_TEXT_MAX);
1067 SDL_assert(Cur_cmd_brief->stage[stage].text);
1069 required_string("$Ani Filename:");
1070 stuff_string(Cur_cmd_brief->stage[stage].ani_filename, F_FILESPEC, NULL);
1071 if (optional_string("+Wave Filename:"))
1072 stuff_string(Cur_cmd_brief->stage[stage].wave_filename, F_FILESPEC, NULL);
1074 Cur_cmd_brief->stage[stage].wave_filename[0] = 0;
1079 Cur_cmd_brief->num_stages = stage;
1082 void parse_cmd_briefs(mission *pm)
1087 // a hack follows because old missions don't have a command briefing
1088 if (required_string_either("#Command Briefing", "#Briefing"))
1091 for (i=0; i<Num_teams; i++) {
1092 Cur_cmd_brief = &Cmd_briefs[i];
1093 parse_cmd_brief(pm);
1097 // -------------------------------------------------------------------------------------------------
1100 // Parse the data required for the mission briefing
1102 // NOTE: This updates the global Briefing struct with all the data necessary to drive the briefing
1104 void parse_briefing(mission *pm)
1106 int nt, i, j, stage_num = 0, icon_num = 0, team_index;
1111 char not_used_text[MAX_ICON_TEXT_LEN];
1115 // MWA -- 2/3/98. we can now have multiple briefing and debreifings in a mission
1116 for ( nt = 0; nt < Num_teams; nt++ ) {
1117 if ( !optional_string("#Briefing") )
1120 bp = &Briefings[nt];
1122 required_string("$start_briefing");
1123 required_string("$num_stages:");
1124 stuff_int(&bp->num_stages);
1125 SDL_assert(bp->num_stages <= MAX_BRIEF_STAGES);
1128 while (required_string_either("$end_briefing", "$start_stage")) {
1129 required_string("$start_stage");
1130 SDL_assert(stage_num < MAX_BRIEF_STAGES);
1131 bs = &bp->stages[stage_num++];
1132 required_string("$multi_text");
1133 if ( Fred_running ) {
1134 stuff_string(bs->new_text, F_MULTITEXT, NULL, MAX_BRIEF_LEN);
1136 bs->new_text = stuff_and_malloc_string(F_MULTITEXT, NULL, MAX_BRIEF_LEN);
1138 required_string("$voice:");
1139 stuff_string(bs->voice, F_FILESPEC, NULL);
1140 required_string("$camera_pos:");
1141 stuff_vector(&bs->camera_pos);
1142 required_string("$camera_orient:");
1143 stuff_matrix(&bs->camera_orient);
1144 required_string("$camera_time:");
1145 stuff_int(&bs->camera_time);
1147 if ( optional_string("$num_lines:") ) {
1148 stuff_int(&bs->num_lines);
1150 if ( Fred_running ) {
1151 SDL_assert(bs->lines!=NULL);
1153 if ( bs->num_lines > 0 ) {
1154 bs->lines = (brief_line *)malloc(sizeof(brief_line)*bs->num_lines);
1155 SDL_assert(bs->lines!=NULL);
1159 for (i=0; i<bs->num_lines; i++) {
1160 required_string("$line_start:");
1161 stuff_int(&bs->lines[i].start_icon);
1162 required_string("$line_end:");
1163 stuff_int(&bs->lines[i].end_icon);
1170 required_string("$num_icons:");
1171 stuff_int(&bs->num_icons);
1173 if ( Fred_running ) {
1174 SDL_assert(bs->lines!=NULL);
1176 if ( bs->num_icons > 0 ) {
1177 bs->icons = (brief_icon *)malloc(sizeof(brief_icon)*bs->num_icons);
1178 SDL_assert(bs->icons!=NULL);
1182 if ( optional_string("$flags:") )
1183 stuff_int(&bs->flags);
1187 if ( optional_string("$formula:") )
1188 bs->formula = get_sexp_main();
1190 bs->formula = Locked_sexp_true;
1192 SDL_assert(bs->num_icons <= MAX_STAGE_ICONS );
1194 while (required_string_either("$end_stage", "$start_icon")) {
1195 required_string("$start_icon");
1196 SDL_assert(icon_num < MAX_STAGE_ICONS);
1197 bi = &bs->icons[icon_num++];
1199 required_string("$type:");
1200 stuff_int(&bi->type);
1202 find_and_stuff("$team:", &team_index, F_NAME, Team_names, Num_team_names, "team name");
1203 SDL_assert((team_index >= 0) && (team_index < MAX_TEAM_NAMES));
1204 bi->team = 1 << team_index;
1206 find_and_stuff("$class:", &bi->ship_class, F_NAME, (const char **)Ship_class_names, Num_ship_types, "ship class");
1208 required_string("$pos:");
1209 stuff_vector(&bi->pos);
1212 if (optional_string("$label:"))
1213 stuff_string(bi->label, F_MESSAGE, NULL);
1215 if (optional_string("+id:")) {
1217 if (bi->id >= Cur_brief_id)
1218 Cur_brief_id = bi->id + 1;
1222 for (i=0; i<stage_num-1; i++)
1223 for (j=0; j < bp->stages[i].num_icons; j++)
1225 if (!SDL_strcasecmp(bp->stages[i].icons[j].label, bi->label))
1226 bi->id = bp->stages[i].icons[j].id;
1230 bi->id = Cur_brief_id++;
1233 required_string("$hlight:");
1237 bi->flags = BI_HIGHLIGHT;
1242 required_string("$multi_text");
1243 // stuff_string(bi->text, F_MULTITEXT, NULL, MAX_ICON_TEXT_LEN);
1244 stuff_string(not_used_text, F_MULTITEXT, NULL, MAX_ICON_TEXT_LEN);
1245 required_string("$end_icon");
1247 SDL_assert(bs->num_icons == icon_num);
1249 required_string("$end_stage");
1252 SDL_assert(bp->num_stages == stage_num);
1253 required_string("$end_briefing");
1256 if ( nt != Num_teams )
1257 Error(LOCATION, "Not enough briefings in mission file. There are %d teams and only %d briefings.", Num_teams, nt );
1260 // -------------------------------------------------------------------------------------------------
1261 // parse_debriefing_old()
1263 // Parse the data required for the mission debriefings
1264 void parse_debriefing_old(mission *pm)
1267 char waste[MAX_DEBRIEF_LEN];
1269 if ( !optional_string("#Debriefing") )
1272 required_string("$num_debriefings:");
1275 while (required_string_either("#Players", "$start_debriefing")) {
1276 required_string("$start_debriefing");
1277 required_string("$formula:");
1278 junk = get_sexp_main();
1279 required_string("$num_stages:");
1281 while (required_string_either("$end_debriefing", "$start_stage")) {
1282 required_string("$start_stage");
1283 required_string("$multi_text");
1284 stuff_string(waste, F_MULTITEXT, NULL, MAX_DEBRIEF_LEN);
1285 required_string("$voice:");
1286 stuff_string(waste, F_FILESPEC, NULL);
1287 required_string("$end_stage");
1289 required_string("$end_debriefing");
1293 // -------------------------------------------------------------------------------------------------
1294 // parse_debriefing_new()
1296 // Parse the data required for the mission debriefings
1297 void parse_debriefing_new(mission *pm)
1305 // next code should be old -- hopefully not called anymore
1306 //if (!optional_string("#Debriefing_info")) {
1307 // parse_debriefing_old(pm);
1311 // 2/3/98 -- MWA. We can now have multiple briefings and debriefings on a team
1312 for ( nt = 0; nt < Num_teams; nt++ ) {
1314 if ( !optional_string("#Debriefing_info") )
1319 db = &Debriefings[nt];
1321 required_string("$Num stages:");
1322 stuff_int(&db->num_stages);
1323 SDL_assert(db->num_stages <= MAX_DEBRIEF_STAGES);
1325 while (required_string_either("#", "$Formula")) {
1326 SDL_assert(stage_num < MAX_DEBRIEF_STAGES);
1327 dbs = &db->stages[stage_num++];
1328 required_string("$Formula:");
1329 dbs->formula = get_sexp_main();
1330 required_string("$multi text");
1331 if ( Fred_running ) {
1332 stuff_string(dbs->new_text, F_MULTITEXT, NULL, MAX_DEBRIEF_LEN);
1334 dbs->new_text = stuff_and_malloc_string(F_MULTITEXT, NULL, MAX_DEBRIEF_LEN);
1336 required_string("$Voice:");
1337 stuff_string(dbs->voice, F_FILESPEC, NULL);
1338 required_string("$Recommendation text:");
1339 if ( Fred_running ) {
1340 stuff_string( dbs->new_recommendation_text, F_MULTITEXT, NULL, MAX_RECOMMENDATION_LEN);
1342 dbs->new_recommendation_text = stuff_and_malloc_string( F_MULTITEXT, NULL, MAX_RECOMMENDATION_LEN);
1346 SDL_assert(db->num_stages == stage_num);
1349 if ( nt != Num_teams )
1350 Error(LOCATION, "Not enough debriefings for mission. There are %d teams and only %d debriefings;\n", Num_teams, nt );
1353 void position_ship_for_knossos_warpin(p_object *objp, int shipnum, int objnum)
1355 // Assume no valid knossos device
1356 Ships[shipnum].special_warp_objnum = -1;
1358 // find knossos device
1361 int knossos_num = -1;
1362 for (so=GET_FIRST(&Ship_obj_list); so!=END_OF_LIST(&Ship_obj_list); so=GET_NEXT(so)) {
1363 knossos_num = Objects[so->objnum].instance;
1364 if (Ship_info[Ships[knossos_num].ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
1365 // be close to the right device [allow multiple knossos's
1366 if (vm_vec_dist_quick(&Objects[knossos_num].pos, &objp->pos) < 2.0f*(Objects[knossos_num].radius + Objects[objnum].radius) ) {
1374 // set ship special_warp_objnum
1375 Ships[shipnum].special_warp_objnum = knossos_num;
1377 // position self for warp on plane of device
1379 float dist = fvi_ray_plane(&new_point, &Objects[knossos_num].pos, &Objects[knossos_num].orient.v.fvec, &objp->pos, &objp->orient.v.fvec, 0.0f);
1380 polymodel *pm = model_get(Ship_info[Ships[shipnum].ship_info_index].modelnum);
1381 float desired_dist = -pm->mins.xyz.z;
1382 vm_vec_scale_add2(&Objects[objnum].pos, &Objects[objnum].orient.v.fvec, (dist - desired_dist));
1383 // if ship is BIG or HUGE, make it go through the center of the knossos
1384 if (Ship_info[Ships[shipnum].ship_info_index].flags & SIF_HUGE_SHIP) {
1386 vm_vec_sub(&offset, &Objects[knossos_num].pos, &new_point);
1387 vm_vec_add2(&Objects[objnum].pos, &offset);
1392 // Given a stuffed p_object struct, create an object and fill in the necessary fields.
1393 // Return object number.
1394 int parse_create_object(p_object *objp)
1396 int i, j, k, objnum, shipnum;
1400 subsys_status *sssp;
1403 // base level creation
1404 objnum = ship_create(&objp->orient, &objp->pos, objp->ship_class);
1405 SDL_assert(objnum != -1);
1406 shipnum = Objects[objnum].instance;
1408 // if arriving through knossos, adjust objpj->pos to plane of knossos and set flag
1409 // special warp is single player only
1410 if ((objp->flags & P_KNOSSOS_WARP_IN) && !(Game_mode & GM_MULTIPLAYER)) {
1411 if (!Fred_running) {
1412 position_ship_for_knossos_warpin(objp, shipnum, objnum);
1416 Ships[shipnum].group = objp->group;
1417 Ships[shipnum].team = objp->team;
1418 SDL_strlcpy(Ships[shipnum].ship_name, objp->name, SDL_arraysize(Ships[0].ship_name));
1419 Ships[shipnum].escort_priority = objp->escort_priority;
1420 Ships[shipnum].special_exp_index = objp->special_exp_index;
1421 Ships[shipnum].respawn_priority = objp->respawn_priority;
1422 // if this is a multiplayer dogfight game, and its from a player wing, make it team traitor
1423 if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT) && (objp->wingnum >= 0)){
1424 for (i = 0; i < MAX_STARTING_WINGS; i++ ) {
1425 if ( !SDL_strcasecmp(Starting_wing_names[i], Wings[objp->wingnum].name) ) {
1426 Ships[shipnum].team = TEAM_TRAITOR;
1431 sip = &Ship_info[Ships[shipnum].ship_info_index];
1433 if ( !Fred_running ) {
1434 ship_assign_sound(&Ships[shipnum]);
1437 aip = &(Ai_info[Ships[shipnum].ai_index]);
1438 aip->behavior = objp->behavior;
1439 aip->mode = aip->behavior;
1441 // alternate type name
1442 Ships[shipnum].alt_type_index = objp->alt_type_index;
1444 aip->ai_class = objp->ai_class;
1445 Ships[shipnum].weapons.ai_class = objp->ai_class; // Fred uses this instead of above.
1447 // must reset the number of ai goals when the object is created
1448 for (i = 0; i < MAX_AI_GOALS; i++ ){
1449 aip->goals[i].ai_mode = AI_GOAL_NONE;
1452 Ships[shipnum].cargo1 = objp->cargo1;
1454 Ships[shipnum].arrival_location = objp->arrival_location;
1455 Ships[shipnum].arrival_distance = objp->arrival_distance;
1456 Ships[shipnum].arrival_anchor = objp->arrival_anchor;
1457 Ships[shipnum].arrival_cue = objp->arrival_cue;
1458 Ships[shipnum].arrival_delay = objp->arrival_delay;
1459 Ships[shipnum].departure_location = objp->departure_location;
1460 Ships[shipnum].departure_anchor = objp->departure_anchor;
1461 Ships[shipnum].departure_cue = objp->departure_cue;
1462 Ships[shipnum].departure_delay = objp->departure_delay;
1463 Ships[shipnum].determination = objp->determination;
1464 Ships[shipnum].wingnum = objp->wingnum;
1465 Ships[shipnum].hotkey = objp->hotkey;
1466 Ships[shipnum].score = objp->score;
1467 Ships[shipnum].persona_index = objp->persona_index;
1469 // set the orders that this ship will accept. It will have already been set to default from the
1470 // ship create code, so only set them if the parse object flags say they are unique
1471 if ( objp->flags & P_SF_USE_UNIQUE_ORDERS ) {
1472 Ships[shipnum].orders_accepted = objp->orders_accepted;
1474 // MWA 5/15/98 -- Added the following debug code because some orders that ships
1475 // will accept were apparently written out incorrectly with Fred. This Int3() should
1476 // trap these instances.
1478 if ( Fred_running ) {
1479 int default_orders, remaining_orders;
1481 default_orders = ship_get_default_orders_accepted( &Ship_info[Ships[shipnum].ship_info_index] );
1482 remaining_orders = objp->orders_accepted & ~default_orders;
1483 if ( remaining_orders ) {
1484 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", Ships[shipnum].ship_name);
1490 // check the parse object's flags for possible flags to set on this newly created ship
1491 if ( objp->flags & P_OF_PROTECTED ) {
1492 Objects[objnum].flags |= OF_PROTECTED;
1495 if ( objp->flags & P_OF_BEAM_PROTECTED ) {
1496 Objects[objnum].flags |= OF_BEAM_PROTECTED;
1499 if (objp->flags & P_OF_CARGO_KNOWN) {
1500 Ships[shipnum].flags |= SF_CARGO_REVEALED;
1503 if ( objp->flags & P_SF_IGNORE_COUNT )
1504 Ships[shipnum].flags |= SF_IGNORE_COUNT;
1506 if ( objp->flags & P_SF_REINFORCEMENT )
1507 Ships[shipnum].flags |= SF_REINFORCEMENT;
1509 if (objp->flags & P_OF_NO_SHIELDS || sip->shields == 0 )
1510 Objects[objnum].flags |= OF_NO_SHIELDS;
1512 if (objp->flags & P_SF_ESCORT)
1513 Ships[shipnum].flags |= SF_ESCORT;
1515 if (objp->flags & P_KNOSSOS_WARP_IN) {
1516 Objects[objnum].flags |= OF_SPECIAL_WARP;
1519 // don't set the flag if the mission is ongoing in a multiplayer situation. This will be set by the players in the
1520 // game only before the game or during respawning.
1521 // MWA -- changed the next line to remove the !(Game_mode & GM_MULTIPLAYER). We shouldn't be setting
1522 // this flag in single player mode -- it gets set in post process mission.
1523 //if ((objp->flags & P_OF_PLAYER_START) && (((Game_mode & GM_MULTIPLAYER) && !(Game_mode & GM_IN_MISSION)) || !(Game_mode & GM_MULTIPLAYER)))
1524 if ( (objp->flags & P_OF_PLAYER_START) && (Fred_running || ((Game_mode & GM_MULTIPLAYER) && !(Game_mode & GM_IN_MISSION))) )
1525 Objects[objnum].flags |= OF_PLAYER_SHIP;
1527 if (objp->flags & P_SF_NO_ARRIVAL_MUSIC)
1528 Ships[shipnum].flags |= SF_NO_ARRIVAL_MUSIC;
1530 if ( objp->flags & P_SF_NO_ARRIVAL_WARP )
1531 Ships[shipnum].flags |= SF_NO_ARRIVAL_WARP;
1533 if ( objp->flags & P_SF_NO_DEPARTURE_WARP )
1534 Ships[shipnum].flags |= SF_NO_DEPARTURE_WARP;
1536 if ( objp->flags & P_SF_INITIALLY_DOCKED )
1537 Ships[shipnum].flags |= SF_INITIALLY_DOCKED;
1539 if ( objp->flags & P_SF_LOCKED )
1540 Ships[shipnum].flags |= SF_LOCKED;
1542 if ( objp->flags & P_SF_WARP_BROKEN )
1543 Ships[shipnum].flags |= SF_WARP_BROKEN;
1545 if ( objp->flags & P_SF_WARP_NEVER )
1546 Ships[shipnum].flags |= SF_WARP_NEVER;
1548 if ( objp->flags & P_SF_HIDDEN_FROM_SENSORS )
1549 Ships[shipnum].flags |= SF_HIDDEN_FROM_SENSORS;
1551 // if ship is in a wing, and the wing's no_warp_effect flag is set, then set the equivalent
1552 // flag for the ship
1553 if ( (Ships[shipnum].wingnum != -1) && (Wings[Ships[shipnum].wingnum].flags & WF_NO_ARRIVAL_WARP) )
1554 Ships[shipnum].flags |= SF_NO_ARRIVAL_WARP;
1556 if ( (Ships[shipnum].wingnum != -1) && (Wings[Ships[shipnum].wingnum].flags & WF_NO_DEPARTURE_WARP) )
1557 Ships[shipnum].flags |= SF_NO_DEPARTURE_WARP;
1559 // mwa -- 1/30/98. Do both flags. Fred uses the ship flag, and FreeSpace will use the object
1560 // flag. I'm to lazy at this point to deal with consolidating them.
1561 if ( objp->flags & P_SF_INVULNERABLE ) {
1562 Ships[shipnum].flags |= SF_INVULNERABLE;
1563 Objects[objnum].flags |= OF_INVULNERABLE;
1566 if ( objp->flags & P_SF_GUARDIAN ) {
1567 Objects[objnum].flags |= OF_GUARDIAN;
1570 if ( objp->flags & P_SF_SCANNABLE )
1571 Ships[shipnum].flags |= SF_SCANNABLE;
1573 if ( objp->flags & P_SF_RED_ALERT_STORE_STATUS ){
1574 SDL_assert(!(Game_mode & GM_MULTIPLAYER));
1575 Ships[shipnum].flags |= SF_RED_ALERT_STORE_STATUS;
1578 // a couple of ai_info flags. Also, do a reasonable default for the kamikaze damage regardless of
1579 // whether this flag is set or not
1580 if ( objp->flags & P_AIF_KAMIKAZE ) {
1581 Ai_info[Ships[shipnum].ai_index].ai_flags |= AIF_KAMIKAZE;
1582 Ai_info[Ships[shipnum].ai_index].kamikaze_damage = objp->kamikaze_damage;
1585 if ( objp->flags & P_AIF_NO_DYNAMIC )
1586 Ai_info[Ships[shipnum].ai_index].ai_flags |= AIF_NO_DYNAMIC;
1588 // if the wing index and wing pos are set for this parse object, set them for the ship. This
1589 // is useful in multiplayer when ships respawn
1590 Ships[shipnum].wing_status_wing_index = objp->wing_status_wing_index;
1591 Ships[shipnum].wing_status_wing_pos = objp->wing_status_wing_pos;
1593 // set up the ai_goals for this object -- all ships created here are AI controlled.
1594 if ( objp->ai_goals != -1 ) {
1597 for ( sexp = CDR(objp->ai_goals); sexp != -1; sexp = CDR(sexp) )
1598 // make a call to the routine in MissionGoals.cpp to set up the ai goals for this object.
1599 ai_add_ship_goal_sexp( sexp, AIG_TYPE_EVENT_SHIP, aip );
1601 if ( objp->wingnum == -1 ) // free the sexpression nodes only for non-wing ships. wing code will handle it's own case
1602 free_sexp2(objp->ai_goals); // free up sexp nodes for reused, since they aren't needed anymore.
1605 SDL_assert(Ships[shipnum].modelnum != -1);
1607 // initialize subsystem statii here. The subsystems are given a percentage damaged. So a percent value
1608 // of 20% means that the subsystem is 20% damaged (*not* 20% of max hits). This is opposite the way
1609 // that the initial velocity/hull strength/shields work
1610 i = objp->subsys_count;
1612 sssp = &Subsys_status[objp->subsys_index + i];
1613 if (!SDL_strcasecmp(sssp->name, NOX("Pilot"))) {
1614 wp = &Ships[shipnum].weapons;
1615 if (sssp->primary_banks[0] != SUBSYS_STATUS_NO_CHANGE) {
1616 for (j=k=0; j<MAX_PRIMARY_BANKS; j++) {
1617 if ( (sssp->primary_banks[j] >= 0) || Fred_running ){
1618 wp->primary_bank_weapons[k] = sssp->primary_banks[j];
1626 wp->num_primary_banks = sip->num_primary_banks;
1628 wp->num_primary_banks = k;
1632 if (sssp->secondary_banks[0] != SUBSYS_STATUS_NO_CHANGE) {
1633 for (j=k=0; j<MAX_SECONDARY_BANKS; j++) {
1634 if ( (sssp->secondary_banks[j] >= 0) || Fred_running ){
1635 wp->secondary_bank_weapons[k++] = sssp->secondary_banks[j];
1640 wp->num_secondary_banks = sip->num_secondary_banks;
1642 wp->num_secondary_banks = k;
1646 for (j=0; j < wp->num_secondary_banks; j++)
1648 wp->secondary_bank_ammo[j] = sssp->secondary_ammo[j];
1650 int capacity = fl2i(sssp->secondary_ammo[j]/100.0f * sip->secondary_bank_ammo_capacity[j] + 0.5f );
1651 wp->secondary_bank_ammo[j] = fl2i(capacity / Weapon_info[wp->secondary_bank_weapons[j]].cargo_size + 0.5f);
1656 ptr = GET_FIRST(&Ships[shipnum].subsys_list);
1657 while (ptr != END_OF_LIST(&Ships[shipnum].subsys_list)) {
1658 if (!SDL_strcasecmp(ptr->system_info->subobj_name, sssp->name)) {
1660 ptr->current_hits = sssp->percent;
1663 new_hits = ptr->system_info->max_hits * (100.0f - sssp->percent) / 100.f;
1664 Ships[shipnum].subsys_info[ptr->system_info->type].current_hits -= (ptr->system_info->max_hits - new_hits);
1665 if ( (100.0f - sssp->percent) < 0.5) {
1666 ptr->current_hits = 0.0f;
1667 ptr->submodel_info_1.blown_off = 1;
1669 ptr->current_hits = new_hits;
1673 if (sssp->primary_banks[0] != SUBSYS_STATUS_NO_CHANGE)
1674 for (j=0; j<MAX_PRIMARY_BANKS; j++)
1675 ptr->weapons.primary_bank_weapons[j] = sssp->primary_banks[j];
1677 if (sssp->secondary_banks[0] != SUBSYS_STATUS_NO_CHANGE)
1678 for (j=0; j<MAX_SECONDARY_BANKS; j++)
1679 ptr->weapons.secondary_bank_weapons[j] = sssp->secondary_banks[j];
1681 for (j=0; j<MAX_SECONDARY_BANKS; j++) {
1682 // AL 3-5-98: This is correct for FRED, but not for FreeSpace... but is this even used?
1683 // As far as I know, turrets cannot run out of ammo
1684 ptr->weapons.secondary_bank_ammo[j] = sssp->secondary_ammo[j];
1687 ptr->subsys_cargo_name = sssp->subsys_cargo_name;
1689 if (sssp->ai_class != SUBSYS_STATUS_NO_CHANGE)
1690 ptr->weapons.ai_class = sssp->ai_class;
1692 ai_turret_select_default_weapon(ptr);
1695 ptr = GET_NEXT(ptr);
1699 // initial hull strength, shields, and velocity are all expressed as a percentage of the max value/
1700 // so a initial_hull value of 90% means 90% of max. This way is opposite of how subsystems are dealt
1703 Objects[objnum].phys_info.speed = (float) objp->initial_velocity;
1704 // Ships[shipnum].hull_hit_points_taken = (float) objp->initial_hull;
1705 Objects[objnum].hull_strength = (float) objp->initial_hull;
1706 Objects[objnum].shields[0] = (float) objp->initial_shields;
1709 int max_allowed_sparks, num_sparks;
1712 // Ships[shipnum].hull_hit_points_taken = (float)objp->initial_hull * sip->max_hull_hit_points / 100.0f;
1713 Objects[objnum].hull_strength = objp->initial_hull * sip->initial_hull_strength / 100.0f;
1714 for (i = 0; i<MAX_SHIELD_SECTIONS; i++)
1715 Objects[objnum].shields[i] = (float)(objp->initial_shields * sip->shields / 100.0f) / MAX_SHIELD_SECTIONS;
1717 // initial velocities now do not apply to ships which warp in after mission starts
1718 if ( !(Game_mode & GM_IN_MISSION) ) {
1719 Objects[objnum].phys_info.speed = (float)objp->initial_velocity * sip->max_speed / 100.0f;
1720 Objects[objnum].phys_info.vel.xyz.z = Objects[objnum].phys_info.speed;
1721 Objects[objnum].phys_info.prev_ramp_vel = Objects[objnum].phys_info.vel;
1722 Objects[objnum].phys_info.desired_vel = Objects[objnum].phys_info.vel;
1725 // recalculate damage of subsystems
1726 ship_recalc_subsys_strength( &Ships[shipnum] );
1728 // create sparks on a ship whose hull is damaged. We will create two sparks for every 20%
1729 // of hull damage done. 100 means no sparks. between 80 and 100 do two sparks. 60 and 80 is
1731 pm = model_get( sip->modelnum );
1732 max_allowed_sparks = get_max_sparks(&Objects[objnum]);
1733 num_sparks = (int)((100.0f - objp->initial_hull) / 5.0f);
1734 if (num_sparks > max_allowed_sparks) {
1735 num_sparks = max_allowed_sparks;
1738 for (i = 0; i < num_sparks; i++ ) {
1741 // DA 10/20/98 - sparks must be chosen on the hull and not any submodel
1742 submodel_get_two_random_points(sip->modelnum, pm->detail[0], &v1, &v2);
1743 ship_hit_sparks_no_rotate(&Objects[objnum], &v1);
1744 // ship_hit_sparks_no_rotate(&Objects[objnum], &v2);
1748 // in mission, we add a log entry -- set ship positions for ships not in wings, and then do
1750 if ( (Game_mode & GM_IN_MISSION) && (!Fred_running) ) {
1751 mission_log_add_entry( LOG_SHIP_ARRIVE, Ships[shipnum].ship_name, NULL );
1753 // if this ship isn't in a wing, determine it's arrival location
1754 if ( !Game_restoring ) {
1755 if ( Ships[shipnum].wingnum == -1 ) {
1757 // multiplayer clients set the arrival location of ships to be at location since their
1758 // position has already been determined. Don't actually set the variable since we
1759 // don't want the warp effect to show if coming from a dock bay.
1760 location = objp->arrival_location;
1761 if ( MULTIPLAYER_CLIENT )
1762 location = ARRIVE_AT_LOCATION;
1763 mission_set_arrival_location(objp->arrival_anchor, location, objp->arrival_distance, objnum, NULL, NULL);
1764 if ( objp->arrival_location != ARRIVE_FROM_DOCK_BAY )
1765 shipfx_warpin_start( &Objects[objnum] );
1769 // possibly add this ship to a hotkey set
1770 if ( (Ships[shipnum].wingnum == -1) && (Ships[shipnum].hotkey != -1 ) )
1771 mission_hotkey_mf_add( Ships[shipnum].hotkey, Ships[shipnum].objnum, HOTKEY_MISSION_FILE_ADDED );
1772 else if ( (Ships[shipnum].wingnum != -1) && (Wings[Ships[shipnum].wingnum].hotkey != -1 ) )
1773 mission_hotkey_mf_add( Wings[Ships[shipnum].wingnum].hotkey, Ships[shipnum].objnum, HOTKEY_MISSION_FILE_ADDED );
1775 // possibly add this ship to the hud escort list
1776 if ( Ships[shipnum].flags & SF_ESCORT ){
1777 hud_add_remove_ship_escort( objnum, 1 );
1781 // for multiplayer games, make a call to the network code to assign the object signature
1782 // of the newly created object. The network host of the netgame will always assign a signature
1783 // to a newly created object. The network signature will get to the clients of the game in
1784 // different manners depending on whether or not an individual ship or a wing was created.
1785 if ( Game_mode & GM_MULTIPLAYER ) {
1786 Objects[objnum].net_signature = objp->net_signature;
1788 if ( (Game_mode & GM_IN_MISSION) && MULTIPLAYER_MASTER && (objp->wingnum == -1) ){
1789 send_ship_create_packet( &Objects[objnum], (objp==Arriving_support_ship)?1:0 );
1793 // if recording a demo, post the event
1794 if(Game_mode & GM_DEMO_RECORD){
1795 demo_POST_obj_create(objp->name, Objects[objnum].signature);
1801 // Mp points at the text of an object, which begins with the "$Name:" field.
1802 // Snags all object information and calls parse_create_object to create a ship.
1803 // Why create a ship? Why not an object? Stay tuned...
1805 // flag is parameter that is used to tell what kind information we are retrieving from the mission.
1806 // if we are just getting player starts, then don't create the objects
1807 int parse_object(mission *pm, int flag, p_object *objp)
1809 // p_object temp_object;
1811 int i, j, count, shipnum, delay, destroy_before_mission_time;
1812 char name[NAME_LENGTH], flag_strings[MAX_PARSE_OBJECT_FLAGS][NAME_LENGTH];
1814 SDL_assert(pm != NULL);
1816 // objp = &temp_object;
1818 required_string("$Name:");
1819 stuff_string(objp->name, F_NAME, NULL);
1820 shipnum = ship_name_lookup(objp->name);
1822 error_display(0, NOX("Redundant ship name: %s\n"), objp->name);
1825 find_and_stuff("$Class:", &objp->ship_class, F_NAME, (const char **)Ship_class_names, Num_ship_types, "ship class");
1826 if (objp->ship_class < 0) {
1827 Warning(LOCATION, "Ship \"%s\" has an invalid ship type (ships.tbl probably changed). Making it type 0", objp->name);
1829 // if fred is running, maybe notify the user that the mission contains MD content
1831 Fred_found_unknown_ship_during_parsing = 1;
1834 objp->ship_class = 0;
1837 // if this is a multiplayer dogfight mission, skip support ships
1838 if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT) && (Ship_info[objp->ship_class].flags & SIF_SUPPORT)){
1842 // optional alternate name type
1843 objp->alt_type_index = -1;
1844 if(optional_string("$Alt:")){
1846 stuff_string(name, F_NAME, NULL, NAME_LENGTH);
1848 // try and find the alternate name
1849 objp->alt_type_index = (char)mission_parse_lookup_alt(name);
1850 SDL_assert(objp->alt_type_index >= 0);
1851 if(objp->alt_type_index < 0){
1852 mprintf(("Error looking up alternate ship type name!\n"));
1854 mprintf(("Using alternate ship type name : %s\n", name));
1859 find_and_stuff("$Team:", &team_index, F_NAME, Team_names, Num_team_names, "team name");
1860 SDL_assert((team_index >= 0) && (team_index < MAX_TEAM_NAMES));
1861 objp->team = 1 << team_index;
1863 required_string("$Location:");
1864 stuff_vector(&objp->pos);
1866 required_string("$Orientation:");
1867 stuff_matrix(&objp->orient);
1869 find_and_stuff("$IFF:", &objp->iff, F_NAME, Iff_names, Num_iff, "IFF");
1870 find_and_stuff("$AI Behavior:", &objp->behavior, F_NAME, Ai_behavior_names, Num_ai_behaviors, "AI behavior");
1871 objp->ai_goals = -1;
1873 if ( optional_string("+AI Class:")) {
1874 objp->ai_class = match_and_stuff(F_NAME, (const char **)Ai_class_names, Num_ai_classes, "AI class");
1875 SDL_assert(objp->ai_class > -1 );
1877 objp->ai_class = Ship_info[objp->ship_class].ai_class;
1880 if ( optional_string("$AI Goals:") ){
1881 objp->ai_goals = get_sexp_main();
1884 if ( !required_string_either("$AI Goals:", "$Cargo 1:") ) {
1885 required_string("$AI Goals:");
1886 objp->ai_goals = get_sexp_main();
1891 find_and_stuff_or_add("$Cargo 1:", &temp, F_NAME, Cargo_names, NAME_LENGTH, &Num_cargo, MAX_CARGO, "cargo");
1892 objp->cargo1 = char(temp);
1893 if ( optional_string("$Cargo 2:") ) {
1894 char buf[NAME_LENGTH];
1895 stuff_string(buf, F_NAME, NULL);
1898 parse_common_object_data(objp); // get initial conditions and subsys status
1900 while (required_string_either("$Arrival Location:", "$Status Description:")) {
1901 SDL_assert(count < MAX_OBJECT_STATUS);
1903 find_and_stuff("$Status Description:", &objp->status_type[count], F_NAME, Status_desc_names, Num_status_names, "Status Description");
1904 find_and_stuff("$Status:", &objp->status[count], F_NAME, Status_type_names, Num_status_names, "Status Type");
1905 find_and_stuff("$Target:", &objp->target[count], F_NAME, Status_target_names, Num_status_names, "Target");
1908 objp->status_count = count;
1910 objp->arrival_anchor = -1;
1911 objp->arrival_distance = 0;
1912 find_and_stuff("$Arrival Location:", &objp->arrival_location, F_NAME, Arrival_location_names, Num_arrival_names, "Arrival Location");
1913 if ( optional_string("+Arrival Distance:") ) {
1914 stuff_int( &objp->arrival_distance );
1915 if ( objp->arrival_location != ARRIVE_AT_LOCATION ) {
1916 required_string("$Arrival Anchor:");
1917 stuff_string(name, F_NAME, NULL);
1918 objp->arrival_anchor = get_anchor(name);
1922 if (optional_string("+Arrival Delay:")) {
1925 Error(LOCATION, "Cannot have arrival delay < 0 (ship %s)", objp->name);
1929 if ( !Fred_running ){
1930 objp->arrival_delay = -delay; // use negative numbers to mean we haven't set up a timer yet
1932 objp->arrival_delay = delay;
1935 required_string("$Arrival Cue:");
1936 objp->arrival_cue = get_sexp_main();
1937 if ( !Fred_running && (objp->arrival_cue >= 0) ) {
1938 // eval the arrival cue. if the cue is true, set up the timestamp for the arrival delay
1939 SDL_assert ( objp->arrival_delay <= 0 );
1941 // don't eval arrival_cues when just looking for player information.
1942 if ( eval_sexp(objp->arrival_cue) ){ // evaluate to determine if sexp is always false.
1943 objp->arrival_delay = timestamp( -objp->arrival_delay * 1000 );
1947 find_and_stuff("$Departure Location:", &objp->departure_location, F_NAME, Departure_location_names, Num_arrival_names, "Departure Location");
1948 objp->departure_anchor = -1;
1949 if ( objp->departure_location == DEPART_AT_DOCK_BAY ) {
1950 required_string("$Departure Anchor:");
1951 stuff_string(name, F_NAME, NULL);
1952 objp->departure_anchor = get_anchor(name);
1955 if (optional_string("+Departure Delay:")) {
1958 Error(LOCATION, "Cannot have departure delay < 0 (ship %s)", objp->name);
1964 if ( !Fred_running ){
1965 objp->departure_delay = -delay;
1967 objp->departure_delay = delay;
1970 required_string("$Departure Cue:");
1971 objp->departure_cue = get_sexp_main();
1973 if (optional_string("$Misc Properties:"))
1974 stuff_string(objp->misc, F_NAME, NULL);
1976 required_string("$Determination:");
1977 stuff_int(&objp->determination);
1980 if (optional_string("+Flags:")) {
1981 count = stuff_string_list(flag_strings, MAX_PARSE_OBJECT_FLAGS);
1982 for (i=0; i<count; i++) {
1983 for (j=0; j<MAX_PARSE_OBJECT_FLAGS; j++) {
1984 if (!SDL_strcasecmp(flag_strings[i], Parse_object_flags[j])) {
1985 objp->flags |= (1 << j);
1990 if (j == MAX_PARSE_OBJECT_FLAGS)
1991 Warning(LOCATION, "Unknown flag in mission file: %s\n", flag_strings[i]);
1995 // always store respawn priority, just for ease of implementation
1996 objp->respawn_priority = 0;
1997 if(optional_string("+Respawn Priority:" )){
1998 stuff_int(&objp->respawn_priority);
2001 objp->escort_priority = 0;
2002 if ( optional_string("+Escort Priority:" ) ) {
2003 SDL_assert(objp->flags & P_SF_ESCORT);
2004 stuff_int(&objp->escort_priority);
2007 if ( objp->flags & P_OF_PLAYER_START ) {
2008 objp->flags |= P_OF_CARGO_KNOWN; // make cargo known for players
2012 objp->special_exp_index = -1;
2013 if ( optional_string("+Special Exp index:" ) ) {
2014 stuff_int(&objp->special_exp_index);
2017 // if the kamikaze flag is set, we should have the next flag
2018 if ( optional_string("+Kamikaze Damage:") ) {
2022 objp->kamikaze_damage = i2fl(damage);
2026 if (optional_string("+Hotkey:")) {
2027 stuff_int(&objp->hotkey);
2028 SDL_assert((objp->hotkey >= 0) && (objp->hotkey < 10));
2031 objp->docked_with[0] = 0;
2032 if (optional_string("+Docked With:")) {
2033 stuff_string(objp->docked_with, F_NAME, NULL);
2034 required_string("$Docker Point:");
2035 stuff_string(objp->docker_point, F_NAME, NULL);
2036 required_string("$Dockee Point:");
2037 stuff_string(objp->dockee_point, F_NAME, NULL);
2039 objp->flags |= P_SF_INITIALLY_DOCKED;
2041 // put this information into the Initially_docked array. We will need to use this
2042 // informatin later since not all ships will initially get created.
2043 SDL_strlcpy(Initially_docked[Total_initially_docked].dockee, objp->docked_with, NAME_LENGTH);
2044 SDL_strlcpy(Initially_docked[Total_initially_docked].docker_point, objp->docker_point, NAME_LENGTH);
2045 SDL_strlcpy(Initially_docked[Total_initially_docked].dockee_point, objp->dockee_point, NAME_LENGTH);
2046 Initially_docked[Total_initially_docked].docker = objp;
2047 Total_initially_docked++;
2050 // check the optional parameter for destroying the ship before the mission starts. If this parameter is
2051 // here, then we need to destroy the ship N seconds before the mission starts (for debris purposes).
2052 // store the time value here. We want to create this object for sure. Set the arrival cue and arrival
2053 // delay to bogus values
2054 destroy_before_mission_time = -1;
2055 if ( optional_string("+Destroy At:") ) {
2057 stuff_int(&destroy_before_mission_time);
2058 SDL_assert ( destroy_before_mission_time >= 0 );
2059 objp->arrival_cue = Locked_sexp_true;
2060 objp->arrival_delay = timestamp(0);
2063 // check for the optional "orders accepted" string which contains the orders from the default
2064 // set that this ship will actually listen to
2065 if ( optional_string("+Orders Accepted:") ) {
2066 stuff_int( &objp->orders_accepted );
2067 if ( objp->orders_accepted != -1 ){
2068 objp->flags |= P_SF_USE_UNIQUE_ORDERS;
2072 if (optional_string("+Group:")){
2073 stuff_int(&objp->group);
2078 if (optional_string("+Score:")){
2079 stuff_int(&objp->score);
2084 // parse the persona index if present
2085 if ( optional_string("+Persona Index:")){
2086 stuff_int(&objp->persona_index);
2088 objp->persona_index = -1;
2091 objp->wingnum = -1; // set the wing number to -1 -- possibly to be set later
2093 // for multiplayer, assign a network signature to this parse object. Doing this here will
2094 // allow servers to use the signature with clients when creating new ships, instead of having
2095 // to pass ship names all the time
2096 if ( Game_mode & GM_MULTIPLAYER ){
2097 objp->net_signature = multi_assign_network_signature( MULTI_SIG_SHIP );
2100 // set the wing_status position to be -1 for all objects. This will get set to an appropriate
2101 // value when the wing positions are finally determined.
2102 objp->wing_status_wing_index = -1;
2103 objp->wing_status_wing_pos = -1;
2104 objp->respawn_count = 0;
2106 // if this if the starting player ship, then copy if to Starting_player_pobject (used for ingame join)
2107 if ( !SDL_strcasecmp( objp->name, Player_start_shipname) ) {
2108 Player_start_pobject = *objp;
2109 Player_start_pobject.flags |= P_SF_PLAYER_START_VALID;
2113 // Now create the object.
2114 // Don't create the new ship blindly. First, check the sexp for the arrival cue
2115 // to determine when this ship should arrive. If not right away, stick this ship
2116 // onto the ship arrival list to be looked at later. Also check to see if it should use the
2117 // wings arrival cue. The ship may get created later depending on whether or not the wing
2119 // always create ships when FRED is running
2121 // don't create the object if it is intially docked for either FreeSpcae or Fred. Fred will
2122 // create the object later in post_process_mission
2123 if ( (objp->flags & P_SF_INITIALLY_DOCKED) || (!Fred_running && (!eval_sexp(objp->arrival_cue) || !timestamp_elapsed(objp->arrival_delay) || (objp->flags & P_SF_REINFORCEMENT))) ) {
2124 SDL_assert ( destroy_before_mission_time == -1 ); // we can't add ships getting destroyed to the arrival list!!!
2125 SDL_assert ( num_ship_arrivals < MAX_SHIP_ARRIVALS );
2126 memcpy( &ship_arrivals[num_ship_arrivals], objp, sizeof(p_object) );
2127 list_append(&ship_arrival_list, &ship_arrivals[num_ship_arrivals]);
2128 num_ship_arrivals++;
2130 // ingame joiners bail here.
2131 else if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)){
2137 real_objnum = parse_create_object(objp); // this object may later get destroyed depending on wing status!!!!
2139 Subsys_index = objp->subsys_index; // free elements that are no longer needed.
2141 // if the ship is supposed to be destroyed before the mission, then blow up the ship, mark the pieces
2142 // as last forever. Only call this stuff when you are blowing up the ship
2143 if ( destroy_before_mission_time >= 0 ) {
2146 robjp = &Objects[real_objnum];
2147 if ( !Fred_running ) {
2148 shipfx_blow_up_model( robjp, Ships[robjp->instance].modelnum, 0, 0, &robjp->pos );
2149 robjp->flags |= OF_SHOULD_BE_DEAD;
2151 // once the ship is exploded, find the debris pieces belonging to this object, mark them
2152 // as not to expire, and move them forward in time N seconds
2153 for (i = 0; i < MAX_DEBRIS_PIECES; i++ ) {
2157 if ( !(db->flags & DEBRIS_USED) ) // not used, move onto the next one.
2159 if ( db->source_objnum != real_objnum ) // not from this ship, move to next one
2162 debris_clear_expired_flag(db); // mark as don't expire
2163 db->lifeleft = -1.0f; // be sure that lifeleft == -1.0 so that it really doesn't expire!
2165 // now move the debris along it's path for N seconds
2166 robjp = &Objects[db->objnum];
2167 physics_sim( &robjp->pos, &robjp->orient, &robjp->phys_info, (float)destroy_before_mission_time );
2170 // be sure to set the variable in the ships structure for the final death time!!!
2171 Ships[robjp->instance].final_death_time = destroy_before_mission_time;
2172 Ships[robjp->instance].flags |= SF_KILL_BEFORE_MISSION;
2180 void parse_common_object_data(p_object *objp)
2184 // set some defaults..
2185 objp->initial_velocity = 0;
2186 objp->initial_hull = 100;
2187 objp->initial_shields = 100;
2189 // now change defaults if present
2190 if (optional_string("+Initial Velocity:")) {
2191 stuff_int(&objp->initial_velocity);
2194 if (optional_string("+Initial Hull:"))
2195 stuff_int(&objp->initial_hull);
2196 if (optional_string("+Initial Shields:"))
2197 stuff_int(&objp->initial_shields);
2199 objp->subsys_index = Subsys_index;
2200 objp->subsys_count = 0;
2201 while (optional_string("+Subsystem:")) {
2202 i = allocate_subsys_status();
2204 objp->subsys_count++;
2205 stuff_string(Subsys_status[i].name, F_NAME, NULL);
2207 if (optional_string("$Damage:"))
2208 stuff_float(&Subsys_status[i].percent);
2210 Subsys_status[i].subsys_cargo_name = -1;
2211 if (optional_string("+Cargo Name:")) {
2212 char cargo_name[256];
2213 stuff_string(cargo_name, F_NAME, NULL);
2214 int index = string_lookup(cargo_name, (const char **)Cargo_names, Num_cargo, "cargo", 0);
2215 if (index == -1 && (Num_cargo < MAX_CARGO)) {
2217 SDL_strlcpy(Cargo_names[Num_cargo++], cargo_name, NAME_LENGTH);
2219 Subsys_status[i].subsys_cargo_name = index;
2222 if (optional_string("+AI Class:"))
2223 Subsys_status[i].ai_class = match_and_stuff(F_NAME, (const char **)Ai_class_names, Num_ai_classes, "AI class");
2225 if (optional_string("+Primary Banks:"))
2226 stuff_int_list(Subsys_status[i].primary_banks, MAX_PRIMARY_BANKS, WEAPON_LIST_TYPE);
2228 if (optional_string("+Secondary Banks:"))
2229 stuff_int_list(Subsys_status[i].secondary_banks, MAX_SECONDARY_BANKS, WEAPON_LIST_TYPE);
2231 if (optional_string("+Sbank Ammo:"))
2232 stuff_int_list(Subsys_status[i].secondary_ammo, MAX_SECONDARY_BANKS, RAW_INTEGER_TYPE);
2237 void parse_objects(mission *pm, int flag)
2241 SDL_assert(pm != NULL);
2243 required_string("#Objects");
2246 num_ship_original = 0;
2247 while (required_string_either("#Wings", "$Name:")){
2248 // not all objects are always valid or legal
2249 if(parse_object(pm, flag, &temp)){
2250 // add to the default list
2251 if(num_ship_original < MAX_SHIP_ORIGINAL){
2252 memcpy(&ship_original[num_ship_original++], &temp, sizeof(p_object));
2258 p_object *mission_parse_get_original_ship( ushort net_signature )
2262 // look for original ships
2263 for(idx=0; idx<num_ship_original; idx++){
2264 if(ship_original[idx].net_signature == net_signature){
2265 return &ship_original[idx];
2273 int find_wing_name(char *name)
2277 for (i=0; i<num_wings; i++){
2278 if (!strcmp(name, Wings[i].name)){
2286 // function to create ships in the wing that need to be created. We psas the wing pointer, it's index
2287 // into the Wings array
2288 int parse_wing_create_ships( wing *wingp, int num_to_create, int force, int specific_instance )
2291 int wingnum, objnum, num_create_save;
2293 int pre_create_count;
2295 // we need to send this in multiplayer
2296 pre_create_count = wingp->total_arrived_count;
2298 // force is used to force creation of the wing -- used for multiplayer
2300 // we only want to evaluate the arrival cue of the wing if:
2302 // 2) multiplayer and I am the host of the game
2303 // can't create any ships if the arrival cue is false or the timestamp has not elapsed.
2305 if ( !eval_sexp(wingp->arrival_cue) ) /* || !timestamp_elapsed(wingp->arrival_delay) ) */
2308 // once the sexpressions becomes true, then check the arrival delay on the wing. The first time, the
2309 // arrival delay will be <= 0 meaning that no timer object has been set yet. Set up the timestamp
2310 // which should always give a number >= 0;
2311 if ( wingp->arrival_delay <= 0 ) {
2312 wingp->arrival_delay = timestamp( -wingp->arrival_delay * 1000 );
2313 SDL_assert ( wingp->arrival_delay >= 0 );
2316 if ( !timestamp_elapsed( wingp->arrival_delay ) )
2319 // if wing is coming from docking bay, then be sure that ship we are arriving from actually exists
2321 if ( wingp->arrival_location == ARRIVE_FROM_DOCK_BAY ) {
2325 SDL_assert( wingp->arrival_anchor >= 0 );
2326 name = Parse_names[wingp->arrival_anchor];
2328 // see if ship is yet to arrive. If so, then return -1 so we can evaluate again later.
2329 if ( mission_parse_get_arrival_ship( name ) )
2332 // see if ship is in mission. If not, then we can assume it was destroyed or departed since
2333 // it is not on the arrival list (as shown by above if statement).
2334 shipnum = ship_name_lookup( name );
2335 if ( shipnum == -1 ) {
2337 // since this wing cannot arrive from this place, we need to mark the wing as destroyed and
2338 // set the wing variables appropriatly. Good for directives.
2340 // set the gone flag
2341 wingp->flags |= WF_WING_GONE;
2343 // if the current wave is zero, it never existed
2344 wingp->flags |= WF_NEVER_EXISTED;
2346 // mark the number of waves and number of ships destroyed equal to the last wave and the number
2347 // of ships yet to arrive
2348 num_remaining = ( (wingp->num_waves - wingp->current_wave) * wingp->wave_count);
2349 wingp->total_arrived_count += num_remaining;
2350 wingp->current_wave = wingp->num_waves;
2352 // replaced following three lines of code with mission log call because of bug with
2353 // the Ships_exited list.
2354 //index = ship_find_exited_ship_by_name( name );
2355 //SDL_assert( index != -1 );
2356 //if (Ships_exited[index].flags & SEF_DESTROYED ) {
2357 if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) ) {
2358 wingp->total_destroyed += num_remaining;
2360 wingp->total_departed += num_remaining;
2363 Sexp_nodes[wingp->arrival_cue].value = SEXP_KNOWN_FALSE;
2368 if ( num_to_create == 0 )
2371 // check the wave_delay_timestamp field. If it is not valid, make it valid (based on wave delay min
2372 // and max valuds). If it is valid, and not elapsed, then return. If it is valid and elasped, then
2374 if ( !timestamp_valid(wingp->wave_delay_timestamp) ) {
2376 // if at least one of these is valid, then reset the timestamp. If they are both zero, we will create the
2378 if ( (wingp->wave_delay_min > 0) || (wingp->wave_delay_max > 0) ) {
2379 SDL_assert ( wingp->wave_delay_min <= wingp->wave_delay_max );
2380 time_to_arrive = wingp->wave_delay_min + (int)(frand() * (wingp->wave_delay_max - wingp->wave_delay_min));
2383 // HACK HACK -- in the presense of Mike Comet and Mitri, I have introduced one of the most
2384 // serious breaches of coding standards. I'm to lazy to fix this the correct way. Insert
2385 // a delay before the next wave of the wing can arrive to that clients in the game have ample
2386 // time to kill off any ships in the wing before the next wave arrives.
2387 if ( Game_mode & GM_MULTIPLAYER ){
2388 time_to_arrive += 7;
2390 wingp->wave_delay_timestamp = timestamp(time_to_arrive * 1000);
2394 // if we get here, both min and max values are 0; See comments above for a most serious hack
2396 if ( Game_mode & GM_MULTIPLAYER )
2397 time_to_arrive += 7;
2398 time_to_arrive *= 1000;
2399 wingp->wave_delay_timestamp = timestamp(time_to_arrive);
2402 // now check to see if the wave_delay_timestamp is elapsed or not
2403 if ( !timestamp_elapsed(wingp->wave_delay_timestamp) )
2407 // finally we can create the wing.
2409 num_create_save = num_to_create;
2411 wingnum = wingp - Wings; // get the wing number
2413 // if there are no ships to create, then all ships must be player start ships -- do nothing in this case.
2414 if ( num_to_create == 0 ){
2418 wingp->current_wave++; // we are creating new ships
2419 // we need to create num_to_create ships. Since the arrival cues for ships in a wing
2420 // are ignored, then *all* ships must be in the ship_arrival_list.
2423 objp = GET_FIRST(&ship_arrival_list);
2424 while( objp != END_OF_LIST(&ship_arrival_list) ) {
2425 p_object *temp = GET_NEXT(objp);
2427 // compare the wingnums. When they are equal, we can create the ship. In the case of
2428 // wings that have multiple waves, this code implies that we essentially creating clones
2429 // of the ships that were created in Fred for the wing when more ships for a new wave
2430 // arrive. The threshold value of a wing can also make one of the ships in a wing be "cloned"
2431 // more often than other ships in the wing. I don't think this matters much.
2432 if ( objp->wingnum == wingnum ) {
2435 // when ingame joining, we need to create a specific ship out of the list of ships for a
2436 // wing. specific_instance is a 0 based integer which specified which ship in the wing
2437 // to create. So, only create the ship we actually need to.
2438 if ( (Game_mode & GM_MULTIPLAYER) && (specific_instance > 0) ) {
2439 specific_instance--;
2444 SDL_assert ( !(objp->flags & P_SF_CANNOT_ARRIVE) ); // get allender
2448 // if we have the maximum number of ships in the wing, we must bail as well
2449 if ( wingp->current_count >= MAX_SHIPS_PER_WING ) {
2450 Int3(); // this is bogus -- we should always allow all ships to be created
2455 // bash the ship name to be the name of the wing + sone number if there is > 1 wave in
2457 // also, if multplayer, set the parse object's net signature to be wing's net signature
2458 // base + total_arrived_count (before adding 1)
2459 if ( Game_mode & GM_MULTIPLAYER ){
2460 objp->net_signature = (ushort)(wingp->net_signature + wingp->total_arrived_count);
2463 wingp->total_arrived_count++;
2464 if ( wingp->num_waves > 1 ){
2465 SDL_snprintf(objp->name, SDL_arraysize(objp->name), NOX("%s %d"), wingp->name, wingp->total_arrived_count);
2468 objnum = parse_create_object(objp);
2469 aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2471 // copy any goals from the wing to the newly created ship
2472 for (index = 0; index < MAX_AI_GOALS; index++) {
2473 if ( wingp->ai_goals[index].ai_mode != AI_GOAL_NONE ){
2474 ai_copy_mission_wing_goal( &wingp->ai_goals[index], aip );
2478 Ai_info[Ships[Objects[objnum].instance].ai_index].wing = wingnum;
2480 if ( wingp->flags & WF_NO_DYNAMIC ){
2481 aip->ai_flags |= AIF_NO_DYNAMIC;
2484 // update housekeeping variables
2485 wingp->ship_index[wingp->current_count] = Objects[objnum].instance;
2487 // set up wingman status index
2488 hud_wingman_status_set_index(wingp->ship_index[wingp->current_count]);
2490 objp->wing_status_wing_index = Ships[Objects[objnum].instance].wing_status_wing_index;
2491 objp->wing_status_wing_pos = Ships[Objects[objnum].instance].wing_status_wing_pos;
2493 wingp->current_count++;
2495 // keep any player ship on the parse object list -- used for respawns
2496 // 5/8/98 -- MWA -- don't remove ships from the list when you are ingame joining
2497 if ( !(objp->flags & P_OF_PLAYER_START) ) {
2498 if ( (Game_mode & GM_NORMAL) || !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ) {
2499 if ( wingp->num_waves == wingp->current_wave ) { // only remove ship if one wave in wing
2500 list_remove( &ship_arrival_list, objp); // remove objp from the list
2501 if ( objp->ai_goals != -1 ){
2502 free_sexp2(objp->ai_goals); // free up sexp nodes for reuse
2508 // flag ship with SF_FROM_PLAYER_WING if a member of player starting wings
2509 if ( (Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM) ) {
2510 // but for team vs. team games, then just check the alpha and zeta wings
2511 if ( !(SDL_strcasecmp(Starting_wing_names[STARTING_WING_ALPHA], wingp->name)) || !(SDL_strcasecmp(Starting_wing_names[STARTING_WING_ZETA], wingp->name)) ) {
2512 Ships[Objects[objnum].instance].flags |= SF_FROM_PLAYER_WING;
2515 for (int i = 0; i < MAX_STARTING_WINGS; i++ ) {
2516 if ( !SDL_strcasecmp(Starting_wing_names[i], wingp->name) ) {
2517 Ships[Objects[objnum].instance].flags |= SF_FROM_PLAYER_WING;
2522 // keep track of how many ships to create. Stop when we have done all that we are supposed
2525 if ( !num_to_create ){
2532 SDL_assert ( num_to_create == 0 ); // we should always have enough ships in the list!!!
2534 // possibly play some event driven music here. Send a network packet indicating the wing was
2535 // created. Only do this stuff if actually in the mission.
2536 if ( (objnum != -1) && (Game_mode & GM_IN_MISSION) ) { // if true, we have created at least one new ship.
2539 // see if this wing is a player starting wing, and if so, call the maybe_add_form_goal
2540 // function to possibly make the wing form on the player
2541 for (i = 0; i < MAX_STARTING_WINGS; i++ ) {
2542 if ( Starting_wings[i] == wingnum ){
2546 if ( i < MAX_STARTING_WINGS ){
2547 ai_maybe_add_form_goal( wingp );
2550 mission_log_add_entry( LOG_WING_ARRIVE, wingp->name, NULL, wingp->current_wave );
2551 ship_num = wingp->ship_index[0];
2553 if ( !(Ships[ship_num].flags & SF_NO_ARRIVAL_MUSIC) && !(wingp->flags & WF_NO_ARRIVAL_MUSIC) ) {
2554 if ( timestamp_elapsed(Allow_arrival_music_timestamp) ) {
2555 Allow_arrival_music_timestamp = timestamp(ARRIVAL_MUSIC_MIN_SEPARATION);
2556 event_music_arrival(Ships[ship_num].team);
2560 // possibly change the location where these ships arrive based on the wings arrival location
2561 mission_set_wing_arrival_location( wingp, num_create_save );
2563 // if in multiplayer (and I am the host) and in the mission, send a wing create command to all
2565 if ( MULTIPLAYER_MASTER ){
2566 send_wing_create_packet( wingp, num_create_save, pre_create_count );
2570 // test code to check to be sure that all ships in the wing are ignoring the same types
2571 // of orders from the player
2572 if ( Fred_running ) {
2573 SDL_assert( wingp->ship_index[0] != -1 );
2574 int orders = Ships[wingp->ship_index[0]].orders_accepted;
2575 for (i = 1; i < wingp->current_count; i++ ) {
2576 if ( orders != Ships[wingp->ship_index[i]].orders_accepted ) {
2577 Warning(LOCATION, "ships in wing %s are ignoring different player orders. Please find Mark A\nto talk to him about this.", wingp->name );
2586 wingp->wave_delay_timestamp = timestamp(-1); // we will need to set this up properly for the next wave
2587 return num_create_save;
2590 void parse_wing(mission *pm)
2592 int wingnum, i, wing_goals, delay;
2593 char name[NAME_LENGTH], ship_names[MAX_SHIPS_PER_WING][NAME_LENGTH];
2594 char wing_flag_strings[MAX_WING_FLAGS][NAME_LENGTH];
2597 SDL_assert(pm != NULL);
2598 wingp = &Wings[num_wings];
2600 required_string("$Name:");
2601 stuff_string(wingp->name, F_NAME, NULL);
2602 wingnum = find_wing_name(wingp->name);
2604 error_display(0, NOX("Redundant wing name: %s\n"), wingp->name);
2605 wingnum = num_wings;
2607 wingp->total_arrived_count = 0;
2608 wingp->total_destroyed = 0;
2611 required_string("$Waves:");
2612 stuff_int(&wingp->num_waves);
2613 SDL_assert ( wingp->num_waves >= 1 ); // there must be at least 1 wave
2615 wingp->current_wave = 0;
2617 required_string("$Wave Threshold:");
2618 stuff_int(&wingp->threshold);
2620 required_string("$Special Ship:");
2621 stuff_int(&wingp->special_ship);
2623 wingp->arrival_anchor = -1;
2624 find_and_stuff("$Arrival Location:", &wingp->arrival_location, F_NAME, Arrival_location_names, Num_arrival_names, "Arrival Location");
2625 if ( optional_string("+Arrival Distance:") ) {
2626 stuff_int( &wingp->arrival_distance );
2627 if ( wingp->arrival_location != ARRIVE_AT_LOCATION ) {
2628 required_string("$Arrival Anchor:");
2629 stuff_string(name, F_NAME, NULL);
2630 wingp->arrival_anchor = get_anchor(name);
2634 if (optional_string("+Arrival delay:")) {
2637 Error(LOCATION, "Cannot have arrival delay < 0 on wing %s", wingp->name );
2641 if ( !Fred_running ){
2642 wingp->arrival_delay = -delay;
2644 wingp->arrival_delay = delay;
2647 required_string("$Arrival Cue:");
2648 wingp->arrival_cue = get_sexp_main();
2649 if ( !Fred_running && (wingp->arrival_cue >= 0) ) {
2650 if ( eval_sexp(wingp->arrival_cue) ) // evaluate to determine if sexp is always false.
2651 wingp->arrival_delay = timestamp( -wingp->arrival_delay * 1000 );
2655 find_and_stuff("$Departure Location:", &wingp->departure_location, F_NAME, Departure_location_names, Num_arrival_names, "Departure Location");
2656 wingp->departure_anchor = -1;
2657 if ( wingp->departure_location == DEPART_AT_DOCK_BAY ) {
2658 required_string("$Departure Anchor:");
2659 stuff_string( name, F_NAME, NULL );
2660 wingp->departure_anchor = get_anchor(name);
2663 if (optional_string("+Departure delay:")) {
2666 Error(LOCATION, "Cannot have departure delay < 0 on wing %s", wingp->name );
2671 if ( !Fred_running )
2672 wingp->departure_delay = -delay; // use negative numbers to mean that delay timer not yet set
2674 wingp->departure_delay = delay;
2676 required_string("$Departure Cue:");
2677 wingp->departure_cue = get_sexp_main();
2679 // stores a list of all names of ships in the wing
2680 required_string("$Ships:");
2681 wingp->wave_count = stuff_string_list( ship_names, MAX_SHIPS_PER_WING );
2682 wingp->current_count = 0;
2684 // get the wings goals, if any
2686 if ( optional_string("$AI Goals:") )
2687 wing_goals = get_sexp_main();
2690 if (optional_string("+Hotkey:")) {
2691 stuff_int(&wingp->hotkey);
2692 SDL_assert((wingp->hotkey >= 0) && (wingp->hotkey < 10));
2695 if (optional_string("+Flags:")) {
2698 count = stuff_string_list( wing_flag_strings, MAX_WING_FLAGS );
2699 for (i = 0; i < count; i++ ) {
2700 if ( !SDL_strcasecmp( wing_flag_strings[i], NOX("ignore-count")) )
2701 wingp->flags |= WF_IGNORE_COUNT;
2702 else if ( !SDL_strcasecmp( wing_flag_strings[i], NOX("reinforcement")) )
2703 wingp->flags |= WF_REINFORCEMENT;
2704 else if ( !SDL_strcasecmp( wing_flag_strings[i], NOX("no-arrival-music")) )
2705 wingp->flags |= WF_NO_ARRIVAL_MUSIC;
2706 else if ( !SDL_strcasecmp( wing_flag_strings[i], NOX("no-arrival-message")) )
2707 wingp->flags |= WF_NO_ARRIVAL_MESSAGE;
2708 else if ( !SDL_strcasecmp( wing_flag_strings[i], NOX("no-arrival-warp")) )
2709 wingp->flags |= WF_NO_ARRIVAL_WARP;
2710 else if ( !SDL_strcasecmp( wing_flag_strings[i], NOX("no-departure-warp")) )
2711 wingp->flags |= WF_NO_DEPARTURE_WARP;
2712 else if ( !SDL_strcasecmp( wing_flag_strings[i], NOX("no-dynamic")) )
2713 wingp->flags |= WF_NO_DYNAMIC;
2715 Warning(LOCATION, "unknown wing flag\n%s\n\nSkipping.", wing_flag_strings[i]);
2719 // get the wave arrival delay bounds (if present). Used as lower and upper bounds (in seconds)
2720 // which determine when new waves of a wing should arrive.
2721 wingp->wave_delay_min = 0;
2722 wingp->wave_delay_max = 0;
2723 if ( optional_string("+Wave Delay Min:") )
2724 stuff_int( &(wingp->wave_delay_min) );
2725 if ( optional_string("+Wave Delay Max:") )
2726 stuff_int( &(wingp->wave_delay_max) );
2728 // be sure to set the wave arrival timestamp of this wing to pop right away so that the
2729 // wing could be created if it needs to be
2730 wingp->wave_delay_timestamp = timestamp(0);
2732 // initialize wing goals
2733 for (i=0; i<MAX_AI_GOALS; i++) {
2734 wingp->ai_goals[i].ai_mode = AI_GOAL_NONE;
2735 wingp->ai_goals[i].priority = -1;
2739 // error checking against the player ship wings to be sure that wave count doesn't exceed one for
2741 if ( Game_mode & GM_MULTIPLAYER ) {
2742 for (i = 0; i < MAX_STARTING_WINGS+1; i++ ) {
2743 if ( !SDL_strcasecmp(Starting_wing_names[i], wingp->name) ) {
2744 if ( wingp->num_waves > 1 ) {
2745 // only end the game if we're the server - clients will eventually find out :)
2746 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2747 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_WAVE_COUNT);
2749 // Error(LOCATION, "Player wings Alpha, Beta, Gamma, or Zeta cannot have more than 1 wave.");
2755 // Get the next starting signature for this in this wing. We want to reserve wave_count * num_waves
2756 // of signature. These can be used to construct wings for ingame joiners.
2757 if ( Game_mode & GM_MULTIPLAYER ) {
2760 wingp->net_signature = multi_assign_network_signature( MULTI_SIG_SHIP );
2761 next_signature = wingp->net_signature + (wingp->wave_count * wingp->num_waves);
2762 if ( next_signature > SHIP_SIG_MAX )
2763 Error(LOCATION, "Too many total ships in mission (%d) for network signature assignment", SHIP_SIG_MAX);
2764 multi_set_network_signature( (ushort)next_signature, MULTI_SIG_SHIP );
2767 for (i=0; i<MAX_SHIPS_PER_WING; i++)
2768 wingp->ship_index[i] = -1;
2770 // set up the ai_goals for this wing -- all ships created from this wing will inherit these goals
2771 // goals for the wing are stored slightly differently than for ships. We simply store the index
2772 // into the sexpression array of each goal (max 10). When a ship in this wing is created, each
2773 // goal in the wings goal array is given to the ship.
2774 if ( wing_goals != -1 ) {
2777 // this will assign the goals to the wings as well as to any ships in the wing that have been
2779 for ( sexp = CDR(wing_goals); sexp != -1; sexp = CDR(sexp) )
2780 ai_add_wing_goal_sexp(sexp, AIG_TYPE_EVENT_WING, wingnum); // used by Fred
2783 free_sexp2(wing_goals); // free up sexp nodes for reused, since they aren't needed anymore.
2786 // set the wing number for all ships in the wing
2787 for (i = 0; i < wingp->wave_count; i++ ) {
2788 //char *ship_name = wingp->ship_names[i];
2790 int num, assigned = 0;
2793 ship_name = ship_names[i];
2795 num = wingp->ship_index[i] = ship_name_lookup(ship_name, 1);
2796 SDL_assert ( num != -1 );
2798 // hack code -- REMOVE
2799 if ( Objects[Ships[num].objnum].flags & OF_PLAYER_SHIP )
2800 Ships[num].wingnum = wingnum;
2803 // determine if this ship is a player ship, and deal with it appropriately.
2804 if ( !SDL_strncasecmp(ship_name, NOX("Player "), 7) ) {
2805 Error(LOCATION, "Old mission file -- please convert by loading/saving in Fred -- see Allender/Hoffoss for help.");
2808 // assign the wing number to the ship -- if the ship has arrived, doulble check that
2809 // there is only one wave of this wing since ships with their own arrival cue cannot be
2810 // in a wing with > 1 wave. Otherwise, find the ship on the ship arrival list and set
2811 // their wing number
2812 if ( (num = ship_name_lookup(ship_name)) != -1 ) {
2813 Int3(); // this is impossible under the new system
2816 objp = GET_FIRST(&ship_arrival_list);
2817 while( objp != END_OF_LIST(&ship_arrival_list) ) {
2818 if ( !strcmp(ship_name, objp->name) ) {
2819 SDL_assert ( objp->wingnum == -1 ); // get Allender -- ship appears to be in multiple wings
2820 objp->wingnum = wingnum;
2823 objp = GET_NEXT(objp);
2827 if ( !assigned || (assigned > 1) )
2828 Error(LOCATION, "Cannot load mission -- wing %s -- ship %s not present in #Objects section (or specified multiple times in wing.\n", wingp->name, ship_name);
2832 // Fred doesn't create the wing. otherwise, create the wing if is isn't a reinforcement.
2833 if ( !Fred_running && !(wingp->flags & WF_REINFORCEMENT) )
2834 parse_wing_create_ships( wingp, wingp->wave_count );
2837 void parse_wings(mission *pm)
2839 required_string("#Wings");
2840 while (required_string_either("#Events", "$Name:")) {
2841 SDL_assert(num_wings < MAX_WINGS);
2847 // mission events are sexpressions which cause things to happen based on the outcome
2848 // of other events in a mission. Essentially scripting the different things that can happen
2851 void parse_event(mission *pm)
2854 mission_event *event;
2856 event = &Mission_events[Num_mission_events];
2857 event->chain_delay = -1;
2859 required_string( "$Formula:" );
2860 event->formula = get_sexp_main();
2862 if (optional_string("+Name:")){
2863 stuff_string(event->name, F_NAME, NULL);
2868 if ( optional_string("+Repeat Count:")){
2869 stuff_int( &(event->repeat_count) );
2871 event->repeat_count = 1;
2874 event->interval = -1;
2875 if ( optional_string("+Interval:")){
2876 stuff_int( &(event->interval) );
2880 if ( optional_string("+Score:") ){
2881 stuff_int(&event->score);
2884 if ( optional_string("+Chained:") ){
2885 stuff_int(&event->chain_delay);
2888 if ( optional_string("+Objective:") ) {
2889 stuff_string(buf, F_NAME, NULL);
2890 event->objective_text = strdup(buf);
2892 event->objective_text = NULL;
2895 if ( optional_string("+Objective key:") ) {
2896 stuff_string(buf, F_NAME, NULL);
2897 event->objective_key_text = strdup(buf);
2899 event->objective_key_text = NULL;
2903 if( optional_string("+Team:") ) {
2904 stuff_int(&event->team);
2907 event->timestamp = timestamp(-1);
2909 // sanity check on the repeat count variable
2910 if ( event->repeat_count <= 0 ){
2911 Error (LOCATION, "Repeat count for mission event %s is <=0.\nMust be >= 1!", event->name );
2915 void parse_events(mission *pm)
2917 required_string("#Events");
2919 while (required_string_either( "#Goals", "$Formula:")) {
2920 SDL_assert( Num_mission_events < MAX_MISSION_EVENTS );
2922 Num_mission_events++;
2926 void parse_goal(mission *pm)
2930 mission_goal *goalp;
2932 goalp = &Mission_goals[Num_goals++];
2934 SDL_assert(Num_goals < MAX_GOALS);
2935 SDL_assert(pm != NULL);
2937 find_and_stuff("$Type:", &goalp->type, F_NAME, Goal_type_names, Num_goal_type_names, "goal type");
2939 required_string("+Name:");
2940 stuff_string(goalp->name, F_NAME, NULL);
2942 // backwards compatibility for old Fred missions - all new missions should use $MessageNew
2943 if(optional_string("$Message:")){
2944 stuff_string(goalp->message, F_NAME, NULL, MAX_GOAL_TEXT);
2946 required_string("$MessageNew:");
2947 stuff_string(goalp->message, F_MULTITEXT, NULL, MAX_GOAL_TEXT);
2950 if (optional_string("$Rating:")){
2951 stuff_int(&dummy); // not used
2954 required_string("$Formula:");
2955 goalp->formula = get_sexp_main();
2958 if ( optional_string("+Invalid:") )
2959 goalp->type |= INVALID_GOAL;
2960 if ( optional_string("+Invalid") )
2961 goalp->type |= INVALID_GOAL;
2962 if ( optional_string("+No music") )
2963 goalp->flags |= MGF_NO_MUSIC;
2966 if ( optional_string("+Score:") ){
2967 stuff_int(&goalp->score);
2971 if ( optional_string("+Team:") ){
2972 stuff_int( &goalp->team );
2976 void parse_goals(mission *pm)
2978 required_string("#Goals");
2980 while (required_string_either("#Waypoints", "$Type:")){
2985 void parse_waypoint_list(mission *pm)
2990 SDL_assert(Num_waypoint_lists < MAX_WAYPOINT_LISTS);
2991 SDL_assert(pm != NULL);
2992 wpl = &Waypoint_lists[Num_waypoint_lists];
2994 required_string("$Name:");
2995 stuff_string(wpl->name, F_NAME, NULL);
2997 required_string("$List:");
2998 wpl->count = stuff_vector_list(wpl->waypoints, MAX_WAYPOINTS_PER_LIST);
3001 // AAAAHH! I don't like to hard code a mission fix but I have no clue what to do
3002 // to fix this properly. In the FS1 mission "Playing Judas" you have to try and fly
3003 // into one of the docking bays on the Lucifer. Due to some change in the code the
3004 // waypoints and the Lucifer's position don't match up so we have to change the
3005 // waypoint position to compensate.
3006 if ( !SDL_strcasecmp(pm->name, "Playing Judas") ) {
3007 if ( !SDL_strcasecmp(wpl->name, "Docking Bay 1") ) {
3008 wpl->waypoints[0].xyz.x = -1262.550903;
3009 wpl->waypoints[0].xyz.y = 27.676950;
3010 wpl->waypoints[0].xyz.z = 4461.702930;
3011 } else if ( !SDL_strcasecmp(wpl->name, "Docking Bat 2") ) { // it really is spelled "Bat" in the mission
3012 wpl->waypoints[0].xyz.x = -1105.347976;
3013 wpl->waypoints[0].xyz.y = 27.676950;
3014 wpl->waypoints[0].xyz.z = 3900.236867;
3019 Num_waypoint_lists++;
3022 void parse_waypoints(mission *pm)
3027 required_string("#Waypoints");
3030 while (optional_string("$Jump Node:")) {
3031 SDL_assert(Num_jump_nodes < MAX_JUMP_NODES);
3033 z = jumpnode_create(&pos);
3036 if (optional_string("$Jump Node Name:")) {
3037 stuff_string(Jump_nodes[Num_jump_nodes - 1].name, F_NAME, NULL);
3040 // If no name exists, then use a standard name
3041 if ( Jump_nodes[Num_jump_nodes - 1].name[0] == 0 ) {
3042 SDL_snprintf(Jump_nodes[Num_jump_nodes - 1].name, NAME_LENGTH, "Jump Node %d", Num_jump_nodes);
3046 while (required_string_either("#Messages", "$Name:"))
3047 parse_waypoint_list(pm);
3050 void parse_messages(mission *pm)
3052 required_string("#Messages");
3054 mprintf(("Starting mission message count : %d\n", Num_message_waves));
3056 // the message_parse function can be found in MissionMessage.h. The format in the
3057 // mission file takes the same format as the messages in messages,tbl. Make parsing
3058 // a whole lot easier!!!
3059 while ( required_string_either("#Reinforcements", "$Name")){
3060 message_parse(); // call the message parsing system
3063 mprintf(("Ending mission message count : %d\n", Num_message_waves));
3066 void parse_reinforcement(mission *pm)
3068 reinforcements *ptr;
3071 SDL_assert(Num_reinforcements < MAX_REINFORCEMENTS);
3072 SDL_assert(pm != NULL);
3073 ptr = &Reinforcements[Num_reinforcements];
3075 required_string("$Name:");
3076 stuff_string(ptr->name, F_NAME, NULL);
3078 find_and_stuff("$Type:", &ptr->type, F_NAME, Reinforcement_type_names, Num_reinforcement_type_names, "reinforcement type");
3080 required_string("$Num times:");
3081 stuff_int(&ptr->uses);
3084 // reset the flags to 0
3087 if ( optional_string("+Arrival delay:") ){
3088 stuff_int( &(ptr->arrival_delay) );
3091 if ( optional_string("+No Messages:") ){
3092 stuff_string_list( ptr->no_messages, MAX_REINFORCEMENT_MESSAGES );
3095 if ( optional_string("+Yes Messages:") ){
3096 stuff_string_list( ptr->yes_messages, MAX_REINFORCEMENT_MESSAGES );
3099 // sanity check on the names of reinforcements -- must either be wings/ships/arrival list.
3100 if ( ship_name_lookup(ptr->name) == -1 ) {
3101 if ( wing_name_lookup(ptr->name, 1) == -1 ) {
3104 for ( p_objp = GET_FIRST(&ship_arrival_list); p_objp != END_OF_LIST(&ship_arrival_list); p_objp = GET_NEXT(p_objp) ) {
3105 if ( !SDL_strcasecmp(ptr->name, p_objp->name) ){
3110 if ( p_objp == END_OF_LIST(&ship_arrival_list) ) {
3111 Warning(LOCATION, "Reinforcement %s not found as ship or wing", ptr->name);
3117 // now, if the reinforcement is a wing, then set the number of waves of the wing == number of
3118 // uses of the reinforcement
3119 instance = wing_name_lookup(ptr->name, 1);
3120 if ( instance != -1 )
3121 Wings[instance].num_waves = ptr->uses;
3123 Num_reinforcements++;
3126 void parse_reinforcements(mission *pm)
3128 Num_reinforcements = 0;
3129 required_string("#Reinforcements");
3131 while (required_string_either("#Background bitmaps", "$Name:"))
3132 parse_reinforcement(pm);
3135 void parse_bitmap(mission *pm)
3138 starfield_bitmap_instance b;
3142 SDL_assert(pm != NULL);
3144 while(optional_string("$Bitmap:")) {
3145 stuff_string(b.filename, F_NAME, NULL);
3147 required_string("$Orientation:");
3150 required_string("$Rotation rate:");
3151 stuff_float(&b.rot);
3153 required_string("$Distance:");
3154 stuff_float(&b.scale_x);
3155 b.scale_y = b.scale_x;
3159 required_string("$Light:");
3160 stuff_int(&b.sun_light);
3162 if(Num_suns < MAX_STARFIELD_BITMAPS){
3164 SDL_strlcpy(Suns[Num_suns].filename, b.filename, SDL_arraysize(b.filename));
3170 char name[NAME_LENGTH];
3172 starfield_bitmaps *ptr;
3174 SDL_assert(Num_starfield_bitmaps < MAX_STARFIELD_BITMAPS);
3175 SDL_assert(pm != NULL);
3176 ptr = &Starfield_bitmaps[Num_starfield_bitmaps];
3178 required_string("$Bitmap:");
3179 stuff_string(name, F_NAME, NULL);
3180 for (z=0; z<Num_starfield_bitmap_lists; z++) {
3181 if (!SDL_strcasecmp(name, Starfield_bitmap_list[z].name)){
3186 if ( z >= Num_starfield_bitmap_lists ) {
3187 Warning( LOCATION, "Bitmap specified in mission not in game!\n" );
3191 ptr->bitmap_index = z;
3192 required_string("$Orientation:");
3193 stuff_matrix(&ptr->m);
3195 required_string("$Rotation rate:");
3196 stuff_float(&ptr->rot);
3198 required_string("$Distance:");
3199 stuff_float(&ptr->dist);
3201 required_string("$Light:");
3202 stuff_int(&ptr->light);
3203 Num_starfield_bitmaps++;
3204 calculate_bitmap_points(ptr);
3210 void parse_bitmaps(mission *pm)
3212 char str[MAX_FILENAME_LEN+1] = "";
3213 starfield_bitmap_instance b;
3216 Num_starfield_bitmaps = 0;
3217 required_string("#Background bitmaps");
3219 required_string("$Num stars:");
3220 stuff_int(&Num_stars);
3221 if (Num_stars >= MAX_STARS)
3222 Num_stars = MAX_STARS;
3224 int Ambient_light_level;
3225 required_string("$Ambient light level:");
3226 stuff_int(&Ambient_light_level);
3228 // This should call light_set_ambient() to
3229 // set the ambient light
3232 Mission_palette = 1;
3234 if(The_mission.flags & MISSION_FLAG_FULLNEB){
3235 // no regular nebula stuff
3239 SDL_strlcpy(Neb2_texture_name, "Eraseme3", SDL_arraysize(Neb2_texture_name));
3240 Neb2_poof_flags = ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4) | (1<<5));
3241 if(optional_string("+Neb2:")){
3242 stuff_string(Neb2_texture_name, F_NAME, NULL);
3244 required_string("+Neb2Flags:");
3245 stuff_int(&Neb2_poof_flags);
3247 // initialize neb effect. its gross to do this here, but Fred is dumb so I have no choice ... :(
3253 if (optional_string("+Nebula:")) {
3254 stuff_string(str, F_NAME, NULL, MAX_FILENAME_LEN);
3256 // parse the proper nebula type (full or not)
3257 for (z=0; z<NUM_NEBULAS; z++){
3258 if(The_mission.flags & MISSION_FLAG_FULLNEB){
3259 if (!SDL_strcasecmp(str, Neb2_filenames[z])) {
3264 if (!SDL_strcasecmp(str, Nebula_filenames[z])) {
3271 if (optional_string("+Color:")) {
3272 stuff_string(str, F_NAME, NULL, MAX_FILENAME_LEN);
3273 for (z=0; z<NUM_NEBULA_COLORS; z++){
3274 if (!SDL_strcasecmp(str, Nebula_colors[z])) {
3275 Mission_palette = z;
3281 if (optional_string("+Pitch:")){
3282 stuff_int(&Nebula_pitch);
3287 if (optional_string("+Bank:")){
3288 stuff_int(&Nebula_bank);
3293 if (optional_string("+Heading:")){
3294 stuff_int(&Nebula_heading);
3300 if (Nebula_index >= 0){
3301 nebula_init(Nebula_filenames[Nebula_index], Nebula_pitch, Nebula_bank, Nebula_heading);
3309 while(optional_string("$Sun:")){
3311 stuff_string(b.filename, F_NAME, NULL);
3314 required_string("+Angles:");
3315 stuff_float(&b.ang.p);
3316 stuff_float(&b.ang.b);
3317 stuff_float(&b.ang.h);
3320 required_string("+Scale:");
3321 stuff_float(&b.scale_x);
3322 b.scale_y = b.scale_x;
3326 // if we have room, store it
3327 if(Num_suns < MAX_STARFIELD_BITMAPS){
3329 SDL_strlcpy(Suns[Num_suns].filename, b.filename, SDL_arraysize(b.filename));
3334 // parse background bitmaps
3335 Num_starfield_bitmaps = 0;
3336 while(optional_string("$Starbitmap:")){
3338 stuff_string(b.filename, F_NAME, NULL);
3341 required_string("+Angles:");
3342 stuff_float(&b.ang.p);
3343 stuff_float(&b.ang.b);
3344 stuff_float(&b.ang.h);
3348 if(optional_string("+Scale:")){
3349 stuff_float(&b.scale_x);
3350 b.scale_y = b.scale_x;
3354 required_string("+ScaleX:");
3355 stuff_float(&b.scale_x);
3357 required_string("+ScaleY:");
3358 stuff_float(&b.scale_y);
3360 required_string("+DivX:");
3361 stuff_int(&b.div_x);
3363 required_string("+DivY:");
3364 stuff_int(&b.div_y);
3367 // if we have room, store it
3368 if(Num_starfield_bitmaps < MAX_STARFIELD_BITMAPS){
3369 Starfield_bitmap_instance[Num_starfield_bitmaps] = b;
3370 SDL_strlcpy(Starfield_bitmap_instance[Num_starfield_bitmaps].filename, b.filename, SDL_arraysize(b.filename));
3371 Num_starfield_bitmaps++;
3379 if ( optional_string("#Asteroid Fields") ){
3380 parse_asteroid_fields(pm);
3384 void parse_asteroid_fields(mission *pm)
3386 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
3388 int i, count, subtype;
3390 SDL_assert(pm != NULL);
3391 for (i=0; i<MAX_ASTEROID_FIELDS; i++)
3392 Asteroid_field.num_initial_asteroids = 0;
3396 // required_string("#Asteroid Fields");
3398 while (required_string_either("#", "$Density:")) {
3400 while (required_string_either("#", "$density:")) {
3402 float speed, density;
3405 required_string("$Density:");
3406 stuff_float(&density);
3408 Asteroid_field.num_initial_asteroids = (int) density;
3410 Asteroid_field.field_type = FT_ACTIVE;
3411 if (optional_string("+Field Type:")) {
3412 stuff_int((int*)&Asteroid_field.field_type);
3415 Asteroid_field.debris_genre = DG_ASTEROID;
3416 if (optional_string("+Debris Genre:")) {
3417 stuff_int((int*)&Asteroid_field.debris_genre);
3420 Asteroid_field.field_debris_type[0] = -1;
3421 Asteroid_field.field_debris_type[1] = -1;
3422 Asteroid_field.field_debris_type[2] = -1;
3423 if (Asteroid_field.debris_genre == DG_SHIP) {
3424 if (optional_string("+Field Debris Type:")) {
3425 stuff_int(&Asteroid_field.field_debris_type[0]);
3427 if (optional_string("+Field Debris Type:")) {
3428 stuff_int(&Asteroid_field.field_debris_type[1]);
3430 if (optional_string("+Field Debris Type:")) {
3431 stuff_int(&Asteroid_field.field_debris_type[2]);
3435 if (optional_string("+Field Debris Type:")) {
3436 stuff_int(&subtype);
3437 Asteroid_field.field_debris_type[subtype] = 1;
3440 if (optional_string("+Field Debris Type:")) {
3441 stuff_int(&subtype);
3442 Asteroid_field.field_debris_type[subtype] = 1;
3445 if (optional_string("+Field Debris Type:")) {
3446 stuff_int(&subtype);
3447 Asteroid_field.field_debris_type[subtype] = 1;
3452 // backward compatibility
3453 if ( (Asteroid_field.debris_genre == DG_ASTEROID) && (count == 0) ) {
3454 Asteroid_field.field_debris_type[0] = 0;
3457 required_string("$Average Speed:");
3458 stuff_float(&speed);
3460 vm_vec_rand_vec_quick(&Asteroid_field.vel);
3461 vm_vec_scale(&Asteroid_field.vel, speed);
3463 Asteroid_field.speed = speed;
3465 required_string("$Minimum:");
3466 stuff_vector(&Asteroid_field.min_bound);
3468 required_string("$Maximum:");
3469 stuff_vector(&Asteroid_field.max_bound);
3471 if (optional_string("+Inner Bound:")) {
3472 Asteroid_field.has_inner_bound = 1;
3474 required_string("$Minimum:");
3475 stuff_vector(&Asteroid_field.inner_min_bound);
3477 required_string("$Maximum:");
3478 stuff_vector(&Asteroid_field.inner_max_bound);
3480 Asteroid_field.has_inner_bound = 0;
3487 void parse_variables()
3489 if (! optional_string("#Sexp_variables") ) {
3492 stuff_sexp_variable_list();
3497 void parse_mission(mission *pm, int flag)
3501 Player_starts = Num_cargo = Num_waypoint_lists = Num_goals = num_wings = num_ship_arrivals = 0;
3502 Player_start_shipnum = -1;
3503 *Player_start_shipname = 0; // make the string 0 length for checking later
3504 memset( &Player_start_pobject, 0, sizeof(Player_start_pobject) );
3506 // initialize the initially_docked array.
3507 for ( i = 0; i < MAX_SHIPS; i++ ) {
3508 Initially_docked[i].docker = NULL;
3509 Initially_docked[i].dockee[0] = '\0';
3510 Initially_docked[i].docker_point[0] = '\0';
3511 Initially_docked[i].dockee_point[0] = '\0';
3513 Total_initially_docked = 0;
3515 list_init( &ship_arrival_list ); // init lists for arrival objects and wings
3520 parse_mission_info(pm);
3521 Current_file_checksum = netmisc_calc_checksum(pm,MISSION_CHECKSUM_SIZE);
3522 if ( flag == MISSION_PARSE_MISSION_INFO )
3524 parse_plot_info(pm);
3526 parse_briefing_info(pm); // TODO: obsolete code, keeping so we don't obsolete existing mission files
3527 parse_cmd_briefs(pm);
3529 parse_debriefing_new(pm);
3530 parse_player_info(pm);
3531 parse_objects(pm, flag);
3535 parse_waypoints(pm);
3537 parse_reinforcements(pm);
3541 post_process_mission();
3544 void post_process_mission()
3547 int indices[MAX_SHIPS], objnum;
3552 // the player_start_shipname had better exist at this point!
3553 Player_start_shipnum = ship_name_lookup( Player_start_shipname );
3554 SDL_assert ( Player_start_shipnum != -1 );
3555 SDL_assert ( Player_start_pobject.flags & P_SF_PLAYER_START_VALID );
3557 // Assign objnum, shipnum, etc. to the player structure
3558 objnum = Ships[Player_start_shipnum].objnum;
3559 Player_obj = &Objects[objnum];
3561 Player->objnum = objnum;
3564 Player_obj->flags |= OF_PLAYER_SHIP; // make this object a player controlled ship.
3565 Player_ship = &Ships[Player_start_shipnum];
3566 Player_ai = &Ai_info[Player_ship->ai_index];
3568 Player_ai->targeted_subsys = NULL;
3569 Player_ai->targeted_subsys_parent = -1;
3571 // determine if player start has initial velocity and set forward cruise percent to relect this
3572 if ( Player_obj->phys_info.vel.xyz.z > 0.0f )
3573 Player->ci.forward_cruise_percent = Player_obj->phys_info.vel.xyz.z / Player_ship->current_max_speed * 100.0f;
3575 // put in hard coded starting wing names.
3576 if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){
3577 Starting_wings[0] = wing_name_lookup(Starting_wing_names[0],1);
3578 Starting_wings[1] = wing_name_lookup(Starting_wing_names[MAX_STARTING_WINGS],1);
3580 for (i = 0; i < MAX_STARTING_WINGS; i++ ) {
3581 Starting_wings[i] = wing_name_lookup(Starting_wing_names[i], 1);
3587 // call a function to deal with intially docked ships
3588 mission_parse_do_initial_docks();
3590 // deal with setting up arrival location for all ships. Must do this now after all ships are created
3591 mission_parse_set_arrival_locations();
3593 // clear out information about arriving support ships
3594 Arriving_support_ship = NULL;
3595 Num_arriving_repair_targets = 0;
3597 // convert all ship name indices to ship indices now that mission has been loaded
3599 for (i=0; i<Num_parse_names; i++) {
3600 indices[i] = ship_name_lookup(Parse_names[i], 1);
3602 Warning(LOCATION, "Ship name \"%s\" referenced, but this ship doesn't exist", Parse_names[i]);
3605 for (i=0; i<MAX_SHIPS; i++) {
3606 if ((Ships[i].objnum >= 0) && (Ships[i].arrival_anchor >= 0) && (Ships[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHORS_OFFSET))
3607 Ships[i].arrival_anchor = indices[Ships[i].arrival_anchor];
3609 if ( (Ships[i].objnum >= 0) && (Ships[i].departure_anchor >= 0) )
3610 Ships[i].departure_anchor = indices[Ships[i].departure_anchor];
3613 for (i=0; i<MAX_WINGS; i++) {
3614 if (Wings[i].wave_count && (Wings[i].arrival_anchor >= 0) && (Wings[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHORS_OFFSET))
3615 Wings[i].arrival_anchor = indices[Wings[i].arrival_anchor];
3617 if (Wings[i].wave_count && (Wings[i].departure_anchor >= 0) )
3618 Wings[i].departure_anchor = indices[Wings[i].departure_anchor];
3623 // before doing anything else, we must validate all of the sexpressions that were loaded into the mission.
3624 // Loop through the Sexp_nodes array and send the top level functions to the check_sexp_syntax parser
3626 for (i = 0; i < MAX_SEXP_NODES; i++) {
3627 if ( is_sexp_top_level(i) && (!Fred_running || (i != Sexp_clipboard))) {
3628 int result, bindex, op;
3630 op = identify_operator(CTEXT(i));
3631 SDL_assert(op != -1); // need to make sure it is an operator before we treat it like one..
3632 result = check_sexp_syntax( i, query_operator_return_type(op), 1, &bindex);
3634 // entering this if statement will result in program termination!!!!!
3635 // print out an error based on the return value from check_sexp_syntax()
3637 char sexp_str[8192], text[8192];
3639 convert_sexp_to_string( i, sexp_str, SDL_arraysize(sexp_str), SEXP_ERROR_CHECK_MODE);
3640 SDL_snprintf(text, SDL_arraysize(text), "%s.\n\nIn sexpression: %s\n(Error appears to be: %s)",
3641 sexp_error_message(result), sexp_str, Sexp_nodes[bindex].text);
3644 Error( LOCATION, text );
3646 Warning( LOCATION, text );
3651 ai_post_process_mission();
3655 for (i=0; i<Total_initially_docked; i++) {
3656 z = ship_name_lookup(Initially_docked[i].dockee);
3658 SDL_assert(Initially_docked[i].docker->type == OBJ_SHIP);
3659 p1 = model_find_dock_name_index(Ships[Initially_docked[i].docker->instance].modelnum,
3660 Initially_docked[i].docker_point);
3661 SDL_assert(Objects[z].type == OBJ_SHIP);
3662 p2 = model_find_dock_name_index(Ships[Objects[z].instance].modelnum,
3663 Initially_docked[i].dockee_point);
3665 if ((p1 >= 0) && (p2 >= 0)) {
3666 nprintf(("AI", "Initially Docked: %s with %s\n", Ships[Initially_docked[i].docker->instance].ship_name, Ships[Objects[z].instance].ship_name));
3667 if (ship_docking_valid(Initially_docked[i].docker->instance, Objects[z].instance)) // only dock if they are allowed to be docked.
3668 ai_dock_with_object(Initially_docked[i].docker, &Objects[z], 89, AIDO_DOCK_NOW, p1, p2);
3671 Int3(); // Curious. Two ships told to dock, but one of the dock points is bogus.
3672 // Get Allender or Hoffoss, one of whom probably wrote the above if ()
3677 // we must also count all of the ships of particular types. We count all of the ships that do not have
3678 // their SF_IGNORE_COUNT flag set. We don't count ships in wings when the equivalent wing flag is set.
3679 // in counting ships in wings, we increment the count by the wing's wave count to account for everyone.
3680 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3681 int siflags, num, shipnum;
3683 shipnum = Objects[so->objnum].instance;
3684 // pass over non-ship objects and player ship objects
3685 if ( Ships[shipnum].objnum == -1 || (Objects[Ships[shipnum].objnum].flags & OF_PLAYER_SHIP) )
3687 if ( Ships[shipnum].flags & SF_IGNORE_COUNT )
3689 if ( (Ships[shipnum].wingnum != -1) && (Wings[Ships[shipnum].wingnum].flags & WF_IGNORE_COUNT) )
3692 siflags = Ship_info[Ships[shipnum].ship_info_index].flags;
3694 // determine the number of times we need to add this ship into the count
3695 // if ( Ships[i].wingnum == -1 )
3698 // num = Wings[Ships[i].wingnum].num_waves;
3700 ship_add_ship_type_count( siflags, num );
3702 // now go through the list of ships yet to arrive
3703 for ( p_objp = GET_FIRST(&ship_arrival_list); p_objp != END_OF_LIST(&ship_arrival_list); p_objp = GET_NEXT(p_objp) ) {
3706 // go through similar motions as above
3707 if ( p_objp->flags & P_SF_IGNORE_COUNT )
3709 if ( (p_objp->wingnum != -1) && (Wings[p_objp->wingnum].flags & WF_IGNORE_COUNT) )
3712 siflags = Ship_info[p_objp->ship_class].flags;
3714 if ( p_objp->wingnum == -1 )
3717 num = Wings[p_objp->wingnum].num_waves - 1; // subtract one since we already counted the first wave
3719 ship_add_ship_type_count( siflags, num );
3722 // set player weapons that are selected by default
3723 // AL 09/17/97: I added this code to select the first primary/secondary weapons,
3724 // since I noticed the player ship sometimes doesn't get default weapons selected
3726 // DB: modified 4/23/98 to take multiplayer into account. Under certain circumstances, multiplayer netplayer ships
3727 // had their current_primary_bank and current_secondary_bank set to -1 (from ship_set()) and left there since
3728 // Player_ship is not the only one we need to need about.
3729 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3730 ship *shipp = &Ships[Objects[so->objnum].instance];
3732 // don't process non player wing ships
3733 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
3736 swp = &shipp->weapons;
3738 // swp = &Player_ship->weapons;
3739 if ( swp->num_primary_banks > 0 ) {
3740 swp->current_primary_bank = 0; // currently selected primary bank
3743 if ( swp->num_secondary_banks > 0 ) {
3744 swp->current_secondary_bank = 0; // currently selected secondary bank
3748 ets_init_ship(Player_obj); // init ETS data for the player
3750 // put the timestamp stuff here for now
3751 Mission_arrival_timestamp = timestamp( ARRIVAL_TIMESTAMP );
3752 Mission_departure_timestamp = timestamp( DEPARTURE_TIMESTAMP );
3753 Mission_end_time = -1;
3755 if(Game_mode & GM_MULTIPLAYER){
3756 multi_respawn_build_points();
3759 // maybe reset hotkey defaults when loading new mission
3760 if ( Last_file_checksum != Current_file_checksum ){
3761 mission_hotkey_reset_saved();
3764 Allow_arrival_music_timestamp=timestamp(0);
3765 Allow_arrival_message_timestamp=timestamp(0);
3766 Arrival_message_delay_timestamp = timestamp(-1);
3769 for(idx=0; idx<2; idx++){
3770 Allow_arrival_music_timestamp_m[idx]=timestamp(0);
3771 Allow_arrival_message_timestamp_m[idx]=timestamp(0);
3772 Arrival_message_delay_timestamp_m[idx] = timestamp(-1);
3775 Last_file_checksum = Current_file_checksum;
3778 int get_mission_info(char *filename, mission *mission_p)
3780 // if mission_p is NULL, make it point to The_mission
3781 if ( mission_p == NULL )
3782 mission_p = &The_mission;
3786 // open localization
3789 CFILE *ftemp = cfopen(filename, "rt");
3791 // close localization
3797 // 7/9/98 -- MWA -- check for 0 length file.
3798 filelength = cfilelength(ftemp);
3800 if ( filelength == 0 ){
3801 // close localization
3808 read_file_text(filename, CF_TYPE_MISSIONS);
3809 memset( mission_p, 0, sizeof(mission) );
3811 parse_mission_info(mission_p);
3812 } catch (parse_error_t rval) {
3813 nprintf(("Error", "Error abort! Code = %d", (int)rval));
3817 // close localization
3823 // mai parse routine for parsing a mission. The default parameter flags tells us which information
3824 // to get when parsing the mission. 0 means get everything (default). Other flags just gets us basic
3825 // info such as game type, number of players etc.
3826 int parse_main(const char *mission_name, int flags)
3830 // fill in Ship_class_names array with the names from the ship_info struct;
3831 Num_parse_names = 0;
3832 Mission_all_attack = 0; // Might get set in mission load.
3833 SDL_assert(Num_ship_types < MAX_SHIP_TYPES);
3835 for (i = 0; i < Num_ship_types; i++)
3836 Ship_class_names[i] = Ship_info[i].name;
3839 // open localization
3842 CFILE *ftemp = cfopen(mission_name, "rt", CFILE_NORMAL, CF_TYPE_MISSIONS);
3846 Error( LOCATION, "Couldn't open mission '%s'\n", mission_name );
3848 Current_file_length = -1;
3849 Current_file_checksum = 0;
3851 // close localization
3857 Current_file_length = cfilelength(ftemp);
3861 read_file_text(mission_name, CF_TYPE_MISSIONS);
3862 memset(&The_mission, 0, sizeof(The_mission));
3863 parse_mission(&The_mission, flags);
3864 display_parse_diagnostics();
3865 } catch (parse_error_t rval) {
3866 nprintf(("Error", "Error abort! Code = %i.", (int)rval));
3870 // close localization
3874 SDL_strlcpy(Mission_filename, mission_name, SDL_arraysize(Mission_filename));
3879 // sets the arrival lcoation of the ships in wingp. pass num_to_set since the threshold value
3880 // for wings may have us create more ships in the wing when there are still some remaining
3881 void mission_set_wing_arrival_location( wing *wingp, int num_to_set )
3885 // get the starting index into the ship_index array of the first ship whose location we need set.
3887 index = wingp->current_count - num_to_set;
3888 if ( (wingp->arrival_location == ARRIVE_FROM_DOCK_BAY) || (wingp->arrival_location == ARRIVE_AT_LOCATION) ) {
3889 while ( index < wingp->current_count ) {
3892 objp = &Objects[Ships[wingp->ship_index[index]].objnum];
3893 mission_set_arrival_location(wingp->arrival_anchor, wingp->arrival_location, wingp->arrival_distance, OBJ_INDEX(objp), NULL, NULL);
3898 object *leader_objp;
3903 // wing is not arriving from a docking bay -- possibly move them based on arriving near
3904 // or in front of some other ship.
3905 index = wingp->current_count - num_to_set;
3906 leader_objp = &Objects[Ships[wingp->ship_index[index]].objnum];
3907 if (mission_set_arrival_location(wingp->arrival_anchor, wingp->arrival_location, wingp->arrival_distance, OBJ_INDEX(leader_objp), &pos, &orient)) {
3908 // modify the remaining ships created
3911 while ( index < wingp->current_count ) {
3914 objp = &Objects[Ships[wingp->ship_index[index]].objnum];
3916 // change the position of the next ships in the wing. Use the cool function in AiCode.cpp which
3917 // Mike K wrote to give new positions to the wing members.
3918 get_absolute_wing_pos( &objp->pos, leader_objp, wing_index++, 0);
3919 memcpy( &objp->orient, &orient, sizeof(matrix) );
3926 // create warp effect if in mission and not arriving from docking bay
3927 if ( (Game_mode & GM_IN_MISSION) && (wingp->arrival_location != ARRIVE_FROM_DOCK_BAY) ) {
3928 for ( index = wingp->current_count - num_to_set; index < wingp->current_count; index ++ ) {
3929 shipfx_warpin_start( &Objects[Ships[wingp->ship_index[index]].objnum] );
3934 // this function is called after a mission is parsed to set the arrival locations of all ships in the
3935 // mission to the apprioriate spot. Mainly needed because ships might be in dock bays to start
3936 // the mission, so their AI mode must be set appropriately.
3937 void mission_parse_set_arrival_locations()
3945 obj_merge_created_list();
3946 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
3949 if ( objp->type != OBJ_SHIP )
3952 shipp = &Ships[objp->instance];
3953 // if the ship is in a wing -- ignore the info and let the wing info handle it
3954 if ( shipp->wingnum != -1 )
3957 // call function to set arrival location for this ship.
3958 mission_set_arrival_location( shipp->arrival_anchor, shipp->arrival_location, shipp->arrival_distance, OBJ_INDEX(objp), NULL, NULL);
3962 for ( i = 0; i < num_wings; i++ ) {
3964 // if wing has no ships, then don't process it.
3965 if ( Wings[i].current_count == 0 )
3968 mission_set_wing_arrival_location( &Wings[i], Wings[i].current_count );
3973 // function which iterates through the ship_arrival_list and creates any ship which
3974 // should be intially docked with a ship which currently exists in the mission
3975 void mission_parse_do_initial_docks()
3977 p_object *pobjp, *tmp;
3979 pobjp = GET_FIRST( &ship_arrival_list );
3980 while ( pobjp != END_OF_LIST(&ship_arrival_list) ) {
3983 tmp = GET_NEXT(pobjp);
3985 // see if the flag for initial docked is set
3986 if ( pobjp->flags & P_SF_INITIALLY_DOCKED ) {
3987 // see if who this parse object is supposed to be docked with is in the mission
3988 shipnum = ship_name_lookup( pobjp->docked_with );
3989 if ( shipnum != -1 ) {
3992 // the ship exists, so create this object, then dock the two.
3993 objnum = parse_create_object( pobjp );
3994 SDL_assert ( objnum != -1 );
3996 list_remove( &ship_arrival_list, pobjp);
3998 // p1 is the parse object's docking point.
3999 // p2 is the existing objects docking point.
4000 p1 = model_find_dock_name_index(Ships[shipnum].modelnum, pobjp->docker_point);
4001 p2 = model_find_dock_name_index(Ships[Objects[objnum].instance].modelnum, pobjp->dockee_point);
4003 if ((p1 >= 0) && (p2 >= 0)) {
4004 nprintf(("AI", "Initially Docked: %s with %s\n", Ships[shipnum].ship_name, Ships[Objects[objnum].instance].ship_name));
4005 if (ship_docking_valid(shipnum, Objects[objnum].instance)) // only dock if they are allowed to be docked.
4006 ai_dock_with_object(&Objects[Ships[shipnum].objnum], &Objects[objnum], 89, AIDO_DOCK_NOW, p1, p2);
4008 ai_dock_with_object(&Objects[objnum], &Objects[Ships[shipnum].objnum], 89, AIDO_DOCK_NOW, p2, p1);
4011 Int3(); // Curious. Two ships told to dock, but one of the dock points is bogus.
4012 // Get Allender or Hoffoss, one of whom probably wrote the above if ()
4020 // function which returns true or false if the given mission support multiplayers
4021 int mission_parse_is_multi(const char *filename, char *mission_name)
4027 // new way of getting information. Open the file, and just get the name and the game_type flags.
4028 // return the flags if a multiplayer mission
4032 ftemp = cfopen(filename, "rt");
4036 // 7/9/98 -- MWA -- check for 0 length file.
4037 filelength = cfilelength(ftemp);
4039 if ( filelength == 0 )
4042 // open localization
4046 read_file_text(filename, CF_TYPE_MISSIONS);
4048 if ( skip_to_string("$Name:") != 1 ) {
4049 nprintf(("Network", "Unable to process %s because we couldn't find $Name:", filename));
4051 // close localization
4056 stuff_string( mission_name, F_NAME, NULL );
4057 if ( skip_to_string("+Game Type Flags:") != 1 ) {
4058 nprintf(("Network", "Unable to process %s because we couldn't find +Game Type Flags:\n", filename));
4060 // close localization
4065 stuff_int(&game_type);
4066 } catch (parse_error_t) {
4067 Error(LOCATION, "Bogus! Trying to get multi game type on mission %s returned as a mission from cf_get_filelist\n", filename);
4070 if ( game_type & MISSION_TYPE_MULTI ){
4071 // close localization
4077 // close localization
4083 // function which gets called to retrieve useful information about a mission. We will get the
4084 // name, description, and number of players for a mission. Probably used for multiplayer only?
4085 // The calling function can use the information in The_mission to get the name/description of the mission
4088 int mission_parse_get_multi_mission_info( const char *filename )
4090 if ( parse_main(filename, MISSION_PARSE_MISSION_INFO) ){
4094 SDL_assert( The_mission.game_type & MISSION_TYPE_MULTI ); // assume multiplayer only for now?
4096 // return the number of parse_players. later, we might want to include (optionally?) the number
4097 // of other ships in the main players wing (usually wing 'alpha') for inclusion of number of
4100 return The_mission.num_players;
4103 // returns true or false if this is on the yet to arrive list
4104 int mission_parse_ship_arrived( const char *shipname )
4108 for ( objp = GET_FIRST(&ship_arrival_list); objp !=END_OF_LIST(&ship_arrival_list); objp = GET_NEXT(objp) ) {
4109 if ( !SDL_strcasecmp( objp->name, shipname) )
4110 return 0; // still on the arrival list
4115 // return the parse object on the ship arrival list associated with the given name
4116 p_object *mission_parse_get_arrival_ship( const char *name )
4120 for ( objp = GET_FIRST(&ship_arrival_list); objp !=END_OF_LIST(&ship_arrival_list); objp = GET_NEXT(objp) ) {
4121 if ( !SDL_strcasecmp( objp->name, name) )
4122 return objp; // still on the arrival list
4128 // return the parse object on the ship arrival list associated with the given signature
4129 p_object *mission_parse_get_arrival_ship( ushort net_signature )
4133 for ( objp = GET_FIRST(&ship_arrival_list); objp !=END_OF_LIST(&ship_arrival_list); objp = GET_NEXT(objp) ) {
4134 if ( objp->net_signature == net_signature )
4135 return objp; // still on the arrival list
4141 // mission_set_arrival_location() sets the arrival location of a parse object according to the arrival location
4142 // of the object. Returns true if object set to new position, false if not.
4143 int mission_set_arrival_location(int anchor, int location, int dist, int objnum, vector *new_pos, matrix *new_orient)
4145 int shipnum, anchor_objnum;
4146 vector anchor_pos, rand_vec, new_fvec;
4149 if ( location == ARRIVE_AT_LOCATION )
4152 SDL_assert(anchor >= 0);
4154 // this ship might possibly arrive at another location. The location is based on the
4155 // proximity of some ship (and some other special tokens)
4157 // if we didn't find the arrival anchor in the list of special nodes, then do a
4158 // ship name lookup on the anchor
4159 if (anchor < SPECIAL_ARRIVAL_ANCHORS_OFFSET) {
4160 shipnum = ship_name_lookup(Parse_names[anchor]);
4161 if ( shipnum == -1 ) {
4162 SDL_assert ( location != ARRIVE_FROM_DOCK_BAY ); // bogus data somewhere!!! get mwa
4163 nprintf (("allender", "couldn't find ship for arrival anchor -- using location ship created at"));
4168 // come up with a position based on the special token names
4171 if (anchor == ANY_FRIENDLY) {
4172 shipnum = ship_get_random_team_ship( TEAM_FRIENDLY, SHIP_GET_ANY_SHIP );
4173 } else if (anchor == ANY_HOSTILE) {
4174 shipnum = ship_get_random_team_ship( opposing_team_mask(Player_ship->team), SHIP_GET_ANY_SHIP );
4175 } else if (anchor == ANY_FRIENDLY_PLAYER) {
4176 shipnum = ship_get_random_team_ship( TEAM_FRIENDLY, SHIP_GET_ONLY_PLAYERS );
4177 } else if (anchor == ANY_HOSTILE_PLAYER) {
4178 shipnum = ship_get_random_team_ship( opposing_team_mask(Player_ship->team), SHIP_GET_ONLY_PLAYERS );
4180 Int3(); // get allender -- unknown special arrival instructions
4182 // if we didn't get an object from one of the above functions, then make the object
4183 // arrive at it's placed location
4184 if ( shipnum == -1 ) {
4185 nprintf (("Allender", "Couldn't find random ship for arrival anchor -- using default location\n"));
4190 // take the shipnum and get the position. once we have positions, we can determine where
4191 // to make this ship appear
4192 SDL_assert ( shipnum != -1 );
4193 anchor_objnum = Ships[shipnum].objnum;
4194 anchor_pos = Objects[anchor_objnum].pos;
4196 // if arriving from docking bay, then set ai mode and call function as per AL's instructions.
4197 if ( location == ARRIVE_FROM_DOCK_BAY ) {
4200 // if we get an error, just let the ship arrive(?)
4201 if ( ai_acquire_emerge_path(&Objects[objnum], anchor_objnum, &pos, &fvec) == -1 ) {
4202 Int3(); // get MWA or AL -- not sure what to do here when we cannot acquire a path
4205 Objects[objnum].pos = pos;
4206 Objects[objnum].orient.v.fvec = fvec;
4209 // AL: ensure dist > 0 (otherwise get errors in vecmat)
4210 // TODO: maybe set distance to 2x ship radius of ship appearing in front of?
4212 Error(LOCATION, "Distance of %d is invalid in mission_set_arrival_location\n", dist);
4216 // get a vector which is the ships arrival position based on the type of arrival
4217 // this ship should have. Arriving near a ship we use a random normalized vector
4218 // scaled by the distance given by the designer. Arriving in front of a ship means
4219 // entering the battle in the view cone.
4220 if ( location == ARRIVE_NEAR_SHIP ) {
4221 // get a random vector -- use static randvec if in multiplayer
4222 if ( Game_mode & GM_NORMAL )
4223 vm_vec_rand_vec_quick(&rand_vec);
4225 static_randvec( Objects[objnum].net_signature, &rand_vec );
4226 } else if ( location == ARRIVE_IN_FRONT_OF_SHIP ) {
4231 // cool function by MK to give a reasonable random vector "in front" of a ship
4232 // rvec and uvec are the right and up vectors.
4233 // If these are not available, this would be an expensive method.
4235 x = (float)cos(ANG_TO_RAD(45));
4236 if ( Game_mode & GM_NORMAL ) {
4237 r1 = myrand() < MY_RAND_MAX/2 ? -1 : 1;
4238 r2 = myrand() < MY_RAND_MAX/2 ? -1 : 1;
4240 // in multiplayer, use the static rand functions so that all clients can get the
4241 // same information.
4242 r1 = static_rand(Objects[objnum].net_signature) < MY_RAND_MAX/2 ? -1 : 1;
4243 r2 = static_rand(Objects[objnum].net_signature+1) < MY_RAND_MAX/2 ? -1 : 1;
4246 vm_vec_copy_scale(&t1, &(Objects[anchor_objnum].orient.v.fvec), x);
4247 vm_vec_copy_scale(&t2, &(Objects[anchor_objnum].orient.v.rvec), (1.0f - x) * r1);
4248 vm_vec_copy_scale(&t3, &(Objects[anchor_objnum].orient.v.uvec), (1.0f - x) * r2);
4250 vm_vec_add(&rand_vec, &t1, &t2);
4251 vm_vec_add2(&rand_vec, &t3);
4252 vm_vec_normalize(&rand_vec);
4255 // add in the radius of the two ships involved. This will make the ship arrive further than
4256 // specified, but will appear more accurate since we are pushing the edge of the model to the
4257 // specified distance. large objects appears to be a lot closer without the following line because
4258 // the object centers were at the correct distance, but the model itself was much closer to the
4260 dist += (int)Objects[objnum].radius + (int)Objects[anchor_objnum].radius;
4261 vm_vec_scale_add(&Objects[objnum].pos, &anchor_pos, &rand_vec, (float)dist);
4263 // I think that we will always want to orient the ship that is arriving to face towards
4264 // the ship it is arriving near/in front of. The effect will be cool!
4266 // calculate the new fvec of the ship arriving and use only that to get the matrix. isn't a big
4267 // deal not getting bank.
4268 vm_vec_sub(&new_fvec, &anchor_pos, &Objects[objnum].pos );
4269 vm_vector_2_matrix( &orient, &new_fvec, NULL, NULL );
4270 Objects[objnum].orient = orient;
4273 // set the new_pos parameter since it might be used outside the function (i.e. when dealing with wings).
4275 memcpy(new_pos, &Objects[objnum].pos, sizeof(vector) );
4278 memcpy( new_orient, &Objects[objnum].orient, sizeof(matrix) );
4283 // mark a reinforcement as available
4284 void mission_parse_mark_reinforcement_available(char *name)
4289 for (i = 0; i < Num_reinforcements; i++) {
4290 rp = &Reinforcements[i];
4291 if ( !SDL_strcasecmp(rp->name, name) ) {
4292 if ( !(rp->flags & RF_IS_AVAILABLE) ) {
4293 rp->flags |= RF_IS_AVAILABLE;
4295 // tell all of the clients.
4296 if ( MULTIPLAYER_MASTER ) {
4297 send_reinforcement_avail( i );
4304 SDL_assert ( i < Num_reinforcements );
4307 // mission_did_ship_arrive takes a parse object and checked the arrival cue and delay and
4308 // creates the object if necessary. Returns -1 if not created. objnum of created ship otherwise
4309 int mission_did_ship_arrive(p_object *objp)
4313 // find out in the arrival cue became true
4314 did_arrive = eval_sexp(objp->arrival_cue);
4316 // we must first check to see if this ship is a reinforcement or not. If so, then don't
4318 if ( objp->flags & P_SF_REINFORCEMENT ) {
4320 // if this ship did arrive, mark the reinforcement as available, and tell clients if in multiplayer
4323 mission_parse_mark_reinforcement_available(objp->name);
4328 if ( did_arrive ) { // has the arrival criteria been met?
4331 SDL_assert ( !(objp->flags & P_SF_CANNOT_ARRIVE) ); // get allender
4333 // check to see if the delay field <= 0. if so, then create a timestamp and then maybe
4334 // create the object
4335 if ( objp->arrival_delay <= 0 ) {
4336 objp->arrival_delay = timestamp( -objp->arrival_delay * 1000 );
4337 SDL_assert( objp->arrival_delay >= 0 );
4340 // if the timestamp hasn't elapsed, move onto the next ship.
4341 if ( !timestamp_elapsed(objp->arrival_delay) )
4344 // check to see if this ship is to arrive via a docking bay. If so, and the ship to arrive from
4345 // doesn't exist, don't create.
4346 if ( objp->arrival_location == ARRIVE_FROM_DOCK_BAY ) {
4350 SDL_assert( objp->arrival_anchor >= 0 );
4351 name = Parse_names[objp->arrival_anchor];
4353 // see if ship is yet to arrive. If so, then return -1 so we can evaluate again later.
4354 if ( mission_parse_get_arrival_ship( name ) )
4357 // see if ship is in mission. If not, then we can assume it was destroyed or departed since
4358 // it is not on the arrival list (as shown by above if statement).
4359 shipnum = ship_name_lookup( name );
4360 if ( shipnum == -1 ) {
4361 Sexp_nodes[objp->arrival_cue].value = SEXP_KNOWN_FALSE;
4366 object_num = parse_create_object(objp); // create the ship
4368 // since this ship is not in a wing, create a SHIP_ARRIVE entry
4369 //mission_log_add_entry( LOG_SHIP_ARRIVE, objp->name, NULL );
4370 SDL_assert(object_num >= 0 && object_num < MAX_OBJECTS);
4372 // Play the music track for an arrival
4373 if ( !(Ships[Objects[object_num].instance].flags & SF_NO_ARRIVAL_MUSIC) )
4374 if ( timestamp_elapsed(Allow_arrival_music_timestamp) ) {
4375 Allow_arrival_music_timestamp = timestamp(ARRIVAL_MUSIC_MIN_SEPARATION);
4376 event_music_arrival(Ships[Objects[object_num].instance].team);
4380 // check to see if the arrival cue of this ship is known false -- if so, then remove
4381 // the parse object from the ship
4382 if ( Sexp_nodes[objp->arrival_cue].value == SEXP_KNOWN_FALSE )
4383 objp->flags |= P_SF_CANNOT_ARRIVE;
4390 // funciton to set a flag on all parse objects on ship arrival list which cannot
4391 // arrive in the mission
4392 void mission_parse_mark_non_arrivals()
4396 for ( pobjp = GET_FIRST(&ship_arrival_list); pobjp != END_OF_LIST(&ship_arrival_list); pobjp = GET_NEXT(pobjp) ) {
4397 if ( pobjp->wingnum != -1 ) {
4398 if ( Sexp_nodes[Wings[pobjp->wingnum].arrival_cue].value == SEXP_KNOWN_FALSE )
4399 pobjp->flags |= P_SF_CANNOT_ARRIVE;
4401 if ( Sexp_nodes[pobjp->arrival_cue].value == SEXP_KNOWN_FALSE )
4402 pobjp->flags |= P_SF_CANNOT_ARRIVE;
4407 // function to deal with support ship arrival. objnum is the object number of the arriving support
4408 // ship. This function can get called from either single or multiplayer. Needed to that clients
4409 // can know when to abort rearm.
4410 void mission_parse_support_arrived( int objnum )
4414 // when the support ship arrives, the shipname it is supposed to repair is in the 'misc'
4415 // field of the parse_object. If the ship still exists, call ai function which actually
4416 // issues the goal for the repair
4417 for ( i = 0; i < Num_arriving_repair_targets; i++ ) {
4420 shipnum = ship_name_lookup( Arriving_repair_targets[i] );
4422 if ( shipnum != -1 ) {
4423 object *requester_objp, *support_objp;
4425 support_objp = &Objects[objnum];
4426 requester_objp = &Objects[Ships[shipnum].objnum];
4427 ai_add_rearm_goal( requester_objp, support_objp );
4431 // MK: A bit of a hack. If on player's team and player isn't allowed shields, don't give this ship shields.
4432 if ((Player_obj->flags & OF_NO_SHIELDS) && (Player_ship->team == Ships[Objects[objnum].instance].team))
4433 Objects[objnum].flags |= OF_NO_SHIELDS;
4435 Ships[Objects[objnum].instance].flags |= SF_WARPED_SUPPORT;
4437 Arriving_support_ship = NULL;
4438 Num_arriving_repair_targets = 0;
4441 MONITOR(NumShipArrivals);
4443 // mission_parse_arrivals will parse the lists of arriving ships and
4444 // wings -- creating new ships/wings if the arrival criteria have been
4446 void mission_eval_arrivals()
4452 // before checking arrivals, check to see if we should play a message concerning arrivals
4453 // of other wings. We use the timestamps to delay the arrival message slightly for
4455 if ( timestamp_valid(Arrival_message_delay_timestamp) && timestamp_elapsed(Arrival_message_delay_timestamp) && !((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ){
4456 int rship, use_terran;
4458 // use terran command 25% of time
4459 use_terran = ((frand() - 0.75) > 0.0f)?1:0;
4461 rship = ship_get_random_player_wing_ship( SHIP_GET_NO_PLAYERS );
4462 if ( (rship == -1) || use_terran ){
4463 message_send_builtin_to_player( MESSAGE_ARRIVE_ENEMY, NULL, MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4464 } else if ( rship != -1 ) {
4465 message_send_builtin_to_player( MESSAGE_ARRIVE_ENEMY, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4468 Arrival_message_delay_timestamp = timestamp(-1); // make the stamp invalid
4471 // if ( !timestamp_elapsed(Mission_arrival_timestamp) )
4474 // check the ship_arrival_list
4475 objp = GET_FIRST(&ship_arrival_list);
4476 while( objp !=END_OF_LIST(&ship_arrival_list) ) {
4477 p_object *temp = GET_NEXT(objp);
4478 if ( objp->wingnum == -1 ) { // if this object has a wing -- let code for wings determine if it should be created
4480 objnum = mission_did_ship_arrive( objp );
4481 if ( objnum != -1 ) {
4482 list_remove( &ship_arrival_list, objp);
4483 MONITOR_INC(NumShipArrivals,1);
4490 // check for any initially docked ships. Do it after all are created since the next function
4491 // messes with the ship_arrival_list
4492 mission_parse_do_initial_docks(); // maybe create it's docked counterpart
4494 mission_parse_mark_non_arrivals(); // mark parse objects which can no longer arrive
4496 // check the support ship arrival list
4497 if ( Arriving_support_ship ) {
4498 objnum = mission_did_ship_arrive( Arriving_support_ship );
4500 if ( objnum != -1 ) {
4501 MONITOR_INC(NumShipArrivals,1);
4502 mission_parse_support_arrived( objnum );
4506 // we must also check to see if there are waves of a wing that must
4507 // reappear if all the ships of the current wing have been destroyed or
4508 // have departed. If this is the case, then create the next wave.
4510 for ( i = 0; i < num_wings; i++ ) {
4513 // should we process this wing anymore
4514 if ( wingp->flags & WF_WING_GONE )
4517 // if we have a reinforcement wing, then don't try to create new ships automatically.
4518 if ( wingp->flags & WF_REINFORCEMENT ) {
4520 // check to see in the wings arrival cue is true, and if so, then mark the reinforcement
4522 if ( eval_sexp(wingp->arrival_cue) ) {
4523 mission_parse_mark_reinforcement_available(wingp->name);
4528 // don't do evaluations for departing wings
4529 if ( wingp->flags & WF_WING_DEPARTING ){
4533 // must check to see if we are at the last wave. Code above to determine when a wing is gone only
4534 // gets run when a ship is destroyed (not every N seconds like it used to). Do a quick check
4536 if ( wingp->current_wave == wingp->num_waves ){
4540 // if the current wave of this wing is 0, then we haven't created the ships in the wing yet.
4541 // call parse_wing_create_ships to try and create it. That function will eval the arrival
4542 // cue of the wing and create the ships if necessary, or if the threshold of the wing has
4543 // been reached, then try and create more ships
4544 if ( (wingp->current_wave == 0) || (wingp->current_count <= wingp->threshold) ) {
4547 created = parse_wing_create_ships( wingp, wingp->wave_count );
4549 // if we created ships in this wing, check to see if the wings was int the reinforcements
4550 // array. If so, then if we have more uses, then reset the reinforcement flag for the wing
4551 // so the user can call in another set if need be.
4552 if ( created > 0 ) {
4555 mission_parse_do_initial_docks(); // maybe create other initially docked ships
4556 if ( Wings[i].flags & WF_RESET_REINFORCEMENT ) {
4557 Wings[i].flags &= ~WF_RESET_REINFORCEMENT;
4558 Wings[i].flags |= WF_REINFORCEMENT;
4561 // possibly send a message to the player when this wing arrives.
4562 if ( wingp->flags & WF_NO_ARRIVAL_MESSAGE ){
4566 // multiplayer team vs. team
4567 if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){
4568 // send a hostile wing arrived message
4569 rship = Wings[i].ship_index[0];
4571 int multi_team_filter = Ships[rship].team == TEAM_FRIENDLY ? 1 : 0;
4573 // there are two timestamps at work here. One to control how often the player receives
4574 // messages about incoming hostile waves, and the other to control how long after
4575 // the wing arrives does the player actually get the message.
4576 if ( timestamp_elapsed(Allow_arrival_message_timestamp_m[multi_team_filter]) ) {
4577 if ( !timestamp_valid(Arrival_message_delay_timestamp_m[multi_team_filter]) ){
4578 Arrival_message_delay_timestamp_m[multi_team_filter] = timestamp_rand(ARRIVAL_MESSAGE_DELAY_MIN, ARRIVAL_MESSAGE_DELAY_MAX );
4580 Allow_arrival_message_timestamp_m[multi_team_filter] = timestamp(ARRIVAL_MESSAGE_MIN_SEPARATION);
4582 // send to the proper team
4583 message_send_builtin_to_player( MESSAGE_ARRIVE_ENEMY, NULL, MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, multi_team_filter );
4588 // see if this is a starting player wing
4589 if ( i == Starting_wings[STARTING_WING_BETA] ) { // this is the beta wing
4590 rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS );
4592 message_send_builtin_to_player( MESSAGE_BETA_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4594 } else if ( i == Starting_wings[STARTING_WING_GAMMA] ) { // this is the gamma wing
4595 rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS );
4596 if ( rship != -1 ) {
4597 message_send_builtin_to_player( MESSAGE_GAMMA_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4599 } else if ( !SDL_strcasecmp( wingp->name, "delta") ) {
4600 rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS );
4601 if ( rship != -1 ) {
4602 message_send_builtin_to_player( MESSAGE_DELTA_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4604 } else if ( !SDL_strcasecmp(wingp->name, "epsilon") ) {
4605 rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS );
4606 if ( rship != -1 ) {
4607 message_send_builtin_to_player( MESSAGE_EPSILON_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4610 // see if we have a hostile wing that arrived
4611 rship = Wings[i].ship_index[0];
4612 if ( Ships[rship].team != TEAM_FRIENDLY ) {
4614 // there are two timestamps at work here. One to control how often the player receives
4615 // messages about incoming hostile waves, and the other to control how long after
4616 // the wing arrives does the player actually get the message.
4617 if ( timestamp_elapsed(Allow_arrival_message_timestamp) ) {
4618 if ( !timestamp_valid(Arrival_message_delay_timestamp) ){
4619 Arrival_message_delay_timestamp = timestamp_rand(ARRIVAL_MESSAGE_DELAY_MIN, ARRIVAL_MESSAGE_DELAY_MAX );
4621 Allow_arrival_message_timestamp = timestamp(ARRIVAL_MESSAGE_MIN_SEPARATION);
4629 Mission_arrival_timestamp = timestamp(ARRIVAL_TIMESTAMP);
4632 MONITOR(NumShipDepartures);
4634 // called to make object objp depart.
4635 void mission_do_departure( object *objp )
4640 MONITOR_INC(NumShipDepartures,1);
4642 SDL_assert ( objp->type == OBJ_SHIP );
4643 shipp = &Ships[objp->instance];
4645 // if departing to a docking bay, try to find the anchor ship to depart to. If not found, then
4646 // just make it warp out like anything else.
4647 if ( shipp->departure_location == DEPART_AT_DOCK_BAY ) {
4651 SDL_assert( shipp->departure_anchor >= 0 );
4652 name = Parse_names[shipp->departure_anchor];
4654 // see if ship is yet to arrive. If so, then return -1 so we can evaluate again later.
4655 if ( mission_parse_get_arrival_ship( name ) )
4656 goto do_departure_warp;
4658 // see if ship is in mission. If not, then we can assume it was destroyed or departed since
4659 // it is not on the arrival list (as shown by above if statement).
4660 anchor_shipnum = ship_name_lookup( name );
4661 if ( anchor_shipnum == -1 )
4662 goto do_departure_warp;
4664 ai_acquire_depart_path(objp, Ships[anchor_shipnum].objnum);
4669 ai_set_mode_warp_out( objp, &Ai_info[Ships[objp->instance].ai_index] );
4673 // put here because mission_eval_arrivals is here. Might move these to a better location
4675 void mission_eval_departures()
4681 // if ( !timestamp_elapsed(Mission_departure_timestamp) )
4684 // scan through the active ships an evaluate their departure cues. For those
4685 // ships whose time has come, set their departing flag.
4687 for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4688 if (objp->type == OBJ_SHIP) {
4691 SDL_assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
4693 shipp = &Ships[objp->instance];
4695 // don't process a ship that is already departing or dying or disabled
4696 // AL 12-30-97: Added SF_CANNOT_WARP to check
4697 if ( (shipp->flags & (SF_DEPARTING | SF_DYING | SF_CANNOT_WARP )) || ship_subsys_disrupted(shipp, SUBSYSTEM_ENGINE) ) {
4701 // don't process ships that are part of a wing -- handled in seperate case
4702 if ( shipp->wingnum != -1 )
4705 // && (!timestamp_valid(shipp->departure_delay) || timestamp_elapsed(shipp->departure_delay)) )
4706 // when the departure cue becomes true, set off the departure delay timer. We store the
4707 // timer as -seconds in Freespace which indicates that the timer has not been set. If the timer
4708 // is not set, then turn it into a valid timer and keep evaluating the timer until it is elapsed
4709 if ( eval_sexp(shipp->departure_cue) ) {
4710 if ( shipp->departure_delay <= 0 )
4711 shipp->departure_delay = timestamp(-shipp->departure_delay * 1000 );
4712 if ( timestamp_elapsed(shipp->departure_delay) )
4713 mission_do_departure( objp );
4718 // now scan through the list of wings and check their departure cues. For wings with
4719 // that cue being true, we must update internal variables to indicate that the wing is
4720 // departed and that no further waves of this wing will appear
4722 for ( i = 0; i < num_wings; i++ ) {
4725 // should we process this wing anymore
4726 if ( wingp->flags & WF_WING_DEPARTING )
4729 // evaluate the sexpression. If true, mark all the ships in this wing as departing and increment
4730 // the num departed in the wing structure. Then add number of remaining waves * ships/wave to
4731 // departed count to get total count of ships in the wing which departed. (We are counting ships
4732 // that have not yet arrived as departed if they never arrive -- this may be bad, but for some reason
4733 // seems like the right thing to do).
4734 //&& (!timestamp_valid(wingp->departure_delay) || timestamp_elapsed(wingp->departure_delay)) ) {
4736 if ( eval_sexp(wingp->departure_cue) ) {
4737 // if we haven't set up the departure timer yet (would be <= 0) setup the timer to pop N seconds
4739 if ( wingp->departure_delay <= 0 )
4740 wingp->departure_delay = timestamp( -wingp->departure_delay * 1000 );
4741 if ( !timestamp_elapsed(wingp->departure_delay) )
4744 wingp->flags |= WF_WING_DEPARTING;
4745 for ( j = 0; j < wingp->current_count; j++ ) {
4748 shipp = &Ships[wingp->ship_index[j]];
4749 if ( (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_DYING) )
4752 // shipp->flags |= SF_DEPARTING;
4753 // shipp->final_depart_time = timestamp(3*1000);
4755 SDL_assert ( shipp->objnum != -1 );
4756 objp = &Objects[shipp->objnum];
4758 // copy the wing's depature information to the ship
4759 shipp->departure_location = wingp->departure_location;
4760 shipp->departure_anchor = wingp->departure_anchor;
4762 mission_do_departure( objp );
4763 // don't add to wingp->total_departed here -- this is taken care of in ship code.
4766 // MWA 2/25/98 -- don't do the follwoing wing member updates. It makes the accurate counts
4767 // sort of messed up and causes problems for the event log. The code in ship_wing_cleanup()
4768 // now keys off of the WF_WING_DEPARTING flag instead of the counts below.
4771 // now be sure that we update wing structure members if there are any remaining waves left
4772 if ( wingp->current_wave < wingp->num_waves ) {
4775 num_remaining = ( (wingp->num_waves - wingp->current_wave) * wingp->wave_count);
4776 wingp->total_departed += num_remaining;
4777 wingp->total_arrived_count += num_remaining;
4778 wingp->current_wave = wingp->num_waves;
4784 Mission_departure_timestamp = timestamp(DEPARTURE_TIMESTAMP);
4787 // function called from high level game loop to do mission evaluation stuff
4788 void mission_parse_eval_stuff()
4790 mission_eval_arrivals();
4791 mission_eval_departures();
4794 int allocate_subsys_status()
4798 SDL_assert(Subsys_index < MAX_SUBSYS_STATUS);
4799 Subsys_status[Subsys_index].percent = 0.0f;
4800 Subsys_status[Subsys_index].primary_banks[0] = SUBSYS_STATUS_NO_CHANGE;
4801 for (i=1; i<MAX_PRIMARY_BANKS; i++)
4802 Subsys_status[Subsys_index].primary_banks[i] = -1; // none
4804 Subsys_status[Subsys_index].secondary_banks[0] = SUBSYS_STATUS_NO_CHANGE;
4805 Subsys_status[Subsys_index].secondary_ammo[0] = 100;
4806 for (i=1; i<MAX_SECONDARY_BANKS; i++) {
4807 Subsys_status[Subsys_index].secondary_banks[i] = -1;
4808 Subsys_status[Subsys_index].secondary_ammo[i] = 100;
4811 Subsys_status[Subsys_index].ai_class = SUBSYS_STATUS_NO_CHANGE;
4812 return Subsys_index++;
4815 // find (or add) the name in the list and return an index to it.
4816 int get_parse_name_index(const char *name)
4820 for (i=0; i<Num_parse_names; i++)
4821 if (!SDL_strcasecmp(name, Parse_names[i]))
4824 SDL_assert(i < MAX_SHIPS + MAX_WINGS);
4825 SDL_assert(strlen(name) < NAME_LENGTH);
4826 SDL_strlcpy(Parse_names[i], name, NAME_LENGTH);
4827 return Num_parse_names++;
4830 int get_anchor(char *name)
4834 for (i=0; i<MAX_SPECIAL_ARRIVAL_ANCHORS; i++)
4835 if (!SDL_strcasecmp(name, Special_arrival_anchor_names[i]))
4836 return SPECIAL_ARRIVAL_ANCHORS_OFFSET + i;
4838 return get_parse_name_index(name);
4841 // function to fixup the goals/ai references for player objects in the mission
4842 void mission_parse_fixup_players()
4846 // merge created list to have all objects on used list
4847 obj_merge_created_list();
4848 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4849 if ( (objp->type == OBJ_SHIP) && (objp->flags & OF_PLAYER_SHIP) ) {
4850 ai_clear_ship_goals( &Ai_info[Ships[objp->instance].ai_index] );
4851 init_ai_object( OBJ_INDEX(objp) );
4856 // code to warp in a new support ship. It works by finding the average position of all ships
4857 // in the mission, creating a vector from that position to the player, and scaling out behind the
4858 // player some distance. Should be sufficient.
4860 #define WARP_IN_MIN_DISTANCE 1000.0f
4861 #define WARP_IN_TIME_MIN 3000 // warps in min 3 seconds later
4862 #define WARP_IN_TIME_MAX 6000 // warps in max 6 seconds later
4864 // function which adds requester_objp onto the queue of ships for the arriving support ship to service
4865 void mission_add_to_arriving_support( object *requester_objp )
4870 SDL_assert ( Arriving_support_ship );
4872 if ( Num_arriving_repair_targets == MAX_AI_GOALS ) {
4873 // Int3(); // get allender -- ship isn't going to get repair, but I hope they never queue up this far!!!
4874 mprintf(("Reached MAX_AI_GOALS trying to add repair request!\n"));
4878 shipp = &Ships[requester_objp->instance];
4879 // check for duplicates before adding
4880 for (i = 0; i < Num_arriving_repair_targets; i++ ) {
4881 if ( !SDL_strcasecmp(Arriving_repair_targets[i], shipp->ship_name) ){
4885 if ( i != Num_arriving_repair_targets ){ // found the ship before reaching the end -- ignore it!
4889 SDL_strlcpy( Arriving_repair_targets[Num_arriving_repair_targets], Ships[requester_objp->instance].ship_name, NAME_LENGTH );
4890 Num_arriving_repair_targets++;
4892 if ( MULTIPLAYER_MASTER ){
4893 multi_maybe_send_repair_info( requester_objp, NULL, REPAIR_INFO_WARP_ADD );
4897 extern int pp_collide_any(vector *curpos, vector *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag);
4899 // Set the warp in position for a support ship relative to an object.
4900 // Caller tries several positions, passing vector in x, y, z.
4901 int get_warp_in_pos(vector *pos, object *objp, float x, float y, float z)
4905 if ( Game_mode & GM_NORMAL )
4908 rand_val = static_randf(objp->net_signature);
4910 rand_val = 1.0f + (rand_val - 0.5f)*0.2f;
4914 vm_vec_scale_add2( pos, &objp->orient.v.rvec, x*rand_val*800.0f);
4915 vm_vec_scale_add2( pos, &objp->orient.v.uvec, y*rand_val*800.0f);
4916 vm_vec_scale_add2( pos, &objp->orient.v.fvec, z*rand_val*800.0f);
4918 return pp_collide_any(&objp->pos, pos, objp->radius, objp, NULL, 1);
4921 void mission_warp_in_support_ship( object *requester_objp )
4923 vector center, warp_in_pos;
4926 int i, requester_species;
4927 ship *requester_shipp;
4929 SDL_assert ( requester_objp->type == OBJ_SHIP );
4930 requester_shipp = &Ships[requester_objp->instance]; // MK, 10/23/97, used to be ->type, bogus, no?
4932 // if the support ship is already arriving, add the requester to the list
4933 if ( Arriving_support_ship ) {
4934 mission_add_to_arriving_support( requester_objp );
4938 // get average position of all ships
4939 obj_get_average_ship_pos( ¢er );
4940 vm_vec_sub( &warp_in_pos, ¢er, &(requester_objp->pos) );
4942 // be sure to account for case as player being only ship left in mission
4944 if ( !(IS_VEC_NULL( warp_in_pos)) ) {
4945 mag = vm_vec_mag( &warp_in_pos );
4946 if ( mag < WARP_IN_MIN_DISTANCE )
4947 vm_vec_scale( &warp_in_pos, WARP_IN_MIN_DISTANCE/mag);
4951 // take -player_pos.fvec scaled by 1000.0f;
4952 warp_in_pos = Player_obj->orient.fvec;
4953 vm_vec_scale( &warp_in_pos, -1000.0f );
4957 // Choose position to warp in ship.
4958 // Temporary, but changed by MK because it used to be exactly behind the player.
4959 // This could cause an SDL_assert if the player immediately targeted it (before moving).
4960 // Tend to put in front of the player to aid him in flying towards the ship.
4962 if (!get_warp_in_pos(&warp_in_pos, requester_objp, 1.0f, 0.1f, 1.0f))
4963 if (!get_warp_in_pos(&warp_in_pos, requester_objp, 1.0f, 0.2f, -1.0f))
4964 if (!get_warp_in_pos(&warp_in_pos, requester_objp, -1.0f, -0.2f, -1.0f))
4965 if (!get_warp_in_pos(&warp_in_pos, requester_objp, -1.0f, -0.1f, 1.0f))
4966 get_warp_in_pos(&warp_in_pos, requester_objp, 0.1f, 1.0f, 0.2f);
4968 // create a parse object, and put it onto the ship_arrival_list. This whole thing kind of sucks.
4969 // I want to put it into a parse object since it needs to arrive just a little later than
4970 // this function is called. I have to make some assumptions in the code about values for the parse
4971 // object since I'm no longer working with a mission file. These exceptions will be noted with
4974 Arriving_support_ship = &Support_ship_pobj;
4975 pobj = Arriving_support_ship;
4977 // create a name for the ship. use "Support #". look for collisions until one isn't found anymore
4980 SDL_snprintf(pobj->name, SDL_arraysize(pobj->name), NOX("Support %d"), i);
4981 if ( (ship_name_lookup(pobj->name) == -1) && (ship_find_exited_ship_by_name(pobj->name) == -1) )
4986 pobj->pos = warp_in_pos;
4987 vm_set_identity( &(pobj->orient) );
4989 // *sigh*. Gotta get the ship class. For now, this will amount to finding a ship in the ship_info
4990 // array with the same team as the requester of type SIF_SUPPORT. Might need to be changed, but who knows
4991 // vasudans use the terran support ship.
4992 requester_species = Ship_info[requester_shipp->ship_info_index].species;
4994 // 5/6/98 -- MWA Don't need to do anything for multiplayer. I think that we always want to use
4995 // the species of the caller ship.
4996 SDL_assert( (requester_species == SPECIES_TERRAN) || (requester_species == SPECIES_VASUDAN) );
4997 // if ( (Game_mode & GM_NORMAL) && (requester_species == SPECIES_VASUDAN) ) { // make vasundan's use the terran support ship
4998 // requester_species = SPECIES_TERRAN;
5001 // get index of correct species support ship
5002 for (i=0; i < Num_ship_types; i++) {
5003 if ( (Ship_info[i].species == requester_species) && (Ship_info[i].flags & SIF_SUPPORT) )
5007 if ( i < Num_ship_types )
5008 pobj->ship_class = i;
5010 Int3(); // BOGUS!!!! gotta figure something out here
5012 pobj->team = requester_shipp->team;
5014 pobj->behavior = AIM_NONE; // ASSUMPTION: the mission file has the string "None" which maps to AIM_NONE
5016 // set the ai_goals to -1. We will put the requester object shipname in repair target array and then take
5017 // care of setting up the goal when creating the ship!!!!
5018 pobj->ai_goals = -1;
5019 Num_arriving_repair_targets = 0;
5020 mission_add_to_arriving_support( requester_objp );
5022 // need to set ship's cargo to nothing. scan the cargo_names array looking for the string nothing.
5023 // add it if not found
5024 for (i = 0; i < Num_cargo; i++ )
5025 if ( !SDL_strcasecmp(Cargo_names[i], NOX("nothing")) )
5028 if ( i == Num_cargo ) {
5029 SDL_strlcpy(Cargo_names[i], NOX("Nothing"), NAME_LENGTH);
5032 pobj->cargo1 = char(i);
5034 pobj->status_count = 0;
5036 pobj->arrival_location = 0; // ASSUMPTION: this is index to arrival_lcation string array for hyperspace!!!!
5037 pobj->arrival_distance = 0;
5038 pobj->arrival_anchor = -1;
5039 pobj->arrival_cue = Locked_sexp_true;
5040 pobj->arrival_delay = timestamp_rand(WARP_IN_TIME_MIN, WARP_IN_TIME_MAX);
5042 pobj->subsys_count = 0; // number of elements used in subsys_status array
5043 pobj->initial_velocity = 100; // start at 100% velocity
5044 pobj->initial_hull = 100; // start at 100% hull
5045 pobj->initial_shields = 100; // and 100% shields
5047 pobj->departure_location = 0; // ASSUMPTION: this is index to departure_lcation string array for hyperspace!!!!
5048 pobj->departure_anchor = -1;
5049 pobj->departure_cue = Locked_sexp_false;
5050 pobj->departure_delay= 0;
5052 pobj->determination = 10; // ASSUMPTION: mission file always had this number written out
5054 if ( Player_obj->flags & P_OF_NO_SHIELDS )
5055 pobj->flags = P_OF_NO_SHIELDS; // support ships have no shields when player has not shields
5057 pobj->ai_class = Ship_info[pobj->ship_class].ai_class;
5061 pobj->docked_with[0] = '\0';
5063 pobj->persona_index = -1;
5064 pobj->net_signature = multi_assign_network_signature(MULTI_SIG_SHIP);
5065 pobj->wing_status_wing_index = -1;
5066 pobj->wing_status_wing_pos = -1;
5067 pobj->respawn_count = 0;
5068 pobj->alt_type_index = -1;
5072 // returns true if a support ship is currently in the process of warping in.
5073 int mission_is_support_ship_arriving()
5075 if ( Arriving_support_ship )
5081 // returns true if the given ship is scheduled to be repaired by the arriving support ship
5082 int mission_is_repair_scheduled( object *objp )
5087 if ( !Arriving_support_ship )
5090 SDL_assert ( objp->type == OBJ_SHIP );
5091 name = Ships[objp->instance].ship_name;
5092 for (i = 0; i < Num_arriving_repair_targets; i++ ) {
5093 if ( !strcmp( name, Arriving_repair_targets[i]) )
5100 // function which removed the given ship from the list of ships that are to get repair
5101 // by arriving support ship
5102 int mission_remove_scheduled_repair( object *objp )
5107 if ( !Arriving_support_ship )
5110 // itereate through the target list looking for this ship name. If not found, we
5111 // can simply return.
5112 SDL_assert ( objp->type == OBJ_SHIP );
5113 name = Ships[objp->instance].ship_name;
5114 for (index = 0; index < Num_arriving_repair_targets; index++ ) {
5115 if ( !strcmp( name, Arriving_repair_targets[index]) )
5118 if ( index == Num_arriving_repair_targets )
5121 // ship is found -- compress the array
5122 for ( i = index; i < Num_arriving_repair_targets - 1; i++ )
5123 SDL_strlcpy( Arriving_repair_targets[i], Arriving_repair_targets[i+1], NAME_LENGTH );
5125 Num_arriving_repair_targets--;
5127 if ( MULTIPLAYER_MASTER )
5128 multi_maybe_send_repair_info( objp, NULL, REPAIR_INFO_WARP_REMOVE );
5133 // alternate name stuff
5134 int mission_parse_lookup_alt(char *name)
5144 for(idx=0; idx<Mission_alt_type_count; idx++){
5145 if(!strcmp(Mission_alt_types[idx], name)){
5154 static int mission_parse_lookup_alt_index_warn = 1;
5155 void mission_parse_lookup_alt_index(int index, char *out, const int max_outlen)
5161 if((index < 0) || (index > Mission_alt_type_count)){
5162 if (mission_parse_lookup_alt_index_warn) {
5163 Warning(LOCATION, "Ship with invalid alt_name. Get a programmer");
5164 mission_parse_lookup_alt_index_warn = 0;
5170 SDL_strlcpy(out, Mission_alt_types[index], max_outlen);
5173 int mission_parse_add_alt(char *name)
5181 if(Mission_alt_type_count < MAX_ALT_TYPE_NAMES){
5183 SDL_strlcpy(Mission_alt_types[Mission_alt_type_count++], name, NAME_LENGTH);
5186 return Mission_alt_type_count - 1;
5192 void mission_parse_reset_alt()
5194 Mission_alt_type_count = 0;
5197 int is_training_mission()
5199 return (The_mission.game_type & MISSION_TYPE_TRAINING);