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