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