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.5 2003/05/25 02:30:43 taylor
21 * Revision 1.4 2002/06/17 06:33:09 relnev
22 * ryan's struct patch for gcc 2.95
24 * Revision 1.3 2002/06/09 04:41:22 relnev
25 * added copyright header
27 * Revision 1.2 2002/05/07 03:16:46 theoddone33
28 * The Great Newline Fix
30 * Revision 1.1.1.1 2002/05/03 03:28:09 root
34 * 63 9/12/99 8:09p Dave
35 * Fixed problem where skip-training button would cause mission messages
36 * not to get paged out for the current mission.
38 * 62 9/09/99 3:53a Andsager
39 * Only reposition HUGE ships to center of knossos device on warp in
41 * 61 8/27/99 9:07p Dave
42 * LOD explosions. Improved beam weapon accuracy.
44 * 60 8/26/99 8:51p Dave
45 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
47 * 59 8/25/99 10:06a Jefff
48 * vasudan pilots get a vasudan support ship.
50 * 58 8/24/99 5:27p Andsager
51 * Make subsystems with zero strength before mission blown off. Protect
52 * red alert pilot against dying between orders and jump.
54 * 57 8/18/99 10:07p Johnson
55 * Fix Fred bug in positioning of Knossos device (when trying to warp in
58 * 56 8/18/99 3:57p Andsager
59 * Add warning for invalid alt_name.
61 * 55 8/18/99 3:48p Andsager
62 * Make support ship take default name and not 0th alt_name.
64 * 54 8/16/99 3:53p Andsager
65 * Add special warp in interface in Fred and saving / reading.
67 * 53 8/16/99 2:01p Andsager
68 * Knossos warp-in warp-out.
70 * 52 8/03/99 5:35p Andsager
71 * Dont draw target dot for instructor in training mission
73 * 51 7/30/99 7:01p Dave
74 * Dogfight escort gauge. Fixed up laser rendering in Glide.
76 * 50 7/28/99 1:36p Andsager
77 * Modify cargo1 to include flag CARGO_NO_DEPLETE. Add sexp
78 * cargo-no-deplete (only for BIG / HUGE). Modify ship struct to pack
81 * 49 7/26/99 5:50p Dave
82 * Revised ingame join. Better? We'll see....
84 * 48 7/19/99 3:01p Dave
85 * Fixed icons. Added single transport icon.
87 * 47 7/15/99 9:20a Andsager
88 * FS2_DEMO initial checkin
90 * 46 7/13/99 5:03p Alanl
91 * make sure object sounds get assigned to ships
93 * 45 7/11/99 2:14p Dave
94 * Added Fred names for the new icon types.
96 * 44 7/02/99 4:\31p Dave
97 * Much more sophisticated lightning support.
99 * 43 7/01/99 4:23p Dave
100 * Full support for multiple linked ambient engine sounds. Added "big
103 * 42 6/28/99 4:51p Andsager
104 * Add ship-guardian sexp (does not allow ship to be killed)
106 * 41 6/21/99 1:34p Alanl
109 * 40 6/16/99 10:20a Dave
110 * Added send-message-list sexpression.
112 * 39 6/14/99 2:06p Andsager
113 * Default load brown asteroids
115 * 38 6/10/99 11:06a Andsager
116 * Mission designed selection of asteroid types.
118 * 37 6/03/99 6:37p Dave
119 * More TNT fun. Made perspective bitmaps more flexible.
121 * 36 5/20/99 7:00p Dave
122 * Added alternate type names for ships. Changed swarm missile table
125 * 35 4/26/99 8:49p Dave
126 * Made all pof based nebula stuff full customizable through fred.
128 * 34 4/26/99 12:49p Andsager
129 * Add protect object from beam support to Fred
131 * 33 4/16/99 2:34p Andsager
132 * Second pass on debris fields
134 * 32 4/15/99 5:00p Andsager
135 * Frist pass on Debris field
137 * 31 4/07/99 6:22p Dave
138 * Fred and Freespace support for multiple background bitmaps and suns.
139 * Fixed link errors on all subprojects. Moved encrypt_init() to
140 * cfile_init() and lcl_init(), since its safe to call twice.
142 * 30 3/31/99 9:52a Andsager
143 * generalization for debris field
145 * 29 3/30/99 5:40p Dave
146 * Fixed reinforcements for TvT in multiplayer.
148 * 28 3/29/99 6:17p Dave
149 * More work on demo system. Got just about everything in except for
150 * blowing ships up, secondary weapons and player death/warpout.
152 * 27 3/24/99 6:23p Dave
153 * Make sure we only apply squadron changes to the player in single-player
156 * 26 3/24/99 4:05p Dave
157 * Put in support for assigning the player to a specific squadron with a
158 * specific logo. Preliminary work for doing pos/orient checksumming in
159 * multiplayer to reduce bandwidth.
161 * 25 3/01/99 7:39p Dave
162 * Added prioritizing ship respawns. Also fixed respawns in TvT so teams
163 * don't mix respawn points.
165 * 24 2/26/99 6:01p Andsager
166 * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay
168 * 23 2/24/99 2:25p Dave
169 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
170 * bug for dogfight more.
172 * 22 2/23/99 8:11p Dave
173 * Tidied up dogfight mode. Fixed TvT ship type problems for alpha wing.
174 * Small pass over todolist items.
176 * 21 2/17/99 2:10p Dave
177 * First full run of squad war. All freespace and tracker side stuff
180 * 20 2/11/99 3:08p Dave
181 * PXO refresh button. Very preliminary squad war support.
183 * 19 2/11/99 2:15p Andsager
184 * Add ship explosion modification to FRED
186 * 18 2/07/99 8:51p Andsager
187 * Add inner bound to asteroid field. Inner bound tries to stay astroid
188 * free. Wrap when within and don't throw at ships inside.
190 * 17 2/05/99 10:38a Johnson
191 * Fixed improper object array reference.
193 * 16 2/04/99 1:23p Andsager
194 * Apply max spark limit to ships created in mission parse
196 * 15 2/03/99 12:42p Andsager
197 * Add escort priority. Modify ship_flags_dlg to include field. Save and
198 * Load. Add escort priority field to ship.
200 * 14 1/27/99 9:56a Dave
201 * Temporary checkin of beam weapons for Dan to make cool sounds.
203 * 13 1/25/99 5:03a Dave
204 * First run of stealth, AWACS and TAG missile support. New mission type
207 * 12 1/19/99 3:57p Andsager
208 * Round 2 of variables
210 * 11 1/07/99 1:52p Andsager
211 * Initial check in of Sexp_variables
213 * 10 11/14/98 5:32p Dave
214 * Lots of nebula work. Put in ship contrails.
216 * 9 11/12/98 12:13a Dave
217 * Tidied code up for multiplayer test. Put in network support for flak
220 * 8 11/05/98 5:55p Dave
221 * Big pass at reducing #includes
223 * 7 11/05/98 4:18p Dave
224 * First run nebula support. Beefed up localization a bit. Removed all
225 * conditional compiles for foreign versions. Modified mission file
228 * 6 10/29/98 9:23p Dave
229 * Removed minor bug concerning externalization of campaign files.
231 * 5 10/23/98 3:51p Dave
232 * Full support for tstrings.tbl and foreign languages. All that remains
233 * is to make it active in Fred.
235 * 4 10/20/98 10:44a Andsager
236 * Add comment for creating sparks before mission starts
238 * 3 10/07/98 6:27p Dave
239 * Globalized mission and campaign file extensions. Removed Silent Threat
240 * special code. Moved \cache \players and \multidata into the \data
243 * 2 10/07/98 10:53a Dave
246 * 1 10/07/98 10:49a Dave
248 * 503 9/21/98 8:46p Dave
249 * Put in special check in fred for identifying unknown ships.
251 * 502 9/14/98 5:09p Dave
252 * Massive hack to always ignore m-clash
254 * 501 9/14/98 3:40p Allender
255 * better error checking for invalid number of waves for player wings in a
256 * multiplayer game. Better popup message in FreeSpace side.
258 * 500 9/11/98 2:05p Allender
259 * make reinforcements work correctly in multiplayer games. There still
260 * may be a team vs team issue that I haven't thought of yet :-(
262 * 499 8/07/98 9:48a Allender
263 * changed how SF_FROM_PLAYER_WING is assigned for team vs. team games
265 * 498 7/16/98 2:22p Allender
266 * don't do wave check in single player
268 * 497 7/13/98 5:19p Dave
270 * 496 7/13/98 3:15p Allender
271 * don't allow multiple waves for any player wings
273 * 495 7/10/98 12:11a Allender
274 * fixed problem where missions files of 0 length would cause game to
277 * 494 5/21/98 3:31p Allender
278 * fix bug where Ship_obj_list was getting overwritten by the exited ships
281 * 493 5/20/98 1:04p Hoffoss
282 * Made credits screen use new artwork and removed rating field usage from
283 * Fred (a goal struct member).
285 * 492 5/18/98 4:41p Comet
286 * allender: fix problem where ships in wings were not deleted on clients
287 * when a new wing create packet came in. A serious hack of all hacks
289 * 491 5/18/98 3:37p Jasen
290 * move Int3() about too many ships in wing to before where ship actually
293 * 490 5/18/98 1:58a Mike
294 * Make Phoenix not be fired at fighters (but yes bombers).
295 * Improve positioning of ships in guard mode.
296 * Make turrets on player ship not fire near end of support ship docking.
298 * 489 5/15/98 9:52a Allender
299 * added code to give Warning when orders accepted don't match the default
302 * 488 5/13/98 4:41p Mike
303 * Make big ships try a tiny bit to avoid collision with each other when
304 * attacking another big ship. Make ships a little less likely to collide
305 * into player when in formation, drop off if player flying wacky.
307 * 487 5/13/98 3:07p Mitri
308 * fixed problem with checking current count of the mission against max
311 * 486 5/11/98 4:33p Allender
312 * fixed ingame join problems -- started to work on new object updating
313 * code (currently ifdef'ed out)
315 * 485 5/08/98 5:05p Dave
316 * Go to the join game screen when quitting multiplayer. Fixed mission
317 * text chat bugs. Put mission type symbols on the create game list.
318 * Started updating standalone gui controls.
330 #include "freespace.h"
332 #include "missionparse.h"
333 #include "missiongoals.h"
334 #include "missionlog.h"
335 #include "missionmessage.h"
337 #include "linklist.h"
343 #include "starfield.h"
345 #include "lighting.h"
346 #include "eventmusic.h"
347 #include "missionbriefcommon.h"
349 #include "multiutil.h"
350 #include "multimsgs.h"
354 #include "fireballs.h"
356 #include "gamesequence.h"
361 #include "missionhotkey.h"
362 #include "hudescort.h"
363 #include "asteroid.h"
365 #include "staticrand.h"
366 #include "missioncmdbrief.h"
367 #include "redalert.h"
368 #include "multi_respawn.h"
369 #include "hudwingmanstatus.h"
370 #include "jumpnode.h"
371 #include "multi_endgame.h"
372 #include "localize.h"
375 #include "neblightning.h"
380 char dockee[NAME_LENGTH];
381 char docker_point[NAME_LENGTH];
382 char dockee_point[NAME_LENGTH];
383 } Initially_docked[MAX_SHIPS];
385 int Total_initially_docked;
388 char Mission_filename[80];
390 int Mission_palette; // index into Nebula_palette_filenames[] of palette file to use for mission
391 int Nebula_index; // index into Nebula_filenames[] of nebula to use in mission.
392 int Num_iff = MAX_IFF;
393 int Num_ai_behaviors = MAX_AI_BEHAVIORS;
395 int Num_status_names = MAX_STATUS_NAMES;
396 int Num_arrival_names = MAX_ARRIVAL_NAMES;
397 int Num_goal_type_names = MAX_GOAL_TYPE_NAMES;
398 int Num_team_names = MAX_TEAM_NAMES;
400 int Player_starts = 1;
402 fix Entry_delay_time;
404 ushort Current_file_checksum = 0;
405 ushort Last_file_checksum = 0;
406 int Current_file_length = 0;
408 // alternate ship type names
409 char Mission_alt_types[MAX_ALT_TYPE_NAMES][NAME_LENGTH];
410 int Mission_alt_type_count = 0;
412 #define SHIP_WARP_TIME 5.0f // how many seconds it takes for ship to warp in
414 // the ship arrival list will contain a list of ships that are yet to arrive. This
415 // list could also include ships that are part of wings!
417 p_object ship_arrivals[MAX_SHIP_ARRIVALS], ship_arrival_list; // for linked list of ships to arrive later
418 int num_ship_arrivals;
420 #define MAX_SHIP_ORIGINAL 100
421 p_object ship_original[MAX_SHIP_ORIGINAL];
422 int num_ship_original;
424 // list for arriving support ship
425 p_object Support_ship_pobj;
426 p_object *Arriving_support_ship;
427 char Arriving_repair_targets[MAX_AI_GOALS][NAME_LENGTH];
428 int Num_arriving_repair_targets;
430 subsys_status Subsys_status[MAX_SUBSYS_STATUS];
433 char Mission_parse_storm_name[NAME_LENGTH] = "none";
435 team_data Team_data[MAX_TEAMS];
437 // variables for player start in single player
438 char Player_start_shipname[NAME_LENGTH];
439 int Player_start_shipnum;
440 p_object Player_start_pobject;
442 // name of all ships to use while parsing a mission (since a ship might be referenced by
443 // something before that ship has even been loaded yet)
444 char Parse_names[MAX_SHIPS + MAX_WINGS][NAME_LENGTH];
449 char *Nebula_filenames[NUM_NEBULAS] = {
455 char *Neb2_filenames[NUM_NEBULAS] = {
461 // Note: Nebula_colors[] and Nebula_palette_filenames are linked via index numbers
462 char *Nebula_colors[NUM_NEBULA_COLORS] = {
474 char *Iff_names[MAX_IFF] = { {"IFF 1"}, {"IFF 2"}, {"IFF 3"},
477 char *Ai_behavior_names[MAX_AI_BEHAVIORS] = {
501 char *Cargo_names[MAX_CARGO];
502 char Cargo_names_buf[MAX_CARGO][NAME_LENGTH];
504 char *Ship_class_names[MAX_SHIP_TYPES]; // to be filled in from Ship_info array
506 char *Icon_names[MAX_BRIEF_ICONS] = {
507 {"Fighter"}, {"Fighter Wing"}, {"Cargo"}, {"Cargo Wing"}, {"Largeship"},
508 {"Largeship Wing"}, {"Capital"}, {"Planet"}, {"Asteroid Field"}, {"Waypoint"},
509 {"Support Ship"}, {"Freighter(no cargo)"}, {"Freighter(has cargo)"},
510 {"Freighter Wing(no cargo)"}, {"Freighter Wing(has cargo)"}, {"Installation"},
511 {"Bomber"}, {"Bomber Wing"}, {"Cruiser"}, {"Cruiser Wing"}, {"Unknown"}, {"Unknown Wing"},
512 {"Player Fighter"}, {"Player Fighter Wing"}, {"Player Bomber"}, {"Player Bomber Wing"},
516 {"Knossos Device"}, {"Transport Wing"}, {"Corvette"}, {"Gas Miner"}, {"Awacs"}, {"Supercap"}, {"Sentry Gun"}, {"Jump Node"}, {"Transport"}
520 // Translate team mask values like TEAM_FRIENDLY to indices in Team_names array.
521 // -1 means an illegal value.
522 int Team_names_index_xlate[MAX_TEAM_NAMES_INDEX+1] = {-1, 0, 1, -1, 2, -1, -1, -1, 3};
524 char *Team_names[MAX_TEAM_NAMES] = {
525 {"Hostile"}, {"Friendly"}, {"Neutral"}, {"Unknown"},
528 char *Status_desc_names[MAX_STATUS_NAMES] = {
529 {"Shields Critical"}, {"Engines Damaged"}, {"Fully Operational"},
532 char *Status_type_names[MAX_STATUS_NAMES] = {
533 {"Damaged"}, {"Disabled"}, {"Corroded"},
536 char *Status_target_names[MAX_STATUS_NAMES] = {
537 {"Weapons"}, {"Engines"}, {"Cable TV"},
540 // definitions for arrival locations for ships/wings
541 char *Arrival_location_names[MAX_ARRIVAL_NAMES] = {
542 {"Hyperspace"}, {"Near Ship"}, {"In front of ship"}, {"Docking Bay"},
545 char *Special_arrival_anchor_names[MAX_SPECIAL_ARRIVAL_ANCHORS] =
550 "<any friendly player>",
551 "<any hostile player>",
552 "<any neutral player>",
555 char *Departure_location_names[MAX_ARRIVAL_NAMES] = {
556 {"Hyperspace"}, {"Docking Bay"},
559 char *Goal_type_names[MAX_GOAL_TYPE_NAMES] = {
560 {"Primary"}, {"Secondary"}, {"Bonus"},
563 char *Species_names[MAX_SPECIES_NAMES] = {
564 {"Terran"}, {"Vasudan"}, {"Shivan"},
567 char *Reinforcement_type_names[] = {
572 char *Old_game_types[OLD_MAX_GAME_TYPES] = {
573 "Single Player Only",
575 "Single/Multi Player",
579 char *Parse_object_flags[MAX_PARSE_OBJECT_FLAGS] = {
592 "hidden-from-sensors",
602 char *Starting_wing_names[MAX_STARTING_WINGS+1] = {
611 int Num_reinforcement_type_names = sizeof(Reinforcement_type_names) / sizeof(char *);
613 vector Parse_viewer_pos;
614 matrix Parse_viewer_orient;
616 // definitions for timestamps for eval'ing arrival/departure cues
617 int Mission_arrival_timestamp;
618 int Mission_departure_timestamp;
619 fix Mission_end_time;
621 #define ARRIVAL_TIMESTAMP 2000 // every 2 seconds
622 #define DEPARTURE_TIMESTAMP 2200 // every 2.2 seconds -- just to be a little different
624 // calculates a "unique" file signature as a ushort (checksum) and an int (file length)
625 // the amount of The_mission we're going to checksum
626 // WARNING : do NOT call this function on the server - it will overwrite goals, etc
627 #define MISSION_CHECKSUM_SIZE (NAME_LENGTH + NAME_LENGTH + 4 + DATE_TIME_LENGTH + DATE_TIME_LENGTH)
629 // timers used to limit arrival messages and music
630 #define ARRIVAL_MUSIC_MIN_SEPARATION 60000
631 #define ARRIVAL_MESSAGE_MIN_SEPARATION 30000
633 #define ARRIVAL_MESSAGE_DELAY_MIN 2000
634 #define ARRIVAL_MESSAGE_DELAY_MAX 3000
636 static int Allow_arrival_music_timestamp;
637 static int Allow_arrival_message_timestamp;
638 static int Arrival_message_delay_timestamp;
641 static int Allow_arrival_music_timestamp_m[2];
642 static int Allow_arrival_message_timestamp_m[2];
643 static int Arrival_message_delay_timestamp_m[2];
646 void parse_player_info2(mission *pm);
647 void post_process_mission();
648 int allocate_subsys_status();
649 void parse_common_object_data(p_object *objp);
650 void parse_asteroid_fields(mission *pm);
651 int mission_set_arrival_location(int anchor, int location, int distance, int objnum, vector *new_pos, matrix *new_orient);
652 int get_parse_name_index(char *name);
653 int get_anchor(char *name);
654 void mission_parse_do_initial_docks();
655 void mission_parse_set_arrival_locations();
656 void mission_set_wing_arrival_location( wing *wingp, int num_to_set );
657 int parse_lookup_alt_name(char *name);
659 void parse_mission_info(mission *pm)
662 char game_string[NAME_LENGTH];
664 required_string("#Mission Info");
666 required_string("$Version:");
667 stuff_float(&pm->version);
668 if (pm->version != MISSION_VERSION)
669 mprintf(("Older mission, should update it (%.2f<-->%.2f)", pm->version, MISSION_VERSION));
671 required_string("$Name:");
672 stuff_string(pm->name, F_NAME, NULL);
674 required_string("$Author:");
675 stuff_string(pm->author, F_NAME, NULL);
677 required_string("$Created:");
678 stuff_string(pm->created, F_DATE, NULL);
680 required_string("$Modified:");
681 stuff_string(pm->modified, F_DATE, NULL);
683 required_string("$Notes:");
684 stuff_string(pm->notes, F_NOTES, NULL);
686 if (optional_string("$Mission Desc:"))
687 stuff_string(pm->mission_desc, F_MULTITEXT, NULL, MISSION_DESC_LENGTH);
689 strcpy(pm->mission_desc, NOX("No description\n"));
691 pm->game_type = MISSION_TYPE_SINGLE; // default to single player only
692 if ( optional_string("+Game Type:")) {
693 // HACK HACK HACK -- stuff_string was changed to *not* ignore carriage returns. Since the
694 // old style missions may have carriage returns, deal with it here.
695 ignore_white_space();
696 stuff_string(game_string, F_NAME, NULL);
697 for ( i = 0; i < OLD_MAX_GAME_TYPES; i++ ) {
698 if ( !stricmp(game_string, Old_game_types[i]) ) {
700 // this block of code is now old mission compatibility code. We specify game
701 // type in a different manner than before.
702 if ( i == OLD_GAME_TYPE_SINGLE_ONLY )
703 pm->game_type = MISSION_TYPE_SINGLE;
704 else if ( i == OLD_GAME_TYPE_MULTI_ONLY )
705 pm->game_type = MISSION_TYPE_MULTI;
706 else if ( i == OLD_GAME_TYPE_SINGLE_MULTI )
707 pm->game_type = (MISSION_TYPE_SINGLE | MISSION_TYPE_MULTI );
708 else if ( i == OLD_GAME_TYPE_TRAINING )
709 pm->game_type = MISSION_TYPE_TRAINING;
713 if ( pm->game_type & MISSION_TYPE_MULTI )
714 pm->game_type |= MISSION_TYPE_MULTI_COOP;
721 if ( optional_string("+Game Type Flags:") ) {
722 stuff_int(&pm->game_type);
726 if (optional_string("+Flags:")){
727 stuff_int(&pm->flags);
730 // nebula mission stuff
732 if(optional_string("+NebAwacs:")){
733 if(pm->flags & MISSION_FLAG_FULLNEB){
734 stuff_float(&Neb2_awacs);
740 if(optional_string("+Storm:")){
741 stuff_string(Mission_parse_storm_name, F_NAME, NULL);
742 nebl_set_storm(Mission_parse_storm_name);
745 // get the number of players if in a multiplayer mission
747 if ( pm->game_type & MISSION_TYPE_MULTI ) {
748 if ( optional_string("+Num Players:") ) {
749 stuff_int( &(pm->num_players) );
753 // get the number of respawns
754 pm->num_respawns = 0;
755 if ( pm->game_type & MISSION_TYPE_MULTI ) {
756 if ( optional_string("+Num Respawns:") ){
757 stuff_int( (int*)&(pm->num_respawns) );
762 if ( optional_string("+Red Alert:")) {
763 stuff_int(&pm->red_alert);
765 red_alert_set_status(pm->red_alert);
768 if ( optional_string("+Scramble:")) {
769 stuff_int(&pm->scramble);
772 pm->disallow_support = 0;
773 if ( optional_string("+Disallow Support:")) {
774 stuff_int(&pm->disallow_support);
777 if (optional_string("+All Teams Attack")){
778 Mission_all_attack = 1;
780 Mission_all_attack = 0;
783 // Maybe delay the player's entry.
784 if (optional_string("+Player Entry Delay:")) {
788 Assert(temp >= 0.0f);
789 Entry_delay_time = fl2f(temp);
792 if (optional_string("+Viewer pos:")){
793 stuff_vector(&Parse_viewer_pos);
796 if (optional_string("+Viewer orient:")){
797 stuff_matrix(&Parse_viewer_orient);
800 // possible squadron reassignment
801 strcpy(The_mission.squad_name, "");
802 strcpy(The_mission.squad_filename, "");
803 if(optional_string("+SquadReassignName:")){
804 stuff_string(The_mission.squad_name, F_NAME, NULL);
805 if(optional_string("+SquadReassignLogo:")){
806 stuff_string(The_mission.squad_filename, F_NAME, NULL);
809 // always clear out squad reassignments if not single player
810 if(Game_mode & GM_MULTIPLAYER){
811 strcpy(The_mission.squad_name, "");
812 strcpy(The_mission.squad_filename, "");
813 mprintf(("Ignoring squadron reassignment"));
815 // reassign the player
817 if(!Fred_running && (Player != NULL) && (strlen(The_mission.squad_name) > 0) && (Game_mode & GM_CAMPAIGN_MODE)){
818 mprintf(("Reassigning player to squadron %s\n", The_mission.squad_name));
819 player_set_squad(Player, The_mission.squad_name);
820 player_set_squad_bitmap(Player, The_mission.squad_filename);
824 // set up the Num_teams variable accoriding to the game_type variable'
825 Num_teams = 1; // assume 1
827 // multiplayer team v. team games have two teams. If we have three teams, we need to use
828 // a new mission mode!
829 if ( (pm->game_type & MISSION_TYPE_MULTI) && (pm->game_type & MISSION_TYPE_MULTI_TEAMS) ){
834 void parse_player_info(mission *pm)
836 char alt[NAME_LENGTH + 2] = "";
839 // alternate type names begin here
840 mission_parse_reset_alt();
841 if(optional_string("#Alternate Types:")){
843 while(!optional_string("#end")){
844 required_string("$Alt:");
845 stuff_string(alt, F_NAME, NULL, NAME_LENGTH);
848 mission_parse_add_alt(alt);
853 required_string("#Players");
855 while (required_string_either("#Objects", "$")){
856 parse_player_info2(pm);
860 void parse_player_info2(mission *pm)
862 char str[NAME_LENGTH];
863 int nt, i, total, list[MAX_SHIP_TYPES * 2], list2[MAX_WEAPON_TYPES * 2], num_starting_wings;
865 char starting_wings[MAX_PLAYER_WINGS][NAME_LENGTH];
867 // read in a ship/weapon pool for each team.
868 for ( nt = 0; nt < Num_teams; nt++ ) {
869 int num_ship_choices;
871 ptr = &Team_data[nt];
872 // get the shipname for single player missions
873 // MWA -- make this required later!!!!
874 if ( optional_string("$Starting Shipname:") )
875 stuff_string( Player_start_shipname, F_NAME, NULL );
877 required_string("$Ship Choices:");
878 total = stuff_int_list(list, MAX_SHIP_TYPES * 2, SHIP_INFO_TYPE);
880 Assert(!(total & 0x01)); // make sure we have an even count
882 num_ship_choices = 0;
883 total /= 2; // there are only 1/2 the ships really on the list.
884 for (i=0; i<total; i++) {
885 // in a campaign, see if the player is allowed the ships or not. Remove them from the
886 // pool if they are not allowed
887 if (Game_mode & GM_CAMPAIGN_MODE || ((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER))) {
888 if ( !Campaign.ships_allowed[list[i*2]] )
892 ptr->ship_list[num_ship_choices] = list[i * 2];
893 ptr->ship_count[num_ship_choices] = list[i * 2 + 1];
896 ptr->number_choices = num_ship_choices;
898 num_starting_wings = 0;
899 if (optional_string("+Starting Wings:"))
900 num_starting_wings = stuff_string_list(starting_wings, MAX_PLAYER_WINGS);
902 ptr->default_ship = -1;
903 if (optional_string("+Default_ship:")) {
904 stuff_string(str, F_NAME, NULL);
905 ptr->default_ship = ship_info_lookup(str);
906 // see if the player's default ship is an allowable ship (campaign only). If not, then what
907 // do we do? choose the first allowable one?
908 if (Game_mode & GM_CAMPAIGN_MODE || ((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER))) {
909 if ( !(Campaign.ships_allowed[ptr->default_ship]) ) {
910 for (i = 0; i < MAX_SHIP_TYPES; i++ ) {
911 if ( Campaign.ships_allowed[ptr->default_ship] ) {
912 ptr->default_ship = i;
916 Assert( i < MAX_SHIP_TYPES );
921 if (ptr->default_ship == -1) // invalid or not specified, make first in list
922 ptr->default_ship = ptr->ship_list[0];
924 for (i=0; i<MAX_WEAPON_TYPES; i++)
925 ptr->weaponry_pool[i] = 0;
927 if (optional_string("+Weaponry Pool:")) {
928 total = stuff_int_list(list2, MAX_WEAPON_TYPES * 2, WEAPON_POOL_TYPE);
930 Assert(!(total & 0x01)); // make sure we have an even count
932 for (i=0; i<total; i++) {
933 // in a campaign, see if the player is allowed the weapons or not. Remove them from the
934 // pool if they are not allowed
935 if (Game_mode & GM_CAMPAIGN_MODE || ((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER))) {
936 if ( !Campaign.weapons_allowed[list2[i*2]] )
940 if ((list2[i * 2] >= 0) && (list2[i * 2] < MAX_WEAPON_TYPES))
941 ptr->weaponry_pool[list2[i * 2]] = list2[i * 2 + 1];
946 if ( nt != Num_teams )
947 Error(LOCATION, "Not enough ship/weapon pools for mission. There are %d teams and only %d pools.", Num_teams, nt);
950 void parse_plot_info(mission *pm)
952 required_string("#Plot Info");
954 required_string("$Tour:");
955 stuff_string(pm->tour_name, F_NAME, NULL);
957 required_string("$Pre-Briefing Cutscene:");
958 stuff_string(pm->pre_briefing_cutscene, F_FILESPEC, NULL);
960 required_string("$Pre-Mission Cutscene:");
961 stuff_string(pm->pre_mission_cutscene, F_FILESPEC, NULL);
963 required_string("$Next Mission Success:");
964 stuff_string(pm->next_mission_success, F_NAME, NULL);
966 required_string("$Next Mission Partial:");
967 stuff_string(pm->next_mission_partial, F_NAME, NULL);
969 required_string("$Next Mission Failure:");
970 stuff_string(pm->next_mission_failure, F_NAME, NULL);
973 void parse_briefing_info(mission *pm)
977 if ( !optional_string("#Briefing Info") )
980 required_string("$Briefing Voice 1:");
981 stuff_string(junk, F_FILESPEC, NULL);
983 required_string("$Briefing Text 1:");
984 stuff_string(junk, F_MULTITEXTOLD, NULL);
986 required_string("$Briefing Voice 2:");
987 stuff_string(junk, F_FILESPEC, NULL);
989 required_string("$Briefing Text 2:");
990 stuff_string(junk, F_MULTITEXTOLD, NULL);
992 required_string("$Briefing Voice 3:");
993 stuff_string(junk, F_FILESPEC, NULL);
995 required_string("$Briefing Text 3:");
996 stuff_string(junk, F_MULTITEXTOLD, NULL);
998 required_string("$Debriefing Voice 1:");
999 stuff_string(junk, F_FILESPEC, NULL);
1001 required_string("$Debriefing Text 1:");
1002 stuff_string(junk, F_MULTITEXTOLD, NULL);
1004 required_string("$Debriefing Voice 2:");
1005 stuff_string(junk, F_FILESPEC, NULL);
1007 required_string("$Debriefing Text 2:");
1008 stuff_string(junk, F_MULTITEXTOLD, NULL);
1010 required_string("$Debriefing Voice 3:");
1011 stuff_string(junk, F_FILESPEC, NULL);
1013 required_string("$Debriefing Text 3:");
1014 stuff_string(junk, F_MULTITEXTOLD, NULL);
1017 // parse the event music and briefing music for the mission
1018 void parse_music(mission *pm)
1020 char name[NAME_LENGTH];
1022 event_music_reset_choices();
1024 if ( !optional_string("#Music") ) {
1028 required_string("$Event Music:");
1029 stuff_string(name, F_NAME, NULL);
1030 event_music_set_soundtrack(name);
1032 required_string("$Briefing Music:");
1033 stuff_string(name, F_NAME, NULL);
1034 event_music_set_score(SCORE_BRIEFING, name);
1036 if ( optional_string("$Debriefing Success Music:") ) {
1037 stuff_string(name, F_NAME, NULL);
1038 event_music_set_score(SCORE_DEBRIEF_SUCCESS, name);
1041 if ( optional_string("$Debriefing Fail Music:") ) {
1042 stuff_string(name, F_NAME, NULL);
1043 event_music_set_score(SCORE_DEBRIEF_FAIL, name);
1047 void parse_cmd_brief(mission *pm)
1051 Assert(!Cur_cmd_brief->num_stages);
1054 required_string("#Command Briefing");
1055 while (optional_string("$Stage Text:")) {
1056 Assert(stage < CMD_BRIEF_STAGES_MAX);
1057 Cur_cmd_brief->stage[stage].text = stuff_and_malloc_string(F_MULTITEXT, NULL, CMD_BRIEF_TEXT_MAX);
1058 Assert(Cur_cmd_brief->stage[stage].text);
1060 required_string("$Ani Filename:");
1061 stuff_string(Cur_cmd_brief->stage[stage].ani_filename, F_FILESPEC, NULL);
1062 if (optional_string("+Wave Filename:"))
1063 stuff_string(Cur_cmd_brief->stage[stage].wave_filename, F_FILESPEC, NULL);
1065 Cur_cmd_brief->stage[stage].wave_filename[0] = 0;
1070 Cur_cmd_brief->num_stages = stage;
1073 void parse_cmd_briefs(mission *pm)
1078 // a hack follows because old missions don't have a command briefing
1079 if (required_string_either("#Command Briefing", "#Briefing"))
1082 for (i=0; i<Num_teams; i++) {
1083 Cur_cmd_brief = &Cmd_briefs[i];
1084 parse_cmd_brief(pm);
1088 // -------------------------------------------------------------------------------------------------
1091 // Parse the data required for the mission briefing
1093 // NOTE: This updates the global Briefing struct with all the data necessary to drive the briefing
1095 void parse_briefing(mission *pm)
1097 int nt, i, j, stage_num = 0, icon_num = 0, team_index;
1102 char not_used_text[MAX_ICON_TEXT_LEN];
1106 // MWA -- 2/3/98. we can now have multiple briefing and debreifings in a mission
1107 for ( nt = 0; nt < Num_teams; nt++ ) {
1108 if ( !optional_string("#Briefing") )
1111 bp = &Briefings[nt];
1113 required_string("$start_briefing");
1114 required_string("$num_stages:");
1115 stuff_int(&bp->num_stages);
1116 Assert(bp->num_stages <= MAX_BRIEF_STAGES);
1119 while (required_string_either("$end_briefing", "$start_stage")) {
1120 required_string("$start_stage");
1121 Assert(stage_num < MAX_BRIEF_STAGES);
1122 bs = &bp->stages[stage_num++];
1123 required_string("$multi_text");
1124 if ( Fred_running ) {
1125 stuff_string(bs->new_text, F_MULTITEXT, NULL, MAX_BRIEF_LEN);
1127 bs->new_text = stuff_and_malloc_string(F_MULTITEXT, NULL, MAX_BRIEF_LEN);
1129 required_string("$voice:");
1130 stuff_string(bs->voice, F_FILESPEC, NULL);
1131 required_string("$camera_pos:");
1132 stuff_vector(&bs->camera_pos);
1133 required_string("$camera_orient:");
1134 stuff_matrix(&bs->camera_orient);
1135 required_string("$camera_time:");
1136 stuff_int(&bs->camera_time);
1138 if ( optional_string("$num_lines:") ) {
1139 stuff_int(&bs->num_lines);
1141 if ( Fred_running ) {
1142 Assert(bs->lines!=NULL);
1144 if ( bs->num_lines > 0 ) {
1145 bs->lines = (brief_line *)malloc(sizeof(brief_line)*bs->num_lines);
1146 Assert(bs->lines!=NULL);
1150 for (i=0; i<bs->num_lines; i++) {
1151 required_string("$line_start:");
1152 stuff_int(&bs->lines[i].start_icon);
1153 required_string("$line_end:");
1154 stuff_int(&bs->lines[i].end_icon);
1161 required_string("$num_icons:");
1162 stuff_int(&bs->num_icons);
1164 if ( Fred_running ) {
1165 Assert(bs->lines!=NULL);
1167 if ( bs->num_icons > 0 ) {
1168 bs->icons = (brief_icon *)malloc(sizeof(brief_icon)*bs->num_icons);
1169 Assert(bs->icons!=NULL);
1173 if ( optional_string("$flags:") )
1174 stuff_int(&bs->flags);
1178 if ( optional_string("$formula:") )
1179 bs->formula = get_sexp_main();
1181 bs->formula = Locked_sexp_true;
1183 Assert(bs->num_icons <= MAX_STAGE_ICONS );
1185 while (required_string_either("$end_stage", "$start_icon")) {
1186 required_string("$start_icon");
1187 Assert(icon_num < MAX_STAGE_ICONS);
1188 bi = &bs->icons[icon_num++];
1190 required_string("$type:");
1191 stuff_int(&bi->type);
1193 find_and_stuff("$team:", &team_index, F_NAME, Team_names, Num_team_names, "team name");
1194 Assert((team_index >= 0) && (team_index < MAX_TEAM_NAMES));
1195 bi->team = 1 << team_index;
1197 find_and_stuff("$class:", &bi->ship_class, F_NAME, Ship_class_names, Num_ship_types, "ship class");
1199 required_string("$pos:");
1200 stuff_vector(&bi->pos);
1203 if (optional_string("$label:"))
1204 stuff_string(bi->label, F_MESSAGE, NULL);
1206 if (optional_string("+id:")) {
1208 if (bi->id >= Cur_brief_id)
1209 Cur_brief_id = bi->id + 1;
1213 for (i=0; i<stage_num-1; i++)
1214 for (j=0; j < bp->stages[i].num_icons; j++)
1216 if (!stricmp(bp->stages[i].icons[j].label, bi->label))
1217 bi->id = bp->stages[i].icons[j].id;
1221 bi->id = Cur_brief_id++;
1224 required_string("$hlight:");
1228 bi->flags = BI_HIGHLIGHT;
1233 required_string("$multi_text");
1234 // stuff_string(bi->text, F_MULTITEXT, NULL, MAX_ICON_TEXT_LEN);
1235 stuff_string(not_used_text, F_MULTITEXT, NULL, MAX_ICON_TEXT_LEN);
1236 required_string("$end_icon");
1238 Assert(bs->num_icons == icon_num);
1240 required_string("$end_stage");
1243 Assert(bp->num_stages == stage_num);
1244 required_string("$end_briefing");
1247 if ( nt != Num_teams )
1248 Error(LOCATION, "Not enough briefings in mission file. There are %d teams and only %d briefings.", Num_teams, nt );
1251 // -------------------------------------------------------------------------------------------------
1252 // parse_debriefing_old()
1254 // Parse the data required for the mission debriefings
1255 void parse_debriefing_old(mission *pm)
1258 char waste[MAX_DEBRIEF_LEN];
1260 if ( !optional_string("#Debriefing") )
1263 required_string("$num_debriefings:");
1266 while (required_string_either("#Players", "$start_debriefing")) {
1267 required_string("$start_debriefing");
1268 required_string("$formula:");
1269 junk = get_sexp_main();
1270 required_string("$num_stages:");
1272 while (required_string_either("$end_debriefing", "$start_stage")) {
1273 required_string("$start_stage");
1274 required_string("$multi_text");
1275 stuff_string(waste, F_MULTITEXT, NULL, MAX_DEBRIEF_LEN);
1276 required_string("$voice:");
1277 stuff_string(waste, F_FILESPEC, NULL);
1278 required_string("$end_stage");
1280 required_string("$end_debriefing");
1284 // -------------------------------------------------------------------------------------------------
1285 // parse_debriefing_new()
1287 // Parse the data required for the mission debriefings
1288 void parse_debriefing_new(mission *pm)
1296 // next code should be old -- hopefully not called anymore
1297 //if (!optional_string("#Debriefing_info")) {
1298 // parse_debriefing_old(pm);
1302 // 2/3/98 -- MWA. We can now have multiple briefings and debriefings on a team
1303 for ( nt = 0; nt < Num_teams; nt++ ) {
1305 if ( !optional_string("#Debriefing_info") )
1310 db = &Debriefings[nt];
1312 required_string("$Num stages:");
1313 stuff_int(&db->num_stages);
1314 Assert(db->num_stages <= MAX_DEBRIEF_STAGES);
1316 while (required_string_either("#", "$Formula")) {
1317 Assert(stage_num < MAX_DEBRIEF_STAGES);
1318 dbs = &db->stages[stage_num++];
1319 required_string("$Formula:");
1320 dbs->formula = get_sexp_main();
1321 required_string("$multi text");
1322 if ( Fred_running ) {
1323 stuff_string(dbs->new_text, F_MULTITEXT, NULL, MAX_DEBRIEF_LEN);
1325 dbs->new_text = stuff_and_malloc_string(F_MULTITEXT, NULL, MAX_DEBRIEF_LEN);
1327 required_string("$Voice:");
1328 stuff_string(dbs->voice, F_FILESPEC, NULL);
1329 required_string("$Recommendation text:");
1330 if ( Fred_running ) {
1331 stuff_string( dbs->new_recommendation_text, F_MULTITEXT, NULL, MAX_RECOMMENDATION_LEN);
1333 dbs->new_recommendation_text = stuff_and_malloc_string( F_MULTITEXT, NULL, MAX_RECOMMENDATION_LEN);
1337 Assert(db->num_stages == stage_num);
1340 if ( nt != Num_teams )
1341 Error(LOCATION, "Not enough debriefings for mission. There are %d teams and only %d debriefings;\n", Num_teams, nt );
1344 void position_ship_for_knossos_warpin(p_object *objp, int shipnum, int objnum)
1346 // Assume no valid knossos device
1347 Ships[shipnum].special_warp_objnum = -1;
1349 // find knossos device
1352 int knossos_num = -1;
1353 for (so=GET_FIRST(&Ship_obj_list); so!=END_OF_LIST(&Ship_obj_list); so=GET_NEXT(so)) {
1354 knossos_num = Objects[so->objnum].instance;
1355 if (Ship_info[Ships[knossos_num].ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
1356 // be close to the right device [allow multiple knossos's
1357 if (vm_vec_dist_quick(&Objects[knossos_num].pos, &objp->pos) < 2.0f*(Objects[knossos_num].radius + Objects[objnum].radius) ) {
1365 // set ship special_warp_objnum
1366 Ships[shipnum].special_warp_objnum = knossos_num;
1368 // position self for warp on plane of device
1370 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);
1371 polymodel *pm = model_get(Ship_info[Ships[shipnum].ship_info_index].modelnum);
1372 float desired_dist = -pm->mins.xyz.z;
1373 vm_vec_scale_add2(&Objects[objnum].pos, &Objects[objnum].orient.v.fvec, (dist - desired_dist));
1374 // if ship is BIG or HUGE, make it go through the center of the knossos
1375 if (Ship_info[Ships[shipnum].ship_info_index].flags & SIF_HUGE_SHIP) {
1377 vm_vec_sub(&offset, &Objects[knossos_num].pos, &new_point);
1378 vm_vec_add2(&Objects[objnum].pos, &offset);
1383 // Given a stuffed p_object struct, create an object and fill in the necessary fields.
1384 // Return object number.
1385 int parse_create_object(p_object *objp)
1387 int i, j, k, objnum, shipnum;
1391 subsys_status *sssp;
1394 // base level creation
1395 objnum = ship_create(&objp->orient, &objp->pos, objp->ship_class);
1396 Assert(objnum != -1);
1397 shipnum = Objects[objnum].instance;
1399 // if arriving through knossos, adjust objpj->pos to plane of knossos and set flag
1400 // special warp is single player only
1401 if ((objp->flags & P_KNOSSOS_WARP_IN) && !(Game_mode & GM_MULTIPLAYER)) {
1402 if (!Fred_running) {
1403 position_ship_for_knossos_warpin(objp, shipnum, objnum);
1407 Ships[shipnum].group = objp->group;
1408 Ships[shipnum].team = objp->team;
1409 strcpy(Ships[shipnum].ship_name, objp->name);
1410 Ships[shipnum].escort_priority = objp->escort_priority;
1411 Ships[shipnum].special_exp_index = objp->special_exp_index;
1412 Ships[shipnum].respawn_priority = objp->respawn_priority;
1413 // if this is a multiplayer dogfight game, and its from a player wing, make it team traitor
1414 if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT) && (objp->wingnum >= 0)){
1415 for (i = 0; i < MAX_STARTING_WINGS; i++ ) {
1416 if ( !stricmp(Starting_wing_names[i], Wings[objp->wingnum].name) ) {
1417 Ships[shipnum].team = TEAM_TRAITOR;
1422 sip = &Ship_info[Ships[shipnum].ship_info_index];
1424 if ( !Fred_running ) {
1425 ship_assign_sound(&Ships[shipnum]);
1428 aip = &(Ai_info[Ships[shipnum].ai_index]);
1429 aip->behavior = objp->behavior;
1430 aip->mode = aip->behavior;
1432 // alternate type name
1433 Ships[shipnum].alt_type_index = objp->alt_type_index;
1435 aip->ai_class = objp->ai_class;
1436 Ships[shipnum].weapons.ai_class = objp->ai_class; // Fred uses this instead of above.
1438 // must reset the number of ai goals when the object is created
1439 for (i = 0; i < MAX_AI_GOALS; i++ ){
1440 aip->goals[i].ai_mode = AI_GOAL_NONE;
1443 Ships[shipnum].cargo1 = objp->cargo1;
1445 Ships[shipnum].arrival_location = objp->arrival_location;
1446 Ships[shipnum].arrival_distance = objp->arrival_distance;
1447 Ships[shipnum].arrival_anchor = objp->arrival_anchor;
1448 Ships[shipnum].arrival_cue = objp->arrival_cue;
1449 Ships[shipnum].arrival_delay = objp->arrival_delay;
1450 Ships[shipnum].departure_location = objp->departure_location;
1451 Ships[shipnum].departure_anchor = objp->departure_anchor;
1452 Ships[shipnum].departure_cue = objp->departure_cue;
1453 Ships[shipnum].departure_delay = objp->departure_delay;
1454 Ships[shipnum].determination = objp->determination;
1455 Ships[shipnum].wingnum = objp->wingnum;
1456 Ships[shipnum].hotkey = objp->hotkey;
1457 Ships[shipnum].score = objp->score;
1458 Ships[shipnum].persona_index = objp->persona_index;
1460 // set the orders that this ship will accept. It will have already been set to default from the
1461 // ship create code, so only set them if the parse object flags say they are unique
1462 if ( objp->flags & P_SF_USE_UNIQUE_ORDERS ) {
1463 Ships[shipnum].orders_accepted = objp->orders_accepted;
1465 // MWA 5/15/98 -- Added the following debug code because some orders that ships
1466 // will accept were apparently written out incorrectly with Fred. This Int3() should
1467 // trap these instances.
1469 if ( Fred_running ) {
1470 int default_orders, remaining_orders;
1472 default_orders = ship_get_default_orders_accepted( &Ship_info[Ships[shipnum].ship_info_index] );
1473 remaining_orders = objp->orders_accepted & ~default_orders;
1474 if ( remaining_orders ) {
1475 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);
1481 // check the parse object's flags for possible flags to set on this newly created ship
1482 if ( objp->flags & P_OF_PROTECTED ) {
1483 Objects[objnum].flags |= OF_PROTECTED;
1486 if ( objp->flags & P_OF_BEAM_PROTECTED ) {
1487 Objects[objnum].flags |= OF_BEAM_PROTECTED;
1490 if (objp->flags & P_OF_CARGO_KNOWN) {
1491 Ships[shipnum].flags |= SF_CARGO_REVEALED;
1494 if ( objp->flags & P_SF_IGNORE_COUNT )
1495 Ships[shipnum].flags |= SF_IGNORE_COUNT;
1497 if ( objp->flags & P_SF_REINFORCEMENT )
1498 Ships[shipnum].flags |= SF_REINFORCEMENT;
1500 if (objp->flags & P_OF_NO_SHIELDS || sip->shields == 0 )
1501 Objects[objnum].flags |= OF_NO_SHIELDS;
1503 if (objp->flags & P_SF_ESCORT)
1504 Ships[shipnum].flags |= SF_ESCORT;
1506 if (objp->flags & P_KNOSSOS_WARP_IN) {
1507 Objects[objnum].flags |= OF_SPECIAL_WARP;
1510 // don't set the flag if the mission is ongoing in a multiplayer situation. This will be set by the players in the
1511 // game only before the game or during respawning.
1512 // MWA -- changed the next line to remove the !(Game_mode & GM_MULTIPLAYER). We shouldn't be setting
1513 // this flag in single player mode -- it gets set in post process mission.
1514 //if ((objp->flags & P_OF_PLAYER_START) && (((Game_mode & GM_MULTIPLAYER) && !(Game_mode & GM_IN_MISSION)) || !(Game_mode & GM_MULTIPLAYER)))
1515 if ( (objp->flags & P_OF_PLAYER_START) && (Fred_running || ((Game_mode & GM_MULTIPLAYER) && !(Game_mode & GM_IN_MISSION))) )
1516 Objects[objnum].flags |= OF_PLAYER_SHIP;
1518 if (objp->flags & P_SF_NO_ARRIVAL_MUSIC)
1519 Ships[shipnum].flags |= SF_NO_ARRIVAL_MUSIC;
1521 if ( objp->flags & P_SF_NO_ARRIVAL_WARP )
1522 Ships[shipnum].flags |= SF_NO_ARRIVAL_WARP;
1524 if ( objp->flags & P_SF_NO_DEPARTURE_WARP )
1525 Ships[shipnum].flags |= SF_NO_DEPARTURE_WARP;
1527 if ( objp->flags & P_SF_INITIALLY_DOCKED )
1528 Ships[shipnum].flags |= SF_INITIALLY_DOCKED;
1530 if ( objp->flags & P_SF_LOCKED )
1531 Ships[shipnum].flags |= SF_LOCKED;
1533 if ( objp->flags & P_SF_WARP_BROKEN )
1534 Ships[shipnum].flags |= SF_WARP_BROKEN;
1536 if ( objp->flags & P_SF_WARP_NEVER )
1537 Ships[shipnum].flags |= SF_WARP_NEVER;
1539 if ( objp->flags & P_SF_HIDDEN_FROM_SENSORS )
1540 Ships[shipnum].flags |= SF_HIDDEN_FROM_SENSORS;
1542 // if ship is in a wing, and the wing's no_warp_effect flag is set, then set the equivalent
1543 // flag for the ship
1544 if ( (Ships[shipnum].wingnum != -1) && (Wings[Ships[shipnum].wingnum].flags & WF_NO_ARRIVAL_WARP) )
1545 Ships[shipnum].flags |= SF_NO_ARRIVAL_WARP;
1547 if ( (Ships[shipnum].wingnum != -1) && (Wings[Ships[shipnum].wingnum].flags & WF_NO_DEPARTURE_WARP) )
1548 Ships[shipnum].flags |= SF_NO_DEPARTURE_WARP;
1550 // mwa -- 1/30/98. Do both flags. Fred uses the ship flag, and FreeSpace will use the object
1551 // flag. I'm to lazy at this point to deal with consolidating them.
1552 if ( objp->flags & P_SF_INVULNERABLE ) {
1553 Ships[shipnum].flags |= SF_INVULNERABLE;
1554 Objects[objnum].flags |= OF_INVULNERABLE;
1557 if ( objp->flags & P_SF_GUARDIAN ) {
1558 Objects[objnum].flags |= OF_GUARDIAN;
1561 if ( objp->flags & P_SF_SCANNABLE )
1562 Ships[shipnum].flags |= SF_SCANNABLE;
1564 if ( objp->flags & P_SF_RED_ALERT_STORE_STATUS ){
1565 Assert(!(Game_mode & GM_MULTIPLAYER));
1566 Ships[shipnum].flags |= SF_RED_ALERT_STORE_STATUS;
1569 // a couple of ai_info flags. Also, do a reasonable default for the kamikaze damage regardless of
1570 // whether this flag is set or not
1571 if ( objp->flags & P_AIF_KAMIKAZE ) {
1572 Ai_info[Ships[shipnum].ai_index].ai_flags |= AIF_KAMIKAZE;
1573 Ai_info[Ships[shipnum].ai_index].kamikaze_damage = objp->kamikaze_damage;
1576 if ( objp->flags & P_AIF_NO_DYNAMIC )
1577 Ai_info[Ships[shipnum].ai_index].ai_flags |= AIF_NO_DYNAMIC;
1579 // if the wing index and wing pos are set for this parse object, set them for the ship. This
1580 // is useful in multiplayer when ships respawn
1581 Ships[shipnum].wing_status_wing_index = objp->wing_status_wing_index;
1582 Ships[shipnum].wing_status_wing_pos = objp->wing_status_wing_pos;
1584 // set up the ai_goals for this object -- all ships created here are AI controlled.
1585 if ( objp->ai_goals != -1 ) {
1588 for ( sexp = CDR(objp->ai_goals); sexp != -1; sexp = CDR(sexp) )
1589 // make a call to the routine in MissionGoals.cpp to set up the ai goals for this object.
1590 ai_add_ship_goal_sexp( sexp, AIG_TYPE_EVENT_SHIP, aip );
1592 if ( objp->wingnum == -1 ) // free the sexpression nodes only for non-wing ships. wing code will handle it's own case
1593 free_sexp2(objp->ai_goals); // free up sexp nodes for reused, since they aren't needed anymore.
1596 Assert(Ships[shipnum].modelnum != -1);
1598 // initialize subsystem statii here. The subsystems are given a percentage damaged. So a percent value
1599 // of 20% means that the subsystem is 20% damaged (*not* 20% of max hits). This is opposite the way
1600 // that the initial velocity/hull strength/shields work
1601 i = objp->subsys_count;
1603 sssp = &Subsys_status[objp->subsys_index + i];
1604 if (!stricmp(sssp->name, NOX("Pilot"))) {
1605 wp = &Ships[shipnum].weapons;
1606 if (sssp->primary_banks[0] != SUBSYS_STATUS_NO_CHANGE) {
1607 for (j=k=0; j<MAX_PRIMARY_BANKS; j++) {
1608 if ( (sssp->primary_banks[j] >= 0) || Fred_running ){
1609 wp->primary_bank_weapons[k] = sssp->primary_banks[j];
1617 wp->num_primary_banks = sip->num_primary_banks;
1619 wp->num_primary_banks = k;
1623 if (sssp->secondary_banks[0] != SUBSYS_STATUS_NO_CHANGE) {
1624 for (j=k=0; j<MAX_SECONDARY_BANKS; j++) {
1625 if ( (sssp->secondary_banks[j] >= 0) || Fred_running ){
1626 wp->secondary_bank_weapons[k++] = sssp->secondary_banks[j];
1631 wp->num_secondary_banks = sip->num_secondary_banks;
1633 wp->num_secondary_banks = k;
1637 for (j=0; j < wp->num_secondary_banks; j++)
1639 wp->secondary_bank_ammo[j] = sssp->secondary_ammo[j];
1641 int capacity = fl2i(sssp->secondary_ammo[j]/100.0f * sip->secondary_bank_ammo_capacity[j] + 0.5f );
1642 wp->secondary_bank_ammo[j] = fl2i(capacity / Weapon_info[wp->secondary_bank_weapons[j]].cargo_size + 0.5f);
1647 ptr = GET_FIRST(&Ships[shipnum].subsys_list);
1648 while (ptr != END_OF_LIST(&Ships[shipnum].subsys_list)) {
1649 if (!stricmp(ptr->system_info->subobj_name, sssp->name)) {
1651 ptr->current_hits = sssp->percent;
1654 new_hits = ptr->system_info->max_hits * (100.0f - sssp->percent) / 100.f;
1655 Ships[shipnum].subsys_info[ptr->system_info->type].current_hits -= (ptr->system_info->max_hits - new_hits);
1656 if ( (100.0f - sssp->percent) < 0.5) {
1657 ptr->current_hits = 0.0f;
1658 ptr->submodel_info_1.blown_off = 1;
1660 ptr->current_hits = new_hits;
1664 if (sssp->primary_banks[0] != SUBSYS_STATUS_NO_CHANGE)
1665 for (j=0; j<MAX_PRIMARY_BANKS; j++)
1666 ptr->weapons.primary_bank_weapons[j] = sssp->primary_banks[j];
1668 if (sssp->secondary_banks[0] != SUBSYS_STATUS_NO_CHANGE)
1669 for (j=0; j<MAX_SECONDARY_BANKS; j++)
1670 ptr->weapons.secondary_bank_weapons[j] = sssp->secondary_banks[j];
1672 for (j=0; j<MAX_SECONDARY_BANKS; j++) {
1673 // AL 3-5-98: This is correct for FRED, but not for FreeSpace... but is this even used?
1674 // As far as I know, turrets cannot run out of ammo
1675 ptr->weapons.secondary_bank_ammo[j] = sssp->secondary_ammo[j];
1678 ptr->subsys_cargo_name = sssp->subsys_cargo_name;
1680 if (sssp->ai_class != SUBSYS_STATUS_NO_CHANGE)
1681 ptr->weapons.ai_class = sssp->ai_class;
1683 ai_turret_select_default_weapon(ptr);
1686 ptr = GET_NEXT(ptr);
1690 // initial hull strength, shields, and velocity are all expressed as a percentage of the max value/
1691 // so a initial_hull value of 90% means 90% of max. This way is opposite of how subsystems are dealt
1694 Objects[objnum].phys_info.speed = (float) objp->initial_velocity;
1695 // Ships[shipnum].hull_hit_points_taken = (float) objp->initial_hull;
1696 Objects[objnum].hull_strength = (float) objp->initial_hull;
1697 Objects[objnum].shields[0] = (float) objp->initial_shields;
1700 int max_allowed_sparks, num_sparks, i;
1703 // Ships[shipnum].hull_hit_points_taken = (float)objp->initial_hull * sip->max_hull_hit_points / 100.0f;
1704 Objects[objnum].hull_strength = objp->initial_hull * sip->initial_hull_strength / 100.0f;
1705 for (i = 0; i<MAX_SHIELD_SECTIONS; i++)
1706 Objects[objnum].shields[i] = (float)(objp->initial_shields * sip->shields / 100.0f) / MAX_SHIELD_SECTIONS;
1708 // initial velocities now do not apply to ships which warp in after mission starts
1709 if ( !(Game_mode & GM_IN_MISSION) ) {
1710 Objects[objnum].phys_info.speed = (float)objp->initial_velocity * sip->max_speed / 100.0f;
1711 Objects[objnum].phys_info.vel.xyz.z = Objects[objnum].phys_info.speed;
1712 Objects[objnum].phys_info.prev_ramp_vel = Objects[objnum].phys_info.vel;
1713 Objects[objnum].phys_info.desired_vel = Objects[objnum].phys_info.vel;
1716 // recalculate damage of subsystems
1717 ship_recalc_subsys_strength( &Ships[shipnum] );
1719 // create sparks on a ship whose hull is damaged. We will create two sparks for every 20%
1720 // of hull damage done. 100 means no sparks. between 80 and 100 do two sparks. 60 and 80 is
1722 pm = model_get( sip->modelnum );
1723 max_allowed_sparks = get_max_sparks(&Objects[objnum]);
1724 num_sparks = (int)((100.0f - objp->initial_hull) / 5.0f);
1725 if (num_sparks > max_allowed_sparks) {
1726 num_sparks = max_allowed_sparks;
1729 for (i = 0; i < num_sparks; i++ ) {
1732 // DA 10/20/98 - sparks must be chosen on the hull and not any submodel
1733 submodel_get_two_random_points(sip->modelnum, pm->detail[0], &v1, &v2);
1734 ship_hit_sparks_no_rotate(&Objects[objnum], &v1);
1735 // ship_hit_sparks_no_rotate(&Objects[objnum], &v2);
1739 // in mission, we add a log entry -- set ship positions for ships not in wings, and then do
1741 if ( (Game_mode & GM_IN_MISSION) && (!Fred_running) ) {
1742 mission_log_add_entry( LOG_SHIP_ARRIVE, Ships[shipnum].ship_name, NULL );
1744 // if this ship isn't in a wing, determine it's arrival location
1745 if ( !Game_restoring ) {
1746 if ( Ships[shipnum].wingnum == -1 ) {
1748 // multiplayer clients set the arrival location of ships to be at location since their
1749 // position has already been determined. Don't actually set the variable since we
1750 // don't want the warp effect to show if coming from a dock bay.
1751 location = objp->arrival_location;
1752 if ( MULTIPLAYER_CLIENT )
1753 location = ARRIVE_AT_LOCATION;
1754 mission_set_arrival_location(objp->arrival_anchor, location, objp->arrival_distance, objnum, NULL, NULL);
1755 if ( objp->arrival_location != ARRIVE_FROM_DOCK_BAY )
1756 shipfx_warpin_start( &Objects[objnum] );
1760 // possibly add this ship to a hotkey set
1761 if ( (Ships[shipnum].wingnum == -1) && (Ships[shipnum].hotkey != -1 ) )
1762 mission_hotkey_mf_add( Ships[shipnum].hotkey, Ships[shipnum].objnum, HOTKEY_MISSION_FILE_ADDED );
1763 else if ( (Ships[shipnum].wingnum != -1) && (Wings[Ships[shipnum].wingnum].hotkey != -1 ) )
1764 mission_hotkey_mf_add( Wings[Ships[shipnum].wingnum].hotkey, Ships[shipnum].objnum, HOTKEY_MISSION_FILE_ADDED );
1766 // possibly add this ship to the hud escort list
1767 if ( Ships[shipnum].flags & SF_ESCORT ){
1768 hud_add_remove_ship_escort( objnum, 1 );
1772 // for multiplayer games, make a call to the network code to assign the object signature
1773 // of the newly created object. The network host of the netgame will always assign a signature
1774 // to a newly created object. The network signature will get to the clients of the game in
1775 // different manners depending on whether or not an individual ship or a wing was created.
1776 if ( Game_mode & GM_MULTIPLAYER ) {
1777 Objects[objnum].net_signature = objp->net_signature;
1779 if ( (Game_mode & GM_IN_MISSION) && MULTIPLAYER_MASTER && (objp->wingnum == -1) ){
1780 send_ship_create_packet( &Objects[objnum], (objp==Arriving_support_ship)?1:0 );
1784 // if recording a demo, post the event
1785 if(Game_mode & GM_DEMO_RECORD){
1786 demo_POST_obj_create(objp->name, Objects[objnum].signature);
1792 // Mp points at the text of an object, which begins with the "$Name:" field.
1793 // Snags all object information and calls parse_create_object to create a ship.
1794 // Why create a ship? Why not an object? Stay tuned...
1796 // flag is parameter that is used to tell what kind information we are retrieving from the mission.
1797 // if we are just getting player starts, then don't create the objects
1798 int parse_object(mission *pm, int flag, p_object *objp)
1800 // p_object temp_object;
1802 int i, j, count, shipnum, delay, destroy_before_mission_time;
1803 char name[NAME_LENGTH], flag_strings[MAX_PARSE_OBJECT_FLAGS][NAME_LENGTH];
1807 // objp = &temp_object;
1809 required_string("$Name:");
1810 stuff_string(objp->name, F_NAME, NULL);
1811 shipnum = ship_name_lookup(objp->name);
1813 error_display(0, NOX("Redundant ship name: %s\n"), objp->name);
1816 find_and_stuff("$Class:", &objp->ship_class, F_NAME, Ship_class_names, Num_ship_types, "ship class");
1817 if (objp->ship_class < 0) {
1818 Warning(LOCATION, "Ship \"%s\" has an invalid ship type (ships.tbl probably changed). Making it type 0", objp->name);
1820 // if fred is running, maybe notify the user that the mission contains MD content
1822 Fred_found_unknown_ship_during_parsing = 1;
1825 objp->ship_class = 0;
1828 // if this is a multiplayer dogfight mission, skip support ships
1829 if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT) && (Ship_info[objp->ship_class].flags & SIF_SUPPORT)){
1833 // optional alternate name type
1834 objp->alt_type_index = -1;
1835 if(optional_string("$Alt:")){
1837 stuff_string(name, F_NAME, NULL, NAME_LENGTH);
1839 // try and find the alternate name
1840 objp->alt_type_index = (char)mission_parse_lookup_alt(name);
1841 Assert(objp->alt_type_index >= 0);
1842 if(objp->alt_type_index < 0){
1843 mprintf(("Error looking up alternate ship type name!\n"));
1845 mprintf(("Using alternate ship type name : %s\n", name));
1850 find_and_stuff("$Team:", &team_index, F_NAME, Team_names, Num_team_names, "team name");
1851 Assert((team_index >= 0) && (team_index < MAX_TEAM_NAMES));
1852 objp->team = 1 << team_index;
1854 required_string("$Location:");
1855 stuff_vector(&objp->pos);
1857 required_string("$Orientation:");
1858 stuff_matrix(&objp->orient);
1860 find_and_stuff("$IFF:", &objp->iff, F_NAME, Iff_names, Num_iff, "IFF");
1861 find_and_stuff("$AI Behavior:", &objp->behavior, F_NAME, Ai_behavior_names, Num_ai_behaviors, "AI behavior");
1862 objp->ai_goals = -1;
1864 if ( optional_string("+AI Class:")) {
1865 objp->ai_class = match_and_stuff(F_NAME, Ai_class_names, Num_ai_classes, "AI class");
1866 Assert(objp->ai_class > -1 );
1868 objp->ai_class = Ship_info[objp->ship_class].ai_class;
1871 if ( optional_string("$AI Goals:") ){
1872 objp->ai_goals = get_sexp_main();
1875 if ( !required_string_either("$AI Goals:", "$Cargo 1:") ) {
1876 required_string("$AI Goals:");
1877 objp->ai_goals = get_sexp_main();
1882 find_and_stuff_or_add("$Cargo 1:", &temp, F_NAME, Cargo_names, &Num_cargo, MAX_CARGO, "cargo");
1883 objp->cargo1 = char(temp);
1884 if ( optional_string("$Cargo 2:") ) {
1885 char buf[NAME_LENGTH];
1886 stuff_string(buf, F_NAME, NULL);
1889 parse_common_object_data(objp); // get initial conditions and subsys status
1891 while (required_string_either("$Arrival Location:", "$Status Description:")) {
1892 Assert(count < MAX_OBJECT_STATUS);
1894 find_and_stuff("$Status Description:", &objp->status_type[count], F_NAME, Status_desc_names, Num_status_names, "Status Description");
1895 find_and_stuff("$Status:", &objp->status[count], F_NAME, Status_type_names, Num_status_names, "Status Type");
1896 find_and_stuff("$Target:", &objp->target[count], F_NAME, Status_target_names, Num_status_names, "Target");
1899 objp->status_count = count;
1901 objp->arrival_anchor = -1;
1902 objp->arrival_distance = 0;
1903 find_and_stuff("$Arrival Location:", &objp->arrival_location, F_NAME, Arrival_location_names, Num_arrival_names, "Arrival Location");
1904 if ( optional_string("+Arrival Distance:") ) {
1905 stuff_int( &objp->arrival_distance );
1906 if ( objp->arrival_location != ARRIVE_AT_LOCATION ) {
1907 required_string("$Arrival Anchor:");
1908 stuff_string(name, F_NAME, NULL);
1909 objp->arrival_anchor = get_anchor(name);
1913 if (optional_string("+Arrival Delay:")) {
1916 Error(LOCATION, "Cannot have arrival delay < 0 (ship %s)", objp->name);
1920 if ( !Fred_running ){
1921 objp->arrival_delay = -delay; // use negative numbers to mean we haven't set up a timer yet
1923 objp->arrival_delay = delay;
1926 required_string("$Arrival Cue:");
1927 objp->arrival_cue = get_sexp_main();
1928 if ( !Fred_running && (objp->arrival_cue >= 0) ) {
1929 // eval the arrival cue. if the cue is true, set up the timestamp for the arrival delay
1930 Assert ( objp->arrival_delay <= 0 );
1932 // don't eval arrival_cues when just looking for player information.
1933 if ( eval_sexp(objp->arrival_cue) ){ // evaluate to determine if sexp is always false.
1934 objp->arrival_delay = timestamp( -objp->arrival_delay * 1000 );
1938 find_and_stuff("$Departure Location:", &objp->departure_location, F_NAME, Departure_location_names, Num_arrival_names, "Departure Location");
1939 objp->departure_anchor = -1;
1940 if ( objp->departure_location == DEPART_AT_DOCK_BAY ) {
1941 required_string("$Departure Anchor:");
1942 stuff_string(name, F_NAME, NULL);
1943 objp->departure_anchor = get_anchor(name);
1946 if (optional_string("+Departure Delay:")) {
1949 Error(LOCATION, "Cannot have departure delay < 0 (ship %s)", objp->name);
1955 if ( !Fred_running ){
1956 objp->departure_delay = -delay;
1958 objp->departure_delay = delay;
1961 required_string("$Departure Cue:");
1962 objp->departure_cue = get_sexp_main();
1964 if (optional_string("$Misc Properties:"))
1965 stuff_string(objp->misc, F_NAME, NULL);
1967 required_string("$Determination:");
1968 stuff_int(&objp->determination);
1971 if (optional_string("+Flags:")) {
1972 count = stuff_string_list(flag_strings, MAX_PARSE_OBJECT_FLAGS);
1973 for (i=0; i<count; i++) {
1974 for (j=0; j<MAX_PARSE_OBJECT_FLAGS; j++) {
1975 if (!stricmp(flag_strings[i], Parse_object_flags[j])) {
1976 objp->flags |= (1 << j);
1981 if (j == MAX_PARSE_OBJECT_FLAGS)
1982 Warning(LOCATION, "Unknown flag in mission file: %s\n", flag_strings[i]);
1986 // always store respawn priority, just for ease of implementation
1987 objp->respawn_priority = 0;
1988 if(optional_string("+Respawn Priority:" )){
1989 stuff_int(&objp->respawn_priority);
1992 objp->escort_priority = 0;
1993 if ( optional_string("+Escort Priority:" ) ) {
1994 Assert(objp->flags & P_SF_ESCORT);
1995 stuff_int(&objp->escort_priority);
1998 if ( objp->flags & P_OF_PLAYER_START ) {
1999 objp->flags |= P_OF_CARGO_KNOWN; // make cargo known for players
2003 objp->special_exp_index = -1;
2004 if ( optional_string("+Special Exp index:" ) ) {
2005 stuff_int(&objp->special_exp_index);
2008 // if the kamikaze flag is set, we should have the next flag
2009 if ( optional_string("+Kamikaze Damage:") ) {
2013 objp->kamikaze_damage = i2fl(damage);
2017 if (optional_string("+Hotkey:")) {
2018 stuff_int(&objp->hotkey);
2019 Assert((objp->hotkey >= 0) && (objp->hotkey < 10));
2022 objp->docked_with[0] = 0;
2023 if (optional_string("+Docked With:")) {
2024 stuff_string(objp->docked_with, F_NAME, NULL);
2025 required_string("$Docker Point:");
2026 stuff_string(objp->docker_point, F_NAME, NULL);
2027 required_string("$Dockee Point:");
2028 stuff_string(objp->dockee_point, F_NAME, NULL);
2030 objp->flags |= P_SF_INITIALLY_DOCKED;
2032 // put this information into the Initially_docked array. We will need to use this
2033 // informatin later since not all ships will initially get created.
2034 strcpy(Initially_docked[Total_initially_docked].dockee, objp->docked_with);
2035 strcpy(Initially_docked[Total_initially_docked].docker_point, objp->docker_point);
2036 strcpy(Initially_docked[Total_initially_docked].dockee_point, objp->dockee_point);
2037 Initially_docked[Total_initially_docked].docker = objp;
2038 Total_initially_docked++;
2041 // check the optional parameter for destroying the ship before the mission starts. If this parameter is
2042 // here, then we need to destroy the ship N seconds before the mission starts (for debris purposes).
2043 // store the time value here. We want to create this object for sure. Set the arrival cue and arrival
2044 // delay to bogus values
2045 destroy_before_mission_time = -1;
2046 if ( optional_string("+Destroy At:") ) {
2048 stuff_int(&destroy_before_mission_time);
2049 Assert ( destroy_before_mission_time >= 0 );
2050 objp->arrival_cue = Locked_sexp_true;
2051 objp->arrival_delay = timestamp(0);
2054 // check for the optional "orders accepted" string which contains the orders from the default
2055 // set that this ship will actually listen to
2056 if ( optional_string("+Orders Accepted:") ) {
2057 stuff_int( &objp->orders_accepted );
2058 if ( objp->orders_accepted != -1 ){
2059 objp->flags |= P_SF_USE_UNIQUE_ORDERS;
2063 if (optional_string("+Group:")){
2064 stuff_int(&objp->group);
2069 if (optional_string("+Score:")){
2070 stuff_int(&objp->score);
2075 // parse the persona index if present
2076 if ( optional_string("+Persona Index:")){
2077 stuff_int(&objp->persona_index);
2079 objp->persona_index = -1;
2082 objp->wingnum = -1; // set the wing number to -1 -- possibly to be set later
2084 // for multiplayer, assign a network signature to this parse object. Doing this here will
2085 // allow servers to use the signature with clients when creating new ships, instead of having
2086 // to pass ship names all the time
2087 if ( Game_mode & GM_MULTIPLAYER ){
2088 objp->net_signature = multi_assign_network_signature( MULTI_SIG_SHIP );
2091 // set the wing_status position to be -1 for all objects. This will get set to an appropriate
2092 // value when the wing positions are finally determined.
2093 objp->wing_status_wing_index = -1;
2094 objp->wing_status_wing_pos = -1;
2095 objp->respawn_count = 0;
2097 // if this if the starting player ship, then copy if to Starting_player_pobject (used for ingame join)
2098 if ( !stricmp( objp->name, Player_start_shipname) ) {
2099 Player_start_pobject = *objp;
2100 Player_start_pobject.flags |= P_SF_PLAYER_START_VALID;
2104 // Now create the object.
2105 // Don't create the new ship blindly. First, check the sexp for the arrival cue
2106 // to determine when this ship should arrive. If not right away, stick this ship
2107 // onto the ship arrival list to be looked at later. Also check to see if it should use the
2108 // wings arrival cue. The ship may get created later depending on whether or not the wing
2110 // always create ships when FRED is running
2112 // don't create the object if it is intially docked for either FreeSpcae or Fred. Fred will
2113 // create the object later in post_process_mission
2114 if ( (objp->flags & P_SF_INITIALLY_DOCKED) || (!Fred_running && (!eval_sexp(objp->arrival_cue) || !timestamp_elapsed(objp->arrival_delay) || (objp->flags & P_SF_REINFORCEMENT))) ) {
2115 Assert ( destroy_before_mission_time == -1 ); // we can't add ships getting destroyed to the arrival list!!!
2116 Assert ( num_ship_arrivals < MAX_SHIP_ARRIVALS );
2117 memcpy( &ship_arrivals[num_ship_arrivals], objp, sizeof(p_object) );
2118 list_append(&ship_arrival_list, &ship_arrivals[num_ship_arrivals]);
2119 num_ship_arrivals++;
2121 // ingame joiners bail here.
2122 else if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)){
2128 real_objnum = parse_create_object(objp); // this object may later get destroyed depending on wing status!!!!
2130 Subsys_index = objp->subsys_index; // free elements that are no longer needed.
2132 // if the ship is supposed to be destroyed before the mission, then blow up the ship, mark the pieces
2133 // as last forever. Only call this stuff when you are blowing up the ship
2134 if ( destroy_before_mission_time >= 0 ) {
2137 objp = &Objects[real_objnum];
2138 if ( !Fred_running ) {
2140 shipfx_blow_up_model( objp, Ships[objp->instance].modelnum, 0, 0, &objp->pos );
2141 objp->flags |= OF_SHOULD_BE_DEAD;
2143 // once the ship is exploded, find the debris pieces belonging to this object, mark them
2144 // as not to expire, and move them forward in time N seconds
2145 for (i = 0; i < MAX_DEBRIS_PIECES; i++ ) {
2149 if ( !db->flags & DEBRIS_USED ) // not used, move onto the next one.
2151 if ( db->source_objnum != real_objnum ) // not from this ship, move to next one
2154 debris_clear_expired_flag(db); // mark as don't expire
2155 db->lifeleft = -1.0f; // be sure that lifeleft == -1.0 so that it really doesn't expire!
2157 // now move the debris along it's path for N seconds
2158 objp = &Objects[db->objnum];
2159 physics_sim( &objp->pos, &objp->orient, &objp->phys_info, (float)destroy_before_mission_time );
2162 // be sure to set the variable in the ships structure for the final death time!!!
2163 Ships[objp->instance].final_death_time = destroy_before_mission_time;
2164 Ships[objp->instance].flags |= SF_KILL_BEFORE_MISSION;
2172 void parse_common_object_data(p_object *objp)
2176 // set some defaults..
2177 objp->initial_velocity = 0;
2178 objp->initial_hull = 100;
2179 objp->initial_shields = 100;
2181 // now change defaults if present
2182 if (optional_string("+Initial Velocity:")) {
2183 stuff_int(&objp->initial_velocity);
2186 if (optional_string("+Initial Hull:"))
2187 stuff_int(&objp->initial_hull);
2188 if (optional_string("+Initial Shields:"))
2189 stuff_int(&objp->initial_shields);
2191 objp->subsys_index = Subsys_index;
2192 objp->subsys_count = 0;
2193 while (optional_string("+Subsystem:")) {
2194 i = allocate_subsys_status();
2196 objp->subsys_count++;
2197 stuff_string(Subsys_status[i].name, F_NAME, NULL);
2199 if (optional_string("$Damage:"))
2200 stuff_float(&Subsys_status[i].percent);
2202 Subsys_status[i].subsys_cargo_name = -1;
2203 if (optional_string("+Cargo Name:")) {
2204 char cargo_name[256];
2205 stuff_string(cargo_name, F_NAME, NULL);
2206 int index = string_lookup(cargo_name, Cargo_names, Num_cargo, "cargo", 0);
2207 if (index == -1 && (Num_cargo < MAX_CARGO)) {
2209 strcpy(Cargo_names[Num_cargo++], cargo_name);
2211 Subsys_status[i].subsys_cargo_name = index;
2214 if (optional_string("+AI Class:"))
2215 Subsys_status[i].ai_class = match_and_stuff(F_NAME, Ai_class_names, Num_ai_classes, "AI class");
2217 if (optional_string("+Primary Banks:"))
2218 stuff_int_list(Subsys_status[i].primary_banks, MAX_PRIMARY_BANKS, WEAPON_LIST_TYPE);
2220 if (optional_string("+Secondary Banks:"))
2221 stuff_int_list(Subsys_status[i].secondary_banks, MAX_SECONDARY_BANKS, WEAPON_LIST_TYPE);
2223 if (optional_string("+Sbank Ammo:"))
2224 stuff_int_list(Subsys_status[i].secondary_ammo, MAX_SECONDARY_BANKS, RAW_INTEGER_TYPE);
2229 void parse_objects(mission *pm, int flag)
2235 required_string("#Objects");
2238 num_ship_original = 0;
2239 while (required_string_either("#Wings", "$Name:")){
2240 // not all objects are always valid or legal
2241 if(parse_object(pm, flag, &temp)){
2242 // add to the default list
2243 if(num_ship_original < MAX_SHIP_ORIGINAL){
2244 memcpy(&ship_original[num_ship_original++], &temp, sizeof(p_object));
2250 p_object *mission_parse_get_original_ship( ushort net_signature )
2254 // look for original ships
2255 for(idx=0; idx<num_ship_original; idx++){
2256 if(ship_original[idx].net_signature == net_signature){
2257 return &ship_original[idx];
2265 int find_wing_name(char *name)
2269 for (i=0; i<num_wings; i++){
2270 if (!strcmp(name, Wings[i].name)){
2278 // function to create ships in the wing that need to be created. We psas the wing pointer, it's index
2279 // into the Wings array
2280 int parse_wing_create_ships( wing *wingp, int num_to_create, int force, int specific_instance )
2283 int wingnum, objnum, num_create_save;
2285 int pre_create_count;
2287 // we need to send this in multiplayer
2288 pre_create_count = wingp->total_arrived_count;
2290 // force is used to force creation of the wing -- used for multiplayer
2292 // we only want to evaluate the arrival cue of the wing if:
2294 // 2) multiplayer and I am the host of the game
2295 // can't create any ships if the arrival cue is false or the timestamp has not elapsed.
2297 if ( !eval_sexp(wingp->arrival_cue) ) /* || !timestamp_elapsed(wingp->arrival_delay) ) */
2300 // once the sexpressions becomes true, then check the arrival delay on the wing. The first time, the
2301 // arrival delay will be <= 0 meaning that no timer object has been set yet. Set up the timestamp
2302 // which should always give a number >= 0;
2303 if ( wingp->arrival_delay <= 0 ) {
2304 wingp->arrival_delay = timestamp( -wingp->arrival_delay * 1000 );
2305 Assert ( wingp->arrival_delay >= 0 );
2308 if ( !timestamp_elapsed( wingp->arrival_delay ) )
2311 // if wing is coming from docking bay, then be sure that ship we are arriving from actually exists
2313 if ( wingp->arrival_location == ARRIVE_FROM_DOCK_BAY ) {
2317 Assert( wingp->arrival_anchor >= 0 );
2318 name = Parse_names[wingp->arrival_anchor];
2320 // see if ship is yet to arrive. If so, then return -1 so we can evaluate again later.
2321 if ( mission_parse_get_arrival_ship( name ) )
2324 // see if ship is in mission. If not, then we can assume it was destroyed or departed since
2325 // it is not on the arrival list (as shown by above if statement).
2326 shipnum = ship_name_lookup( name );
2327 if ( shipnum == -1 ) {
2329 // since this wing cannot arrive from this place, we need to mark the wing as destroyed and
2330 // set the wing variables appropriatly. Good for directives.
2332 // set the gone flag
2333 wingp->flags |= WF_WING_GONE;
2335 // if the current wave is zero, it never existed
2336 wingp->flags |= WF_NEVER_EXISTED;
2338 // mark the number of waves and number of ships destroyed equal to the last wave and the number
2339 // of ships yet to arrive
2340 num_remaining = ( (wingp->num_waves - wingp->current_wave) * wingp->wave_count);
2341 wingp->total_arrived_count += num_remaining;
2342 wingp->current_wave = wingp->num_waves;
2344 // replaced following three lines of code with mission log call because of bug with
2345 // the Ships_exited list.
2346 //index = ship_find_exited_ship_by_name( name );
2347 //Assert( index != -1 );
2348 //if (Ships_exited[index].flags & SEF_DESTROYED ) {
2349 if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) ) {
2350 wingp->total_destroyed += num_remaining;
2352 wingp->total_departed += num_remaining;
2355 Sexp_nodes[wingp->arrival_cue].value = SEXP_KNOWN_FALSE;
2360 if ( num_to_create == 0 )
2363 // check the wave_delay_timestamp field. If it is not valid, make it valid (based on wave delay min
2364 // and max valuds). If it is valid, and not elapsed, then return. If it is valid and elasped, then
2366 if ( !timestamp_valid(wingp->wave_delay_timestamp) ) {
2368 // if at least one of these is valid, then reset the timestamp. If they are both zero, we will create the
2370 if ( (wingp->wave_delay_min > 0) || (wingp->wave_delay_max > 0) ) {
2371 Assert ( wingp->wave_delay_min <= wingp->wave_delay_max );
2372 time_to_arrive = wingp->wave_delay_min + (int)(frand() * (wingp->wave_delay_max - wingp->wave_delay_min));
2375 // HACK HACK -- in the presense of Mike Comet and Mitri, I have introduced one of the most
2376 // serious breaches of coding standards. I'm to lazy to fix this the correct way. Insert
2377 // a delay before the next wave of the wing can arrive to that clients in the game have ample
2378 // time to kill off any ships in the wing before the next wave arrives.
2379 if ( Game_mode & GM_MULTIPLAYER ){
2380 time_to_arrive += 7;
2382 wingp->wave_delay_timestamp = timestamp(time_to_arrive * 1000);
2386 // if we get here, both min and max values are 0; See comments above for a most serious hack
2388 if ( Game_mode & GM_MULTIPLAYER )
2389 time_to_arrive += 7;
2390 time_to_arrive *= 1000;
2391 wingp->wave_delay_timestamp = timestamp(time_to_arrive);
2394 // now check to see if the wave_delay_timestamp is elapsed or not
2395 if ( !timestamp_elapsed(wingp->wave_delay_timestamp) )
2399 // finally we can create the wing.
2401 num_create_save = num_to_create;
2403 wingnum = wingp - Wings; // get the wing number
2405 // if there are no ships to create, then all ships must be player start ships -- do nothing in this case.
2406 if ( num_to_create == 0 ){
2410 wingp->current_wave++; // we are creating new ships
2411 // we need to create num_to_create ships. Since the arrival cues for ships in a wing
2412 // are ignored, then *all* ships must be in the ship_arrival_list.
2415 objp = GET_FIRST(&ship_arrival_list);
2416 while( objp != END_OF_LIST(&ship_arrival_list) ) {
2417 p_object *temp = GET_NEXT(objp);
2419 // compare the wingnums. When they are equal, we can create the ship. In the case of
2420 // wings that have multiple waves, this code implies that we essentially creating clones
2421 // of the ships that were created in Fred for the wing when more ships for a new wave
2422 // arrive. The threshold value of a wing can also make one of the ships in a wing be "cloned"
2423 // more often than other ships in the wing. I don't think this matters much.
2424 if ( objp->wingnum == wingnum ) {
2427 // when ingame joining, we need to create a specific ship out of the list of ships for a
2428 // wing. specific_instance is a 0 based integer which specified which ship in the wing
2429 // to create. So, only create the ship we actually need to.
2430 if ( (Game_mode & GM_MULTIPLAYER) && (specific_instance > 0) ) {
2431 specific_instance--;
2436 Assert ( !(objp->flags & P_SF_CANNOT_ARRIVE) ); // get allender
2440 // if we have the maximum number of ships in the wing, we must bail as well
2441 if ( wingp->current_count >= MAX_SHIPS_PER_WING ) {
2442 Int3(); // this is bogus -- we should always allow all ships to be created
2447 // bash the ship name to be the name of the wing + sone number if there is > 1 wave in
2449 // also, if multplayer, set the parse object's net signature to be wing's net signature
2450 // base + total_arrived_count (before adding 1)
2451 if ( Game_mode & GM_MULTIPLAYER ){
2452 objp->net_signature = (ushort)(wingp->net_signature + wingp->total_arrived_count);
2455 wingp->total_arrived_count++;
2456 if ( wingp->num_waves > 1 ){
2457 sprintf(objp->name, NOX("%s %d"), wingp->name, wingp->total_arrived_count);
2460 objnum = parse_create_object(objp);
2461 aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2463 // copy any goals from the wing to the newly created ship
2464 for (index = 0; index < MAX_AI_GOALS; index++) {
2465 if ( wingp->ai_goals[index].ai_mode != AI_GOAL_NONE ){
2466 ai_copy_mission_wing_goal( &wingp->ai_goals[index], aip );
2470 Ai_info[Ships[Objects[objnum].instance].ai_index].wing = wingnum;
2472 if ( wingp->flags & WF_NO_DYNAMIC ){
2473 aip->ai_flags |= AIF_NO_DYNAMIC;
2476 // update housekeeping variables
2477 wingp->ship_index[wingp->current_count] = Objects[objnum].instance;
2479 // set up wingman status index
2480 hud_wingman_status_set_index(wingp->ship_index[wingp->current_count]);
2482 objp->wing_status_wing_index = Ships[Objects[objnum].instance].wing_status_wing_index;
2483 objp->wing_status_wing_pos = Ships[Objects[objnum].instance].wing_status_wing_pos;
2485 wingp->current_count++;
2487 // keep any player ship on the parse object list -- used for respawns
2488 // 5/8/98 -- MWA -- don't remove ships from the list when you are ingame joining
2489 if ( !(objp->flags & P_OF_PLAYER_START) ) {
2490 if ( (Game_mode & GM_NORMAL) || !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ) {
2491 if ( wingp->num_waves == wingp->current_wave ) { // only remove ship if one wave in wing
2492 list_remove( &ship_arrival_list, objp); // remove objp from the list
2493 if ( objp->ai_goals != -1 ){
2494 free_sexp2(objp->ai_goals); // free up sexp nodes for reuse
2500 // flag ship with SF_FROM_PLAYER_WING if a member of player starting wings
2501 if ( (Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM) ) {
2502 // but for team vs. team games, then just check the alpha and zeta wings
2503 if ( !(stricmp(Starting_wing_names[STARTING_WING_ALPHA], wingp->name)) || !(stricmp(Starting_wing_names[STARTING_WING_ZETA], wingp->name)) ) {
2504 Ships[Objects[objnum].instance].flags |= SF_FROM_PLAYER_WING;
2507 for (int i = 0; i < MAX_STARTING_WINGS; i++ ) {
2508 if ( !stricmp(Starting_wing_names[i], wingp->name) ) {
2509 Ships[Objects[objnum].instance].flags |= SF_FROM_PLAYER_WING;
2514 // keep track of how many ships to create. Stop when we have done all that we are supposed
2517 if ( !num_to_create ){
2524 Assert ( num_to_create == 0 ); // we should always have enough ships in the list!!!
2526 // possibly play some event driven music here. Send a network packet indicating the wing was
2527 // created. Only do this stuff if actually in the mission.
2528 if ( (objnum != -1) && (Game_mode & GM_IN_MISSION) ) { // if true, we have created at least one new ship.
2531 // see if this wing is a player starting wing, and if so, call the maybe_add_form_goal
2532 // function to possibly make the wing form on the player
2533 for (i = 0; i < MAX_STARTING_WINGS; i++ ) {
2534 if ( Starting_wings[i] == wingnum ){
2538 if ( i < MAX_STARTING_WINGS ){
2539 ai_maybe_add_form_goal( wingp );
2542 mission_log_add_entry( LOG_WING_ARRIVE, wingp->name, NULL, wingp->current_wave );
2543 ship_num = wingp->ship_index[0];
2545 if ( !(Ships[ship_num].flags & SF_NO_ARRIVAL_MUSIC) && !(wingp->flags & WF_NO_ARRIVAL_MUSIC) ) {
2546 if ( timestamp_elapsed(Allow_arrival_music_timestamp) ) {
2547 Allow_arrival_music_timestamp = timestamp(ARRIVAL_MUSIC_MIN_SEPARATION);
2548 event_music_arrival(Ships[ship_num].team);
2552 // possibly change the location where these ships arrive based on the wings arrival location
2553 mission_set_wing_arrival_location( wingp, num_create_save );
2555 // if in multiplayer (and I am the host) and in the mission, send a wing create command to all
2557 if ( MULTIPLAYER_MASTER ){
2558 send_wing_create_packet( wingp, num_create_save, pre_create_count );
2562 // test code to check to be sure that all ships in the wing are ignoring the same types
2563 // of orders from the player
2564 if ( Fred_running ) {
2565 Assert( wingp->ship_index[0] != -1 );
2566 int orders = Ships[wingp->ship_index[0]].orders_accepted;
2567 for (i = 1; i < wingp->current_count; i++ ) {
2568 if ( orders != Ships[wingp->ship_index[i]].orders_accepted ) {
2569 Warning(LOCATION, "ships in wing %s are ignoring different player orders. Please find Mark A\nto talk to him about this.", wingp->name );
2578 wingp->wave_delay_timestamp = timestamp(-1); // we will need to set this up properly for the next wave
2579 return num_create_save;
2582 void parse_wing(mission *pm)
2584 int wingnum, i, wing_goals, delay;
2585 char name[NAME_LENGTH], ship_names[MAX_SHIPS_PER_WING][NAME_LENGTH];
2586 char wing_flag_strings[MAX_WING_FLAGS][NAME_LENGTH];
2590 wingp = &Wings[num_wings];
2592 required_string("$Name:");
2593 stuff_string(wingp->name, F_NAME, NULL);
2594 wingnum = find_wing_name(wingp->name);
2596 error_display(0, NOX("Redundant wing name: %s\n"), wingp->name);
2597 wingnum = num_wings;
2599 wingp->total_arrived_count = 0;
2600 wingp->total_destroyed = 0;
2603 required_string("$Waves:");
2604 stuff_int(&wingp->num_waves);
2605 Assert ( wingp->num_waves >= 1 ); // there must be at least 1 wave
2607 wingp->current_wave = 0;
2609 required_string("$Wave Threshold:");
2610 stuff_int(&wingp->threshold);
2612 required_string("$Special Ship:");
2613 stuff_int(&wingp->special_ship);
2615 wingp->arrival_anchor = -1;
2616 find_and_stuff("$Arrival Location:", &wingp->arrival_location, F_NAME, Arrival_location_names, Num_arrival_names, "Arrival Location");
2617 if ( optional_string("+Arrival Distance:") ) {
2618 stuff_int( &wingp->arrival_distance );
2619 if ( wingp->arrival_location != ARRIVE_AT_LOCATION ) {
2620 required_string("$Arrival Anchor:");
2621 stuff_string(name, F_NAME, NULL);
2622 wingp->arrival_anchor = get_anchor(name);
2626 if (optional_string("+Arrival delay:")) {
2629 Error(LOCATION, "Cannot have arrival delay < 0 on wing %s", wingp->name );
2633 if ( !Fred_running ){
2634 wingp->arrival_delay = -delay;
2636 wingp->arrival_delay = delay;
2639 required_string("$Arrival Cue:");
2640 wingp->arrival_cue = get_sexp_main();
2641 if ( !Fred_running && (wingp->arrival_cue >= 0) ) {
2642 if ( eval_sexp(wingp->arrival_cue) ) // evaluate to determine if sexp is always false.
2643 wingp->arrival_delay = timestamp( -wingp->arrival_delay * 1000 );
2647 find_and_stuff("$Departure Location:", &wingp->departure_location, F_NAME, Departure_location_names, Num_arrival_names, "Departure Location");
2648 wingp->departure_anchor = -1;
2649 if ( wingp->departure_location == DEPART_AT_DOCK_BAY ) {
2650 required_string("$Departure Anchor:");
2651 stuff_string( name, F_NAME, NULL );
2652 wingp->departure_anchor = get_anchor(name);
2655 if (optional_string("+Departure delay:")) {
2658 Error(LOCATION, "Cannot have departure delay < 0 on wing %s", wingp->name );
2663 if ( !Fred_running )
2664 wingp->departure_delay = -delay; // use negative numbers to mean that delay timer not yet set
2666 wingp->departure_delay = delay;
2668 required_string("$Departure Cue:");
2669 wingp->departure_cue = get_sexp_main();
2671 // stores a list of all names of ships in the wing
2672 required_string("$Ships:");
2673 wingp->wave_count = stuff_string_list( ship_names, MAX_SHIPS_PER_WING );
2674 wingp->current_count = 0;
2676 // get the wings goals, if any
2678 if ( optional_string("$AI Goals:") )
2679 wing_goals = get_sexp_main();
2682 if (optional_string("+Hotkey:")) {
2683 stuff_int(&wingp->hotkey);
2684 Assert((wingp->hotkey >= 0) && (wingp->hotkey < 10));
2687 if (optional_string("+Flags:")) {
2690 count = stuff_string_list( wing_flag_strings, MAX_WING_FLAGS );
2691 for (i = 0; i < count; i++ ) {
2692 if ( !stricmp( wing_flag_strings[i], NOX("ignore-count")) )
2693 wingp->flags |= WF_IGNORE_COUNT;
2694 else if ( !stricmp( wing_flag_strings[i], NOX("reinforcement")) )
2695 wingp->flags |= WF_REINFORCEMENT;
2696 else if ( !stricmp( wing_flag_strings[i], NOX("no-arrival-music")) )
2697 wingp->flags |= WF_NO_ARRIVAL_MUSIC;
2698 else if ( !stricmp( wing_flag_strings[i], NOX("no-arrival-message")) )
2699 wingp->flags |= WF_NO_ARRIVAL_MESSAGE;
2700 else if ( !stricmp( wing_flag_strings[i], NOX("no-arrival-warp")) )
2701 wingp->flags |= WF_NO_ARRIVAL_WARP;
2702 else if ( !stricmp( wing_flag_strings[i], NOX("no-departure-warp")) )
2703 wingp->flags |= WF_NO_DEPARTURE_WARP;
2704 else if ( !stricmp( wing_flag_strings[i], NOX("no-dynamic")) )
2705 wingp->flags |= WF_NO_DYNAMIC;
2707 Warning(LOCATION, "unknown wing flag\n%s\n\nSkipping.", wing_flag_strings[i]);
2711 // get the wave arrival delay bounds (if present). Used as lower and upper bounds (in seconds)
2712 // which determine when new waves of a wing should arrive.
2713 wingp->wave_delay_min = 0;
2714 wingp->wave_delay_max = 0;
2715 if ( optional_string("+Wave Delay Min:") )
2716 stuff_int( &(wingp->wave_delay_min) );
2717 if ( optional_string("+Wave Delay Max:") )
2718 stuff_int( &(wingp->wave_delay_max) );
2720 // be sure to set the wave arrival timestamp of this wing to pop right away so that the
2721 // wing could be created if it needs to be
2722 wingp->wave_delay_timestamp = timestamp(0);
2724 // initialize wing goals
2725 for (i=0; i<MAX_AI_GOALS; i++) {
2726 wingp->ai_goals[i].ai_mode = AI_GOAL_NONE;
2727 wingp->ai_goals[i].priority = -1;
2731 // error checking against the player ship wings to be sure that wave count doesn't exceed one for
2733 if ( Game_mode & GM_MULTIPLAYER ) {
2734 for (i = 0; i < MAX_STARTING_WINGS+1; i++ ) {
2735 if ( !stricmp(Starting_wing_names[i], wingp->name) ) {
2736 if ( wingp->num_waves > 1 ) {
2737 // only end the game if we're the server - clients will eventually find out :)
2738 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2739 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_WAVE_COUNT);
2741 // Error(LOCATION, "Player wings Alpha, Beta, Gamma, or Zeta cannot have more than 1 wave.");
2747 // Get the next starting signature for this in this wing. We want to reserve wave_count * num_waves
2748 // of signature. These can be used to construct wings for ingame joiners.
2749 if ( Game_mode & GM_MULTIPLAYER ) {
2752 wingp->net_signature = multi_assign_network_signature( MULTI_SIG_SHIP );
2753 next_signature = wingp->net_signature + (wingp->wave_count * wingp->num_waves);
2754 if ( next_signature > SHIP_SIG_MAX )
2755 Error(LOCATION, "Too many total ships in mission (%d) for network signature assignment", SHIP_SIG_MAX);
2756 multi_set_network_signature( (ushort)next_signature, MULTI_SIG_SHIP );
2759 for (i=0; i<MAX_SHIPS_PER_WING; i++)
2760 wingp->ship_index[i] = -1;
2762 // set up the ai_goals for this wing -- all ships created from this wing will inherit these goals
2763 // goals for the wing are stored slightly differently than for ships. We simply store the index
2764 // into the sexpression array of each goal (max 10). When a ship in this wing is created, each
2765 // goal in the wings goal array is given to the ship.
2766 if ( wing_goals != -1 ) {
2769 // this will assign the goals to the wings as well as to any ships in the wing that have been
2772 for ( sexp = CDR(wing_goals); sexp != -1; sexp = CDR(sexp) )
2773 ai_add_wing_goal_sexp(sexp, AIG_TYPE_EVENT_WING, wingnum); // used by Fred
2776 free_sexp2(wing_goals); // free up sexp nodes for reused, since they aren't needed anymore.
2779 // set the wing number for all ships in the wing
2780 for (i = 0; i < wingp->wave_count; i++ ) {
2781 //char *ship_name = wingp->ship_names[i];
2783 int num, assigned = 0;
2786 ship_name = ship_names[i];
2788 num = wingp->ship_index[i] = ship_name_lookup(ship_name, 1);
2789 Assert ( num != -1 );
2791 // hack code -- REMOVE
2792 if ( Objects[Ships[num].objnum].flags & OF_PLAYER_SHIP )
2793 Ships[num].wingnum = wingnum;
2796 // determine if this ship is a player ship, and deal with it appropriately.
2797 if ( !strnicmp(ship_name, NOX("Player "), 7) ) {
2798 Error(LOCATION, "Old mission file -- please convert by loading/saving in Fred -- see Allender/Hoffoss for help.");
2801 // assign the wing number to the ship -- if the ship has arrived, doulble check that
2802 // there is only one wave of this wing since ships with their own arrival cue cannot be
2803 // in a wing with > 1 wave. Otherwise, find the ship on the ship arrival list and set
2804 // their wing number
2805 if ( (num = ship_name_lookup(ship_name)) != -1 ) {
2806 Int3(); // this is impossible under the new system
2809 objp = GET_FIRST(&ship_arrival_list);
2810 while( objp != END_OF_LIST(&ship_arrival_list) ) {
2811 if ( !strcmp(ship_name, objp->name) ) {
2812 Assert ( objp->wingnum == -1 ); // get Allender -- ship appears to be in multiple wings
2813 objp->wingnum = wingnum;
2816 objp = GET_NEXT(objp);
2820 if ( !assigned || (assigned > 1) )
2821 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);
2825 // Fred doesn't create the wing. otherwise, create the wing if is isn't a reinforcement.
2826 if ( !Fred_running && !(wingp->flags & WF_REINFORCEMENT) )
2827 parse_wing_create_ships( wingp, wingp->wave_count );
2830 void parse_wings(mission *pm)
2832 required_string("#Wings");
2833 while (required_string_either("#Events", "$Name:")) {
2834 Assert(num_wings < MAX_WINGS);
2840 // mission events are sexpressions which cause things to happen based on the outcome
2841 // of other events in a mission. Essentially scripting the different things that can happen
2844 void parse_event(mission *pm)
2847 mission_event *event;
2849 event = &Mission_events[Num_mission_events];
2850 event->chain_delay = -1;
2852 required_string( "$Formula:" );
2853 event->formula = get_sexp_main();
2855 if (optional_string("+Name:")){
2856 stuff_string(event->name, F_NAME, NULL);
2861 if ( optional_string("+Repeat Count:")){
2862 stuff_int( &(event->repeat_count) );
2864 event->repeat_count = 1;
2867 event->interval = -1;
2868 if ( optional_string("+Interval:")){
2869 stuff_int( &(event->interval) );
2873 if ( optional_string("+Score:") ){
2874 stuff_int(&event->score);
2877 if ( optional_string("+Chained:") ){
2878 stuff_int(&event->chain_delay);
2881 if ( optional_string("+Objective:") ) {
2882 stuff_string(buf, F_NAME, NULL);
2883 event->objective_text = strdup(buf);
2885 event->objective_text = NULL;
2888 if ( optional_string("+Objective key:") ) {
2889 stuff_string(buf, F_NAME, NULL);
2890 event->objective_key_text = strdup(buf);
2892 event->objective_key_text = NULL;
2896 if( optional_string("+Team:") ) {
2897 stuff_int(&event->team);
2900 event->timestamp = timestamp(-1);
2902 // sanity check on the repeat count variable
2903 if ( event->repeat_count <= 0 ){
2904 Error (LOCATION, "Repeat count for mission event %s is <=0.\nMust be >= 1!", event->name );
2908 void parse_events(mission *pm)
2910 required_string("#Events");
2912 while (required_string_either( "#Goals", "$Formula:")) {
2913 Assert( Num_mission_events < MAX_MISSION_EVENTS );
2915 Num_mission_events++;
2919 void parse_goal(mission *pm)
2923 mission_goal *goalp;
2925 goalp = &Mission_goals[Num_goals++];
2927 Assert(Num_goals < MAX_GOALS);
2930 find_and_stuff("$Type:", &goalp->type, F_NAME, Goal_type_names, Num_goal_type_names, "goal type");
2932 required_string("+Name:");
2933 stuff_string(goalp->name, F_NAME, NULL);
2935 // backwards compatibility for old Fred missions - all new missions should use $MessageNew
2936 if(optional_string("$Message:")){
2937 stuff_string(goalp->message, F_NAME, NULL, MAX_GOAL_TEXT);
2939 required_string("$MessageNew:");
2940 stuff_string(goalp->message, F_MULTITEXT, NULL, MAX_GOAL_TEXT);
2943 if (optional_string("$Rating:")){
2944 stuff_int(&dummy); // not used
2947 required_string("$Formula:");
2948 goalp->formula = get_sexp_main();
2951 if ( optional_string("+Invalid:") )
2952 goalp->type |= INVALID_GOAL;
2953 if ( optional_string("+Invalid") )
2954 goalp->type |= INVALID_GOAL;
2955 if ( optional_string("+No music") )
2956 goalp->flags |= MGF_NO_MUSIC;
2959 if ( optional_string("+Score:") ){
2960 stuff_int(&goalp->score);
2964 if ( optional_string("+Team:") ){
2965 stuff_int( &goalp->team );
2969 void parse_goals(mission *pm)
2971 required_string("#Goals");
2973 while (required_string_either("#Waypoints", "$Type:")){
2978 void parse_waypoint_list(mission *pm)
2983 Assert(Num_waypoint_lists < MAX_WAYPOINT_LISTS);
2985 wpl = &Waypoint_lists[Num_waypoint_lists];
2987 required_string("$Name:");
2988 stuff_string(wpl->name, F_NAME, NULL);
2990 required_string("$List:");
2991 wpl->count = stuff_vector_list(wpl->waypoints, MAX_WAYPOINTS_PER_LIST);
2993 Num_waypoint_lists++;
2996 void parse_waypoints(mission *pm)
3001 required_string("#Waypoints");
3004 while (optional_string("$Jump Node:")) {
3005 Assert(Num_jump_nodes < MAX_JUMP_NODES);
3007 z = jumpnode_create(&pos);
3010 if (optional_string("$Jump Node Name:")) {
3011 stuff_string(Jump_nodes[Num_jump_nodes - 1].name, F_NAME, NULL);
3014 // If no name exists, then use a standard name
3015 if ( Jump_nodes[Num_jump_nodes - 1].name[0] == 0 ) {
3016 sprintf(Jump_nodes[Num_jump_nodes - 1].name, "Jump Node %d", Num_jump_nodes);
3020 while (required_string_either("#Messages", "$Name:"))
3021 parse_waypoint_list(pm);
3024 void parse_messages(mission *pm)
3026 required_string("#Messages");
3028 mprintf(("Starting mission message count : %d\n", Num_message_waves));
3030 // the message_parse function can be found in MissionMessage.h. The format in the
3031 // mission file takes the same format as the messages in messages,tbl. Make parsing
3032 // a whole lot easier!!!
3033 while ( required_string_either("#Reinforcements", "$Name")){
3034 message_parse(); // call the message parsing system
3037 mprintf(("Ending mission message count : %d\n", Num_message_waves));
3040 void parse_reinforcement(mission *pm)
3042 reinforcements *ptr;
3045 Assert(Num_reinforcements < MAX_REINFORCEMENTS);
3047 ptr = &Reinforcements[Num_reinforcements];
3049 required_string("$Name:");
3050 stuff_string(ptr->name, F_NAME, NULL);
3052 find_and_stuff("$Type:", &ptr->type, F_NAME, Reinforcement_type_names, Num_reinforcement_type_names, "reinforcement type");
3054 required_string("$Num times:");
3055 stuff_int(&ptr->uses);
3058 // reset the flags to 0
3061 if ( optional_string("+Arrival delay:") ){
3062 stuff_int( &(ptr->arrival_delay) );
3065 if ( optional_string("+No Messages:") ){
3066 stuff_string_list( ptr->no_messages, MAX_REINFORCEMENT_MESSAGES );
3069 if ( optional_string("+Yes Messages:") ){
3070 stuff_string_list( ptr->yes_messages, MAX_REINFORCEMENT_MESSAGES );
3073 // sanity check on the names of reinforcements -- must either be wings/ships/arrival list.
3074 if ( ship_name_lookup(ptr->name) == -1 ) {
3075 if ( wing_name_lookup(ptr->name, 1) == -1 ) {
3078 for ( p_objp = GET_FIRST(&ship_arrival_list); p_objp != END_OF_LIST(&ship_arrival_list); p_objp = GET_NEXT(p_objp) ) {
3079 if ( !stricmp(ptr->name, p_objp->name) ){
3084 if ( p_objp == END_OF_LIST(&ship_arrival_list) ) {
3085 Warning(LOCATION, "Reinforcement %s not found as ship or wing", ptr->name);
3091 // now, if the reinforcement is a wing, then set the number of waves of the wing == number of
3092 // uses of the reinforcement
3093 instance = wing_name_lookup(ptr->name, 1);
3094 if ( instance != -1 )
3095 Wings[instance].num_waves = ptr->uses;
3097 Num_reinforcements++;
3100 void parse_reinforcements(mission *pm)
3102 Num_reinforcements = 0;
3103 required_string("#Reinforcements");
3105 while (required_string_either("#Background bitmaps", "$Name:"))
3106 parse_reinforcement(pm);
3109 void parse_bitmap(mission *pm)
3112 starfield_bitmap_instance b;
3118 while(optional_string("$Bitmap:")) {
3119 stuff_string(b.filename, F_NAME, NULL);
3121 required_string("$Orientation:");
3124 required_string("$Rotation rate:");
3125 stuff_float(&b.rot);
3127 required_string("$Distance:");
3128 stuff_float(&b.scale_x);
3129 b.scale_y = b.scale_x;
3133 required_string("$Light:");
3134 stuff_int(&b.sun_light);
3136 if(Num_suns < MAX_STARFIELD_BITMAPS){
3138 strcpy(Suns[Num_suns].filename, b.filename);
3144 char name[NAME_LENGTH];
3146 starfield_bitmaps *ptr;
3148 Assert(Num_starfield_bitmaps < MAX_STARFIELD_BITMAPS);
3150 ptr = &Starfield_bitmaps[Num_starfield_bitmaps];
3152 required_string("$Bitmap:");
3153 stuff_string(name, F_NAME, NULL);
3154 for (z=0; z<Num_starfield_bitmap_lists; z++) {
3155 if (!stricmp(name, Starfield_bitmap_list[z].name)){
3160 if ( z >= Num_starfield_bitmap_lists ) {
3161 Warning( LOCATION, "Bitmap specified in mission not in game!\n" );
3165 ptr->bitmap_index = z;
3166 required_string("$Orientation:");
3167 stuff_matrix(&ptr->m);
3169 required_string("$Rotation rate:");
3170 stuff_float(&ptr->rot);
3172 required_string("$Distance:");
3173 stuff_float(&ptr->dist);
3175 required_string("$Light:");
3176 stuff_int(&ptr->light);
3177 Num_starfield_bitmaps++;
3178 calculate_bitmap_points(ptr);
3184 void parse_bitmaps(mission *pm)
3186 char str[MAX_FILENAME_LEN+1] = "";
3187 starfield_bitmap_instance b;
3190 Num_starfield_bitmaps = 0;
3191 required_string("#Background bitmaps");
3193 required_string("$Num stars:");
3194 stuff_int(&Num_stars);
3195 if (Num_stars >= MAX_STARS)
3196 Num_stars = MAX_STARS;
3198 int Ambient_light_level;
3199 required_string("$Ambient light level:");
3200 stuff_int(&Ambient_light_level);
3202 // This should call light_set_ambient() to
3203 // set the ambient light
3206 Mission_palette = 1;
3208 if(The_mission.flags & MISSION_FLAG_FULLNEB){
3209 // no regular nebula stuff
3213 strcpy(Neb2_texture_name, "Eraseme3");
3214 Neb2_poof_flags = ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4) | (1<<5));
3215 if(optional_string("+Neb2:")){
3216 stuff_string(Neb2_texture_name, F_NAME, NULL);
3218 required_string("+Neb2Flags:");
3219 stuff_int(&Neb2_poof_flags);
3221 // initialize neb effect. its gross to do this here, but Fred is dumb so I have no choice ... :(
3227 if (optional_string("+Nebula:")) {
3228 stuff_string(str, F_NAME, NULL, MAX_FILENAME_LEN);
3230 // parse the proper nebula type (full or not)
3231 for (z=0; z<NUM_NEBULAS; z++){
3232 if(The_mission.flags & MISSION_FLAG_FULLNEB){
3233 if (!stricmp(str, Neb2_filenames[z])) {
3238 if (!stricmp(str, Nebula_filenames[z])) {
3245 if (optional_string("+Color:")) {
3246 stuff_string(str, F_NAME, NULL, MAX_FILENAME_LEN);
3247 for (z=0; z<NUM_NEBULA_COLORS; z++){
3248 if (!stricmp(str, Nebula_colors[z])) {
3249 Mission_palette = z;
3255 if (optional_string("+Pitch:")){
3256 stuff_int(&Nebula_pitch);
3261 if (optional_string("+Bank:")){
3262 stuff_int(&Nebula_bank);
3267 if (optional_string("+Heading:")){
3268 stuff_int(&Nebula_heading);
3274 if (Nebula_index >= 0){
3275 nebula_init(Nebula_filenames[Nebula_index], Nebula_pitch, Nebula_bank, Nebula_heading);
3283 while(optional_string("$Sun:")){
3285 stuff_string(b.filename, F_NAME, NULL);
3288 required_string("+Angles:");
3289 stuff_float(&b.ang.p);
3290 stuff_float(&b.ang.b);
3291 stuff_float(&b.ang.h);
3294 required_string("+Scale:");
3295 stuff_float(&b.scale_x);
3296 b.scale_y = b.scale_x;
3300 // if we have room, store it
3301 if(Num_suns < MAX_STARFIELD_BITMAPS){
3303 strcpy(Suns[Num_suns].filename, b.filename);
3308 // parse background bitmaps
3309 Num_starfield_bitmaps = 0;
3310 while(optional_string("$Starbitmap:")){
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);
3322 if(optional_string("+Scale:")){
3323 stuff_float(&b.scale_x);
3324 b.scale_y = b.scale_x;
3328 required_string("+ScaleX:");
3329 stuff_float(&b.scale_x);
3331 required_string("+ScaleY:");
3332 stuff_float(&b.scale_y);
3334 required_string("+DivX:");
3335 stuff_int(&b.div_x);
3337 required_string("+DivY:");
3338 stuff_int(&b.div_y);
3341 // if we have room, store it
3342 if(Num_starfield_bitmaps < MAX_STARFIELD_BITMAPS){
3343 Starfield_bitmap_instance[Num_starfield_bitmaps] = b;
3344 strcpy(Starfield_bitmap_instance[Num_starfield_bitmaps].filename, b.filename);
3345 Num_starfield_bitmaps++;
3353 if ( optional_string("#Asteroid Fields") ){
3354 parse_asteroid_fields(pm);
3358 void parse_asteroid_fields(mission *pm)
3360 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
3362 int i, count, subtype;
3365 for (i=0; i<MAX_ASTEROID_FIELDS; i++)
3366 Asteroid_field.num_initial_asteroids = 0;
3370 // required_string("#Asteroid Fields");
3372 while (required_string_either("#", "$Density:")) {
3374 while (required_string_either("#", "$density:")) {
3376 float speed, density;
3379 required_string("$Density:");
3380 stuff_float(&density);
3382 Asteroid_field.num_initial_asteroids = (int) density;
3384 Asteroid_field.field_type = FT_ACTIVE;
3385 if (optional_string("+Field Type:")) {
3386 stuff_int((int*)&Asteroid_field.field_type);
3389 Asteroid_field.debris_genre = DG_ASTEROID;
3390 if (optional_string("+Debris Genre:")) {
3391 stuff_int((int*)&Asteroid_field.debris_genre);
3394 Asteroid_field.field_debris_type[0] = -1;
3395 Asteroid_field.field_debris_type[1] = -1;
3396 Asteroid_field.field_debris_type[2] = -1;
3397 if (Asteroid_field.debris_genre == DG_SHIP) {
3398 if (optional_string("+Field Debris Type:")) {
3399 stuff_int(&Asteroid_field.field_debris_type[0]);
3401 if (optional_string("+Field Debris Type:")) {
3402 stuff_int(&Asteroid_field.field_debris_type[1]);
3404 if (optional_string("+Field Debris Type:")) {
3405 stuff_int(&Asteroid_field.field_debris_type[2]);
3409 if (optional_string("+Field Debris Type:")) {
3410 stuff_int(&subtype);
3411 Asteroid_field.field_debris_type[subtype] = 1;
3414 if (optional_string("+Field Debris Type:")) {
3415 stuff_int(&subtype);
3416 Asteroid_field.field_debris_type[subtype] = 1;
3419 if (optional_string("+Field Debris Type:")) {
3420 stuff_int(&subtype);
3421 Asteroid_field.field_debris_type[subtype] = 1;
3426 // backward compatibility
3427 if ( (Asteroid_field.debris_genre == DG_ASTEROID) && (count == 0) ) {
3428 Asteroid_field.field_debris_type[0] = 0;
3431 required_string("$Average Speed:");
3432 stuff_float(&speed);
3434 vm_vec_rand_vec_quick(&Asteroid_field.vel);
3435 vm_vec_scale(&Asteroid_field.vel, speed);
3437 Asteroid_field.speed = speed;
3439 required_string("$Minimum:");
3440 stuff_vector(&Asteroid_field.min_bound);
3442 required_string("$Maximum:");
3443 stuff_vector(&Asteroid_field.max_bound);
3445 if (optional_string("+Inner Bound:")) {
3446 Asteroid_field.has_inner_bound = 1;
3448 required_string("$Minimum:");
3449 stuff_vector(&Asteroid_field.inner_min_bound);
3451 required_string("$Maximum:");
3452 stuff_vector(&Asteroid_field.inner_max_bound);
3454 Asteroid_field.has_inner_bound = 0;
3461 void parse_variables()
3463 if (! optional_string("#Sexp_variables") ) {
3467 num_variables = stuff_sexp_variable_list();
3472 void parse_mission(mission *pm, int flag)
3476 Player_starts = Num_cargo = Num_waypoint_lists = Num_goals = num_wings = num_ship_arrivals = 0;
3477 Player_start_shipnum = -1;
3478 *Player_start_shipname = 0; // make the string 0 length for checking later
3479 memset( &Player_start_pobject, 0, sizeof(Player_start_pobject) );
3481 // initialize the initially_docked array.
3482 for ( i = 0; i < MAX_SHIPS; i++ ) {
3483 Initially_docked[i].docker = NULL;
3484 Initially_docked[i].dockee[0] = '\0';
3485 Initially_docked[i].docker_point[0] = '\0';
3486 Initially_docked[i].dockee_point[0] = '\0';
3488 Total_initially_docked = 0;
3490 list_init( &ship_arrival_list ); // init lists for arrival objects and wings
3495 parse_mission_info(pm);
3496 Current_file_checksum = netmisc_calc_checksum(pm,MISSION_CHECKSUM_SIZE);
3497 if ( flag == MISSION_PARSE_MISSION_INFO )
3499 parse_plot_info(pm);
3501 parse_briefing_info(pm); // TODO: obsolete code, keeping so we don't obsolete existing mission files
3502 parse_cmd_briefs(pm);
3504 parse_debriefing_new(pm);
3505 parse_player_info(pm);
3506 parse_objects(pm, flag);
3510 parse_waypoints(pm);
3512 parse_reinforcements(pm);
3516 post_process_mission();
3519 void post_process_mission()
3522 int indices[MAX_SHIPS], objnum;
3527 // the player_start_shipname had better exist at this point!
3528 Player_start_shipnum = ship_name_lookup( Player_start_shipname );
3529 Assert ( Player_start_shipnum != -1 );
3530 Assert ( Player_start_pobject.flags & P_SF_PLAYER_START_VALID );
3532 // Assign objnum, shipnum, etc. to the player structure
3533 objnum = Ships[Player_start_shipnum].objnum;
3534 Player_obj = &Objects[objnum];
3536 Player->objnum = objnum;
3539 Player_obj->flags |= OF_PLAYER_SHIP; // make this object a player controlled ship.
3540 Player_ship = &Ships[Player_start_shipnum];
3541 Player_ai = &Ai_info[Player_ship->ai_index];
3543 Player_ai->targeted_subsys = NULL;
3544 Player_ai->targeted_subsys_parent = -1;
3546 // determine if player start has initial velocity and set forward cruise percent to relect this
3547 if ( Player_obj->phys_info.vel.xyz.z > 0.0f )
3548 Player->ci.forward_cruise_percent = Player_obj->phys_info.vel.xyz.z / Player_ship->current_max_speed * 100.0f;
3550 // put in hard coded starting wing names.
3551 if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){
3552 Starting_wings[0] = wing_name_lookup(Starting_wing_names[0],1);
3553 Starting_wings[1] = wing_name_lookup(Starting_wing_names[MAX_STARTING_WINGS],1);
3555 for (i = 0; i < MAX_STARTING_WINGS; i++ ) {
3556 Starting_wings[i] = wing_name_lookup(Starting_wing_names[i], 1);
3562 // call a function to deal with intially docked ships
3563 mission_parse_do_initial_docks();
3565 // deal with setting up arrival location for all ships. Must do this now after all ships are created
3566 mission_parse_set_arrival_locations();
3568 // clear out information about arriving support ships
3569 Arriving_support_ship = NULL;
3570 Num_arriving_repair_targets = 0;
3572 // convert all ship name indices to ship indices now that mission has been loaded
3574 for (i=0; i<Num_parse_names; i++) {
3575 indices[i] = ship_name_lookup(Parse_names[i], 1);
3577 Warning(LOCATION, "Ship name \"%s\" referenced, but this ship doesn't exist", Parse_names[i]);
3580 for (i=0; i<MAX_SHIPS; i++) {
3581 if ((Ships[i].objnum >= 0) && (Ships[i].arrival_anchor >= 0) && (Ships[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHORS_OFFSET))
3582 Ships[i].arrival_anchor = indices[Ships[i].arrival_anchor];
3584 if ( (Ships[i].objnum >= 0) && (Ships[i].departure_anchor >= 0) )
3585 Ships[i].departure_anchor = indices[Ships[i].departure_anchor];
3588 for (i=0; i<MAX_WINGS; i++) {
3589 if (Wings[i].wave_count && (Wings[i].arrival_anchor >= 0) && (Wings[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHORS_OFFSET))
3590 Wings[i].arrival_anchor = indices[Wings[i].arrival_anchor];
3592 if (Wings[i].wave_count && (Wings[i].departure_anchor >= 0) )
3593 Wings[i].departure_anchor = indices[Wings[i].departure_anchor];
3598 // before doing anything else, we must validate all of the sexpressions that were loaded into the mission.
3599 // Loop through the Sexp_nodes array and send the top level functions to the check_sexp_syntax parser
3601 for (i = 0; i < MAX_SEXP_NODES; i++) {
3602 if ( is_sexp_top_level(i) && (!Fred_running || (i != Sexp_clipboard))) {
3603 int result, bindex, op;
3605 op = identify_operator(CTEXT(i));
3606 Assert(op != -1); // need to make sure it is an operator before we treat it like one..
3607 result = check_sexp_syntax( i, query_operator_return_type(op), 1, &bindex);
3609 // entering this if statement will result in program termination!!!!!
3610 // print out an error based on the return value from check_sexp_syntax()
3612 char sexp_str[8192], text[8192];
3614 convert_sexp_to_string( i, sexp_str, SEXP_ERROR_CHECK_MODE);
3615 sprintf(text, "%s.\n\nIn sexpression: %s\n(Error appears to be: %s)",
3616 sexp_error_message(result), sexp_str, Sexp_nodes[bindex].text);
3619 Error( LOCATION, text );
3621 Warning( LOCATION, text );
3626 ai_post_process_mission();
3630 for (i=0; i<Total_initially_docked; i++) {
3631 z = ship_name_lookup(Initially_docked[i].dockee);
3633 Assert(Initially_docked[i].docker->type == OBJ_SHIP);
3634 p1 = model_find_dock_name_index(Ships[Initially_docked[i].docker->instance].modelnum,
3635 Initially_docked[i].docker_point);
3636 Assert(Objects[z].type == OBJ_SHIP);
3637 p2 = model_find_dock_name_index(Ships[Objects[z].instance].modelnum,
3638 Initially_docked[i].dockee_point);
3640 if ((p1 >= 0) && (p2 >= 0)) {
3641 nprintf(("AI", "Initially Docked: %s with %s\n", Ships[Initially_docked[i].docker->instance].ship_name, Ships[Objects[z].instance].ship_name));
3642 if (ship_docking_valid(Initially_docked[i].docker->instance, Objects[z].instance)) // only dock if they are allowed to be docked.
3643 ai_dock_with_object(Initially_docked[i].docker, &Objects[z], 89, AIDO_DOCK_NOW, p1, p2);
3646 Int3(); // Curious. Two ships told to dock, but one of the dock points is bogus.
3647 // Get Allender or Hoffoss, one of whom probably wrote the above if ()
3652 // we must also count all of the ships of particular types. We count all of the ships that do not have
3653 // their SF_IGNORE_COUNT flag set. We don't count ships in wings when the equivalent wing flag is set.
3654 // in counting ships in wings, we increment the count by the wing's wave count to account for everyone.
3655 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3656 int siflags, num, shipnum;
3658 shipnum = Objects[so->objnum].instance;
3659 // pass over non-ship objects and player ship objects
3660 if ( Ships[shipnum].objnum == -1 || (Objects[Ships[shipnum].objnum].flags & OF_PLAYER_SHIP) )
3662 if ( Ships[shipnum].flags & SF_IGNORE_COUNT )
3664 if ( (Ships[shipnum].wingnum != -1) && (Wings[Ships[shipnum].wingnum].flags & WF_IGNORE_COUNT) )
3667 siflags = Ship_info[Ships[shipnum].ship_info_index].flags;
3669 // determine the number of times we need to add this ship into the count
3670 // if ( Ships[i].wingnum == -1 )
3673 // num = Wings[Ships[i].wingnum].num_waves;
3675 ship_add_ship_type_count( siflags, num );
3677 // now go through the list of ships yet to arrive
3678 for ( p_objp = GET_FIRST(&ship_arrival_list); p_objp != END_OF_LIST(&ship_arrival_list); p_objp = GET_NEXT(p_objp) ) {
3681 // go through similar motions as above
3682 if ( p_objp->flags & P_SF_IGNORE_COUNT )
3684 if ( (p_objp->wingnum != -1) && (Wings[p_objp->wingnum].flags & WF_IGNORE_COUNT) )
3687 siflags = Ship_info[p_objp->ship_class].flags;
3689 if ( p_objp->wingnum == -1 )
3692 num = Wings[p_objp->wingnum].num_waves - 1; // subtract one since we already counted the first wave
3694 ship_add_ship_type_count( siflags, num );
3697 // set player weapons that are selected by default
3698 // AL 09/17/97: I added this code to select the first primary/secondary weapons,
3699 // since I noticed the player ship sometimes doesn't get default weapons selected
3701 // DB: modified 4/23/98 to take multiplayer into account. Under certain circumstances, multiplayer netplayer ships
3702 // had their current_primary_bank and current_secondary_bank set to -1 (from ship_set()) and left there since
3703 // Player_ship is not the only one we need to need about.
3704 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3705 ship *shipp = &Ships[Objects[so->objnum].instance];
3707 // don't process non player wing ships
3708 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
3711 swp = &shipp->weapons;
3713 // swp = &Player_ship->weapons;
3714 if ( swp->num_primary_banks > 0 ) {
3715 swp->current_primary_bank = 0; // currently selected primary bank
3718 if ( swp->num_secondary_banks > 0 ) {
3719 swp->current_secondary_bank = 0; // currently selected secondary bank
3723 ets_init_ship(Player_obj); // init ETS data for the player
3725 // put the timestamp stuff here for now
3726 Mission_arrival_timestamp = timestamp( ARRIVAL_TIMESTAMP );
3727 Mission_departure_timestamp = timestamp( DEPARTURE_TIMESTAMP );
3728 Mission_end_time = -1;
3730 if(Game_mode & GM_MULTIPLAYER){
3731 multi_respawn_build_points();
3734 // maybe reset hotkey defaults when loading new mission
3735 if ( Last_file_checksum != Current_file_checksum ){
3736 mission_hotkey_reset_saved();
3739 Allow_arrival_music_timestamp=timestamp(0);
3740 Allow_arrival_message_timestamp=timestamp(0);
3741 Arrival_message_delay_timestamp = timestamp(-1);
3744 for(idx=0; idx<2; idx++){
3745 Allow_arrival_music_timestamp_m[idx]=timestamp(0);
3746 Allow_arrival_message_timestamp_m[idx]=timestamp(0);
3747 Arrival_message_delay_timestamp_m[idx] = timestamp(-1);
3750 Last_file_checksum = Current_file_checksum;
3753 int get_mission_info(char *filename, mission *mission_p)
3757 // if mission_p is NULL, make it point to The_mission
3758 if ( mission_p == NULL )
3759 mission_p = &The_mission;
3761 if ((rval = setjmp(parse_abort)) != 0) {
3762 nprintf(("Error", "Error abort! Code = %d", rval));
3768 // open localization
3771 CFILE *ftemp = cfopen(filename, "rt");
3773 // close localization
3779 // 7/9/98 -- MWA -- check for 0 length file.
3780 filelength = cfilelength(ftemp);
3782 if ( filelength == 0 ){
3783 // close localization
3789 read_file_text(filename, CF_TYPE_MISSIONS);
3790 memset( mission_p, 0, sizeof(mission) );
3792 parse_mission_info(mission_p);
3794 // close localization
3801 // mai parse routine for parsing a mission. The default parameter flags tells us which information
3802 // to get when parsing the mission. 0 means get everything (default). Other flags just gets us basic
3803 // info such as game type, number of players etc.
3804 int parse_main(char *mission_name, int flags)
3808 // fill in Ship_class_names array with the names from the ship_info struct;
3809 Num_parse_names = 0;
3810 Mission_all_attack = 0; // Might get set in mission load.
3811 Assert(Num_ship_types < MAX_SHIP_TYPES);
3813 for (i = 0; i < Num_ship_types; i++)
3814 Ship_class_names[i] = Ship_info[i].name;
3816 if ((rval = setjmp(parse_abort)) != 0) {
3817 nprintf(("Error", "Error abort! Code = %i.", rval));
3821 // open localization
3824 CFILE *ftemp = cfopen(mission_name, "rt", CFILE_NORMAL, CF_TYPE_MISSIONS);
3828 Error( LOCATION, "Couldn't open mission '%s'\n", mission_name );
3830 Current_file_length = -1;
3831 Current_file_checksum = 0;
3833 // close localization
3839 Current_file_length = cfilelength(ftemp);
3842 read_file_text(mission_name, CF_TYPE_MISSIONS);
3843 memset(&The_mission, 0, sizeof(The_mission));
3844 parse_mission(&The_mission, flags);
3845 display_parse_diagnostics();
3847 // close localization
3852 strcpy(Mission_filename, mission_name);
3857 // sets the arrival lcoation of the ships in wingp. pass num_to_set since the threshold value
3858 // for wings may have us create more ships in the wing when there are still some remaining
3859 void mission_set_wing_arrival_location( wing *wingp, int num_to_set )
3863 // get the starting index into the ship_index array of the first ship whose location we need set.
3865 index = wingp->current_count - num_to_set;
3866 if ( (wingp->arrival_location == ARRIVE_FROM_DOCK_BAY) || (wingp->arrival_location == ARRIVE_AT_LOCATION) ) {
3867 while ( index < wingp->current_count ) {
3870 objp = &Objects[Ships[wingp->ship_index[index]].objnum];
3871 mission_set_arrival_location(wingp->arrival_anchor, wingp->arrival_location, wingp->arrival_distance, OBJ_INDEX(objp), NULL, NULL);
3876 object *leader_objp;
3881 // wing is not arriving from a docking bay -- possibly move them based on arriving near
3882 // or in front of some other ship.
3883 index = wingp->current_count - num_to_set;
3884 leader_objp = &Objects[Ships[wingp->ship_index[index]].objnum];
3885 if (mission_set_arrival_location(wingp->arrival_anchor, wingp->arrival_location, wingp->arrival_distance, OBJ_INDEX(leader_objp), &pos, &orient)) {
3886 // modify the remaining ships created
3889 while ( index < wingp->current_count ) {
3892 objp = &Objects[Ships[wingp->ship_index[index]].objnum];
3894 // change the position of the next ships in the wing. Use the cool function in AiCode.cpp which
3895 // Mike K wrote to give new positions to the wing members.
3896 get_absolute_wing_pos( &objp->pos, leader_objp, wing_index++, 0);
3897 memcpy( &objp->orient, &orient, sizeof(matrix) );
3904 // create warp effect if in mission and not arriving from docking bay
3905 if ( (Game_mode & GM_IN_MISSION) && (wingp->arrival_location != ARRIVE_FROM_DOCK_BAY) ) {
3906 for ( index = wingp->current_count - num_to_set; index < wingp->current_count; index ++ ) {
3907 shipfx_warpin_start( &Objects[Ships[wingp->ship_index[index]].objnum] );
3912 // this function is called after a mission is parsed to set the arrival locations of all ships in the
3913 // mission to the apprioriate spot. Mainly needed because ships might be in dock bays to start
3914 // the mission, so their AI mode must be set appropriately.
3915 void mission_parse_set_arrival_locations()
3923 obj_merge_created_list();
3924 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
3927 if ( objp->type != OBJ_SHIP )
3930 shipp = &Ships[objp->instance];
3931 // if the ship is in a wing -- ignore the info and let the wing info handle it
3932 if ( shipp->wingnum != -1 )
3935 // call function to set arrival location for this ship.
3936 mission_set_arrival_location( shipp->arrival_anchor, shipp->arrival_location, shipp->arrival_distance, OBJ_INDEX(objp), NULL, NULL);
3940 for ( i = 0; i < num_wings; i++ ) {
3942 // if wing has no ships, then don't process it.
3943 if ( Wings[i].current_count == 0 )
3946 mission_set_wing_arrival_location( &Wings[i], Wings[i].current_count );
3951 // function which iterates through the ship_arrival_list and creates any ship which
3952 // should be intially docked with a ship which currently exists in the mission
3953 void mission_parse_do_initial_docks()
3955 p_object *pobjp, *tmp;
3957 pobjp = GET_FIRST( &ship_arrival_list );
3958 while ( pobjp != END_OF_LIST(&ship_arrival_list) ) {
3961 tmp = GET_NEXT(pobjp);
3963 // see if the flag for initial docked is set
3964 if ( pobjp->flags & P_SF_INITIALLY_DOCKED ) {
3965 // see if who this parse object is supposed to be docked with is in the mission
3966 shipnum = ship_name_lookup( pobjp->docked_with );
3967 if ( shipnum != -1 ) {
3970 // the ship exists, so create this object, then dock the two.
3971 objnum = parse_create_object( pobjp );
3972 Assert ( objnum != -1 );
3974 list_remove( &ship_arrival_list, pobjp);
3976 // p1 is the parse object's docking point.
3977 // p2 is the existing objects docking point.
3978 p1 = model_find_dock_name_index(Ships[shipnum].modelnum, pobjp->docker_point);
3979 p2 = model_find_dock_name_index(Ships[Objects[objnum].instance].modelnum, pobjp->dockee_point);
3981 if ((p1 >= 0) && (p2 >= 0)) {
3982 nprintf(("AI", "Initially Docked: %s with %s\n", Ships[shipnum].ship_name, Ships[Objects[objnum].instance].ship_name));
3983 if (ship_docking_valid(shipnum, Objects[objnum].instance)) // only dock if they are allowed to be docked.
3984 ai_dock_with_object(&Objects[Ships[shipnum].objnum], &Objects[objnum], 89, AIDO_DOCK_NOW, p1, p2);
3986 ai_dock_with_object(&Objects[objnum], &Objects[Ships[shipnum].objnum], 89, AIDO_DOCK_NOW, p2, p1);
3989 Int3(); // Curious. Two ships told to dock, but one of the dock points is bogus.
3990 // Get Allender or Hoffoss, one of whom probably wrote the above if ()
3998 // function which returns true or false if the given mission support multiplayers
3999 int mission_parse_is_multi(char *filename, char *mission_name)
4001 int rval, game_type;
4005 // new way of getting information. Open the file, and just get the name and the game_type flags.
4006 // return the flags if a multiplayer mission
4010 ftemp = cfopen(filename, "rt");
4014 // 7/9/98 -- MWA -- check for 0 length file.
4015 filelength = cfilelength(ftemp);
4017 if ( filelength == 0 )
4020 // open localization
4023 if ((rval = setjmp(parse_abort)) != 0) {
4024 Error(LOCATION, "Bogus! Trying to get multi game type on mission %s returned as a mission from cf_get_filelist\n");
4026 read_file_text(filename, CF_TYPE_MISSIONS);
4028 if ( skip_to_string("$Name:") != 1 ) {
4029 nprintf(("Network", "Unable to process %s because we couldn't find $Name:", filename));
4031 // close localization
4036 stuff_string( mission_name, F_NAME, NULL );
4037 if ( skip_to_string("+Game Type Flags:") != 1 ) {
4038 nprintf(("Network", "Unable to process %s because we couldn't find +Game Type Flags:\n", filename));
4040 // close localization
4045 stuff_int(&game_type);
4047 if ( game_type & MISSION_TYPE_MULTI ){
4048 // close localization
4054 // close localization
4060 // function which gets called to retrieve useful information about a mission. We will get the
4061 // name, description, and number of players for a mission. Probably used for multiplayer only?
4062 // The calling function can use the information in The_mission to get the name/description of the mission
4065 int mission_parse_get_multi_mission_info( char *filename )
4067 if ( parse_main(filename, MISSION_PARSE_MISSION_INFO) ){
4071 Assert( The_mission.game_type & MISSION_TYPE_MULTI ); // assume multiplayer only for now?
4073 // return the number of parse_players. later, we might want to include (optionally?) the number
4074 // of other ships in the main players wing (usually wing 'alpha') for inclusion of number of
4077 return The_mission.num_players;
4080 // returns true or false if this is on the yet to arrive list
4081 int mission_parse_ship_arrived( char *shipname )
4085 for ( objp = GET_FIRST(&ship_arrival_list); objp !=END_OF_LIST(&ship_arrival_list); objp = GET_NEXT(objp) ) {
4086 if ( !stricmp( objp->name, shipname) )
4087 return 0; // still on the arrival list
4092 // return the parse object on the ship arrival list associated with the given name
4093 p_object *mission_parse_get_arrival_ship( char *name )
4097 for ( objp = GET_FIRST(&ship_arrival_list); objp !=END_OF_LIST(&ship_arrival_list); objp = GET_NEXT(objp) ) {
4098 if ( !stricmp( objp->name, name) )
4099 return objp; // still on the arrival list
4105 // return the parse object on the ship arrival list associated with the given signature
4106 p_object *mission_parse_get_arrival_ship( ushort net_signature )
4110 for ( objp = GET_FIRST(&ship_arrival_list); objp !=END_OF_LIST(&ship_arrival_list); objp = GET_NEXT(objp) ) {
4111 if ( objp->net_signature == net_signature )
4112 return objp; // still on the arrival list
4118 // mission_set_arrival_location() sets the arrival location of a parse object according to the arrival location
4119 // of the object. Returns true if object set to new position, false if not.
4120 int mission_set_arrival_location(int anchor, int location, int dist, int objnum, vector *new_pos, matrix *new_orient)
4122 int shipnum, anchor_objnum;
4123 vector anchor_pos, rand_vec, new_fvec;
4126 if ( location == ARRIVE_AT_LOCATION )
4129 Assert(anchor >= 0);
4131 // this ship might possibly arrive at another location. The location is based on the
4132 // proximity of some ship (and some other special tokens)
4134 // if we didn't find the arrival anchor in the list of special nodes, then do a
4135 // ship name lookup on the anchor
4136 if (anchor < SPECIAL_ARRIVAL_ANCHORS_OFFSET) {
4137 shipnum = ship_name_lookup(Parse_names[anchor]);
4138 if ( shipnum == -1 ) {
4139 Assert ( location != ARRIVE_FROM_DOCK_BAY ); // bogus data somewhere!!! get mwa
4140 nprintf (("allender", "couldn't find ship for arrival anchor -- using location ship created at"));
4145 // come up with a position based on the special token names
4148 if (anchor == ANY_FRIENDLY) {
4149 shipnum = ship_get_random_team_ship( TEAM_FRIENDLY, SHIP_GET_ANY_SHIP );
4150 } else if (anchor == ANY_HOSTILE) {
4151 shipnum = ship_get_random_team_ship( opposing_team_mask(Player_ship->team), SHIP_GET_ANY_SHIP );
4152 } else if (anchor == ANY_FRIENDLY_PLAYER) {
4153 shipnum = ship_get_random_team_ship( TEAM_FRIENDLY, SHIP_GET_ONLY_PLAYERS );
4154 } else if (anchor == ANY_HOSTILE_PLAYER) {
4155 shipnum = ship_get_random_team_ship( opposing_team_mask(Player_ship->team), SHIP_GET_ONLY_PLAYERS );
4157 Int3(); // get allender -- unknown special arrival instructions
4159 // if we didn't get an object from one of the above functions, then make the object
4160 // arrive at it's placed location
4161 if ( shipnum == -1 ) {
4162 nprintf (("Allender", "Couldn't find random ship for arrival anchor -- using default location\n"));
4167 // take the shipnum and get the position. once we have positions, we can determine where
4168 // to make this ship appear
4169 Assert ( shipnum != -1 );
4170 anchor_objnum = Ships[shipnum].objnum;
4171 anchor_pos = Objects[anchor_objnum].pos;
4173 // if arriving from docking bay, then set ai mode and call function as per AL's instructions.
4174 if ( location == ARRIVE_FROM_DOCK_BAY ) {
4177 // if we get an error, just let the ship arrive(?)
4178 if ( ai_acquire_emerge_path(&Objects[objnum], anchor_objnum, &pos, &fvec) == -1 ) {
4179 Int3(); // get MWA or AL -- not sure what to do here when we cannot acquire a path
4182 Objects[objnum].pos = pos;
4183 Objects[objnum].orient.v.fvec = fvec;
4186 // AL: ensure dist > 0 (otherwise get errors in vecmat)
4187 // TODO: maybe set distance to 2x ship radius of ship appearing in front of?
4189 Error(LOCATION, "Distance of %d is invalid in mission_set_arrival_location\n", dist);
4193 // get a vector which is the ships arrival position based on the type of arrival
4194 // this ship should have. Arriving near a ship we use a random normalized vector
4195 // scaled by the distance given by the designer. Arriving in front of a ship means
4196 // entering the battle in the view cone.
4197 if ( location == ARRIVE_NEAR_SHIP ) {
4198 // get a random vector -- use static randvec if in multiplayer
4199 if ( Game_mode & GM_NORMAL )
4200 vm_vec_rand_vec_quick(&rand_vec);
4202 static_randvec( Objects[objnum].net_signature, &rand_vec );
4203 } else if ( location == ARRIVE_IN_FRONT_OF_SHIP ) {
4208 // cool function by MK to give a reasonable random vector "in front" of a ship
4209 // rvec and uvec are the right and up vectors.
4210 // If these are not available, this would be an expensive method.
4212 x = (float)cos(ANG_TO_RAD(45));
4213 if ( Game_mode & GM_NORMAL ) {
4214 r1 = rand() < RAND_MAX/2 ? -1 : 1;
4215 r2 = rand() < RAND_MAX/2 ? -1 : 1;
4217 // in multiplayer, use the static rand functions so that all clients can get the
4218 // same information.
4219 r1 = static_rand(Objects[objnum].net_signature) < RAND_MAX/2 ? -1 : 1;
4220 r2 = static_rand(Objects[objnum].net_signature+1) < RAND_MAX/2 ? -1 : 1;
4223 vm_vec_copy_scale(&t1, &(Objects[anchor_objnum].orient.v.fvec), x);
4224 vm_vec_copy_scale(&t2, &(Objects[anchor_objnum].orient.v.rvec), (1.0f - x) * r1);
4225 vm_vec_copy_scale(&t3, &(Objects[anchor_objnum].orient.v.uvec), (1.0f - x) * r2);
4227 vm_vec_add(&rand_vec, &t1, &t2);
4228 vm_vec_add2(&rand_vec, &t3);
4229 vm_vec_normalize(&rand_vec);
4232 // add in the radius of the two ships involved. This will make the ship arrive further than
4233 // specified, but will appear more accurate since we are pushing the edge of the model to the
4234 // specified distance. large objects appears to be a lot closer without the following line because
4235 // the object centers were at the correct distance, but the model itself was much closer to the
4237 dist += (int)Objects[objnum].radius + (int)Objects[anchor_objnum].radius;
4238 vm_vec_scale_add(&Objects[objnum].pos, &anchor_pos, &rand_vec, (float)dist);
4240 // I think that we will always want to orient the ship that is arriving to face towards
4241 // the ship it is arriving near/in front of. The effect will be cool!
4243 // calculate the new fvec of the ship arriving and use only that to get the matrix. isn't a big
4244 // deal not getting bank.
4245 vm_vec_sub(&new_fvec, &anchor_pos, &Objects[objnum].pos );
4246 vm_vector_2_matrix( &orient, &new_fvec, NULL, NULL );
4247 Objects[objnum].orient = orient;
4250 // set the new_pos parameter since it might be used outside the function (i.e. when dealing with wings).
4252 memcpy(new_pos, &Objects[objnum].pos, sizeof(vector) );
4255 memcpy( new_orient, &Objects[objnum].orient, sizeof(matrix) );
4260 // mark a reinforcement as available
4261 void mission_parse_mark_reinforcement_available(char *name)
4266 for (i = 0; i < Num_reinforcements; i++) {
4267 rp = &Reinforcements[i];
4268 if ( !stricmp(rp->name, name) ) {
4269 if ( !(rp->flags & RF_IS_AVAILABLE) ) {
4270 rp->flags |= RF_IS_AVAILABLE;
4272 // tell all of the clients.
4273 if ( MULTIPLAYER_MASTER ) {
4274 send_reinforcement_avail( i );
4281 Assert ( i < Num_reinforcements );
4284 // mission_did_ship_arrive takes a parse object and checked the arrival cue and delay and
4285 // creates the object if necessary. Returns -1 if not created. objnum of created ship otherwise
4286 int mission_did_ship_arrive(p_object *objp)
4290 // find out in the arrival cue became true
4291 did_arrive = eval_sexp(objp->arrival_cue);
4293 // we must first check to see if this ship is a reinforcement or not. If so, then don't
4295 if ( objp->flags & P_SF_REINFORCEMENT ) {
4297 // if this ship did arrive, mark the reinforcement as available, and tell clients if in multiplayer
4300 mission_parse_mark_reinforcement_available(objp->name);
4305 if ( did_arrive ) { // has the arrival criteria been met?
4308 Assert ( !(objp->flags & P_SF_CANNOT_ARRIVE) ); // get allender
4310 // check to see if the delay field <= 0. if so, then create a timestamp and then maybe
4311 // create the object
4312 if ( objp->arrival_delay <= 0 ) {
4313 objp->arrival_delay = timestamp( -objp->arrival_delay * 1000 );
4314 Assert( objp->arrival_delay >= 0 );
4317 // if the timestamp hasn't elapsed, move onto the next ship.
4318 if ( !timestamp_elapsed(objp->arrival_delay) )
4321 // check to see if this ship is to arrive via a docking bay. If so, and the ship to arrive from
4322 // doesn't exist, don't create.
4323 if ( objp->arrival_location == ARRIVE_FROM_DOCK_BAY ) {
4327 Assert( objp->arrival_anchor >= 0 );
4328 name = Parse_names[objp->arrival_anchor];
4330 // see if ship is yet to arrive. If so, then return -1 so we can evaluate again later.
4331 if ( mission_parse_get_arrival_ship( name ) )
4334 // see if ship is in mission. If not, then we can assume it was destroyed or departed since
4335 // it is not on the arrival list (as shown by above if statement).
4336 shipnum = ship_name_lookup( name );
4337 if ( shipnum == -1 ) {
4338 Sexp_nodes[objp->arrival_cue].value = SEXP_KNOWN_FALSE;
4343 object_num = parse_create_object(objp); // create the ship
4345 // since this ship is not in a wing, create a SHIP_ARRIVE entry
4346 //mission_log_add_entry( LOG_SHIP_ARRIVE, objp->name, NULL );
4347 Assert(object_num >= 0 && object_num < MAX_OBJECTS);
4349 // Play the music track for an arrival
4350 if ( !(Ships[Objects[object_num].instance].flags & SF_NO_ARRIVAL_MUSIC) )
4351 if ( timestamp_elapsed(Allow_arrival_music_timestamp) ) {
4352 Allow_arrival_music_timestamp = timestamp(ARRIVAL_MUSIC_MIN_SEPARATION);
4353 event_music_arrival(Ships[Objects[object_num].instance].team);
4357 // check to see if the arrival cue of this ship is known false -- if so, then remove
4358 // the parse object from the ship
4359 if ( Sexp_nodes[objp->arrival_cue].value == SEXP_KNOWN_FALSE )
4360 objp->flags |= P_SF_CANNOT_ARRIVE;
4367 // funciton to set a flag on all parse objects on ship arrival list which cannot
4368 // arrive in the mission
4369 void mission_parse_mark_non_arrivals()
4373 for ( pobjp = GET_FIRST(&ship_arrival_list); pobjp != END_OF_LIST(&ship_arrival_list); pobjp = GET_NEXT(pobjp) ) {
4374 if ( pobjp->wingnum != -1 ) {
4375 if ( Sexp_nodes[Wings[pobjp->wingnum].arrival_cue].value == SEXP_KNOWN_FALSE )
4376 pobjp->flags |= P_SF_CANNOT_ARRIVE;
4378 if ( Sexp_nodes[pobjp->arrival_cue].value == SEXP_KNOWN_FALSE )
4379 pobjp->flags |= P_SF_CANNOT_ARRIVE;
4384 // function to deal with support ship arrival. objnum is the object number of the arriving support
4385 // ship. This function can get called from either single or multiplayer. Needed to that clients
4386 // can know when to abort rearm.
4387 void mission_parse_support_arrived( int objnum )
4391 // when the support ship arrives, the shipname it is supposed to repair is in the 'misc'
4392 // field of the parse_object. If the ship still exists, call ai function which actually
4393 // issues the goal for the repair
4394 for ( i = 0; i < Num_arriving_repair_targets; i++ ) {
4397 shipnum = ship_name_lookup( Arriving_repair_targets[i] );
4399 if ( shipnum != -1 ) {
4400 object *requester_objp, *support_objp;
4402 support_objp = &Objects[objnum];
4403 requester_objp = &Objects[Ships[shipnum].objnum];
4404 ai_add_rearm_goal( requester_objp, support_objp );
4408 // MK: A bit of a hack. If on player's team and player isn't allowed shields, don't give this ship shields.
4409 if ((Player_obj->flags & OF_NO_SHIELDS) && (Player_ship->team == Ships[Objects[objnum].instance].team))
4410 Objects[objnum].flags |= OF_NO_SHIELDS;
4412 Ships[Objects[objnum].instance].flags |= SF_WARPED_SUPPORT;
4414 Arriving_support_ship = NULL;
4415 Num_arriving_repair_targets = 0;
4418 MONITOR(NumShipArrivals);
4420 // mission_parse_arrivals will parse the lists of arriving ships and
4421 // wings -- creating new ships/wings if the arrival criteria have been
4423 void mission_eval_arrivals()
4429 // before checking arrivals, check to see if we should play a message concerning arrivals
4430 // of other wings. We use the timestamps to delay the arrival message slightly for
4432 if ( timestamp_valid(Arrival_message_delay_timestamp) && timestamp_elapsed(Arrival_message_delay_timestamp) && !((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ){
4433 int rship, use_terran;
4435 // use terran command 25% of time
4436 use_terran = ((frand() - 0.75) > 0.0f)?1:0;
4438 rship = ship_get_random_player_wing_ship( SHIP_GET_NO_PLAYERS );
4439 if ( (rship == -1) || use_terran ){
4440 message_send_builtin_to_player( MESSAGE_ARRIVE_ENEMY, NULL, MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4441 } else if ( rship != -1 ) {
4442 message_send_builtin_to_player( MESSAGE_ARRIVE_ENEMY, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4445 Arrival_message_delay_timestamp = timestamp(-1); // make the stamp invalid
4448 // if ( !timestamp_elapsed(Mission_arrival_timestamp) )
4451 // check the ship_arrival_list
4453 objp = GET_FIRST(&ship_arrival_list);
4454 while( objp !=END_OF_LIST(&ship_arrival_list) ) {
4455 p_object *temp = GET_NEXT(objp);
4456 if ( objp->wingnum == -1 ) { // if this object has a wing -- let code for wings determine if it should be created
4458 objnum = mission_did_ship_arrive( objp );
4459 if ( objnum != -1 ) {
4460 list_remove( &ship_arrival_list, objp);
4461 MONITOR_INC(NumShipArrivals,1);
4468 // check for any initially docked ships. Do it after all are created since the next function
4469 // messes with the ship_arrival_list
4470 mission_parse_do_initial_docks(); // maybe create it's docked counterpart
4472 mission_parse_mark_non_arrivals(); // mark parse objects which can no longer arrive
4474 // check the support ship arrival list
4475 if ( Arriving_support_ship ) {
4478 objnum = mission_did_ship_arrive( Arriving_support_ship );
4480 if ( objnum != -1 ) {
4481 MONITOR_INC(NumShipArrivals,1);
4482 mission_parse_support_arrived( objnum );
4486 // we must also check to see if there are waves of a wing that must
4487 // reappear if all the ships of the current wing have been destroyed or
4488 // have departed. If this is the case, then create the next wave.
4490 for ( i = 0; i < num_wings; i++ ) {
4493 // should we process this wing anymore
4494 if ( wingp->flags & WF_WING_GONE )
4497 // if we have a reinforcement wing, then don't try to create new ships automatically.
4498 if ( wingp->flags & WF_REINFORCEMENT ) {
4500 // check to see in the wings arrival cue is true, and if so, then mark the reinforcement
4502 if ( eval_sexp(wingp->arrival_cue) ) {
4503 mission_parse_mark_reinforcement_available(wingp->name);
4508 // don't do evaluations for departing wings
4509 if ( wingp->flags & WF_WING_DEPARTING ){
4513 // must check to see if we are at the last wave. Code above to determine when a wing is gone only
4514 // gets run when a ship is destroyed (not every N seconds like it used to). Do a quick check
4516 if ( wingp->current_wave == wingp->num_waves ){
4520 // if the current wave of this wing is 0, then we haven't created the ships in the wing yet.
4521 // call parse_wing_create_ships to try and create it. That function will eval the arrival
4522 // cue of the wing and create the ships if necessary, or if the threshold of the wing has
4523 // been reached, then try and create more ships
4524 if ( (wingp->current_wave == 0) || (wingp->current_count <= wingp->threshold) ) {
4527 created = parse_wing_create_ships( wingp, wingp->wave_count );
4529 // if we created ships in this wing, check to see if the wings was int the reinforcements
4530 // array. If so, then if we have more uses, then reset the reinforcement flag for the wing
4531 // so the user can call in another set if need be.
4532 if ( created > 0 ) {
4535 mission_parse_do_initial_docks(); // maybe create other initially docked ships
4536 if ( Wings[i].flags & WF_RESET_REINFORCEMENT ) {
4537 Wings[i].flags &= ~WF_RESET_REINFORCEMENT;
4538 Wings[i].flags |= WF_REINFORCEMENT;
4541 // possibly send a message to the player when this wing arrives.
4542 if ( wingp->flags & WF_NO_ARRIVAL_MESSAGE ){
4546 // multiplayer team vs. team
4547 if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){
4548 // send a hostile wing arrived message
4549 rship = Wings[i].ship_index[0];
4551 int multi_team_filter = Ships[rship].team == TEAM_FRIENDLY ? 1 : 0;
4553 // there are two timestamps at work here. One to control how often the player receives
4554 // messages about incoming hostile waves, and the other to control how long after
4555 // the wing arrives does the player actually get the message.
4556 if ( timestamp_elapsed(Allow_arrival_message_timestamp_m[multi_team_filter]) ) {
4557 if ( !timestamp_valid(Arrival_message_delay_timestamp_m[multi_team_filter]) ){
4558 Arrival_message_delay_timestamp_m[multi_team_filter] = timestamp_rand(ARRIVAL_MESSAGE_DELAY_MIN, ARRIVAL_MESSAGE_DELAY_MAX );
4560 Allow_arrival_message_timestamp_m[multi_team_filter] = timestamp(ARRIVAL_MESSAGE_MIN_SEPARATION);
4562 // send to the proper team
4563 message_send_builtin_to_player( MESSAGE_ARRIVE_ENEMY, NULL, MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, multi_team_filter );
4568 // see if this is a starting player wing
4569 if ( i == Starting_wings[STARTING_WING_BETA] ) { // this is the beta wing
4570 rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS );
4572 message_send_builtin_to_player( MESSAGE_BETA_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4574 } else if ( i == Starting_wings[STARTING_WING_GAMMA] ) { // this is the gamma wing
4575 rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS );
4576 if ( rship != -1 ) {
4577 message_send_builtin_to_player( MESSAGE_GAMMA_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4579 } else if ( !stricmp( wingp->name, "delta") ) {
4580 rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS );
4581 if ( rship != -1 ) {
4582 message_send_builtin_to_player( MESSAGE_DELTA_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4584 } else if ( !stricmp(wingp->name, "epsilon") ) {
4585 rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS );
4586 if ( rship != -1 ) {
4587 message_send_builtin_to_player( MESSAGE_EPSILON_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 );
4590 // see if we have a hostile wing that arrived
4591 rship = Wings[i].ship_index[0];
4592 if ( Ships[rship].team != TEAM_FRIENDLY ) {
4594 // there are two timestamps at work here. One to control how often the player receives
4595 // messages about incoming hostile waves, and the other to control how long after
4596 // the wing arrives does the player actually get the message.
4597 if ( timestamp_elapsed(Allow_arrival_message_timestamp) ) {
4598 if ( !timestamp_valid(Arrival_message_delay_timestamp) ){
4599 Arrival_message_delay_timestamp = timestamp_rand(ARRIVAL_MESSAGE_DELAY_MIN, ARRIVAL_MESSAGE_DELAY_MAX );
4601 Allow_arrival_message_timestamp = timestamp(ARRIVAL_MESSAGE_MIN_SEPARATION);
4609 Mission_arrival_timestamp = timestamp(ARRIVAL_TIMESTAMP);
4612 MONITOR(NumShipDepartures);
4614 // called to make object objp depart.
4615 void mission_do_departure( object *objp )
4620 MONITOR_INC(NumShipDepartures,1);
4622 Assert ( objp->type == OBJ_SHIP );
4623 shipp = &Ships[objp->instance];
4625 // if departing to a docking bay, try to find the anchor ship to depart to. If not found, then
4626 // just make it warp out like anything else.
4627 if ( shipp->departure_location == DEPART_AT_DOCK_BAY ) {
4631 Assert( shipp->departure_anchor >= 0 );
4632 name = Parse_names[shipp->departure_anchor];
4634 // see if ship is yet to arrive. If so, then return -1 so we can evaluate again later.
4635 if ( mission_parse_get_arrival_ship( name ) )
4636 goto do_departure_warp;
4638 // see if ship is in mission. If not, then we can assume it was destroyed or departed since
4639 // it is not on the arrival list (as shown by above if statement).
4640 anchor_shipnum = ship_name_lookup( name );
4641 if ( anchor_shipnum == -1 )
4642 goto do_departure_warp;
4644 ai_acquire_depart_path(objp, Ships[anchor_shipnum].objnum);
4649 ai_set_mode_warp_out( objp, &Ai_info[Ships[objp->instance].ai_index] );
4653 // put here because mission_eval_arrivals is here. Might move these to a better location
4655 void mission_eval_departures()
4661 // if ( !timestamp_elapsed(Mission_departure_timestamp) )
4664 // scan through the active ships an evaluate their departure cues. For those
4665 // ships whose time has come, set their departing flag.
4667 for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4668 if (objp->type == OBJ_SHIP) {
4671 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
4673 shipp = &Ships[objp->instance];
4675 // don't process a ship that is already departing or dying or disabled
4676 // AL 12-30-97: Added SF_CANNOT_WARP to check
4677 if ( (shipp->flags & (SF_DEPARTING | SF_DYING | SF_CANNOT_WARP )) || ship_subsys_disrupted(shipp, SUBSYSTEM_ENGINE) ) {
4681 // don't process ships that are part of a wing -- handled in seperate case
4682 if ( shipp->wingnum != -1 )
4685 // && (!timestamp_valid(shipp->departure_delay) || timestamp_elapsed(shipp->departure_delay)) )
4686 // when the departure cue becomes true, set off the departure delay timer. We store the
4687 // timer as -seconds in Freespace which indicates that the timer has not been set. If the timer
4688 // is not set, then turn it into a valid timer and keep evaluating the timer until it is elapsed
4689 if ( eval_sexp(shipp->departure_cue) ) {
4690 if ( shipp->departure_delay <= 0 )
4691 shipp->departure_delay = timestamp(-shipp->departure_delay * 1000 );
4692 if ( timestamp_elapsed(shipp->departure_delay) )
4693 mission_do_departure( objp );
4698 // now scan through the list of wings and check their departure cues. For wings with
4699 // that cue being true, we must update internal variables to indicate that the wing is
4700 // departed and that no further waves of this wing will appear
4702 for ( i = 0; i < num_wings; i++ ) {
4705 // should we process this wing anymore
4706 if ( wingp->flags & WF_WING_DEPARTING )
4709 // evaluate the sexpression. If true, mark all the ships in this wing as departing and increment
4710 // the num departed in the wing structure. Then add number of remaining waves * ships/wave to
4711 // departed count to get total count of ships in the wing which departed. (We are counting ships
4712 // that have not yet arrived as departed if they never arrive -- this may be bad, but for some reason
4713 // seems like the right thing to do).
4714 //&& (!timestamp_valid(wingp->departure_delay) || timestamp_elapsed(wingp->departure_delay)) ) {
4716 if ( eval_sexp(wingp->departure_cue) ) {
4717 // if we haven't set up the departure timer yet (would be <= 0) setup the timer to pop N seconds
4719 if ( wingp->departure_delay <= 0 )
4720 wingp->departure_delay = timestamp( -wingp->departure_delay * 1000 );
4721 if ( !timestamp_elapsed(wingp->departure_delay) )
4724 wingp->flags |= WF_WING_DEPARTING;
4725 for ( j = 0; j < wingp->current_count; j++ ) {
4728 shipp = &Ships[wingp->ship_index[j]];
4729 if ( (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_DYING) )
4732 // shipp->flags |= SF_DEPARTING;
4733 // shipp->final_depart_time = timestamp(3*1000);
4735 Assert ( shipp->objnum != -1 );
4736 objp = &Objects[shipp->objnum];
4738 // copy the wing's depature information to the ship
4739 shipp->departure_location = wingp->departure_location;
4740 shipp->departure_anchor = wingp->departure_anchor;
4742 mission_do_departure( objp );
4743 // don't add to wingp->total_departed here -- this is taken care of in ship code.
4746 // MWA 2/25/98 -- don't do the follwoing wing member updates. It makes the accurate counts
4747 // sort of messed up and causes problems for the event log. The code in ship_wing_cleanup()
4748 // now keys off of the WF_WING_DEPARTING flag instead of the counts below.
4751 // now be sure that we update wing structure members if there are any remaining waves left
4752 if ( wingp->current_wave < wingp->num_waves ) {
4755 num_remaining = ( (wingp->num_waves - wingp->current_wave) * wingp->wave_count);
4756 wingp->total_departed += num_remaining;
4757 wingp->total_arrived_count += num_remaining;
4758 wingp->current_wave = wingp->num_waves;
4764 Mission_departure_timestamp = timestamp(DEPARTURE_TIMESTAMP);
4767 // function called from high level game loop to do mission evaluation stuff
4768 void mission_parse_eval_stuff()
4770 mission_eval_arrivals();
4771 mission_eval_departures();
4774 int allocate_subsys_status()
4778 Assert(Subsys_index < MAX_SUBSYS_STATUS);
4779 Subsys_status[Subsys_index].percent = 0.0f;
4780 Subsys_status[Subsys_index].primary_banks[0] = SUBSYS_STATUS_NO_CHANGE;
4781 for (i=1; i<MAX_PRIMARY_BANKS; i++)
4782 Subsys_status[Subsys_index].primary_banks[i] = -1; // none
4784 Subsys_status[Subsys_index].secondary_banks[0] = SUBSYS_STATUS_NO_CHANGE;
4785 Subsys_status[Subsys_index].secondary_ammo[0] = 100;
4786 for (i=1; i<MAX_SECONDARY_BANKS; i++) {
4787 Subsys_status[Subsys_index].secondary_banks[i] = -1;
4788 Subsys_status[Subsys_index].secondary_ammo[i] = 100;
4791 Subsys_status[Subsys_index].ai_class = SUBSYS_STATUS_NO_CHANGE;
4792 return Subsys_index++;
4795 // find (or add) the name in the list and return an index to it.
4796 int get_parse_name_index(char *name)
4800 for (i=0; i<Num_parse_names; i++)
4801 if (!stricmp(name, Parse_names[i]))
4804 Assert(i < MAX_SHIPS + MAX_WINGS);
4805 Assert(strlen(name) < NAME_LENGTH);
4806 strcpy(Parse_names[i], name);
4807 return Num_parse_names++;
4810 int get_anchor(char *name)
4814 for (i=0; i<MAX_SPECIAL_ARRIVAL_ANCHORS; i++)
4815 if (!stricmp(name, Special_arrival_anchor_names[i]))
4816 return SPECIAL_ARRIVAL_ANCHORS_OFFSET + i;
4818 return get_parse_name_index(name);
4821 // function to fixup the goals/ai references for player objects in the mission
4822 void mission_parse_fixup_players()
4826 // merge created list to have all objects on used list
4827 obj_merge_created_list();
4828 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4829 if ( (objp->type == OBJ_SHIP) && (objp->flags & OF_PLAYER_SHIP) ) {
4830 ai_clear_ship_goals( &Ai_info[Ships[objp->instance].ai_index] );
4831 init_ai_object( OBJ_INDEX(objp) );
4836 // code to warp in a new support ship. It works by finding the average position of all ships
4837 // in the mission, creating a vector from that position to the player, and scaling out behind the
4838 // player some distance. Should be sufficient.
4840 #define WARP_IN_MIN_DISTANCE 1000.0f
4841 #define WARP_IN_TIME_MIN 3000 // warps in min 3 seconds later
4842 #define WARP_IN_TIME_MAX 6000 // warps in max 6 seconds later
4844 // function which adds requester_objp onto the queue of ships for the arriving support ship to service
4845 void mission_add_to_arriving_support( object *requester_objp )
4850 Assert ( Arriving_support_ship );
4852 if ( Num_arriving_repair_targets == MAX_AI_GOALS ) {
4853 // Int3(); // get allender -- ship isn't going to get repair, but I hope they never queue up this far!!!
4854 mprintf(("Reached MAX_AI_GOALS trying to add repair request!\n"));
4858 shipp = &Ships[requester_objp->instance];
4859 // check for duplicates before adding
4860 for (i = 0; i < Num_arriving_repair_targets; i++ ) {
4861 if ( !stricmp(Arriving_repair_targets[i], shipp->ship_name) ){
4865 if ( i != Num_arriving_repair_targets ){ // found the ship before reaching the end -- ignore it!
4869 strcpy( Arriving_repair_targets[Num_arriving_repair_targets], Ships[requester_objp->instance].ship_name );
4870 Num_arriving_repair_targets++;
4872 if ( MULTIPLAYER_MASTER ){
4873 multi_maybe_send_repair_info( requester_objp, NULL, REPAIR_INFO_WARP_ADD );
4877 extern int pp_collide_any(vector *curpos, vector *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag);
4879 // Set the warp in position for a support ship relative to an object.
4880 // Caller tries several positions, passing vector in x, y, z.
4881 int get_warp_in_pos(vector *pos, object *objp, float x, float y, float z)
4885 if ( Game_mode & GM_NORMAL )
4888 rand_val = static_randf(objp->net_signature);
4890 rand_val = 1.0f + (rand_val - 0.5f)*0.2f;
4894 vm_vec_scale_add2( pos, &objp->orient.v.rvec, x*rand_val*800.0f);
4895 vm_vec_scale_add2( pos, &objp->orient.v.uvec, y*rand_val*800.0f);
4896 vm_vec_scale_add2( pos, &objp->orient.v.fvec, z*rand_val*800.0f);
4898 return pp_collide_any(&objp->pos, pos, objp->radius, objp, NULL, 1);
4901 void mission_warp_in_support_ship( object *requester_objp )
4903 vector center, warp_in_pos;
4906 int i, requester_species;
4907 ship *requester_shipp;
4909 Assert ( requester_objp->type == OBJ_SHIP );
4910 requester_shipp = &Ships[requester_objp->instance]; // MK, 10/23/97, used to be ->type, bogus, no?
4912 // if the support ship is already arriving, add the requester to the list
4913 if ( Arriving_support_ship ) {
4914 mission_add_to_arriving_support( requester_objp );
4918 // get average position of all ships
4919 obj_get_average_ship_pos( ¢er );
4920 vm_vec_sub( &warp_in_pos, ¢er, &(requester_objp->pos) );
4922 // be sure to account for case as player being only ship left in mission
4924 if ( !(IS_VEC_NULL( warp_in_pos)) ) {
4925 mag = vm_vec_mag( &warp_in_pos );
4926 if ( mag < WARP_IN_MIN_DISTANCE )
4927 vm_vec_scale( &warp_in_pos, WARP_IN_MIN_DISTANCE/mag);
4931 // take -player_pos.fvec scaled by 1000.0f;
4932 warp_in_pos = Player_obj->orient.fvec;
4933 vm_vec_scale( &warp_in_pos, -1000.0f );
4937 // Choose position to warp in ship.
4938 // Temporary, but changed by MK because it used to be exactly behind the player.
4939 // This could cause an Assert if the player immediately targeted it (before moving).
4940 // Tend to put in front of the player to aid him in flying towards the ship.
4942 if (!get_warp_in_pos(&warp_in_pos, requester_objp, 1.0f, 0.1f, 1.0f))
4943 if (!get_warp_in_pos(&warp_in_pos, requester_objp, 1.0f, 0.2f, -1.0f))
4944 if (!get_warp_in_pos(&warp_in_pos, requester_objp, -1.0f, -0.2f, -1.0f))
4945 if (!get_warp_in_pos(&warp_in_pos, requester_objp, -1.0f, -0.1f, 1.0f))
4946 get_warp_in_pos(&warp_in_pos, requester_objp, 0.1f, 1.0f, 0.2f);
4948 // create a parse object, and put it onto the ship_arrival_list. This whole thing kind of sucks.
4949 // I want to put it into a parse object since it needs to arrive just a little later than
4950 // this function is called. I have to make some assumptions in the code about values for the parse
4951 // object since I'm no longer working with a mission file. These exceptions will be noted with
4954 Arriving_support_ship = &Support_ship_pobj;
4955 pobj = Arriving_support_ship;
4957 // create a name for the ship. use "Support #". look for collisions until one isn't found anymore
4960 sprintf(pobj->name, NOX("Support %d"), i);
4961 if ( (ship_name_lookup(pobj->name) == -1) && (ship_find_exited_ship_by_name(pobj->name) == -1) )
4966 pobj->pos = warp_in_pos;
4967 vm_set_identity( &(pobj->orient) );
4969 // *sigh*. Gotta get the ship class. For now, this will amount to finding a ship in the ship_info
4970 // array with the same team as the requester of type SIF_SUPPORT. Might need to be changed, but who knows
4971 // vasudans use the terran support ship.
4972 requester_species = Ship_info[requester_shipp->ship_info_index].species;
4974 // 5/6/98 -- MWA Don't need to do anything for multiplayer. I think that we always want to use
4975 // the species of the caller ship.
4976 Assert( (requester_species == SPECIES_TERRAN) || (requester_species == SPECIES_VASUDAN) );
4977 // if ( (Game_mode & GM_NORMAL) && (requester_species == SPECIES_VASUDAN) ) { // make vasundan's use the terran support ship
4978 // requester_species = SPECIES_TERRAN;
4981 // get index of correct species support ship
4982 for (i=0; i < Num_ship_types; i++) {
4983 if ( (Ship_info[i].species == requester_species) && (Ship_info[i].flags & SIF_SUPPORT) )
4987 if ( i < Num_ship_types )
4988 pobj->ship_class = i;
4990 Int3(); // BOGUS!!!! gotta figure something out here
4992 pobj->team = requester_shipp->team;
4994 pobj->behavior = AIM_NONE; // ASSUMPTION: the mission file has the string "None" which maps to AIM_NONE
4996 // set the ai_goals to -1. We will put the requester object shipname in repair target array and then take
4997 // care of setting up the goal when creating the ship!!!!
4998 pobj->ai_goals = -1;
4999 Num_arriving_repair_targets = 0;
5000 mission_add_to_arriving_support( requester_objp );
5002 // need to set ship's cargo to nothing. scan the cargo_names array looking for the string nothing.
5003 // add it if not found
5004 for (i = 0; i < Num_cargo; i++ )
5005 if ( !stricmp(Cargo_names[i], NOX("nothing")) )
5008 if ( i == Num_cargo ) {
5009 strcpy(Cargo_names[i], NOX("Nothing"));
5012 pobj->cargo1 = char(i);
5014 pobj->status_count = 0;
5016 pobj->arrival_location = 0; // ASSUMPTION: this is index to arrival_lcation string array for hyperspace!!!!
5017 pobj->arrival_distance = 0;
5018 pobj->arrival_anchor = -1;
5019 pobj->arrival_cue = Locked_sexp_true;
5020 pobj->arrival_delay = timestamp_rand(WARP_IN_TIME_MIN, WARP_IN_TIME_MAX);
5022 pobj->subsys_count = 0; // number of elements used in subsys_status array
5023 pobj->initial_velocity = 100; // start at 100% velocity
5024 pobj->initial_hull = 100; // start at 100% hull
5025 pobj->initial_shields = 100; // and 100% shields
5027 pobj->departure_location = 0; // ASSUMPTION: this is index to departure_lcation string array for hyperspace!!!!
5028 pobj->departure_anchor = -1;
5029 pobj->departure_cue = Locked_sexp_false;
5030 pobj->departure_delay= 0;
5032 pobj->determination = 10; // ASSUMPTION: mission file always had this number written out
5034 if ( Player_obj->flags & P_OF_NO_SHIELDS )
5035 pobj->flags = P_OF_NO_SHIELDS; // support ships have no shields when player has not shields
5037 pobj->ai_class = Ship_info[pobj->ship_class].ai_class;
5041 pobj->docked_with[0] = '\0';
5043 pobj->persona_index = -1;
5044 pobj->net_signature = multi_assign_network_signature(MULTI_SIG_SHIP);
5045 pobj->wing_status_wing_index = -1;
5046 pobj->wing_status_wing_pos = -1;
5047 pobj->respawn_count = 0;
5048 pobj->alt_type_index = -1;
5052 // returns true if a support ship is currently in the process of warping in.
5053 int mission_is_support_ship_arriving()
5055 if ( Arriving_support_ship )
5061 // returns true if the given ship is scheduled to be repaired by the arriving support ship
5062 int mission_is_repair_scheduled( object *objp )
5067 if ( !Arriving_support_ship )
5070 Assert ( objp->type == OBJ_SHIP );
5071 name = Ships[objp->instance].ship_name;
5072 for (i = 0; i < Num_arriving_repair_targets; i++ ) {
5073 if ( !strcmp( name, Arriving_repair_targets[i]) )
5080 // function which removed the given ship from the list of ships that are to get repair
5081 // by arriving support ship
5082 int mission_remove_scheduled_repair( object *objp )
5087 if ( !Arriving_support_ship )
5090 // itereate through the target list looking for this ship name. If not found, we
5091 // can simply return.
5092 Assert ( objp->type == OBJ_SHIP );
5093 name = Ships[objp->instance].ship_name;
5094 for (index = 0; index < Num_arriving_repair_targets; index++ ) {
5095 if ( !strcmp( name, Arriving_repair_targets[index]) )
5098 if ( index == Num_arriving_repair_targets )
5101 // ship is found -- compress the array
5102 for ( i = index; i < Num_arriving_repair_targets - 1; i++ )
5103 strcpy( Arriving_repair_targets[i], Arriving_repair_targets[i+1] );
5105 Num_arriving_repair_targets--;
5107 if ( MULTIPLAYER_MASTER )
5108 multi_maybe_send_repair_info( objp, NULL, REPAIR_INFO_WARP_REMOVE );
5113 // alternate name stuff
5114 int mission_parse_lookup_alt(char *name)
5124 for(idx=0; idx<Mission_alt_type_count; idx++){
5125 if(!strcmp(Mission_alt_types[idx], name)){
5134 static int mission_parse_lookup_alt_index_warn = 1;
5135 void mission_parse_lookup_alt_index(int index, char *out)
5141 if((index < 0) || (index > Mission_alt_type_count)){
5142 if (mission_parse_lookup_alt_index_warn) {
5143 Warning(LOCATION, "Ship with invalid alt_name. Get a programmer");
5144 mission_parse_lookup_alt_index_warn = 0;
5150 strcpy(out, Mission_alt_types[index]);
5153 int mission_parse_add_alt(char *name)
5161 if(Mission_alt_type_count < MAX_ALT_TYPE_NAMES){
5163 strncpy(Mission_alt_types[Mission_alt_type_count++], name, NAME_LENGTH);
5166 return Mission_alt_type_count - 1;
5172 void mission_parse_reset_alt()
5174 Mission_alt_type_count = 0;
5177 int is_training_mission()
5179 return (The_mission.game_type & MISSION_TYPE_TRAINING);