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.1 2002/05/03 03:28:09 root
17 * 29 10/13/99 9:22a Daveb
18 * Fixed Fred jumpnode placing bug. Fixed 1024 glide tiled texture problem
19 * related to movies. Fixed launcher spawning from PXO screen.
21 * 28 9/08/99 10:01p Dave
22 * Make sure game won't run in a drive's root directory. Make sure
23 * standalone routes suqad war messages properly to the host.
25 * 27 7/23/99 2:02p Jamesa
26 * Don't require gamepalettes
28 * 26 7/15/99 3:07p Dave
29 * 32 bit detection support. Mouse coord commandline.
31 * 25 7/02/99 4:30p Dave
32 * Much more sophisticated lightning support.
34 * 24 5/20/99 6:59p Dave
35 * Added alternate type names for ships. Changed swarm missile table
38 * 23 4/26/99 8:47p Dave
39 * Made all pof related nebula stuff customizable through Fred.
41 * 22 4/16/99 2:34p Andsager
42 * Second pass on debris fields
44 * 21 4/15/99 5:00p Andsager
45 * Frist pass on Debris field
47 * 20 4/07/99 6:21p Dave
48 * Fred and Freespace support for multiple background bitmaps and suns.
49 * Fixed link errors on all subprojects. Moved encrypt_init() to
50 * cfile_init() and lcl_init(), since its safe to call twice.
52 * 19 3/31/99 9:50a Andsager
53 * Interface for generalization of asteroid field (debris field)
55 * 18 3/30/99 5:40p Dave
56 * Fixed reinforcements for TvT in multiplayer.
58 * 17 3/24/99 4:05p Dave
59 * Put in support for assigning the player to a specific squadron with a
60 * specific logo. Preliminary work for doing pos/orient checksumming in
61 * multiplayer to reduce bandwidth.
63 * 16 3/20/99 5:09p Dave
64 * Fixed release build fred warnings and unhandled exception.
66 * 15 2/23/99 7:03p Dave
67 * Rewrote a horribly mangled and evil team loadout dialog. Bugs gone.
69 * 14 2/17/99 2:11p Dave
70 * First full run of squad war. All freespace and tracker side stuff
73 * 13 2/10/99 11:11a Johnson
74 * Think I fixed the problem where the Neb2_awacs value was improperly
75 * being checked for in Fred.
77 * 12 2/07/99 8:51p Andsager
78 * Add inner bound to asteroid field. Inner bound tries to stay astroid
79 * free. Wrap when within and don't throw at ships inside.
81 * 11 1/25/99 5:03a Dave
82 * First run of stealth, AWACS and TAG missile support. New mission type
85 * 10 1/19/99 3:57p Andsager
86 * Round 2 of variables
88 * 9 12/18/98 1:14a Dave
89 * Rough 1024x768 support for Direct3D. Proper detection and usage through
92 * 8 11/06/98 10:15a Dave
94 * 7 10/29/98 9:22p Dave
95 * Removed minor bug concering externalization of campaign files.
97 * 6 10/29/98 6:49p Dave
98 * Finished up Fred support for externalizing mission and campaign files.
100 * 5 10/29/98 10:41a Dave
101 * Change the way cfile initializes exe directory.
103 * 4 10/23/98 5:07p Dave
104 * Put in beginnings of localization/externalization functionality.
106 * 3 10/22/98 6:13p Dave
107 * Added registry and localization support.
109 * 2 10/07/98 6:28p Dave
110 * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
111 * Fred. Globalized mission and campaign file extensions. Removed Silent
112 * Threat specific code.
114 * 1 10/07/98 3:02p Dave
116 * 1 10/07/98 3:00p Dave
118 * 227 6/17/98 2:05p Hoffoss
119 * Fixed bug: Shield system for team or ship type flags new reset when
120 * mission gets cleared out (new or load mission).
122 * 226 5/20/98 10:19p Allender
123 * make Fred work with NDEBUG build
125 * 225 5/17/98 1:48p Allender
126 * made number of respawns for new missions 3
128 * 224 5/01/98 12:34p John
129 * Added code to force FreeSpace to run in the same dir as exe and made
130 * all the parse error messages a little nicer.
132 * 223 4/30/98 8:23p John
133 * Fixed some bugs with Fred caused by my new cfile code.
135 * 222 4/27/98 4:07p Allender
136 * make orders_accepted not be assigned to -1 when creating new ships.
137 * Don't set the use_default_orders flag when orders are -1
139 * 221 4/25/98 6:43p Allender
140 * reset model data when initializeing new mission
142 * 220 4/17/98 1:41p Allender
143 * took out function calls in NDEBUG mode
145 * 219 4/13/98 10:59a Hoffoss
146 * Made "new mission" clear the cmd brief structure.
148 * 218 4/13/98 10:11a John
149 * Made timer functions thread safe. Made timer_init be called in all
152 * 217 4/06/98 5:37p Hoffoss
153 * Added sexp tree support to briefings in Fred.
155 * 216 4/06/98 12:55p John
156 * Upped the gamma for Fred.
158 * 215 4/03/98 11:34a John
159 * Fixed the stuff I broke in Fred from the new breifing
161 * 214 4/02/98 3:00p Johnson
162 * Fixed a bug a release build turned up.
164 * 213 3/24/98 1:36p Hoffoss
165 * Moved call to load_filter_info() to after cfile_init(), as it is
166 * dependent on that being set up.
168 * 212 3/24/98 12:42p Allender
169 * fixed a couple of minor problems with arrival targets
171 * 211 3/21/98 7:36p Lawrance
172 * Move jump nodes to own lib.
174 * 210 3/17/98 4:16p Allender
175 * minor changes to the kamikaze flag
177 * 209 3/17/98 11:55a Johnson
178 * Fixed bug where jump nodes wheren't being cleared on a new mission.
180 * 208 3/10/98 4:26p Hoffoss
181 * Changed jump node structure to include a name. Position is now taken
182 * from the object (each jump node has an associated object now).
184 * 207 3/09/98 4:30p Allender
185 * multiplayer secondary weapon changes. red-alert and cargo-known-delay
186 * sexpressions. Add time cargo revealed to ship structure
188 * 206 3/09/98 10:56a Hoffoss
189 * Added jump node objects to Fred.
191 * 205 3/06/98 5:10p Allender
192 * made time to: field in extended targetbox use support time to dock code
193 * for all docking shpis. Only display for waypoints and docking (not
194 * undocking). Small fixups to message menu -- not allowing depart when
195 * disabled. Depart is now by default ignored for all non-small ships
197 * 204 3/05/98 3:59p Hoffoss
198 * Added a bunch of new command brief stuff, and asteroid initialization
201 * 203 2/26/98 4:59p Allender
202 * groundwork for team vs team briefings. Moved weaponry pool into the
203 * Team_data structure. Added team field into the p_info structure.
204 * Allow for mutliple structures in the briefing code.
206 * 202 2/17/98 12:07p Hoffoss
207 * Changed over to using SF_CARGO_REVEALED in fred.
209 * 201 2/17/98 10:12a Hoffoss
210 * Fixed bug with sprintf() in reference_handler(). Forgot the first
211 * argument! :) Amazing it never crashed before.
213 * 200 2/13/98 11:45a Hoffoss
214 * Made all new ships created in Fred default to 33% initial speed.
223 #include "fredview.h"
224 #include "fredrender.h"
228 #include "linklist.h"
229 #include "missionparse.h"
230 #include "missionmessage.h"
231 #include "missiongoals.h"
232 #include "missionbriefcommon.h"
233 #include "management.h"
242 #include "starfield.h"
245 #include "missioncampaign.h"
247 #include "messageeditordlg.h"
248 #include "eventeditor.h"
249 #include "missiongoalsdlg.h"
250 #include "shieldsysdlg.h"
251 #include "eventmusic.h"
252 #include "debriefingeditordlg.h"
254 #include "asteroid.h"
255 #include "hudsquadmsg.h"
256 #include "jumpnode.h"
258 #include "localize.h"
259 #include "osregistry.h"
263 #include "neblightning.h"
267 #define UNKNOWN_USER "Unknown"
271 int cur_object_index = -1;
273 int cur_model_index = 0;
274 int cur_waypoint = -1;
275 int cur_waypoint_list = -1;
277 int bypass_update = 0;
278 int Default_player_model = 0;
283 char Fred_exe_dir[512] = "";
285 char Fred_alt_names[MAX_SHIPS][NAME_LENGTH+1];
287 // object numbers for ships in a wing.
288 int wing_objects[MAX_WINGS][MAX_SHIPS_PER_WING];
290 char *Docking_bay_list[MAX_DOCKS];
292 CCriticalSection CS_cur_object_index;
294 ai_goal_list Ai_goal_list[] = {
295 "Waypoints", AI_GOAL_WAYPOINTS,
296 "Waypoints once", AI_GOAL_WAYPOINTS_ONCE,
297 "Warp", AI_GOAL_WARP,
298 "Destroy subsystem", AI_GOAL_DESTROY_SUBSYSTEM,
299 "Attack", AI_GOAL_CHASE | AI_GOAL_CHASE_WING,
300 "Dock", AI_GOAL_DOCK,
301 "Undock", AI_GOAL_UNDOCK,
302 "Guard", AI_GOAL_GUARD | AI_GOAL_GUARD_WING,
303 "Attack any ship", AI_GOAL_CHASE_ANY,
304 "Disable ship", AI_GOAL_DISABLE_SHIP,
305 "Disarm ship", AI_GOAL_DISARM_SHIP,
306 "Evade ship", AI_GOAL_EVADE_SHIP,
307 "Ignore ship", AI_GOAL_IGNORE,
308 "Stay near ship", AI_GOAL_STAY_NEAR_SHIP,
309 "Keep safe distance", AI_GOAL_KEEP_SAFE_DISTANCE,
310 "Stay still", AI_GOAL_STAY_STILL,
311 "Play dead", AI_GOAL_PLAY_DEAD,
314 int Ai_goal_list_size = sizeof(Ai_goal_list) / sizeof(ai_goal_list);
316 // internal function prototypes
317 void set_cur_indices(int obj);
318 int common_object_delete(int obj);
319 int create_waypoint(vector *pos, int list);
320 int create_ship(matrix *orient, vector *pos, int ship_type);
321 int query_ship_name_duplicate(int ship);
322 char *reg_read_string( char *section, char *name, char *default_value );
324 extern int Nmodel_num;
325 extern int Nmodel_bitmap;
327 void string_copy(char *dest, CString &src, int max_len, int modify)
332 if (strcmp(src, dest))
339 strncpy(dest, src, len);
343 // converts a multiline string (one with newlines in it) into a windows format multiline
344 // string (newlines changed to '\r\n').
345 CString convert_multiline_string(char *src)
352 while ((ptr = strchr(src, '\n'))!=NULL) {
355 strncpy(buf, src, 250);
363 strncpy(buf, src, i);
378 // Converts a windows format multiline CString back into a normal multiline string.
379 void deconvert_multiline_string(char *buf, CString &str, int max_len)
388 while ((i = str.Find("\r\n")) >= 0) {
396 str2 = str.Mid(i + 2);
407 // removed 10/29/98 by DB
408 // this was generating an extra newline - why?
409 //if (*(ptr - 1) != '\n')
414 // medal_stuff Medals[NUM_MEDALS];
416 void parse_medal_tbl()
418 int rval, num_medals;
420 if ((rval = setjmp(parse_abort)) != 0) {
421 Error(LOCATION, "Error parsing '%s'\r\nError code = %i.\r\n", "medals.tbl", rval);
427 read_file_text("medals.tbl");
431 // parse in all the rank names
433 required_string("#Medals");
434 while ( required_string_either("#End", "$Name:") ) {
435 Assert ( num_medals < NUM_MEDALS);
436 required_string("$Name:");
437 stuff_string( Medals[num_medals].name, F_NAME, NULL );
438 required_string("$Bitmap:");
439 stuff_string( Medals[num_medals].bitmap, F_NAME, NULL );
440 required_string("$Num mods:");
441 stuff_int( &Medals[num_medals].num_versions);
443 // some medals are based on kill counts. When string +Num Kills: is present, we know that
444 // this medal is a badge and should be treated specially
445 Medals[num_medals].kills_needed = 0;
447 if ( optional_string("+Num Kills:") ) {
448 char buf[MULTITEXT_LENGTH + 1];
450 stuff_int( &Medals[num_medals].kills_needed );
452 required_string("$Wavefile 1:");
453 stuff_string(buf, F_NAME, NULL, MAX_FILENAME_LEN);
455 required_string("$Wavefile 2:");
456 stuff_string(buf, F_NAME, NULL, MAX_FILENAME_LEN);
458 required_string("$Promotion Text:");
459 stuff_string(buf, F_MULTITEXT, NULL);
465 required_string("#End");
467 // close localization
475 char palette_filename[1024];
477 if (!vm_init(24*1024*1024)) {
478 MessageBox( NULL, "Not enough memory to run Fred.\r\nTry closing down some other applications.\r\n", "Not Enough Memory", MB_OK );
482 srand( (unsigned) time(NULL) );
483 init_pending_messages();
485 // initialize registry stuff
486 os_init_registry_stuff(Osreg_company_name, Osreg_app_name, NULL);
490 Assert(strlen(Fred_exe_dir) > 0);
493 if(cfile_init(Fred_exe_dir)){
497 // initialize localization module. Make sure this is done AFTER initialzing OS.
498 // NOTE : Fred should ALWAYS run in Enlish. Otherwise it might swap in another language
499 // when saving - which would cause inconsistencies when externalizing to tstrings.tbl via Exstr
500 // trust me on this :)
501 lcl_init(LCL_ENGLISH);
507 gr_init(GR_640, GR_SOFTWARE, 8);
510 sprintf(palette_filename, "gamepalette%d-%02d", 1, 1);
511 mprintf(("Loading palette %s\n", palette_filename));
512 palette_load_table(palette_filename);
514 Fred_font = gr_init_font("font01.vf");
518 mission_brief_common_init();
520 model_free_all(); // Free all existing models
523 parse_medal_tbl(); // get medal names for sexpression usage
527 // initialize and activate external string hash table
528 // make sure to do here so that we don't parse the table files into the hash table - waste of space
532 create_new_mission();
533 neb2_init(); // fullneb stuff
536 event_music_parse_musictbl();
538 Show_waypoints = TRUE;
539 Campaign.filename[0] = 0; // indicate initialized state
548 g3_set_view_matrix(&eye_pos, &eye_orient, 0.5f);
550 for (i=0; i<Num_ship_types; i++)
551 if (Ship_info[i].flags & SIF_DEFAULT_PLAYER_SHIP) {
552 Default_player_model = cur_model_index = i;
556 Id_select_type_start = Num_ship_types + 2;
557 Id_select_type_jump_node = Num_ship_types + 1;
558 Id_select_type_waypoint = Num_ship_types;
559 Fred_main_wnd -> init_tools();
562 void set_physics_controls()
564 physics_init(&view_physics);
565 view_physics.max_vel.x *= physics_speed / 3.0f;
566 view_physics.max_vel.y *= physics_speed / 3.0f;
567 view_physics.max_vel.z *= physics_speed / 3.0f;
568 view_physics.max_rear_vel *= physics_speed / 3.0f;
570 view_physics.max_rotvel.x *= physics_rot / 30.0f;
571 view_physics.max_rotvel.y *= physics_rot / 30.0f;
572 view_physics.max_rotvel.z *= physics_rot / 30.0f;
573 view_physics.flags |= PF_ACCELERATES | PF_SLIDE_ENABLED;
574 theApp.write_ini_file(1);
577 int create_object_on_grid(int list)
583 g3_point_to_vec_delayed(&dir, marking_box.x2, marking_box.y2);
585 rval = fvi_ray_plane(&pos, &The_grid->center, &The_grid->gmatrix.uvec, &view_pos, &dir, 0.0f);
589 obj = create_object(&pos, list);
592 FREDDoc_ptr->autosave("object create");
594 } else if (obj == -1)
595 Fred_main_wnd->MessageBox("Maximum ship limit reached. Can't add any more ships.");
601 void fix_ship_name(int ship)
606 sprintf(Ships[ship].ship_name, "U.R.A. Moron %d", i++);
607 } while (query_ship_name_duplicate(ship));
610 int create_ship(matrix *orient, vector *pos, int ship_type)
612 int obj, ship, z1, z2;
615 obj = ship_create(orient, pos, ship_type);
619 Objects[obj].phys_info.speed = 33.0f;
621 ship = Objects[obj].instance;
622 sip = &Ship_info[Ships[ship].ship_info_index];
624 if (query_ship_name_duplicate(ship))
627 z1 = Shield_sys_teams[Ships[ship].team];
628 z2 = Shield_sys_types[ship_type];
629 if (((z1 == 1) && z2) || (z2 == 1))
630 Objects[obj].flags |= OF_NO_SHIELDS;
632 z1 = Ship_info[Ships[ship].ship_info_index].species;
633 if (z1 == SPECIES_SHIVAN) {
634 Ships[ship].team = TEAM_HOSTILE;
635 Ships[ship].flags &= ~SF_CARGO_REVEALED;
638 Ships[ship].team = TEAM_FRIENDLY;
639 Ships[ship].flags |= SF_CARGO_REVEALED;
642 if ( Ships[ship].team == TEAM_FRIENDLY ) {
644 // if this ship is not a small ship, then make the orders be the default orders without
646 if ( !(sip->flags & SIF_SMALL_SHIP) ) {
647 Ships[ship].orders_accepted = ship_get_default_orders_accepted( sip );
648 Ships[ship].orders_accepted &= ~DEPART_ITEM;
652 Ships[ship].orders_accepted = 0;
655 Ai_info[Ships[ship].ai_index].kamikaze_damage = min(1000.0f, 200.0f + (sip->initial_hull_strength / 4.0f));
660 int query_ship_name_duplicate(int ship)
664 for (i=0; i<MAX_SHIPS; i++)
665 if ((i != ship) && (Ships[i].objnum != -1))
666 if (!stricmp(Ships[i].ship_name, Ships[ship].ship_name))
672 void copy_bits(int *dest, int src, int mask)
678 int dup_object(object *objp)
680 int i, j, n, inst, obj = -1;
681 ai_info *aip1, *aip2;
682 object *objp1, *objp2;
683 ship_subsys *subp1, *subp2;
691 inst = objp->instance;
692 if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) {
693 obj = create_ship(&objp->orient, &objp->pos, Ships[inst].ship_info_index);
697 n = Objects[obj].instance;
698 Ships[n].team = Ships[inst].team;
699 Ships[n].arrival_cue = dup_sexp_chain(Ships[inst].arrival_cue);
700 Ships[n].departure_cue = dup_sexp_chain(Ships[inst].departure_cue);
701 Ships[n].cargo1 = Ships[inst].cargo1;
702 Ships[n].arrival_location = Ships[inst].arrival_location;
703 Ships[n].departure_location = Ships[inst].departure_location;
704 Ships[n].arrival_delay = Ships[inst].arrival_delay;
705 Ships[n].departure_delay = Ships[inst].departure_delay;
706 Ships[n].weapons = Ships[inst].weapons;
707 Ships[n].hotkey = Ships[inst].hotkey;
709 aip1 = &Ai_info[Ships[n].ai_index];
710 aip2 = &Ai_info[Ships[inst].ai_index];
711 aip1->behavior = aip2->behavior;
712 aip1->ai_class = aip2->ai_class;
713 for (i=0; i<MAX_AI_GOALS; i++)
714 aip1->goals[i] = aip2->goals[i];
716 if ( aip2->ai_flags & AIF_KAMIKAZE )
717 aip1->ai_flags |= AIF_KAMIKAZE;
718 if ( aip2->ai_flags & AIF_NO_DYNAMIC )
719 aip2->ai_flags |= AIF_NO_DYNAMIC;
721 aip1->kamikaze_damage = aip2->kamikaze_damage;
723 objp1 = &Objects[obj];
724 objp2 = &Objects[Ships[inst].objnum];
725 objp1->phys_info.speed = objp2->phys_info.speed;
726 objp1->phys_info.fspeed = objp2->phys_info.fspeed;
727 objp1->hull_strength = objp2->hull_strength;
728 objp1->shields[0] = objp2->shields[0];
730 subp1 = GET_FIRST(&Ships[n].subsys_list);
731 subp2 = GET_FIRST(&Ships[inst].subsys_list);
732 while (subp1 != END_OF_LIST(&Ships[n].subsys_list)) {
733 Assert(subp2 != END_OF_LIST(&Ships[inst].subsys_list));
734 subp1 -> current_hits = subp2 -> current_hits;
735 subp1 = GET_NEXT(subp1);
736 subp2 = GET_NEXT(subp2);
739 for (i=0; i<Num_reinforcements; i++)
740 if (!stricmp(Reinforcements[i].name, Ships[inst].ship_name)) {
741 if (Num_reinforcements < MAX_REINFORCEMENTS) {
742 j = Num_reinforcements++;
743 strcpy(Reinforcements[j].name, Ships[n].ship_name);
744 Reinforcements[j].type = Reinforcements[i].type;
745 Reinforcements[j].uses = Reinforcements[i].uses;
751 } else if (objp->type == OBJ_WAYPOINT) {
752 obj = create_waypoint(&objp->pos, list);
753 list = Objects[obj].instance;
759 Objects[obj].pos = objp->pos;
760 Objects[obj].orient = objp->orient;
761 Objects[obj].flags |= OF_TEMP_MARKED;
765 int create_object(vector *pos, int list)
769 if (cur_model_index == Id_select_type_waypoint)
770 obj = create_waypoint(pos, list);
772 else if (cur_model_index == Id_select_type_start) {
773 if (Player_starts >= MAX_PLAYERS) {
774 Fred_main_wnd->MessageBox("Unable to create new player start point.\n"
775 "You have reached the maximum limit.", NULL, MB_OK | MB_ICONEXCLAMATION);
778 } else if (The_mission.game_type & MISSION_TYPE_SINGLE) {
779 Fred_main_wnd->MessageBox("You can't have more than one player start in\n"
780 "single player missions.\n", NULL, MB_OK | MB_ICONEXCLAMATION);
783 } else if (The_mission.game_type & MISSION_TYPE_TRAINING) {
784 Fred_main_wnd->MessageBox("You can't have more than one player start in\n"
785 "a training missions.\n", NULL, MB_OK | MB_ICONEXCLAMATION);
789 obj = create_player(Player_starts, pos, NULL, Default_player_model);
791 } else if (cur_model_index == Id_select_type_jump_node) {
792 if (Num_jump_nodes >= MAX_JUMP_NODES) {
793 Fred_main_wnd->MessageBox("Unable to create more jump nodes. You have reached the limit.", NULL, MB_OK | MB_ICONEXCLAMATION);
797 obj = jumpnode_create(pos);
800 } else if(Ship_info[cur_model_index].flags & SIF_NO_FRED){
802 } else { // creating a ship
803 obj = create_ship(NULL, pos, cur_model_index);
807 n = Objects[obj].instance;
808 Ships[n].arrival_cue = alloc_sexp("true", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1);
809 Ships[n].departure_cue = alloc_sexp("false", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1);
816 obj_merge_created_list();
822 int create_player(int num, vector *pos, matrix *orient, int type, int init)
827 type = Default_player_model;
831 Assert(Player_starts < MAX_PLAYERS);
833 obj = create_ship(orient, pos, type);
834 Objects[obj].type = OBJ_START;
836 // be sure arrival/departure cues are set
837 Ships[Objects[obj].instance].arrival_cue = Locked_sexp_true;
838 Ships[Objects[obj].instance].departure_cue = Locked_sexp_false;
839 obj_merge_created_list();
844 int query_waypoint_path_name_duplicate(int list)
848 for (i=0; i<Num_waypoint_lists; i++)
850 if (!stricmp(Waypoint_lists[i].name, Waypoint_lists[list].name))
856 void get_unique_waypoint_path_name(int list)
860 sprintf(Waypoint_lists[list].name, "Waypoint path %d", list + 1);
861 while (query_waypoint_path_name_duplicate(list)) {
862 sprintf(Waypoint_lists[list].name, "Waypoint path U%d", i++);
866 int create_waypoint(vector *pos, int list)
868 int i, obj, index = 0;
871 if (list == -1) { // find a new list to start.
872 for (list=0; list<MAX_WAYPOINT_LISTS; list++){
873 if (!Waypoint_lists[list].count) {
874 get_unique_waypoint_path_name(list);
879 index = (list & 0xffff) + 1;
883 if (list == MAX_WAYPOINT_LISTS) {
884 Fred_main_wnd->MessageBox("Unable to create new waypoint path. You\n"
885 "have reached the maximum limit.", NULL, MB_OK | MB_ICONEXCLAMATION);
889 Assert((list >= 0) && (list < MAX_WAYPOINT_LISTS)); // illegal index or out of lists.
890 if (Waypoint_lists[list].count >= MAX_WAYPOINTS_PER_LIST) {
891 Fred_main_wnd->MessageBox("Unable to create new waypoint. You have\n"
892 "reached the maximum limit on waypoints per list.", NULL, MB_OK | MB_ICONEXCLAMATION);
896 if (Waypoint_lists[list].count > index) {
897 i = Waypoint_lists[list].count;
899 Waypoint_lists[list].waypoints[i] = Waypoint_lists[list].waypoints[i - 1];
900 Waypoint_lists[list].flags[i] = Waypoint_lists[list].flags[i - 1];
905 ptr = GET_FIRST(&obj_used_list);
906 while (ptr != END_OF_LIST(&obj_used_list)) {
907 Assert(ptr->type != OBJ_NONE);
908 if (ptr->type == OBJ_WAYPOINT) {
910 if ((i / 65536 == list) && ((i & 0xffff) >= index)){
918 Waypoint_lists[list].count++;
919 Waypoint_lists[list].flags[index] = 0;
920 Waypoint_lists[list].waypoints[index] = *pos;
921 if (list >= Num_waypoint_lists){
922 Num_waypoint_lists = list + 1;
925 obj = obj_create(OBJ_WAYPOINT, -1, list * 65536 + index, NULL, pos, 0.0f, OF_RENDERS);
930 void create_new_mission()
933 *Mission_filename = 0;
934 FREDDoc_ptr->autosave("nothing");
941 player_start1 = create_player(0, &vmd_zero_vector, &vmd_identity_matrix);
950 // clean up everything we need to before we reset back to defaults.
951 if (Briefing_dialog){
952 Briefing_dialog->reset_editor();
956 mission_event_shutdown();
958 Asteroid_field.num_initial_asteroids = 0; // disable asteroid field by default.
959 Asteroid_field.speed = 0.0f;
960 vm_vec_make(&Asteroid_field.min_bound, -1000.0f, -1000.0f, -1000.0f);
961 vm_vec_make(&Asteroid_field.max_bound, 1000.0f, 1000.0f, 1000.0f);
962 vm_vec_make(&Asteroid_field.inner_min_bound, -500.0f, -500.0f, -500.0f);
963 vm_vec_make(&Asteroid_field.inner_max_bound, 500.0f, 500.0f, 500.0f);
964 Asteroid_field.has_inner_bound = 0;
965 Asteroid_field.field_type = FT_ACTIVE;
966 Asteroid_field.debris_genre = DG_ASTEROID;
967 Asteroid_field.field_debris_type[0] = -1;
968 Asteroid_field.field_debris_type[1] = -1;
969 Asteroid_field.field_debris_type[2] = -1;
971 strcpy(Mission_parse_storm_name, "none");
974 model_free_all(); // Free all existing models
977 Num_ai_dock_names = 0;
980 for (i=0; i<MAX_WINGS; i++){
981 Wings[i].wave_count = 0;
984 for (i=0; i<MAX_WAYPOINT_LISTS; i++){
985 Waypoint_lists[i].count = 0;
988 for (i=0; i<MAX_TEAM_NAMES; i++){
989 Shield_sys_teams[i] = 0;
992 for (i=0; i<MAX_SHIP_TYPES; i++){
993 Shield_sys_types[i] = 0;
996 Num_reinforcements = 0;
999 str = reg_read_string("SOFTWARE\\Microsoft\\Windows\\CurrentVersion", "RegisteredOwner", NULL);
1001 str = reg_read_string("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "RegisteredOwner", NULL);
1003 str = getenv("USERNAME");
1010 t = CTime::GetCurrentTime();
1011 strcpy(The_mission.name, "Untitled");
1012 strncpy(The_mission.author, str, NAME_LENGTH - 1);
1013 The_mission.author[NAME_LENGTH - 1] = 0;
1014 strcpy(The_mission.created, t.Format("%x at %X"));
1015 strcpy(The_mission.modified, The_mission.created);
1016 strcpy(The_mission.notes, "This is a FRED created mission\n");
1017 strcpy(The_mission.mission_desc, "Put mission description here\n");
1018 strcpy(The_mission.tour_name, "Blah");
1019 The_mission.game_type = MISSION_TYPE_SINGLE;
1020 strcpy(The_mission.pre_briefing_cutscene, "Blah");
1021 strcpy(The_mission.pre_mission_cutscene, "Blah");
1022 strcpy(The_mission.next_mission_success, "Blah");
1023 strcpy(The_mission.next_mission_partial, "Blah");
1024 strcpy(The_mission.next_mission_failure, "Blah");
1025 strcpy(The_mission.squad_name, "");
1026 strcpy(The_mission.squad_filename, "");
1027 The_mission.num_respawns = 3;
1032 // background bitmaps and suns
1034 Num_starfield_bitmaps = 0;
1036 // reset alternate name stuff
1037 for(i=0; i<MAX_SHIPS; i++){
1038 strcpy(Fred_alt_names[i], "");
1041 // set up the default ship types for all teams. For now, this is the same class
1042 // of ships for all teams
1043 for (i=0; i<MAX_TEAMS; i++) {
1045 for ( j = 0; j < MAX_SHIP_TYPES; j++ ) {
1046 if (Ship_info[j].flags & SIF_DEFAULT_PLAYER_SHIP) {
1047 Team_data[i].ship_list[count] = j;
1048 Team_data[i].ship_count[count++] = 5;
1051 Team_data[i].number_choices = count;
1053 for (j=0; j<MAX_WEAPON_TYPES; j++){
1054 if (Weapon_info[j].wi_flags & WIF_PLAYER_ALLOWED){
1055 if(Weapon_info[j].subtype == WP_LASER){
1056 Team_data[i].weaponry_pool[j] = 16;
1058 Team_data[i].weaponry_pool[j] = 500;
1061 Team_data[i].weaponry_pool[j] = 0;
1066 *Mission_text = *Mission_text_raw = EOF_CHAR;
1067 Mission_text[1] = Mission_text_raw[1] = 0;
1069 Num_waypoint_lists = 0;
1070 Num_mission_events = 0;
1074 model_free_all(); // Free all existing models
1081 // alternate ship type names
1082 mission_parse_reset_alt();
1084 strcpy(Cargo_names[0], "Nothing");
1086 set_physics_controls();
1087 Num_starfield_bitmaps = 0;
1089 Mission_palette = 1;
1090 Nebula_pitch = (int) ((float) (rand() & 0x0fff) * 360.0f / 4096.0f);
1091 Nebula_bank = (int) ((float) (rand() & 0x0fff) * 360.0f / 4096.0f);
1092 Nebula_heading = (int) ((float) (rand() & 0x0fff) * 360.0f / 4096.0f);
1094 Neb2_poof_flags = 0;
1095 strcpy(Neb2_texture_name, "");
1096 for(i=0; i<MAX_NEB2_POOFS; i++){
1097 Neb2_poof_flags |= (1<<i);
1101 The_mission.flags &= ~(MISSION_FLAG_FULLNEB);
1102 nebula_init(Nebula_filenames[Nebula_index], Nebula_pitch, Nebula_bank, Nebula_heading);
1104 char palette_filename[1024];
1105 strcpy(palette_filename, "gamepalette1-01");
1106 // sprintf( palette_filename, "gamepalette%d-%02d", 1, Mission_palette+1 );
1107 mprintf(( "Loading palette %s\n", palette_filename ));
1108 palette_load_table(palette_filename);
1110 set_modified(FALSE);
1114 int query_valid_object(int index)
1116 int obj_found = FALSE;
1119 if (index < 0 || index >= MAX_OBJECTS || Objects[index].type == OBJ_NONE)
1122 ptr = GET_FIRST(&obj_used_list);
1123 while (ptr != END_OF_LIST(&obj_used_list)) {
1124 Assert(ptr->type != OBJ_NONE);
1125 if (OBJ_INDEX(ptr) == index)
1128 ptr = GET_NEXT(ptr);
1131 Assert(obj_found); // just to make sure it's in the list like it should be.
1135 int query_valid_ship(int index)
1137 int obj_found = FALSE;
1140 if (index < 0 || index >= MAX_OBJECTS || Objects[index].type != OBJ_SHIP)
1143 ptr = GET_FIRST(&obj_used_list);
1144 while (ptr != END_OF_LIST(&obj_used_list)) {
1145 Assert(ptr->type != OBJ_NONE);
1146 if (OBJ_INDEX(ptr) == index)
1149 ptr = GET_NEXT(ptr);
1152 Assert(obj_found); // just to make sure it's in the list like it should be.
1156 int query_valid_waypoint(int index)
1158 int obj_found = FALSE;
1161 if (index < 0 || index >= MAX_OBJECTS || Objects[index].type != OBJ_WAYPOINT)
1164 ptr = GET_FIRST(&obj_used_list);
1165 while (ptr != END_OF_LIST(&obj_used_list)) {
1166 Assert(ptr->type != OBJ_NONE);
1167 if (OBJ_INDEX(ptr) == index)
1170 ptr = GET_NEXT(ptr);
1173 Assert(obj_found); // just to make sure it's in the list like it should be.
1177 // Sets the current object to whatever is specified or advances to the next object
1178 // in the list if nothing is passed.
1179 void set_cur_object_index(int obj)
1186 set_cur_indices(obj); // select the new object
1187 Update_ship = Update_wing = 1;
1188 Waypoint_editor_dialog.initialize_data(1);
1192 // changes the currently selected wing. It is assumed that cur_wing == cur_ship's wing
1193 // number. Don't call this if this won't be true, or else you'll screw things up.
1194 void set_cur_wing(int wing)
1197 /* if (cur_ship != -1)
1198 Assert(cur_wing == Ships[cur_ship].wingnum);
1199 if ((cur_object_index != -1) && (Objects[cur_object_index].type == OBJ_SHIP))
1200 Assert(cur_wing == Ships[Objects[cur_object_index].instance].wingnum);*/
1205 // sets up the various cur_* global variables related to the selecting of an object. This
1206 // is an internal function that shouldn't typically get called directly. Use set_cur_object_index() instead.
1207 void set_cur_indices(int obj)
1211 CSingleLock sync(&CS_cur_object_index);
1213 sync.Lock(); // Don't modify until it's unlocked (if it's locked elsewhere).
1214 if (query_valid_object(obj)) {
1215 cur_object_index = obj;
1216 cur_ship = cur_wing = cur_waypoint_list = cur_waypoint = -1;
1217 if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START)) {
1218 cur_ship = Objects[obj].instance;
1219 cur_wing = Ships[cur_ship].wingnum;
1221 for (i=0; i<Wings[cur_wing].wave_count; i++)
1222 if (wing_objects[cur_wing][i] == cur_object_index) {
1227 } else if (Objects[obj].type == OBJ_WAYPOINT) {
1228 cur_waypoint_list = Objects[obj].instance / 65536;
1229 cur_waypoint = Objects[obj].instance & 0xffff;
1235 if (obj == -1 || !num_objects) {
1236 cur_object_index = cur_ship = cur_wing = cur_waypoint_list = cur_waypoint = -1;
1240 if (query_valid_object(cur_object_index))
1241 ptr = Objects[cur_object_index].next;
1243 ptr = GET_FIRST(&obj_used_list);
1245 if (ptr == END_OF_LIST(&obj_used_list))
1248 Assert(ptr != END_OF_LIST(&obj_used_list));
1249 cur_object_index = OBJ_INDEX(ptr);
1250 Assert(ptr->type != OBJ_NONE);
1251 cur_ship = cur_wing = cur_waypoint_list = cur_waypoint = -1;
1252 if (ptr->type == OBJ_SHIP) {
1253 cur_ship = ptr->instance;
1254 cur_wing = Ships[cur_ship].wingnum;
1255 for (i=0; i<Wings[cur_wing].wave_count; i++)
1256 if (wing_objects[cur_wing][i] == cur_object_index) {
1261 } else if (ptr->type == OBJ_WAYPOINT) {
1262 cur_waypoint_list = ptr->instance / 65536;
1263 cur_waypoint = ptr->instance & 0xffff;
1267 int update_dialog_boxes()
1271 nprintf(("Fred routing", "updating dialog boxes\n"));
1273 // check wing first, since ships are dependent on wings, but not the reverse
1274 z = Wing_editor_dialog.update_data(0);
1276 nprintf(("Fred routing", "wing dialog save failed\n"));
1277 Wing_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
1278 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1283 z = Ship_editor_dialog.update_data(0);
1285 nprintf(("Fred routing", "ship dialog save failed\n"));
1286 Ship_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
1287 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1292 z = Waypoint_editor_dialog.update_data(0);
1294 nprintf(("Fred routing", "waypoint dialog save failed\n"));
1295 Waypoint_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
1296 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1301 update_map_window();
1305 int delete_object(int obj)
1309 Ship_editor_dialog.bypass_all++;
1310 r = common_object_delete(obj);
1311 Ship_editor_dialog.bypass_all--;
1315 int delete_object(object *ptr)
1319 Ship_editor_dialog.bypass_all++;
1320 r = common_object_delete(OBJ_INDEX(ptr));
1321 Ship_editor_dialog.bypass_all--;
1325 int delete_ship(int ship)
1329 Ship_editor_dialog.bypass_all++;
1330 r = common_object_delete(Ships[ship].objnum);
1331 Ship_editor_dialog.bypass_all--;
1335 int common_object_delete(int obj)
1337 char msg[255], *name;
1338 int i, z, r, type, num;
1341 type = Objects[obj].type;
1342 if (type == OBJ_START) {
1343 i = Objects[obj].instance;
1344 if (Player_starts < 2) { // player 1 start
1345 Fred_main_wnd->MessageBox("Must have at least 1 player starting point!",
1346 NULL, MB_OK | MB_ICONEXCLAMATION);
1352 Assert((i >= 0) && (i < MAX_SHIPS));
1353 sprintf(msg, "Player %d", i + 1);
1355 r = reference_handler(name, REF_TYPE_PLAYER, obj);
1359 if (Ships[i].wingnum >= 0) {
1360 r = delete_ship_from_wing(i);
1365 Objects[obj].type = OBJ_SHIP; // was allocated as a ship originally, so remove as such.
1366 invalidate_references(name, REF_TYPE_PLAYER);
1367 objp = GET_FIRST(&obj_used_list);
1368 while (objp != END_OF_LIST(&obj_used_list)) {
1369 // check if any ship is docked with this ship and break dock if so.
1370 if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) {
1371 num = get_ship_from_obj(objp);
1372 if (Ai_info[Ships[num].ai_index].dock_objnum == obj)
1373 Ai_info[Ships[num].ai_index].dock_objnum = -1;
1376 objp = GET_NEXT(objp);
1379 if (Player_start_shipnum == i) { // need a new single player start.
1380 objp = GET_FIRST(&obj_used_list);
1381 while (objp != END_OF_LIST(&obj_used_list)) {
1382 if (objp->type == OBJ_START) {
1383 Player_start_shipnum = objp->instance;
1387 objp = GET_NEXT(objp);
1393 } else if (type == OBJ_WAYPOINT) {
1396 list = Objects[obj].instance / 65536;
1397 i = Objects[obj].instance & 0xffff;
1398 Assert(list >= 0 && list < MAX_WAYPOINT_LISTS);
1399 count = Waypoint_lists[list].count;
1400 Assert(i >= 0 && i < count);
1402 if (Waypoint_lists[list].count == 1) {
1403 name = Waypoint_lists[list].name;
1404 r = reference_handler(name, REF_TYPE_PATH, obj);
1409 sprintf(msg, "%s:%d", Waypoint_lists[list].name, i + 1);
1411 r = reference_handler(name, REF_TYPE_WAYPOINT, obj);
1415 invalidate_references(name, REF_TYPE_WAYPOINT);
1416 objp = GET_FIRST(&obj_used_list);
1417 while (objp != END_OF_LIST(&obj_used_list)) {
1418 if ((objp->type == OBJ_WAYPOINT) && ((objp->instance / 65536) == list))
1419 if ((objp->instance & 0xffff) > i)
1422 objp = GET_NEXT(objp);
1425 while (i < count - 1) {
1426 Waypoint_lists[list].waypoints[i] = Waypoint_lists[list].waypoints[i + 1];
1430 Waypoint_lists[list].count--;
1431 if (!Waypoint_lists[list].count) {
1432 invalidate_references(Waypoint_lists[list].name, REF_TYPE_PATH);
1433 objp = GET_FIRST(&obj_used_list);
1434 while (objp != END_OF_LIST(&obj_used_list)) {
1435 if ((objp->type == OBJ_WAYPOINT) && ((objp->instance / 65536) > list))
1436 objp->instance -= 65536;
1438 objp = GET_NEXT(objp);
1441 while (list < Num_waypoint_lists - 1) {
1442 Waypoint_lists[list] = Waypoint_lists[list + 1];
1446 Num_waypoint_lists--;
1447 Waypoint_lists[list].count = 0;
1450 } else if (type == OBJ_SHIP) {
1451 name = Ships[Objects[obj].instance].ship_name;
1452 r = reference_handler(name, REF_TYPE_SHIP, obj);
1456 z = Objects[obj].instance;
1457 if (Ships[z].wingnum >= 1) {
1458 invalidate_references(name, REF_TYPE_SHIP);
1459 r = delete_ship_from_wing(z);
1463 } else if (Ships[z].wingnum >= 0) {
1464 r = delete_ship_from_wing(z);
1468 invalidate_references(name, REF_TYPE_SHIP);
1471 for (i=0; i<Num_reinforcements; i++)
1472 if (!stricmp(name, Reinforcements[i].name)) {
1473 delete_reinforcement(i);
1477 objp = GET_FIRST(&obj_used_list);
1478 while (objp != END_OF_LIST(&obj_used_list)) {
1479 // check if any ship is docked with this ship and break dock if so.
1480 if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) {
1481 num = get_ship_from_obj(objp);
1482 if (Ai_info[Ships[num].ai_index].dock_objnum == obj)
1483 Ai_info[Ships[num].ai_index].dock_objnum = -1;
1486 objp = GET_NEXT(objp);
1489 } else if (type == OBJ_POINT) {
1490 Assert(Briefing_dialog);
1491 Briefing_dialog->delete_icon(Objects[obj].instance);
1495 } else if (type == OBJ_JUMP_NODE) {
1496 i = Objects[obj].instance;
1497 objp = GET_FIRST(&obj_used_list);
1498 while (objp != END_OF_LIST(&obj_used_list)) {
1499 if ((objp->type == OBJ_JUMP_NODE) && (objp->instance > i))
1502 objp = GET_NEXT(objp);
1505 while (i < Num_jump_nodes - 1) {
1506 Jump_nodes[i] = Jump_nodes[i + 1];
1520 void delete_marked()
1525 ptr = GET_FIRST(&obj_used_list);
1526 while (ptr != END_OF_LIST(&obj_used_list)) {
1527 next = GET_NEXT(ptr);
1528 if (ptr->flags & OF_MARKED)
1529 if (delete_object(ptr) == 2) // user went to a reference, so don't get in the way.
1536 set_cur_object_index(-1);
1541 void delete_reinforcement(int num)
1545 for (i=num; i<Num_reinforcements-1; i++)
1546 Reinforcements[i] = Reinforcements[i + 1];
1548 Num_reinforcements--;
1552 // delete ship, removing it from it's wing if necessary.
1553 int delete_ship_from_wing(int ship)
1555 char name[NAME_LENGTH];
1556 int i, r, wing, end;
1558 wing = Ships[ship].wingnum;
1560 if (Wings[wing].wave_count == 1) {
1563 r = delete_wing(wing, 1);
1573 i = Wings[wing].wave_count;
1576 if (wing_objects[wing][i] == Ships[ship].objnum){
1581 Assert(i != -1); // Error, object should be in wing.
1582 if (Wings[wing].special_ship == i){
1583 Wings[wing].special_ship = 0;
1584 } else if (Wings[wing].special_ship > i) {
1585 Wings[wing].special_ship--;
1589 wing_objects[wing][i] = wing_objects[wing][end];
1590 Wings[wing].ship_index[i] = Wings[wing].ship_index[end];
1591 if (Objects[wing_objects[wing][i]].type == OBJ_SHIP) {
1592 sprintf(name, "%s %d", Wings[wing].name, i + 1);
1593 rename_ship(Wings[wing].ship_index[i], name);
1597 if (Wings[wing].threshold >= Wings[wing].wave_count){
1598 Wings[wing].threshold = Wings[wing].wave_count - 1;
1601 Wings[wing].wave_count--;
1602 if (Wings[wing].wave_count && (Wings[wing].threshold >= Wings[wing].wave_count)){
1603 Wings[wing].threshold = Wings[wing].wave_count - 1;
1612 // What does this do?
1613 void add_ship_to_wing()
1615 int org_object = cur_object_index;
1618 set_cur_object_index();
1619 if (Objects[org_object].type == OBJ_NONE) {
1620 create_object(vm_vec_make(&tvec, 10.0f, 10.0f, 10.0f));
1623 Objects[cur_object_index] = Objects[org_object];
1624 Objects[cur_object_index].pos.x += 3.0f;
1625 Objects[cur_object_index].pos.y += 3.0f;
1626 physics_init(&Objects[cur_object_index].phys_info);
1627 Objects[cur_object_index].orient = Objects[org_object].orient;
1633 // Return true if current object is valid and is in a wing.
1634 // Else return false.
1635 int query_object_in_wing(int obj)
1637 if (query_valid_object(obj)){
1638 if (Ships[Objects[obj].instance].wingnum != -1){
1646 void mark_object(int obj)
1648 Assert(query_valid_object(obj));
1649 if (!(Objects[obj].flags & OF_MARKED)) {
1650 Objects[obj].flags |= OF_MARKED; // set as marked
1653 if (cur_object_index == -1){
1654 set_cur_object_index(obj);
1656 Update_ship = Update_wing = 1;
1657 Waypoint_editor_dialog.initialize_data(1);
1661 void unmark_object(int obj)
1663 Assert(query_valid_object(obj));
1664 if (Objects[obj].flags & OF_MARKED) {
1665 Objects[obj].flags &= ~OF_MARKED;
1668 if (obj == cur_object_index) { // need to find a new index
1671 ptr = GET_FIRST(&obj_used_list);
1672 while (ptr != END_OF_LIST(&obj_used_list)) {
1673 if (ptr->flags & OF_MARKED) {
1674 set_cur_object_index(OBJ_INDEX(ptr)); // found one
1678 ptr = GET_NEXT(ptr);
1681 set_cur_object_index(-1); // can't find one; nothing is marked.
1683 Update_ship = Update_wing = 1;
1684 Waypoint_editor_dialog.initialize_data(1);
1688 // clears the marked flag of all objects (so nothing is marked)
1694 for (i=0; i<MAX_OBJECTS; i++){
1695 Objects[i].flags &= ~OF_MARKED;
1700 set_cur_object_index(-1);
1704 void clear_menu(CMenu *ptr)
1708 count = ptr->GetMenuItemCount();
1710 ptr->DeleteMenu(count, MF_BYPOSITION);
1714 void generate_wing_popup_menu(CMenu *mptr, int first_id, int state)
1716 int i, z, columns, rows, count;
1722 rows = num_wings / columns;
1726 for (i=0; i<MAX_WINGS; i++){
1727 if (Wings[i].wave_count) {
1728 z = state | MF_STRING;
1731 z |= MF_MENUBARBREAK;
1734 mptr->AppendMenu(z, first_id + i, Wings[i].name);
1738 mptr->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1741 void generate_ship_popup_menu(CMenu *mptr, int first_id, int state, int filter)
1743 int z, ship, columns, rows, count, num_ships;
1747 num_ships = ship_get_num_ships();
1751 rows = num_ships / columns;
1755 ptr = GET_FIRST(&obj_used_list);
1756 while (ptr != END_OF_LIST(&obj_used_list)) {
1757 if ((ptr->type == OBJ_SHIP) || ((ptr->type == OBJ_START) && (filter & SHIP_FILTER_PLAYERS))) {
1759 if (filter & SHIP_FILTER_FLYABLE) {
1760 if (Ship_info[Ships[get_ship_from_obj(ptr)].ship_info_index].flags & SIF_NOT_FLYABLE){
1766 z = state | MF_STRING;
1769 z |= MF_MENUBARBREAK;
1772 ship = ptr->instance;
1773 mptr->AppendMenu(z, first_id + ship, Ships[ship].ship_name);
1777 ptr = GET_NEXT(ptr);
1780 mptr->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1783 // Alternate string lookup function, taking a CString instead. The reason that it's here,
1784 // instead of parselo.cpp, is because the class CString require an include of windows.h,
1785 // which everyone wants to avoid including in any freespace header files. So..
1786 int string_lookup(CString str1, char *strlist[], int max)
1790 for (i=0; i<max; i++) {
1791 Assert(strlen(strlist[i]));
1793 if (!stricmp(str1, strlist[i])){
1801 int gray_menu_tree(CMenu *base)
1803 int i, z, count = 0;
1806 i = base->GetMenuItemCount();
1808 if ((submenu = base->GetSubMenu(i))>0) {
1809 if (gray_menu_tree(submenu)) {
1812 base->EnableMenuItem(i, MF_GRAYED | MF_BYPOSITION);
1816 z = base->GetMenuState(i, MF_BYPOSITION);
1817 if (z == MF_ENABLED){
1826 int query_initial_orders_conflict(int wing)
1835 if (query_initial_orders_empty(Wings[wing].ai_goals)){
1839 i = Wings[wing].wave_count; // wing has orders, now check ships.
1841 z = Ships[Objects[wing_objects[wing][i]].instance].ai_index;
1842 if (!query_initial_orders_empty(Ai_info[z].goals)){ // ship also has orders
1850 int query_initial_orders_empty(ai_goal *ai_goals)
1854 for (i=0; i<MAX_AI_GOALS; i++){
1855 if (ai_goals[i].ai_mode != AI_GOAL_NONE){
1863 int set_reinforcement(char *name, int state)
1865 int i, index, cur = -1;
1867 for (i=0; i<Num_reinforcements; i++){
1868 if (!stricmp(Reinforcements[i].name, name)){
1873 if (!state && (cur != -1)) {
1874 Num_reinforcements--;
1875 Reinforcements[cur] = Reinforcements[Num_reinforcements];
1877 // clear the ship/wing flag for this reinforcement
1878 index = ship_name_lookup(name);
1880 Ships[index].flags &= ~SF_REINFORCEMENT;
1882 index = wing_name_lookup(name);
1884 Wings[index].flags &= ~WF_REINFORCEMENT;
1888 Int3(); // get allender -- coudln't find ship/wing for clearing reinforcement flag
1895 if (state && (cur == -1) && (Num_reinforcements < MAX_REINFORCEMENTS)) {
1896 Assert(strlen(name) < NAME_LENGTH);
1897 strcpy(Reinforcements[Num_reinforcements].name, name);
1898 Reinforcements[Num_reinforcements].uses = 1;
1899 Reinforcements[Num_reinforcements].arrival_delay = 0;
1900 memset( Reinforcements[Num_reinforcements].no_messages, 0, MAX_REINFORCEMENT_MESSAGES * NAME_LENGTH );
1901 memset( Reinforcements[Num_reinforcements].yes_messages, 0, MAX_REINFORCEMENT_MESSAGES * NAME_LENGTH );
1902 Num_reinforcements++;
1904 // set the reinforcement flag on the ship or wing
1905 index = ship_name_lookup(name);
1907 Ships[index].flags |= SF_REINFORCEMENT;
1909 index = wing_name_lookup(name);
1911 Wings[index].flags |= WF_REINFORCEMENT;
1915 Int3(); // get allender -- coudln't find ship/wing for setting reinforcement flag
1922 // this code will take care of setting the bits for the ship/wing flags
1923 if ( state && (cur != -1) ) {
1924 // set the reinforcement flag on the ship or wing
1925 index = ship_name_lookup(name);
1927 Ships[index].flags |= SF_REINFORCEMENT;
1929 index = wing_name_lookup(name);
1931 Wings[index].flags |= WF_REINFORCEMENT;
1935 Int3(); // get allender -- coudln't find ship/wing for setting reinforcement flag
1942 int get_docking_list(int model_index)
1947 pm = model_get(model_index);
1948 Assert(pm->n_docks <= MAX_DOCKS);
1949 for (i=0; i<pm->n_docks; i++)
1950 Docking_bay_list[i] = pm->docking_bays[i].name;
1955 // DA 1/7/99 These ship names are not variables
1956 int rename_ship(int ship, char *name)
1961 Assert(strlen(name) < NAME_LENGTH);
1963 update_sexp_references(Ships[ship].ship_name, name);
1964 ai_update_goal_references(REF_TYPE_SHIP, Ships[ship].ship_name, name);
1965 for (i=0; i<Num_reinforcements; i++)
1966 if (!stricmp(Ships[ship].ship_name, Reinforcements[i].name)) {
1967 strcpy(Reinforcements[i].name, name);
1970 strcpy(Ships[ship].ship_name, name);
1971 if (ship == cur_ship)
1972 Ship_editor_dialog.m_ship_name = _T(name);
1977 int invalidate_references(char *name, int type)
1982 sprintf(new_name, "<%s>", name);
1983 update_sexp_references(name, new_name);
1984 ai_update_goal_references(type, name, new_name);
1985 for (i=0; i<Num_reinforcements; i++)
1986 if (!stricmp(name, Reinforcements[i].name)) {
1987 strcpy(Reinforcements[i].name, new_name);
1993 int internal_integrity_check()
1997 for (i=0; i<Num_mission_events; i++)
1998 verify_sexp_tree(Mission_events[i].formula);
2000 for (i=0; i<Num_goals; i++)
2001 verify_sexp_tree(Mission_goals[i].formula);
2003 for (i=0; i<MAX_WINGS; i++)
2004 if (Wings[i].wave_count) {
2005 verify_sexp_tree(Wings[i].arrival_cue);
2006 verify_sexp_tree(Wings[i].departure_cue);
2009 for (i=0; i<MAX_SHIPS; i++)
2010 if (Ships[i].objnum >= 0) {
2011 verify_sexp_tree(Ships[i].arrival_cue);
2012 verify_sexp_tree(Ships[i].departure_cue);
2013 if (Ships[i].ai_index < 0)
2015 if (Ai_info[Ships[i].ai_index].shipnum != i)
2022 void correct_marking()
2026 ptr = GET_FIRST(&obj_used_list);
2027 while (ptr != END_OF_LIST(&obj_used_list)) {
2028 if (ptr->flags & OF_MARKED) {
2029 if (ptr->flags & OF_HIDDEN)
2030 unmark_object(OBJ_INDEX(ptr));
2032 else switch (ptr->type) {
2034 if (!Show_waypoints)
2035 unmark_object(OBJ_INDEX(ptr));
2040 unmark_object(OBJ_INDEX(ptr));
2045 unmark_object(OBJ_INDEX(ptr));
2047 switch (Ships[ptr->instance].team) {
2050 unmark_object(OBJ_INDEX(ptr));
2055 unmark_object(OBJ_INDEX(ptr));
2060 unmark_object(OBJ_INDEX(ptr));
2068 ptr = GET_NEXT(ptr);
2072 // Fills a combo box with a list of all docking points of type 'type' on ship 'ship'.
2073 // Item data is the actual docking point index.
2074 void set_valid_dock_points(int ship, int type, CComboBox *box)
2076 int i, z, num, model;
2078 model = Ships[ship].modelnum;
2079 num = model_get_num_dock_points(model);
2080 for (i=0; i<num; i++)
2081 if (model_get_dock_index_type(model, i) & type) {
2082 z = box->AddString(model_get_dock_name(model, i));
2083 box->SetItemData(z, i);
2086 Assert(box->GetCount());
2089 // Given an object index, find the ship index for that object.
2090 int get_ship_from_obj(int obj)
2092 if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START))
2093 return Objects[obj].instance;
2099 int get_ship_from_obj(object *objp)
2101 if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START))
2102 return objp->instance;
2108 void ai_update_goal_references(int type, char *old_name, char *new_name)
2112 for (i=0; i<MAX_AI_INFO; i++) // loop through all Ai_info entries
2113 if (Ai_info[i].shipnum != -1) // skip if unused
2114 ai_update_goal_references(Ai_info[i].goals, type, old_name, new_name);
2116 for (i=0; i<MAX_WINGS; i++)
2117 if (Wings[i].wave_count)
2118 ai_update_goal_references(Wings[i].ai_goals, type, old_name, new_name);
2121 int query_referenced_in_ai_goals(int type, char *name)
2125 for (i=0; i<MAX_AI_INFO; i++) // loop through all Ai_info entries
2126 if (Ai_info[i].shipnum >= 0) // skip if unused
2127 if (query_referenced_in_ai_goals(Ai_info[i].goals, type, name))
2128 return Ai_info[i].shipnum | SRC_SHIP_ORDER;
2130 for (i=0; i<MAX_WINGS; i++)
2131 if (Wings[i].wave_count)
2132 if (query_referenced_in_ai_goals(Wings[i].ai_goals, type, name))
2133 return i | SRC_WING_ORDER;
2138 int advanced_stricmp(char *one, char *two)
2149 return stricmp(one, two);
2152 // returns 0: go ahead change object
2153 // 1: don't change it
2154 // 2: abort (they used cancel to go to reference)
2155 int reference_handler(char *name, int type, int obj)
2157 char msg[2048], text[128], type_name[128];
2162 sprintf(type_name, "Ship \"%s\"", name);
2166 sprintf(type_name, "Wing \"%s\"", name);
2169 case REF_TYPE_PLAYER:
2170 strcpy(type_name, name);
2173 case REF_TYPE_WAYPOINT:
2174 sprintf(type_name, "Waypoint \"%s\"", name);
2178 sprintf(type_name, "Waypoint path \"%s\"", name);
2182 Error(LOCATION, "Type unknown for object \"%s\". Let Hoffos know now!", name);
2185 r = query_referenced_in_sexp(type, name, &node);
2187 n = r & SRC_DATA_MASK;
2188 switch (r & SRC_MASK) {
2189 case SRC_SHIP_ARRIVAL:
2190 sprintf(text, "the arrival cue of ship \"%s\"", Ships[n].ship_name);
2193 case SRC_SHIP_DEPARTURE:
2194 sprintf(text, "the departure cue of ship \"%s\"", Ships[n].ship_name);
2197 case SRC_WING_ARRIVAL:
2198 sprintf(text, "the arrival cue of wing \"%s\"", Wings[n].name);
2201 case SRC_WING_DEPARTURE:
2202 sprintf(text, "the departure cue of wing \"%s\"", Wings[n].name);
2206 if (*Mission_events[n].name)
2207 sprintf(text, "event \"%s\"", Mission_events[n].name);
2209 sprintf(text, "event #%d", n);
2213 case SRC_MISSION_GOAL:
2214 if (*Mission_goals[n].name)
2215 sprintf(text, "mission goal \"%s\"", Mission_goals[n].name);
2217 sprintf(text, "mission goal #%d", n);
2221 case SRC_DEBRIEFING:
2222 sprintf(text, "debriefing #%d", n);
2226 sprintf(text, "briefing #%d", n);
2229 default: // very bad. Someone added an sexp somewhere and didn't change this.
2230 Warning(LOCATION, "\"%s\" referenced by an unknown sexp source! "
2231 "Run for the hills and let Hoffoss know right now!", name);
2237 sprintf(msg, "%s is referenced by %s (possibly more sexps).\n"
2238 "Do you want to delete it anyway?\n\n"
2239 "(click Cancel to go to the reference)", type_name, text);
2241 r = sexp_reference_handler(node, r, msg);
2255 r = query_referenced_in_ai_goals(type, name);
2257 n = r & SRC_DATA_MASK;
2258 switch (r & SRC_MASK) {
2259 case SRC_SHIP_ORDER:
2260 sprintf(text, "ship \"%s\"", Ships[n].ship_name);
2263 case SRC_WING_ORDER:
2264 sprintf(text, "wing \"%s\"", Wings[n].name);
2267 default: // very bad. Someone added an sexp somewhere and didn't change this.
2268 Error(LOCATION, "\"%s\" referenced by an unknown initial orders source! "
2269 "Run for the hills and let Hoffoss know right now!", name);
2272 sprintf(msg, "%s is referenced by the initial orders of %s (possibly \n"
2273 "more initial orders). Do you want to delete it anyway?\n\n"
2274 "(click Cancel to go to the reference)", type_name, text);
2276 r = orders_reference_handler(r, msg);
2290 if ((type != REF_TYPE_SHIP) && (type != REF_TYPE_WING))
2293 for (n=0; n<Num_reinforcements; n++)
2294 if (!stricmp(name, Reinforcements[n].name))
2297 if (n < Num_reinforcements) {
2298 sprintf(msg, "Ship \"%s\" is a reinforcement unit.\n"
2299 "Do you want to delete it anyway?", name);
2301 r = Fred_main_wnd->MessageBox(msg, NULL, MB_YESNO | MB_ICONEXCLAMATION);
2313 int orders_reference_handler(int code, char *msg)
2317 r = Fred_main_wnd->MessageBox(msg, "Warning", MB_YESNOCANCEL | MB_ICONEXCLAMATION);
2324 ShipGoalsDlg dlg_goals;
2326 n = code & SRC_DATA_MASK;
2327 switch (code & SRC_MASK) {
2328 case SRC_SHIP_ORDER:
2330 mark_object(Ships[n].objnum);
2332 dlg_goals.self_ship = n;
2333 dlg_goals.DoModal();
2334 if (!query_initial_orders_empty(Ai_info[Ships[n].ai_index].goals))
2335 if ((Ships[n].wingnum >= 0) && (query_initial_orders_conflict(Ships[n].wingnum)))
2336 Fred_main_wnd->MessageBox("This ship's wing also has initial orders", "Possible conflict");
2340 case SRC_WING_ORDER:
2344 dlg_goals.self_wing = n;
2345 dlg_goals.DoModal();
2346 if (query_initial_orders_conflict(n))
2347 Fred_main_wnd->MessageBox("One or more ships of this wing also has initial orders", "Possible conflict");
2351 default: // very bad. Someone added an sexp somewhere and didn't change this.
2352 Error(LOCATION, "Unknown initial order reference source");
2359 int sexp_reference_handler(int node, int code, char *msg)
2363 r = Fred_main_wnd->MessageBox(msg, "Warning", MB_YESNOCANCEL | MB_ICONEXCLAMATION);
2370 switch (code & SRC_MASK) {
2371 case SRC_SHIP_ARRIVAL:
2372 case SRC_SHIP_DEPARTURE:
2373 if (!Ship_editor_dialog.GetSafeHwnd())
2374 Ship_editor_dialog.Create();
2376 Ship_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
2377 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2378 Ship_editor_dialog.ShowWindow(SW_RESTORE);
2380 Ship_editor_dialog.select_sexp_node = node;
2382 mark_object(Ships[code & SRC_DATA_MASK].objnum);
2385 case SRC_WING_ARRIVAL:
2386 case SRC_WING_DEPARTURE:
2387 if (!Wing_editor_dialog.GetSafeHwnd())
2388 Wing_editor_dialog.Create();
2390 Wing_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
2391 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2392 Wing_editor_dialog.ShowWindow(SW_RESTORE);
2394 Wing_editor_dialog.select_sexp_node = node;
2396 mark_wing(code & SRC_DATA_MASK);
2400 if (Message_editor_dlg) {
2401 Fred_main_wnd->MessageBox("You must close the message editor before the event editor can be opened");
2405 if (!Event_editor_dlg) {
2406 Event_editor_dlg = new event_editor;
2407 Event_editor_dlg->select_sexp_node = node;
2408 Event_editor_dlg->Create(event_editor::IDD);
2411 Event_editor_dlg->SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2412 Event_editor_dlg->ShowWindow(SW_RESTORE);
2415 case SRC_MISSION_GOAL: {
2416 CMissionGoalsDlg dlg;
2418 dlg.select_sexp_node = node;
2423 case SRC_DEBRIEFING: {
2424 debriefing_editor_dlg dlg;
2426 dlg.select_sexp_node = node;
2431 case SRC_BRIEFING: {
2432 if (!Briefing_dialog) {
2433 Briefing_dialog = new briefing_editor_dlg;
2434 Briefing_dialog->create();
2437 Briefing_dialog->SetWindowPos(&Briefing_dialog->wndTop, 0, 0, 0, 0,
2438 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2439 Briefing_dialog->ShowWindow(SW_RESTORE);
2440 Briefing_dialog->focus_sexp(node);
2444 default: // very bad. Someone added an sexp somewhere and didn't change this.
2445 Error(LOCATION, "Unknown sexp reference source");
2452 char *object_name(int obj)
2454 static char text[80];
2457 if (!query_valid_object(obj))
2460 i = Objects[obj].instance;
2461 switch (Objects[obj].type) {
2464 return Ships[i].ship_name;
2467 sprintf(text, "%s:%d", Waypoint_lists[i / 65536].name, (i & 0xffff) + 1);
2471 return "Briefing icon";
2477 char *get_order_name(int order)
2481 if (order == AI_GOAL_NONE) // special case
2484 for (i=0; i<Ai_goal_list_size; i++)
2485 if (Ai_goal_list[i].def & order)
2486 return Ai_goal_list[i].name;
2491 void object_moved(object *ptr, int mark)
2495 if (ptr->type == OBJ_WAYPOINT)
2496 Waypoint_lists[ptr->instance / 65536].waypoints[ptr->instance & 0xffff] = ptr->pos;
2498 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) { // do we have a ship?
2499 sh1 = get_ship_from_obj(ptr);
2500 o2 = Ai_info[Ships[sh1].ai_index].dock_objnum;
2501 if (o2 >= 0) { // is it docked with something?
2502 sh2 = get_ship_from_obj(o2);
2503 if (mark || !(Objects[o2].flags & OF_MARKED) || (sh1 < sh2))
2504 dock_orient_and_approach(&Objects[o2], ptr, DOA_DOCK_STAY);
2509 // determine if all the ships in a given wing are all marked or not.
2510 int query_whole_wing_marked(int wing)
2515 if (!Wings[wing].wave_count)
2518 ptr = GET_FIRST(&obj_used_list);
2519 while (ptr != END_OF_LIST(&obj_used_list)) {
2520 if (ptr->flags & OF_MARKED)
2521 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START))
2522 if (Ships[get_ship_from_obj(ptr)].wingnum == wing)
2525 ptr = GET_NEXT(ptr);
2528 if (count == Wings[wing].wave_count)
2534 void generate_weaponry_usage_list(int *arr, int wing)
2542 i = Wings[wing].wave_count;
2544 swp = &Ships[Wings[wing].ship_index[i]].weapons;
2545 j = swp->num_primary_banks;
2547 arr[swp->primary_bank_weapons[j]]++;
2549 j = swp->num_secondary_banks;
2551 arr[swp->secondary_bank_weapons[j]] += int(ceil(swp->secondary_bank_ammo[j] * swp->secondary_bank_capacity[j] / 100 / Weapon_info[swp->secondary_bank_weapons[j]].cargo_size));
2555 void generate_weaponry_usage_list(int *arr)
2559 for (i=0; i<MAX_WEAPON_TYPES; i++)
2562 generate_weaponry_usage_list(arr, wing_name_lookup("alpha"));
2563 generate_weaponry_usage_list(arr, wing_name_lookup("beta"));
2564 generate_weaponry_usage_list(arr, wing_name_lookup("gamma"));
2567 // function which adds all current ships in the Fred mission to the passed in combo box. useful for
2568 // building up ship lists for arrival/departure targets
2569 void management_add_ships_to_combo( CComboBox *box, int flags )
2574 box->ResetContent();
2576 // add the "special" targets, i.e. any friendly, any hostile, etc.
2577 if ( flags & SHIPS_2_COMBO_SPECIAL ) {
2578 for (i=0; i<MAX_SPECIAL_ARRIVAL_ANCHORS; i++) {
2579 id = box->AddString(Special_arrival_anchor_names[i]);
2580 box->SetItemData(id, SPECIAL_ARRIVAL_ANCHORS_OFFSET + i);
2584 // either add all ships to the list, or only add ships with docking bays.
2585 if ( flags & SHIPS_2_COMBO_ALL_SHIPS ) {
2586 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
2587 if ( ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && !(objp->flags & OF_MARKED) ) {
2588 id = box->AddString(Ships[get_ship_from_obj(objp)].ship_name);
2589 box->SetItemData(id, get_ship_from_obj(objp));
2592 } else if ( flags & SHIPS_2_COMBO_DOCKING_BAY_ONLY ) {
2593 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
2594 if ( ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && !(objp->flags & OF_MARKED) ) {
2597 // determine if this ship has a docking bay
2598 pm = model_get( Ships[objp->instance].modelnum );
2600 if ( pm->ship_bay && (pm->ship_bay->num_paths > 0) ) {
2601 id = box->AddString(Ships[get_ship_from_obj(objp)].ship_name);
2602 box->SetItemData(id, get_ship_from_obj(objp));
2609 char *reg_read_string( char *section, char *name, char *default_value )
2612 DWORD dwType, dwLen;
2614 static char tmp_string_data[1024];
2617 strcpy( keyname, section );
2619 lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, // Where it is
2620 keyname, // name of key
2621 NULL, // DWORD reserved
2622 KEY_QUERY_VALUE, // Allows all changes
2623 &hKey ); // Location to store key
2625 if ( lResult != ERROR_SUCCESS ) {
2626 mprintf(( "Error opening registry key '%s'\n", keyname ));
2631 mprintf(( "No variable name passed\n" ));
2636 lResult = RegQueryValueEx( hKey, // Handle to key
2637 name, // The values name
2638 NULL, // DWORD reserved
2639 &dwType, // What kind it is
2640 (ubyte *)&tmp_string_data, // value to set
2641 &dwLen ); // How many bytes to set
2643 if ( lResult != ERROR_SUCCESS ) {
2644 mprintf(( "Error reading registry key '%s'\n", name ));
2648 default_value = tmp_string_data;
2654 return default_value;