2 * $Logfile: /Freespace2/code/Fred2/Management.cpp $
7 * This file handles the management of Objects, Ships, Wings, etc. Basically
8 * all the little structures we have that usually inter-relate that need to
9 * be handled in a standard way, and thus should be handled by a single
13 * Revision 1.2 2002/05/07 03:16:44 theoddone33
14 * The Great Newline Fix
16 * Revision 1.1.1.1 2002/05/03 03:28:09 root
20 * 29 10/13/99 9:22a Daveb
21 * Fixed Fred jumpnode placing bug. Fixed 1024 glide tiled texture problem
22 * related to movies. Fixed launcher spawning from PXO screen.
24 * 28 9/08/99 10:01p Dave
25 * Make sure game won't run in a drive's root directory. Make sure
26 * standalone routes suqad war messages properly to the host.
28 * 27 7/23/99 2:02p Jamesa
29 * Don't require gamepalettes
31 * 26 7/15/99 3:07p Dave
32 * 32 bit detection support. Mouse coord commandline.
34 * 25 7/02/99 4:30p Dave
35 * Much more sophisticated lightning support.
37 * 24 5/20/99 6:59p Dave
38 * Added alternate type names for ships. Changed swarm missile table
41 * 23 4/26/99 8:47p Dave
42 * Made all pof related nebula stuff customizable through Fred.
44 * 22 4/16/99 2:34p Andsager
45 * Second pass on debris fields
47 * 21 4/15/99 5:00p Andsager
48 * Frist pass on Debris field
50 * 20 4/07/99 6:21p Dave
51 * Fred and Freespace support for multiple background bitmaps and suns.
52 * Fixed link errors on all subprojects. Moved encrypt_init() to
53 * cfile_init() and lcl_init(), since its safe to call twice.
55 * 19 3/31/99 9:50a Andsager
56 * Interface for generalization of asteroid field (debris field)
58 * 18 3/30/99 5:40p Dave
59 * Fixed reinforcements for TvT in multiplayer.
61 * 17 3/24/99 4:05p Dave
62 * Put in support for assigning the player to a specific squadron with a
63 * specific logo. Preliminary work for doing pos/orient checksumming in
64 * multiplayer to reduce bandwidth.
66 * 16 3/20/99 5:09p Dave
67 * Fixed release build fred warnings and unhandled exception.
69 * 15 2/23/99 7:03p Dave
70 * Rewrote a horribly mangled and evil team loadout dialog. Bugs gone.
72 * 14 2/17/99 2:11p Dave
73 * First full run of squad war. All freespace and tracker side stuff
76 * 13 2/10/99 11:11a Johnson
77 * Think I fixed the problem where the Neb2_awacs value was improperly
78 * being checked for in Fred.
80 * 12 2/07/99 8:51p Andsager
81 * Add inner bound to asteroid field. Inner bound tries to stay astroid
82 * free. Wrap when within and don't throw at ships inside.
84 * 11 1/25/99 5:03a Dave
85 * First run of stealth, AWACS and TAG missile support. New mission type
88 * 10 1/19/99 3:57p Andsager
89 * Round 2 of variables
91 * 9 12/18/98 1:14a Dave
92 * Rough 1024x768 support for Direct3D. Proper detection and usage through
95 * 8 11/06/98 10:15a Dave
97 * 7 10/29/98 9:22p Dave
98 * Removed minor bug concering externalization of campaign files.
100 * 6 10/29/98 6:49p Dave
101 * Finished up Fred support for externalizing mission and campaign files.
103 * 5 10/29/98 10:41a Dave
104 * Change the way cfile initializes exe directory.
106 * 4 10/23/98 5:07p Dave
107 * Put in beginnings of localization/externalization functionality.
109 * 3 10/22/98 6:13p Dave
110 * Added registry and localization support.
112 * 2 10/07/98 6:28p Dave
113 * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
114 * Fred. Globalized mission and campaign file extensions. Removed Silent
115 * Threat specific code.
117 * 1 10/07/98 3:02p Dave
119 * 1 10/07/98 3:00p Dave
121 * 227 6/17/98 2:05p Hoffoss
122 * Fixed bug: Shield system for team or ship type flags new reset when
123 * mission gets cleared out (new or load mission).
125 * 226 5/20/98 10:19p Allender
126 * make Fred work with NDEBUG build
128 * 225 5/17/98 1:48p Allender
129 * made number of respawns for new missions 3
131 * 224 5/01/98 12:34p John
132 * Added code to force FreeSpace to run in the same dir as exe and made
133 * all the parse error messages a little nicer.
135 * 223 4/30/98 8:23p John
136 * Fixed some bugs with Fred caused by my new cfile code.
138 * 222 4/27/98 4:07p Allender
139 * make orders_accepted not be assigned to -1 when creating new ships.
140 * Don't set the use_default_orders flag when orders are -1
142 * 221 4/25/98 6:43p Allender
143 * reset model data when initializeing new mission
145 * 220 4/17/98 1:41p Allender
146 * took out function calls in NDEBUG mode
148 * 219 4/13/98 10:59a Hoffoss
149 * Made "new mission" clear the cmd brief structure.
151 * 218 4/13/98 10:11a John
152 * Made timer functions thread safe. Made timer_init be called in all
155 * 217 4/06/98 5:37p Hoffoss
156 * Added sexp tree support to briefings in Fred.
158 * 216 4/06/98 12:55p John
159 * Upped the gamma for Fred.
161 * 215 4/03/98 11:34a John
162 * Fixed the stuff I broke in Fred from the new breifing
164 * 214 4/02/98 3:00p Johnson
165 * Fixed a bug a release build turned up.
167 * 213 3/24/98 1:36p Hoffoss
168 * Moved call to load_filter_info() to after cfile_init(), as it is
169 * dependent on that being set up.
171 * 212 3/24/98 12:42p Allender
172 * fixed a couple of minor problems with arrival targets
174 * 211 3/21/98 7:36p Lawrance
175 * Move jump nodes to own lib.
177 * 210 3/17/98 4:16p Allender
178 * minor changes to the kamikaze flag
180 * 209 3/17/98 11:55a Johnson
181 * Fixed bug where jump nodes wheren't being cleared on a new mission.
183 * 208 3/10/98 4:26p Hoffoss
184 * Changed jump node structure to include a name. Position is now taken
185 * from the object (each jump node has an associated object now).
187 * 207 3/09/98 4:30p Allender
188 * multiplayer secondary weapon changes. red-alert and cargo-known-delay
189 * sexpressions. Add time cargo revealed to ship structure
191 * 206 3/09/98 10:56a Hoffoss
192 * Added jump node objects to Fred.
194 * 205 3/06/98 5:10p Allender
195 * made time to: field in extended targetbox use support time to dock code
196 * for all docking shpis. Only display for waypoints and docking (not
197 * undocking). Small fixups to message menu -- not allowing depart when
198 * disabled. Depart is now by default ignored for all non-small ships
200 * 204 3/05/98 3:59p Hoffoss
201 * Added a bunch of new command brief stuff, and asteroid initialization
204 * 203 2/26/98 4:59p Allender
205 * groundwork for team vs team briefings. Moved weaponry pool into the
206 * Team_data structure. Added team field into the p_info structure.
207 * Allow for mutliple structures in the briefing code.
209 * 202 2/17/98 12:07p Hoffoss
210 * Changed over to using SF_CARGO_REVEALED in fred.
212 * 201 2/17/98 10:12a Hoffoss
213 * Fixed bug with sprintf() in reference_handler(). Forgot the first
214 * argument! :) Amazing it never crashed before.
216 * 200 2/13/98 11:45a Hoffoss
217 * Made all new ships created in Fred default to 33% initial speed.
226 #include "fredview.h"
227 #include "fredrender.h"
231 #include "linklist.h"
232 #include "missionparse.h"
233 #include "missionmessage.h"
234 #include "missiongoals.h"
235 #include "missionbriefcommon.h"
236 #include "management.h"
245 #include "starfield.h"
248 #include "missioncampaign.h"
250 #include "messageeditordlg.h"
251 #include "eventeditor.h"
252 #include "missiongoalsdlg.h"
253 #include "shieldsysdlg.h"
254 #include "eventmusic.h"
255 #include "debriefingeditordlg.h"
257 #include "asteroid.h"
258 #include "hudsquadmsg.h"
259 #include "jumpnode.h"
261 #include "localize.h"
262 #include "osregistry.h"
266 #include "neblightning.h"
270 #define UNKNOWN_USER "Unknown"
274 int cur_object_index = -1;
276 int cur_model_index = 0;
277 int cur_waypoint = -1;
278 int cur_waypoint_list = -1;
280 int bypass_update = 0;
281 int Default_player_model = 0;
286 char Fred_exe_dir[512] = "";
288 char Fred_alt_names[MAX_SHIPS][NAME_LENGTH+1];
290 // object numbers for ships in a wing.
291 int wing_objects[MAX_WINGS][MAX_SHIPS_PER_WING];
293 char *Docking_bay_list[MAX_DOCKS];
295 CCriticalSection CS_cur_object_index;
297 ai_goal_list Ai_goal_list[] = {
298 "Waypoints", AI_GOAL_WAYPOINTS,
299 "Waypoints once", AI_GOAL_WAYPOINTS_ONCE,
300 "Warp", AI_GOAL_WARP,
301 "Destroy subsystem", AI_GOAL_DESTROY_SUBSYSTEM,
302 "Attack", AI_GOAL_CHASE | AI_GOAL_CHASE_WING,
303 "Dock", AI_GOAL_DOCK,
304 "Undock", AI_GOAL_UNDOCK,
305 "Guard", AI_GOAL_GUARD | AI_GOAL_GUARD_WING,
306 "Attack any ship", AI_GOAL_CHASE_ANY,
307 "Disable ship", AI_GOAL_DISABLE_SHIP,
308 "Disarm ship", AI_GOAL_DISARM_SHIP,
309 "Evade ship", AI_GOAL_EVADE_SHIP,
310 "Ignore ship", AI_GOAL_IGNORE,
311 "Stay near ship", AI_GOAL_STAY_NEAR_SHIP,
312 "Keep safe distance", AI_GOAL_KEEP_SAFE_DISTANCE,
313 "Stay still", AI_GOAL_STAY_STILL,
314 "Play dead", AI_GOAL_PLAY_DEAD,
317 int Ai_goal_list_size = sizeof(Ai_goal_list) / sizeof(ai_goal_list);
319 // internal function prototypes
320 void set_cur_indices(int obj);
321 int common_object_delete(int obj);
322 int create_waypoint(vector *pos, int list);
323 int create_ship(matrix *orient, vector *pos, int ship_type);
324 int query_ship_name_duplicate(int ship);
325 char *reg_read_string( char *section, char *name, char *default_value );
327 extern int Nmodel_num;
328 extern int Nmodel_bitmap;
330 void string_copy(char *dest, CString &src, int max_len, int modify)
335 if (strcmp(src, dest))
342 strncpy(dest, src, len);
346 // converts a multiline string (one with newlines in it) into a windows format multiline
347 // string (newlines changed to '\r\n').
348 CString convert_multiline_string(char *src)
355 while ((ptr = strchr(src, '\n'))!=NULL) {
358 strncpy(buf, src, 250);
366 strncpy(buf, src, i);
381 // Converts a windows format multiline CString back into a normal multiline string.
382 void deconvert_multiline_string(char *buf, CString &str, int max_len)
391 while ((i = str.Find("\r\n")) >= 0) {
399 str2 = str.Mid(i + 2);
410 // removed 10/29/98 by DB
411 // this was generating an extra newline - why?
412 //if (*(ptr - 1) != '\n')
417 // medal_stuff Medals[NUM_MEDALS];
419 void parse_medal_tbl()
421 int rval, num_medals;
423 if ((rval = setjmp(parse_abort)) != 0) {
424 Error(LOCATION, "Error parsing '%s'\r\nError code = %i.\r\n", "medals.tbl", rval);
430 read_file_text("medals.tbl");
434 // parse in all the rank names
436 required_string("#Medals");
437 while ( required_string_either("#End", "$Name:") ) {
438 Assert ( num_medals < NUM_MEDALS);
439 required_string("$Name:");
440 stuff_string( Medals[num_medals].name, F_NAME, NULL );
441 required_string("$Bitmap:");
442 stuff_string( Medals[num_medals].bitmap, F_NAME, NULL );
443 required_string("$Num mods:");
444 stuff_int( &Medals[num_medals].num_versions);
446 // some medals are based on kill counts. When string +Num Kills: is present, we know that
447 // this medal is a badge and should be treated specially
448 Medals[num_medals].kills_needed = 0;
450 if ( optional_string("+Num Kills:") ) {
451 char buf[MULTITEXT_LENGTH + 1];
453 stuff_int( &Medals[num_medals].kills_needed );
455 required_string("$Wavefile 1:");
456 stuff_string(buf, F_NAME, NULL, MAX_FILENAME_LEN);
458 required_string("$Wavefile 2:");
459 stuff_string(buf, F_NAME, NULL, MAX_FILENAME_LEN);
461 required_string("$Promotion Text:");
462 stuff_string(buf, F_MULTITEXT, NULL);
468 required_string("#End");
470 // close localization
478 char palette_filename[1024];
480 if (!vm_init(24*1024*1024)) {
481 MessageBox( NULL, "Not enough memory to run Fred.\r\nTry closing down some other applications.\r\n", "Not Enough Memory", MB_OK );
485 srand( (unsigned) time(NULL) );
486 init_pending_messages();
488 // initialize registry stuff
489 os_init_registry_stuff(Osreg_company_name, Osreg_app_name, NULL);
493 Assert(strlen(Fred_exe_dir) > 0);
496 if(cfile_init(Fred_exe_dir)){
500 // initialize localization module. Make sure this is done AFTER initialzing OS.
501 // NOTE : Fred should ALWAYS run in Enlish. Otherwise it might swap in another language
502 // when saving - which would cause inconsistencies when externalizing to tstrings.tbl via Exstr
503 // trust me on this :)
504 lcl_init(LCL_ENGLISH);
510 gr_init(GR_640, GR_SOFTWARE, 8);
513 sprintf(palette_filename, "gamepalette%d-%02d", 1, 1);
514 mprintf(("Loading palette %s\n", palette_filename));
515 palette_load_table(palette_filename);
517 Fred_font = gr_init_font("font01.vf");
521 mission_brief_common_init();
523 model_free_all(); // Free all existing models
526 parse_medal_tbl(); // get medal names for sexpression usage
530 // initialize and activate external string hash table
531 // make sure to do here so that we don't parse the table files into the hash table - waste of space
535 create_new_mission();
536 neb2_init(); // fullneb stuff
539 event_music_parse_musictbl();
541 Show_waypoints = TRUE;
542 Campaign.filename[0] = 0; // indicate initialized state
551 g3_set_view_matrix(&eye_pos, &eye_orient, 0.5f);
553 for (i=0; i<Num_ship_types; i++)
554 if (Ship_info[i].flags & SIF_DEFAULT_PLAYER_SHIP) {
555 Default_player_model = cur_model_index = i;
559 Id_select_type_start = Num_ship_types + 2;
560 Id_select_type_jump_node = Num_ship_types + 1;
561 Id_select_type_waypoint = Num_ship_types;
562 Fred_main_wnd -> init_tools();
565 void set_physics_controls()
567 physics_init(&view_physics);
568 view_physics.max_vel.x *= physics_speed / 3.0f;
569 view_physics.max_vel.y *= physics_speed / 3.0f;
570 view_physics.max_vel.z *= physics_speed / 3.0f;
571 view_physics.max_rear_vel *= physics_speed / 3.0f;
573 view_physics.max_rotvel.x *= physics_rot / 30.0f;
574 view_physics.max_rotvel.y *= physics_rot / 30.0f;
575 view_physics.max_rotvel.z *= physics_rot / 30.0f;
576 view_physics.flags |= PF_ACCELERATES | PF_SLIDE_ENABLED;
577 theApp.write_ini_file(1);
580 int create_object_on_grid(int list)
586 g3_point_to_vec_delayed(&dir, marking_box.x2, marking_box.y2);
588 rval = fvi_ray_plane(&pos, &The_grid->center, &The_grid->gmatrix.uvec, &view_pos, &dir, 0.0f);
592 obj = create_object(&pos, list);
595 FREDDoc_ptr->autosave("object create");
597 } else if (obj == -1)
598 Fred_main_wnd->MessageBox("Maximum ship limit reached. Can't add any more ships.");
604 void fix_ship_name(int ship)
609 sprintf(Ships[ship].ship_name, "U.R.A. Moron %d", i++);
610 } while (query_ship_name_duplicate(ship));
613 int create_ship(matrix *orient, vector *pos, int ship_type)
615 int obj, ship, z1, z2;
618 obj = ship_create(orient, pos, ship_type);
622 Objects[obj].phys_info.speed = 33.0f;
624 ship = Objects[obj].instance;
625 sip = &Ship_info[Ships[ship].ship_info_index];
627 if (query_ship_name_duplicate(ship))
630 z1 = Shield_sys_teams[Ships[ship].team];
631 z2 = Shield_sys_types[ship_type];
632 if (((z1 == 1) && z2) || (z2 == 1))
633 Objects[obj].flags |= OF_NO_SHIELDS;
635 z1 = Ship_info[Ships[ship].ship_info_index].species;
636 if (z1 == SPECIES_SHIVAN) {
637 Ships[ship].team = TEAM_HOSTILE;
638 Ships[ship].flags &= ~SF_CARGO_REVEALED;
641 Ships[ship].team = TEAM_FRIENDLY;
642 Ships[ship].flags |= SF_CARGO_REVEALED;
645 if ( Ships[ship].team == TEAM_FRIENDLY ) {
647 // if this ship is not a small ship, then make the orders be the default orders without
649 if ( !(sip->flags & SIF_SMALL_SHIP) ) {
650 Ships[ship].orders_accepted = ship_get_default_orders_accepted( sip );
651 Ships[ship].orders_accepted &= ~DEPART_ITEM;
655 Ships[ship].orders_accepted = 0;
658 Ai_info[Ships[ship].ai_index].kamikaze_damage = min(1000.0f, 200.0f + (sip->initial_hull_strength / 4.0f));
663 int query_ship_name_duplicate(int ship)
667 for (i=0; i<MAX_SHIPS; i++)
668 if ((i != ship) && (Ships[i].objnum != -1))
669 if (!stricmp(Ships[i].ship_name, Ships[ship].ship_name))
675 void copy_bits(int *dest, int src, int mask)
681 int dup_object(object *objp)
683 int i, j, n, inst, obj = -1;
684 ai_info *aip1, *aip2;
685 object *objp1, *objp2;
686 ship_subsys *subp1, *subp2;
694 inst = objp->instance;
695 if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) {
696 obj = create_ship(&objp->orient, &objp->pos, Ships[inst].ship_info_index);
700 n = Objects[obj].instance;
701 Ships[n].team = Ships[inst].team;
702 Ships[n].arrival_cue = dup_sexp_chain(Ships[inst].arrival_cue);
703 Ships[n].departure_cue = dup_sexp_chain(Ships[inst].departure_cue);
704 Ships[n].cargo1 = Ships[inst].cargo1;
705 Ships[n].arrival_location = Ships[inst].arrival_location;
706 Ships[n].departure_location = Ships[inst].departure_location;
707 Ships[n].arrival_delay = Ships[inst].arrival_delay;
708 Ships[n].departure_delay = Ships[inst].departure_delay;
709 Ships[n].weapons = Ships[inst].weapons;
710 Ships[n].hotkey = Ships[inst].hotkey;
712 aip1 = &Ai_info[Ships[n].ai_index];
713 aip2 = &Ai_info[Ships[inst].ai_index];
714 aip1->behavior = aip2->behavior;
715 aip1->ai_class = aip2->ai_class;
716 for (i=0; i<MAX_AI_GOALS; i++)
717 aip1->goals[i] = aip2->goals[i];
719 if ( aip2->ai_flags & AIF_KAMIKAZE )
720 aip1->ai_flags |= AIF_KAMIKAZE;
721 if ( aip2->ai_flags & AIF_NO_DYNAMIC )
722 aip2->ai_flags |= AIF_NO_DYNAMIC;
724 aip1->kamikaze_damage = aip2->kamikaze_damage;
726 objp1 = &Objects[obj];
727 objp2 = &Objects[Ships[inst].objnum];
728 objp1->phys_info.speed = objp2->phys_info.speed;
729 objp1->phys_info.fspeed = objp2->phys_info.fspeed;
730 objp1->hull_strength = objp2->hull_strength;
731 objp1->shields[0] = objp2->shields[0];
733 subp1 = GET_FIRST(&Ships[n].subsys_list);
734 subp2 = GET_FIRST(&Ships[inst].subsys_list);
735 while (subp1 != END_OF_LIST(&Ships[n].subsys_list)) {
736 Assert(subp2 != END_OF_LIST(&Ships[inst].subsys_list));
737 subp1 -> current_hits = subp2 -> current_hits;
738 subp1 = GET_NEXT(subp1);
739 subp2 = GET_NEXT(subp2);
742 for (i=0; i<Num_reinforcements; i++)
743 if (!stricmp(Reinforcements[i].name, Ships[inst].ship_name)) {
744 if (Num_reinforcements < MAX_REINFORCEMENTS) {
745 j = Num_reinforcements++;
746 strcpy(Reinforcements[j].name, Ships[n].ship_name);
747 Reinforcements[j].type = Reinforcements[i].type;
748 Reinforcements[j].uses = Reinforcements[i].uses;
754 } else if (objp->type == OBJ_WAYPOINT) {
755 obj = create_waypoint(&objp->pos, list);
756 list = Objects[obj].instance;
762 Objects[obj].pos = objp->pos;
763 Objects[obj].orient = objp->orient;
764 Objects[obj].flags |= OF_TEMP_MARKED;
768 int create_object(vector *pos, int list)
772 if (cur_model_index == Id_select_type_waypoint)
773 obj = create_waypoint(pos, list);
775 else if (cur_model_index == Id_select_type_start) {
776 if (Player_starts >= MAX_PLAYERS) {
777 Fred_main_wnd->MessageBox("Unable to create new player start point.\n"
778 "You have reached the maximum limit.", NULL, MB_OK | MB_ICONEXCLAMATION);
781 } else if (The_mission.game_type & MISSION_TYPE_SINGLE) {
782 Fred_main_wnd->MessageBox("You can't have more than one player start in\n"
783 "single player missions.\n", NULL, MB_OK | MB_ICONEXCLAMATION);
786 } else if (The_mission.game_type & MISSION_TYPE_TRAINING) {
787 Fred_main_wnd->MessageBox("You can't have more than one player start in\n"
788 "a training missions.\n", NULL, MB_OK | MB_ICONEXCLAMATION);
792 obj = create_player(Player_starts, pos, NULL, Default_player_model);
794 } else if (cur_model_index == Id_select_type_jump_node) {
795 if (Num_jump_nodes >= MAX_JUMP_NODES) {
796 Fred_main_wnd->MessageBox("Unable to create more jump nodes. You have reached the limit.", NULL, MB_OK | MB_ICONEXCLAMATION);
800 obj = jumpnode_create(pos);
803 } else if(Ship_info[cur_model_index].flags & SIF_NO_FRED){
805 } else { // creating a ship
806 obj = create_ship(NULL, pos, cur_model_index);
810 n = Objects[obj].instance;
811 Ships[n].arrival_cue = alloc_sexp("true", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1);
812 Ships[n].departure_cue = alloc_sexp("false", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1);
819 obj_merge_created_list();
825 int create_player(int num, vector *pos, matrix *orient, int type, int init)
830 type = Default_player_model;
834 Assert(Player_starts < MAX_PLAYERS);
836 obj = create_ship(orient, pos, type);
837 Objects[obj].type = OBJ_START;
839 // be sure arrival/departure cues are set
840 Ships[Objects[obj].instance].arrival_cue = Locked_sexp_true;
841 Ships[Objects[obj].instance].departure_cue = Locked_sexp_false;
842 obj_merge_created_list();
847 int query_waypoint_path_name_duplicate(int list)
851 for (i=0; i<Num_waypoint_lists; i++)
853 if (!stricmp(Waypoint_lists[i].name, Waypoint_lists[list].name))
859 void get_unique_waypoint_path_name(int list)
863 sprintf(Waypoint_lists[list].name, "Waypoint path %d", list + 1);
864 while (query_waypoint_path_name_duplicate(list)) {
865 sprintf(Waypoint_lists[list].name, "Waypoint path U%d", i++);
869 int create_waypoint(vector *pos, int list)
871 int i, obj, index = 0;
874 if (list == -1) { // find a new list to start.
875 for (list=0; list<MAX_WAYPOINT_LISTS; list++){
876 if (!Waypoint_lists[list].count) {
877 get_unique_waypoint_path_name(list);
882 index = (list & 0xffff) + 1;
886 if (list == MAX_WAYPOINT_LISTS) {
887 Fred_main_wnd->MessageBox("Unable to create new waypoint path. You\n"
888 "have reached the maximum limit.", NULL, MB_OK | MB_ICONEXCLAMATION);
892 Assert((list >= 0) && (list < MAX_WAYPOINT_LISTS)); // illegal index or out of lists.
893 if (Waypoint_lists[list].count >= MAX_WAYPOINTS_PER_LIST) {
894 Fred_main_wnd->MessageBox("Unable to create new waypoint. You have\n"
895 "reached the maximum limit on waypoints per list.", NULL, MB_OK | MB_ICONEXCLAMATION);
899 if (Waypoint_lists[list].count > index) {
900 i = Waypoint_lists[list].count;
902 Waypoint_lists[list].waypoints[i] = Waypoint_lists[list].waypoints[i - 1];
903 Waypoint_lists[list].flags[i] = Waypoint_lists[list].flags[i - 1];
908 ptr = GET_FIRST(&obj_used_list);
909 while (ptr != END_OF_LIST(&obj_used_list)) {
910 Assert(ptr->type != OBJ_NONE);
911 if (ptr->type == OBJ_WAYPOINT) {
913 if ((i / 65536 == list) && ((i & 0xffff) >= index)){
921 Waypoint_lists[list].count++;
922 Waypoint_lists[list].flags[index] = 0;
923 Waypoint_lists[list].waypoints[index] = *pos;
924 if (list >= Num_waypoint_lists){
925 Num_waypoint_lists = list + 1;
928 obj = obj_create(OBJ_WAYPOINT, -1, list * 65536 + index, NULL, pos, 0.0f, OF_RENDERS);
933 void create_new_mission()
936 *Mission_filename = 0;
937 FREDDoc_ptr->autosave("nothing");
944 player_start1 = create_player(0, &vmd_zero_vector, &vmd_identity_matrix);
953 // clean up everything we need to before we reset back to defaults.
954 if (Briefing_dialog){
955 Briefing_dialog->reset_editor();
959 mission_event_shutdown();
961 Asteroid_field.num_initial_asteroids = 0; // disable asteroid field by default.
962 Asteroid_field.speed = 0.0f;
963 vm_vec_make(&Asteroid_field.min_bound, -1000.0f, -1000.0f, -1000.0f);
964 vm_vec_make(&Asteroid_field.max_bound, 1000.0f, 1000.0f, 1000.0f);
965 vm_vec_make(&Asteroid_field.inner_min_bound, -500.0f, -500.0f, -500.0f);
966 vm_vec_make(&Asteroid_field.inner_max_bound, 500.0f, 500.0f, 500.0f);
967 Asteroid_field.has_inner_bound = 0;
968 Asteroid_field.field_type = FT_ACTIVE;
969 Asteroid_field.debris_genre = DG_ASTEROID;
970 Asteroid_field.field_debris_type[0] = -1;
971 Asteroid_field.field_debris_type[1] = -1;
972 Asteroid_field.field_debris_type[2] = -1;
974 strcpy(Mission_parse_storm_name, "none");
977 model_free_all(); // Free all existing models
980 Num_ai_dock_names = 0;
983 for (i=0; i<MAX_WINGS; i++){
984 Wings[i].wave_count = 0;
987 for (i=0; i<MAX_WAYPOINT_LISTS; i++){
988 Waypoint_lists[i].count = 0;
991 for (i=0; i<MAX_TEAM_NAMES; i++){
992 Shield_sys_teams[i] = 0;
995 for (i=0; i<MAX_SHIP_TYPES; i++){
996 Shield_sys_types[i] = 0;
999 Num_reinforcements = 0;
1000 set_cur_indices(-1);
1002 str = reg_read_string("SOFTWARE\\Microsoft\\Windows\\CurrentVersion", "RegisteredOwner", NULL);
1004 str = reg_read_string("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "RegisteredOwner", NULL);
1006 str = getenv("USERNAME");
1013 t = CTime::GetCurrentTime();
1014 strcpy(The_mission.name, "Untitled");
1015 strncpy(The_mission.author, str, NAME_LENGTH - 1);
1016 The_mission.author[NAME_LENGTH - 1] = 0;
1017 strcpy(The_mission.created, t.Format("%x at %X"));
1018 strcpy(The_mission.modified, The_mission.created);
1019 strcpy(The_mission.notes, "This is a FRED created mission\n");
1020 strcpy(The_mission.mission_desc, "Put mission description here\n");
1021 strcpy(The_mission.tour_name, "Blah");
1022 The_mission.game_type = MISSION_TYPE_SINGLE;
1023 strcpy(The_mission.pre_briefing_cutscene, "Blah");
1024 strcpy(The_mission.pre_mission_cutscene, "Blah");
1025 strcpy(The_mission.next_mission_success, "Blah");
1026 strcpy(The_mission.next_mission_partial, "Blah");
1027 strcpy(The_mission.next_mission_failure, "Blah");
1028 strcpy(The_mission.squad_name, "");
1029 strcpy(The_mission.squad_filename, "");
1030 The_mission.num_respawns = 3;
1035 // background bitmaps and suns
1037 Num_starfield_bitmaps = 0;
1039 // reset alternate name stuff
1040 for(i=0; i<MAX_SHIPS; i++){
1041 strcpy(Fred_alt_names[i], "");
1044 // set up the default ship types for all teams. For now, this is the same class
1045 // of ships for all teams
1046 for (i=0; i<MAX_TEAMS; i++) {
1048 for ( j = 0; j < MAX_SHIP_TYPES; j++ ) {
1049 if (Ship_info[j].flags & SIF_DEFAULT_PLAYER_SHIP) {
1050 Team_data[i].ship_list[count] = j;
1051 Team_data[i].ship_count[count++] = 5;
1054 Team_data[i].number_choices = count;
1056 for (j=0; j<MAX_WEAPON_TYPES; j++){
1057 if (Weapon_info[j].wi_flags & WIF_PLAYER_ALLOWED){
1058 if(Weapon_info[j].subtype == WP_LASER){
1059 Team_data[i].weaponry_pool[j] = 16;
1061 Team_data[i].weaponry_pool[j] = 500;
1064 Team_data[i].weaponry_pool[j] = 0;
1069 *Mission_text = *Mission_text_raw = EOF_CHAR;
1070 Mission_text[1] = Mission_text_raw[1] = 0;
1072 Num_waypoint_lists = 0;
1073 Num_mission_events = 0;
1077 model_free_all(); // Free all existing models
1084 // alternate ship type names
1085 mission_parse_reset_alt();
1087 strcpy(Cargo_names[0], "Nothing");
1089 set_physics_controls();
1090 Num_starfield_bitmaps = 0;
1092 Mission_palette = 1;
1093 Nebula_pitch = (int) ((float) (rand() & 0x0fff) * 360.0f / 4096.0f);
1094 Nebula_bank = (int) ((float) (rand() & 0x0fff) * 360.0f / 4096.0f);
1095 Nebula_heading = (int) ((float) (rand() & 0x0fff) * 360.0f / 4096.0f);
1097 Neb2_poof_flags = 0;
1098 strcpy(Neb2_texture_name, "");
1099 for(i=0; i<MAX_NEB2_POOFS; i++){
1100 Neb2_poof_flags |= (1<<i);
1104 The_mission.flags &= ~(MISSION_FLAG_FULLNEB);
1105 nebula_init(Nebula_filenames[Nebula_index], Nebula_pitch, Nebula_bank, Nebula_heading);
1107 char palette_filename[1024];
1108 strcpy(palette_filename, "gamepalette1-01");
1109 // sprintf( palette_filename, "gamepalette%d-%02d", 1, Mission_palette+1 );
1110 mprintf(( "Loading palette %s\n", palette_filename ));
1111 palette_load_table(palette_filename);
1113 set_modified(FALSE);
1117 int query_valid_object(int index)
1119 int obj_found = FALSE;
1122 if (index < 0 || index >= MAX_OBJECTS || Objects[index].type == OBJ_NONE)
1125 ptr = GET_FIRST(&obj_used_list);
1126 while (ptr != END_OF_LIST(&obj_used_list)) {
1127 Assert(ptr->type != OBJ_NONE);
1128 if (OBJ_INDEX(ptr) == index)
1131 ptr = GET_NEXT(ptr);
1134 Assert(obj_found); // just to make sure it's in the list like it should be.
1138 int query_valid_ship(int index)
1140 int obj_found = FALSE;
1143 if (index < 0 || index >= MAX_OBJECTS || Objects[index].type != OBJ_SHIP)
1146 ptr = GET_FIRST(&obj_used_list);
1147 while (ptr != END_OF_LIST(&obj_used_list)) {
1148 Assert(ptr->type != OBJ_NONE);
1149 if (OBJ_INDEX(ptr) == index)
1152 ptr = GET_NEXT(ptr);
1155 Assert(obj_found); // just to make sure it's in the list like it should be.
1159 int query_valid_waypoint(int index)
1161 int obj_found = FALSE;
1164 if (index < 0 || index >= MAX_OBJECTS || Objects[index].type != OBJ_WAYPOINT)
1167 ptr = GET_FIRST(&obj_used_list);
1168 while (ptr != END_OF_LIST(&obj_used_list)) {
1169 Assert(ptr->type != OBJ_NONE);
1170 if (OBJ_INDEX(ptr) == index)
1173 ptr = GET_NEXT(ptr);
1176 Assert(obj_found); // just to make sure it's in the list like it should be.
1180 // Sets the current object to whatever is specified or advances to the next object
1181 // in the list if nothing is passed.
1182 void set_cur_object_index(int obj)
1189 set_cur_indices(obj); // select the new object
1190 Update_ship = Update_wing = 1;
1191 Waypoint_editor_dialog.initialize_data(1);
1195 // changes the currently selected wing. It is assumed that cur_wing == cur_ship's wing
1196 // number. Don't call this if this won't be true, or else you'll screw things up.
1197 void set_cur_wing(int wing)
1200 /* if (cur_ship != -1)
1201 Assert(cur_wing == Ships[cur_ship].wingnum);
1202 if ((cur_object_index != -1) && (Objects[cur_object_index].type == OBJ_SHIP))
1203 Assert(cur_wing == Ships[Objects[cur_object_index].instance].wingnum);*/
1208 // sets up the various cur_* global variables related to the selecting of an object. This
1209 // is an internal function that shouldn't typically get called directly. Use set_cur_object_index() instead.
1210 void set_cur_indices(int obj)
1214 CSingleLock sync(&CS_cur_object_index);
1216 sync.Lock(); // Don't modify until it's unlocked (if it's locked elsewhere).
1217 if (query_valid_object(obj)) {
1218 cur_object_index = obj;
1219 cur_ship = cur_wing = cur_waypoint_list = cur_waypoint = -1;
1220 if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START)) {
1221 cur_ship = Objects[obj].instance;
1222 cur_wing = Ships[cur_ship].wingnum;
1224 for (i=0; i<Wings[cur_wing].wave_count; i++)
1225 if (wing_objects[cur_wing][i] == cur_object_index) {
1230 } else if (Objects[obj].type == OBJ_WAYPOINT) {
1231 cur_waypoint_list = Objects[obj].instance / 65536;
1232 cur_waypoint = Objects[obj].instance & 0xffff;
1238 if (obj == -1 || !num_objects) {
1239 cur_object_index = cur_ship = cur_wing = cur_waypoint_list = cur_waypoint = -1;
1243 if (query_valid_object(cur_object_index))
1244 ptr = Objects[cur_object_index].next;
1246 ptr = GET_FIRST(&obj_used_list);
1248 if (ptr == END_OF_LIST(&obj_used_list))
1251 Assert(ptr != END_OF_LIST(&obj_used_list));
1252 cur_object_index = OBJ_INDEX(ptr);
1253 Assert(ptr->type != OBJ_NONE);
1254 cur_ship = cur_wing = cur_waypoint_list = cur_waypoint = -1;
1255 if (ptr->type == OBJ_SHIP) {
1256 cur_ship = ptr->instance;
1257 cur_wing = Ships[cur_ship].wingnum;
1258 for (i=0; i<Wings[cur_wing].wave_count; i++)
1259 if (wing_objects[cur_wing][i] == cur_object_index) {
1264 } else if (ptr->type == OBJ_WAYPOINT) {
1265 cur_waypoint_list = ptr->instance / 65536;
1266 cur_waypoint = ptr->instance & 0xffff;
1270 int update_dialog_boxes()
1274 nprintf(("Fred routing", "updating dialog boxes\n"));
1276 // check wing first, since ships are dependent on wings, but not the reverse
1277 z = Wing_editor_dialog.update_data(0);
1279 nprintf(("Fred routing", "wing dialog save failed\n"));
1280 Wing_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
1281 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1286 z = Ship_editor_dialog.update_data(0);
1288 nprintf(("Fred routing", "ship dialog save failed\n"));
1289 Ship_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
1290 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1295 z = Waypoint_editor_dialog.update_data(0);
1297 nprintf(("Fred routing", "waypoint dialog save failed\n"));
1298 Waypoint_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
1299 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1304 update_map_window();
1308 int delete_object(int obj)
1312 Ship_editor_dialog.bypass_all++;
1313 r = common_object_delete(obj);
1314 Ship_editor_dialog.bypass_all--;
1318 int delete_object(object *ptr)
1322 Ship_editor_dialog.bypass_all++;
1323 r = common_object_delete(OBJ_INDEX(ptr));
1324 Ship_editor_dialog.bypass_all--;
1328 int delete_ship(int ship)
1332 Ship_editor_dialog.bypass_all++;
1333 r = common_object_delete(Ships[ship].objnum);
1334 Ship_editor_dialog.bypass_all--;
1338 int common_object_delete(int obj)
1340 char msg[255], *name;
1341 int i, z, r, type, num;
1344 type = Objects[obj].type;
1345 if (type == OBJ_START) {
1346 i = Objects[obj].instance;
1347 if (Player_starts < 2) { // player 1 start
1348 Fred_main_wnd->MessageBox("Must have at least 1 player starting point!",
1349 NULL, MB_OK | MB_ICONEXCLAMATION);
1355 Assert((i >= 0) && (i < MAX_SHIPS));
1356 sprintf(msg, "Player %d", i + 1);
1358 r = reference_handler(name, REF_TYPE_PLAYER, obj);
1362 if (Ships[i].wingnum >= 0) {
1363 r = delete_ship_from_wing(i);
1368 Objects[obj].type = OBJ_SHIP; // was allocated as a ship originally, so remove as such.
1369 invalidate_references(name, REF_TYPE_PLAYER);
1370 objp = GET_FIRST(&obj_used_list);
1371 while (objp != END_OF_LIST(&obj_used_list)) {
1372 // check if any ship is docked with this ship and break dock if so.
1373 if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) {
1374 num = get_ship_from_obj(objp);
1375 if (Ai_info[Ships[num].ai_index].dock_objnum == obj)
1376 Ai_info[Ships[num].ai_index].dock_objnum = -1;
1379 objp = GET_NEXT(objp);
1382 if (Player_start_shipnum == i) { // need a new single player start.
1383 objp = GET_FIRST(&obj_used_list);
1384 while (objp != END_OF_LIST(&obj_used_list)) {
1385 if (objp->type == OBJ_START) {
1386 Player_start_shipnum = objp->instance;
1390 objp = GET_NEXT(objp);
1396 } else if (type == OBJ_WAYPOINT) {
1399 list = Objects[obj].instance / 65536;
1400 i = Objects[obj].instance & 0xffff;
1401 Assert(list >= 0 && list < MAX_WAYPOINT_LISTS);
1402 count = Waypoint_lists[list].count;
1403 Assert(i >= 0 && i < count);
1405 if (Waypoint_lists[list].count == 1) {
1406 name = Waypoint_lists[list].name;
1407 r = reference_handler(name, REF_TYPE_PATH, obj);
1412 sprintf(msg, "%s:%d", Waypoint_lists[list].name, i + 1);
1414 r = reference_handler(name, REF_TYPE_WAYPOINT, obj);
1418 invalidate_references(name, REF_TYPE_WAYPOINT);
1419 objp = GET_FIRST(&obj_used_list);
1420 while (objp != END_OF_LIST(&obj_used_list)) {
1421 if ((objp->type == OBJ_WAYPOINT) && ((objp->instance / 65536) == list))
1422 if ((objp->instance & 0xffff) > i)
1425 objp = GET_NEXT(objp);
1428 while (i < count - 1) {
1429 Waypoint_lists[list].waypoints[i] = Waypoint_lists[list].waypoints[i + 1];
1433 Waypoint_lists[list].count--;
1434 if (!Waypoint_lists[list].count) {
1435 invalidate_references(Waypoint_lists[list].name, REF_TYPE_PATH);
1436 objp = GET_FIRST(&obj_used_list);
1437 while (objp != END_OF_LIST(&obj_used_list)) {
1438 if ((objp->type == OBJ_WAYPOINT) && ((objp->instance / 65536) > list))
1439 objp->instance -= 65536;
1441 objp = GET_NEXT(objp);
1444 while (list < Num_waypoint_lists - 1) {
1445 Waypoint_lists[list] = Waypoint_lists[list + 1];
1449 Num_waypoint_lists--;
1450 Waypoint_lists[list].count = 0;
1453 } else if (type == OBJ_SHIP) {
1454 name = Ships[Objects[obj].instance].ship_name;
1455 r = reference_handler(name, REF_TYPE_SHIP, obj);
1459 z = Objects[obj].instance;
1460 if (Ships[z].wingnum >= 1) {
1461 invalidate_references(name, REF_TYPE_SHIP);
1462 r = delete_ship_from_wing(z);
1466 } else if (Ships[z].wingnum >= 0) {
1467 r = delete_ship_from_wing(z);
1471 invalidate_references(name, REF_TYPE_SHIP);
1474 for (i=0; i<Num_reinforcements; i++)
1475 if (!stricmp(name, Reinforcements[i].name)) {
1476 delete_reinforcement(i);
1480 objp = GET_FIRST(&obj_used_list);
1481 while (objp != END_OF_LIST(&obj_used_list)) {
1482 // check if any ship is docked with this ship and break dock if so.
1483 if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) {
1484 num = get_ship_from_obj(objp);
1485 if (Ai_info[Ships[num].ai_index].dock_objnum == obj)
1486 Ai_info[Ships[num].ai_index].dock_objnum = -1;
1489 objp = GET_NEXT(objp);
1492 } else if (type == OBJ_POINT) {
1493 Assert(Briefing_dialog);
1494 Briefing_dialog->delete_icon(Objects[obj].instance);
1498 } else if (type == OBJ_JUMP_NODE) {
1499 i = Objects[obj].instance;
1500 objp = GET_FIRST(&obj_used_list);
1501 while (objp != END_OF_LIST(&obj_used_list)) {
1502 if ((objp->type == OBJ_JUMP_NODE) && (objp->instance > i))
1505 objp = GET_NEXT(objp);
1508 while (i < Num_jump_nodes - 1) {
1509 Jump_nodes[i] = Jump_nodes[i + 1];
1523 void delete_marked()
1528 ptr = GET_FIRST(&obj_used_list);
1529 while (ptr != END_OF_LIST(&obj_used_list)) {
1530 next = GET_NEXT(ptr);
1531 if (ptr->flags & OF_MARKED)
1532 if (delete_object(ptr) == 2) // user went to a reference, so don't get in the way.
1539 set_cur_object_index(-1);
1544 void delete_reinforcement(int num)
1548 for (i=num; i<Num_reinforcements-1; i++)
1549 Reinforcements[i] = Reinforcements[i + 1];
1551 Num_reinforcements--;
1555 // delete ship, removing it from it's wing if necessary.
1556 int delete_ship_from_wing(int ship)
1558 char name[NAME_LENGTH];
1559 int i, r, wing, end;
1561 wing = Ships[ship].wingnum;
1563 if (Wings[wing].wave_count == 1) {
1566 r = delete_wing(wing, 1);
1576 i = Wings[wing].wave_count;
1579 if (wing_objects[wing][i] == Ships[ship].objnum){
1584 Assert(i != -1); // Error, object should be in wing.
1585 if (Wings[wing].special_ship == i){
1586 Wings[wing].special_ship = 0;
1587 } else if (Wings[wing].special_ship > i) {
1588 Wings[wing].special_ship--;
1592 wing_objects[wing][i] = wing_objects[wing][end];
1593 Wings[wing].ship_index[i] = Wings[wing].ship_index[end];
1594 if (Objects[wing_objects[wing][i]].type == OBJ_SHIP) {
1595 sprintf(name, "%s %d", Wings[wing].name, i + 1);
1596 rename_ship(Wings[wing].ship_index[i], name);
1600 if (Wings[wing].threshold >= Wings[wing].wave_count){
1601 Wings[wing].threshold = Wings[wing].wave_count - 1;
1604 Wings[wing].wave_count--;
1605 if (Wings[wing].wave_count && (Wings[wing].threshold >= Wings[wing].wave_count)){
1606 Wings[wing].threshold = Wings[wing].wave_count - 1;
1615 // What does this do?
1616 void add_ship_to_wing()
1618 int org_object = cur_object_index;
1621 set_cur_object_index();
1622 if (Objects[org_object].type == OBJ_NONE) {
1623 create_object(vm_vec_make(&tvec, 10.0f, 10.0f, 10.0f));
1626 Objects[cur_object_index] = Objects[org_object];
1627 Objects[cur_object_index].pos.x += 3.0f;
1628 Objects[cur_object_index].pos.y += 3.0f;
1629 physics_init(&Objects[cur_object_index].phys_info);
1630 Objects[cur_object_index].orient = Objects[org_object].orient;
1636 // Return true if current object is valid and is in a wing.
1637 // Else return false.
1638 int query_object_in_wing(int obj)
1640 if (query_valid_object(obj)){
1641 if (Ships[Objects[obj].instance].wingnum != -1){
1649 void mark_object(int obj)
1651 Assert(query_valid_object(obj));
1652 if (!(Objects[obj].flags & OF_MARKED)) {
1653 Objects[obj].flags |= OF_MARKED; // set as marked
1656 if (cur_object_index == -1){
1657 set_cur_object_index(obj);
1659 Update_ship = Update_wing = 1;
1660 Waypoint_editor_dialog.initialize_data(1);
1664 void unmark_object(int obj)
1666 Assert(query_valid_object(obj));
1667 if (Objects[obj].flags & OF_MARKED) {
1668 Objects[obj].flags &= ~OF_MARKED;
1671 if (obj == cur_object_index) { // need to find a new index
1674 ptr = GET_FIRST(&obj_used_list);
1675 while (ptr != END_OF_LIST(&obj_used_list)) {
1676 if (ptr->flags & OF_MARKED) {
1677 set_cur_object_index(OBJ_INDEX(ptr)); // found one
1681 ptr = GET_NEXT(ptr);
1684 set_cur_object_index(-1); // can't find one; nothing is marked.
1686 Update_ship = Update_wing = 1;
1687 Waypoint_editor_dialog.initialize_data(1);
1691 // clears the marked flag of all objects (so nothing is marked)
1697 for (i=0; i<MAX_OBJECTS; i++){
1698 Objects[i].flags &= ~OF_MARKED;
1703 set_cur_object_index(-1);
1707 void clear_menu(CMenu *ptr)
1711 count = ptr->GetMenuItemCount();
1713 ptr->DeleteMenu(count, MF_BYPOSITION);
1717 void generate_wing_popup_menu(CMenu *mptr, int first_id, int state)
1719 int i, z, columns, rows, count;
1725 rows = num_wings / columns;
1729 for (i=0; i<MAX_WINGS; i++){
1730 if (Wings[i].wave_count) {
1731 z = state | MF_STRING;
1734 z |= MF_MENUBARBREAK;
1737 mptr->AppendMenu(z, first_id + i, Wings[i].name);
1741 mptr->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1744 void generate_ship_popup_menu(CMenu *mptr, int first_id, int state, int filter)
1746 int z, ship, columns, rows, count, num_ships;
1750 num_ships = ship_get_num_ships();
1754 rows = num_ships / columns;
1758 ptr = GET_FIRST(&obj_used_list);
1759 while (ptr != END_OF_LIST(&obj_used_list)) {
1760 if ((ptr->type == OBJ_SHIP) || ((ptr->type == OBJ_START) && (filter & SHIP_FILTER_PLAYERS))) {
1762 if (filter & SHIP_FILTER_FLYABLE) {
1763 if (Ship_info[Ships[get_ship_from_obj(ptr)].ship_info_index].flags & SIF_NOT_FLYABLE){
1769 z = state | MF_STRING;
1772 z |= MF_MENUBARBREAK;
1775 ship = ptr->instance;
1776 mptr->AppendMenu(z, first_id + ship, Ships[ship].ship_name);
1780 ptr = GET_NEXT(ptr);
1783 mptr->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1786 // Alternate string lookup function, taking a CString instead. The reason that it's here,
1787 // instead of parselo.cpp, is because the class CString require an include of windows.h,
1788 // which everyone wants to avoid including in any freespace header files. So..
1789 int string_lookup(CString str1, char *strlist[], int max)
1793 for (i=0; i<max; i++) {
1794 Assert(strlen(strlist[i]));
1796 if (!stricmp(str1, strlist[i])){
1804 int gray_menu_tree(CMenu *base)
1806 int i, z, count = 0;
1809 i = base->GetMenuItemCount();
1811 if ((submenu = base->GetSubMenu(i))>0) {
1812 if (gray_menu_tree(submenu)) {
1815 base->EnableMenuItem(i, MF_GRAYED | MF_BYPOSITION);
1819 z = base->GetMenuState(i, MF_BYPOSITION);
1820 if (z == MF_ENABLED){
1829 int query_initial_orders_conflict(int wing)
1838 if (query_initial_orders_empty(Wings[wing].ai_goals)){
1842 i = Wings[wing].wave_count; // wing has orders, now check ships.
1844 z = Ships[Objects[wing_objects[wing][i]].instance].ai_index;
1845 if (!query_initial_orders_empty(Ai_info[z].goals)){ // ship also has orders
1853 int query_initial_orders_empty(ai_goal *ai_goals)
1857 for (i=0; i<MAX_AI_GOALS; i++){
1858 if (ai_goals[i].ai_mode != AI_GOAL_NONE){
1866 int set_reinforcement(char *name, int state)
1868 int i, index, cur = -1;
1870 for (i=0; i<Num_reinforcements; i++){
1871 if (!stricmp(Reinforcements[i].name, name)){
1876 if (!state && (cur != -1)) {
1877 Num_reinforcements--;
1878 Reinforcements[cur] = Reinforcements[Num_reinforcements];
1880 // clear the ship/wing flag for this reinforcement
1881 index = ship_name_lookup(name);
1883 Ships[index].flags &= ~SF_REINFORCEMENT;
1885 index = wing_name_lookup(name);
1887 Wings[index].flags &= ~WF_REINFORCEMENT;
1891 Int3(); // get allender -- coudln't find ship/wing for clearing reinforcement flag
1898 if (state && (cur == -1) && (Num_reinforcements < MAX_REINFORCEMENTS)) {
1899 Assert(strlen(name) < NAME_LENGTH);
1900 strcpy(Reinforcements[Num_reinforcements].name, name);
1901 Reinforcements[Num_reinforcements].uses = 1;
1902 Reinforcements[Num_reinforcements].arrival_delay = 0;
1903 memset( Reinforcements[Num_reinforcements].no_messages, 0, MAX_REINFORCEMENT_MESSAGES * NAME_LENGTH );
1904 memset( Reinforcements[Num_reinforcements].yes_messages, 0, MAX_REINFORCEMENT_MESSAGES * NAME_LENGTH );
1905 Num_reinforcements++;
1907 // set the reinforcement flag on the ship or wing
1908 index = ship_name_lookup(name);
1910 Ships[index].flags |= SF_REINFORCEMENT;
1912 index = wing_name_lookup(name);
1914 Wings[index].flags |= WF_REINFORCEMENT;
1918 Int3(); // get allender -- coudln't find ship/wing for setting reinforcement flag
1925 // this code will take care of setting the bits for the ship/wing flags
1926 if ( state && (cur != -1) ) {
1927 // set the reinforcement flag on the ship or wing
1928 index = ship_name_lookup(name);
1930 Ships[index].flags |= SF_REINFORCEMENT;
1932 index = wing_name_lookup(name);
1934 Wings[index].flags |= WF_REINFORCEMENT;
1938 Int3(); // get allender -- coudln't find ship/wing for setting reinforcement flag
1945 int get_docking_list(int model_index)
1950 pm = model_get(model_index);
1951 Assert(pm->n_docks <= MAX_DOCKS);
1952 for (i=0; i<pm->n_docks; i++)
1953 Docking_bay_list[i] = pm->docking_bays[i].name;
1958 // DA 1/7/99 These ship names are not variables
1959 int rename_ship(int ship, char *name)
1964 Assert(strlen(name) < NAME_LENGTH);
1966 update_sexp_references(Ships[ship].ship_name, name);
1967 ai_update_goal_references(REF_TYPE_SHIP, Ships[ship].ship_name, name);
1968 for (i=0; i<Num_reinforcements; i++)
1969 if (!stricmp(Ships[ship].ship_name, Reinforcements[i].name)) {
1970 strcpy(Reinforcements[i].name, name);
1973 strcpy(Ships[ship].ship_name, name);
1974 if (ship == cur_ship)
1975 Ship_editor_dialog.m_ship_name = _T(name);
1980 int invalidate_references(char *name, int type)
1985 sprintf(new_name, "<%s>", name);
1986 update_sexp_references(name, new_name);
1987 ai_update_goal_references(type, name, new_name);
1988 for (i=0; i<Num_reinforcements; i++)
1989 if (!stricmp(name, Reinforcements[i].name)) {
1990 strcpy(Reinforcements[i].name, new_name);
1996 int internal_integrity_check()
2000 for (i=0; i<Num_mission_events; i++)
2001 verify_sexp_tree(Mission_events[i].formula);
2003 for (i=0; i<Num_goals; i++)
2004 verify_sexp_tree(Mission_goals[i].formula);
2006 for (i=0; i<MAX_WINGS; i++)
2007 if (Wings[i].wave_count) {
2008 verify_sexp_tree(Wings[i].arrival_cue);
2009 verify_sexp_tree(Wings[i].departure_cue);
2012 for (i=0; i<MAX_SHIPS; i++)
2013 if (Ships[i].objnum >= 0) {
2014 verify_sexp_tree(Ships[i].arrival_cue);
2015 verify_sexp_tree(Ships[i].departure_cue);
2016 if (Ships[i].ai_index < 0)
2018 if (Ai_info[Ships[i].ai_index].shipnum != i)
2025 void correct_marking()
2029 ptr = GET_FIRST(&obj_used_list);
2030 while (ptr != END_OF_LIST(&obj_used_list)) {
2031 if (ptr->flags & OF_MARKED) {
2032 if (ptr->flags & OF_HIDDEN)
2033 unmark_object(OBJ_INDEX(ptr));
2035 else switch (ptr->type) {
2037 if (!Show_waypoints)
2038 unmark_object(OBJ_INDEX(ptr));
2043 unmark_object(OBJ_INDEX(ptr));
2048 unmark_object(OBJ_INDEX(ptr));
2050 switch (Ships[ptr->instance].team) {
2053 unmark_object(OBJ_INDEX(ptr));
2058 unmark_object(OBJ_INDEX(ptr));
2063 unmark_object(OBJ_INDEX(ptr));
2071 ptr = GET_NEXT(ptr);
2075 // Fills a combo box with a list of all docking points of type 'type' on ship 'ship'.
2076 // Item data is the actual docking point index.
2077 void set_valid_dock_points(int ship, int type, CComboBox *box)
2079 int i, z, num, model;
2081 model = Ships[ship].modelnum;
2082 num = model_get_num_dock_points(model);
2083 for (i=0; i<num; i++)
2084 if (model_get_dock_index_type(model, i) & type) {
2085 z = box->AddString(model_get_dock_name(model, i));
2086 box->SetItemData(z, i);
2089 Assert(box->GetCount());
2092 // Given an object index, find the ship index for that object.
2093 int get_ship_from_obj(int obj)
2095 if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START))
2096 return Objects[obj].instance;
2102 int get_ship_from_obj(object *objp)
2104 if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START))
2105 return objp->instance;
2111 void ai_update_goal_references(int type, char *old_name, char *new_name)
2115 for (i=0; i<MAX_AI_INFO; i++) // loop through all Ai_info entries
2116 if (Ai_info[i].shipnum != -1) // skip if unused
2117 ai_update_goal_references(Ai_info[i].goals, type, old_name, new_name);
2119 for (i=0; i<MAX_WINGS; i++)
2120 if (Wings[i].wave_count)
2121 ai_update_goal_references(Wings[i].ai_goals, type, old_name, new_name);
2124 int query_referenced_in_ai_goals(int type, char *name)
2128 for (i=0; i<MAX_AI_INFO; i++) // loop through all Ai_info entries
2129 if (Ai_info[i].shipnum >= 0) // skip if unused
2130 if (query_referenced_in_ai_goals(Ai_info[i].goals, type, name))
2131 return Ai_info[i].shipnum | SRC_SHIP_ORDER;
2133 for (i=0; i<MAX_WINGS; i++)
2134 if (Wings[i].wave_count)
2135 if (query_referenced_in_ai_goals(Wings[i].ai_goals, type, name))
2136 return i | SRC_WING_ORDER;
2141 int advanced_stricmp(char *one, char *two)
2152 return stricmp(one, two);
2155 // returns 0: go ahead change object
2156 // 1: don't change it
2157 // 2: abort (they used cancel to go to reference)
2158 int reference_handler(char *name, int type, int obj)
2160 char msg[2048], text[128], type_name[128];
2165 sprintf(type_name, "Ship \"%s\"", name);
2169 sprintf(type_name, "Wing \"%s\"", name);
2172 case REF_TYPE_PLAYER:
2173 strcpy(type_name, name);
2176 case REF_TYPE_WAYPOINT:
2177 sprintf(type_name, "Waypoint \"%s\"", name);
2181 sprintf(type_name, "Waypoint path \"%s\"", name);
2185 Error(LOCATION, "Type unknown for object \"%s\". Let Hoffos know now!", name);
2188 r = query_referenced_in_sexp(type, name, &node);
2190 n = r & SRC_DATA_MASK;
2191 switch (r & SRC_MASK) {
2192 case SRC_SHIP_ARRIVAL:
2193 sprintf(text, "the arrival cue of ship \"%s\"", Ships[n].ship_name);
2196 case SRC_SHIP_DEPARTURE:
2197 sprintf(text, "the departure cue of ship \"%s\"", Ships[n].ship_name);
2200 case SRC_WING_ARRIVAL:
2201 sprintf(text, "the arrival cue of wing \"%s\"", Wings[n].name);
2204 case SRC_WING_DEPARTURE:
2205 sprintf(text, "the departure cue of wing \"%s\"", Wings[n].name);
2209 if (*Mission_events[n].name)
2210 sprintf(text, "event \"%s\"", Mission_events[n].name);
2212 sprintf(text, "event #%d", n);
2216 case SRC_MISSION_GOAL:
2217 if (*Mission_goals[n].name)
2218 sprintf(text, "mission goal \"%s\"", Mission_goals[n].name);
2220 sprintf(text, "mission goal #%d", n);
2224 case SRC_DEBRIEFING:
2225 sprintf(text, "debriefing #%d", n);
2229 sprintf(text, "briefing #%d", n);
2232 default: // very bad. Someone added an sexp somewhere and didn't change this.
2233 Warning(LOCATION, "\"%s\" referenced by an unknown sexp source! "
2234 "Run for the hills and let Hoffoss know right now!", name);
2240 sprintf(msg, "%s is referenced by %s (possibly more sexps).\n"
2241 "Do you want to delete it anyway?\n\n"
2242 "(click Cancel to go to the reference)", type_name, text);
2244 r = sexp_reference_handler(node, r, msg);
2258 r = query_referenced_in_ai_goals(type, name);
2260 n = r & SRC_DATA_MASK;
2261 switch (r & SRC_MASK) {
2262 case SRC_SHIP_ORDER:
2263 sprintf(text, "ship \"%s\"", Ships[n].ship_name);
2266 case SRC_WING_ORDER:
2267 sprintf(text, "wing \"%s\"", Wings[n].name);
2270 default: // very bad. Someone added an sexp somewhere and didn't change this.
2271 Error(LOCATION, "\"%s\" referenced by an unknown initial orders source! "
2272 "Run for the hills and let Hoffoss know right now!", name);
2275 sprintf(msg, "%s is referenced by the initial orders of %s (possibly \n"
2276 "more initial orders). Do you want to delete it anyway?\n\n"
2277 "(click Cancel to go to the reference)", type_name, text);
2279 r = orders_reference_handler(r, msg);
2293 if ((type != REF_TYPE_SHIP) && (type != REF_TYPE_WING))
2296 for (n=0; n<Num_reinforcements; n++)
2297 if (!stricmp(name, Reinforcements[n].name))
2300 if (n < Num_reinforcements) {
2301 sprintf(msg, "Ship \"%s\" is a reinforcement unit.\n"
2302 "Do you want to delete it anyway?", name);
2304 r = Fred_main_wnd->MessageBox(msg, NULL, MB_YESNO | MB_ICONEXCLAMATION);
2316 int orders_reference_handler(int code, char *msg)
2320 r = Fred_main_wnd->MessageBox(msg, "Warning", MB_YESNOCANCEL | MB_ICONEXCLAMATION);
2327 ShipGoalsDlg dlg_goals;
2329 n = code & SRC_DATA_MASK;
2330 switch (code & SRC_MASK) {
2331 case SRC_SHIP_ORDER:
2333 mark_object(Ships[n].objnum);
2335 dlg_goals.self_ship = n;
2336 dlg_goals.DoModal();
2337 if (!query_initial_orders_empty(Ai_info[Ships[n].ai_index].goals))
2338 if ((Ships[n].wingnum >= 0) && (query_initial_orders_conflict(Ships[n].wingnum)))
2339 Fred_main_wnd->MessageBox("This ship's wing also has initial orders", "Possible conflict");
2343 case SRC_WING_ORDER:
2347 dlg_goals.self_wing = n;
2348 dlg_goals.DoModal();
2349 if (query_initial_orders_conflict(n))
2350 Fred_main_wnd->MessageBox("One or more ships of this wing also has initial orders", "Possible conflict");
2354 default: // very bad. Someone added an sexp somewhere and didn't change this.
2355 Error(LOCATION, "Unknown initial order reference source");
2362 int sexp_reference_handler(int node, int code, char *msg)
2366 r = Fred_main_wnd->MessageBox(msg, "Warning", MB_YESNOCANCEL | MB_ICONEXCLAMATION);
2373 switch (code & SRC_MASK) {
2374 case SRC_SHIP_ARRIVAL:
2375 case SRC_SHIP_DEPARTURE:
2376 if (!Ship_editor_dialog.GetSafeHwnd())
2377 Ship_editor_dialog.Create();
2379 Ship_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
2380 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2381 Ship_editor_dialog.ShowWindow(SW_RESTORE);
2383 Ship_editor_dialog.select_sexp_node = node;
2385 mark_object(Ships[code & SRC_DATA_MASK].objnum);
2388 case SRC_WING_ARRIVAL:
2389 case SRC_WING_DEPARTURE:
2390 if (!Wing_editor_dialog.GetSafeHwnd())
2391 Wing_editor_dialog.Create();
2393 Wing_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
2394 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2395 Wing_editor_dialog.ShowWindow(SW_RESTORE);
2397 Wing_editor_dialog.select_sexp_node = node;
2399 mark_wing(code & SRC_DATA_MASK);
2403 if (Message_editor_dlg) {
2404 Fred_main_wnd->MessageBox("You must close the message editor before the event editor can be opened");
2408 if (!Event_editor_dlg) {
2409 Event_editor_dlg = new event_editor;
2410 Event_editor_dlg->select_sexp_node = node;
2411 Event_editor_dlg->Create(event_editor::IDD);
2414 Event_editor_dlg->SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2415 Event_editor_dlg->ShowWindow(SW_RESTORE);
2418 case SRC_MISSION_GOAL: {
2419 CMissionGoalsDlg dlg;
2421 dlg.select_sexp_node = node;
2426 case SRC_DEBRIEFING: {
2427 debriefing_editor_dlg dlg;
2429 dlg.select_sexp_node = node;
2434 case SRC_BRIEFING: {
2435 if (!Briefing_dialog) {
2436 Briefing_dialog = new briefing_editor_dlg;
2437 Briefing_dialog->create();
2440 Briefing_dialog->SetWindowPos(&Briefing_dialog->wndTop, 0, 0, 0, 0,
2441 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2442 Briefing_dialog->ShowWindow(SW_RESTORE);
2443 Briefing_dialog->focus_sexp(node);
2447 default: // very bad. Someone added an sexp somewhere and didn't change this.
2448 Error(LOCATION, "Unknown sexp reference source");
2455 char *object_name(int obj)
2457 static char text[80];
2460 if (!query_valid_object(obj))
2463 i = Objects[obj].instance;
2464 switch (Objects[obj].type) {
2467 return Ships[i].ship_name;
2470 sprintf(text, "%s:%d", Waypoint_lists[i / 65536].name, (i & 0xffff) + 1);
2474 return "Briefing icon";
2480 char *get_order_name(int order)
2484 if (order == AI_GOAL_NONE) // special case
2487 for (i=0; i<Ai_goal_list_size; i++)
2488 if (Ai_goal_list[i].def & order)
2489 return Ai_goal_list[i].name;
2494 void object_moved(object *ptr, int mark)
2498 if (ptr->type == OBJ_WAYPOINT)
2499 Waypoint_lists[ptr->instance / 65536].waypoints[ptr->instance & 0xffff] = ptr->pos;
2501 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) { // do we have a ship?
2502 sh1 = get_ship_from_obj(ptr);
2503 o2 = Ai_info[Ships[sh1].ai_index].dock_objnum;
2504 if (o2 >= 0) { // is it docked with something?
2505 sh2 = get_ship_from_obj(o2);
2506 if (mark || !(Objects[o2].flags & OF_MARKED) || (sh1 < sh2))
2507 dock_orient_and_approach(&Objects[o2], ptr, DOA_DOCK_STAY);
2512 // determine if all the ships in a given wing are all marked or not.
2513 int query_whole_wing_marked(int wing)
2518 if (!Wings[wing].wave_count)
2521 ptr = GET_FIRST(&obj_used_list);
2522 while (ptr != END_OF_LIST(&obj_used_list)) {
2523 if (ptr->flags & OF_MARKED)
2524 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START))
2525 if (Ships[get_ship_from_obj(ptr)].wingnum == wing)
2528 ptr = GET_NEXT(ptr);
2531 if (count == Wings[wing].wave_count)
2537 void generate_weaponry_usage_list(int *arr, int wing)
2545 i = Wings[wing].wave_count;
2547 swp = &Ships[Wings[wing].ship_index[i]].weapons;
2548 j = swp->num_primary_banks;
2550 arr[swp->primary_bank_weapons[j]]++;
2552 j = swp->num_secondary_banks;
2554 arr[swp->secondary_bank_weapons[j]] += int(ceil(swp->secondary_bank_ammo[j] * swp->secondary_bank_capacity[j] / 100 / Weapon_info[swp->secondary_bank_weapons[j]].cargo_size));
2558 void generate_weaponry_usage_list(int *arr)
2562 for (i=0; i<MAX_WEAPON_TYPES; i++)
2565 generate_weaponry_usage_list(arr, wing_name_lookup("alpha"));
2566 generate_weaponry_usage_list(arr, wing_name_lookup("beta"));
2567 generate_weaponry_usage_list(arr, wing_name_lookup("gamma"));
2570 // function which adds all current ships in the Fred mission to the passed in combo box. useful for
2571 // building up ship lists for arrival/departure targets
2572 void management_add_ships_to_combo( CComboBox *box, int flags )
2577 box->ResetContent();
2579 // add the "special" targets, i.e. any friendly, any hostile, etc.
2580 if ( flags & SHIPS_2_COMBO_SPECIAL ) {
2581 for (i=0; i<MAX_SPECIAL_ARRIVAL_ANCHORS; i++) {
2582 id = box->AddString(Special_arrival_anchor_names[i]);
2583 box->SetItemData(id, SPECIAL_ARRIVAL_ANCHORS_OFFSET + i);
2587 // either add all ships to the list, or only add ships with docking bays.
2588 if ( flags & SHIPS_2_COMBO_ALL_SHIPS ) {
2589 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
2590 if ( ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && !(objp->flags & OF_MARKED) ) {
2591 id = box->AddString(Ships[get_ship_from_obj(objp)].ship_name);
2592 box->SetItemData(id, get_ship_from_obj(objp));
2595 } else if ( flags & SHIPS_2_COMBO_DOCKING_BAY_ONLY ) {
2596 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
2597 if ( ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && !(objp->flags & OF_MARKED) ) {
2600 // determine if this ship has a docking bay
2601 pm = model_get( Ships[objp->instance].modelnum );
2603 if ( pm->ship_bay && (pm->ship_bay->num_paths > 0) ) {
2604 id = box->AddString(Ships[get_ship_from_obj(objp)].ship_name);
2605 box->SetItemData(id, get_ship_from_obj(objp));
2612 char *reg_read_string( char *section, char *name, char *default_value )
2615 DWORD dwType, dwLen;
2617 static char tmp_string_data[1024];
2620 strcpy( keyname, section );
2622 lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, // Where it is
2623 keyname, // name of key
2624 NULL, // DWORD reserved
2625 KEY_QUERY_VALUE, // Allows all changes
2626 &hKey ); // Location to store key
2628 if ( lResult != ERROR_SUCCESS ) {
2629 mprintf(( "Error opening registry key '%s'\n", keyname ));
2634 mprintf(( "No variable name passed\n" ));
2639 lResult = RegQueryValueEx( hKey, // Handle to key
2640 name, // The values name
2641 NULL, // DWORD reserved
2642 &dwType, // What kind it is
2643 (ubyte *)&tmp_string_data, // value to set
2644 &dwLen ); // How many bytes to set
2646 if ( lResult != ERROR_SUCCESS ) {
2647 mprintf(( "Error reading registry key '%s'\n", name ));
2651 default_value = tmp_string_data;
2657 return default_value;