]> icculus.org git repositories - taylor/freespace2.git/blob - src/fred2/management.cpp
The Great Newline Fix
[taylor/freespace2.git] / src / fred2 / management.cpp
1 /*
2  * $Logfile: /Freespace2/code/Fred2/Management.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * This file handles the management of Objects, Ships, Wings, etc.  Basically
8  * all the little structures we have that usually inter-relate that need to
9  * be handled in a standard way, and thus should be handled by a single
10  * function.
11  *
12  * $Log$
13  * Revision 1.2  2002/05/07 03:16:44  theoddone33
14  * The Great Newline Fix
15  *
16  * Revision 1.1.1.1  2002/05/03 03:28:09  root
17  * Initial import.
18  *
19  * 
20  * 29    10/13/99 9:22a Daveb
21  * Fixed Fred jumpnode placing bug. Fixed 1024 glide tiled texture problem
22  * related to movies. Fixed launcher spawning from PXO screen.
23  * 
24  * 28    9/08/99 10:01p Dave
25  * Make sure game won't run in a drive's root directory. Make sure
26  * standalone routes suqad war messages properly to the host.
27  * 
28  * 27    7/23/99 2:02p Jamesa
29  * Don't require gamepalettes
30  * 
31  * 26    7/15/99 3:07p Dave
32  * 32 bit detection support. Mouse coord commandline.
33  * 
34  * 25    7/02/99 4:30p Dave
35  * Much more sophisticated lightning support.
36  * 
37  * 24    5/20/99 6:59p Dave
38  * Added alternate type names for ships. Changed swarm missile table
39  * entries.
40  * 
41  * 23    4/26/99 8:47p Dave
42  * Made all pof related nebula stuff customizable through Fred.
43  * 
44  * 22    4/16/99 2:34p Andsager
45  * Second pass on debris fields
46  * 
47  * 21    4/15/99 5:00p Andsager
48  * Frist pass on Debris field
49  * 
50  * 20    4/07/99 6:21p Dave
51  * Fred and Freespace support for multiple background bitmaps and suns.
52  * Fixed link errors on all subprojects. Moved encrypt_init() to
53  * cfile_init() and lcl_init(), since its safe to call twice.
54  * 
55  * 19    3/31/99 9:50a Andsager
56  * Interface for generalization of asteroid field (debris field)
57  * 
58  * 18    3/30/99 5:40p Dave
59  * Fixed reinforcements for TvT in multiplayer.
60  * 
61  * 17    3/24/99 4:05p Dave
62  * Put in support for assigning the player to a specific squadron with a
63  * specific logo. Preliminary work for doing pos/orient checksumming in
64  * multiplayer to reduce bandwidth.
65  * 
66  * 16    3/20/99 5:09p Dave
67  * Fixed release build fred warnings and unhandled exception.
68  * 
69  * 15    2/23/99 7:03p Dave
70  * Rewrote a horribly mangled and evil team loadout dialog. Bugs gone.
71  * 
72  * 14    2/17/99 2:11p Dave
73  * First full run of squad war. All freespace and tracker side stuff
74  * works.
75  * 
76  * 13    2/10/99 11:11a Johnson
77  * Think I fixed the problem where the Neb2_awacs value was improperly
78  * being checked for in Fred.
79  * 
80  * 12    2/07/99 8:51p Andsager
81  * Add inner bound to asteroid field.  Inner bound tries to stay astroid
82  * free.  Wrap when within and don't throw at ships inside.
83  * 
84  * 11    1/25/99 5:03a Dave
85  * First run of stealth, AWACS and TAG missile support. New mission type
86  * :)
87  * 
88  * 10    1/19/99 3:57p Andsager
89  * Round 2 of variables
90  * 
91  * 9     12/18/98 1:14a Dave
92  * Rough 1024x768 support for Direct3D. Proper detection and usage through
93  * the launcher.
94  * 
95  * 8     11/06/98 10:15a Dave
96  * 
97  * 7     10/29/98 9:22p Dave
98  * Removed minor bug concering externalization of campaign files.
99  * 
100  * 6     10/29/98 6:49p Dave
101  * Finished up Fred support for externalizing mission and campaign files.
102  * 
103  * 5     10/29/98 10:41a Dave
104  * Change the way cfile initializes exe directory.
105  * 
106  * 4     10/23/98 5:07p Dave
107  * Put in beginnings of localization/externalization functionality.
108  * 
109  * 3     10/22/98 6:13p Dave
110  * Added registry and localization support.
111  * 
112  * 2     10/07/98 6:28p Dave
113  * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
114  * Fred. Globalized mission and campaign file extensions. Removed Silent
115  * Threat specific code.
116  * 
117  * 1     10/07/98 3:02p Dave
118  * 
119  * 1     10/07/98 3:00p Dave
120  * 
121  * 227   6/17/98 2:05p Hoffoss
122  * Fixed bug: Shield system for team or ship type flags new reset when
123  * mission gets cleared out (new or load mission).
124  * 
125  * 226   5/20/98 10:19p Allender
126  * make Fred work with NDEBUG build
127  * 
128  * 225   5/17/98 1:48p Allender
129  * made number of respawns for new missions 3
130  * 
131  * 224   5/01/98 12:34p John
132  * Added code to force FreeSpace to run in the same dir as exe and made
133  * all the parse error messages a little nicer.
134  * 
135  * 223   4/30/98 8:23p John
136  * Fixed some bugs with Fred caused by my new cfile code.
137  * 
138  * 222   4/27/98 4:07p Allender
139  * make orders_accepted not be assigned to -1 when creating new ships.
140  * Don't set the use_default_orders flag when orders are -1
141  * 
142  * 221   4/25/98 6:43p Allender
143  * reset model data when initializeing new mission
144  * 
145  * 220   4/17/98 1:41p Allender
146  * took out function calls in NDEBUG mode
147  * 
148  * 219   4/13/98 10:59a Hoffoss
149  * Made "new mission" clear the cmd brief structure.
150  * 
151  * 218   4/13/98 10:11a John
152  * Made timer functions thread safe.  Made timer_init be called in all
153  * projects.
154  * 
155  * 217   4/06/98 5:37p Hoffoss
156  * Added sexp tree support to briefings in Fred.
157  * 
158  * 216   4/06/98 12:55p John
159  * Upped the gamma for Fred.
160  * 
161  * 215   4/03/98 11:34a John
162  * Fixed the stuff I broke in Fred from the new breifing
163  * 
164  * 214   4/02/98 3:00p Johnson
165  * Fixed a bug a release build turned up.
166  * 
167  * 213   3/24/98 1:36p Hoffoss
168  * Moved call to load_filter_info() to after cfile_init(), as it is
169  * dependent on that being set up.
170  * 
171  * 212   3/24/98 12:42p Allender
172  * fixed a couple of minor problems with arrival targets
173  * 
174  * 211   3/21/98 7:36p Lawrance
175  * Move jump nodes to own lib.
176  * 
177  * 210   3/17/98 4:16p Allender
178  * minor changes to the kamikaze flag
179  * 
180  * 209   3/17/98 11:55a Johnson
181  * Fixed bug where jump nodes wheren't being cleared on a new mission.
182  * 
183  * 208   3/10/98 4:26p Hoffoss
184  * Changed jump node structure to include a name.  Position is now taken
185  * from the object (each jump node has an associated object now).
186  * 
187  * 207   3/09/98 4:30p Allender
188  * multiplayer secondary weapon changes.  red-alert and cargo-known-delay
189  * sexpressions.  Add time cargo revealed to ship structure
190  * 
191  * 206   3/09/98 10:56a Hoffoss
192  * Added jump node objects to Fred.
193  * 
194  * 205   3/06/98 5:10p Allender
195  * made time to: field in extended targetbox use support time to dock code
196  * for all docking shpis.  Only display for waypoints and docking (not
197  * undocking).  Small fixups to message menu -- not allowing depart when
198  * disabled.  Depart is now by default ignored for all non-small ships
199  * 
200  * 204   3/05/98 3:59p Hoffoss
201  * Added a bunch of new command brief stuff, and asteroid initialization
202  * to Fred.
203  * 
204  * 203   2/26/98 4:59p Allender
205  * groundwork for team vs team briefings.  Moved weaponry pool into the
206  * Team_data structure.  Added team field into the p_info structure.
207  * Allow for mutliple structures in the briefing code.
208  * 
209  * 202   2/17/98 12:07p Hoffoss
210  * Changed over to using SF_CARGO_REVEALED in fred.
211  * 
212  * 201   2/17/98 10:12a Hoffoss
213  * Fixed bug with sprintf() in reference_handler().  Forgot the first
214  * argument! :)  Amazing it never crashed before.
215  * 
216  * 200   2/13/98 11:45a Hoffoss
217  * Made all new ships created in Fred default to 33% initial speed.
218  * 
219  * $NoKeywords: $
220  */
221
222 #include "stdafx.h"
223 #include "fred.h"
224 #include "mainfrm.h"
225 #include "freddoc.h"
226 #include "fredview.h"
227 #include "fredrender.h"
228 #include "ailocal.h"
229 #include "aigoals.h"
230 #include "ship.h"
231 #include "linklist.h"
232 #include "missionparse.h"
233 #include "missionmessage.h"
234 #include "missiongoals.h"
235 #include "missionbriefcommon.h"
236 #include "management.h"
237 #include "cfile.h"
238 #include "palman.h"
239 #include "2d.h"
240 #include "3d.h"
241 #include "weapon.h"
242 #include "key.h"
243 #include "parselo.h"
244 #include "fvi.h"
245 #include "starfield.h"
246 #include "sexp.h"
247 #include "mouse.h"
248 #include "missioncampaign.h"
249 #include "wing.h"
250 #include "messageeditordlg.h"
251 #include "eventeditor.h"
252 #include "missiongoalsdlg.h"
253 #include "shieldsysdlg.h"
254 #include "eventmusic.h"
255 #include "debriefingeditordlg.h"
256 #include "nebula.h"
257 #include "asteroid.h"
258 #include "hudsquadmsg.h"
259 #include "jumpnode.h"
260 #include "medals.h"
261 #include "localize.h" 
262 #include "osregistry.h"
263 #include "fhash.h"
264 #include "timer.h"
265 #include "neb.h"
266 #include "neblightning.h"
267
268 #define MAX_DOCKS 50
269
270 #define UNKNOWN_USER            "Unknown"
271
272 int cur_wing = -1;
273 int cur_wing_index;
274 int cur_object_index = -1;
275 int cur_ship = -1;
276 int cur_model_index = 0;
277 int cur_waypoint = -1;
278 int cur_waypoint_list = -1;
279 int delete_flag;
280 int bypass_update = 0;
281 int Default_player_model = 0;
282 int Update_ship = 0;
283 int Update_wing = 0;
284 int Fred_font;
285
286 char Fred_exe_dir[512] = "";
287
288 char Fred_alt_names[MAX_SHIPS][NAME_LENGTH+1];
289
290 // object numbers for ships in a wing.
291 int wing_objects[MAX_WINGS][MAX_SHIPS_PER_WING];
292
293 char *Docking_bay_list[MAX_DOCKS];
294
295 CCriticalSection CS_cur_object_index;
296
297 ai_goal_list Ai_goal_list[] = {
298         "Waypoints",                            AI_GOAL_WAYPOINTS,
299         "Waypoints once",                       AI_GOAL_WAYPOINTS_ONCE,
300         "Warp",                                         AI_GOAL_WARP,
301         "Destroy subsystem",            AI_GOAL_DESTROY_SUBSYSTEM,
302         "Attack",                                       AI_GOAL_CHASE | AI_GOAL_CHASE_WING,
303         "Dock",                                         AI_GOAL_DOCK,
304         "Undock",                                       AI_GOAL_UNDOCK,
305         "Guard",                                                AI_GOAL_GUARD | AI_GOAL_GUARD_WING,
306         "Attack any ship",              AI_GOAL_CHASE_ANY,
307         "Disable ship",                 AI_GOAL_DISABLE_SHIP,
308         "Disarm ship",                          AI_GOAL_DISARM_SHIP,
309         "Evade ship",                           AI_GOAL_EVADE_SHIP,
310         "Ignore ship",                          AI_GOAL_IGNORE,
311         "Stay near ship",                       AI_GOAL_STAY_NEAR_SHIP,
312         "Keep safe distance",   AI_GOAL_KEEP_SAFE_DISTANCE,
313         "Stay still",                           AI_GOAL_STAY_STILL,
314         "Play dead",                            AI_GOAL_PLAY_DEAD,
315 };
316
317 int Ai_goal_list_size = sizeof(Ai_goal_list) / sizeof(ai_goal_list);
318
319 // internal function prototypes
320 void set_cur_indices(int obj);
321 int common_object_delete(int obj);
322 int create_waypoint(vector *pos, int list);
323 int create_ship(matrix *orient, vector *pos, int ship_type);
324 int query_ship_name_duplicate(int ship);
325 char *reg_read_string( char *section, char *name, char *default_value );
326
327 extern int Nmodel_num;
328 extern int Nmodel_bitmap;
329
330 void string_copy(char *dest, CString &src, int max_len, int modify)
331 {
332         int len;
333
334         if (modify)
335                 if (strcmp(src, dest))
336                         set_modified();
337
338         len = strlen(src);
339         if (len >= max_len)
340                 len = max_len - 1;
341
342         strncpy(dest, src, len);
343         dest[len] = 0;
344 }
345
346 // converts a multiline string (one with newlines in it) into a windows format multiline
347 // string (newlines changed to '\r\n').
348 CString convert_multiline_string(char *src)
349 {
350         char *ptr, buf[256];
351         int i;
352         static CString str;
353
354         str = _T("");
355         while ((ptr = strchr(src, '\n'))!=NULL) {
356                 i = ptr - src;
357                 while (i > 250) {
358                         strncpy(buf, src, 250);
359                         buf[250] = 0;
360                         str += buf;
361                         src += 250;
362                         i -= 250;
363                 }
364
365                 if (i)
366                         strncpy(buf, src, i);
367
368                 buf[i] = 0;
369                 str += buf;
370                 str += "\r\n";
371                 src = ptr + 1;
372         }
373
374         i = strlen(src);
375         if (i)
376                 str += src;
377
378         return str;
379 }
380
381 // Converts a windows format multiline CString back into a normal multiline string.
382 void deconvert_multiline_string(char *buf, CString &str, int max_len)
383 {
384         char *ptr = buf;
385         int i, j;
386         CString str2;
387
388         Assert(max_len > 1);
389         Assert(buf);
390         max_len -= 2;
391         while ((i = str.Find("\r\n")) >= 0) {
392                 for (j=0; j<i; j++)
393                         if (max_len) {
394                                 *ptr++ = str[j];
395                                 max_len--;
396                         }
397
398                 *ptr++ = '\n';
399                 str2 = str.Mid(i + 2);
400                 str = str2;
401         }
402
403         i = str.GetLength();
404         for (j=0; j<i; j++)
405                 if (max_len) {
406                         *ptr++ = str[j];
407                         max_len--;
408                 }
409
410         // removed 10/29/98 by DB
411         // this was generating an extra newline - why?
412         //if (*(ptr - 1) != '\n')
413         //      *ptr++ = '\n';
414         *ptr = 0;
415 }
416
417 // medal_stuff Medals[NUM_MEDALS];
418 /*
419 void parse_medal_tbl()
420 {
421         int rval, num_medals;
422
423         if ((rval = setjmp(parse_abort)) != 0) {
424                 Error(LOCATION, "Error parsing '%s'\r\nError code = %i.\r\n", "medals.tbl", rval);
425         } 
426
427         // open localization
428         lcl_ext_open();
429
430         read_file_text("medals.tbl");
431
432         reset_parse();
433
434         // parse in all the rank names
435         num_medals = 0;
436         required_string("#Medals");
437         while ( required_string_either("#End", "$Name:") ) {
438                 Assert ( num_medals < NUM_MEDALS);
439                 required_string("$Name:");
440                 stuff_string( Medals[num_medals].name, F_NAME, NULL );
441                 required_string("$Bitmap:");
442                 stuff_string( Medals[num_medals].bitmap, F_NAME, NULL );
443                 required_string("$Num mods:");
444                 stuff_int( &Medals[num_medals].num_versions);
445
446                 // some medals are based on kill counts.  When string +Num Kills: is present, we know that
447                 // this medal is a badge and should be treated specially
448                 Medals[num_medals].kills_needed = 0;
449
450                 if ( optional_string("+Num Kills:") ) {
451                         char buf[MULTITEXT_LENGTH + 1];
452
453                         stuff_int( &Medals[num_medals].kills_needed );
454
455                         required_string("$Wavefile 1:");
456                         stuff_string(buf, F_NAME, NULL, MAX_FILENAME_LEN);
457
458                         required_string("$Wavefile 2:");
459                         stuff_string(buf, F_NAME, NULL, MAX_FILENAME_LEN);
460
461                         required_string("$Promotion Text:");
462                         stuff_string(buf, F_MULTITEXT, NULL);
463                 }
464
465                 num_medals++;
466         }
467
468         required_string("#End");      
469
470         // close localization
471         lcl_ext_close();
472 }
473 */
474
475 void fred_init()
476 {
477         int i;
478         char palette_filename[1024];
479
480         if (!vm_init(24*1024*1024)) {
481                 MessageBox( NULL, "Not enough memory to run Fred.\r\nTry closing down some other applications.\r\n", "Not Enough Memory", MB_OK );
482                 return;
483         }
484
485    srand( (unsigned) time(NULL) );
486         init_pending_messages();
487
488         // initialize registry stuff
489         os_init_registry_stuff(Osreg_company_name, Osreg_app_name, NULL);
490
491         timer_init();
492
493         Assert(strlen(Fred_exe_dir) > 0);
494         
495         // doh
496         if(cfile_init(Fred_exe_dir)){
497                 exit(1);
498         }
499
500         // initialize localization module. Make sure this is done AFTER initialzing OS.
501         // NOTE : Fred should ALWAYS run in Enlish. Otherwise it might swap in another language
502         // when saving - which would cause inconsistencies when externalizing to tstrings.tbl via Exstr
503         // trust me on this :)
504         lcl_init(LCL_ENGLISH);  
505
506         #ifndef NDEBUG
507         load_filter_info();
508         #endif
509
510         gr_init(GR_640, GR_SOFTWARE, 8);
511         gr_set_gamma(3.0f);
512
513         sprintf(palette_filename, "gamepalette%d-%02d", 1, 1);
514         mprintf(("Loading palette %s\n", palette_filename));
515         palette_load_table(palette_filename);
516
517         Fred_font = gr_init_font("font01.vf");
518         key_init();
519         mouse_init();
520
521         mission_brief_common_init();    
522         obj_init();
523         model_free_all();                               // Free all existing models
524         ai_init();
525         weapon_init();
526         parse_medal_tbl();                      // get medal names for sexpression usage
527         ship_init();
528         init_parse();
529
530         // initialize and activate external string hash table
531         // make sure to do here so that we don't parse the table files into the hash table - waste of space
532         fhash_init();
533         fhash_activate();
534
535         create_new_mission();
536         neb2_init();                                            // fullneb stuff
537         stars_init();
538         brief_init_icons();
539         event_music_parse_musictbl();
540         cmd_brief_reset();
541         Show_waypoints = TRUE;
542         Campaign.filename[0] = 0;  // indicate initialized state
543
544         stars_level_init();
545
546         // neb lightning
547         nebl_init();
548         
549         gr_reset_clip();
550         g3_start_frame(0);
551         g3_set_view_matrix(&eye_pos, &eye_orient, 0.5f);
552
553         for (i=0; i<Num_ship_types; i++)
554                 if (Ship_info[i].flags & SIF_DEFAULT_PLAYER_SHIP) {
555                         Default_player_model = cur_model_index = i;
556                         break;
557                 }
558
559         Id_select_type_start = Num_ship_types + 2;
560         Id_select_type_jump_node = Num_ship_types + 1;
561         Id_select_type_waypoint = Num_ship_types;
562         Fred_main_wnd -> init_tools();  
563 }
564
565 void set_physics_controls()
566 {
567         physics_init(&view_physics);
568         view_physics.max_vel.x *= physics_speed / 3.0f;
569         view_physics.max_vel.y *= physics_speed / 3.0f;
570         view_physics.max_vel.z *= physics_speed / 3.0f;
571         view_physics.max_rear_vel *= physics_speed / 3.0f;
572
573         view_physics.max_rotvel.x *= physics_rot / 30.0f;
574         view_physics.max_rotvel.y *= physics_rot / 30.0f;
575         view_physics.max_rotvel.z *= physics_rot / 30.0f;
576         view_physics.flags |= PF_ACCELERATES | PF_SLIDE_ENABLED;
577         theApp.write_ini_file(1);
578 }
579
580 int create_object_on_grid(int list)
581 {
582         int obj = -1;
583         float rval;
584         vector dir,pos;
585
586         g3_point_to_vec_delayed(&dir, marking_box.x2, marking_box.y2);
587
588         rval = fvi_ray_plane(&pos, &The_grid->center, &The_grid->gmatrix.uvec, &view_pos, &dir, 0.0f);
589
590         if (rval>=0.0f) {
591                 unmark_all();
592                 obj = create_object(&pos, list);
593                 if (obj >= 0) {
594                         mark_object(obj);
595                         FREDDoc_ptr->autosave("object create");
596
597                 } else if (obj == -1)
598                         Fred_main_wnd->MessageBox("Maximum ship limit reached.  Can't add any more ships.");
599         }
600
601         return obj;
602 }
603
604 void fix_ship_name(int ship)
605 {
606         int i = 1;
607
608         do {
609                 sprintf(Ships[ship].ship_name, "U.R.A. Moron %d", i++);
610         } while (query_ship_name_duplicate(ship));
611 }
612
613 int create_ship(matrix *orient, vector *pos, int ship_type)
614 {
615         int obj, ship, z1, z2;
616         ship_info *sip;
617
618         obj = ship_create(orient, pos, ship_type);
619         if (obj == -1)
620                 return -1;
621
622         Objects[obj].phys_info.speed = 33.0f;
623
624         ship = Objects[obj].instance;
625         sip = &Ship_info[Ships[ship].ship_info_index];
626
627         if (query_ship_name_duplicate(ship))
628                 fix_ship_name(ship);
629
630         z1 = Shield_sys_teams[Ships[ship].team];
631         z2 = Shield_sys_types[ship_type];
632         if (((z1 == 1) && z2) || (z2 == 1))
633                 Objects[obj].flags |= OF_NO_SHIELDS;
634
635         z1 = Ship_info[Ships[ship].ship_info_index].species;
636         if (z1 == SPECIES_SHIVAN) {
637                 Ships[ship].team = TEAM_HOSTILE;
638                 Ships[ship].flags &= ~SF_CARGO_REVEALED;
639
640         } else {
641                 Ships[ship].team = TEAM_FRIENDLY;
642                 Ships[ship].flags |= SF_CARGO_REVEALED;
643         }
644
645         if ( Ships[ship].team == TEAM_FRIENDLY ) {
646
647                 // if this ship is not a small ship, then make the orders be the default orders without
648                 // the depart item
649                 if ( !(sip->flags & SIF_SMALL_SHIP) ) {
650                         Ships[ship].orders_accepted = ship_get_default_orders_accepted( sip );
651                         Ships[ship].orders_accepted &= ~DEPART_ITEM;
652                 }
653
654         } else {
655                 Ships[ship].orders_accepted = 0;
656         }
657
658         Ai_info[Ships[ship].ai_index].kamikaze_damage = min(1000.0f, 200.0f + (sip->initial_hull_strength / 4.0f));
659
660         return obj;
661 }
662
663 int query_ship_name_duplicate(int ship)
664 {
665         int i;
666
667         for (i=0; i<MAX_SHIPS; i++)
668                 if ((i != ship) && (Ships[i].objnum != -1))
669                         if (!stricmp(Ships[i].ship_name, Ships[ship].ship_name))
670                                 return 1;
671
672         return 0;
673 }
674
675 void copy_bits(int *dest, int src, int mask)
676 {
677         *dest &= ~mask;
678         *dest |= src & mask;
679 }
680
681 int dup_object(object *objp)
682 {
683         int i, j, n, inst, obj = -1;
684         ai_info *aip1, *aip2;
685         object *objp1, *objp2;
686         ship_subsys *subp1, *subp2;
687         static int list;
688
689         if (!objp) {
690                 list = -1;
691                 return 0;
692         }
693
694         inst = objp->instance;
695         if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) {
696                 obj = create_ship(&objp->orient, &objp->pos, Ships[inst].ship_info_index);
697                 if (obj == -1)
698                         return -1;
699
700                 n = Objects[obj].instance;
701                 Ships[n].team = Ships[inst].team;
702                 Ships[n].arrival_cue = dup_sexp_chain(Ships[inst].arrival_cue);
703                 Ships[n].departure_cue = dup_sexp_chain(Ships[inst].departure_cue);
704                 Ships[n].cargo1 = Ships[inst].cargo1;
705                 Ships[n].arrival_location = Ships[inst].arrival_location;
706                 Ships[n].departure_location = Ships[inst].departure_location;
707                 Ships[n].arrival_delay = Ships[inst].arrival_delay;
708                 Ships[n].departure_delay = Ships[inst].departure_delay;
709                 Ships[n].weapons = Ships[inst].weapons;
710                 Ships[n].hotkey = Ships[inst].hotkey;
711
712                 aip1 = &Ai_info[Ships[n].ai_index];
713                 aip2 = &Ai_info[Ships[inst].ai_index];
714                 aip1->behavior = aip2->behavior;
715                 aip1->ai_class = aip2->ai_class;
716                 for (i=0; i<MAX_AI_GOALS; i++)
717                         aip1->goals[i] = aip2->goals[i];
718
719                 if ( aip2->ai_flags & AIF_KAMIKAZE )
720                         aip1->ai_flags |= AIF_KAMIKAZE;
721                 if ( aip2->ai_flags & AIF_NO_DYNAMIC )
722                         aip2->ai_flags |= AIF_NO_DYNAMIC;
723
724                 aip1->kamikaze_damage = aip2->kamikaze_damage;
725
726                 objp1 = &Objects[obj];
727                 objp2 = &Objects[Ships[inst].objnum];
728                 objp1->phys_info.speed = objp2->phys_info.speed;
729                 objp1->phys_info.fspeed = objp2->phys_info.fspeed;
730                 objp1->hull_strength = objp2->hull_strength;
731                 objp1->shields[0] = objp2->shields[0];
732
733                 subp1 = GET_FIRST(&Ships[n].subsys_list);
734                 subp2 = GET_FIRST(&Ships[inst].subsys_list);
735                 while (subp1 != END_OF_LIST(&Ships[n].subsys_list)) {
736                         Assert(subp2 != END_OF_LIST(&Ships[inst].subsys_list));
737                         subp1 -> current_hits = subp2 -> current_hits;
738                         subp1 = GET_NEXT(subp1);
739                         subp2 = GET_NEXT(subp2);
740                 }
741
742                 for (i=0; i<Num_reinforcements; i++)
743                         if (!stricmp(Reinforcements[i].name, Ships[inst].ship_name)) {
744                                 if (Num_reinforcements < MAX_REINFORCEMENTS) {
745                                         j = Num_reinforcements++;
746                                         strcpy(Reinforcements[j].name, Ships[n].ship_name);
747                                         Reinforcements[j].type = Reinforcements[i].type;
748                                         Reinforcements[j].uses = Reinforcements[i].uses;
749                                 }
750
751                                 break;
752                         }
753
754         } else if (objp->type == OBJ_WAYPOINT) {
755                 obj = create_waypoint(&objp->pos, list);
756                 list = Objects[obj].instance;
757         }
758
759         if (obj == -1)
760                 return -1;
761
762         Objects[obj].pos = objp->pos;
763         Objects[obj].orient = objp->orient;
764         Objects[obj].flags |= OF_TEMP_MARKED;
765         return obj;
766 }
767
768 int create_object(vector *pos, int list)
769 {
770         int obj, n;
771
772         if (cur_model_index == Id_select_type_waypoint)
773                 obj = create_waypoint(pos, list);
774
775         else if (cur_model_index == Id_select_type_start) {
776                 if (Player_starts >= MAX_PLAYERS) {
777                         Fred_main_wnd->MessageBox("Unable to create new player start point.\n"
778                                 "You have reached the maximum limit.", NULL, MB_OK | MB_ICONEXCLAMATION);
779                         obj = -2;
780
781                 } else if (The_mission.game_type & MISSION_TYPE_SINGLE) {
782                         Fred_main_wnd->MessageBox("You can't have more than one player start in\n"
783                                 "single player missions.\n", NULL, MB_OK | MB_ICONEXCLAMATION);
784                         obj = -2;
785
786                 } else if (The_mission.game_type & MISSION_TYPE_TRAINING) {
787                         Fred_main_wnd->MessageBox("You can't have more than one player start in\n"
788                                 "a training missions.\n", NULL, MB_OK | MB_ICONEXCLAMATION);
789                         obj = -2;
790
791                 } else
792                         obj = create_player(Player_starts, pos, NULL, Default_player_model);
793
794         } else if (cur_model_index == Id_select_type_jump_node) {
795                 if (Num_jump_nodes >= MAX_JUMP_NODES) {
796                         Fred_main_wnd->MessageBox("Unable to create more jump nodes.  You have reached the limit.", NULL, MB_OK | MB_ICONEXCLAMATION);
797                         obj = -2;
798
799                 } else {
800                         obj = jumpnode_create(pos);
801                 }
802
803         } else if(Ship_info[cur_model_index].flags & SIF_NO_FRED){              
804                 obj = -1;
805         } else {  // creating a ship
806                 obj = create_ship(NULL, pos, cur_model_index);
807                 if (obj == -1)
808                         return -1;
809
810                 n = Objects[obj].instance;
811                 Ships[n].arrival_cue = alloc_sexp("true", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1);
812                 Ships[n].departure_cue = alloc_sexp("false", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1);
813                 Ships[n].cargo1 = 0;
814         }
815
816         if (obj < 0)
817                 return obj;
818
819         obj_merge_created_list();
820         set_modified();
821         Update_window = 1;
822         return obj;
823 }
824
825 int create_player(int num, vector *pos, matrix *orient, int type, int init)
826 {
827         int obj;
828
829         if (type == -1){
830                 type = Default_player_model;
831         }
832
833         Assert(type >= 0);
834         Assert(Player_starts < MAX_PLAYERS);
835         Player_starts++;
836         obj = create_ship(orient, pos, type);
837         Objects[obj].type = OBJ_START;
838
839         // be sure arrival/departure cues are set
840         Ships[Objects[obj].instance].arrival_cue = Locked_sexp_true;
841         Ships[Objects[obj].instance].departure_cue = Locked_sexp_false;
842         obj_merge_created_list();
843         set_modified();
844         return obj;
845 }
846
847 int query_waypoint_path_name_duplicate(int list)
848 {
849         int i;
850
851         for (i=0; i<Num_waypoint_lists; i++)
852                 if (i != list)
853                         if (!stricmp(Waypoint_lists[i].name, Waypoint_lists[list].name))
854                                 return 1;
855
856         return 0;
857 }
858
859 void get_unique_waypoint_path_name(int list)
860 {
861         int i = 1;
862
863         sprintf(Waypoint_lists[list].name, "Waypoint path %d", list + 1);
864         while (query_waypoint_path_name_duplicate(list)) {
865                 sprintf(Waypoint_lists[list].name, "Waypoint path U%d", i++);
866         }
867 }
868
869 int create_waypoint(vector *pos, int list)
870 {
871         int i, obj, index = 0;
872         object *ptr;
873
874         if (list == -1) {  // find a new list to start.
875                 for (list=0; list<MAX_WAYPOINT_LISTS; list++){
876                         if (!Waypoint_lists[list].count) {
877                                 get_unique_waypoint_path_name(list);
878                                 break;
879                         }
880                 }
881         } else {
882                 index = (list & 0xffff) + 1;
883                 list /= 65536;
884         }
885
886         if (list == MAX_WAYPOINT_LISTS) {
887                 Fred_main_wnd->MessageBox("Unable to create new waypoint path.  You\n"
888                         "have reached the maximum limit.", NULL, MB_OK | MB_ICONEXCLAMATION);
889                 return -1;
890         }
891
892         Assert((list >= 0) && (list < MAX_WAYPOINT_LISTS));  // illegal index or out of lists.
893         if (Waypoint_lists[list].count >= MAX_WAYPOINTS_PER_LIST) {
894                 Fred_main_wnd->MessageBox("Unable to create new waypoint.  You have\n"
895                         "reached the maximum limit on waypoints per list.", NULL, MB_OK | MB_ICONEXCLAMATION);
896                 return -1;
897         }
898
899         if (Waypoint_lists[list].count > index) {
900                 i = Waypoint_lists[list].count;
901                 while (i > index) {
902                         Waypoint_lists[list].waypoints[i] = Waypoint_lists[list].waypoints[i - 1];
903                         Waypoint_lists[list].flags[i] = Waypoint_lists[list].flags[i - 1];
904                         i--;
905                 }
906         }
907
908         ptr = GET_FIRST(&obj_used_list);
909         while (ptr != END_OF_LIST(&obj_used_list)) {
910                 Assert(ptr->type != OBJ_NONE);
911                 if (ptr->type == OBJ_WAYPOINT) {
912                         i = ptr->instance;
913                         if ((i / 65536 == list) && ((i & 0xffff) >= index)){
914                                 ptr->instance++;
915                         }
916                 }
917
918                 ptr = GET_NEXT(ptr);
919         }
920
921         Waypoint_lists[list].count++;
922         Waypoint_lists[list].flags[index] = 0;
923         Waypoint_lists[list].waypoints[index] = *pos;
924         if (list >= Num_waypoint_lists){
925                 Num_waypoint_lists = list + 1;
926         }
927
928         obj = obj_create(OBJ_WAYPOINT, -1, list * 65536 + index, NULL, pos, 0.0f, OF_RENDERS);
929         set_modified();
930         return obj;
931 }
932
933 void create_new_mission()
934 {
935         reset_mission();
936         *Mission_filename = 0;
937         FREDDoc_ptr->autosave("nothing");
938         Undo_count = 0;
939 }
940
941 void reset_mission()
942 {
943         clear_mission();
944         player_start1 = create_player(0, &vmd_zero_vector, &vmd_identity_matrix);
945 }
946
947 void clear_mission()
948 {
949         char *str;
950         int i, j, count;
951         CTime t;
952
953         // clean up everything we need to before we reset back to defaults.
954         if (Briefing_dialog){
955                 Briefing_dialog->reset_editor();
956         }
957
958         cmd_brief_reset();
959         mission_event_shutdown();
960
961         Asteroid_field.num_initial_asteroids = 0;  // disable asteroid field by default.
962         Asteroid_field.speed = 0.0f;
963         vm_vec_make(&Asteroid_field.min_bound, -1000.0f, -1000.0f, -1000.0f);
964         vm_vec_make(&Asteroid_field.max_bound,  1000.0f,  1000.0f,  1000.0f);
965         vm_vec_make(&Asteroid_field.inner_min_bound, -500.0f, -500.0f, -500.0f);
966         vm_vec_make(&Asteroid_field.inner_max_bound,  500.0f,  500.0f,  500.0f);
967         Asteroid_field.has_inner_bound = 0;
968         Asteroid_field.field_type = FT_ACTIVE;
969         Asteroid_field.debris_genre = DG_ASTEROID;
970         Asteroid_field.field_debris_type[0] = -1;
971         Asteroid_field.field_debris_type[1] = -1;
972         Asteroid_field.field_debris_type[2] = -1;
973
974         strcpy(Mission_parse_storm_name, "none");
975
976         obj_init();
977         model_free_all();                               // Free all existing models
978         ai_init();
979         ship_init();
980         Num_ai_dock_names = 0;
981         Num_jump_nodes = 0;
982         num_wings = 0;
983         for (i=0; i<MAX_WINGS; i++){
984                 Wings[i].wave_count = 0;
985         }
986
987         for (i=0; i<MAX_WAYPOINT_LISTS; i++){
988                 Waypoint_lists[i].count = 0;
989         }
990
991         for (i=0; i<MAX_TEAM_NAMES; i++){
992                 Shield_sys_teams[i] = 0;
993         }
994
995         for (i=0; i<MAX_SHIP_TYPES; i++){
996                 Shield_sys_types[i] = 0;
997         }
998
999         Num_reinforcements = 0;
1000         set_cur_indices(-1);
1001
1002         str = reg_read_string("SOFTWARE\\Microsoft\\Windows\\CurrentVersion", "RegisteredOwner", NULL);
1003         if (!str) {
1004                 str = reg_read_string("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "RegisteredOwner", NULL);
1005                 if (!str) {
1006                         str = getenv("USERNAME");
1007                         if (!str){
1008                                 str = UNKNOWN_USER;
1009                         }
1010                 }
1011         }
1012
1013         t = CTime::GetCurrentTime();
1014         strcpy(The_mission.name, "Untitled");
1015         strncpy(The_mission.author, str, NAME_LENGTH - 1);
1016         The_mission.author[NAME_LENGTH - 1] = 0;
1017         strcpy(The_mission.created, t.Format("%x at %X"));
1018         strcpy(The_mission.modified, The_mission.created);
1019         strcpy(The_mission.notes, "This is a FRED created mission\n");
1020         strcpy(The_mission.mission_desc, "Put mission description here\n");
1021         strcpy(The_mission.tour_name, "Blah");
1022         The_mission.game_type = MISSION_TYPE_SINGLE;
1023         strcpy(The_mission.pre_briefing_cutscene, "Blah");
1024         strcpy(The_mission.pre_mission_cutscene, "Blah");
1025         strcpy(The_mission.next_mission_success, "Blah");
1026         strcpy(The_mission.next_mission_partial, "Blah");
1027         strcpy(The_mission.next_mission_failure, "Blah");
1028         strcpy(The_mission.squad_name, "");
1029         strcpy(The_mission.squad_filename, "");
1030         The_mission.num_respawns = 3;
1031
1032         Player_starts = 0;
1033         Num_teams = 1;
1034
1035         // background bitmaps and suns
1036         Num_suns = 0;
1037         Num_starfield_bitmaps = 0;
1038
1039         // reset alternate name stuff
1040         for(i=0; i<MAX_SHIPS; i++){
1041                 strcpy(Fred_alt_names[i], "");
1042         }
1043
1044         // set up the default ship types for all teams.  For now, this is the same class
1045         // of ships for all teams
1046         for (i=0; i<MAX_TEAMS; i++) {
1047                 count = 0;
1048                 for ( j = 0; j < MAX_SHIP_TYPES; j++ ) {
1049                         if (Ship_info[j].flags & SIF_DEFAULT_PLAYER_SHIP) {
1050                                 Team_data[i].ship_list[count] = j;
1051                                 Team_data[i].ship_count[count++] = 5;
1052                         }
1053                 }
1054                 Team_data[i].number_choices = count;
1055
1056                 for (j=0; j<MAX_WEAPON_TYPES; j++){
1057                         if (Weapon_info[j].wi_flags & WIF_PLAYER_ALLOWED){
1058                                 if(Weapon_info[j].subtype == WP_LASER){
1059                                         Team_data[i].weaponry_pool[j] = 16;
1060                                 } else {
1061                                         Team_data[i].weaponry_pool[j] = 500;
1062                                 }
1063                         } else {
1064                                 Team_data[i].weaponry_pool[j] = 0;
1065                         }
1066                 }
1067         }
1068
1069         *Mission_text = *Mission_text_raw = EOF_CHAR;
1070         Mission_text[1] = Mission_text_raw[1] = 0;
1071
1072         Num_waypoint_lists = 0;
1073         Num_mission_events = 0;
1074         Num_goals = 0;
1075         unmark_all();
1076         obj_init();
1077         model_free_all();                               // Free all existing models
1078         fred_render_init();
1079         init_sexp();
1080         messages_init();
1081         brief_reset();
1082         debrief_reset();
1083
1084         // alternate ship type names
1085         mission_parse_reset_alt();
1086
1087         strcpy(Cargo_names[0], "Nothing");
1088         Num_cargo = 1;
1089         set_physics_controls();
1090         Num_starfield_bitmaps = 0;
1091         Nebula_index = 0;
1092         Mission_palette = 1;
1093         Nebula_pitch = (int) ((float) (rand() & 0x0fff) * 360.0f / 4096.0f);
1094         Nebula_bank = (int) ((float) (rand() & 0x0fff) * 360.0f / 4096.0f);
1095         Nebula_heading = (int) ((float) (rand() & 0x0fff) * 360.0f / 4096.0f);
1096         Neb2_awacs = -1.0f;
1097         Neb2_poof_flags = 0;
1098         strcpy(Neb2_texture_name, "");
1099         for(i=0; i<MAX_NEB2_POOFS; i++){
1100                 Neb2_poof_flags |= (1<<i);
1101         }
1102         Nmodel_num = -1;
1103         Nmodel_bitmap = -1;
1104         The_mission.flags &= ~(MISSION_FLAG_FULLNEB);
1105         nebula_init(Nebula_filenames[Nebula_index], Nebula_pitch, Nebula_bank, Nebula_heading);
1106
1107         char palette_filename[1024];
1108         strcpy(palette_filename, "gamepalette1-01");
1109 //      sprintf( palette_filename, "gamepalette%d-%02d", 1, Mission_palette+1 );
1110         mprintf(( "Loading palette %s\n", palette_filename ));
1111         palette_load_table(palette_filename);
1112
1113         set_modified(FALSE);
1114         Update_window = 1;
1115 }
1116
1117 int query_valid_object(int index)
1118 {
1119         int obj_found = FALSE;
1120         object *ptr;
1121
1122         if (index < 0 || index >= MAX_OBJECTS || Objects[index].type == OBJ_NONE)
1123                 return FALSE;
1124
1125         ptr = GET_FIRST(&obj_used_list);
1126         while (ptr != END_OF_LIST(&obj_used_list)) {
1127                 Assert(ptr->type != OBJ_NONE);
1128                 if (OBJ_INDEX(ptr) == index)
1129                         obj_found = TRUE;
1130                 
1131                 ptr = GET_NEXT(ptr);
1132         }
1133
1134         Assert(obj_found);  // just to make sure it's in the list like it should be.    
1135         return TRUE;
1136 }
1137
1138 int query_valid_ship(int index)
1139 {
1140         int obj_found = FALSE;
1141         object *ptr;
1142
1143         if (index < 0 || index >= MAX_OBJECTS || Objects[index].type != OBJ_SHIP)
1144                 return FALSE;
1145
1146         ptr = GET_FIRST(&obj_used_list);
1147         while (ptr != END_OF_LIST(&obj_used_list)) {
1148                 Assert(ptr->type != OBJ_NONE);
1149                 if (OBJ_INDEX(ptr) == index)
1150                         obj_found = TRUE;
1151                 
1152                 ptr = GET_NEXT(ptr);
1153         }
1154
1155         Assert(obj_found);  // just to make sure it's in the list like it should be.    
1156         return TRUE;
1157 }
1158
1159 int query_valid_waypoint(int index)
1160 {
1161         int obj_found = FALSE;
1162         object *ptr;
1163
1164         if (index < 0 || index >= MAX_OBJECTS || Objects[index].type != OBJ_WAYPOINT)
1165                 return FALSE;
1166
1167         ptr = GET_FIRST(&obj_used_list);
1168         while (ptr != END_OF_LIST(&obj_used_list)) {
1169                 Assert(ptr->type != OBJ_NONE);
1170                 if (OBJ_INDEX(ptr) == index)
1171                         obj_found = TRUE;
1172                 
1173                 ptr = GET_NEXT(ptr);
1174         }
1175
1176         Assert(obj_found);  // just to make sure it's in the list like it should be.    
1177         return TRUE;
1178 }
1179
1180 // Sets the current object to whatever is specified or advances to the next object
1181 // in the list if nothing is passed.
1182 void set_cur_object_index(int obj)
1183 {
1184         if (obj < 0)
1185                 unmark_all();
1186         else
1187                 mark_object(obj);
1188
1189         set_cur_indices(obj);  // select the new object
1190         Update_ship = Update_wing = 1;
1191         Waypoint_editor_dialog.initialize_data(1);
1192         Update_window = 1;
1193 }
1194
1195 // changes the currently selected wing.  It is assumed that cur_wing == cur_ship's wing
1196 // number.  Don't call this if this won't be true, or else you'll screw things up.
1197 void set_cur_wing(int wing)
1198 {
1199         cur_wing = wing;
1200 /*      if (cur_ship != -1)
1201                 Assert(cur_wing == Ships[cur_ship].wingnum);
1202         if ((cur_object_index != -1) && (Objects[cur_object_index].type == OBJ_SHIP))
1203                 Assert(cur_wing == Ships[Objects[cur_object_index].instance].wingnum);*/
1204         Update_wing = 1;
1205         Update_window = 1;
1206 }
1207
1208 // sets up the various cur_* global variables related to the selecting of an object.  This
1209 // is an internal function that shouldn't typically get called directly.  Use set_cur_object_index() instead.
1210 void set_cur_indices(int obj)
1211 {
1212         int i;
1213         object *ptr;
1214         CSingleLock sync(&CS_cur_object_index);
1215
1216         sync.Lock();  // Don't modify until it's unlocked (if it's locked elsewhere).
1217         if (query_valid_object(obj)) {
1218                 cur_object_index = obj;
1219                 cur_ship = cur_wing = cur_waypoint_list = cur_waypoint = -1;
1220                 if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START)) {
1221                         cur_ship = Objects[obj].instance;
1222                         cur_wing = Ships[cur_ship].wingnum;
1223                         if (cur_wing >= 0)
1224                                 for (i=0; i<Wings[cur_wing].wave_count; i++)
1225                                         if (wing_objects[cur_wing][i] == cur_object_index) {
1226                                                 cur_wing_index = i;
1227                                                 break;
1228                                         }
1229
1230                 } else if (Objects[obj].type == OBJ_WAYPOINT) {
1231                         cur_waypoint_list = Objects[obj].instance / 65536;
1232                         cur_waypoint = Objects[obj].instance & 0xffff;
1233                 }
1234
1235                 return;
1236         }
1237
1238         if (obj == -1 || !num_objects) {
1239                 cur_object_index = cur_ship = cur_wing = cur_waypoint_list = cur_waypoint = -1;
1240                 return;
1241         }
1242
1243         if (query_valid_object(cur_object_index))
1244                 ptr = Objects[cur_object_index].next;
1245         else
1246                 ptr = GET_FIRST(&obj_used_list);
1247
1248         if (ptr == END_OF_LIST(&obj_used_list))
1249                 ptr = ptr->next;
1250
1251         Assert(ptr != END_OF_LIST(&obj_used_list));
1252         cur_object_index = OBJ_INDEX(ptr);
1253         Assert(ptr->type != OBJ_NONE);
1254         cur_ship = cur_wing = cur_waypoint_list = cur_waypoint = -1;
1255         if (ptr->type == OBJ_SHIP) {
1256                 cur_ship = ptr->instance;
1257                 cur_wing = Ships[cur_ship].wingnum;
1258                 for (i=0; i<Wings[cur_wing].wave_count; i++)
1259                         if (wing_objects[cur_wing][i] == cur_object_index) {
1260                                 cur_wing_index = i;
1261                                 break;
1262                         }
1263
1264         } else if (ptr->type == OBJ_WAYPOINT) {
1265                 cur_waypoint_list = ptr->instance / 65536;
1266                 cur_waypoint = ptr->instance & 0xffff;
1267         }
1268 }
1269
1270 int update_dialog_boxes()
1271 {
1272         int z;
1273
1274         nprintf(("Fred routing", "updating dialog boxes\n"));
1275
1276         // check wing first, since ships are dependent on wings, but not the reverse
1277         z = Wing_editor_dialog.update_data(0);
1278         if (z) {
1279                 nprintf(("Fred routing", "wing dialog save failed\n"));
1280                 Wing_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
1281                         SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1282
1283                 return z;
1284         }
1285
1286         z = Ship_editor_dialog.update_data(0);
1287         if (z) {
1288                 nprintf(("Fred routing", "ship dialog save failed\n"));
1289                 Ship_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
1290                         SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1291
1292                 return z;
1293         }
1294
1295         z = Waypoint_editor_dialog.update_data(0);
1296         if (z) {
1297                 nprintf(("Fred routing", "waypoint dialog save failed\n"));
1298                 Waypoint_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
1299                         SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1300
1301                 return z;
1302         }
1303
1304         update_map_window();
1305         return 0;
1306 }
1307
1308 int delete_object(int obj)
1309 {
1310         int r;
1311
1312         Ship_editor_dialog.bypass_all++;
1313         r = common_object_delete(obj);
1314         Ship_editor_dialog.bypass_all--;
1315         return r;
1316 }
1317
1318 int delete_object(object *ptr)
1319 {
1320         int r;
1321
1322         Ship_editor_dialog.bypass_all++;
1323         r = common_object_delete(OBJ_INDEX(ptr));
1324         Ship_editor_dialog.bypass_all--;
1325         return r;
1326 }
1327
1328 int delete_ship(int ship)
1329 {
1330         int r;
1331
1332         Ship_editor_dialog.bypass_all++;
1333         r = common_object_delete(Ships[ship].objnum);
1334         Ship_editor_dialog.bypass_all--;
1335         return r;
1336 }
1337
1338 int common_object_delete(int obj)
1339 {
1340         char msg[255], *name;
1341         int i, z, r, type, num;
1342         object *objp;
1343
1344         type = Objects[obj].type;
1345         if (type == OBJ_START) {
1346                 i = Objects[obj].instance;
1347                 if (Player_starts < 2) {  // player 1 start
1348                         Fred_main_wnd->MessageBox("Must have at least 1 player starting point!",
1349                                 NULL, MB_OK | MB_ICONEXCLAMATION);
1350
1351                         unmark_object(obj);
1352                         return 1;
1353                 }
1354
1355                 Assert((i >= 0) && (i < MAX_SHIPS));
1356                 sprintf(msg, "Player %d", i + 1);
1357                 name = msg;
1358                 r = reference_handler(name, REF_TYPE_PLAYER, obj);
1359                 if (r)
1360                         return r;
1361
1362                 if (Ships[i].wingnum >= 0) {
1363                         r = delete_ship_from_wing(i);
1364                         if (r)
1365                                 return r;
1366                 }
1367
1368                 Objects[obj].type = OBJ_SHIP;  // was allocated as a ship originally, so remove as such.
1369                 invalidate_references(name, REF_TYPE_PLAYER);
1370                 objp = GET_FIRST(&obj_used_list);
1371                 while (objp != END_OF_LIST(&obj_used_list)) {
1372                         // check if any ship is docked with this ship and break dock if so.
1373                         if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) {
1374                                 num = get_ship_from_obj(objp);
1375                                 if (Ai_info[Ships[num].ai_index].dock_objnum == obj)
1376                                         Ai_info[Ships[num].ai_index].dock_objnum = -1;
1377                         }
1378
1379                         objp = GET_NEXT(objp);
1380                 }
1381
1382                 if (Player_start_shipnum == i) {  // need a new single player start.
1383                         objp = GET_FIRST(&obj_used_list);
1384                         while (objp != END_OF_LIST(&obj_used_list)) {
1385                                 if (objp->type == OBJ_START) {
1386                                         Player_start_shipnum = objp->instance;
1387                                         break;
1388                                 }
1389
1390                                 objp = GET_NEXT(objp);
1391                         }
1392                 }
1393
1394                 Player_starts--;
1395
1396         } else if (type == OBJ_WAYPOINT) {
1397                 int list, count;
1398
1399                 list = Objects[obj].instance / 65536;
1400                 i = Objects[obj].instance & 0xffff;
1401                 Assert(list >= 0 && list < MAX_WAYPOINT_LISTS);
1402                 count = Waypoint_lists[list].count;
1403                 Assert(i >= 0 && i < count);
1404
1405                 if (Waypoint_lists[list].count == 1) {
1406                         name = Waypoint_lists[list].name;
1407                         r = reference_handler(name, REF_TYPE_PATH, obj);
1408                         if (r)
1409                                 return r;
1410                 }
1411
1412                 sprintf(msg, "%s:%d", Waypoint_lists[list].name, i + 1);
1413                 name = msg;
1414                 r = reference_handler(name, REF_TYPE_WAYPOINT, obj);
1415                 if (r)
1416                         return r;
1417
1418                 invalidate_references(name, REF_TYPE_WAYPOINT);
1419                 objp = GET_FIRST(&obj_used_list);
1420                 while (objp != END_OF_LIST(&obj_used_list)) {
1421                         if ((objp->type == OBJ_WAYPOINT) && ((objp->instance / 65536) == list))
1422                                 if ((objp->instance & 0xffff) > i)
1423                                         objp->instance--;
1424
1425                         objp = GET_NEXT(objp);
1426                 }
1427
1428                 while (i < count - 1) {
1429                         Waypoint_lists[list].waypoints[i] = Waypoint_lists[list].waypoints[i + 1];
1430                         i++;
1431                 }
1432
1433                 Waypoint_lists[list].count--;
1434                 if (!Waypoint_lists[list].count) {
1435                         invalidate_references(Waypoint_lists[list].name, REF_TYPE_PATH);
1436                         objp = GET_FIRST(&obj_used_list);
1437                         while (objp != END_OF_LIST(&obj_used_list)) {
1438                                 if ((objp->type == OBJ_WAYPOINT) && ((objp->instance / 65536) > list))
1439                                         objp->instance -= 65536;
1440
1441                                 objp = GET_NEXT(objp);
1442                         }
1443
1444                         while (list < Num_waypoint_lists - 1) {
1445                                 Waypoint_lists[list] = Waypoint_lists[list + 1];
1446                                 list++;
1447                         }
1448
1449                         Num_waypoint_lists--;
1450                         Waypoint_lists[list].count = 0;
1451                 }
1452
1453         } else if (type == OBJ_SHIP) {
1454                 name = Ships[Objects[obj].instance].ship_name;
1455                 r = reference_handler(name, REF_TYPE_SHIP, obj);
1456                 if (r)
1457                         return r;
1458
1459                 z = Objects[obj].instance;
1460                 if (Ships[z].wingnum >= 1) {
1461                         invalidate_references(name, REF_TYPE_SHIP);
1462                         r = delete_ship_from_wing(z);
1463                         if (r)
1464                                 return r;
1465
1466                 } else if (Ships[z].wingnum >= 0) {
1467                         r = delete_ship_from_wing(z);
1468                         if (r)
1469                                 return r;
1470
1471                         invalidate_references(name, REF_TYPE_SHIP);
1472                 }
1473
1474                 for (i=0; i<Num_reinforcements; i++)
1475                         if (!stricmp(name, Reinforcements[i].name)) {
1476                                 delete_reinforcement(i);
1477                                 break;
1478                         }
1479
1480                 objp = GET_FIRST(&obj_used_list);
1481                 while (objp != END_OF_LIST(&obj_used_list)) {
1482                         // check if any ship is docked with this ship and break dock if so.
1483                         if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) {
1484                                 num = get_ship_from_obj(objp);
1485                                 if (Ai_info[Ships[num].ai_index].dock_objnum == obj)
1486                                         Ai_info[Ships[num].ai_index].dock_objnum = -1;
1487                         }
1488
1489                         objp = GET_NEXT(objp);
1490                 }
1491
1492         } else if (type == OBJ_POINT) {
1493                 Assert(Briefing_dialog);
1494                 Briefing_dialog->delete_icon(Objects[obj].instance);
1495                 Update_window = 1;
1496                 return 0;
1497
1498         } else if (type == OBJ_JUMP_NODE) {
1499                 i = Objects[obj].instance;
1500                 objp = GET_FIRST(&obj_used_list);
1501                 while (objp != END_OF_LIST(&obj_used_list)) {
1502                         if ((objp->type == OBJ_JUMP_NODE) && (objp->instance > i))
1503                                 objp->instance--;
1504
1505                         objp = GET_NEXT(objp);
1506                 }
1507
1508                 while (i < Num_jump_nodes - 1) {
1509                         Jump_nodes[i] = Jump_nodes[i + 1];
1510                         i++;
1511                 }
1512
1513                 Num_jump_nodes--;
1514         }
1515
1516         unmark_object(obj);
1517         obj_delete(obj);
1518         set_modified();
1519         Update_window = 1;
1520         return 0;
1521 }
1522
1523 void delete_marked()
1524 {
1525         object *ptr, *next;
1526
1527         delete_flag = 0;
1528         ptr = GET_FIRST(&obj_used_list);
1529         while (ptr != END_OF_LIST(&obj_used_list)) {
1530                 next = GET_NEXT(ptr);
1531                 if (ptr->flags & OF_MARKED)
1532                         if (delete_object(ptr) == 2)  // user went to a reference, so don't get in the way.
1533                                 break;
1534                 
1535                 ptr = next;
1536         }
1537
1538         if (!delete_flag)
1539                 set_cur_object_index(-1);
1540
1541         Update_window = 1;
1542 }
1543
1544 void delete_reinforcement(int num)
1545 {
1546         int i;
1547
1548         for (i=num; i<Num_reinforcements-1; i++)
1549                 Reinforcements[i] = Reinforcements[i + 1];
1550
1551         Num_reinforcements--;
1552         set_modified();
1553 }
1554
1555 // delete ship, removing it from it's wing if necessary.
1556 int delete_ship_from_wing(int ship)
1557 {
1558         char name[NAME_LENGTH];
1559         int i, r, wing, end;
1560
1561         wing = Ships[ship].wingnum;
1562         if (wing >= 0) {
1563                 if (Wings[wing].wave_count == 1) {
1564                         cur_wing = -1;
1565                         Update_wing = 1;
1566                         r = delete_wing(wing, 1);
1567                         if (r) {
1568                                 if (r == 2){
1569                                         delete_flag = 1;
1570                                 }
1571
1572                                 return r;
1573                         }
1574                 
1575                 } else {
1576                         i = Wings[wing].wave_count;
1577                         end = i - 1;
1578                         while (i--){
1579                                 if (wing_objects[wing][i] == Ships[ship].objnum){
1580                                         break;
1581                                 }
1582                         }
1583
1584                         Assert(i != -1);  // Error, object should be in wing.
1585                         if (Wings[wing].special_ship == i){
1586                                 Wings[wing].special_ship = 0;
1587                         } else if (Wings[wing].special_ship > i) {
1588                                 Wings[wing].special_ship--;
1589                         }
1590
1591                         if (i != end) {
1592                                 wing_objects[wing][i] = wing_objects[wing][end];
1593                                 Wings[wing].ship_index[i] = Wings[wing].ship_index[end];
1594                                 if (Objects[wing_objects[wing][i]].type == OBJ_SHIP) {
1595                                         sprintf(name, "%s %d", Wings[wing].name, i + 1);
1596                                         rename_ship(Wings[wing].ship_index[i], name);
1597                                 }
1598                         }
1599
1600                         if (Wings[wing].threshold >= Wings[wing].wave_count){
1601                                 Wings[wing].threshold = Wings[wing].wave_count - 1;
1602                         }
1603
1604                         Wings[wing].wave_count--;
1605                         if (Wings[wing].wave_count && (Wings[wing].threshold >= Wings[wing].wave_count)){
1606                                 Wings[wing].threshold = Wings[wing].wave_count - 1;
1607                         }
1608                 }
1609         }
1610
1611         set_modified();
1612         return 0;
1613 }
1614
1615 // What does this do?
1616 void add_ship_to_wing()
1617 {
1618         int             org_object = cur_object_index;
1619         vector  tvec;
1620
1621         set_cur_object_index();
1622         if (Objects[org_object].type == OBJ_NONE) {
1623                 create_object(vm_vec_make(&tvec, 10.0f, 10.0f, 10.0f));
1624         
1625         } else {
1626                 Objects[cur_object_index] = Objects[org_object];
1627                 Objects[cur_object_index].pos.x += 3.0f;
1628                 Objects[cur_object_index].pos.y += 3.0f;
1629                 physics_init(&Objects[cur_object_index].phys_info);
1630                 Objects[cur_object_index].orient = Objects[org_object].orient;
1631         }
1632
1633         set_modified();
1634 }
1635
1636 //      Return true if current object is valid and is in a wing.
1637 //      Else return false.
1638 int query_object_in_wing(int obj)
1639 {
1640         if (query_valid_object(obj)){
1641                 if (Ships[Objects[obj].instance].wingnum != -1){
1642                         return TRUE;
1643                 }
1644         }
1645         
1646         return FALSE;
1647 }
1648
1649 void mark_object(int obj)
1650 {
1651         Assert(query_valid_object(obj));
1652         if (!(Objects[obj].flags & OF_MARKED)) {
1653                 Objects[obj].flags |= OF_MARKED;  // set as marked
1654                 Marked++;
1655                 Update_window = 1;
1656                 if (cur_object_index == -1){
1657                         set_cur_object_index(obj);
1658                 }
1659                 Update_ship = Update_wing = 1;
1660                 Waypoint_editor_dialog.initialize_data(1);
1661         }
1662 }
1663
1664 void unmark_object(int obj)
1665 {
1666         Assert(query_valid_object(obj));
1667         if (Objects[obj].flags & OF_MARKED) {
1668                 Objects[obj].flags &= ~OF_MARKED;
1669                 Marked--;
1670                 Update_window = 1;
1671                 if (obj == cur_object_index) {  // need to find a new index
1672                         object *ptr;
1673
1674                         ptr = GET_FIRST(&obj_used_list);
1675                         while (ptr != END_OF_LIST(&obj_used_list)) {
1676                                 if (ptr->flags & OF_MARKED) {
1677                                         set_cur_object_index(OBJ_INDEX(ptr));  // found one
1678                                         return;
1679                                 }
1680
1681                                 ptr = GET_NEXT(ptr);
1682                         }
1683
1684                         set_cur_object_index(-1);  // can't find one; nothing is marked.
1685                 }
1686                 Update_ship = Update_wing = 1;
1687                 Waypoint_editor_dialog.initialize_data(1);
1688         }
1689 }
1690
1691 // clears the marked flag of all objects (so nothing is marked)
1692 void unmark_all()
1693 {
1694         int i;
1695
1696         if (Marked) {
1697                 for (i=0; i<MAX_OBJECTS; i++){
1698                         Objects[i].flags &= ~OF_MARKED;
1699                 }
1700
1701                 Marked = 0;
1702                 Update_window = 1;
1703                 set_cur_object_index(-1);
1704         }
1705 }
1706
1707 void clear_menu(CMenu *ptr)
1708 {
1709         int count;
1710
1711         count = ptr->GetMenuItemCount();
1712         while (count--){
1713                 ptr->DeleteMenu(count, MF_BYPOSITION);
1714         }
1715 }
1716
1717 void generate_wing_popup_menu(CMenu *mptr, int first_id, int state)
1718 {
1719         int i, z, columns, rows, count;
1720
1721         columns = 1;
1722         rows = num_wings;
1723         while (rows > 25) {
1724                 columns++;
1725                 rows = num_wings / columns;
1726         }
1727
1728         count = rows + 1;
1729         for (i=0; i<MAX_WINGS; i++){
1730                 if (Wings[i].wave_count) {
1731                         z = state | MF_STRING;
1732                         if (!count--) {
1733                                 count = rows;
1734                                 z |= MF_MENUBARBREAK;
1735                         }
1736
1737                         mptr->AppendMenu(z, first_id + i, Wings[i].name);
1738                 }
1739         }
1740
1741         mptr->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1742 }
1743
1744 void generate_ship_popup_menu(CMenu *mptr, int first_id, int state, int filter)
1745 {
1746         int z, ship, columns, rows, count, num_ships;
1747         object *ptr;
1748
1749         columns = 1;
1750         num_ships = ship_get_num_ships();
1751         rows = num_ships;
1752         while (rows > 25) {
1753                 columns++;
1754                 rows = num_ships / columns;
1755         }
1756
1757         count = rows + 1;
1758         ptr = GET_FIRST(&obj_used_list);
1759         while (ptr != END_OF_LIST(&obj_used_list)) {
1760                 if ((ptr->type == OBJ_SHIP) || ((ptr->type == OBJ_START) && (filter & SHIP_FILTER_PLAYERS))) {
1761                         z = 1;
1762                         if (filter & SHIP_FILTER_FLYABLE) {
1763                                 if (Ship_info[Ships[get_ship_from_obj(ptr)].ship_info_index].flags & SIF_NOT_FLYABLE){
1764                                         z = 0;
1765                                 }
1766                         }
1767
1768                         if (z) {
1769                                 z = state | MF_STRING;
1770                                 if (!count--) {
1771                                         count = rows;
1772                                         z |= MF_MENUBARBREAK;
1773                                 }
1774
1775                                 ship = ptr->instance;
1776                                 mptr->AppendMenu(z, first_id + ship, Ships[ship].ship_name);
1777                         }
1778                 }
1779
1780                 ptr = GET_NEXT(ptr);
1781         }
1782
1783         mptr->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1784 }
1785
1786 // Alternate string lookup function, taking a CString instead.  The reason that it's here,
1787 // instead of parselo.cpp, is because the class CString require an include of windows.h,
1788 // which everyone wants to avoid including in any freespace header files.  So..
1789 int string_lookup(CString str1, char *strlist[], int max)
1790 {
1791         int     i;
1792
1793         for (i=0; i<max; i++) {
1794                 Assert(strlen(strlist[i]));
1795
1796                 if (!stricmp(str1, strlist[i])){
1797                         return i;
1798                 }
1799         }
1800
1801         return -1;
1802 }
1803
1804 int gray_menu_tree(CMenu *base)
1805 {
1806         int i, z, count = 0;
1807         CMenu *submenu;
1808
1809         i = base->GetMenuItemCount();
1810         while (i--) {
1811                 if ((submenu = base->GetSubMenu(i))>0) {
1812                         if (gray_menu_tree(submenu)) {
1813                                 count++;
1814                         } else {
1815                                 base->EnableMenuItem(i, MF_GRAYED | MF_BYPOSITION);
1816                         }
1817
1818                 } else {
1819                         z = base->GetMenuState(i, MF_BYPOSITION);
1820                         if (z == MF_ENABLED){
1821                                 count++;
1822                         }
1823                 }
1824         }
1825
1826         return count;
1827 }
1828
1829 int query_initial_orders_conflict(int wing)
1830 {
1831         int i, z;
1832
1833         Assert(wing != -1);
1834         if (wing == -1){
1835                 return 0;
1836         }
1837
1838         if (query_initial_orders_empty(Wings[wing].ai_goals)){
1839                 return 0;
1840         }
1841
1842         i = Wings[wing].wave_count;  // wing has orders, now check ships.
1843         while (i--) {
1844                 z = Ships[Objects[wing_objects[wing][i]].instance].ai_index;
1845                 if (!query_initial_orders_empty(Ai_info[z].goals)){  // ship also has orders
1846                         return 1;
1847                 }
1848         }
1849
1850         return 0;
1851 }
1852
1853 int query_initial_orders_empty(ai_goal *ai_goals)
1854 {
1855         int i;
1856
1857         for (i=0; i<MAX_AI_GOALS; i++){
1858                 if (ai_goals[i].ai_mode != AI_GOAL_NONE){
1859                         return 0;
1860                 }
1861         }
1862
1863         return 1;
1864 }
1865
1866 int set_reinforcement(char *name, int state)
1867 {
1868         int i, index, cur = -1;
1869
1870         for (i=0; i<Num_reinforcements; i++){
1871                 if (!stricmp(Reinforcements[i].name, name)){
1872                         cur = i;
1873                 }
1874         }
1875
1876         if (!state && (cur != -1)) {
1877                 Num_reinforcements--;
1878                 Reinforcements[cur] = Reinforcements[Num_reinforcements];
1879
1880                 // clear the ship/wing flag for this reinforcement
1881                 index = ship_name_lookup(name);
1882                 if ( index != -1 ){
1883                         Ships[index].flags &= ~SF_REINFORCEMENT;
1884                 } else {
1885                         index = wing_name_lookup(name);
1886                         if ( index != -1 ){
1887                                 Wings[index].flags &= ~WF_REINFORCEMENT;
1888                         }
1889                 }
1890                 if (index == -1 ){
1891                         Int3();                         // get allender -- coudln't find ship/wing for clearing reinforcement flag
1892                 }
1893
1894                 set_modified();
1895                 return -1;
1896         }
1897
1898         if (state && (cur == -1) && (Num_reinforcements < MAX_REINFORCEMENTS)) {
1899                 Assert(strlen(name) < NAME_LENGTH);
1900                 strcpy(Reinforcements[Num_reinforcements].name, name);
1901                 Reinforcements[Num_reinforcements].uses = 1;
1902                 Reinforcements[Num_reinforcements].arrival_delay = 0;
1903                 memset( Reinforcements[Num_reinforcements].no_messages, 0, MAX_REINFORCEMENT_MESSAGES * NAME_LENGTH );
1904                 memset( Reinforcements[Num_reinforcements].yes_messages, 0, MAX_REINFORCEMENT_MESSAGES * NAME_LENGTH );
1905                 Num_reinforcements++;
1906
1907                 // set the reinforcement flag on the ship or wing
1908                 index = ship_name_lookup(name);
1909                 if ( index != -1 ){
1910                         Ships[index].flags |= SF_REINFORCEMENT;
1911                 } else {
1912                         index = wing_name_lookup(name);
1913                         if ( index != -1 ){
1914                                 Wings[index].flags |= WF_REINFORCEMENT;
1915                         }
1916                 }
1917                 if ( index == -1 ){
1918                         Int3();                         // get allender -- coudln't find ship/wing for setting reinforcement flag
1919                 }
1920
1921                 set_modified();
1922                 return 1;
1923         }
1924
1925         // this code will take care of setting the bits for the ship/wing flags
1926         if ( state && (cur != -1) ) {
1927                 // set the reinforcement flag on the ship or wing
1928                 index = ship_name_lookup(name);
1929                 if ( index != -1 ){
1930                         Ships[index].flags |= SF_REINFORCEMENT;
1931                 } else {
1932                         index = wing_name_lookup(name);
1933                         if ( index != -1 ){
1934                                 Wings[index].flags |= WF_REINFORCEMENT;
1935                         }
1936                 }
1937                 if ( index == -1 ){
1938                         Int3();                         // get allender -- coudln't find ship/wing for setting reinforcement flag
1939                 }
1940         }
1941
1942         return 0;
1943 }
1944
1945 int get_docking_list(int model_index)
1946 {
1947         int i;
1948         polymodel *pm;
1949
1950         pm = model_get(model_index);
1951         Assert(pm->n_docks <= MAX_DOCKS);
1952         for (i=0; i<pm->n_docks; i++)
1953                 Docking_bay_list[i] = pm->docking_bays[i].name;
1954
1955         return pm->n_docks;
1956 }
1957
1958 // DA 1/7/99 These ship names are not variables
1959 int rename_ship(int ship, char *name)
1960 {
1961         int i;
1962
1963         Assert(ship >= 0);
1964         Assert(strlen(name) < NAME_LENGTH);
1965
1966         update_sexp_references(Ships[ship].ship_name, name);
1967         ai_update_goal_references(REF_TYPE_SHIP, Ships[ship].ship_name, name);
1968         for (i=0; i<Num_reinforcements; i++)
1969                 if (!stricmp(Ships[ship].ship_name, Reinforcements[i].name)) {
1970                         strcpy(Reinforcements[i].name, name);
1971                 }
1972
1973         strcpy(Ships[ship].ship_name, name);
1974         if (ship == cur_ship)
1975                 Ship_editor_dialog.m_ship_name = _T(name);
1976
1977         return 0;
1978 }
1979
1980 int invalidate_references(char *name, int type)
1981 {
1982         char new_name[512];
1983         int i;
1984
1985         sprintf(new_name, "<%s>", name);
1986         update_sexp_references(name, new_name);
1987         ai_update_goal_references(type, name, new_name);
1988         for (i=0; i<Num_reinforcements; i++)
1989                 if (!stricmp(name, Reinforcements[i].name)) {
1990                         strcpy(Reinforcements[i].name, new_name);
1991                 }
1992
1993         return 0;
1994 }
1995
1996 int internal_integrity_check()
1997 {
1998         int i;
1999
2000         for (i=0; i<Num_mission_events; i++)
2001                 verify_sexp_tree(Mission_events[i].formula);
2002
2003         for (i=0; i<Num_goals; i++)
2004                 verify_sexp_tree(Mission_goals[i].formula);
2005
2006         for (i=0; i<MAX_WINGS; i++)
2007                 if (Wings[i].wave_count) {
2008                         verify_sexp_tree(Wings[i].arrival_cue);
2009                         verify_sexp_tree(Wings[i].departure_cue);
2010                 }
2011
2012         for (i=0; i<MAX_SHIPS; i++)
2013                 if (Ships[i].objnum >= 0) {
2014                         verify_sexp_tree(Ships[i].arrival_cue);
2015                         verify_sexp_tree(Ships[i].departure_cue);
2016                         if (Ships[i].ai_index < 0)
2017                                 Assert(0);
2018                         if (Ai_info[Ships[i].ai_index].shipnum != i)
2019                                 Int3();
2020                 }
2021
2022         return 0;
2023 }
2024
2025 void correct_marking()
2026 {
2027         object *ptr;
2028
2029         ptr = GET_FIRST(&obj_used_list);
2030         while (ptr != END_OF_LIST(&obj_used_list)) {
2031                 if (ptr->flags & OF_MARKED) {
2032                         if (ptr->flags & OF_HIDDEN)
2033                                 unmark_object(OBJ_INDEX(ptr));
2034
2035                         else switch (ptr->type) {
2036                                 case OBJ_WAYPOINT:
2037                                         if (!Show_waypoints)
2038                                                 unmark_object(OBJ_INDEX(ptr));
2039                                         break;
2040
2041                                 case OBJ_START:
2042                                         if (!Show_starts)
2043                                                 unmark_object(OBJ_INDEX(ptr));
2044                                         break;
2045
2046                                 case OBJ_SHIP:
2047                                         if (!Show_ships)
2048                                                 unmark_object(OBJ_INDEX(ptr));
2049
2050                                         switch (Ships[ptr->instance].team) {
2051                                                 case TEAM_FRIENDLY:
2052                                                         if (!Show_friendly)
2053                                                                 unmark_object(OBJ_INDEX(ptr));
2054                                                         break;
2055
2056                                                 case TEAM_HOSTILE:
2057                                                         if (!Show_hostile)
2058                                                                 unmark_object(OBJ_INDEX(ptr));
2059                                                         break;
2060
2061                                                 case TEAM_NEUTRAL:
2062                                                         if (!Show_neutral)
2063                                                                 unmark_object(OBJ_INDEX(ptr));
2064                                                         break;
2065                                         }
2066
2067                                         break;
2068                         }
2069                 }
2070
2071                 ptr = GET_NEXT(ptr);
2072         }
2073 }
2074
2075 // Fills a combo box with a list of all docking points of type 'type' on ship 'ship'.
2076 // Item data is the actual docking point index.
2077 void set_valid_dock_points(int ship, int type, CComboBox *box)
2078 {
2079         int i, z, num, model;
2080
2081         model = Ships[ship].modelnum;
2082         num = model_get_num_dock_points(model);
2083         for (i=0; i<num; i++)
2084                 if (model_get_dock_index_type(model, i) & type) {
2085                         z = box->AddString(model_get_dock_name(model, i));
2086                         box->SetItemData(z, i);
2087                 }
2088
2089         Assert(box->GetCount());
2090 }
2091
2092 // Given an object index, find the ship index for that object.
2093 int get_ship_from_obj(int obj)
2094 {
2095         if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START))
2096                 return Objects[obj].instance;
2097
2098         Int3();
2099         return 0;
2100 }
2101
2102 int get_ship_from_obj(object *objp)
2103 {
2104         if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START))
2105                 return objp->instance;
2106
2107         Int3();
2108         return 0;
2109 }
2110
2111 void ai_update_goal_references(int type, char *old_name, char *new_name)
2112 {
2113         int i;
2114
2115         for (i=0; i<MAX_AI_INFO; i++)  // loop through all Ai_info entries
2116                 if (Ai_info[i].shipnum != -1)  // skip if unused
2117                         ai_update_goal_references(Ai_info[i].goals, type, old_name, new_name);
2118
2119         for (i=0; i<MAX_WINGS; i++)
2120                 if (Wings[i].wave_count)
2121                         ai_update_goal_references(Wings[i].ai_goals, type, old_name, new_name);
2122 }
2123
2124 int query_referenced_in_ai_goals(int type, char *name)
2125 {
2126         int i;
2127
2128         for (i=0; i<MAX_AI_INFO; i++)  // loop through all Ai_info entries
2129                 if (Ai_info[i].shipnum >= 0)  // skip if unused
2130                         if (query_referenced_in_ai_goals(Ai_info[i].goals, type, name))
2131                                 return Ai_info[i].shipnum | SRC_SHIP_ORDER;
2132
2133         for (i=0; i<MAX_WINGS; i++)
2134                 if (Wings[i].wave_count)
2135                         if (query_referenced_in_ai_goals(Wings[i].ai_goals, type, name))
2136                                 return i | SRC_WING_ORDER;
2137
2138         return 0;
2139 }
2140
2141 int advanced_stricmp(char *one, char *two)
2142 {
2143         if (!one && !two)
2144                 return 0;
2145
2146         if (!one)
2147                 return -1;
2148
2149         if (!two)
2150                 return 1;
2151
2152         return stricmp(one, two);
2153 }
2154
2155 // returns 0: go ahead change object
2156 //                        1: don't change it
2157 //                        2: abort (they used cancel to go to reference)
2158 int reference_handler(char *name, int type, int obj)
2159 {
2160         char msg[2048], text[128], type_name[128];
2161         int r, n, node;
2162
2163         switch (type) {
2164                 case REF_TYPE_SHIP:
2165                         sprintf(type_name, "Ship \"%s\"", name);
2166                         break;
2167
2168                 case REF_TYPE_WING:
2169                         sprintf(type_name, "Wing \"%s\"", name);
2170                         break;
2171
2172                 case REF_TYPE_PLAYER:
2173                         strcpy(type_name, name);
2174                         break;
2175
2176                 case REF_TYPE_WAYPOINT:
2177                         sprintf(type_name, "Waypoint \"%s\"", name);
2178                         break;
2179
2180                 case REF_TYPE_PATH:
2181                         sprintf(type_name, "Waypoint path \"%s\"", name);
2182                         break;
2183
2184                 default:
2185                         Error(LOCATION, "Type unknown for object \"%s\".  Let Hoffos know now!", name);
2186         }
2187
2188         r = query_referenced_in_sexp(type, name, &node);
2189         if (r) {
2190                 n = r & SRC_DATA_MASK;
2191                 switch (r & SRC_MASK) {
2192                         case SRC_SHIP_ARRIVAL:
2193                                 sprintf(text, "the arrival cue of ship \"%s\"", Ships[n].ship_name);
2194                                 break;
2195
2196                         case SRC_SHIP_DEPARTURE:
2197                                 sprintf(text, "the departure cue of ship \"%s\"", Ships[n].ship_name);
2198                                 break;
2199
2200                         case SRC_WING_ARRIVAL:
2201                                 sprintf(text, "the arrival cue of wing \"%s\"", Wings[n].name);
2202                                 break;
2203
2204                         case SRC_WING_DEPARTURE:
2205                                 sprintf(text, "the departure cue of wing \"%s\"", Wings[n].name);
2206                                 break;
2207
2208                         case SRC_EVENT:
2209                                 if (*Mission_events[n].name)
2210                                         sprintf(text, "event \"%s\"", Mission_events[n].name);
2211                                 else
2212                                         sprintf(text, "event #%d", n);
2213
2214                                 break;
2215
2216                         case SRC_MISSION_GOAL:
2217                                 if (*Mission_goals[n].name)
2218                                         sprintf(text, "mission goal \"%s\"", Mission_goals[n].name);
2219                                 else
2220                                         sprintf(text, "mission goal #%d", n);
2221
2222                                 break;
2223
2224                         case SRC_DEBRIEFING:
2225                                 sprintf(text, "debriefing #%d", n);
2226                                 break;
2227
2228                         case SRC_BRIEFING:
2229                                 sprintf(text, "briefing #%d", n);
2230                                 break;
2231
2232                         default:  // very bad.  Someone added an sexp somewhere and didn't change this.
2233                                 Warning(LOCATION, "\"%s\" referenced by an unknown sexp source!  "
2234                                         "Run for the hills and let Hoffoss know right now!", name);
2235
2236                                 delete_flag = 1;
2237                                 return 2;
2238                 }
2239
2240                 sprintf(msg, "%s is referenced by %s (possibly more sexps).\n"
2241                         "Do you want to delete it anyway?\n\n"
2242                         "(click Cancel to go to the reference)", type_name, text);
2243
2244                 r = sexp_reference_handler(node, r, msg);
2245                 if (r == 1) {
2246                         if (obj >= 0)
2247                                 unmark_object(obj);
2248
2249                         return 1;
2250                 }
2251
2252                 if (r == 2) {
2253                         delete_flag = 1;
2254                         return 2;
2255                 }
2256         }
2257
2258         r = query_referenced_in_ai_goals(type, name);
2259         if (r) {
2260                 n = r & SRC_DATA_MASK;
2261                 switch (r & SRC_MASK) {
2262                         case SRC_SHIP_ORDER:
2263                                 sprintf(text, "ship \"%s\"", Ships[n].ship_name);
2264                                 break;
2265
2266                         case SRC_WING_ORDER:
2267                                 sprintf(text, "wing \"%s\"", Wings[n].name);
2268                                 break;
2269
2270                         default:  // very bad.  Someone added an sexp somewhere and didn't change this.
2271                                 Error(LOCATION, "\"%s\" referenced by an unknown initial orders source!  "
2272                                         "Run for the hills and let Hoffoss know right now!", name);
2273                 }
2274
2275                 sprintf(msg, "%s is referenced by the initial orders of %s (possibly \n"
2276                         "more initial orders).  Do you want to delete it anyway?\n\n"
2277                         "(click Cancel to go to the reference)", type_name, text);
2278
2279                 r = orders_reference_handler(r, msg);
2280                 if (r == 1) {
2281                         if (obj >= 0)
2282                                 unmark_object(obj);
2283
2284                         return 1;
2285                 }
2286
2287                 if (r == 2) {
2288                         delete_flag = 1;
2289                         return 2;
2290                 }
2291         }
2292
2293         if ((type != REF_TYPE_SHIP) && (type != REF_TYPE_WING))
2294                 return 0;
2295
2296         for (n=0; n<Num_reinforcements; n++)
2297                 if (!stricmp(name, Reinforcements[n].name))
2298                         break;
2299
2300         if (n < Num_reinforcements) {
2301                 sprintf(msg, "Ship \"%s\" is a reinforcement unit.\n"
2302                         "Do you want to delete it anyway?", name);
2303
2304                 r = Fred_main_wnd->MessageBox(msg, NULL, MB_YESNO | MB_ICONEXCLAMATION);
2305                 if (r == IDNO) {
2306                         if (obj >= 0)
2307                                 unmark_object(obj);
2308
2309                         return 1;
2310                 }
2311         }
2312
2313         return 0;
2314 }
2315
2316 int orders_reference_handler(int code, char *msg)
2317 {
2318         int r, n;
2319
2320         r = Fred_main_wnd->MessageBox(msg, "Warning", MB_YESNOCANCEL | MB_ICONEXCLAMATION);
2321         if (r == IDNO)
2322                 return 1;
2323
2324         if (r == IDYES)
2325                 return 0;
2326
2327         ShipGoalsDlg dlg_goals;
2328
2329         n = code & SRC_DATA_MASK;
2330         switch (code & SRC_MASK) {
2331                 case SRC_SHIP_ORDER:
2332                         unmark_all();
2333                         mark_object(Ships[n].objnum);
2334
2335                         dlg_goals.self_ship = n;
2336                         dlg_goals.DoModal();
2337                         if (!query_initial_orders_empty(Ai_info[Ships[n].ai_index].goals))
2338                                 if ((Ships[n].wingnum >= 0) && (query_initial_orders_conflict(Ships[n].wingnum)))
2339                                         Fred_main_wnd->MessageBox("This ship's wing also has initial orders", "Possible conflict");
2340
2341                         break;
2342
2343                 case SRC_WING_ORDER:
2344                         unmark_all();
2345                         mark_wing(n);
2346
2347                         dlg_goals.self_wing = n;
2348                         dlg_goals.DoModal();
2349                         if (query_initial_orders_conflict(n))
2350                                 Fred_main_wnd->MessageBox("One or more ships of this wing also has initial orders", "Possible conflict");
2351
2352                         break;
2353
2354                 default:  // very bad.  Someone added an sexp somewhere and didn't change this.
2355                         Error(LOCATION, "Unknown initial order reference source");
2356         }
2357
2358         delete_flag = 1;
2359         return 2;
2360 }
2361
2362 int sexp_reference_handler(int node, int code, char *msg)
2363 {
2364         int r;
2365
2366         r = Fred_main_wnd->MessageBox(msg, "Warning", MB_YESNOCANCEL | MB_ICONEXCLAMATION);
2367         if (r == IDNO)
2368                 return 1;
2369
2370         if (r == IDYES)
2371                 return 0;
2372
2373         switch (code & SRC_MASK) {
2374                 case SRC_SHIP_ARRIVAL:
2375                 case SRC_SHIP_DEPARTURE:
2376                         if (!Ship_editor_dialog.GetSafeHwnd())
2377                                 Ship_editor_dialog.Create();
2378
2379                         Ship_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
2380                                 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2381                         Ship_editor_dialog.ShowWindow(SW_RESTORE);
2382
2383                         Ship_editor_dialog.select_sexp_node = node;
2384                         unmark_all();
2385                         mark_object(Ships[code & SRC_DATA_MASK].objnum);
2386                         break;
2387
2388                 case SRC_WING_ARRIVAL:
2389                 case SRC_WING_DEPARTURE:
2390                         if (!Wing_editor_dialog.GetSafeHwnd())
2391                                 Wing_editor_dialog.Create();
2392
2393                         Wing_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
2394                                 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2395                         Wing_editor_dialog.ShowWindow(SW_RESTORE);
2396
2397                         Wing_editor_dialog.select_sexp_node = node;
2398                         unmark_all();
2399                         mark_wing(code & SRC_DATA_MASK);
2400                         break;
2401
2402                 case SRC_EVENT:
2403                         if (Message_editor_dlg) {
2404                                 Fred_main_wnd->MessageBox("You must close the message editor before the event editor can be opened");
2405                                 break;
2406                         }
2407
2408                         if (!Event_editor_dlg) {
2409                                 Event_editor_dlg = new event_editor;
2410                                 Event_editor_dlg->select_sexp_node = node;
2411                                 Event_editor_dlg->Create(event_editor::IDD);
2412                         }
2413
2414                         Event_editor_dlg->SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2415                         Event_editor_dlg->ShowWindow(SW_RESTORE);
2416                         break;
2417
2418                 case SRC_MISSION_GOAL: {
2419                         CMissionGoalsDlg dlg;
2420
2421                         dlg.select_sexp_node = node;
2422                         dlg.DoModal();
2423                         break;
2424                 }
2425
2426                 case SRC_DEBRIEFING: {
2427                         debriefing_editor_dlg dlg;
2428
2429                         dlg.select_sexp_node = node;
2430                         dlg.DoModal();
2431                         break;
2432                 }
2433
2434                 case SRC_BRIEFING: {
2435                         if (!Briefing_dialog) {
2436                                 Briefing_dialog = new briefing_editor_dlg;
2437                                 Briefing_dialog->create();
2438                         }
2439
2440                         Briefing_dialog->SetWindowPos(&Briefing_dialog->wndTop, 0, 0, 0, 0,
2441                                 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2442                         Briefing_dialog->ShowWindow(SW_RESTORE);
2443                         Briefing_dialog->focus_sexp(node);
2444                         break;
2445                 }
2446
2447                 default:  // very bad.  Someone added an sexp somewhere and didn't change this.
2448                         Error(LOCATION, "Unknown sexp reference source");
2449         }
2450
2451         delete_flag = 1;
2452         return 2;
2453 }
2454
2455 char *object_name(int obj)
2456 {
2457         static char text[80];
2458         int i;
2459
2460         if (!query_valid_object(obj))
2461                 return "*none*";
2462
2463         i = Objects[obj].instance;
2464         switch (Objects[obj].type) {
2465                 case OBJ_SHIP:
2466                 case OBJ_START:
2467                         return Ships[i].ship_name;
2468
2469                 case OBJ_WAYPOINT:
2470                         sprintf(text, "%s:%d", Waypoint_lists[i / 65536].name, (i & 0xffff) + 1);
2471                         return text;
2472
2473                 case OBJ_POINT:
2474                         return "Briefing icon";
2475         }
2476
2477         return "*unknown*";
2478 }
2479
2480 char *get_order_name(int order)
2481 {
2482         int i;
2483
2484         if (order == AI_GOAL_NONE)  // special case
2485                 return "None";
2486
2487         for (i=0; i<Ai_goal_list_size; i++)
2488                 if (Ai_goal_list[i].def & order)
2489                         return Ai_goal_list[i].name;
2490
2491         return "???";
2492 }
2493
2494 void object_moved(object *ptr, int mark)
2495 {
2496         int o2, sh1, sh2;
2497
2498         if (ptr->type == OBJ_WAYPOINT)
2499                 Waypoint_lists[ptr->instance / 65536].waypoints[ptr->instance & 0xffff] = ptr->pos;
2500
2501         if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) {  // do we have a ship?
2502                 sh1 = get_ship_from_obj(ptr);
2503                 o2 = Ai_info[Ships[sh1].ai_index].dock_objnum;
2504                 if (o2 >= 0) {  // is it docked with something?
2505                         sh2 = get_ship_from_obj(o2);
2506                         if (mark || !(Objects[o2].flags & OF_MARKED) || (sh1 < sh2))
2507                                 dock_orient_and_approach(&Objects[o2], ptr, DOA_DOCK_STAY);
2508                 }
2509         }
2510 }
2511
2512 // determine if all the ships in a given wing are all marked or not.
2513 int query_whole_wing_marked(int wing)
2514 {
2515         int count = 0;
2516         object *ptr;
2517
2518         if (!Wings[wing].wave_count)
2519                 return 0;
2520
2521         ptr = GET_FIRST(&obj_used_list);
2522         while (ptr != END_OF_LIST(&obj_used_list)) {
2523                 if (ptr->flags & OF_MARKED)
2524                         if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START))
2525                                 if (Ships[get_ship_from_obj(ptr)].wingnum == wing)
2526                                         count++;
2527
2528                 ptr = GET_NEXT(ptr);
2529         }
2530
2531         if (count == Wings[wing].wave_count)
2532                 return 1;
2533
2534         return 0;
2535 }
2536
2537 void generate_weaponry_usage_list(int *arr, int wing)
2538 {
2539         int i, j;
2540         ship_weapon *swp;
2541
2542         if (wing < 0)
2543                 return;
2544
2545         i = Wings[wing].wave_count;
2546         while (i--) {
2547                 swp = &Ships[Wings[wing].ship_index[i]].weapons;
2548                 j = swp->num_primary_banks;
2549                 while (j--)
2550                         arr[swp->primary_bank_weapons[j]]++;
2551
2552                 j = swp->num_secondary_banks;
2553                 while (j--)
2554                         arr[swp->secondary_bank_weapons[j]] += int(ceil(swp->secondary_bank_ammo[j] * swp->secondary_bank_capacity[j] / 100 / Weapon_info[swp->secondary_bank_weapons[j]].cargo_size));
2555         }
2556 }
2557
2558 void generate_weaponry_usage_list(int *arr)
2559 {
2560         int i;
2561
2562         for (i=0; i<MAX_WEAPON_TYPES; i++)
2563                 arr[i] = 0;
2564
2565         generate_weaponry_usage_list(arr, wing_name_lookup("alpha"));
2566         generate_weaponry_usage_list(arr, wing_name_lookup("beta"));
2567         generate_weaponry_usage_list(arr, wing_name_lookup("gamma"));
2568 }
2569
2570 // function which adds all current ships in the Fred mission to the passed in combo box.  useful for
2571 // building up ship lists for arrival/departure targets
2572 void management_add_ships_to_combo( CComboBox *box, int flags )
2573 {
2574         object *objp;
2575         int id, i;
2576
2577         box->ResetContent();
2578         
2579         // add the "special" targets, i.e. any friendly, any hostile, etc.
2580         if ( flags & SHIPS_2_COMBO_SPECIAL ) {
2581                 for (i=0; i<MAX_SPECIAL_ARRIVAL_ANCHORS; i++) {
2582                         id = box->AddString(Special_arrival_anchor_names[i]);
2583                         box->SetItemData(id, SPECIAL_ARRIVAL_ANCHORS_OFFSET + i);
2584                 }
2585         }
2586
2587         // either add all ships to the list, or only add ships with docking bays.
2588         if ( flags & SHIPS_2_COMBO_ALL_SHIPS ) {
2589                 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
2590                         if ( ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && !(objp->flags & OF_MARKED) ) {
2591                                 id = box->AddString(Ships[get_ship_from_obj(objp)].ship_name);
2592                                 box->SetItemData(id, get_ship_from_obj(objp));
2593                         }
2594                 }
2595         } else if ( flags & SHIPS_2_COMBO_DOCKING_BAY_ONLY ) {
2596                 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
2597                         if ( ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && !(objp->flags & OF_MARKED) ) {
2598                                 polymodel *pm;
2599
2600                                 // determine if this ship has a docking bay
2601                                 pm = model_get( Ships[objp->instance].modelnum );
2602                                 Assert( pm );
2603                                 if ( pm->ship_bay && (pm->ship_bay->num_paths > 0) ) {
2604                                         id = box->AddString(Ships[get_ship_from_obj(objp)].ship_name);
2605                                         box->SetItemData(id, get_ship_from_obj(objp));
2606                                 }
2607                         }
2608                 }
2609         }
2610 }
2611
2612 char *reg_read_string( char *section, char *name, char *default_value )
2613 {
2614         HKEY hKey = NULL;
2615         DWORD dwType, dwLen;
2616         char keyname[1024];
2617         static char tmp_string_data[1024];
2618         LONG lResult;
2619
2620         strcpy( keyname, section );
2621
2622         lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,                                                     // Where it is
2623                                                                                          keyname,                                                               // name of key
2624                                                                                          NULL,                                                                  // DWORD reserved
2625                                                                                          KEY_QUERY_VALUE,                                               // Allows all changes
2626                                                                                          &hKey );                                                               // Location to store key
2627
2628         if ( lResult != ERROR_SUCCESS ) {
2629                 mprintf(( "Error opening registry key '%s'\n", keyname ));
2630                 goto Cleanup;
2631         }
2632
2633         if ( !name )     {
2634                 mprintf(( "No variable name passed\n" ));
2635                 goto Cleanup;
2636         }
2637
2638         dwLen = 1024;
2639         lResult = RegQueryValueEx( hKey,                                                                        // Handle to key
2640                                                                          name,                                                                                  // The values name
2641                                                                          NULL,                                                                                  // DWORD reserved
2642                                  &dwType,                                                                               // What kind it is
2643                                                                          (ubyte *)&tmp_string_data,                                             // value to set
2644                                                                          &dwLen );                                                              // How many bytes to set
2645                                                                                                                                                                 
2646         if ( lResult != ERROR_SUCCESS ) {
2647                 mprintf(( "Error reading registry key '%s'\n", name ));
2648                 goto Cleanup;
2649         }
2650
2651         default_value = tmp_string_data;
2652
2653 Cleanup:
2654         if ( hKey )
2655                 RegCloseKey(hKey);
2656
2657         return default_value;
2658 }
2659