2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Fred2/Management.cpp $
15 * This file handles the management of Objects, Ships, Wings, etc. Basically
16 * all the little structures we have that usually inter-relate that need to
17 * be handled in a standard way, and thus should be handled by a single
21 * Revision 1.3 2002/06/09 04:41:16 relnev
22 * added copyright header
24 * Revision 1.2 2002/05/07 03:16:44 theoddone33
25 * The Great Newline Fix
27 * Revision 1.1.1.1 2002/05/03 03:28:09 root
31 * 29 10/13/99 9:22a Daveb
32 * Fixed Fred jumpnode placing bug. Fixed 1024 glide tiled texture problem
33 * related to movies. Fixed launcher spawning from PXO screen.
35 * 28 9/08/99 10:01p Dave
36 * Make sure game won't run in a drive's root directory. Make sure
37 * standalone routes suqad war messages properly to the host.
39 * 27 7/23/99 2:02p Jamesa
40 * Don't require gamepalettes
42 * 26 7/15/99 3:07p Dave
43 * 32 bit detection support. Mouse coord commandline.
45 * 25 7/02/99 4:30p Dave
46 * Much more sophisticated lightning support.
48 * 24 5/20/99 6:59p Dave
49 * Added alternate type names for ships. Changed swarm missile table
52 * 23 4/26/99 8:47p Dave
53 * Made all pof related nebula stuff customizable through Fred.
55 * 22 4/16/99 2:34p Andsager
56 * Second pass on debris fields
58 * 21 4/15/99 5:00p Andsager
59 * Frist pass on Debris field
61 * 20 4/07/99 6:21p Dave
62 * Fred and Freespace support for multiple background bitmaps and suns.
63 * Fixed link errors on all subprojects. Moved encrypt_init() to
64 * cfile_init() and lcl_init(), since its safe to call twice.
66 * 19 3/31/99 9:50a Andsager
67 * Interface for generalization of asteroid field (debris field)
69 * 18 3/30/99 5:40p Dave
70 * Fixed reinforcements for TvT in multiplayer.
72 * 17 3/24/99 4:05p Dave
73 * Put in support for assigning the player to a specific squadron with a
74 * specific logo. Preliminary work for doing pos/orient checksumming in
75 * multiplayer to reduce bandwidth.
77 * 16 3/20/99 5:09p Dave
78 * Fixed release build fred warnings and unhandled exception.
80 * 15 2/23/99 7:03p Dave
81 * Rewrote a horribly mangled and evil team loadout dialog. Bugs gone.
83 * 14 2/17/99 2:11p Dave
84 * First full run of squad war. All freespace and tracker side stuff
87 * 13 2/10/99 11:11a Johnson
88 * Think I fixed the problem where the Neb2_awacs value was improperly
89 * being checked for in Fred.
91 * 12 2/07/99 8:51p Andsager
92 * Add inner bound to asteroid field. Inner bound tries to stay astroid
93 * free. Wrap when within and don't throw at ships inside.
95 * 11 1/25/99 5:03a Dave
96 * First run of stealth, AWACS and TAG missile support. New mission type
99 * 10 1/19/99 3:57p Andsager
100 * Round 2 of variables
102 * 9 12/18/98 1:14a Dave
103 * Rough 1024x768 support for Direct3D. Proper detection and usage through
106 * 8 11/06/98 10:15a Dave
108 * 7 10/29/98 9:22p Dave
109 * Removed minor bug concering externalization of campaign files.
111 * 6 10/29/98 6:49p Dave
112 * Finished up Fred support for externalizing mission and campaign files.
114 * 5 10/29/98 10:41a Dave
115 * Change the way cfile initializes exe directory.
117 * 4 10/23/98 5:07p Dave
118 * Put in beginnings of localization/externalization functionality.
120 * 3 10/22/98 6:13p Dave
121 * Added registry and localization support.
123 * 2 10/07/98 6:28p Dave
124 * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
125 * Fred. Globalized mission and campaign file extensions. Removed Silent
126 * Threat specific code.
128 * 1 10/07/98 3:02p Dave
130 * 1 10/07/98 3:00p Dave
132 * 227 6/17/98 2:05p Hoffoss
133 * Fixed bug: Shield system for team or ship type flags new reset when
134 * mission gets cleared out (new or load mission).
136 * 226 5/20/98 10:19p Allender
137 * make Fred work with NDEBUG build
139 * 225 5/17/98 1:48p Allender
140 * made number of respawns for new missions 3
142 * 224 5/01/98 12:34p John
143 * Added code to force FreeSpace to run in the same dir as exe and made
144 * all the parse error messages a little nicer.
146 * 223 4/30/98 8:23p John
147 * Fixed some bugs with Fred caused by my new cfile code.
149 * 222 4/27/98 4:07p Allender
150 * make orders_accepted not be assigned to -1 when creating new ships.
151 * Don't set the use_default_orders flag when orders are -1
153 * 221 4/25/98 6:43p Allender
154 * reset model data when initializeing new mission
156 * 220 4/17/98 1:41p Allender
157 * took out function calls in NDEBUG mode
159 * 219 4/13/98 10:59a Hoffoss
160 * Made "new mission" clear the cmd brief structure.
162 * 218 4/13/98 10:11a John
163 * Made timer functions thread safe. Made timer_init be called in all
166 * 217 4/06/98 5:37p Hoffoss
167 * Added sexp tree support to briefings in Fred.
169 * 216 4/06/98 12:55p John
170 * Upped the gamma for Fred.
172 * 215 4/03/98 11:34a John
173 * Fixed the stuff I broke in Fred from the new breifing
175 * 214 4/02/98 3:00p Johnson
176 * Fixed a bug a release build turned up.
178 * 213 3/24/98 1:36p Hoffoss
179 * Moved call to load_filter_info() to after cfile_init(), as it is
180 * dependent on that being set up.
182 * 212 3/24/98 12:42p Allender
183 * fixed a couple of minor problems with arrival targets
185 * 211 3/21/98 7:36p Lawrance
186 * Move jump nodes to own lib.
188 * 210 3/17/98 4:16p Allender
189 * minor changes to the kamikaze flag
191 * 209 3/17/98 11:55a Johnson
192 * Fixed bug where jump nodes wheren't being cleared on a new mission.
194 * 208 3/10/98 4:26p Hoffoss
195 * Changed jump node structure to include a name. Position is now taken
196 * from the object (each jump node has an associated object now).
198 * 207 3/09/98 4:30p Allender
199 * multiplayer secondary weapon changes. red-alert and cargo-known-delay
200 * sexpressions. Add time cargo revealed to ship structure
202 * 206 3/09/98 10:56a Hoffoss
203 * Added jump node objects to Fred.
205 * 205 3/06/98 5:10p Allender
206 * made time to: field in extended targetbox use support time to dock code
207 * for all docking shpis. Only display for waypoints and docking (not
208 * undocking). Small fixups to message menu -- not allowing depart when
209 * disabled. Depart is now by default ignored for all non-small ships
211 * 204 3/05/98 3:59p Hoffoss
212 * Added a bunch of new command brief stuff, and asteroid initialization
215 * 203 2/26/98 4:59p Allender
216 * groundwork for team vs team briefings. Moved weaponry pool into the
217 * Team_data structure. Added team field into the p_info structure.
218 * Allow for mutliple structures in the briefing code.
220 * 202 2/17/98 12:07p Hoffoss
221 * Changed over to using SF_CARGO_REVEALED in fred.
223 * 201 2/17/98 10:12a Hoffoss
224 * Fixed bug with sprintf() in reference_handler(). Forgot the first
225 * argument! :) Amazing it never crashed before.
227 * 200 2/13/98 11:45a Hoffoss
228 * Made all new ships created in Fred default to 33% initial speed.
237 #include "fredview.h"
238 #include "fredrender.h"
242 #include "linklist.h"
243 #include "missionparse.h"
244 #include "missionmessage.h"
245 #include "missiongoals.h"
246 #include "missionbriefcommon.h"
247 #include "management.h"
256 #include "starfield.h"
259 #include "missioncampaign.h"
261 #include "messageeditordlg.h"
262 #include "eventeditor.h"
263 #include "missiongoalsdlg.h"
264 #include "shieldsysdlg.h"
265 #include "eventmusic.h"
266 #include "debriefingeditordlg.h"
268 #include "asteroid.h"
269 #include "hudsquadmsg.h"
270 #include "jumpnode.h"
272 #include "localize.h"
273 #include "osregistry.h"
277 #include "neblightning.h"
281 #define UNKNOWN_USER "Unknown"
285 int cur_object_index = -1;
287 int cur_model_index = 0;
288 int cur_waypoint = -1;
289 int cur_waypoint_list = -1;
291 int bypass_update = 0;
292 int Default_player_model = 0;
297 char Fred_exe_dir[512] = "";
299 char Fred_alt_names[MAX_SHIPS][NAME_LENGTH+1];
301 // object numbers for ships in a wing.
302 int wing_objects[MAX_WINGS][MAX_SHIPS_PER_WING];
304 char *Docking_bay_list[MAX_DOCKS];
306 CCriticalSection CS_cur_object_index;
308 ai_goal_list Ai_goal_list[] = {
309 "Waypoints", AI_GOAL_WAYPOINTS,
310 "Waypoints once", AI_GOAL_WAYPOINTS_ONCE,
311 "Warp", AI_GOAL_WARP,
312 "Destroy subsystem", AI_GOAL_DESTROY_SUBSYSTEM,
313 "Attack", AI_GOAL_CHASE | AI_GOAL_CHASE_WING,
314 "Dock", AI_GOAL_DOCK,
315 "Undock", AI_GOAL_UNDOCK,
316 "Guard", AI_GOAL_GUARD | AI_GOAL_GUARD_WING,
317 "Attack any ship", AI_GOAL_CHASE_ANY,
318 "Disable ship", AI_GOAL_DISABLE_SHIP,
319 "Disarm ship", AI_GOAL_DISARM_SHIP,
320 "Evade ship", AI_GOAL_EVADE_SHIP,
321 "Ignore ship", AI_GOAL_IGNORE,
322 "Stay near ship", AI_GOAL_STAY_NEAR_SHIP,
323 "Keep safe distance", AI_GOAL_KEEP_SAFE_DISTANCE,
324 "Stay still", AI_GOAL_STAY_STILL,
325 "Play dead", AI_GOAL_PLAY_DEAD,
328 int Ai_goal_list_size = sizeof(Ai_goal_list) / sizeof(ai_goal_list);
330 // internal function prototypes
331 void set_cur_indices(int obj);
332 int common_object_delete(int obj);
333 int create_waypoint(vector *pos, int list);
334 int create_ship(matrix *orient, vector *pos, int ship_type);
335 int query_ship_name_duplicate(int ship);
336 char *reg_read_string( char *section, char *name, char *default_value );
338 extern int Nmodel_num;
339 extern int Nmodel_bitmap;
341 void string_copy(char *dest, CString &src, int max_len, int modify)
346 if (strcmp(src, dest))
353 strncpy(dest, src, len);
357 // converts a multiline string (one with newlines in it) into a windows format multiline
358 // string (newlines changed to '\r\n').
359 CString convert_multiline_string(char *src)
366 while ((ptr = strchr(src, '\n'))!=NULL) {
369 strncpy(buf, src, 250);
377 strncpy(buf, src, i);
392 // Converts a windows format multiline CString back into a normal multiline string.
393 void deconvert_multiline_string(char *buf, CString &str, int max_len)
399 SDL_assert(max_len > 1);
402 while ((i = str.Find("\r\n")) >= 0) {
410 str2 = str.Mid(i + 2);
421 // removed 10/29/98 by DB
422 // this was generating an extra newline - why?
423 //if (*(ptr - 1) != '\n')
428 // medal_stuff Medals[NUM_MEDALS];
430 void parse_medal_tbl()
432 int rval, num_medals;
434 if ((rval = setjmp(parse_abort)) != 0) {
435 Error(LOCATION, "Error parsing '%s'\r\nError code = %i.\r\n", "medals.tbl", rval);
441 read_file_text("medals.tbl");
445 // parse in all the rank names
447 required_string("#Medals");
448 while ( required_string_either("#End", "$Name:") ) {
449 SDL_assert ( num_medals < NUM_MEDALS);
450 required_string("$Name:");
451 stuff_string( Medals[num_medals].name, F_NAME, NULL );
452 required_string("$Bitmap:");
453 stuff_string( Medals[num_medals].bitmap, F_NAME, NULL );
454 required_string("$Num mods:");
455 stuff_int( &Medals[num_medals].num_versions);
457 // some medals are based on kill counts. When string +Num Kills: is present, we know that
458 // this medal is a badge and should be treated specially
459 Medals[num_medals].kills_needed = 0;
461 if ( optional_string("+Num Kills:") ) {
462 char buf[MULTITEXT_LENGTH + 1];
464 stuff_int( &Medals[num_medals].kills_needed );
466 required_string("$Wavefile 1:");
467 stuff_string(buf, F_NAME, NULL, MAX_FILENAME_LEN);
469 required_string("$Wavefile 2:");
470 stuff_string(buf, F_NAME, NULL, MAX_FILENAME_LEN);
472 required_string("$Promotion Text:");
473 stuff_string(buf, F_MULTITEXT, NULL);
479 required_string("#End");
481 // close localization
489 char palette_filename[1024];
491 if (!vm_init(24*1024*1024)) {
492 MessageBox( NULL, "Not enough memory to run Fred.\r\nTry closing down some other applications.\r\n", "Not Enough Memory", MB_OK );
496 srand( (unsigned) time(NULL) );
497 init_pending_messages();
499 // initialize registry stuff
500 os_init_registry_stuff(Osreg_company_name, Osreg_app_name, NULL);
504 SDL_assert(strlen(Fred_exe_dir) > 0);
507 if(cfile_init(Fred_exe_dir)){
511 // initialize localization module. Make sure this is done AFTER initialzing OS.
512 // NOTE : Fred should ALWAYS run in Enlish. Otherwise it might swap in another language
513 // when saving - which would cause inconsistencies when externalizing to tstrings.tbl via Exstr
514 // trust me on this :)
515 lcl_init(LCL_ENGLISH);
521 gr_init(GR_640, GR_SOFTWARE, 8);
524 sprintf(palette_filename, "gamepalette%d-%02d", 1, 1);
525 mprintf(("Loading palette %s\n", palette_filename));
526 palette_load_table(palette_filename);
528 Fred_font = gr_init_font("font01.vf");
532 mission_brief_common_init();
534 model_free_all(); // Free all existing models
537 parse_medal_tbl(); // get medal names for sexpression usage
541 // initialize and activate external string hash table
542 // make sure to do here so that we don't parse the table files into the hash table - waste of space
546 create_new_mission();
547 neb2_init(); // fullneb stuff
550 event_music_parse_musictbl();
552 Show_waypoints = TRUE;
553 Campaign.filename[0] = 0; // indicate initialized state
562 g3_set_view_matrix(&eye_pos, &eye_orient, 0.5f);
564 for (i=0; i<Num_ship_types; i++)
565 if (Ship_info[i].flags & SIF_DEFAULT_PLAYER_SHIP) {
566 Default_player_model = cur_model_index = i;
570 Id_select_type_start = Num_ship_types + 2;
571 Id_select_type_jump_node = Num_ship_types + 1;
572 Id_select_type_waypoint = Num_ship_types;
573 Fred_main_wnd -> init_tools();
576 void set_physics_controls()
578 physics_init(&view_physics);
579 view_physics.max_vel.x *= physics_speed / 3.0f;
580 view_physics.max_vel.y *= physics_speed / 3.0f;
581 view_physics.max_vel.z *= physics_speed / 3.0f;
582 view_physics.max_rear_vel *= physics_speed / 3.0f;
584 view_physics.max_rotvel.x *= physics_rot / 30.0f;
585 view_physics.max_rotvel.y *= physics_rot / 30.0f;
586 view_physics.max_rotvel.z *= physics_rot / 30.0f;
587 view_physics.flags |= PF_ACCELERATES | PF_SLIDE_ENABLED;
588 theApp.write_ini_file(1);
591 int create_object_on_grid(int list)
597 g3_point_to_vec_delayed(&dir, marking_box.x2, marking_box.y2);
599 rval = fvi_ray_plane(&pos, &The_grid->center, &The_grid->gmatrix.uvec, &view_pos, &dir, 0.0f);
603 obj = create_object(&pos, list);
606 FREDDoc_ptr->autosave("object create");
608 } else if (obj == -1)
609 Fred_main_wnd->MessageBox("Maximum ship limit reached. Can't add any more ships.");
615 void fix_ship_name(int ship)
620 sprintf(Ships[ship].ship_name, "U.R.A. Moron %d", i++);
621 } while (query_ship_name_duplicate(ship));
624 int create_ship(matrix *orient, vector *pos, int ship_type)
626 int obj, ship, z1, z2;
629 obj = ship_create(orient, pos, ship_type);
633 Objects[obj].phys_info.speed = 33.0f;
635 ship = Objects[obj].instance;
636 sip = &Ship_info[Ships[ship].ship_info_index];
638 if (query_ship_name_duplicate(ship))
641 z1 = Shield_sys_teams[Ships[ship].team];
642 z2 = Shield_sys_types[ship_type];
643 if (((z1 == 1) && z2) || (z2 == 1))
644 Objects[obj].flags |= OF_NO_SHIELDS;
646 z1 = Ship_info[Ships[ship].ship_info_index].species;
647 if (z1 == SPECIES_SHIVAN) {
648 Ships[ship].team = TEAM_HOSTILE;
649 Ships[ship].flags &= ~SF_CARGO_REVEALED;
652 Ships[ship].team = TEAM_FRIENDLY;
653 Ships[ship].flags |= SF_CARGO_REVEALED;
656 if ( Ships[ship].team == TEAM_FRIENDLY ) {
658 // if this ship is not a small ship, then make the orders be the default orders without
660 if ( !(sip->flags & SIF_SMALL_SHIP) ) {
661 Ships[ship].orders_accepted = ship_get_default_orders_accepted( sip );
662 Ships[ship].orders_accepted &= ~DEPART_ITEM;
666 Ships[ship].orders_accepted = 0;
669 Ai_info[Ships[ship].ai_index].kamikaze_damage = min(1000.0f, 200.0f + (sip->initial_hull_strength / 4.0f));
674 int query_ship_name_duplicate(int ship)
678 for (i=0; i<MAX_SHIPS; i++)
679 if ((i != ship) && (Ships[i].objnum != -1))
680 if (!stricmp(Ships[i].ship_name, Ships[ship].ship_name))
686 void copy_bits(int *dest, int src, int mask)
692 int dup_object(object *objp)
694 int i, j, n, inst, obj = -1;
695 ai_info *aip1, *aip2;
696 object *objp1, *objp2;
697 ship_subsys *subp1, *subp2;
705 inst = objp->instance;
706 if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) {
707 obj = create_ship(&objp->orient, &objp->pos, Ships[inst].ship_info_index);
711 n = Objects[obj].instance;
712 Ships[n].team = Ships[inst].team;
713 Ships[n].arrival_cue = dup_sexp_chain(Ships[inst].arrival_cue);
714 Ships[n].departure_cue = dup_sexp_chain(Ships[inst].departure_cue);
715 Ships[n].cargo1 = Ships[inst].cargo1;
716 Ships[n].arrival_location = Ships[inst].arrival_location;
717 Ships[n].departure_location = Ships[inst].departure_location;
718 Ships[n].arrival_delay = Ships[inst].arrival_delay;
719 Ships[n].departure_delay = Ships[inst].departure_delay;
720 Ships[n].weapons = Ships[inst].weapons;
721 Ships[n].hotkey = Ships[inst].hotkey;
723 aip1 = &Ai_info[Ships[n].ai_index];
724 aip2 = &Ai_info[Ships[inst].ai_index];
725 aip1->behavior = aip2->behavior;
726 aip1->ai_class = aip2->ai_class;
727 for (i=0; i<MAX_AI_GOALS; i++)
728 aip1->goals[i] = aip2->goals[i];
730 if ( aip2->ai_flags & AIF_KAMIKAZE )
731 aip1->ai_flags |= AIF_KAMIKAZE;
732 if ( aip2->ai_flags & AIF_NO_DYNAMIC )
733 aip2->ai_flags |= AIF_NO_DYNAMIC;
735 aip1->kamikaze_damage = aip2->kamikaze_damage;
737 objp1 = &Objects[obj];
738 objp2 = &Objects[Ships[inst].objnum];
739 objp1->phys_info.speed = objp2->phys_info.speed;
740 objp1->phys_info.fspeed = objp2->phys_info.fspeed;
741 objp1->hull_strength = objp2->hull_strength;
742 objp1->shields[0] = objp2->shields[0];
744 subp1 = GET_FIRST(&Ships[n].subsys_list);
745 subp2 = GET_FIRST(&Ships[inst].subsys_list);
746 while (subp1 != END_OF_LIST(&Ships[n].subsys_list)) {
747 SDL_assert(subp2 != END_OF_LIST(&Ships[inst].subsys_list));
748 subp1 -> current_hits = subp2 -> current_hits;
749 subp1 = GET_NEXT(subp1);
750 subp2 = GET_NEXT(subp2);
753 for (i=0; i<Num_reinforcements; i++)
754 if (!stricmp(Reinforcements[i].name, Ships[inst].ship_name)) {
755 if (Num_reinforcements < MAX_REINFORCEMENTS) {
756 j = Num_reinforcements++;
757 strcpy(Reinforcements[j].name, Ships[n].ship_name);
758 Reinforcements[j].type = Reinforcements[i].type;
759 Reinforcements[j].uses = Reinforcements[i].uses;
765 } else if (objp->type == OBJ_WAYPOINT) {
766 obj = create_waypoint(&objp->pos, list);
767 list = Objects[obj].instance;
773 Objects[obj].pos = objp->pos;
774 Objects[obj].orient = objp->orient;
775 Objects[obj].flags |= OF_TEMP_MARKED;
779 int create_object(vector *pos, int list)
783 if (cur_model_index == Id_select_type_waypoint)
784 obj = create_waypoint(pos, list);
786 else if (cur_model_index == Id_select_type_start) {
787 if (Player_starts >= MAX_PLAYERS) {
788 Fred_main_wnd->MessageBox("Unable to create new player start point.\n"
789 "You have reached the maximum limit.", NULL, MB_OK | MB_ICONEXCLAMATION);
792 } else if (The_mission.game_type & MISSION_TYPE_SINGLE) {
793 Fred_main_wnd->MessageBox("You can't have more than one player start in\n"
794 "single player missions.\n", NULL, MB_OK | MB_ICONEXCLAMATION);
797 } else if (The_mission.game_type & MISSION_TYPE_TRAINING) {
798 Fred_main_wnd->MessageBox("You can't have more than one player start in\n"
799 "a training missions.\n", NULL, MB_OK | MB_ICONEXCLAMATION);
803 obj = create_player(Player_starts, pos, NULL, Default_player_model);
805 } else if (cur_model_index == Id_select_type_jump_node) {
806 if (Num_jump_nodes >= MAX_JUMP_NODES) {
807 Fred_main_wnd->MessageBox("Unable to create more jump nodes. You have reached the limit.", NULL, MB_OK | MB_ICONEXCLAMATION);
811 obj = jumpnode_create(pos);
814 } else if(Ship_info[cur_model_index].flags & SIF_NO_FRED){
816 } else { // creating a ship
817 obj = create_ship(NULL, pos, cur_model_index);
821 n = Objects[obj].instance;
822 Ships[n].arrival_cue = alloc_sexp("true", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1);
823 Ships[n].departure_cue = alloc_sexp("false", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1);
830 obj_merge_created_list();
836 int create_player(int num, vector *pos, matrix *orient, int type, int init)
841 type = Default_player_model;
844 SDL_assert(type >= 0);
845 SDL_assert(Player_starts < MAX_PLAYERS);
847 obj = create_ship(orient, pos, type);
848 Objects[obj].type = OBJ_START;
850 // be sure arrival/departure cues are set
851 Ships[Objects[obj].instance].arrival_cue = Locked_sexp_true;
852 Ships[Objects[obj].instance].departure_cue = Locked_sexp_false;
853 obj_merge_created_list();
858 int query_waypoint_path_name_duplicate(int list)
862 for (i=0; i<Num_waypoint_lists; i++)
864 if (!stricmp(Waypoint_lists[i].name, Waypoint_lists[list].name))
870 void get_unique_waypoint_path_name(int list)
874 sprintf(Waypoint_lists[list].name, "Waypoint path %d", list + 1);
875 while (query_waypoint_path_name_duplicate(list)) {
876 sprintf(Waypoint_lists[list].name, "Waypoint path U%d", i++);
880 int create_waypoint(vector *pos, int list)
882 int i, obj, index = 0;
885 if (list == -1) { // find a new list to start.
886 for (list=0; list<MAX_WAYPOINT_LISTS; list++){
887 if (!Waypoint_lists[list].count) {
888 get_unique_waypoint_path_name(list);
893 index = (list & 0xffff) + 1;
897 if (list == MAX_WAYPOINT_LISTS) {
898 Fred_main_wnd->MessageBox("Unable to create new waypoint path. You\n"
899 "have reached the maximum limit.", NULL, MB_OK | MB_ICONEXCLAMATION);
903 SDL_assert((list >= 0) && (list < MAX_WAYPOINT_LISTS)); // illegal index or out of lists.
904 if (Waypoint_lists[list].count >= MAX_WAYPOINTS_PER_LIST) {
905 Fred_main_wnd->MessageBox("Unable to create new waypoint. You have\n"
906 "reached the maximum limit on waypoints per list.", NULL, MB_OK | MB_ICONEXCLAMATION);
910 if (Waypoint_lists[list].count > index) {
911 i = Waypoint_lists[list].count;
913 Waypoint_lists[list].waypoints[i] = Waypoint_lists[list].waypoints[i - 1];
914 Waypoint_lists[list].flags[i] = Waypoint_lists[list].flags[i - 1];
919 ptr = GET_FIRST(&obj_used_list);
920 while (ptr != END_OF_LIST(&obj_used_list)) {
921 SDL_assert(ptr->type != OBJ_NONE);
922 if (ptr->type == OBJ_WAYPOINT) {
924 if ((i / 65536 == list) && ((i & 0xffff) >= index)){
932 Waypoint_lists[list].count++;
933 Waypoint_lists[list].flags[index] = 0;
934 Waypoint_lists[list].waypoints[index] = *pos;
935 if (list >= Num_waypoint_lists){
936 Num_waypoint_lists = list + 1;
939 obj = obj_create(OBJ_WAYPOINT, -1, list * 65536 + index, NULL, pos, 0.0f, OF_RENDERS);
944 void create_new_mission()
947 *Mission_filename = 0;
948 FREDDoc_ptr->autosave("nothing");
955 player_start1 = create_player(0, &vmd_zero_vector, &vmd_identity_matrix);
964 // clean up everything we need to before we reset back to defaults.
965 if (Briefing_dialog){
966 Briefing_dialog->reset_editor();
970 mission_event_shutdown();
972 Asteroid_field.num_initial_asteroids = 0; // disable asteroid field by default.
973 Asteroid_field.speed = 0.0f;
974 vm_vec_make(&Asteroid_field.min_bound, -1000.0f, -1000.0f, -1000.0f);
975 vm_vec_make(&Asteroid_field.max_bound, 1000.0f, 1000.0f, 1000.0f);
976 vm_vec_make(&Asteroid_field.inner_min_bound, -500.0f, -500.0f, -500.0f);
977 vm_vec_make(&Asteroid_field.inner_max_bound, 500.0f, 500.0f, 500.0f);
978 Asteroid_field.has_inner_bound = 0;
979 Asteroid_field.field_type = FT_ACTIVE;
980 Asteroid_field.debris_genre = DG_ASTEROID;
981 Asteroid_field.field_debris_type[0] = -1;
982 Asteroid_field.field_debris_type[1] = -1;
983 Asteroid_field.field_debris_type[2] = -1;
985 strcpy(Mission_parse_storm_name, "none");
988 model_free_all(); // Free all existing models
991 Num_ai_dock_names = 0;
994 for (i=0; i<MAX_WINGS; i++){
995 Wings[i].wave_count = 0;
998 for (i=0; i<MAX_WAYPOINT_LISTS; i++){
999 Waypoint_lists[i].count = 0;
1002 for (i=0; i<MAX_TEAM_NAMES; i++){
1003 Shield_sys_teams[i] = 0;
1006 for (i=0; i<MAX_SHIP_TYPES; i++){
1007 Shield_sys_types[i] = 0;
1010 Num_reinforcements = 0;
1011 set_cur_indices(-1);
1013 str = reg_read_string("SOFTWARE\\Microsoft\\Windows\\CurrentVersion", "RegisteredOwner", NULL);
1015 str = reg_read_string("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "RegisteredOwner", NULL);
1017 str = getenv("USERNAME");
1024 t = CTime::GetCurrentTime();
1025 strcpy(The_mission.name, "Untitled");
1026 strncpy(The_mission.author, str, NAME_LENGTH - 1);
1027 The_mission.author[NAME_LENGTH - 1] = 0;
1028 strcpy(The_mission.created, t.Format("%x at %X"));
1029 strcpy(The_mission.modified, The_mission.created);
1030 strcpy(The_mission.notes, "This is a FRED created mission\n");
1031 strcpy(The_mission.mission_desc, "Put mission description here\n");
1032 strcpy(The_mission.tour_name, "Blah");
1033 The_mission.game_type = MISSION_TYPE_SINGLE;
1034 strcpy(The_mission.pre_briefing_cutscene, "Blah");
1035 strcpy(The_mission.pre_mission_cutscene, "Blah");
1036 strcpy(The_mission.next_mission_success, "Blah");
1037 strcpy(The_mission.next_mission_partial, "Blah");
1038 strcpy(The_mission.next_mission_failure, "Blah");
1039 strcpy(The_mission.squad_name, "");
1040 strcpy(The_mission.squad_filename, "");
1041 The_mission.num_respawns = 3;
1046 // background bitmaps and suns
1048 Num_starfield_bitmaps = 0;
1050 // reset alternate name stuff
1051 for(i=0; i<MAX_SHIPS; i++){
1052 strcpy(Fred_alt_names[i], "");
1055 // set up the default ship types for all teams. For now, this is the same class
1056 // of ships for all teams
1057 for (i=0; i<MAX_TEAMS; i++) {
1059 for ( j = 0; j < MAX_SHIP_TYPES; j++ ) {
1060 if (Ship_info[j].flags & SIF_DEFAULT_PLAYER_SHIP) {
1061 Team_data[i].ship_list[count] = j;
1062 Team_data[i].ship_count[count++] = 5;
1065 Team_data[i].number_choices = count;
1067 for (j=0; j<MAX_WEAPON_TYPES; j++){
1068 if (Weapon_info[j].wi_flags & WIF_PLAYER_ALLOWED){
1069 if(Weapon_info[j].subtype == WP_LASER){
1070 Team_data[i].weaponry_pool[j] = 16;
1072 Team_data[i].weaponry_pool[j] = 500;
1075 Team_data[i].weaponry_pool[j] = 0;
1080 *Mission_text = *Mission_text_raw = EOF_CHAR;
1081 Mission_text[1] = Mission_text_raw[1] = 0;
1083 Num_waypoint_lists = 0;
1084 Num_mission_events = 0;
1088 model_free_all(); // Free all existing models
1095 // alternate ship type names
1096 mission_parse_reset_alt();
1098 strcpy(Cargo_names[0], "Nothing");
1100 set_physics_controls();
1101 Num_starfield_bitmaps = 0;
1103 Mission_palette = 1;
1104 Nebula_pitch = (int) ((float) (rand() & 0x0fff) * 360.0f / 4096.0f);
1105 Nebula_bank = (int) ((float) (rand() & 0x0fff) * 360.0f / 4096.0f);
1106 Nebula_heading = (int) ((float) (rand() & 0x0fff) * 360.0f / 4096.0f);
1108 Neb2_poof_flags = 0;
1109 strcpy(Neb2_texture_name, "");
1110 for(i=0; i<MAX_NEB2_POOFS; i++){
1111 Neb2_poof_flags |= (1<<i);
1115 The_mission.flags &= ~(MISSION_FLAG_FULLNEB);
1116 nebula_init(Nebula_filenames[Nebula_index], Nebula_pitch, Nebula_bank, Nebula_heading);
1118 char palette_filename[1024];
1119 strcpy(palette_filename, "gamepalette1-01");
1120 // sprintf( palette_filename, "gamepalette%d-%02d", 1, Mission_palette+1 );
1121 mprintf(( "Loading palette %s\n", palette_filename ));
1122 palette_load_table(palette_filename);
1124 set_modified(FALSE);
1128 int query_valid_object(int index)
1130 int obj_found = FALSE;
1133 if (index < 0 || index >= MAX_OBJECTS || Objects[index].type == OBJ_NONE)
1136 ptr = GET_FIRST(&obj_used_list);
1137 while (ptr != END_OF_LIST(&obj_used_list)) {
1138 SDL_assert(ptr->type != OBJ_NONE);
1139 if (OBJ_INDEX(ptr) == index)
1142 ptr = GET_NEXT(ptr);
1145 SDL_assert(obj_found); // just to make sure it's in the list like it should be.
1149 int query_valid_ship(int index)
1151 int obj_found = FALSE;
1154 if (index < 0 || index >= MAX_OBJECTS || Objects[index].type != OBJ_SHIP)
1157 ptr = GET_FIRST(&obj_used_list);
1158 while (ptr != END_OF_LIST(&obj_used_list)) {
1159 SDL_assert(ptr->type != OBJ_NONE);
1160 if (OBJ_INDEX(ptr) == index)
1163 ptr = GET_NEXT(ptr);
1166 SDL_assert(obj_found); // just to make sure it's in the list like it should be.
1170 int query_valid_waypoint(int index)
1172 int obj_found = FALSE;
1175 if (index < 0 || index >= MAX_OBJECTS || Objects[index].type != OBJ_WAYPOINT)
1178 ptr = GET_FIRST(&obj_used_list);
1179 while (ptr != END_OF_LIST(&obj_used_list)) {
1180 SDL_assert(ptr->type != OBJ_NONE);
1181 if (OBJ_INDEX(ptr) == index)
1184 ptr = GET_NEXT(ptr);
1187 SDL_assert(obj_found); // just to make sure it's in the list like it should be.
1191 // Sets the current object to whatever is specified or advances to the next object
1192 // in the list if nothing is passed.
1193 void set_cur_object_index(int obj)
1200 set_cur_indices(obj); // select the new object
1201 Update_ship = Update_wing = 1;
1202 Waypoint_editor_dialog.initialize_data(1);
1206 // changes the currently selected wing. It is assumed that cur_wing == cur_ship's wing
1207 // number. Don't call this if this won't be true, or else you'll screw things up.
1208 void set_cur_wing(int wing)
1211 /* if (cur_ship != -1)
1212 SDL_assert(cur_wing == Ships[cur_ship].wingnum);
1213 if ((cur_object_index != -1) && (Objects[cur_object_index].type == OBJ_SHIP))
1214 SDL_assert(cur_wing == Ships[Objects[cur_object_index].instance].wingnum);*/
1219 // sets up the various cur_* global variables related to the selecting of an object. This
1220 // is an internal function that shouldn't typically get called directly. Use set_cur_object_index() instead.
1221 void set_cur_indices(int obj)
1225 CSingleLock sync(&CS_cur_object_index);
1227 sync.Lock(); // Don't modify until it's unlocked (if it's locked elsewhere).
1228 if (query_valid_object(obj)) {
1229 cur_object_index = obj;
1230 cur_ship = cur_wing = cur_waypoint_list = cur_waypoint = -1;
1231 if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START)) {
1232 cur_ship = Objects[obj].instance;
1233 cur_wing = Ships[cur_ship].wingnum;
1235 for (i=0; i<Wings[cur_wing].wave_count; i++)
1236 if (wing_objects[cur_wing][i] == cur_object_index) {
1241 } else if (Objects[obj].type == OBJ_WAYPOINT) {
1242 cur_waypoint_list = Objects[obj].instance / 65536;
1243 cur_waypoint = Objects[obj].instance & 0xffff;
1249 if (obj == -1 || !num_objects) {
1250 cur_object_index = cur_ship = cur_wing = cur_waypoint_list = cur_waypoint = -1;
1254 if (query_valid_object(cur_object_index))
1255 ptr = Objects[cur_object_index].next;
1257 ptr = GET_FIRST(&obj_used_list);
1259 if (ptr == END_OF_LIST(&obj_used_list))
1262 SDL_assert(ptr != END_OF_LIST(&obj_used_list));
1263 cur_object_index = OBJ_INDEX(ptr);
1264 SDL_assert(ptr->type != OBJ_NONE);
1265 cur_ship = cur_wing = cur_waypoint_list = cur_waypoint = -1;
1266 if (ptr->type == OBJ_SHIP) {
1267 cur_ship = ptr->instance;
1268 cur_wing = Ships[cur_ship].wingnum;
1269 for (i=0; i<Wings[cur_wing].wave_count; i++)
1270 if (wing_objects[cur_wing][i] == cur_object_index) {
1275 } else if (ptr->type == OBJ_WAYPOINT) {
1276 cur_waypoint_list = ptr->instance / 65536;
1277 cur_waypoint = ptr->instance & 0xffff;
1281 int update_dialog_boxes()
1285 nprintf(("Fred routing", "updating dialog boxes\n"));
1287 // check wing first, since ships are dependent on wings, but not the reverse
1288 z = Wing_editor_dialog.update_data(0);
1290 nprintf(("Fred routing", "wing dialog save failed\n"));
1291 Wing_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
1292 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1297 z = Ship_editor_dialog.update_data(0);
1299 nprintf(("Fred routing", "ship dialog save failed\n"));
1300 Ship_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
1301 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1306 z = Waypoint_editor_dialog.update_data(0);
1308 nprintf(("Fred routing", "waypoint dialog save failed\n"));
1309 Waypoint_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
1310 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1315 update_map_window();
1319 int delete_object(int obj)
1323 Ship_editor_dialog.bypass_all++;
1324 r = common_object_delete(obj);
1325 Ship_editor_dialog.bypass_all--;
1329 int delete_object(object *ptr)
1333 Ship_editor_dialog.bypass_all++;
1334 r = common_object_delete(OBJ_INDEX(ptr));
1335 Ship_editor_dialog.bypass_all--;
1339 int delete_ship(int ship)
1343 Ship_editor_dialog.bypass_all++;
1344 r = common_object_delete(Ships[ship].objnum);
1345 Ship_editor_dialog.bypass_all--;
1349 int common_object_delete(int obj)
1351 char msg[255], *name;
1352 int i, z, r, type, num;
1355 type = Objects[obj].type;
1356 if (type == OBJ_START) {
1357 i = Objects[obj].instance;
1358 if (Player_starts < 2) { // player 1 start
1359 Fred_main_wnd->MessageBox("Must have at least 1 player starting point!",
1360 NULL, MB_OK | MB_ICONEXCLAMATION);
1366 SDL_assert((i >= 0) && (i < MAX_SHIPS));
1367 sprintf(msg, "Player %d", i + 1);
1369 r = reference_handler(name, REF_TYPE_PLAYER, obj);
1373 if (Ships[i].wingnum >= 0) {
1374 r = delete_ship_from_wing(i);
1379 Objects[obj].type = OBJ_SHIP; // was allocated as a ship originally, so remove as such.
1380 invalidate_references(name, REF_TYPE_PLAYER);
1381 objp = GET_FIRST(&obj_used_list);
1382 while (objp != END_OF_LIST(&obj_used_list)) {
1383 // check if any ship is docked with this ship and break dock if so.
1384 if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) {
1385 num = get_ship_from_obj(objp);
1386 if (Ai_info[Ships[num].ai_index].dock_objnum == obj)
1387 Ai_info[Ships[num].ai_index].dock_objnum = -1;
1390 objp = GET_NEXT(objp);
1393 if (Player_start_shipnum == i) { // need a new single player start.
1394 objp = GET_FIRST(&obj_used_list);
1395 while (objp != END_OF_LIST(&obj_used_list)) {
1396 if (objp->type == OBJ_START) {
1397 Player_start_shipnum = objp->instance;
1401 objp = GET_NEXT(objp);
1407 } else if (type == OBJ_WAYPOINT) {
1410 list = Objects[obj].instance / 65536;
1411 i = Objects[obj].instance & 0xffff;
1412 SDL_assert(list >= 0 && list < MAX_WAYPOINT_LISTS);
1413 count = Waypoint_lists[list].count;
1414 SDL_assert(i >= 0 && i < count);
1416 if (Waypoint_lists[list].count == 1) {
1417 name = Waypoint_lists[list].name;
1418 r = reference_handler(name, REF_TYPE_PATH, obj);
1423 sprintf(msg, "%s:%d", Waypoint_lists[list].name, i + 1);
1425 r = reference_handler(name, REF_TYPE_WAYPOINT, obj);
1429 invalidate_references(name, REF_TYPE_WAYPOINT);
1430 objp = GET_FIRST(&obj_used_list);
1431 while (objp != END_OF_LIST(&obj_used_list)) {
1432 if ((objp->type == OBJ_WAYPOINT) && ((objp->instance / 65536) == list))
1433 if ((objp->instance & 0xffff) > i)
1436 objp = GET_NEXT(objp);
1439 while (i < count - 1) {
1440 Waypoint_lists[list].waypoints[i] = Waypoint_lists[list].waypoints[i + 1];
1444 Waypoint_lists[list].count--;
1445 if (!Waypoint_lists[list].count) {
1446 invalidate_references(Waypoint_lists[list].name, REF_TYPE_PATH);
1447 objp = GET_FIRST(&obj_used_list);
1448 while (objp != END_OF_LIST(&obj_used_list)) {
1449 if ((objp->type == OBJ_WAYPOINT) && ((objp->instance / 65536) > list))
1450 objp->instance -= 65536;
1452 objp = GET_NEXT(objp);
1455 while (list < Num_waypoint_lists - 1) {
1456 Waypoint_lists[list] = Waypoint_lists[list + 1];
1460 Num_waypoint_lists--;
1461 Waypoint_lists[list].count = 0;
1464 } else if (type == OBJ_SHIP) {
1465 name = Ships[Objects[obj].instance].ship_name;
1466 r = reference_handler(name, REF_TYPE_SHIP, obj);
1470 z = Objects[obj].instance;
1471 if (Ships[z].wingnum >= 1) {
1472 invalidate_references(name, REF_TYPE_SHIP);
1473 r = delete_ship_from_wing(z);
1477 } else if (Ships[z].wingnum >= 0) {
1478 r = delete_ship_from_wing(z);
1482 invalidate_references(name, REF_TYPE_SHIP);
1485 for (i=0; i<Num_reinforcements; i++)
1486 if (!stricmp(name, Reinforcements[i].name)) {
1487 delete_reinforcement(i);
1491 objp = GET_FIRST(&obj_used_list);
1492 while (objp != END_OF_LIST(&obj_used_list)) {
1493 // check if any ship is docked with this ship and break dock if so.
1494 if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) {
1495 num = get_ship_from_obj(objp);
1496 if (Ai_info[Ships[num].ai_index].dock_objnum == obj)
1497 Ai_info[Ships[num].ai_index].dock_objnum = -1;
1500 objp = GET_NEXT(objp);
1503 } else if (type == OBJ_POINT) {
1504 SDL_assert(Briefing_dialog);
1505 Briefing_dialog->delete_icon(Objects[obj].instance);
1509 } else if (type == OBJ_JUMP_NODE) {
1510 i = Objects[obj].instance;
1511 objp = GET_FIRST(&obj_used_list);
1512 while (objp != END_OF_LIST(&obj_used_list)) {
1513 if ((objp->type == OBJ_JUMP_NODE) && (objp->instance > i))
1516 objp = GET_NEXT(objp);
1519 while (i < Num_jump_nodes - 1) {
1520 Jump_nodes[i] = Jump_nodes[i + 1];
1534 void delete_marked()
1539 ptr = GET_FIRST(&obj_used_list);
1540 while (ptr != END_OF_LIST(&obj_used_list)) {
1541 next = GET_NEXT(ptr);
1542 if (ptr->flags & OF_MARKED)
1543 if (delete_object(ptr) == 2) // user went to a reference, so don't get in the way.
1550 set_cur_object_index(-1);
1555 void delete_reinforcement(int num)
1559 for (i=num; i<Num_reinforcements-1; i++)
1560 Reinforcements[i] = Reinforcements[i + 1];
1562 Num_reinforcements--;
1566 // delete ship, removing it from it's wing if necessary.
1567 int delete_ship_from_wing(int ship)
1569 char name[NAME_LENGTH];
1570 int i, r, wing, end;
1572 wing = Ships[ship].wingnum;
1574 if (Wings[wing].wave_count == 1) {
1577 r = delete_wing(wing, 1);
1587 i = Wings[wing].wave_count;
1590 if (wing_objects[wing][i] == Ships[ship].objnum){
1595 SDL_assert(i != -1); // Error, object should be in wing.
1596 if (Wings[wing].special_ship == i){
1597 Wings[wing].special_ship = 0;
1598 } else if (Wings[wing].special_ship > i) {
1599 Wings[wing].special_ship--;
1603 wing_objects[wing][i] = wing_objects[wing][end];
1604 Wings[wing].ship_index[i] = Wings[wing].ship_index[end];
1605 if (Objects[wing_objects[wing][i]].type == OBJ_SHIP) {
1606 sprintf(name, "%s %d", Wings[wing].name, i + 1);
1607 rename_ship(Wings[wing].ship_index[i], name);
1611 if (Wings[wing].threshold >= Wings[wing].wave_count){
1612 Wings[wing].threshold = Wings[wing].wave_count - 1;
1615 Wings[wing].wave_count--;
1616 if (Wings[wing].wave_count && (Wings[wing].threshold >= Wings[wing].wave_count)){
1617 Wings[wing].threshold = Wings[wing].wave_count - 1;
1626 // What does this do?
1627 void add_ship_to_wing()
1629 int org_object = cur_object_index;
1632 set_cur_object_index();
1633 if (Objects[org_object].type == OBJ_NONE) {
1634 create_object(vm_vec_make(&tvec, 10.0f, 10.0f, 10.0f));
1637 Objects[cur_object_index] = Objects[org_object];
1638 Objects[cur_object_index].pos.x += 3.0f;
1639 Objects[cur_object_index].pos.y += 3.0f;
1640 physics_init(&Objects[cur_object_index].phys_info);
1641 Objects[cur_object_index].orient = Objects[org_object].orient;
1647 // Return true if current object is valid and is in a wing.
1648 // Else return false.
1649 int query_object_in_wing(int obj)
1651 if (query_valid_object(obj)){
1652 if (Ships[Objects[obj].instance].wingnum != -1){
1660 void mark_object(int obj)
1662 SDL_assert(query_valid_object(obj));
1663 if (!(Objects[obj].flags & OF_MARKED)) {
1664 Objects[obj].flags |= OF_MARKED; // set as marked
1667 if (cur_object_index == -1){
1668 set_cur_object_index(obj);
1670 Update_ship = Update_wing = 1;
1671 Waypoint_editor_dialog.initialize_data(1);
1675 void unmark_object(int obj)
1677 SDL_assert(query_valid_object(obj));
1678 if (Objects[obj].flags & OF_MARKED) {
1679 Objects[obj].flags &= ~OF_MARKED;
1682 if (obj == cur_object_index) { // need to find a new index
1685 ptr = GET_FIRST(&obj_used_list);
1686 while (ptr != END_OF_LIST(&obj_used_list)) {
1687 if (ptr->flags & OF_MARKED) {
1688 set_cur_object_index(OBJ_INDEX(ptr)); // found one
1692 ptr = GET_NEXT(ptr);
1695 set_cur_object_index(-1); // can't find one; nothing is marked.
1697 Update_ship = Update_wing = 1;
1698 Waypoint_editor_dialog.initialize_data(1);
1702 // clears the marked flag of all objects (so nothing is marked)
1708 for (i=0; i<MAX_OBJECTS; i++){
1709 Objects[i].flags &= ~OF_MARKED;
1714 set_cur_object_index(-1);
1718 void clear_menu(CMenu *ptr)
1722 count = ptr->GetMenuItemCount();
1724 ptr->DeleteMenu(count, MF_BYPOSITION);
1728 void generate_wing_popup_menu(CMenu *mptr, int first_id, int state)
1730 int i, z, columns, rows, count;
1736 rows = num_wings / columns;
1740 for (i=0; i<MAX_WINGS; i++){
1741 if (Wings[i].wave_count) {
1742 z = state | MF_STRING;
1745 z |= MF_MENUBARBREAK;
1748 mptr->AppendMenu(z, first_id + i, Wings[i].name);
1752 mptr->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1755 void generate_ship_popup_menu(CMenu *mptr, int first_id, int state, int filter)
1757 int z, ship, columns, rows, count, num_ships;
1761 num_ships = ship_get_num_ships();
1765 rows = num_ships / columns;
1769 ptr = GET_FIRST(&obj_used_list);
1770 while (ptr != END_OF_LIST(&obj_used_list)) {
1771 if ((ptr->type == OBJ_SHIP) || ((ptr->type == OBJ_START) && (filter & SHIP_FILTER_PLAYERS))) {
1773 if (filter & SHIP_FILTER_FLYABLE) {
1774 if (Ship_info[Ships[get_ship_from_obj(ptr)].ship_info_index].flags & SIF_NOT_FLYABLE){
1780 z = state | MF_STRING;
1783 z |= MF_MENUBARBREAK;
1786 ship = ptr->instance;
1787 mptr->AppendMenu(z, first_id + ship, Ships[ship].ship_name);
1791 ptr = GET_NEXT(ptr);
1794 mptr->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1797 // Alternate string lookup function, taking a CString instead. The reason that it's here,
1798 // instead of parselo.cpp, is because the class CString require an include of windows.h,
1799 // which everyone wants to avoid including in any freespace header files. So..
1800 int string_lookup(CString str1, char *strlist[], int max)
1804 for (i=0; i<max; i++) {
1805 SDL_assert(strlen(strlist[i]));
1807 if (!stricmp(str1, strlist[i])){
1815 int gray_menu_tree(CMenu *base)
1817 int i, z, count = 0;
1820 i = base->GetMenuItemCount();
1822 if ((submenu = base->GetSubMenu(i))>0) {
1823 if (gray_menu_tree(submenu)) {
1826 base->EnableMenuItem(i, MF_GRAYED | MF_BYPOSITION);
1830 z = base->GetMenuState(i, MF_BYPOSITION);
1831 if (z == MF_ENABLED){
1840 int query_initial_orders_conflict(int wing)
1844 SDL_assert(wing != -1);
1849 if (query_initial_orders_empty(Wings[wing].ai_goals)){
1853 i = Wings[wing].wave_count; // wing has orders, now check ships.
1855 z = Ships[Objects[wing_objects[wing][i]].instance].ai_index;
1856 if (!query_initial_orders_empty(Ai_info[z].goals)){ // ship also has orders
1864 int query_initial_orders_empty(ai_goal *ai_goals)
1868 for (i=0; i<MAX_AI_GOALS; i++){
1869 if (ai_goals[i].ai_mode != AI_GOAL_NONE){
1877 int set_reinforcement(char *name, int state)
1879 int i, index, cur = -1;
1881 for (i=0; i<Num_reinforcements; i++){
1882 if (!stricmp(Reinforcements[i].name, name)){
1887 if (!state && (cur != -1)) {
1888 Num_reinforcements--;
1889 Reinforcements[cur] = Reinforcements[Num_reinforcements];
1891 // clear the ship/wing flag for this reinforcement
1892 index = ship_name_lookup(name);
1894 Ships[index].flags &= ~SF_REINFORCEMENT;
1896 index = wing_name_lookup(name);
1898 Wings[index].flags &= ~WF_REINFORCEMENT;
1902 Int3(); // get allender -- coudln't find ship/wing for clearing reinforcement flag
1909 if (state && (cur == -1) && (Num_reinforcements < MAX_REINFORCEMENTS)) {
1910 SDL_assert(strlen(name) < NAME_LENGTH);
1911 strcpy(Reinforcements[Num_reinforcements].name, name);
1912 Reinforcements[Num_reinforcements].uses = 1;
1913 Reinforcements[Num_reinforcements].arrival_delay = 0;
1914 memset( Reinforcements[Num_reinforcements].no_messages, 0, MAX_REINFORCEMENT_MESSAGES * NAME_LENGTH );
1915 memset( Reinforcements[Num_reinforcements].yes_messages, 0, MAX_REINFORCEMENT_MESSAGES * NAME_LENGTH );
1916 Num_reinforcements++;
1918 // set the reinforcement flag on the ship or wing
1919 index = ship_name_lookup(name);
1921 Ships[index].flags |= SF_REINFORCEMENT;
1923 index = wing_name_lookup(name);
1925 Wings[index].flags |= WF_REINFORCEMENT;
1929 Int3(); // get allender -- coudln't find ship/wing for setting reinforcement flag
1936 // this code will take care of setting the bits for the ship/wing flags
1937 if ( state && (cur != -1) ) {
1938 // set the reinforcement flag on the ship or wing
1939 index = ship_name_lookup(name);
1941 Ships[index].flags |= SF_REINFORCEMENT;
1943 index = wing_name_lookup(name);
1945 Wings[index].flags |= WF_REINFORCEMENT;
1949 Int3(); // get allender -- coudln't find ship/wing for setting reinforcement flag
1956 int get_docking_list(int model_index)
1961 pm = model_get(model_index);
1962 SDL_assert(pm->n_docks <= MAX_DOCKS);
1963 for (i=0; i<pm->n_docks; i++)
1964 Docking_bay_list[i] = pm->docking_bays[i].name;
1969 // DA 1/7/99 These ship names are not variables
1970 int rename_ship(int ship, char *name)
1974 SDL_assert(ship >= 0);
1975 SDL_assert(strlen(name) < NAME_LENGTH);
1977 update_sexp_references(Ships[ship].ship_name, name);
1978 ai_update_goal_references(REF_TYPE_SHIP, Ships[ship].ship_name, name);
1979 for (i=0; i<Num_reinforcements; i++)
1980 if (!stricmp(Ships[ship].ship_name, Reinforcements[i].name)) {
1981 strcpy(Reinforcements[i].name, name);
1984 strcpy(Ships[ship].ship_name, name);
1985 if (ship == cur_ship)
1986 Ship_editor_dialog.m_ship_name = _T(name);
1991 int invalidate_references(char *name, int type)
1996 sprintf(new_name, "<%s>", name);
1997 update_sexp_references(name, new_name);
1998 ai_update_goal_references(type, name, new_name);
1999 for (i=0; i<Num_reinforcements; i++)
2000 if (!stricmp(name, Reinforcements[i].name)) {
2001 strcpy(Reinforcements[i].name, new_name);
2007 int internal_integrity_check()
2011 for (i=0; i<Num_mission_events; i++)
2012 verify_sexp_tree(Mission_events[i].formula);
2014 for (i=0; i<Num_goals; i++)
2015 verify_sexp_tree(Mission_goals[i].formula);
2017 for (i=0; i<MAX_WINGS; i++)
2018 if (Wings[i].wave_count) {
2019 verify_sexp_tree(Wings[i].arrival_cue);
2020 verify_sexp_tree(Wings[i].departure_cue);
2023 for (i=0; i<MAX_SHIPS; i++)
2024 if (Ships[i].objnum >= 0) {
2025 verify_sexp_tree(Ships[i].arrival_cue);
2026 verify_sexp_tree(Ships[i].departure_cue);
2027 if (Ships[i].ai_index < 0)
2029 if (Ai_info[Ships[i].ai_index].shipnum != i)
2036 void correct_marking()
2040 ptr = GET_FIRST(&obj_used_list);
2041 while (ptr != END_OF_LIST(&obj_used_list)) {
2042 if (ptr->flags & OF_MARKED) {
2043 if (ptr->flags & OF_HIDDEN)
2044 unmark_object(OBJ_INDEX(ptr));
2046 else switch (ptr->type) {
2048 if (!Show_waypoints)
2049 unmark_object(OBJ_INDEX(ptr));
2054 unmark_object(OBJ_INDEX(ptr));
2059 unmark_object(OBJ_INDEX(ptr));
2061 switch (Ships[ptr->instance].team) {
2064 unmark_object(OBJ_INDEX(ptr));
2069 unmark_object(OBJ_INDEX(ptr));
2074 unmark_object(OBJ_INDEX(ptr));
2082 ptr = GET_NEXT(ptr);
2086 // Fills a combo box with a list of all docking points of type 'type' on ship 'ship'.
2087 // Item data is the actual docking point index.
2088 void set_valid_dock_points(int ship, int type, CComboBox *box)
2090 int i, z, num, model;
2092 model = Ships[ship].modelnum;
2093 num = model_get_num_dock_points(model);
2094 for (i=0; i<num; i++)
2095 if (model_get_dock_index_type(model, i) & type) {
2096 z = box->AddString(model_get_dock_name(model, i));
2097 box->SetItemData(z, i);
2100 SDL_assert(box->GetCount());
2103 // Given an object index, find the ship index for that object.
2104 int get_ship_from_obj(int obj)
2106 if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START))
2107 return Objects[obj].instance;
2113 int get_ship_from_obj(object *objp)
2115 if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START))
2116 return objp->instance;
2122 void ai_update_goal_references(int type, char *old_name, char *new_name)
2126 for (i=0; i<MAX_AI_INFO; i++) // loop through all Ai_info entries
2127 if (Ai_info[i].shipnum != -1) // skip if unused
2128 ai_update_goal_references(Ai_info[i].goals, type, old_name, new_name);
2130 for (i=0; i<MAX_WINGS; i++)
2131 if (Wings[i].wave_count)
2132 ai_update_goal_references(Wings[i].ai_goals, type, old_name, new_name);
2135 int query_referenced_in_ai_goals(int type, char *name)
2139 for (i=0; i<MAX_AI_INFO; i++) // loop through all Ai_info entries
2140 if (Ai_info[i].shipnum >= 0) // skip if unused
2141 if (query_referenced_in_ai_goals(Ai_info[i].goals, type, name))
2142 return Ai_info[i].shipnum | SRC_SHIP_ORDER;
2144 for (i=0; i<MAX_WINGS; i++)
2145 if (Wings[i].wave_count)
2146 if (query_referenced_in_ai_goals(Wings[i].ai_goals, type, name))
2147 return i | SRC_WING_ORDER;
2152 int advanced_stricmp(char *one, char *two)
2163 return stricmp(one, two);
2166 // returns 0: go ahead change object
2167 // 1: don't change it
2168 // 2: abort (they used cancel to go to reference)
2169 int reference_handler(char *name, int type, int obj)
2171 char msg[2048], text[128], type_name[128];
2176 sprintf(type_name, "Ship \"%s\"", name);
2180 sprintf(type_name, "Wing \"%s\"", name);
2183 case REF_TYPE_PLAYER:
2184 strcpy(type_name, name);
2187 case REF_TYPE_WAYPOINT:
2188 sprintf(type_name, "Waypoint \"%s\"", name);
2192 sprintf(type_name, "Waypoint path \"%s\"", name);
2196 Error(LOCATION, "Type unknown for object \"%s\". Let Hoffos know now!", name);
2199 r = query_referenced_in_sexp(type, name, &node);
2201 n = r & SRC_DATA_MASK;
2202 switch (r & SRC_MASK) {
2203 case SRC_SHIP_ARRIVAL:
2204 sprintf(text, "the arrival cue of ship \"%s\"", Ships[n].ship_name);
2207 case SRC_SHIP_DEPARTURE:
2208 sprintf(text, "the departure cue of ship \"%s\"", Ships[n].ship_name);
2211 case SRC_WING_ARRIVAL:
2212 sprintf(text, "the arrival cue of wing \"%s\"", Wings[n].name);
2215 case SRC_WING_DEPARTURE:
2216 sprintf(text, "the departure cue of wing \"%s\"", Wings[n].name);
2220 if (*Mission_events[n].name)
2221 sprintf(text, "event \"%s\"", Mission_events[n].name);
2223 sprintf(text, "event #%d", n);
2227 case SRC_MISSION_GOAL:
2228 if (*Mission_goals[n].name)
2229 sprintf(text, "mission goal \"%s\"", Mission_goals[n].name);
2231 sprintf(text, "mission goal #%d", n);
2235 case SRC_DEBRIEFING:
2236 sprintf(text, "debriefing #%d", n);
2240 sprintf(text, "briefing #%d", n);
2243 default: // very bad. Someone added an sexp somewhere and didn't change this.
2244 Warning(LOCATION, "\"%s\" referenced by an unknown sexp source! "
2245 "Run for the hills and let Hoffoss know right now!", name);
2251 sprintf(msg, "%s is referenced by %s (possibly more sexps).\n"
2252 "Do you want to delete it anyway?\n\n"
2253 "(click Cancel to go to the reference)", type_name, text);
2255 r = sexp_reference_handler(node, r, msg);
2269 r = query_referenced_in_ai_goals(type, name);
2271 n = r & SRC_DATA_MASK;
2272 switch (r & SRC_MASK) {
2273 case SRC_SHIP_ORDER:
2274 sprintf(text, "ship \"%s\"", Ships[n].ship_name);
2277 case SRC_WING_ORDER:
2278 sprintf(text, "wing \"%s\"", Wings[n].name);
2281 default: // very bad. Someone added an sexp somewhere and didn't change this.
2282 Error(LOCATION, "\"%s\" referenced by an unknown initial orders source! "
2283 "Run for the hills and let Hoffoss know right now!", name);
2286 sprintf(msg, "%s is referenced by the initial orders of %s (possibly \n"
2287 "more initial orders). Do you want to delete it anyway?\n\n"
2288 "(click Cancel to go to the reference)", type_name, text);
2290 r = orders_reference_handler(r, msg);
2304 if ((type != REF_TYPE_SHIP) && (type != REF_TYPE_WING))
2307 for (n=0; n<Num_reinforcements; n++)
2308 if (!stricmp(name, Reinforcements[n].name))
2311 if (n < Num_reinforcements) {
2312 sprintf(msg, "Ship \"%s\" is a reinforcement unit.\n"
2313 "Do you want to delete it anyway?", name);
2315 r = Fred_main_wnd->MessageBox(msg, NULL, MB_YESNO | MB_ICONEXCLAMATION);
2327 int orders_reference_handler(int code, char *msg)
2331 r = Fred_main_wnd->MessageBox(msg, "Warning", MB_YESNOCANCEL | MB_ICONEXCLAMATION);
2338 ShipGoalsDlg dlg_goals;
2340 n = code & SRC_DATA_MASK;
2341 switch (code & SRC_MASK) {
2342 case SRC_SHIP_ORDER:
2344 mark_object(Ships[n].objnum);
2346 dlg_goals.self_ship = n;
2347 dlg_goals.DoModal();
2348 if (!query_initial_orders_empty(Ai_info[Ships[n].ai_index].goals))
2349 if ((Ships[n].wingnum >= 0) && (query_initial_orders_conflict(Ships[n].wingnum)))
2350 Fred_main_wnd->MessageBox("This ship's wing also has initial orders", "Possible conflict");
2354 case SRC_WING_ORDER:
2358 dlg_goals.self_wing = n;
2359 dlg_goals.DoModal();
2360 if (query_initial_orders_conflict(n))
2361 Fred_main_wnd->MessageBox("One or more ships of this wing also has initial orders", "Possible conflict");
2365 default: // very bad. Someone added an sexp somewhere and didn't change this.
2366 Error(LOCATION, "Unknown initial order reference source");
2373 int sexp_reference_handler(int node, int code, char *msg)
2377 r = Fred_main_wnd->MessageBox(msg, "Warning", MB_YESNOCANCEL | MB_ICONEXCLAMATION);
2384 switch (code & SRC_MASK) {
2385 case SRC_SHIP_ARRIVAL:
2386 case SRC_SHIP_DEPARTURE:
2387 if (!Ship_editor_dialog.GetSafeHwnd())
2388 Ship_editor_dialog.Create();
2390 Ship_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
2391 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2392 Ship_editor_dialog.ShowWindow(SW_RESTORE);
2394 Ship_editor_dialog.select_sexp_node = node;
2396 mark_object(Ships[code & SRC_DATA_MASK].objnum);
2399 case SRC_WING_ARRIVAL:
2400 case SRC_WING_DEPARTURE:
2401 if (!Wing_editor_dialog.GetSafeHwnd())
2402 Wing_editor_dialog.Create();
2404 Wing_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
2405 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2406 Wing_editor_dialog.ShowWindow(SW_RESTORE);
2408 Wing_editor_dialog.select_sexp_node = node;
2410 mark_wing(code & SRC_DATA_MASK);
2414 if (Message_editor_dlg) {
2415 Fred_main_wnd->MessageBox("You must close the message editor before the event editor can be opened");
2419 if (!Event_editor_dlg) {
2420 Event_editor_dlg = new event_editor;
2421 Event_editor_dlg->select_sexp_node = node;
2422 Event_editor_dlg->Create(event_editor::IDD);
2425 Event_editor_dlg->SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2426 Event_editor_dlg->ShowWindow(SW_RESTORE);
2429 case SRC_MISSION_GOAL: {
2430 CMissionGoalsDlg dlg;
2432 dlg.select_sexp_node = node;
2437 case SRC_DEBRIEFING: {
2438 debriefing_editor_dlg dlg;
2440 dlg.select_sexp_node = node;
2445 case SRC_BRIEFING: {
2446 if (!Briefing_dialog) {
2447 Briefing_dialog = new briefing_editor_dlg;
2448 Briefing_dialog->create();
2451 Briefing_dialog->SetWindowPos(&Briefing_dialog->wndTop, 0, 0, 0, 0,
2452 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2453 Briefing_dialog->ShowWindow(SW_RESTORE);
2454 Briefing_dialog->focus_sexp(node);
2458 default: // very bad. Someone added an sexp somewhere and didn't change this.
2459 Error(LOCATION, "Unknown sexp reference source");
2466 char *object_name(int obj)
2468 static char text[80];
2471 if (!query_valid_object(obj))
2474 i = Objects[obj].instance;
2475 switch (Objects[obj].type) {
2478 return Ships[i].ship_name;
2481 sprintf(text, "%s:%d", Waypoint_lists[i / 65536].name, (i & 0xffff) + 1);
2485 return "Briefing icon";
2491 char *get_order_name(int order)
2495 if (order == AI_GOAL_NONE) // special case
2498 for (i=0; i<Ai_goal_list_size; i++)
2499 if (Ai_goal_list[i].def & order)
2500 return Ai_goal_list[i].name;
2505 void object_moved(object *ptr, int mark)
2509 if (ptr->type == OBJ_WAYPOINT)
2510 Waypoint_lists[ptr->instance / 65536].waypoints[ptr->instance & 0xffff] = ptr->pos;
2512 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) { // do we have a ship?
2513 sh1 = get_ship_from_obj(ptr);
2514 o2 = Ai_info[Ships[sh1].ai_index].dock_objnum;
2515 if (o2 >= 0) { // is it docked with something?
2516 sh2 = get_ship_from_obj(o2);
2517 if (mark || !(Objects[o2].flags & OF_MARKED) || (sh1 < sh2))
2518 dock_orient_and_approach(&Objects[o2], ptr, DOA_DOCK_STAY);
2523 // determine if all the ships in a given wing are all marked or not.
2524 int query_whole_wing_marked(int wing)
2529 if (!Wings[wing].wave_count)
2532 ptr = GET_FIRST(&obj_used_list);
2533 while (ptr != END_OF_LIST(&obj_used_list)) {
2534 if (ptr->flags & OF_MARKED)
2535 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START))
2536 if (Ships[get_ship_from_obj(ptr)].wingnum == wing)
2539 ptr = GET_NEXT(ptr);
2542 if (count == Wings[wing].wave_count)
2548 void generate_weaponry_usage_list(int *arr, int wing)
2556 i = Wings[wing].wave_count;
2558 swp = &Ships[Wings[wing].ship_index[i]].weapons;
2559 j = swp->num_primary_banks;
2561 arr[swp->primary_bank_weapons[j]]++;
2563 j = swp->num_secondary_banks;
2565 arr[swp->secondary_bank_weapons[j]] += int(ceil(swp->secondary_bank_ammo[j] * swp->secondary_bank_capacity[j] / 100 / Weapon_info[swp->secondary_bank_weapons[j]].cargo_size));
2569 void generate_weaponry_usage_list(int *arr)
2573 for (i=0; i<MAX_WEAPON_TYPES; i++)
2576 generate_weaponry_usage_list(arr, wing_name_lookup("alpha"));
2577 generate_weaponry_usage_list(arr, wing_name_lookup("beta"));
2578 generate_weaponry_usage_list(arr, wing_name_lookup("gamma"));
2581 // function which adds all current ships in the Fred mission to the passed in combo box. useful for
2582 // building up ship lists for arrival/departure targets
2583 void management_add_ships_to_combo( CComboBox *box, int flags )
2588 box->ResetContent();
2590 // add the "special" targets, i.e. any friendly, any hostile, etc.
2591 if ( flags & SHIPS_2_COMBO_SPECIAL ) {
2592 for (i=0; i<MAX_SPECIAL_ARRIVAL_ANCHORS; i++) {
2593 id = box->AddString(Special_arrival_anchor_names[i]);
2594 box->SetItemData(id, SPECIAL_ARRIVAL_ANCHORS_OFFSET + i);
2598 // either add all ships to the list, or only add ships with docking bays.
2599 if ( flags & SHIPS_2_COMBO_ALL_SHIPS ) {
2600 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
2601 if ( ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && !(objp->flags & OF_MARKED) ) {
2602 id = box->AddString(Ships[get_ship_from_obj(objp)].ship_name);
2603 box->SetItemData(id, get_ship_from_obj(objp));
2606 } else if ( flags & SHIPS_2_COMBO_DOCKING_BAY_ONLY ) {
2607 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
2608 if ( ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && !(objp->flags & OF_MARKED) ) {
2611 // determine if this ship has a docking bay
2612 pm = model_get( Ships[objp->instance].modelnum );
2614 if ( pm->ship_bay && (pm->ship_bay->num_paths > 0) ) {
2615 id = box->AddString(Ships[get_ship_from_obj(objp)].ship_name);
2616 box->SetItemData(id, get_ship_from_obj(objp));
2623 char *reg_read_string( char *section, char *name, char *default_value )
2626 DWORD dwType, dwLen;
2628 static char tmp_string_data[1024];
2631 strcpy( keyname, section );
2633 lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, // Where it is
2634 keyname, // name of key
2635 NULL, // DWORD reserved
2636 KEY_QUERY_VALUE, // Allows all changes
2637 &hKey ); // Location to store key
2639 if ( lResult != ERROR_SUCCESS ) {
2640 mprintf(( "Error opening registry key '%s'\n", keyname ));
2645 mprintf(( "No variable name passed\n" ));
2650 lResult = RegQueryValueEx( hKey, // Handle to key
2651 name, // The values name
2652 NULL, // DWORD reserved
2653 &dwType, // What kind it is
2654 (ubyte *)&tmp_string_data, // value to set
2655 &dwLen ); // How many bytes to set
2657 if ( lResult != ERROR_SUCCESS ) {
2658 mprintf(( "Error reading registry key '%s'\n", name ));
2662 default_value = tmp_string_data;
2668 return default_value;