2 * $Logfile: /Freespace2/code/Fred2/wing.cpp $
7 * Wing management functions for dealing with wing related operations
10 * Revision 1.2 2002/05/07 03:16:44 theoddone33
11 * The Great Newline Fix
13 * Revision 1.1.1.1 2002/05/03 03:28:09 root
17 * 4 7/09/99 5:54p Dave
18 * Seperated cruiser types into individual types. Added tons of new
19 * briefing icons. Campaign screen.
21 * 3 3/01/99 10:00a Dave
22 * Fxied several dogfight related stats bugs.
24 * 2 10/07/98 6:28p Dave
25 * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
26 * Fred. Globalized mission and campaign file extensions. Removed Silent
27 * Threat specific code.
29 * 1 10/07/98 3:02p Dave
31 * 1 10/07/98 3:00p Dave
33 * 59 2/26/98 4:59p Allender
34 * groundwork for team vs team briefings. Moved weaponry pool into the
35 * Team_data structure. Added team field into the p_info structure.
36 * Allow for mutliple structures in the briefing code.
38 * 58 1/06/98 2:27p Hoffoss
39 * Made wing deleting free up arrival and departure cue sexp trees, which
40 * wasn't happening before for some reason.
42 * 57 12/29/97 4:55p Johnson
45 * 56 11/21/97 11:46a Johnson
46 * Changed code to delete reinforcement wings when the wing is deleted.
48 * 55 11/13/97 4:14p Allender
49 * automatic assignment of hotkeys for starting wings. Appripriate
50 * warnings when they are incorrectly used. hotkeys correctly assigned to
51 * ships/wing arriving after mission start
53 * 54 10/01/97 12:37p Hoffoss
54 * Changed Fred (and FreeSpace) to utilize alpha, beta and gamma as player
55 * starting wing names.
57 * 53 9/16/97 9:41p Hoffoss
58 * Changed Fred code around to stop using Parse_player structure for
59 * player information, and use actual ships instead.
61 * 52 9/11/97 4:04p Hoffoss
62 * Fixed bug in Fred: disband wing wasn't making sure the ship editor's
65 * 51 9/06/97 2:13p Mike
66 * Replace support for TEAM_NEUTRAL
68 * 50 9/02/97 3:44p Johnson
69 * Fixed bugs with thresholds not being lowered.
71 * 49 9/02/97 3:24p Johnson
72 * Fixed bug: ships removed from wings will lower threshold if required.
74 * 48 8/30/97 9:52p Hoffoss
75 * Implemented arrival location, distance, and anchor in Fred.
77 * 47 8/25/97 5:58p Hoffoss
78 * Created menu items for keypress functions in Fred, and fixed bug this
79 * uncovered with wing_delete function.
81 * 46 8/16/97 6:44p Hoffoss
82 * Changes to allow any player to be in a wing.
84 * 45 8/16/97 4:51p Hoffoss
85 * Fixed bugs with wing deletion and removing ships from a wing.
87 * 44 8/15/97 1:07a Hoffoss
88 * Changed code to disallow some ship types from being in a wing, and
89 * disallowing some ship types from being able to have initial orders.
91 * 43 8/12/97 1:55a Hoffoss
92 * Made extensive changes to object reference checking and handling for
93 * object deletion call.
95 * 42 8/10/97 4:24p Hoffoss
96 * Added warning when creating a wing if wing is mixed.
98 * 41 8/08/97 1:31p Hoffoss
99 * Added syncronization protection to cur_object_index changes.
101 * 40 8/08/97 10:07a Hoffoss
102 * Fixed bug where ship references aren't updated when wing is created
103 * (i.e. ships being renamed, but not in the references too).
105 * 39 8/01/97 3:24p Hoffoss
106 * Fixed bug where when player is removed from a wing, the ship associated
107 * with the player doesn't get it's wingnum variable updated.
109 * 38 7/09/97 2:38p Allender
110 * organized ship/wing editor dialogs. Added protect ship and ignore
111 * count checkboxes to those dialogs. Changed flag code for
112 * parse_objects. Added unprotect sexpressions
114 * 37 6/30/97 11:36a Hoffoss
115 * Fixed bug with wing reforming.
117 * 36 6/18/97 2:36p Hoffoss
118 * Wing ship numbering starts at 1 instead of 0, and changed form wing to
119 * allow reforming a wing.
121 * 35 6/04/97 12:00a Allender
122 * make wing goal priorities default to -1 so that their priorities get
123 * set more appropriatly in the initial order dialog
125 * 34 5/14/97 4:08p Lawrance
126 * removing my_index from game arrays
128 * 33 5/08/97 10:54a Hoffoss
129 * Fixed bug in reforming wings screwing up ship names.
131 * 32 4/30/97 9:17a Hoffoss
132 * Hotkey for wing set to none when new wing created.
134 * 31 3/28/97 3:19p Hoffoss
137 * 30 3/20/97 3:55p Hoffoss
138 * Major changes to how dialog boxes initialize (load) and update (save)
139 * their internal data. This should simplify things and create less
142 * 29 3/17/97 4:29p Hoffoss
143 * Automated player's wing flaging as a starting player wing.
145 * 28 3/12/97 12:40p Hoffoss
146 * Fixed bugs in wing object management functions, several small additions
147 * and rearrangements.
149 * 27 3/04/97 6:27p Hoffoss
150 * Changes to Fred to handle new wing structure.
152 * 26 2/28/97 11:31a Hoffoss
153 * Implemented modeless dialog saving and restoring, and changed some
156 * 25 2/27/97 3:16p Allender
157 * major wing structure enhancement. simplified wing code. All around
158 * better wing support
160 * 24 2/27/97 2:31p Hoffoss
161 * Changed wing create code to bash ship's cues to false.
163 * 23 2/24/97 5:38p Hoffoss
164 * Added dialog box to name a wing at creation, code to change ship names
165 * to match wing name, and code to maintain these ship names.
167 * 22 2/20/97 4:03p Hoffoss
168 * Several ToDo items: new reinforcement clears arrival cue, reinforcement
169 * control from ship and wing dialogs, show grid toggle.
171 * 21 2/17/97 5:28p Hoffoss
172 * Checked RCS headers, added them were missing, changing description to
173 * something better, etc where needed.
181 #include "fredview.h"
189 #include "management.h"
190 #include "linklist.h"
193 #include "createwingdlg.h"
194 #include "management.h"
196 #define MULTI_WING 999999
199 #define new DEBUG_NEW
201 static char THIS_FILE[] = __FILE__;
204 int already_deleting_wing = 0;
206 void remove_player_from_wing(int player, int min = 1);
208 // Finds a free wing slot (i.e. unused)
213 for (i=0; i<MAX_WINGS; i++)
214 if (!Wings[i].wave_count)
220 int check_wing_dependencies(int wing_num)
224 name = Wings[wing_num].name;
225 return reference_handler(name, REF_TYPE_WING, -1);
228 void mark_wing(int wing)
233 Assert(Wings[wing].special_ship >= 0 && Wings[wing].special_ship < Wings[wing].wave_count);
234 set_cur_object_index(wing_objects[wing][Wings[wing].special_ship]);
235 for (i=0; i<Wings[wing].wave_count; i++)
236 mark_object(wing_objects[wing][i]);
239 // delete a whole wing, also deleting it's ships if necessary.
240 int delete_wing(int wing_num, int bypass)
244 if (already_deleting_wing)
247 r = check_wing_dependencies(wing_num);
251 already_deleting_wing = 1;
252 for (i=0; i<Num_reinforcements; i++)
253 if (!stricmp(Wings[wing_num].name, Reinforcements[i].name)) {
254 delete_reinforcement(i);
258 invalidate_references(Wings[wing_num].name, REF_TYPE_WING);
260 total = Wings[wing_num].wave_count;
261 for (i=0; i<total; i++)
262 delete_object(wing_objects[wing_num][i]);
265 Wings[wing_num].wave_count = 0;
266 if (cur_wing == wing_num)
267 set_cur_wing(cur_wing = -1); // yes, one '=' is correct.
269 free_sexp2(Wings[wing_num].arrival_cue);
270 free_sexp2(Wings[wing_num].departure_cue);
274 already_deleting_wing = 0;
278 // delete a whole wing, leaving ships intact but wingless.
279 void remove_wing(int wing_num)
284 if (check_wing_dependencies(wing_num))
287 Ship_editor_dialog.bypass_errors = Wing_editor_dialog.bypass_errors = 1;
288 Ship_editor_dialog.update_data(0);
289 total = Wings[wing_num].wave_count;
290 for (i=0; i<total; i++) {
291 ptr = &Objects[wing_objects[wing_num][i]];
292 if (ptr->type == OBJ_SHIP)
293 remove_ship_from_wing(ptr->instance);
294 else if (ptr->type == OBJ_START)
295 remove_player_from_wing(ptr->instance);
298 Assert(!Wings[wing_num].wave_count);
299 Ship_editor_dialog.initialize_data(1);
300 Ship_editor_dialog.bypass_errors = Wing_editor_dialog.bypass_errors = 0;
302 if (cur_wing == wing_num) {
303 set_cur_wing(cur_wing = -1); // yes, one '=' is correct.
309 // Takes a ship out of a wing, deleting wing if that was the only ship in it.
310 void remove_ship_from_wing(int ship, int min)
313 int i, wing, end, obj;
315 wing = Ships[ship].wingnum;
317 if (Wings[wing].wave_count == min) {
318 Wings[wing].wave_count = 0;
322 i = Wings[wing].wave_count;
325 if (wing_objects[wing][i] == Ships[ship].objnum)
328 Assert(i != -1); // Error, object should be in wing.
329 if (Wings[wing].special_ship == i)
330 Wings[wing].special_ship = 0;
332 // if not last element, move last element to position to fill gap
334 obj = wing_objects[wing][i] = wing_objects[wing][end];
335 Wings[wing].ship_index[i] = Wings[wing].ship_index[end];
336 if (Objects[obj].type == OBJ_SHIP) {
337 sprintf(buf, "%s %d", Wings[wing].name, i + 1);
338 rename_ship(Wings[wing].ship_index[i], buf);
342 Wings[wing].wave_count--;
343 if (Wings[wing].wave_count && (Wings[wing].threshold >= Wings[wing].wave_count))
344 Wings[wing].threshold = Wings[wing].wave_count - 1;
347 Ships[ship].wingnum = -1;
351 // reset ship name to non-wing default ship name
352 sprintf(buf, "%s %d", Ship_info[Ships[ship].ship_info_index].name, ship);
353 rename_ship(ship, buf);
356 // Takes a player out of a wing, deleting wing if that was the only ship in it.
357 void remove_player_from_wing(int player, int min)
359 remove_ship_from_wing(player, min);
362 // Forms a wing from marked objects
366 int i, ship, wing = -1, waypoints = 0, count = 0, illegal_ships = 0;
367 int friendly, hostile, leader;
371 if (!query_valid_object())
374 leader = cur_object_index;
375 ptr = GET_FIRST(&obj_used_list);
376 while (ptr != END_OF_LIST(&obj_used_list)) {
377 if ((ptr->type == OBJ_SHIP) && (ptr->flags & OF_MARKED)) {
383 i = Ships[ptr->instance].wingnum;
398 if (count > MAX_SHIPS_PER_WING) {
399 sprintf(msg, "You have too many ships marked!\n"
400 "A wing is limited to %d ships total", MAX_SHIPS_PER_WING);
402 Fred_main_wnd->MessageBox(msg, "Error", MB_ICONEXCLAMATION);
406 if ((wing >= 0) && (wing != MULTI_WING)) {
407 sprintf(msg, "Do you want to reform wing \"%s\"?", Wings[wing].name);
408 i = Fred_main_wnd->MessageBox(msg, "Query", MB_YESNOCANCEL);
415 else { // must be IDYES
416 for (i=Wings[wing].wave_count-1; i>=0; i--) {
417 ptr = &Objects[wing_objects[wing][i]];
420 remove_ship_from_wing(ptr->instance, 0);
424 remove_player_from_wing(ptr->instance, 0);
428 Int3(); // shouldn't be in a wing!
432 Assert(!Wings[wing].wave_count);
440 wing = find_free_wing();
441 Wings[wing].num_waves = 1;
442 Wings[wing].threshold = 0;
443 Wings[wing].arrival_location = Wings[wing].departure_location = 0;
444 Wings[wing].arrival_distance = 0;
445 Wings[wing].arrival_anchor = -1;
446 Wings[wing].arrival_cue = Locked_sexp_true;
447 Wings[wing].departure_cue = Locked_sexp_false;
448 Wings[wing].hotkey = -1;
449 Wings[wing].flags = 0;
451 for (i=0; i<MAX_AI_GOALS; i++) {
452 Wings[wing].ai_goals[i].ai_mode = AI_GOAL_NONE;
453 Wings[wing].ai_goals[i].priority = -1; // this sets up the priority field to be like ships
457 Fred_main_wnd->MessageBox("Too many wings, can't create more!",
458 "Error", MB_ICONEXCLAMATION);
463 if (dlg.DoModal() == IDCANCEL)
466 string_copy(Wings[wing].name, dlg.m_name, NAME_LENGTH - 1);
470 ptr = GET_FIRST(&obj_used_list);
471 while (ptr != END_OF_LIST(&obj_used_list)) {
472 if (ptr->flags & OF_MARKED) {
473 // if ((ptr->type == OBJ_START) && (ptr->instance)) {
475 // unmark_object(OBJ_INDEX(ptr));
477 // } else if (ptr->type == OBJ_WAYPOINT) {
478 if (ptr->type == OBJ_WAYPOINT) {
480 unmark_object(OBJ_INDEX(ptr));
482 } else if (ptr->type == OBJ_SHIP) {
483 switch (ship_query_general_type(ptr->instance))
485 case SHIP_TYPE_FIGHTER_BOMBER:
486 case SHIP_TYPE_CRUISER:
487 case SHIP_TYPE_AWACS:
488 case SHIP_TYPE_GAS_MINER:
489 case SHIP_TYPE_CORVETTE:
490 case SHIP_TYPE_FREIGHTER:
491 case SHIP_TYPE_CAPITAL:
492 case SHIP_TYPE_TRANSPORT:
493 case SHIP_TYPE_SUPERCAP:
498 unmark_object(OBJ_INDEX(ptr));
506 // if this wing is a player starting wing, automatically set the hotkey for this wing
507 for (i = 0; i < MAX_STARTING_WINGS; i++ ) {
508 if ( !stricmp(Wings[wing].name, Starting_wing_names[i]) ) {
509 Wings[wing].hotkey = i;
514 count = friendly = hostile = 0;
515 if (Objects[Ships[Player_start_shipnum].objnum].flags & OF_MARKED)
518 ptr = GET_FIRST(&obj_used_list);
519 while (ptr != END_OF_LIST(&obj_used_list)) {
520 if (ptr->flags & OF_MARKED) {
521 if ((ptr->type == OBJ_START) && (ptr->instance == Player_start_shipnum))
522 i = 0; // player 1 start always goes to front of the wing
526 Assert((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START));
527 ship = ptr->instance;
528 if (Ships[ship].wingnum != -1) {
529 if (ptr->type == OBJ_SHIP)
530 remove_ship_from_wing(ship);
532 remove_player_from_wing(ptr->instance);
535 sprintf(msg, "%s %d", Wings[wing].name, i + 1);
536 rename_ship(ship, msg);
537 if (Ships[ship].team == TEAM_FRIENDLY)
539 else if ((Ships[ship].team == TEAM_HOSTILE) || (Ships[ship].team == TEAM_NEUTRAL))
542 Wings[wing].ship_index[i] = ship;
543 Ships[ship].wingnum = wing;
544 if (Ships[ship].arrival_cue >= 0)
545 free_sexp2(Ships[ship].arrival_cue);
547 Ships[ship].arrival_cue = Locked_sexp_false;
548 if (Ships[ship].departure_cue >= 0)
549 free_sexp2(Ships[ship].departure_cue);
551 Ships[ship].departure_cue = Locked_sexp_false;
553 wing_objects[wing][i] = OBJ_INDEX(ptr);
554 if (OBJ_INDEX(ptr) == leader)
555 Wings[wing].special_ship = i;
561 if (!count) // this should never happen, so if it does, needs to be fixed now.
562 Error(LOCATION, "No valid ships were selected to form wing from");
564 Wings[wing].wave_count = count;
568 // Fred_main_wnd->MessageBox("Multi-player starting points can't be part of a wing!\n"
569 // "All marked multi-player starting points were ignored",
570 // "Error", MB_ICONEXCLAMATION);
573 Fred_main_wnd->MessageBox("Waypoints can't be part of a wing!\n"
574 "All marked waypoints were ignored",
575 "Error", MB_ICONEXCLAMATION);
578 Fred_main_wnd->MessageBox("Some ship types aren't allowed to be in a wing.\n"
579 "All marked ships of these types were ignored",
580 "Error", MB_ICONEXCLAMATION);
582 if (friendly && hostile)
583 Fred_main_wnd->MessageBox("Both hostile and friendly ships in same wing", "Warning");
589 /////////////////////////////////////////////////////////////////////////////////////
590 // Old stuff down there..
592 #define MAX_WING_VECTORS 8 // 8 vectors per wing formation. Possible to have more
593 // than 8 ships. A vector is where a ship is located relative
594 // to the leader. Other ships can be located at the same
595 // vector relative to another member. So wing formation
596 // size is not limited by this constant.
597 #define MAX_WING_FORMATIONS 8 // 8 different kinds of wing formations
599 typedef struct formation {
601 vector offsets[MAX_WING_VECTORS];
604 formation Wing_formations[MAX_WING_FORMATIONS];
606 //wing Wings[MAX_WINGS];
608 int Wings_initialized = 0;
610 void initialize_wings(void)
612 if (Wings_initialized)
615 Wings_initialized = 1;
617 Wing_formations[0].num_vectors = 2;
619 Wing_formations[0].offsets[0].x = -5.0f;
620 Wing_formations[0].offsets[0].y = +1.0f;
621 Wing_formations[0].offsets[0].z = -5.0f;
623 Wing_formations[0].offsets[1].x = +5.0f;
624 Wing_formations[0].offsets[1].y = +1.0f;
625 Wing_formations[0].offsets[1].z = -5.0f;
628 void create_wings_from_objects(void)
632 for (i=0; i<MAX_WINGS; i++)
633 Wings[i].wave_count= 0;
635 for (i=0; i<MAX_OBJECTS; i++)
636 if (Objects[i].type != OBJ_NONE)
637 if (get_wingnum(i) != -1) {
638 int wingnum = get_wingnum(i);
640 Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
641 Assert(Wings[wingnum].wave_count < MAX_SHIPS_PER_WING);
642 // JEH strcpy(Wings[wingnum].ship_names[Wings[wingnum].count++], i;
647 int get_free_objnum(void)
651 for (i=1; i<MAX_OBJECTS; i++)
652 if (Objects[i].type == OBJ_NONE)
659 // wing_type is the type of wing from the Wing_formations array to create.
660 // leader_index is the index in Objects of the leader object. This object must
661 // have a position and an orientation.
662 // *wingmen is a list of indices of existing ships to be added to the wing.
663 // The wingmen list is terminated by -1.
664 // max_size is the maximum number of ships to add to the wing
665 // fill_flag is set if more ships are to be added to fill out the wing to max_size
666 void create_wing(int wing_type, int leader_index, int *wingmen, int max_size, int fill_flag)
668 int num_placed, num_vectors, cur_vec_index;
669 object *lobjp = &Objects[leader_index];
672 int wing_list[MAX_OBJECTS];
677 Assert((wing_type >= 0) && (wing_type < MAX_WING_FORMATIONS));
678 Assert(Wing_formations[wing_type].num_vectors > 0);
679 Assert(Wing_formations[wing_type].num_vectors < MAX_WING_VECTORS);
681 Assert(Objects[leader_index].type != OBJ_NONE);
682 Assert(max_size < MAX_SHIPS_PER_WING);
685 wingp = &Wing_formations[wing_type];
686 num_vectors = wingp->num_vectors;
689 vm_copy_transpose_matrix(&rotmat, &lobjp->orient);
691 while (num_placed < max_size) {
695 if (*wingmen == -1) {
699 curobj = get_free_objnum();
700 Assert(curobj != -1);
701 Objects[curobj].type = lobjp->type;
702 Assert(Wings[cur_wing].wave_count < MAX_SHIPS_PER_WING);
703 // JEH Wings[cur_wing].ship_list[Wings[cur_wing].count] = curobj;
704 Wings[cur_wing].wave_count++;
709 Objects[curobj] = *lobjp;
710 vm_vec_rotate(&wvec, &wingp->offsets[cur_vec_index], &rotmat);
711 cur_vec_index = (cur_vec_index + 1) % num_vectors;
713 if (num_placed < num_vectors)
716 parent = &Objects[wing_list[num_placed - num_vectors]];
718 wing_list[num_placed] = curobj;
720 vm_vec_add(&Objects[curobj].pos, &parent->pos, &wvec);
728 // cur_object_index becomes the leader.
729 // If count == -1, then all objects of wing cur_wing get added to the wing.
730 // If count == +n, then n objects are added to the wing.
731 void test_form_wing(int count)
733 int i, wingmen[MAX_OBJECTS];
738 Assert(cur_object_index != -1);
739 Assert(Objects[cur_object_index].type != OBJ_NONE);
740 Assert(get_wingnum(cur_object_index) != -1);
741 get_wingnum(cur_object_index);
745 for (i=1; i<MAX_OBJECTS; i++)
746 if ((get_wingnum(i) == cur_wing) && (Objects[i].type != OBJ_NONE))
747 if (i != cur_object_index)
759 set_wingnum(cur_object_index, cur_wing);
760 create_wing(0, cur_object_index, wingmen, j, fill_flag);