2 * $Logfile: /Freespace2/code/Fred2/wing.cpp $
7 * Wing management functions for dealing with wing related operations
10 * Revision 1.1 2002/05/03 03:28:09 root
14 * 4 7/09/99 5:54p Dave
15 * Seperated cruiser types into individual types. Added tons of new
16 * briefing icons. Campaign screen.
18 * 3 3/01/99 10:00a Dave
19 * Fxied several dogfight related stats bugs.
21 * 2 10/07/98 6:28p Dave
22 * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
23 * Fred. Globalized mission and campaign file extensions. Removed Silent
24 * Threat specific code.
26 * 1 10/07/98 3:02p Dave
28 * 1 10/07/98 3:00p Dave
30 * 59 2/26/98 4:59p Allender
31 * groundwork for team vs team briefings. Moved weaponry pool into the
32 * Team_data structure. Added team field into the p_info structure.
33 * Allow for mutliple structures in the briefing code.
35 * 58 1/06/98 2:27p Hoffoss
36 * Made wing deleting free up arrival and departure cue sexp trees, which
37 * wasn't happening before for some reason.
39 * 57 12/29/97 4:55p Johnson
42 * 56 11/21/97 11:46a Johnson
43 * Changed code to delete reinforcement wings when the wing is deleted.
45 * 55 11/13/97 4:14p Allender
46 * automatic assignment of hotkeys for starting wings. Appripriate
47 * warnings when they are incorrectly used. hotkeys correctly assigned to
48 * ships/wing arriving after mission start
50 * 54 10/01/97 12:37p Hoffoss
51 * Changed Fred (and FreeSpace) to utilize alpha, beta and gamma as player
52 * starting wing names.
54 * 53 9/16/97 9:41p Hoffoss
55 * Changed Fred code around to stop using Parse_player structure for
56 * player information, and use actual ships instead.
58 * 52 9/11/97 4:04p Hoffoss
59 * Fixed bug in Fred: disband wing wasn't making sure the ship editor's
62 * 51 9/06/97 2:13p Mike
63 * Replace support for TEAM_NEUTRAL
65 * 50 9/02/97 3:44p Johnson
66 * Fixed bugs with thresholds not being lowered.
68 * 49 9/02/97 3:24p Johnson
69 * Fixed bug: ships removed from wings will lower threshold if required.
71 * 48 8/30/97 9:52p Hoffoss
72 * Implemented arrival location, distance, and anchor in Fred.
74 * 47 8/25/97 5:58p Hoffoss
75 * Created menu items for keypress functions in Fred, and fixed bug this
76 * uncovered with wing_delete function.
78 * 46 8/16/97 6:44p Hoffoss
79 * Changes to allow any player to be in a wing.
81 * 45 8/16/97 4:51p Hoffoss
82 * Fixed bugs with wing deletion and removing ships from a wing.
84 * 44 8/15/97 1:07a Hoffoss
85 * Changed code to disallow some ship types from being in a wing, and
86 * disallowing some ship types from being able to have initial orders.
88 * 43 8/12/97 1:55a Hoffoss
89 * Made extensive changes to object reference checking and handling for
90 * object deletion call.
92 * 42 8/10/97 4:24p Hoffoss
93 * Added warning when creating a wing if wing is mixed.
95 * 41 8/08/97 1:31p Hoffoss
96 * Added syncronization protection to cur_object_index changes.
98 * 40 8/08/97 10:07a Hoffoss
99 * Fixed bug where ship references aren't updated when wing is created
100 * (i.e. ships being renamed, but not in the references too).
102 * 39 8/01/97 3:24p Hoffoss
103 * Fixed bug where when player is removed from a wing, the ship associated
104 * with the player doesn't get it's wingnum variable updated.
106 * 38 7/09/97 2:38p Allender
107 * organized ship/wing editor dialogs. Added protect ship and ignore
108 * count checkboxes to those dialogs. Changed flag code for
109 * parse_objects. Added unprotect sexpressions
111 * 37 6/30/97 11:36a Hoffoss
112 * Fixed bug with wing reforming.
114 * 36 6/18/97 2:36p Hoffoss
115 * Wing ship numbering starts at 1 instead of 0, and changed form wing to
116 * allow reforming a wing.
118 * 35 6/04/97 12:00a Allender
119 * make wing goal priorities default to -1 so that their priorities get
120 * set more appropriatly in the initial order dialog
122 * 34 5/14/97 4:08p Lawrance
123 * removing my_index from game arrays
125 * 33 5/08/97 10:54a Hoffoss
126 * Fixed bug in reforming wings screwing up ship names.
128 * 32 4/30/97 9:17a Hoffoss
129 * Hotkey for wing set to none when new wing created.
131 * 31 3/28/97 3:19p Hoffoss
134 * 30 3/20/97 3:55p Hoffoss
135 * Major changes to how dialog boxes initialize (load) and update (save)
136 * their internal data. This should simplify things and create less
139 * 29 3/17/97 4:29p Hoffoss
140 * Automated player's wing flaging as a starting player wing.
142 * 28 3/12/97 12:40p Hoffoss
143 * Fixed bugs in wing object management functions, several small additions
144 * and rearrangements.
146 * 27 3/04/97 6:27p Hoffoss
147 * Changes to Fred to handle new wing structure.
149 * 26 2/28/97 11:31a Hoffoss
150 * Implemented modeless dialog saving and restoring, and changed some
153 * 25 2/27/97 3:16p Allender
154 * major wing structure enhancement. simplified wing code. All around
155 * better wing support
157 * 24 2/27/97 2:31p Hoffoss
158 * Changed wing create code to bash ship's cues to false.
160 * 23 2/24/97 5:38p Hoffoss
161 * Added dialog box to name a wing at creation, code to change ship names
162 * to match wing name, and code to maintain these ship names.
164 * 22 2/20/97 4:03p Hoffoss
165 * Several ToDo items: new reinforcement clears arrival cue, reinforcement
166 * control from ship and wing dialogs, show grid toggle.
168 * 21 2/17/97 5:28p Hoffoss
169 * Checked RCS headers, added them were missing, changing description to
170 * something better, etc where needed.
178 #include "fredview.h"
186 #include "management.h"
187 #include "linklist.h"
190 #include "createwingdlg.h"
191 #include "management.h"
193 #define MULTI_WING 999999
196 #define new DEBUG_NEW
198 static char THIS_FILE[] = __FILE__;
201 int already_deleting_wing = 0;
203 void remove_player_from_wing(int player, int min = 1);
205 // Finds a free wing slot (i.e. unused)
210 for (i=0; i<MAX_WINGS; i++)
211 if (!Wings[i].wave_count)
217 int check_wing_dependencies(int wing_num)
221 name = Wings[wing_num].name;
222 return reference_handler(name, REF_TYPE_WING, -1);
225 void mark_wing(int wing)
230 Assert(Wings[wing].special_ship >= 0 && Wings[wing].special_ship < Wings[wing].wave_count);
231 set_cur_object_index(wing_objects[wing][Wings[wing].special_ship]);
232 for (i=0; i<Wings[wing].wave_count; i++)
233 mark_object(wing_objects[wing][i]);
236 // delete a whole wing, also deleting it's ships if necessary.
237 int delete_wing(int wing_num, int bypass)
241 if (already_deleting_wing)
244 r = check_wing_dependencies(wing_num);
248 already_deleting_wing = 1;
249 for (i=0; i<Num_reinforcements; i++)
250 if (!stricmp(Wings[wing_num].name, Reinforcements[i].name)) {
251 delete_reinforcement(i);
255 invalidate_references(Wings[wing_num].name, REF_TYPE_WING);
257 total = Wings[wing_num].wave_count;
258 for (i=0; i<total; i++)
259 delete_object(wing_objects[wing_num][i]);
262 Wings[wing_num].wave_count = 0;
263 if (cur_wing == wing_num)
264 set_cur_wing(cur_wing = -1); // yes, one '=' is correct.
266 free_sexp2(Wings[wing_num].arrival_cue);
267 free_sexp2(Wings[wing_num].departure_cue);
271 already_deleting_wing = 0;
275 // delete a whole wing, leaving ships intact but wingless.
276 void remove_wing(int wing_num)
281 if (check_wing_dependencies(wing_num))
284 Ship_editor_dialog.bypass_errors = Wing_editor_dialog.bypass_errors = 1;
285 Ship_editor_dialog.update_data(0);
286 total = Wings[wing_num].wave_count;
287 for (i=0; i<total; i++) {
288 ptr = &Objects[wing_objects[wing_num][i]];
289 if (ptr->type == OBJ_SHIP)
290 remove_ship_from_wing(ptr->instance);
291 else if (ptr->type == OBJ_START)
292 remove_player_from_wing(ptr->instance);
295 Assert(!Wings[wing_num].wave_count);
296 Ship_editor_dialog.initialize_data(1);
297 Ship_editor_dialog.bypass_errors = Wing_editor_dialog.bypass_errors = 0;
299 if (cur_wing == wing_num) {
300 set_cur_wing(cur_wing = -1); // yes, one '=' is correct.
306 // Takes a ship out of a wing, deleting wing if that was the only ship in it.
307 void remove_ship_from_wing(int ship, int min)
310 int i, wing, end, obj;
312 wing = Ships[ship].wingnum;
314 if (Wings[wing].wave_count == min) {
315 Wings[wing].wave_count = 0;
319 i = Wings[wing].wave_count;
322 if (wing_objects[wing][i] == Ships[ship].objnum)
325 Assert(i != -1); // Error, object should be in wing.
326 if (Wings[wing].special_ship == i)
327 Wings[wing].special_ship = 0;
329 // if not last element, move last element to position to fill gap
331 obj = wing_objects[wing][i] = wing_objects[wing][end];
332 Wings[wing].ship_index[i] = Wings[wing].ship_index[end];
333 if (Objects[obj].type == OBJ_SHIP) {
334 sprintf(buf, "%s %d", Wings[wing].name, i + 1);
335 rename_ship(Wings[wing].ship_index[i], buf);
339 Wings[wing].wave_count--;
340 if (Wings[wing].wave_count && (Wings[wing].threshold >= Wings[wing].wave_count))
341 Wings[wing].threshold = Wings[wing].wave_count - 1;
344 Ships[ship].wingnum = -1;
348 // reset ship name to non-wing default ship name
349 sprintf(buf, "%s %d", Ship_info[Ships[ship].ship_info_index].name, ship);
350 rename_ship(ship, buf);
353 // Takes a player out of a wing, deleting wing if that was the only ship in it.
354 void remove_player_from_wing(int player, int min)
356 remove_ship_from_wing(player, min);
359 // Forms a wing from marked objects
363 int i, ship, wing = -1, waypoints = 0, count = 0, illegal_ships = 0;
364 int friendly, hostile, leader;
368 if (!query_valid_object())
371 leader = cur_object_index;
372 ptr = GET_FIRST(&obj_used_list);
373 while (ptr != END_OF_LIST(&obj_used_list)) {
374 if ((ptr->type == OBJ_SHIP) && (ptr->flags & OF_MARKED)) {
380 i = Ships[ptr->instance].wingnum;
395 if (count > MAX_SHIPS_PER_WING) {
396 sprintf(msg, "You have too many ships marked!\n"
397 "A wing is limited to %d ships total", MAX_SHIPS_PER_WING);
399 Fred_main_wnd->MessageBox(msg, "Error", MB_ICONEXCLAMATION);
403 if ((wing >= 0) && (wing != MULTI_WING)) {
404 sprintf(msg, "Do you want to reform wing \"%s\"?", Wings[wing].name);
405 i = Fred_main_wnd->MessageBox(msg, "Query", MB_YESNOCANCEL);
412 else { // must be IDYES
413 for (i=Wings[wing].wave_count-1; i>=0; i--) {
414 ptr = &Objects[wing_objects[wing][i]];
417 remove_ship_from_wing(ptr->instance, 0);
421 remove_player_from_wing(ptr->instance, 0);
425 Int3(); // shouldn't be in a wing!
429 Assert(!Wings[wing].wave_count);
437 wing = find_free_wing();
438 Wings[wing].num_waves = 1;
439 Wings[wing].threshold = 0;
440 Wings[wing].arrival_location = Wings[wing].departure_location = 0;
441 Wings[wing].arrival_distance = 0;
442 Wings[wing].arrival_anchor = -1;
443 Wings[wing].arrival_cue = Locked_sexp_true;
444 Wings[wing].departure_cue = Locked_sexp_false;
445 Wings[wing].hotkey = -1;
446 Wings[wing].flags = 0;
448 for (i=0; i<MAX_AI_GOALS; i++) {
449 Wings[wing].ai_goals[i].ai_mode = AI_GOAL_NONE;
450 Wings[wing].ai_goals[i].priority = -1; // this sets up the priority field to be like ships
454 Fred_main_wnd->MessageBox("Too many wings, can't create more!",
455 "Error", MB_ICONEXCLAMATION);
460 if (dlg.DoModal() == IDCANCEL)
463 string_copy(Wings[wing].name, dlg.m_name, NAME_LENGTH - 1);
467 ptr = GET_FIRST(&obj_used_list);
468 while (ptr != END_OF_LIST(&obj_used_list)) {
469 if (ptr->flags & OF_MARKED) {
470 // if ((ptr->type == OBJ_START) && (ptr->instance)) {
472 // unmark_object(OBJ_INDEX(ptr));
474 // } else if (ptr->type == OBJ_WAYPOINT) {
475 if (ptr->type == OBJ_WAYPOINT) {
477 unmark_object(OBJ_INDEX(ptr));
479 } else if (ptr->type == OBJ_SHIP) {
480 switch (ship_query_general_type(ptr->instance))
482 case SHIP_TYPE_FIGHTER_BOMBER:
483 case SHIP_TYPE_CRUISER:
484 case SHIP_TYPE_AWACS:
485 case SHIP_TYPE_GAS_MINER:
486 case SHIP_TYPE_CORVETTE:
487 case SHIP_TYPE_FREIGHTER:
488 case SHIP_TYPE_CAPITAL:
489 case SHIP_TYPE_TRANSPORT:
490 case SHIP_TYPE_SUPERCAP:
495 unmark_object(OBJ_INDEX(ptr));
503 // if this wing is a player starting wing, automatically set the hotkey for this wing
504 for (i = 0; i < MAX_STARTING_WINGS; i++ ) {
505 if ( !stricmp(Wings[wing].name, Starting_wing_names[i]) ) {
506 Wings[wing].hotkey = i;
511 count = friendly = hostile = 0;
512 if (Objects[Ships[Player_start_shipnum].objnum].flags & OF_MARKED)
515 ptr = GET_FIRST(&obj_used_list);
516 while (ptr != END_OF_LIST(&obj_used_list)) {
517 if (ptr->flags & OF_MARKED) {
518 if ((ptr->type == OBJ_START) && (ptr->instance == Player_start_shipnum))
519 i = 0; // player 1 start always goes to front of the wing
523 Assert((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START));
524 ship = ptr->instance;
525 if (Ships[ship].wingnum != -1) {
526 if (ptr->type == OBJ_SHIP)
527 remove_ship_from_wing(ship);
529 remove_player_from_wing(ptr->instance);
532 sprintf(msg, "%s %d", Wings[wing].name, i + 1);
533 rename_ship(ship, msg);
534 if (Ships[ship].team == TEAM_FRIENDLY)
536 else if ((Ships[ship].team == TEAM_HOSTILE) || (Ships[ship].team == TEAM_NEUTRAL))
539 Wings[wing].ship_index[i] = ship;
540 Ships[ship].wingnum = wing;
541 if (Ships[ship].arrival_cue >= 0)
542 free_sexp2(Ships[ship].arrival_cue);
544 Ships[ship].arrival_cue = Locked_sexp_false;
545 if (Ships[ship].departure_cue >= 0)
546 free_sexp2(Ships[ship].departure_cue);
548 Ships[ship].departure_cue = Locked_sexp_false;
550 wing_objects[wing][i] = OBJ_INDEX(ptr);
551 if (OBJ_INDEX(ptr) == leader)
552 Wings[wing].special_ship = i;
558 if (!count) // this should never happen, so if it does, needs to be fixed now.
559 Error(LOCATION, "No valid ships were selected to form wing from");
561 Wings[wing].wave_count = count;
565 // Fred_main_wnd->MessageBox("Multi-player starting points can't be part of a wing!\n"
566 // "All marked multi-player starting points were ignored",
567 // "Error", MB_ICONEXCLAMATION);
570 Fred_main_wnd->MessageBox("Waypoints can't be part of a wing!\n"
571 "All marked waypoints were ignored",
572 "Error", MB_ICONEXCLAMATION);
575 Fred_main_wnd->MessageBox("Some ship types aren't allowed to be in a wing.\n"
576 "All marked ships of these types were ignored",
577 "Error", MB_ICONEXCLAMATION);
579 if (friendly && hostile)
580 Fred_main_wnd->MessageBox("Both hostile and friendly ships in same wing", "Warning");
586 /////////////////////////////////////////////////////////////////////////////////////
587 // Old stuff down there..
589 #define MAX_WING_VECTORS 8 // 8 vectors per wing formation. Possible to have more
590 // than 8 ships. A vector is where a ship is located relative
591 // to the leader. Other ships can be located at the same
592 // vector relative to another member. So wing formation
593 // size is not limited by this constant.
594 #define MAX_WING_FORMATIONS 8 // 8 different kinds of wing formations
596 typedef struct formation {
598 vector offsets[MAX_WING_VECTORS];
601 formation Wing_formations[MAX_WING_FORMATIONS];
603 //wing Wings[MAX_WINGS];
605 int Wings_initialized = 0;
607 void initialize_wings(void)
609 if (Wings_initialized)
612 Wings_initialized = 1;
614 Wing_formations[0].num_vectors = 2;
616 Wing_formations[0].offsets[0].x = -5.0f;
617 Wing_formations[0].offsets[0].y = +1.0f;
618 Wing_formations[0].offsets[0].z = -5.0f;
620 Wing_formations[0].offsets[1].x = +5.0f;
621 Wing_formations[0].offsets[1].y = +1.0f;
622 Wing_formations[0].offsets[1].z = -5.0f;
625 void create_wings_from_objects(void)
629 for (i=0; i<MAX_WINGS; i++)
630 Wings[i].wave_count= 0;
632 for (i=0; i<MAX_OBJECTS; i++)
633 if (Objects[i].type != OBJ_NONE)
634 if (get_wingnum(i) != -1) {
635 int wingnum = get_wingnum(i);
637 Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
638 Assert(Wings[wingnum].wave_count < MAX_SHIPS_PER_WING);
639 // JEH strcpy(Wings[wingnum].ship_names[Wings[wingnum].count++], i;
644 int get_free_objnum(void)
648 for (i=1; i<MAX_OBJECTS; i++)
649 if (Objects[i].type == OBJ_NONE)
656 // wing_type is the type of wing from the Wing_formations array to create.
657 // leader_index is the index in Objects of the leader object. This object must
658 // have a position and an orientation.
659 // *wingmen is a list of indices of existing ships to be added to the wing.
660 // The wingmen list is terminated by -1.
661 // max_size is the maximum number of ships to add to the wing
662 // fill_flag is set if more ships are to be added to fill out the wing to max_size
663 void create_wing(int wing_type, int leader_index, int *wingmen, int max_size, int fill_flag)
665 int num_placed, num_vectors, cur_vec_index;
666 object *lobjp = &Objects[leader_index];
669 int wing_list[MAX_OBJECTS];
674 Assert((wing_type >= 0) && (wing_type < MAX_WING_FORMATIONS));
675 Assert(Wing_formations[wing_type].num_vectors > 0);
676 Assert(Wing_formations[wing_type].num_vectors < MAX_WING_VECTORS);
678 Assert(Objects[leader_index].type != OBJ_NONE);
679 Assert(max_size < MAX_SHIPS_PER_WING);
682 wingp = &Wing_formations[wing_type];
683 num_vectors = wingp->num_vectors;
686 vm_copy_transpose_matrix(&rotmat, &lobjp->orient);
688 while (num_placed < max_size) {
692 if (*wingmen == -1) {
696 curobj = get_free_objnum();
697 Assert(curobj != -1);
698 Objects[curobj].type = lobjp->type;
699 Assert(Wings[cur_wing].wave_count < MAX_SHIPS_PER_WING);
700 // JEH Wings[cur_wing].ship_list[Wings[cur_wing].count] = curobj;
701 Wings[cur_wing].wave_count++;
706 Objects[curobj] = *lobjp;
707 vm_vec_rotate(&wvec, &wingp->offsets[cur_vec_index], &rotmat);
708 cur_vec_index = (cur_vec_index + 1) % num_vectors;
710 if (num_placed < num_vectors)
713 parent = &Objects[wing_list[num_placed - num_vectors]];
715 wing_list[num_placed] = curobj;
717 vm_vec_add(&Objects[curobj].pos, &parent->pos, &wvec);
725 // cur_object_index becomes the leader.
726 // If count == -1, then all objects of wing cur_wing get added to the wing.
727 // If count == +n, then n objects are added to the wing.
728 void test_form_wing(int count)
730 int i, wingmen[MAX_OBJECTS];
735 Assert(cur_object_index != -1);
736 Assert(Objects[cur_object_index].type != OBJ_NONE);
737 Assert(get_wingnum(cur_object_index) != -1);
738 get_wingnum(cur_object_index);
742 for (i=1; i<MAX_OBJECTS; i++)
743 if ((get_wingnum(i) == cur_wing) && (Objects[i].type != OBJ_NONE))
744 if (i != cur_object_index)
756 set_wingnum(cur_object_index, cur_wing);
757 create_wing(0, cur_object_index, wingmen, j, fill_flag);