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"));
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, i;
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 objp = &Objects[real_objnum];
2147 if ( !Fred_running ) {
2149 shipfx_blow_up_model( objp, Ships[objp->instance].modelnum, 0, 0, &objp->pos );
2150 objp->flags |= OF_SHOULD_BE_DEAD;
2152 // once the ship is exploded, find the debris pieces belonging to this object, mark them
2153 // as not to expire, and move them forward in time N seconds
2154 for (i = 0; i < MAX_DEBRIS_PIECES; i++ ) {
2158 if ( !db->flags & DEBRIS_USED ) // not used, move onto the next one.
2160 if ( db->source_objnum != real_objnum ) // not from this ship, move to next one
2163 debris_clear_expired_flag(db); // mark as don't expire
2164 db->lifeleft = -1.0f; // be sure that lifeleft == -1.0 so that it really doesn't expire!
2166 // now move the debris along it's path for N seconds
2167 objp = &Objects[db->objnum];
2168 physics_sim( &objp->pos, &objp->orient, &objp->phys_info, (float)destroy_before_mission_time );
2171 // be sure to set the variable in the ships structure for the final death time!!!
2172 Ships[objp->instance].final_death_time = destroy_before_mission_time;
2173 Ships[objp->instance].flags |= SF_KILL_BEFORE_MISSION;
2181 void parse_common_object_data(p_object *objp)
2185 // set some defaults..
2186 objp->initial_velocity = 0;
2187 objp->initial_hull = 100;
2188 objp->initial_shields = 100;
2190 // now change defaults if present
2191 if (optional_string("+Initial Velocity:")) {
2192 stuff_int(&objp->initial_velocity);
2195 if (optional_string("+Initial Hull:"))
2196 stuff_int(&objp->initial_hull);
2197 if (optional_string("+Initial Shields:"))
2198 stuff_int(&objp->initial_shields);
2200 objp->subsys_index = Subsys_index;
2201 objp->subsys_count = 0;
2202 while (optional_string("+Subsystem:")) {
2203 i = allocate_subsys_status();
2205 objp->subsys_count++;
2206 stuff_string(Subsys_status[i].name, F_NAME, NULL);
2208 if (optional_string("$Damage:"))
2209 stuff_float(&Subsys_status[i].percent);
2211 Subsys_status[i].subsys_cargo_name = -1;
2212 if (optional_string("+Cargo Name:")) {
2213 char cargo_name[256];
2214 stuff_string(cargo_name, F_NAME, NULL);
2215 int index = string_lookup(cargo_name, (const char **)Cargo_names, Num_cargo, "cargo", 0);
2216 if (index == -1 && (Num_cargo < MAX_CARGO)) {
2218 SDL_strlcpy(Cargo_names[Num_cargo++], cargo_name, NAME_LENGTH);
2220 Subsys_status[i].subsys_cargo_name = index;
2223 if (optional_string("+AI Class:"))
2224 Subsys_status[i].ai_class = match_and_stuff(F_NAME, (const char **)Ai_class_names, Num_ai_classes, "AI class");
2226 if (optional_string("+Primary Banks:"))
2227 stuff_int_list(Subsys_status[i].primary_banks, MAX_PRIMARY_BANKS, WEAPON_LIST_TYPE);
2229 if (optional_string("+Secondary Banks:"))
2230 stuff_int_list(Subsys_status[i].secondary_banks, MAX_SECONDARY_BANKS, WEAPON_LIST_TYPE);
2232 if (optional_string("+Sbank Ammo:"))
2233 stuff_int_list(Subsys_status[i].secondary_ammo, MAX_SECONDARY_BANKS, RAW_INTEGER_TYPE);
2238 void parse_objects(mission *pm, int flag)
2242 SDL_assert(pm != NULL);
2244 required_string("#Objects");
2247 num_ship_original = 0;
2248 while (required_string_either("#Wings", "$Name:")){
2249 // not all objects are always valid or legal
2250 if(parse_object(pm, flag, &temp)){
2251 // add to the default list
2252 if(num_ship_original < MAX_SHIP_ORIGINAL){
2253 memcpy(&ship_original[num_ship_original++], &temp, sizeof(p_object));
2259 p_object *mission_parse_get_original_ship( ushort net_signature )
2263 // look for original ships
2264 for(idx=0; idx<num_ship_original; idx++){
2265 if(ship_original[idx].net_signature == net_signature){
2266 return &ship_original[idx];
2274 int find_wing_name(char *name)
2278 for (i=0; i<num_wings; i++){
2279 if (!strcmp(name, Wings[i].name)){
2287 // function to create ships in the wing that need to be created. We psas the wing pointer, it's index
2288 // into the Wings array
2289 int parse_wing_create_ships( wing *wingp, int num_to_create, int force, int specific_instance )
2292 int wingnum, objnum, num_create_save;
2294 int pre_create_count;
2296 // we need to send this in multiplayer
2297 pre_create_count = wingp->total_arrived_count;
2299 // force is used to force creation of the wing -- used for multiplayer
2301 // we only want to evaluate the arrival cue of the wing if:
2303 // 2) multiplayer and I am the host of the game
2304 // can't create any ships if the arrival cue is false or the timestamp has not elapsed.
2306 if ( !eval_sexp(wingp->arrival_cue) ) /* || !timestamp_elapsed(wingp->arrival_delay) ) */
2309 // once the sexpressions becomes true, then check the arrival delay on the wing. The first time, the
2310 // arrival delay will be <= 0 meaning that no timer object has been set yet. Set up the timestamp
2311 // which should always give a number >= 0;
2312 if ( wingp->arrival_delay <= 0 ) {
2313 wingp->arrival_delay = timestamp( -wingp->arrival_delay * 1000 );
2314 SDL_assert ( wingp->arrival_delay >= 0 );
2317 if ( !timestamp_elapsed( wingp->arrival_delay ) )
2320 // if wing is coming from docking bay, then be sure that ship we are arriving from actually exists
2322 if ( wingp->arrival_location == ARRIVE_FROM_DOCK_BAY ) {
2326 SDL_assert( wingp->arrival_anchor >= 0 );
2327 name = Parse_names[wingp->arrival_anchor];
2329 // see if ship is yet to arrive. If so, then return -1 so we can evaluate again later.
2330 if ( mission_parse_get_arrival_ship( name ) )
2333 // see if ship is in mission. If not, then we can assume it was destroyed or departed since
2334 // it is not on the arrival list (as shown by above if statement).
2335 shipnum = ship_name_lookup( name );
2336 if ( shipnum == -1 ) {
2338 // since this wing cannot arrive from this place, we need to mark the wing as destroyed and
2339 // set the wing variables appropriatly. Good for directives.
2341 // set the gone flag
2342 wingp->flags |= WF_WING_GONE;
2344 // if the current wave is zero, it never existed
2345 wingp->flags |= WF_NEVER_EXISTED;
2347 // mark the number of waves and number of ships destroyed equal to the last wave and the number
2348 // of ships yet to arrive
2349 num_remaining = ( (wingp->num_waves - wingp->current_wave) * wingp->wave_count);
2350 wingp->total_arrived_count += num_remaining;
2351 wingp->current_wave = wingp->num_waves;
2353 // replaced following three lines of code with mission log call because of bug with
2354 // the Ships_exited list.
2355 //index = ship_find_exited_ship_by_name( name );
2356 //SDL_assert( index != -1 );
2357 //if (Ships_exited[index].flags & SEF_DESTROYED ) {
2358 if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) ) {
2359 wingp->total_destroyed += num_remaining;
2361 wingp->total_departed += num_remaining;
2364 Sexp_nodes[wingp->arrival_cue].value = SEXP_KNOWN_FALSE;
2369 if ( num_to_create == 0 )
2372 // check the wave_delay_timestamp field. If it is not valid, make it valid (based on wave delay min
2373 // and max valuds). If it is valid, and not elapsed, then return. If it is valid and elasped, then
2375 if ( !timestamp_valid(wingp->wave_delay_timestamp) ) {
2377 // if at least one of these is valid, then reset the timestamp. If they are both zero, we will create the
2379 if ( (wingp->wave_delay_min > 0) || (wingp->wave_delay_max > 0) ) {
2380 SDL_assert ( wingp->wave_delay_min <= wingp->wave_delay_max );
2381 time_to_arrive = wingp->wave_delay_min + (int)(frand() * (wingp->wave_delay_max - wingp->wave_delay_min));
2384 // HACK HACK -- in the presense of Mike Comet and Mitri, I have introduced one of the most
2385 // serious breaches of coding standards. I'm to lazy to fix this the correct way. Insert
2386 // a delay before the next wave of the wing can arrive to that clients in the game have ample
2387 // time to kill off any ships in the wing before the next wave arrives.
2388 if ( Game_mode & GM_MULTIPLAYER ){
2389 time_to_arrive += 7;
2391 wingp->wave_delay_timestamp = timestamp(time_to_arrive * 1000);
2395 // if we get here, both min and max values are 0; See comments above for a most serious hack
2397 if ( Game_mode & GM_MULTIPLAYER )
2398 time_to_arrive += 7;
2399 time_to_arrive *= 1000;
2400 wingp->wave_delay_timestamp = timestamp(time_to_arrive);
2403 // now check to see if the wave_delay_timestamp is elapsed or not
2404 if ( !timestamp_elapsed(wingp->wave_delay_timestamp) )
2408 // finally we can create the wing.
2410 num_create_save = num_to_create;
2412 wingnum = wingp - Wings; // get the wing number
2414 // if there are no ships to create, then all ships must be player start ships -- do nothing in this case.
2415 if ( num_to_create == 0 ){
2419 wingp->current_wave++; // we are creating new ships
2420 // we need to create num_to_create ships. Since the arrival cues for ships in a wing
2421 // are ignored, then *all* ships must be in the ship_arrival_list.
2424 objp = GET_FIRST(&ship_arrival_list);
2425 while( objp != END_OF_LIST(&ship_arrival_list) ) {
2426 p_object *temp = GET_NEXT(objp);
2428 // compare the wingnums. When they are equal, we can create the ship. In the case of
2429 // wings that have multiple waves, this code implies that we essentially creating clones
2430 // of the ships that were created in Fred for the wing when more ships for a new wave
2431 // arrive. The threshold value of a wing can also make one of the ships in a wing be "cloned"
2432 // more often than other ships in the wing. I don't think this matters much.
2433 if ( objp->wingnum == wingnum ) {
2436 // when ingame joining, we need to create a specific ship out of the list of ships for a
2437 // wing. specific_instance is a 0 based integer which specified which ship in the wing
2438 // to create. So, only create the ship we actually need to.
2439 if ( (Game_mode & GM_MULTIPLAYER) && (specific_instance > 0) ) {
2440 specific_instance--;
2445 SDL_assert ( !(objp->flags & P_SF_CANNOT_ARRIVE) ); // get allender
2449 // if we have the maximum number of ships in the wing, we must bail as well
2450 if ( wingp->current_count >= MAX_SHIPS_PER_WING ) {
2451 Int3(); // this is bogus -- we should always allow all ships to be created
2456 // bash the ship name to be the name of the wing + sone number if there is > 1 wave in
2458 // also, if multplayer, set the parse object's net signature to be wing's net signature
2459 // base + total_arrived_count (before adding 1)
2460 if ( Game_mode & GM_MULTIPLAYER ){
2461 objp->net_signature = (ushort)(wingp->net_signature + wingp->total_arrived_count);
2464 wingp->total_arrived_count++;
2465 if ( wingp->num_waves > 1 ){
2466 SDL_snprintf(objp->name, SDL_arraysize(objp->name), NOX("%s %d"), wingp->name, wingp->total_arrived_count);
2469 objnum = parse_create_object(objp);
2470 aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2472 // copy any goals from the wing to the newly created ship
2473 for (index = 0; index < MAX_AI_GOALS; index++) {
2474 if ( wingp->ai_goals[index].ai_mode != AI_GOAL_NONE ){
2475 ai_copy_mission_wing_goal( &wingp->ai_goals[index], aip );
2479 Ai_info[Ships[Objects[objnum].instance].ai_index].wing = wingnum;
2481 if ( wingp->flags & WF_NO_DYNAMIC ){
2482 aip->ai_flags |= AIF_NO_DYNAMIC;
2485 // update housekeeping variables
2486 wingp->ship_index[wingp->current_count] = Objects[objnum].instance;
2488 // set up wingman status index
2489 hud_wingman_status_set_index(wingp->ship_index[wingp->current_count]);
2491 objp->wing_status_wing_index = Ships[Objects[objnum].instance].wing_status_wing_index;
2492 objp->wing_status_wing_pos = Ships[Objects[objnum].instance].wing_status_wing_pos;
2494 wingp->current_count++;
2496 // keep any player ship on the parse object list -- used for respawns
2497 // 5/8/98 -- MWA -- don't remove ships from the list when you are ingame joining
2498 if ( !(objp->flags & P_OF_PLAYER_START) ) {
2499 if ( (Game_mode & GM_NORMAL) || !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ) {
2500 if ( wingp->num_waves == wingp->current_wave ) { // only remove ship if one wave in wing
2501 list_remove( &ship_arrival_list, objp); // remove objp from the list
2502 if ( objp->ai_goals != -1 ){
2503 free_sexp2(objp->ai_goals); // free up sexp nodes for reuse
2509 // flag ship with SF_FROM_PLAYER_WING if a member of player starting wings
2510 if ( (Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM) ) {
2511 // but for team vs. team games, then just check the alpha and zeta wings
2512 if ( !(SDL_strcasecmp(Starting_wing_names[STARTING_WING_ALPHA], wingp->name)) || !(SDL_strcasecmp(Starting_wing_names[STARTING_WING_ZETA], wingp->name)) ) {
2513 Ships[Objects[objnum].instance].flags |= SF_FROM_PLAYER_WING;
2516 for (int i = 0; i < MAX_STARTING_WINGS; i++ ) {
2517 if ( !SDL_strcasecmp(Starting_wing_names[i], wingp->name) ) {
2518 Ships[Objects[objnum].instance].flags |= SF_FROM_PLAYER_WING;
2523 // keep track of how many ships to create. Stop when we have done all that we are supposed
2526 if ( !num_to_create ){
2533 SDL_assert ( num_to_create == 0 ); // we should always have enough ships in the list!!!
2535 // possibly play some event driven music here. Send a network packet indicating the wing was
2536 // created. Only do this stuff if actually in the mission.
2537 if ( (objnum != -1) && (Game_mode & GM_IN_MISSION) ) { // if true, we have created at least one new ship.
2540 // see if this wing is a player starting wing, and if so, call the maybe_add_form_goal
2541 // function to possibly make the wing form on the player
2542 for (i = 0; i < MAX_STARTING_WINGS; i++ ) {
2543 if ( Starting_wings[i] == wingnum ){
2547 if ( i < MAX_STARTING_WINGS ){
2548 ai_maybe_add_form_goal( wingp );
2551 mission_log_add_entry( LOG_WING_ARRIVE, wingp->name, NULL, wingp->current_wave );
2552 ship_num = wingp->ship_index[0];
2554 if ( !(Ships[ship_num].flags & SF_NO_ARRIVAL_MUSIC) && !(wingp->flags & WF_NO_ARRIVAL_MUSIC) ) {
2555 if ( timestamp_elapsed(Allow_arrival_music_timestamp) ) {
2556 Allow_arrival_music_timestamp = timestamp(ARRIVAL_MUSIC_MIN_SEPARATION);
2557 event_music_arrival(Ships[ship_num].team);
2561 // possibly change the location where these ships arrive based on the wings arrival location
2562 mission_set_wing_arrival_location( wingp, num_create_save );
2564 // if in multiplayer (and I am the host) and in the mission, send a wing create command to all
2566 if ( MULTIPLAYER_MASTER ){
2567 send_wing_create_packet( wingp, num_create_save, pre_create_count );
2571 // test code to check to be sure that all ships in the wing are ignoring the same types
2572 // of orders from the player
2573 if ( Fred_running ) {
2574 SDL_assert( wingp->ship_index[0] != -1 );
2575 int orders = Ships[wingp->ship_index[0]].orders_accepted;
2576 for (i = 1; i < wingp->current_count; i++ ) {
2577 if ( orders != Ships[wingp->ship_index[i]].orders_accepted ) {
2578 Warning(LOCATION, "ships in wing %s are ignoring different player orders. Please find Mark A\nto talk to him about this.", wingp->name );
2587 wingp->wave_delay_timestamp = timestamp(-1); // we will need to set this up properly for the next wave
2588 return num_create_save;
2591 void parse_wing(mission *pm)
2593 int wingnum, i, wing_goals, delay;
2594 char name[NAME_LENGTH], ship_names[MAX_SHIPS_PER_WING][NAME_LENGTH];
2595 char wing_flag_strings[MAX_WING_FLAGS][NAME_LENGTH];
2598 SDL_assert(pm != NULL);
2599 wingp = &Wings[num_wings];
2601 required_string("$Name:");
2602 stuff_string(wingp->name, F_NAME, NULL);
2603 wingnum = find_wing_name(wingp->name);
2605 error_display(0, NOX("Redundant wing name: %s\n"), wingp->name);
2606 wingnum = num_wings;
2608 wingp->total_arrived_count = 0;
2609 wingp->total_destroyed = 0;
2612 required_string("$Waves:");
2613 stuff_int(&wingp->num_waves);
2614 SDL_assert ( wingp->num_waves >= 1 ); // there must be at least 1 wave
2616 wingp->current_wave = 0;
2618 required_string("$Wave Threshold:");
2619 stuff_int(&wingp->threshold);
2621 required_string("$Special Ship:");
2622 stuff_int(&wingp->special_ship);
2624 wingp->arrival_anchor = -1;
2625 find_and_stuff("$Arrival Location:", &wingp->arrival_location, F_NAME, Arrival_location_names, Num_arrival_names, "Arrival Location");
2626 if ( optional_string("+Arrival Distance:") ) {
2627 stuff_int( &wingp->arrival_distance );
2628 if ( wingp->arrival_location != ARRIVE_AT_LOCATION ) {
2629 required_string("$Arrival Anchor:");
2630 stuff_string(name, F_NAME, NULL);
2631 wingp->arrival_anchor = get_anchor(name);
2635 if (optional_string("+Arrival delay:")) {
2638 Error(LOCATION, "Cannot have arrival delay < 0 on wing %s", wingp->name );
2642 if ( !Fred_running ){
2643 wingp->arrival_delay = -delay;
2645 wingp->arrival_delay = delay;
2648 required_string("$Arrival Cue:");
2649 wingp->arrival_cue = get_sexp_main();
2650 if ( !Fred_running && (wingp->arrival_cue >= 0) ) {
2651 if ( eval_sexp(wingp->arrival_cue) ) // evaluate to determine if sexp is always false.
2652 wingp->arrival_delay = timestamp( -wingp->arrival_delay * 1000 );
2656 find_and_stuff("$Departure Location:", &wingp->departure_location, F_NAME, Departure_location_names, Num_arrival_names, "Departure Location");
2657 wingp->departure_anchor = -1;
2658 if ( wingp->departure_location == DEPART_AT_DOCK_BAY ) {
2659 required_string("$Departure Anchor:");
2660 stuff_string( name, F_NAME, NULL );
2661 wingp->departure_anchor = get_anchor(name);
2664 if (optional_string("+Departure delay:")) {
2667 Error(LOCATION, "Cannot have departure delay < 0 on wing %s", wingp->name );
2672 if ( !Fred_running )
2673 wingp->departure_delay = -delay; // use negative numbers to mean that delay timer not yet set
2675 wingp->departure_delay = delay;
2677 required_string("$Departure Cue:");
2678 wingp->departure_cue = get_sexp_main();
2680 // stores a list of all names of ships in the wing
2681 required_string("$Ships:");
2682 wingp->wave_count = stuff_string_list( ship_names, MAX_SHIPS_PER_WING );
2683 wingp->current_count = 0;
2685 // get the wings goals, if any
2687 if ( optional_string("$AI Goals:") )
2688 wing_goals = get_sexp_main();
2691 if (optional_string("+Hotkey:")) {
2692 stuff_int(&wingp->hotkey);
2693 SDL_assert((wingp->hotkey >= 0) && (wingp->hotkey < 10));
2696 if (optional_string("+Flags:")) {
2699 count = stuff_string_list( wing_flag_strings, MAX_WING_FLAGS );
2700 for (i = 0; i < count; i++ ) {
2701 if ( !SDL_strcasecmp( wing_flag_strings[i], NOX("ignore-count")) )
2702 wingp->flags |= WF_IGNORE_COUNT;
2703 else if ( !SDL_strcasecmp( wing_flag_strings[i], NOX("reinforcement")) )
2704 wingp->flags |= WF_REINFORCEMENT;
2705 else if ( !SDL_strcasecmp( wing_flag_strings[i], NOX("no-arrival-music")) )
2706 wingp->flags |= WF_NO_ARRIVAL_MUSIC;
2707 else if ( !SDL_strcasecmp( wing_flag_strings[i], NOX("no-arrival-message")) )
2708 wingp->flags |= WF_NO_ARRIVAL_MESSAGE;
2709 else if ( !SDL_strcasecmp( wing_flag_strings[i], NOX("no-arrival-warp")) )
2710 wingp->flags |= WF_NO_ARRIVAL_WARP;
2711 else if ( !SDL_strcasecmp( wing_flag_strings[i], NOX("no-departure-warp")) )
2712 wingp->flags |= WF_NO_DEPARTURE_WARP;
2713 else if ( !SDL_strcasecmp( wing_flag_strings[i], NOX("no-dynamic")) )
2714 wingp->flags |= WF_NO_DYNAMIC;
2716 Warning(LOCATION, "unknown wing flag\n%s\n\nSkipping.", wing_flag_strings[i]);
2720 // get the wave arrival delay bounds (if present). Used as lower and upper bounds (in seconds)
2721 // which determine when new waves of a wing should arrive.
2722 wingp->wave_delay_min = 0;
2723 wingp->wave_delay_max = 0;
2724 if ( optional_string("+Wave Delay Min:") )
2725 stuff_int( &(wingp->wave_delay_min) );
2726 if ( optional_string("+Wave Delay Max:") )
2727 stuff_int( &(wingp->wave_delay_max) );
2729 // be sure to set the wave arrival timestamp of this wing to pop right away so that the
2730 // wing could be created if it needs to be
2731 wingp->wave_delay_timestamp = timestamp(0);
2733 // initialize wing goals
2734 for (i=0; i<MAX_AI_GOALS; i++) {
2735 wingp->ai_goals[i].ai_mode = AI_GOAL_NONE;
2736 wingp->ai_goals[i].priority = -1;
2740 // error checking against the player ship wings to be sure that wave count doesn't exceed one for
2742 if ( Game_mode & GM_MULTIPLAYER ) {
2743 for (i = 0; i < MAX_STARTING_WINGS+1; i++ ) {
2744 if ( !SDL_strcasecmp(Starting_wing_names[i], wingp->name) ) {
2745 if ( wingp->num_waves > 1 ) {
2746 // only end the game if we're the server - clients will eventually find out :)
2747 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2748 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_WAVE_COUNT);
2750 // Error(LOCATION, "Player wings Alpha, Beta, Gamma, or Zeta cannot have more than 1 wave.");
2756 // Get the next starting signature for this in this wing. We want to reserve wave_count * num_waves
2757 // of signature. These can be used to construct wings for ingame joiners.
2758 if ( Game_mode & GM_MULTIPLAYER ) {
2761 wingp->net_signature = multi_assign_network_signature( MULTI_SIG_SHIP );
2762 next_signature = wingp->net_signature + (wingp->wave_count * wingp->num_waves);
2763 if ( next_signature > SHIP_SIG_MAX )
2764 Error(LOCATION, "Too many total ships in mission (%d) for network signature assignment", SHIP_SIG_MAX);
2765 multi_set_network_signature( (ushort)next_signature, MULTI_SIG_SHIP );
2768 for (i=0; i<MAX_SHIPS_PER_WING; i++)
2769 wingp->ship_index[i] = -1;
2771 // set up the ai_goals for this wing -- all ships created from this wing will inherit these goals
2772 // goals for the wing are stored slightly differently than for ships. We simply store the index
2773 // into the sexpression array of each goal (max 10). When a ship in this wing is created, each
2774 // goal in the wings goal array is given to the ship.
2775 if ( wing_goals != -1 ) {
2778 // this will assign the goals to the wings as well as to any ships in the wing that have been
2780 for ( sexp = CDR(wing_goals); sexp != -1; sexp = CDR(sexp) )
2781 ai_add_wing_goal_sexp(sexp, AIG_TYPE_EVENT_WING, wingnum); // used by Fred
2784 free_sexp2(wing_goals); // free up sexp nodes for reused, since they aren't needed anymore.
2787 // set the wing number for all ships in the wing
2788 for (i = 0; i < wingp->wave_count; i++ ) {
2789 //char *ship_name = wingp->ship_names[i];
2791 int num, assigned = 0;
2794 ship_name = ship_names[i];
2796 num = wingp->ship_index[i] = ship_name_lookup(ship_name, 1);
2797 SDL_assert ( num != -1 );
2799 // hack code -- REMOVE
2800 if ( Objects[Ships[num].objnum].flags & OF_PLAYER_SHIP )
2801 Ships[num].wingnum = wingnum;
2804 // determine if this ship is a player ship, and deal with it appropriately.
2805 if ( !SDL_strncasecmp(ship_name, NOX("Player "), 7) ) {
2806 Error(LOCATION, "Old mission file -- please convert by loading/saving in Fred -- see Allender/Hoffoss for help.");
2809 // assign the wing number to the ship -- if the ship has arrived, doulble check that
2810 // there is only one wave of this wing since ships with their own arrival cue cannot be
2811 // in a wing with > 1 wave. Otherwise, find the ship on the ship arrival list and set
2812 // their wing number
2813 if ( (num = ship_name_lookup(ship_name)) != -1 ) {
2814 Int3(); // this is impossible under the new system
2817 objp = GET_FIRST(&ship_arrival_list);
2818 while( objp != END_OF_LIST(&ship_arrival_list) ) {
2819 if ( !strcmp(ship_name, objp->name) ) {
2820 SDL_assert ( objp->wingnum == -1 ); // get Allender -- ship appears to be in multiple wings
2821 objp->wingnum = wingnum;
2824 objp = GET_NEXT(objp);
2828 if ( !assigned || (assigned > 1) )
2829 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);
2833 // Fred doesn't create the wing. otherwise, create the wing if is isn't a reinforcement.
2834 if ( !Fred_running && !(wingp->flags & WF_REINFORCEMENT) )
2835 parse_wing_create_ships( wingp, wingp->wave_count );
2838 void parse_wings(mission *pm)
2840 required_string("#Wings");
2841 while (required_string_either("#Events", "$Name:")) {
2842 SDL_assert(num_wings < MAX_WINGS);
2848 // mission events are sexpressions which cause things to happen based on the outcome
2849 // of other events in a mission. Essentially scripting the different things that can happen
2852 void parse_event(mission *pm)
2855 mission_event *event;
2857 event = &Mission_events[Num_mission_events];
2858 event->chain_delay = -1;
2860 required_string( "$Formula:" );
2861 event->formula = get_sexp_main();
2863 if (optional_string("+Name:")){
2864 stuff_string(event->name, F_NAME, NULL);
2869 if ( optional_string("+Repeat Count:")){
2870 stuff_int( &(event->repeat_count) );
2872 event->repeat_count = 1;
2875 event->interval = -1;
2876 if ( optional_string("+Interval:")){
2877 stuff_int( &(event->interval) );
2881 if ( optional_string("+Score:") ){
2882 stuff_int(&event->score);
2885 if ( optional_string("+Chained:") ){
2886 stuff_int(&event->chain_delay);
2889 if ( optional_string("+Objective:") ) {
2890 stuff_string(buf, F_NAME, NULL);
2891 event->objective_text = strdup(buf);
2893 event->objective_text = NULL;
2896 if ( optional_string("+Objective key:") ) {
2897 stuff_string(buf, F_NAME, NULL);
2898 event->objective_key_text = strdup(buf);
2900 event->objective_key_text = NULL;
2904 if( optional_string("+Team:") ) {
2905 stuff_int(&event->team);
2908 event->timestamp = timestamp(-1);
2910 // sanity check on the repeat count variable
2911 if ( event->repeat_count <= 0 ){
2912 Error (LOCATION, "Repeat count for mission event %s is <=0.\nMust be >= 1!", event->name );
2916 void parse_events(mission *pm)
2918 required_string("#Events");
2920 while (required_string_either( "#Goals", "$Formula:")) {
2921 SDL_assert( Num_mission_events < MAX_MISSION_EVENTS );
2923 Num_mission_events++;
2927 void parse_goal(mission *pm)
2931 mission_goal *goalp;
2933 goalp = &Mission_goals[Num_goals++];
2935 SDL_assert(Num_goals < MAX_GOALS);
2936 SDL_assert(pm != NULL);
2938 find_and_stuff("$Type:", &goalp->type, F_NAME, Goal_type_names, Num_goal_type_names, "goal type");
2940 required_string("+Name:");
2941 stuff_string(goalp->name, F_NAME, NULL);
2943 // backwards compatibility for old Fred missions - all new missions should use $MessageNew
2944 if(optional_string("$Message:")){
2945 stuff_string(goalp->message, F_NAME, NULL, MAX_GOAL_TEXT);
2947 required_string("$MessageNew:");
2948 stuff_string(goalp->message, F_MULTITEXT, NULL, MAX_GOAL_TEXT);
2951 if (optional_string("$Rating:")){
2952 stuff_int(&dummy); // not used
2955 required_string("$Formula:");
2956 goalp->formula = get_sexp_main();
2959 if ( optional_string("+Invalid:") )
2960 goalp->type |= INVALID_GOAL;
2961 if ( optional_string("+Invalid") )
2962 goalp->type |= INVALID_GOAL;
2963 if ( optional_string("+No music") )
2964 goalp->flags |= MGF_NO_MUSIC;
2967 if ( optional_string("+Score:") ){
2968 stuff_int(&goalp->score);
2972 if ( optional_string("+Team:") ){
2973 stuff_int( &goalp->team );
2977 void parse_goals(mission *pm)
2979 required_string("#Goals");
2981 while (required_string_either("#Waypoints", "$Type:")){
2986 void parse_waypoint_list(mission *pm)
2991 SDL_assert(Num_waypoint_lists < MAX_WAYPOINT_LISTS);
2992 SDL_assert(pm != NULL);
2993 wpl = &Waypoint_lists[Num_waypoint_lists];
2995 required_string("$Name:");
2996 stuff_string(wpl->name, F_NAME, NULL);
2998 required_string("$List:");
2999 wpl->count = stuff_vector_list(wpl->waypoints, MAX_WAYPOINTS_PER_LIST);
3002 // AAAAHH! I don't like to hard code a mission fix but I have no clue what to do
3003 // to fix this properly. In the FS1 mission "Playing Judas" you have to try and fly
3004 // into one of the docking bays on the Lucifer. Due to some change in the code the
3005 // waypoints and the Lucifer's position don't match up so we have to change the
3006 // waypoint position to compensate.
3007 if ( !SDL_strcasecmp(pm->name, "Playing Judas") ) {
3008 if ( !SDL_strcasecmp(wpl->name, "Docking Bay 1") ) {
3009 wpl->waypoints[0].xyz.x = -1262.550903;
3010 wpl->waypoints[0].xyz.y = 27.676950;
3011 wpl->waypoints[0].xyz.z = 4461.702930;
3012 } else if ( !SDL_strcasecmp(wpl->name, "Docking Bat 2") ) { // it really is spelled "Bat" in the mission
3013 wpl->waypoints[0].xyz.x = -1105.347976;
3014 wpl->waypoints[0].xyz.y = 27.676950;
3015 wpl->waypoints[0].xyz.z = 3900.236867;
3020 Num_waypoint_lists++;
3023 void parse_waypoints(mission *pm)
3028 required_string("#Waypoints");
3031 while (optional_string("$Jump Node:")) {
3032 SDL_assert(Num_jump_nodes < MAX_JUMP_NODES);
3034 z = jumpnode_create(&pos);
3037 if (optional_string("$Jump Node Name:")) {
3038 stuff_string(Jump_nodes[Num_jump_nodes - 1].name, F_NAME, NULL);
3041 // If no name exists, then use a standard name
3042 if ( Jump_nodes[Num_jump_nodes - 1].name[0] == 0 ) {
3043 SDL_snprintf(Jump_nodes[Num_jump_nodes - 1].name, NAME_LENGTH, "Jump Node %d", Num_jump_nodes);
3047 while (required_string_either("#Messages", "$Name:"))
3048 parse_waypoint_list(pm);
3051 void parse_messages(mission *pm)
3053 required_string("#Messages");
3055 mprintf(("Starting mission message count : %d\n", Num_message_waves));
3057 // the message_parse function can be found in MissionMessage.h. The format in the
3058 // mission file takes the same format as the messages in messages,tbl. Make parsing
3059 // a whole lot easier!!!
3060 while ( required_string_either("#Reinforcements", "$Name")){
3061 message_parse(); // call the message parsing system
3064 mprintf(("Ending mission message count : %d\n", Num_message_waves));
3067 void parse_reinforcement(mission *pm)
3069 reinforcements *ptr;
3072 SDL_assert(Num_reinforcements < MAX_REINFORCEMENTS);
3073 SDL_assert(pm != NULL);
3074 ptr = &Reinforcements[Num_reinforcements];
3076 required_string("$Name:");
3077 stuff_string(ptr->name, F_NAME, NULL);
3079 find_and_stuff("$Type:", &ptr->type, F_NAME, Reinforcement_type_names, Num_reinforcement_type_names, "reinforcement type");
3081 required_string("$Num times:");
3082 stuff_int(&ptr->uses);
3085 // reset the flags to 0
3088 if ( optional_string("+Arrival delay:") ){
3089 stuff_int( &(ptr->arrival_delay) );
3092 if ( optional_string("+No Messages:") ){
3093 stuff_string_list( ptr->no_messages, MAX_REINFORCEMENT_MESSAGES );
3096 if ( optional_string("+Yes Messages:") ){
3097 stuff_string_list( ptr->yes_messages, MAX_REINFORCEMENT_MESSAGES );
3100 // sanity check on the names of reinforcements -- must either be wings/ships/arrival list.
3101 if ( ship_name_lookup(ptr->name) == -1 ) {
3102 if ( wing_name_lookup(ptr->name, 1) == -1 ) {
3105 for ( p_objp = GET_FIRST(&ship_arrival_list); p_objp != END_OF_LIST(&ship_arrival_list); p_objp = GET_NEXT(p_objp) ) {
3106 if ( !SDL_strcasecmp(ptr->name, p_objp->name) ){
3111 if ( p_objp == END_OF_LIST(&ship_arrival_list) ) {
3112 Warning(LOCATION, "Reinforcement %s not found as ship or wing", ptr->name);
3118 // now, if the reinforcement is a wing, then set the number of waves of the wing == number of
3119 // uses of the reinforcement
3120 instance = wing_name_lookup(ptr->name, 1);
3121 if ( instance != -1 )
3122 Wings[instance].num_waves = ptr->uses;
3124 Num_reinforcements++;
3127 void parse_reinforcements(mission *pm)
3129 Num_reinforcements = 0;
3130 required_string("#Reinforcements");
3132 while (required_string_either("#Background bitmaps", "$Name:"))
3133 parse_reinforcement(pm);
3136 void parse_bitmap(mission *pm)
3139 starfield_bitmap_instance b;
3143 SDL_assert(pm != NULL);
3145 while(optional_string("$Bitmap:")) {
3146 stuff_string(b.filename, F_NAME, NULL);
3148 required_string("$Orientation:");
3151 required_string("$Rotation rate:");
3152 stuff_float(&b.rot);
3154 required_string("$Distance:");
3155 stuff_float(&b.scale_x);
3156 b.scale_y = b.scale_x;
3160 required_string("$Light:");
3161 stuff_int(&b.sun_light);
3163 if(Num_suns < MAX_STARFIELD_BITMAPS){
3165 SDL_strlcpy(Suns[Num_suns].filename, b.filename, SDL_arraysize(b.filename));
3171 char name[NAME_LENGTH];
3173 starfield_bitmaps *ptr;
3175 SDL_assert(Num_starfield_bitmaps < MAX_STARFIELD_BITMAPS);
3176 SDL_assert(pm != NULL);
3177 ptr = &Starfield_bitmaps[Num_starfield_bitmaps];
3179 required_string("$Bitmap:");
3180 stuff_string(name, F_NAME, NULL);
3181 for (z=0; z<Num_starfield_bitmap_lists; z++) {
3182 if (!SDL_strcasecmp(name, Starfield_bitmap_list[z].name)){
3187 if ( z >= Num_starfield_bitmap_lists ) {
3188 Warning( LOCATION, "Bitmap specified in mission not in game!\n" );
3192 ptr->bitmap_index = z;
3193 required_string("$Orientation:");
3194 stuff_matrix(&ptr->m);
3196 required_string("$Rotation rate:");
3197 stuff_float(&ptr->rot);
3199 required_string("$Distance:");
3200 stuff_float(&ptr->dist);
3202 required_string("$Light:");
3203 stuff_int(&ptr->light);
3204 Num_starfield_bitmaps++;
3205 calculate_bitmap_points(ptr);
3211 void parse_bitmaps(mission *pm)
3213 char str[MAX_FILENAME_LEN+1] = "";
3214 starfield_bitmap_instance b;
3217 Num_starfield_bitmaps = 0;
3218 required_string("#Background bitmaps");
3220 required_string("$Num stars:");
3221 stuff_int(&Num_stars);
3222 if (Num_stars >= MAX_STARS)
3223 Num_stars = MAX_STARS;
3225 int Ambient_light_level;
3226 required_string("$Ambient light level:");
3227 stuff_int(&Ambient_light_level);
3229 // This should call light_set_ambient() to
3230 // set the ambient light
3233 Mission_palette = 1;
3235 if(The_mission.flags & MISSION_FLAG_FULLNEB){
3236 // no regular nebula stuff
3240 SDL_strlcpy(Neb2_texture_name, "Eraseme3", SDL_arraysize(Neb2_texture_name));
3241 Neb2_poof_flags = ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4) | (1<<5));
3242 if(optional_string("+Neb2:")){
3243 stuff_string(Neb2_texture_name, F_NAME, NULL);
3245 required_string("+Neb2Flags:");
3246 stuff_int(&Neb2_poof_flags);
3248 // initialize neb effect. its gross to do this here, but Fred is dumb so I have no choice ... :(
3254 if (optional_string("+Nebula:")) {
3255 stuff_string(str, F_NAME, NULL, MAX_FILENAME_LEN);
3257 // parse the proper nebula type (full or not)
3258 for (z=0; z<NUM_NEBULAS; z++){
3259 if(The_mission.flags & MISSION_FLAG_FULLNEB){
3260 if (!SDL_strcasecmp(str, Neb2_filenames[z])) {
3265 if (!SDL_strcasecmp(str, Nebula_filenames[z])) {
3272 if (optional_string("+Color:")) {
3273 stuff_string(str, F_NAME, NULL, MAX_FILENAME_LEN);
3274 for (z=0; z<NUM_NEBULA_COLORS; z++){
3275 if (!SDL_strcasecmp(str, Nebula_colors[z])) {
3276 Mission_palette = z;
3282 if (optional_string("+Pitch:")){
3283 stuff_int(&Nebula_pitch);
3288 if (optional_string("+Bank:")){
3289 stuff_int(&Nebula_bank);
3294 if (optional_string("+Heading:")){
3295 stuff_int(&Nebula_heading);
3301 if (Nebula_index >= 0){
3302 nebula_init(Nebula_filenames[Nebula_index], Nebula_pitch, Nebula_bank, Nebula_heading);
3310 while(optional_string("$Sun:")){
3312 stuff_string(b.filename, F_NAME, NULL);
3315 required_string("+Angles:");
3316 stuff_float(&b.ang.p);
3317 stuff_float(&b.ang.b);
3318 stuff_float(&b.ang.h);
3321 required_string("+Scale:");
3322 stuff_float(&b.scale_x);
3323 b.scale_y = b.scale_x;
3327 // if we have room, store it
3328 if(Num_suns < MAX_STARFIELD_BITMAPS){
3330 SDL_strlcpy(Suns[Num_suns].filename, b.filename, SDL_arraysize(b.filename));
3335 // parse background bitmaps
3336 Num_starfield_bitmaps = 0;
3337 while(optional_string("$Starbitmap:")){
3339 stuff_string(b.filename, F_NAME, NULL);
3342 required_string("+Angles:");
3343 stuff_float(&b.ang.p);
3344 stuff_float(&b.ang.b);
3345 stuff_float(&b.ang.h);
3349 if(optional_string("+Scale:")){
3350 stuff_float(&b.scale_x);
3351 b.scale_y = b.scale_x;
3355 required_string("+ScaleX:");
3356 stuff_float(&b.scale_x);
3358 required_string("+ScaleY:");
3359 stuff_float(&b.scale_y);
3361 required_string("+DivX:");
3362 stuff_int(&b.div_x);
3364 required_string("+DivY:");
3365 stuff_int(&b.div_y);
3368 // if we have room, store it
3369 if(Num_starfield_bitmaps < MAX_STARFIELD_BITMAPS){
3370 Starfield_bitmap_instance[Num_starfield_bitmaps] = b;
3371 SDL_strlcpy(Starfield_bitmap_instance[Num_starfield_bitmaps].filename, b.filename, SDL_arraysize(b.filename));
3372 Num_starfield_bitmaps++;
3380 if ( optional_string("#Asteroid Fields") ){
3381 parse_asteroid_fields(pm);
3385 void parse_asteroid_fields(mission *pm)
3387 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
3389 int i, count, subtype;
3391 SDL_assert(pm != NULL);
3392 for (i=0; i<MAX_ASTEROID_FIELDS; i++)
3393 Asteroid_field.num_initial_asteroids = 0;
3397 // required_string("#Asteroid Fields");
3399 while (required_string_either("#", "$Density:")) {
3401 while (required_string_either("#", "$density:")) {
3403 float speed, density;
3406 required_string("$Density:");
3407 stuff_float(&density);
3409 Asteroid_field.num_initial_asteroids = (int) density;
3411 Asteroid_field.field_type = FT_ACTIVE;
3412 if (optional_string("+Field Type:")) {
3413 stuff_int((int*)&Asteroid_field.field_type);
3416 Asteroid_field.debris_genre = DG_ASTEROID;
3417 if (optional_string("+Debris Genre:")) {
3418 stuff_int((int*)&Asteroid_field.debris_genre);
3421 Asteroid_field.field_debris_type[0] = -1;
3422 Asteroid_field.field_debris_type[1] = -1;
3423 Asteroid_field.field_debris_type[2] = -1;
3424 if (Asteroid_field.debris_genre == DG_SHIP) {
3425 if (optional_string("+Field Debris Type:")) {
3426 stuff_int(&Asteroid_field.field_debris_type[0]);
3428 if (optional_string("+Field Debris Type:")) {
3429 stuff_int(&Asteroid_field.field_debris_type[1]);
3431 if (optional_string("+Field Debris Type:")) {
3432 stuff_int(&Asteroid_field.field_debris_type[2]);
3436 if (optional_string("+Field Debris Type:")) {
3437 stuff_int(&subtype);
3438 Asteroid_field.field_debris_type[subtype] = 1;
3441 if (optional_string("+Field Debris Type:")) {
3442 stuff_int(&subtype);
3443 Asteroid_field.field_debris_type[subtype] = 1;
3446 if (optional_string("+Field Debris Type:")) {
3447 stuff_int(&subtype);
3448 Asteroid_field.field_debris_type[subtype] = 1;
3453 // backward compatibility
3454 if ( (Asteroid_field.debris_genre == DG_ASTEROID) && (count == 0) ) {
3455 Asteroid_field.field_debris_type[0] = 0;
3458 required_string("$Average Speed:");
3459 stuff_float(&speed);
3461 vm_vec_rand_vec_quick(&Asteroid_field.vel);
3462 vm_vec_scale(&Asteroid_field.vel, speed);
3464 Asteroid_field.speed = speed;
3466 required_string("$Minimum:");
3467 stuff_vector(&Asteroid_field.min_bound);
3469 required_string("$Maximum:");
3470 stuff_vector(&Asteroid_field.max_bound);
3472 if (optional_string("+Inner Bound:")) {
3473 Asteroid_field.has_inner_bound = 1;
3475 required_string("$Minimum:");
3476 stuff_vector(&Asteroid_field.inner_min_bound);
3478 required_string("$Maximum:");
3479 stuff_vector(&Asteroid_field.inner_max_bound);
3481 Asteroid_field.has_inner_bound = 0;
3488 void parse_variables()
3490 if (! optional_string("#Sexp_variables") ) {
3493 stuff_sexp_variable_list();
3498 void parse_mission(mission *pm, int flag)
3502 Player_starts = Num_cargo = Num_waypoint_lists = Num_goals = num_wings = num_ship_arrivals = 0;
3503 Player_start_shipnum = -1;
3504 *Player_start_shipname = 0; // make the string 0 length for checking later
3505 memset( &Player_start_pobject, 0, sizeof(Player_start_pobject) );
3507 // initialize the initially_docked array.
3508 for ( i = 0; i < MAX_SHIPS; i++ ) {
3509 Initially_docked[i].docker = NULL;
3510 Initially_docked[i].dockee[0] = '\0';
3511 Initially_docked[i].docker_point[0] = '\0';
3512 Initially_docked[i].dockee_point[0] = '\0';
3514 Total_initially_docked = 0;
3516 list_init( &ship_arrival_list ); // init lists for arrival objects and wings
3521 parse_mission_info(pm);
3522 Current_file_checksum = netmisc_calc_checksum(pm,MISSION_CHECKSUM_SIZE);
3523 if ( flag == MISSION_PARSE_MISSION_INFO )
3525 parse_plot_info(pm);
3527 parse_briefing_info(pm); // TODO: obsolete code, keeping so we don't obsolete existing mission files
3528 parse_cmd_briefs(pm);
3530 parse_debriefing_new(pm);
3531 parse_player_info(pm);
3532 parse_objects(pm, flag);
3536 parse_waypoints(pm);
3538 parse_reinforcements(pm);
3542 post_process_mission();
3545 void post_process_mission()
3548 int indices[MAX_SHIPS], objnum;
3553 // the player_start_shipname had better exist at this point!
3554 Player_start_shipnum = ship_name_lookup( Player_start_shipname );
3555 SDL_assert ( Player_start_shipnum != -1 );
3556 SDL_assert ( Player_start_pobject.flags & P_SF_PLAYER_START_VALID );
3558 // Assign objnum, shipnum, etc. to the player structure
3559 objnum = Ships[Player_start_shipnum].objnum;
3560 Player_obj = &Objects[objnum];
3562 Player->objnum = objnum;
3565 Player_obj->flags |= OF_PLAYER_SHIP; // make this object a player controlled ship.
3566 Player_ship = &Ships[Player_start_shipnum];
3567 Player_ai = &Ai_info[Player_ship->ai_index];
3569 Player_ai->targeted_subsys = NULL;
3570 Player_ai->targeted_subsys_parent = -1;
3572 // determine if player start has initial velocity and set forward cruise percent to relect this
3573 if ( Player_obj->phys_info.vel.xyz.z > 0.0f )
3574 Player->ci.forward_cruise_percent = Player_obj->phys_info.vel.xyz.z / Player_ship->current_max_speed * 100.0f;
3576 // put in hard coded starting wing names.
3577 if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){
3578 Starting_wings[0] = wing_name_lookup(Starting_wing_names[0],1);
3579 Starting_wings[1] = wing_name_lookup(Starting_wing_names[MAX_STARTING_WINGS],1);
3581 for (i = 0; i < MAX_STARTING_WINGS; i++ ) {
3582 Starting_wings[i] = wing_name_lookup(Starting_wing_names[i], 1);
3588 // call a function to deal with intially docked ships
3589 mission_parse_do_initial_docks();
3591 // deal with setting up arrival location for all ships. Must do this now after all ships are created
3592 mission_parse_set_arrival_locations();
3594 // clear out information about arriving support ships
3595 Arriving_support_ship = NULL;
3596 Num_arriving_repair_targets = 0;
3598 // convert all ship name indices to ship indices now that mission has been loaded
3600 for (i=0; i<Num_parse_names; i++) {
3601 indices[i] = ship_name_lookup(Parse_names[i], 1);
3603 Warning(LOCATION, "Ship name \"%s\" referenced, but this ship doesn't exist", Parse_names[i]);
3606 for (i=0; i<MAX_SHIPS; i++) {
3607 if ((Ships[i].objnum >= 0) && (Ships[i].arrival_anchor >= 0) && (Ships[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHORS_OFFSET))
3608 Ships[i].arrival_anchor = indices[Ships[i].arrival_anchor];
3610 if ( (Ships[i].objnum >= 0) && (Ships[i].departure_anchor >= 0) )
3611 Ships[i].departure_anchor = indices[Ships[i].departure_anchor];
3614 for (i=0; i<MAX_WINGS; i++) {
3615 if (Wings[i].wave_count && (Wings[i].arrival_anchor >= 0) && (Wings[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHORS_OFFSET))
3616 Wings[i].arrival_anchor = indices[Wings[i].arrival_anchor];
3618 if (Wings[i].wave_count && (Wings[i].departure_anchor >= 0) )
3619 Wings[i].departure_anchor = indices[Wings[i].departure_anchor];
3624 // before doing anything else, we must validate all of the sexpressions that were loaded into the mission.
3625 // Loop through the Sexp_nodes array and send the top level functions to the check_sexp_syntax parser
3627 for (i = 0; i < MAX_SEXP_NODES; i++) {
3628 if ( is_sexp_top_level(i) && (!Fred_running || (i != Sexp_clipboard))) {
3629 int result, bindex, op;
3631 op = identify_operator(CTEXT(i));
3632 SDL_assert(op != -1); // need to make sure it is an operator before we treat it like one..
3633 result = check_sexp_syntax( i, query_operator_return_type(op), 1, &bindex);
3635 // entering this if statement will result in program termination!!!!!
3636 // print out an error based on the return value from check_sexp_syntax()
3638 char sexp_str[8192], text[8192];
3640 convert_sexp_to_string( i, sexp_str, SDL_arraysize(sexp_str), SEXP_ERROR_CHECK_MODE);
3641 SDL_snprintf(text, SDL_arraysize(text), "%s.\n\nIn sexpression: %s\n(Error appears to be: %s)",
3642 sexp_error_message(result), sexp_str, Sexp_nodes[bindex].text);
3645 Error( LOCATION, text );
3647 Warning( LOCATION, text );
3652 ai_post_process_mission();
3656 for (i=0; i<Total_initially_docked; i++) {
3657 z = ship_name_lookup(Initially_docked[i].dockee);
3659 SDL_assert(Initially_docked[i].docker->type == OBJ_SHIP);
3660 p1 = model_find_dock_name_index(Ships[Initially_docked[i].docker->instance].modelnum,
3661 Initially_docked[i].docker_point);
3662 SDL_assert(Objects[z].type == OBJ_SHIP);
3663 p2 = model_find_dock_name_index(Ships[Objects[z].instance].modelnum,
3664 Initially_docked[i].dockee_point);
3666 if ((p1 >= 0) && (p2 >= 0)) {
3667 nprintf(("AI", "Initially Docked: %s with %s\n", Ships[Initially_docked[i].docker->instance].ship_name, Ships[Objects[z].instance].ship_name));
3668 if (ship_docking_valid(Initially_docked[i].docker->instance, Objects[z].instance)) // only dock if they are allowed to be docked.
3669 ai_dock_with_object(Initially_docked[i].docker, &Objects[z], 89, AIDO_DOCK_NOW, p1, p2);
3672 Int3(); // Curious. Two ships told to dock, but one of the dock points is bogus.
3673 // Get Allender or Hoffoss, one of whom probably wrote the above if ()
3678 // we must also count all of the ships of particular types. We count all of the ships that do not have
3679 // their SF_IGNORE_COUNT flag set. We don't count ships in wings when the equivalent wing flag is set.
3680 // in counting ships in wings, we increment the count by the wing's wave count to account for everyone.
3681 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3682 int siflags, num, shipnum;
3684 shipnum = Objects[so->objnum].instance;
3685 // pass over non-ship objects and player ship objects
3686 if ( Ships[shipnum].objnum == -1 || (Objects[Ships[shipnum].objnum].flags & OF_PLAYER_SHIP) )
3688 if ( Ships[shipnum].flags & SF_IGNORE_COUNT )
3690 if ( (Ships[shipnum].wingnum != -1) && (Wings[Ships[shipnum].wingnum].flags & WF_IGNORE_COUNT) )
3693 siflags = Ship_info[Ships[shipnum].ship_info_index].flags;
3695 // determine the number of times we need to add this ship into the count
3696 // if ( Ships[i].wingnum == -1 )
3699 // num = Wings[Ships[i].wingnum].num_waves;
3701 ship_add_ship_type_count( siflags, num );
3703 // now go through the list of ships yet to arrive
3704 for ( p_objp = GET_FIRST(&ship_arrival_list); p_objp != END_OF_LIST(&ship_arrival_list); p_objp = GET_NEXT(p_objp) ) {
3707 // go through similar motions as above
3708 if ( p_objp->flags & P_SF_IGNORE_COUNT )
3710 if ( (p_objp->wingnum != -1) && (Wings[p_objp->wingnum].flags & WF_IGNORE_COUNT) )
3713 siflags = Ship_info[p_objp->ship_class].flags;
3715 if ( p_objp->wingnum == -1 )
3718 num = Wings[p_objp->wingnum].num_waves - 1; // subtract one since we already counted the first wave
3720 ship_add_ship_type_count( siflags, num );
3723 // set player weapons that are selected by default
3724 // AL 09/17/97: I added this code to select the first primary/secondary weapons,
3725 // since I noticed the player ship sometimes doesn't get default weapons selected
3727 // DB: modified 4/23/98 to take multiplayer into account. Under certain circumstances, multiplayer netplayer ships
3728 // had their current_primary_bank and current_secondary_bank set to -1 (from ship_set()) and left there since
3729 // Player_ship is not the only one we need to need about.
3730 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3731 ship *shipp = &Ships[Objects[so->objnum].instance];
3733 // don't process non player wing ships
3734 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
3737 swp = &shipp->weapons;
3739 // swp = &Player_ship->weapons;
3740 if ( swp->num_primary_banks > 0 ) {
3741 swp->current_primary_bank = 0; // currently selected primary bank
3744 if ( swp->num_secondary_banks > 0 ) {
3745 swp->current_secondary_bank = 0; // currently selected secondary bank
3749 ets_init_ship(Player_obj); // init ETS data for the player
3751 // put the timestamp stuff here for now
3752 Mission_arrival_timestamp = timestamp( ARRIVAL_TIMESTAMP );
3753 Mission_departure_timestamp = timestamp( DEPARTURE_TIMESTAMP );
3754 Mission_end_time = -1;
3756 if(Game_mode & GM_MULTIPLAYER){
3757 multi_respawn_build_points();
3760 // maybe reset hotkey defaults when loading new mission
3761 if ( Last_file_checksum != Current_file_checksum ){
3762 mission_hotkey_reset_saved();
3765 Allow_arrival_music_timestamp=timestamp(0);
3766 Allow_arrival_message_timestamp=timestamp(0);
3767 Arrival_message_delay_timestamp = timestamp(-1);
3770 for(idx=0; idx<2; idx++){
3771 Allow_arrival_music_timestamp_m[idx]=timestamp(0);
3772 Allow_arrival_message_timestamp_m[idx]=timestamp(0);
3773 Arrival_message_delay_timestamp_m[idx] = timestamp(-1);
3776 Last_file_checksum = Current_file_checksum;
3779 int get_mission_info(char *filename, mission *mission_p)
3781 // if mission_p is NULL, make it point to The_mission
3782 if ( mission_p == NULL )
3783 mission_p = &The_mission;
3787 // open localization
3790 CFILE *ftemp = cfopen(filename, "rt");
3792 // close localization
3798 // 7/9/98 -- MWA -- check for 0 length file.
3799 filelength = cfilelength(ftemp);
3801 if ( filelength == 0 ){
3802 // close localization
3809 read_file_text(filename, CF_TYPE_MISSIONS);
3810 memset( mission_p, 0, sizeof(mission) );
3812 parse_mission_info(mission_p);
3813 } catch (parse_error_t rval) {
3814 nprintf(("Error", "Error abort! Code = %d", (int)rval));
3818 // close localization
3824 // mai parse routine for parsing a mission. The default parameter flags tells us which information
3825 // to get when parsing the mission. 0 means get everything (default). Other flags just gets us basic
3826 // info such as game type, number of players etc.
3827 int parse_main(const char *mission_name, int flags)
3831 // fill in Ship_class_names array with the names from the ship_info struct;
3832 Num_parse_names = 0;
3833 Mission_all_attack = 0; // Might get set in mission load.
3834 SDL_assert(Num_ship_types < MAX_SHIP_TYPES);
3836 for (i = 0; i < Num_ship_types; i++)
3837 Ship_class_names[i] = Ship_info[i].name;
3840 // open localization
3843 CFILE *ftemp = cfopen(mission_name, "rt", CFILE_NORMAL, CF_TYPE_MISSIONS);
3847 Error( LOCATION, "Couldn't open mission '%s'\n", mission_name );
3849 Current_file_length = -1;
3850 Current_file_checksum = 0;
3852 // close localization
3858 Current_file_length = cfilelength(ftemp);
3862 read_file_text(mission_name, CF_TYPE_MISSIONS);
3863 memset(&The_mission, 0, sizeof(The_mission));
3864 parse_mission(&The_mission, flags);
3865 display_parse_diagnostics();
3866 } catch (parse_error_t rval) {
3867 nprintf(("Error", "Error abort! Code = %i.", (int)rval));
3871 // close localization
3875 SDL_strlcpy(Mission_filename, mission_name, SDL_arraysize(Mission_filename));
3880 // sets the arrival lcoation of the ships in wingp. pass num_to_set since the threshold value
3881 // for wings may have us create more ships in the wing when there are still some remaining
3882 void mission_set_wing_arrival_location( wing *wingp, int num_to_set )
3886 // get the starting index into the ship_index array of the first ship whose location we need set.
3888 index = wingp->current_count - num_to_set;
3889 if ( (wingp->arrival_location == ARRIVE_FROM_DOCK_BAY) || (wingp->arrival_location == ARRIVE_AT_LOCATION) ) {
3890 while ( index < wingp->current_count ) {
3893 objp = &Objects[Ships[wingp->ship_index[index]].objnum];
3894 mission_set_arrival_location(wingp->arrival_anchor, wingp->arrival_location, wingp->arrival_distance, OBJ_INDEX(objp), NULL, NULL);
3899 object *leader_objp;
3904 // wing is not arriving from a docking bay -- possibly move them based on arriving near
3905 // or in front of some other ship.
3906 index = wingp->current_count - num_to_set;
3907 leader_objp = &Objects[Ships[wingp->ship_index[index]].objnum];
3908 if (mission_set_arrival_location(wingp->arrival_anchor, wingp->arrival_location, wingp->arrival_distance, OBJ_INDEX(leader_objp), &pos, &orient)) {
3909 // modify the remaining ships created
3912 while ( index < wingp->current_count ) {
3915 objp = &Objects[Ships[wingp->ship_index[index]].objnum];
3917 // change the position of the next ships in the wing. Use the cool function in AiCode.cpp which
3918 // Mike K wrote to give new positions to the wing members.
3919 get_absolute_wing_pos( &objp->pos, leader_objp, wing_index++, 0);
3920 memcpy( &objp->orient, &orient, sizeof(matrix) );
3927 // create warp effect if in mission and not arriving from docking bay
3928 if ( (Game_mode & GM_IN_MISSION) && (wingp->arrival_location != ARRIVE_FROM_DOCK_BAY) ) {
3929 for ( index = wingp->current_count - num_to_set; index < wingp->current_count; index ++ ) {
3930 shipfx_warpin_start( &Objects[Ships[wingp->ship_index[index]].objnum] );
3935 // this function is called after a mission is parsed to set the arrival locations of all ships in the
3936 // mission to the apprioriate spot. Mainly needed because ships might be in dock bays to start
3937 // the mission, so their AI mode must be set appropriately.
3938 void mission_parse_set_arrival_locations()
3946 obj_merge_created_list();
3947 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
3950 if ( objp->type != OBJ_SHIP )
3953 shipp = &Ships[objp->instance];
3954 // if the ship is in a wing -- ignore the info and let the wing info handle it
3955 if ( shipp->wingnum != -1 )
3958 // call function to set arrival location for this ship.
3959 mission_set_arrival_location( shipp->arrival_anchor, shipp->arrival_location, shipp->arrival_distance, OBJ_INDEX(objp), NULL, NULL);
3963 for ( i = 0; i < num_wings; i++ ) {
3965 // if wing has no ships, then don't process it.
3966 if ( Wings[i].current_count == 0 )
3969 mission_set_wing_arrival_location( &Wings[i], Wings[i].current_count );
3974 // function which iterates through the ship_arrival_list and creates any ship which
3975 // should be intially docked with a ship which currently exists in the mission
3976 void mission_parse_do_initial_docks()
3978 p_object *pobjp, *tmp;
3980 pobjp = GET_FIRST( &ship_arrival_list );
3981 while ( pobjp != END_OF_LIST(&ship_arrival_list) ) {
3984 tmp = GET_NEXT(pobjp);
3986 // see if the flag for initial docked is set
3987 if ( pobjp->flags & P_SF_INITIALLY_DOCKED ) {
3988 // see if who this parse object is supposed to be docked with is in the mission
3989 shipnum = ship_name_lookup( pobjp->docked_with );
3990 if ( shipnum != -1 ) {
3993 // the ship exists, so create this object, then dock the two.
3994 objnum = parse_create_object( pobjp );
3995 SDL_assert ( objnum != -1 );
3997 list_remove( &ship_arrival_list, pobjp);
3999 // p1 is the parse object's docking point.
4000 // p2 is the existing objects docking point.
4001 p1 = model_find_dock_name_index(Ships[shipnum].modelnum, pobjp->docker_point);
4002 p2 = model_find_dock_name_index(Ships[Objects[objnum].instance].modelnum, pobjp->dockee_point);
4004 if ((p1 >= 0) && (p2 >= 0)) {
4005 nprintf(("AI", "Initially Docked: %s with %s\n", Ships[shipnum].ship_name, Ships[Objects[objnum].instance].ship_name));
4006 if (ship_docking_valid(shipnum, Objects[objnum].instance)) // only dock if they are allowed to be docked.
4007 ai_dock_with_object(&Objects[Ships[shipnum].objnum], &Objects[objnum], 89, AIDO_DOCK_NOW, p1, p2);
4009 ai_dock_with_object(&Objects[objnum], &Objects[Ships[shipnum].objnum], 89, AIDO_DOCK_NOW, p2, p1);
4012 Int3(); // Curious. Two ships told to dock, but one of the dock points is bogus.
4013 // Get Allender or Hoffoss, one of whom probably wrote the above if ()
4021 // function which returns true or false if the given mission support multiplayers
4022 int mission_parse_is_multi(const char *filename, char *mission_name)
4028 // new way of getting information. Open the file, and just get the name and the game_type flags.
4029 // return the flags if a multiplayer mission
4033 ftemp = cfopen(filename, "rt");
4037 // 7/9/98 -- MWA -- check for 0 length file.
4038 filelength = cfilelength(ftemp);
4040 if ( filelength == 0 )
4043 // open localization
4047 read_file_text(filename, CF_TYPE_MISSIONS);
4049 if ( skip_to_string("$Name:") != 1 ) {
4050 nprintf(("Network", "Unable to process %s because we couldn't find $Name:", filename));
4052 // close localization
4057 stuff_string( mission_name, F_NAME, NULL );
4058 if ( skip_to_string("+Game Type Flags:") != 1 ) {
4059 nprintf(("Network", "Unable to process %s because we couldn't find +Game Type Flags:\n", filename));
4061 // close localization
4066 stuff_int(&game_type);
4067 } catch (parse_error_t) {
4068 Error(LOCATION, "Bogus! Trying to get multi game type on mission %s returned as a mission from cf_get_filelist\n", filename);
4071 if ( game_type & MISSION_TYPE_MULTI ){
4072 // close localization
4078 // close localization
4084 // function which gets called to retrieve useful information about a mission. We will get the
4085 // name, description, and number of players for a mission. Probably used for multiplayer only?
4086 // The calling function can use the information in The_mission to get the name/description of the mission
4089 int mission_parse_get_multi_mission_info( const char *filename )
4091 if ( parse_main(filename, MISSION_PARSE_MISSION_INFO) ){
4095 SDL_assert( The_mission.game_type & MISSION_TYPE_MULTI ); // assume multiplayer only for now?
4097 // return the number of parse_players. later, we might want to include (optionally?) the number
4098 // of other ships in the main players wing (usually wing 'alpha') for inclusion of number of
4101 return The_mission.num_players;
4104 // returns true or false if this is on the yet to arrive list
4105 int mission_parse_ship_arrived( const char *shipname )
4109 for ( objp = GET_FIRST(&ship_arrival_list); objp !=END_OF_LIST(&ship_arrival_list); objp = GET_NEXT(objp) ) {
4110 if ( !SDL_strcasecmp( objp->name, shipname) )
4111 return 0; // still on the arrival list
4116 // return the parse object on the ship arrival list associated with the given name
4117 p_object *mission_parse_get_arrival_ship( const char *name )
4121 for ( objp = GET_FIRST(&ship_arrival_list); objp !=END_OF_LIST(&ship_arrival_list); objp = GET_NEXT(objp) ) {
4122 if ( !SDL_strcasecmp( objp->name, name) )
4123 return objp; // still on the arrival list
4129 // return the parse object on the ship arrival list associated with the given signature
4130 p_object *mission_parse_get_arrival_ship( ushort net_signature )
4134 for ( objp = GET_FIRST(&ship_arrival_list); objp !=END_OF_LIST(&ship_arrival_list); objp = GET_NEXT(objp) ) {
4135 if ( objp->net_signature == net_signature )
4136 return objp; // still on the arrival list
4142 // mission_set_arrival_location() sets the arrival location of a parse object according to the arrival location
4143 // of the object. Returns true if object set to new position, false if not.
4144 int mission_set_arrival_location(int anchor, int location, int dist, int objnum, vector *new_pos, matrix *new_orient)
4146 int shipnum, anchor_objnum;
4147 vector anchor_pos, rand_vec, new_fvec;
4150 if ( location == ARRIVE_AT_LOCATION )
4153 SDL_assert(anchor >= 0);
4155 // this ship might possibly arrive at another location. The location is based on the
4156 // proximity of some ship (and some other special tokens)
4158 // if we didn't find the arrival anchor in the list of special nodes, then do a
4159 // ship name lookup on the anchor
4160 if (anchor < SPECIAL_ARRIVAL_ANCHORS_OFFSET) {
4161 shipnum = ship_name_lookup(Parse_names[anchor]);
4162 if ( shipnum == -1 ) {
4163 SDL_assert ( location != ARRIVE_FROM_DOCK_BAY ); // bogus data somewhere!!! get mwa
4164 nprintf (("allender", "couldn't find ship for arrival anchor -- using location ship created at"));
4169 // come up with a position based on the special token names
4172 if (anchor == ANY_FRIENDLY) {
4173 shipnum = ship_get_random_team_ship( TEAM_FRIENDLY, SHIP_GET_ANY_SHIP );
4174 } else if (anchor == ANY_HOSTILE) {
4175 shipnum = ship_get_random_team_ship( opposing_team_mask(Player_ship->team), SHIP_GET_ANY_SHIP );
4176 } else if (anchor == ANY_FRIENDLY_PLAYER) {
4177 shipnum = ship_get_random_team_ship( TEAM_FRIENDLY, SHIP_GET_ONLY_PLAYERS );
4178 } else if (anchor == ANY_HOSTILE_PLAYER) {
4179 shipnum = ship_get_random_team_ship( opposing_team_mask(Player_ship->team), SHIP_GET_ONLY_PLAYERS );
4181 Int3(); // get allender -- unknown special arrival instructions
4183 // if we didn't get an object from one of the above functions, then make the object
4184 // arrive at it's placed location
4185 if ( shipnum == -1 ) {
4186 nprintf (("Allender", "Couldn't find random ship for arrival anchor -- using default location\n"));
4191 // take the shipnum and get the position. once we have positions, we can determine where
4192 // to make this ship appear
4193 SDL_assert ( shipnum != -1 );
4194 anchor_objnum = Ships[shipnum].objnum;
4195 anchor_pos = Objects[anchor_objnum].pos;
4197 // if arriving from docking bay, then set ai mode and call function as per AL's instructions.
4198 if ( location == ARRIVE_FROM_DOCK_BAY ) {
4201 // if we get an error, just let the ship arrive(?)
4202 if ( ai_acquire_emerge_path(&Objects[objnum], anchor_objnum, &pos, &fvec) == -1 ) {
4203 Int3(); // get MWA or AL -- not sure what to do here when we cannot acquire a path
4206 Objects[objnum].pos = pos;
4207 Objects[objnum].orient.v.fvec = fvec;
4210 // AL: ensure dist > 0 (otherwise get errors in vecmat)
4211 // TODO: maybe set distance to 2x ship radius of ship appearing in front of?
4213 Error(LOCATION, "Distance of %d is invalid in mission_set_arrival_location\n", dist);
4217 // get a vector which is the ships arrival position based on the type of arrival
4218 // this ship should have. Arriving near a ship we use a random normalized vector
4219 // scaled by the distance given by the designer. Arriving in front of a ship means
4220 // entering the battle in the view cone.
4221 if ( location == ARRIVE_NEAR_SHIP ) {
4222 // get a random vector -- use static randvec if in multiplayer
4223 if ( Game_mode & GM_NORMAL )
4224 vm_vec_rand_vec_quick(&rand_vec);
4226 static_randvec( Objects[objnum].net_signature, &rand_vec );
4227 } else if ( location == ARRIVE_IN_FRONT_OF_SHIP ) {
4232 // cool function by MK to give a reasonable random vector "in front" of a ship
4233 // rvec and uvec are the right and up vectors.
4234 // If these are not available, this would be an expensive method.
4236 x = (float)cos(ANG_TO_RAD(45));
4237 if ( Game_mode & GM_NORMAL ) {
4238 r1 = myrand() < MY_RAND_MAX/2 ? -1 : 1;
4239 r2 = myrand() < MY_RAND_MAX/2 ? -1 : 1;
4241 // in multiplayer, use the static rand functions so that all clients can get the
4242 // same information.
4243 r1 = static_rand(Objects[objnum].net_signature) < MY_RAND_MAX/2 ? -1 : 1;
4244 r2 = static_rand(Objects[objnum].net_signature+1) < MY_RAND_MAX/2 ? -1 : 1;
4247 vm_vec_copy_scale(&t1, &(Objects[anchor_objnum].orient.v.fvec), x);
4248 vm_vec_copy_scale(&t2, &(Objects[anchor_objnum].orient.v.rvec), (1.0f - x) * r1);
4249 vm_vec_copy_scale(&t3, &(Objects[anchor_objnum].orient.v.uvec), (1.0f - x) * r2);
4251 vm_vec_add(&rand_vec, &t1, &t2);
4252 vm_vec_add2(&rand_vec, &t3);
4253 vm_vec_normalize(&rand_vec);
4256 // add in the radius of the two ships involved. This will make the ship arrive further than
4257 // specified, but will appear more accurate since we are pushing the edge of the model to the
4258 // specified distance. large objects appears to be a lot closer without the following line because
4259 // the object centers were at the correct distance, but the model itself was much closer to the
4261 dist += (int)Objects[objnum].radius + (int)Objects[anchor_objnum].radius;
4262 vm_vec_scale_add(&Objects[objnum].pos, &anchor_pos, &rand_vec, (float)dist);
4264 // I think that we will always want to orient the ship that is arriving to face towards
4265 // the ship it is arriving near/in front of. The effect will be cool!
4267 // calculate the new fvec of the ship arriving and use only that to get the matrix. isn't a big
4268 // deal not getting bank.
4269 vm_vec_sub(&new_fvec, &anchor_pos, &Objects[objnum].pos );
4270 vm_vector_2_matrix( &orient, &new_fvec, NULL, NULL );
4271 Objects[objnum].orient = orient;
4274 // set the new_pos parameter since it might be used outside the function (i.e. when dealing with wings).
4276 memcpy(new_pos, &Objects[objnum].pos, sizeof(vector) );
4279 memcpy( new_orient, &Objects[objnum].orient, sizeof(matrix) );
4284 // mark a reinforcement as available
4285 void mission_parse_mark_reinforcement_available(char *name)
4290 for (i = 0; i < Num_reinforcements; i++) {
4291 rp = &Reinforcements[i];
4292 if ( !SDL_strcasecmp(rp->name, name) ) {
4293 if ( !(rp->flags & RF_IS_AVAILABLE) ) {
4294 rp->flags |= RF_IS_AVAILABLE;
4296 // tell all of the clients.
4297 if ( MULTIPLAYER_MASTER ) {
4298 send_reinforcement_avail( i );
4305 SDL_assert ( i < Num_reinforcements );
4308 // mission_did_ship_arrive takes a parse object and checked the arrival cue and delay and
4309 // creates the object if necessary. Returns -1 if not created. objnum of created ship otherwise
4310 int mission_did_ship_arrive(p_object *objp)
4314 // find out in the arrival cue became true
4315 did_arrive = eval_sexp(objp->arrival_cue);
4317 // we must first check to see if this ship is a reinforcement or not. If so, then don't
4319 if ( objp->flags & P_SF_REINFORCEMENT ) {
4321 // if this ship did arrive, mark the reinforcement as available, and tell clients if in multiplayer
4324 mission_parse_mark_reinforcement_available(objp->name);
4329 if ( did_arrive ) { // has the arrival criteria been met?
4332 SDL_assert ( !(objp->flags & P_SF_CANNOT_ARRIVE) ); // get allender
4334 // check to see if the delay field <= 0. if so, then create a timestamp and then maybe
4335 // create the object
4336 if ( objp->arrival_delay <= 0 ) {
4337 objp->arrival_delay = timestamp( -objp->arrival_delay * 1000 );
4338 SDL_assert( objp->arrival_delay >= 0 );
4341 // if the timestamp hasn't elapsed, move onto the next ship.
4342 if ( !timestamp_elapsed(objp->arrival_delay) )
4345 // check to see if this ship is to arrive via a docking bay. If so, and the ship to arrive from
4346 // doesn't exist, don't create.
4347 if ( objp->arrival_location == ARRIVE_FROM_DOCK_BAY ) {
4351 SDL_assert( objp->arrival_anchor >= 0 );
4352 name = Parse_names[objp->arrival_anchor];
4354 // see if ship is yet to arrive. If so, then return -1 so we can evaluate again later.
4355 if ( mission_parse_get_arrival_ship( name ) )
4358 // see if ship is in mission. If not, then we can assume it was destroyed or departed since
4359 // it is not on the arrival list (as shown by above if statement).
4360 shipnum = ship_name_lookup( name );
4361 if ( shipnum == -1 ) {
4362 Sexp_nodes[objp->arrival_cue].value = SEXP_KNOWN_FALSE;
4367 object_num = parse_create_object(objp); // create the ship
4369 // since this ship is not in a wing, create a SHIP_ARRIVE entry
4370 //mission_log_add_entry( LOG_SHIP_ARRIVE, objp->name, NULL );
4371 SDL_assert(object_num >= 0 && object_num < MAX_OBJECTS);
4373 // Play the music track for an arrival
4374 if ( !(Ships[Objects[object_num].instance].flags & SF_NO_ARRIVAL_MUSIC) )
4375 if ( timestamp_elapsed(Allow_arrival_music_timestamp) ) {
4376 Allow_arrival_music_timestamp = timestamp(ARRIVAL_MUSIC_MIN_SEPARATION);
4377 event_music_arrival(Ships[Objects[object_num].instance].team);
4381 // check to see if the arrival cue of this ship is known false -- if so, then remove
4382 // the parse object from the ship
4383 if ( Sexp_nodes[objp->arrival_cue].value == SEXP_KNOWN_FALSE )
4384 objp->flags |= P_SF_CANNOT_ARRIVE;
4391 // funciton to set a flag on all parse objects on ship arrival list which cannot
4392 // arrive in the mission
4393 void mission_parse_mark_non_arrivals()
4397 for ( pobjp = GET_FIRST(&ship_arrival_list); pobjp != END_OF_LIST(&ship_arrival_list); pobjp = GET_NEXT(pobjp) ) {
4398 if ( pobjp->wingnum != -1 ) {
4399 if ( Sexp_nodes[Wings[pobjp->wingnum].arrival_cue].value == SEXP_KNOWN_FALSE )
4400 pobjp->flags |= P_SF_CANNOT_ARRIVE;
4402 if ( Sexp_nodes[pobjp->arrival_cue].value == SEXP_KNOWN_FALSE )
4403 pobjp->flags |= P_SF_CANNOT_ARRIVE;
4408 // function to deal with support ship arrival. objnum is the object number of the arriving support
4409 // ship. This function can get called from either single or multiplayer. Needed to that clients
4410 // can know when to abort rearm.
4411 void mission_parse_support_arrived( int objnum )
4415 // when the support ship arrives, the shipname it is supposed to repair is in the 'misc'
4416 // field of the parse_object. If the ship still exists, call ai function which actually
4417 // issues the goal for the repair
4418 for ( i = 0; i < Num_arriving_repair_targets; i++ ) {
4421 shipnum = ship_name_lookup( Arriving_repair_targets[i] );
4423 if ( shipnum != -1 ) {
4424 object *requester_objp, *support_objp;
4426 support_objp = &Objects[objnum];
4427 requester_objp = &Objects[Ships[shipnum].objnum];
4428 ai_add_rearm_goal( requester_objp, support_objp );
4432 // MK: A bit of a hack. If on player's team and player isn't allowed shields, don't give this ship shields.
4433 if ((Player_obj->flags & OF_NO_SHIELDS) && (Player_ship->team == Ships[Objects[objnum].instance].team))
4434 Objects[objnum].flags |= OF_NO_SHIELDS;
4436 Ships[Objects[objnum].instance].flags |= SF_WARPED_SUPPORT;
4438 Arriving_support_ship = NULL;
4439 Num_arriving_repair_targets = 0;
4442 MONITOR(NumShipArrivals);
4444 // mission_parse_arrivals will parse the lists of arriving ships and
4445 // wings -- creating new ships/wings if the arrival criteria have been
4447 void mission_eval_arrivals()
4453 // before checking arrivals, check to see if we should play a message concerning arrivals
4454 // of other wings. We use the timestamps to delay the arrival message slightly for
4456 if ( timestamp_valid(Arrival_message_delay_timestamp) && timestamp_elapsed(Arrival_message_delay_timestamp) && !((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ){
4457 int rship, use_terran;
4459 // use terran command 25% of time
4460 use_terran = ((frand() - 0.75) > 0.0f)?1:0;
4462 rship = ship_get_random_player_wing_ship( SHIP_GET_NO_PLAYERS );
4463 if ( (rship == -1) || use_terran ){
4464 message_send_builtin_to_player( MESSAGE_ARRIVE_ENEMY, NULL, MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4465 } else if ( rship != -1 ) {
4466 message_send_builtin_to_player( MESSAGE_ARRIVE_ENEMY, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4469 Arrival_message_delay_timestamp = timestamp(-1); // make the stamp invalid
4472 // if ( !timestamp_elapsed(Mission_arrival_timestamp) )
4475 // check the ship_arrival_list
4476 objp = GET_FIRST(&ship_arrival_list);
4477 while( objp !=END_OF_LIST(&ship_arrival_list) ) {
4478 p_object *temp = GET_NEXT(objp);
4479 if ( objp->wingnum == -1 ) { // if this object has a wing -- let code for wings determine if it should be created
4481 objnum = mission_did_ship_arrive( objp );
4482 if ( objnum != -1 ) {
4483 list_remove( &ship_arrival_list, objp);
4484 MONITOR_INC(NumShipArrivals,1);
4491 // check for any initially docked ships. Do it after all are created since the next function
4492 // messes with the ship_arrival_list
4493 mission_parse_do_initial_docks(); // maybe create it's docked counterpart
4495 mission_parse_mark_non_arrivals(); // mark parse objects which can no longer arrive
4497 // check the support ship arrival list
4498 if ( Arriving_support_ship ) {
4501 objnum = mission_did_ship_arrive( Arriving_support_ship );
4503 if ( objnum != -1 ) {
4504 MONITOR_INC(NumShipArrivals,1);
4505 mission_parse_support_arrived( objnum );
4509 // we must also check to see if there are waves of a wing that must
4510 // reappear if all the ships of the current wing have been destroyed or
4511 // have departed. If this is the case, then create the next wave.
4513 for ( i = 0; i < num_wings; i++ ) {
4516 // should we process this wing anymore
4517 if ( wingp->flags & WF_WING_GONE )
4520 // if we have a reinforcement wing, then don't try to create new ships automatically.
4521 if ( wingp->flags & WF_REINFORCEMENT ) {
4523 // check to see in the wings arrival cue is true, and if so, then mark the reinforcement
4525 if ( eval_sexp(wingp->arrival_cue) ) {
4526 mission_parse_mark_reinforcement_available(wingp->name);
4531 // don't do evaluations for departing wings
4532 if ( wingp->flags & WF_WING_DEPARTING ){
4536 // must check to see if we are at the last wave. Code above to determine when a wing is gone only
4537 // gets run when a ship is destroyed (not every N seconds like it used to). Do a quick check
4539 if ( wingp->current_wave == wingp->num_waves ){
4543 // if the current wave of this wing is 0, then we haven't created the ships in the wing yet.
4544 // call parse_wing_create_ships to try and create it. That function will eval the arrival
4545 // cue of the wing and create the ships if necessary, or if the threshold of the wing has
4546 // been reached, then try and create more ships
4547 if ( (wingp->current_wave == 0) || (wingp->current_count <= wingp->threshold) ) {
4550 created = parse_wing_create_ships( wingp, wingp->wave_count );
4552 // if we created ships in this wing, check to see if the wings was int the reinforcements
4553 // array. If so, then if we have more uses, then reset the reinforcement flag for the wing
4554 // so the user can call in another set if need be.
4555 if ( created > 0 ) {
4558 mission_parse_do_initial_docks(); // maybe create other initially docked ships
4559 if ( Wings[i].flags & WF_RESET_REINFORCEMENT ) {
4560 Wings[i].flags &= ~WF_RESET_REINFORCEMENT;
4561 Wings[i].flags |= WF_REINFORCEMENT;
4564 // possibly send a message to the player when this wing arrives.
4565 if ( wingp->flags & WF_NO_ARRIVAL_MESSAGE ){
4569 // multiplayer team vs. team
4570 if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){
4571 // send a hostile wing arrived message
4572 rship = Wings[i].ship_index[0];
4574 int multi_team_filter = Ships[rship].team == TEAM_FRIENDLY ? 1 : 0;
4576 // there are two timestamps at work here. One to control how often the player receives
4577 // messages about incoming hostile waves, and the other to control how long after
4578 // the wing arrives does the player actually get the message.
4579 if ( timestamp_elapsed(Allow_arrival_message_timestamp_m[multi_team_filter]) ) {
4580 if ( !timestamp_valid(Arrival_message_delay_timestamp_m[multi_team_filter]) ){
4581 Arrival_message_delay_timestamp_m[multi_team_filter] = timestamp_rand(ARRIVAL_MESSAGE_DELAY_MIN, ARRIVAL_MESSAGE_DELAY_MAX );
4583 Allow_arrival_message_timestamp_m[multi_team_filter] = timestamp(ARRIVAL_MESSAGE_MIN_SEPARATION);
4585 // send to the proper team
4586 message_send_builtin_to_player( MESSAGE_ARRIVE_ENEMY, NULL, MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, multi_team_filter );
4591 // see if this is a starting player wing
4592 if ( i == Starting_wings[STARTING_WING_BETA] ) { // this is the beta wing
4593 rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS );
4595 message_send_builtin_to_player( MESSAGE_BETA_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4597 } else if ( i == Starting_wings[STARTING_WING_GAMMA] ) { // this is the gamma wing
4598 rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS );
4599 if ( rship != -1 ) {
4600 message_send_builtin_to_player( MESSAGE_GAMMA_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4602 } else if ( !SDL_strcasecmp( wingp->name, "delta") ) {
4603 rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS );
4604 if ( rship != -1 ) {
4605 message_send_builtin_to_player( MESSAGE_DELTA_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4607 } else if ( !SDL_strcasecmp(wingp->name, "epsilon") ) {
4608 rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS );
4609 if ( rship != -1 ) {
4610 message_send_builtin_to_player( MESSAGE_EPSILON_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4613 // see if we have a hostile wing that arrived
4614 rship = Wings[i].ship_index[0];
4615 if ( Ships[rship].team != TEAM_FRIENDLY ) {
4617 // there are two timestamps at work here. One to control how often the player receives
4618 // messages about incoming hostile waves, and the other to control how long after
4619 // the wing arrives does the player actually get the message.
4620 if ( timestamp_elapsed(Allow_arrival_message_timestamp) ) {
4621 if ( !timestamp_valid(Arrival_message_delay_timestamp) ){
4622 Arrival_message_delay_timestamp = timestamp_rand(ARRIVAL_MESSAGE_DELAY_MIN, ARRIVAL_MESSAGE_DELAY_MAX );
4624 Allow_arrival_message_timestamp = timestamp(ARRIVAL_MESSAGE_MIN_SEPARATION);
4632 Mission_arrival_timestamp = timestamp(ARRIVAL_TIMESTAMP);
4635 MONITOR(NumShipDepartures);
4637 // called to make object objp depart.
4638 void mission_do_departure( object *objp )
4643 MONITOR_INC(NumShipDepartures,1);
4645 SDL_assert ( objp->type == OBJ_SHIP );
4646 shipp = &Ships[objp->instance];
4648 // if departing to a docking bay, try to find the anchor ship to depart to. If not found, then
4649 // just make it warp out like anything else.
4650 if ( shipp->departure_location == DEPART_AT_DOCK_BAY ) {
4654 SDL_assert( shipp->departure_anchor >= 0 );
4655 name = Parse_names[shipp->departure_anchor];
4657 // see if ship is yet to arrive. If so, then return -1 so we can evaluate again later.
4658 if ( mission_parse_get_arrival_ship( name ) )
4659 goto do_departure_warp;
4661 // see if ship is in mission. If not, then we can assume it was destroyed or departed since
4662 // it is not on the arrival list (as shown by above if statement).
4663 anchor_shipnum = ship_name_lookup( name );
4664 if ( anchor_shipnum == -1 )
4665 goto do_departure_warp;
4667 ai_acquire_depart_path(objp, Ships[anchor_shipnum].objnum);
4672 ai_set_mode_warp_out( objp, &Ai_info[Ships[objp->instance].ai_index] );
4676 // put here because mission_eval_arrivals is here. Might move these to a better location
4678 void mission_eval_departures()
4684 // if ( !timestamp_elapsed(Mission_departure_timestamp) )
4687 // scan through the active ships an evaluate their departure cues. For those
4688 // ships whose time has come, set their departing flag.
4690 for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4691 if (objp->type == OBJ_SHIP) {
4694 SDL_assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
4696 shipp = &Ships[objp->instance];
4698 // don't process a ship that is already departing or dying or disabled
4699 // AL 12-30-97: Added SF_CANNOT_WARP to check
4700 if ( (shipp->flags & (SF_DEPARTING | SF_DYING | SF_CANNOT_WARP )) || ship_subsys_disrupted(shipp, SUBSYSTEM_ENGINE) ) {
4704 // don't process ships that are part of a wing -- handled in seperate case
4705 if ( shipp->wingnum != -1 )
4708 // && (!timestamp_valid(shipp->departure_delay) || timestamp_elapsed(shipp->departure_delay)) )
4709 // when the departure cue becomes true, set off the departure delay timer. We store the
4710 // timer as -seconds in Freespace which indicates that the timer has not been set. If the timer
4711 // is not set, then turn it into a valid timer and keep evaluating the timer until it is elapsed
4712 if ( eval_sexp(shipp->departure_cue) ) {
4713 if ( shipp->departure_delay <= 0 )
4714 shipp->departure_delay = timestamp(-shipp->departure_delay * 1000 );
4715 if ( timestamp_elapsed(shipp->departure_delay) )
4716 mission_do_departure( objp );
4721 // now scan through the list of wings and check their departure cues. For wings with
4722 // that cue being true, we must update internal variables to indicate that the wing is
4723 // departed and that no further waves of this wing will appear
4725 for ( i = 0; i < num_wings; i++ ) {
4728 // should we process this wing anymore
4729 if ( wingp->flags & WF_WING_DEPARTING )
4732 // evaluate the sexpression. If true, mark all the ships in this wing as departing and increment
4733 // the num departed in the wing structure. Then add number of remaining waves * ships/wave to
4734 // departed count to get total count of ships in the wing which departed. (We are counting ships
4735 // that have not yet arrived as departed if they never arrive -- this may be bad, but for some reason
4736 // seems like the right thing to do).
4737 //&& (!timestamp_valid(wingp->departure_delay) || timestamp_elapsed(wingp->departure_delay)) ) {
4739 if ( eval_sexp(wingp->departure_cue) ) {
4740 // if we haven't set up the departure timer yet (would be <= 0) setup the timer to pop N seconds
4742 if ( wingp->departure_delay <= 0 )
4743 wingp->departure_delay = timestamp( -wingp->departure_delay * 1000 );
4744 if ( !timestamp_elapsed(wingp->departure_delay) )
4747 wingp->flags |= WF_WING_DEPARTING;
4748 for ( j = 0; j < wingp->current_count; j++ ) {
4751 shipp = &Ships[wingp->ship_index[j]];
4752 if ( (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_DYING) )
4755 // shipp->flags |= SF_DEPARTING;
4756 // shipp->final_depart_time = timestamp(3*1000);
4758 SDL_assert ( shipp->objnum != -1 );
4759 objp = &Objects[shipp->objnum];
4761 // copy the wing's depature information to the ship
4762 shipp->departure_location = wingp->departure_location;
4763 shipp->departure_anchor = wingp->departure_anchor;
4765 mission_do_departure( objp );
4766 // don't add to wingp->total_departed here -- this is taken care of in ship code.
4769 // MWA 2/25/98 -- don't do the follwoing wing member updates. It makes the accurate counts
4770 // sort of messed up and causes problems for the event log. The code in ship_wing_cleanup()
4771 // now keys off of the WF_WING_DEPARTING flag instead of the counts below.
4774 // now be sure that we update wing structure members if there are any remaining waves left
4775 if ( wingp->current_wave < wingp->num_waves ) {
4778 num_remaining = ( (wingp->num_waves - wingp->current_wave) * wingp->wave_count);
4779 wingp->total_departed += num_remaining;
4780 wingp->total_arrived_count += num_remaining;
4781 wingp->current_wave = wingp->num_waves;
4787 Mission_departure_timestamp = timestamp(DEPARTURE_TIMESTAMP);
4790 // function called from high level game loop to do mission evaluation stuff
4791 void mission_parse_eval_stuff()
4793 mission_eval_arrivals();
4794 mission_eval_departures();
4797 int allocate_subsys_status()
4801 SDL_assert(Subsys_index < MAX_SUBSYS_STATUS);
4802 Subsys_status[Subsys_index].percent = 0.0f;
4803 Subsys_status[Subsys_index].primary_banks[0] = SUBSYS_STATUS_NO_CHANGE;
4804 for (i=1; i<MAX_PRIMARY_BANKS; i++)
4805 Subsys_status[Subsys_index].primary_banks[i] = -1; // none
4807 Subsys_status[Subsys_index].secondary_banks[0] = SUBSYS_STATUS_NO_CHANGE;
4808 Subsys_status[Subsys_index].secondary_ammo[0] = 100;
4809 for (i=1; i<MAX_SECONDARY_BANKS; i++) {
4810 Subsys_status[Subsys_index].secondary_banks[i] = -1;
4811 Subsys_status[Subsys_index].secondary_ammo[i] = 100;
4814 Subsys_status[Subsys_index].ai_class = SUBSYS_STATUS_NO_CHANGE;
4815 return Subsys_index++;
4818 // find (or add) the name in the list and return an index to it.
4819 int get_parse_name_index(const char *name)
4823 for (i=0; i<Num_parse_names; i++)
4824 if (!SDL_strcasecmp(name, Parse_names[i]))
4827 SDL_assert(i < MAX_SHIPS + MAX_WINGS);
4828 SDL_assert(strlen(name) < NAME_LENGTH);
4829 SDL_strlcpy(Parse_names[i], name, NAME_LENGTH);
4830 return Num_parse_names++;
4833 int get_anchor(char *name)
4837 for (i=0; i<MAX_SPECIAL_ARRIVAL_ANCHORS; i++)
4838 if (!SDL_strcasecmp(name, Special_arrival_anchor_names[i]))
4839 return SPECIAL_ARRIVAL_ANCHORS_OFFSET + i;
4841 return get_parse_name_index(name);
4844 // function to fixup the goals/ai references for player objects in the mission
4845 void mission_parse_fixup_players()
4849 // merge created list to have all objects on used list
4850 obj_merge_created_list();
4851 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4852 if ( (objp->type == OBJ_SHIP) && (objp->flags & OF_PLAYER_SHIP) ) {
4853 ai_clear_ship_goals( &Ai_info[Ships[objp->instance].ai_index] );
4854 init_ai_object( OBJ_INDEX(objp) );
4859 // code to warp in a new support ship. It works by finding the average position of all ships
4860 // in the mission, creating a vector from that position to the player, and scaling out behind the
4861 // player some distance. Should be sufficient.
4863 #define WARP_IN_MIN_DISTANCE 1000.0f
4864 #define WARP_IN_TIME_MIN 3000 // warps in min 3 seconds later
4865 #define WARP_IN_TIME_MAX 6000 // warps in max 6 seconds later
4867 // function which adds requester_objp onto the queue of ships for the arriving support ship to service
4868 void mission_add_to_arriving_support( object *requester_objp )
4873 SDL_assert ( Arriving_support_ship );
4875 if ( Num_arriving_repair_targets == MAX_AI_GOALS ) {
4876 // Int3(); // get allender -- ship isn't going to get repair, but I hope they never queue up this far!!!
4877 mprintf(("Reached MAX_AI_GOALS trying to add repair request!\n"));
4881 shipp = &Ships[requester_objp->instance];
4882 // check for duplicates before adding
4883 for (i = 0; i < Num_arriving_repair_targets; i++ ) {
4884 if ( !SDL_strcasecmp(Arriving_repair_targets[i], shipp->ship_name) ){
4888 if ( i != Num_arriving_repair_targets ){ // found the ship before reaching the end -- ignore it!
4892 SDL_strlcpy( Arriving_repair_targets[Num_arriving_repair_targets], Ships[requester_objp->instance].ship_name, NAME_LENGTH );
4893 Num_arriving_repair_targets++;
4895 if ( MULTIPLAYER_MASTER ){
4896 multi_maybe_send_repair_info( requester_objp, NULL, REPAIR_INFO_WARP_ADD );
4900 extern int pp_collide_any(vector *curpos, vector *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag);
4902 // Set the warp in position for a support ship relative to an object.
4903 // Caller tries several positions, passing vector in x, y, z.
4904 int get_warp_in_pos(vector *pos, object *objp, float x, float y, float z)
4908 if ( Game_mode & GM_NORMAL )
4911 rand_val = static_randf(objp->net_signature);
4913 rand_val = 1.0f + (rand_val - 0.5f)*0.2f;
4917 vm_vec_scale_add2( pos, &objp->orient.v.rvec, x*rand_val*800.0f);
4918 vm_vec_scale_add2( pos, &objp->orient.v.uvec, y*rand_val*800.0f);
4919 vm_vec_scale_add2( pos, &objp->orient.v.fvec, z*rand_val*800.0f);
4921 return pp_collide_any(&objp->pos, pos, objp->radius, objp, NULL, 1);
4924 void mission_warp_in_support_ship( object *requester_objp )
4926 vector center, warp_in_pos;
4929 int i, requester_species;
4930 ship *requester_shipp;
4932 SDL_assert ( requester_objp->type == OBJ_SHIP );
4933 requester_shipp = &Ships[requester_objp->instance]; // MK, 10/23/97, used to be ->type, bogus, no?
4935 // if the support ship is already arriving, add the requester to the list
4936 if ( Arriving_support_ship ) {
4937 mission_add_to_arriving_support( requester_objp );
4941 // get average position of all ships
4942 obj_get_average_ship_pos( ¢er );
4943 vm_vec_sub( &warp_in_pos, ¢er, &(requester_objp->pos) );
4945 // be sure to account for case as player being only ship left in mission
4947 if ( !(IS_VEC_NULL( warp_in_pos)) ) {
4948 mag = vm_vec_mag( &warp_in_pos );
4949 if ( mag < WARP_IN_MIN_DISTANCE )
4950 vm_vec_scale( &warp_in_pos, WARP_IN_MIN_DISTANCE/mag);
4954 // take -player_pos.fvec scaled by 1000.0f;
4955 warp_in_pos = Player_obj->orient.fvec;
4956 vm_vec_scale( &warp_in_pos, -1000.0f );
4960 // Choose position to warp in ship.
4961 // Temporary, but changed by MK because it used to be exactly behind the player.
4962 // This could cause an SDL_assert if the player immediately targeted it (before moving).
4963 // Tend to put in front of the player to aid him in flying towards the ship.
4965 if (!get_warp_in_pos(&warp_in_pos, requester_objp, 1.0f, 0.1f, 1.0f))
4966 if (!get_warp_in_pos(&warp_in_pos, requester_objp, 1.0f, 0.2f, -1.0f))
4967 if (!get_warp_in_pos(&warp_in_pos, requester_objp, -1.0f, -0.2f, -1.0f))
4968 if (!get_warp_in_pos(&warp_in_pos, requester_objp, -1.0f, -0.1f, 1.0f))
4969 get_warp_in_pos(&warp_in_pos, requester_objp, 0.1f, 1.0f, 0.2f);
4971 // create a parse object, and put it onto the ship_arrival_list. This whole thing kind of sucks.
4972 // I want to put it into a parse object since it needs to arrive just a little later than
4973 // this function is called. I have to make some assumptions in the code about values for the parse
4974 // object since I'm no longer working with a mission file. These exceptions will be noted with
4977 Arriving_support_ship = &Support_ship_pobj;
4978 pobj = Arriving_support_ship;
4980 // create a name for the ship. use "Support #". look for collisions until one isn't found anymore
4983 SDL_snprintf(pobj->name, SDL_arraysize(pobj->name), NOX("Support %d"), i);
4984 if ( (ship_name_lookup(pobj->name) == -1) && (ship_find_exited_ship_by_name(pobj->name) == -1) )
4989 pobj->pos = warp_in_pos;
4990 vm_set_identity( &(pobj->orient) );
4992 // *sigh*. Gotta get the ship class. For now, this will amount to finding a ship in the ship_info
4993 // array with the same team as the requester of type SIF_SUPPORT. Might need to be changed, but who knows
4994 // vasudans use the terran support ship.
4995 requester_species = Ship_info[requester_shipp->ship_info_index].species;
4997 // 5/6/98 -- MWA Don't need to do anything for multiplayer. I think that we always want to use
4998 // the species of the caller ship.
4999 SDL_assert( (requester_species == SPECIES_TERRAN) || (requester_species == SPECIES_VASUDAN) );
5000 // if ( (Game_mode & GM_NORMAL) && (requester_species == SPECIES_VASUDAN) ) { // make vasundan's use the terran support ship
5001 // requester_species = SPECIES_TERRAN;
5004 // get index of correct species support ship
5005 for (i=0; i < Num_ship_types; i++) {
5006 if ( (Ship_info[i].species == requester_species) && (Ship_info[i].flags & SIF_SUPPORT) )
5010 if ( i < Num_ship_types )
5011 pobj->ship_class = i;
5013 Int3(); // BOGUS!!!! gotta figure something out here
5015 pobj->team = requester_shipp->team;
5017 pobj->behavior = AIM_NONE; // ASSUMPTION: the mission file has the string "None" which maps to AIM_NONE
5019 // set the ai_goals to -1. We will put the requester object shipname in repair target array and then take
5020 // care of setting up the goal when creating the ship!!!!
5021 pobj->ai_goals = -1;
5022 Num_arriving_repair_targets = 0;
5023 mission_add_to_arriving_support( requester_objp );
5025 // need to set ship's cargo to nothing. scan the cargo_names array looking for the string nothing.
5026 // add it if not found
5027 for (i = 0; i < Num_cargo; i++ )
5028 if ( !SDL_strcasecmp(Cargo_names[i], NOX("nothing")) )
5031 if ( i == Num_cargo ) {
5032 SDL_strlcpy(Cargo_names[i], NOX("Nothing"), NAME_LENGTH);
5035 pobj->cargo1 = char(i);
5037 pobj->status_count = 0;
5039 pobj->arrival_location = 0; // ASSUMPTION: this is index to arrival_lcation string array for hyperspace!!!!
5040 pobj->arrival_distance = 0;
5041 pobj->arrival_anchor = -1;
5042 pobj->arrival_cue = Locked_sexp_true;
5043 pobj->arrival_delay = timestamp_rand(WARP_IN_TIME_MIN, WARP_IN_TIME_MAX);
5045 pobj->subsys_count = 0; // number of elements used in subsys_status array
5046 pobj->initial_velocity = 100; // start at 100% velocity
5047 pobj->initial_hull = 100; // start at 100% hull
5048 pobj->initial_shields = 100; // and 100% shields
5050 pobj->departure_location = 0; // ASSUMPTION: this is index to departure_lcation string array for hyperspace!!!!
5051 pobj->departure_anchor = -1;
5052 pobj->departure_cue = Locked_sexp_false;
5053 pobj->departure_delay= 0;
5055 pobj->determination = 10; // ASSUMPTION: mission file always had this number written out
5057 if ( Player_obj->flags & P_OF_NO_SHIELDS )
5058 pobj->flags = P_OF_NO_SHIELDS; // support ships have no shields when player has not shields
5060 pobj->ai_class = Ship_info[pobj->ship_class].ai_class;
5064 pobj->docked_with[0] = '\0';
5066 pobj->persona_index = -1;
5067 pobj->net_signature = multi_assign_network_signature(MULTI_SIG_SHIP);
5068 pobj->wing_status_wing_index = -1;
5069 pobj->wing_status_wing_pos = -1;
5070 pobj->respawn_count = 0;
5071 pobj->alt_type_index = -1;
5075 // returns true if a support ship is currently in the process of warping in.
5076 int mission_is_support_ship_arriving()
5078 if ( Arriving_support_ship )
5084 // returns true if the given ship is scheduled to be repaired by the arriving support ship
5085 int mission_is_repair_scheduled( object *objp )
5090 if ( !Arriving_support_ship )
5093 SDL_assert ( objp->type == OBJ_SHIP );
5094 name = Ships[objp->instance].ship_name;
5095 for (i = 0; i < Num_arriving_repair_targets; i++ ) {
5096 if ( !strcmp( name, Arriving_repair_targets[i]) )
5103 // function which removed the given ship from the list of ships that are to get repair
5104 // by arriving support ship
5105 int mission_remove_scheduled_repair( object *objp )
5110 if ( !Arriving_support_ship )
5113 // itereate through the target list looking for this ship name. If not found, we
5114 // can simply return.
5115 SDL_assert ( objp->type == OBJ_SHIP );
5116 name = Ships[objp->instance].ship_name;
5117 for (index = 0; index < Num_arriving_repair_targets; index++ ) {
5118 if ( !strcmp( name, Arriving_repair_targets[index]) )
5121 if ( index == Num_arriving_repair_targets )
5124 // ship is found -- compress the array
5125 for ( i = index; i < Num_arriving_repair_targets - 1; i++ )
5126 SDL_strlcpy( Arriving_repair_targets[i], Arriving_repair_targets[i+1], NAME_LENGTH );
5128 Num_arriving_repair_targets--;
5130 if ( MULTIPLAYER_MASTER )
5131 multi_maybe_send_repair_info( objp, NULL, REPAIR_INFO_WARP_REMOVE );
5136 // alternate name stuff
5137 int mission_parse_lookup_alt(char *name)
5147 for(idx=0; idx<Mission_alt_type_count; idx++){
5148 if(!strcmp(Mission_alt_types[idx], name)){
5157 static int mission_parse_lookup_alt_index_warn = 1;
5158 void mission_parse_lookup_alt_index(int index, char *out, const int max_outlen)
5164 if((index < 0) || (index > Mission_alt_type_count)){
5165 if (mission_parse_lookup_alt_index_warn) {
5166 Warning(LOCATION, "Ship with invalid alt_name. Get a programmer");
5167 mission_parse_lookup_alt_index_warn = 0;
5173 SDL_strlcpy(out, Mission_alt_types[index], max_outlen);
5176 int mission_parse_add_alt(char *name)
5184 if(Mission_alt_type_count < MAX_ALT_TYPE_NAMES){
5186 SDL_strlcpy(Mission_alt_types[Mission_alt_type_count++], name, NAME_LENGTH);
5189 return Mission_alt_type_count - 1;
5195 void mission_parse_reset_alt()
5197 Mission_alt_type_count = 0;
5200 int is_training_mission()
5202 return (The_mission.game_type & MISSION_TYPE_TRAINING);