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/wing.cpp $
15 * Wing management functions for dealing with wing related operations
18 * Revision 1.3 2002/06/09 04:41:17 relnev
19 * added copyright header
21 * Revision 1.2 2002/05/07 03:16:44 theoddone33
22 * The Great Newline Fix
24 * Revision 1.1.1.1 2002/05/03 03:28:09 root
28 * 4 7/09/99 5:54p Dave
29 * Seperated cruiser types into individual types. Added tons of new
30 * briefing icons. Campaign screen.
32 * 3 3/01/99 10:00a Dave
33 * Fxied several dogfight related stats bugs.
35 * 2 10/07/98 6:28p Dave
36 * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
37 * Fred. Globalized mission and campaign file extensions. Removed Silent
38 * Threat specific code.
40 * 1 10/07/98 3:02p Dave
42 * 1 10/07/98 3:00p Dave
44 * 59 2/26/98 4:59p Allender
45 * groundwork for team vs team briefings. Moved weaponry pool into the
46 * Team_data structure. Added team field into the p_info structure.
47 * Allow for mutliple structures in the briefing code.
49 * 58 1/06/98 2:27p Hoffoss
50 * Made wing deleting free up arrival and departure cue sexp trees, which
51 * wasn't happening before for some reason.
53 * 57 12/29/97 4:55p Johnson
56 * 56 11/21/97 11:46a Johnson
57 * Changed code to delete reinforcement wings when the wing is deleted.
59 * 55 11/13/97 4:14p Allender
60 * automatic assignment of hotkeys for starting wings. Appripriate
61 * warnings when they are incorrectly used. hotkeys correctly assigned to
62 * ships/wing arriving after mission start
64 * 54 10/01/97 12:37p Hoffoss
65 * Changed Fred (and FreeSpace) to utilize alpha, beta and gamma as player
66 * starting wing names.
68 * 53 9/16/97 9:41p Hoffoss
69 * Changed Fred code around to stop using Parse_player structure for
70 * player information, and use actual ships instead.
72 * 52 9/11/97 4:04p Hoffoss
73 * Fixed bug in Fred: disband wing wasn't making sure the ship editor's
76 * 51 9/06/97 2:13p Mike
77 * Replace support for TEAM_NEUTRAL
79 * 50 9/02/97 3:44p Johnson
80 * Fixed bugs with thresholds not being lowered.
82 * 49 9/02/97 3:24p Johnson
83 * Fixed bug: ships removed from wings will lower threshold if required.
85 * 48 8/30/97 9:52p Hoffoss
86 * Implemented arrival location, distance, and anchor in Fred.
88 * 47 8/25/97 5:58p Hoffoss
89 * Created menu items for keypress functions in Fred, and fixed bug this
90 * uncovered with wing_delete function.
92 * 46 8/16/97 6:44p Hoffoss
93 * Changes to allow any player to be in a wing.
95 * 45 8/16/97 4:51p Hoffoss
96 * Fixed bugs with wing deletion and removing ships from a wing.
98 * 44 8/15/97 1:07a Hoffoss
99 * Changed code to disallow some ship types from being in a wing, and
100 * disallowing some ship types from being able to have initial orders.
102 * 43 8/12/97 1:55a Hoffoss
103 * Made extensive changes to object reference checking and handling for
104 * object deletion call.
106 * 42 8/10/97 4:24p Hoffoss
107 * Added warning when creating a wing if wing is mixed.
109 * 41 8/08/97 1:31p Hoffoss
110 * Added syncronization protection to cur_object_index changes.
112 * 40 8/08/97 10:07a Hoffoss
113 * Fixed bug where ship references aren't updated when wing is created
114 * (i.e. ships being renamed, but not in the references too).
116 * 39 8/01/97 3:24p Hoffoss
117 * Fixed bug where when player is removed from a wing, the ship associated
118 * with the player doesn't get it's wingnum variable updated.
120 * 38 7/09/97 2:38p Allender
121 * organized ship/wing editor dialogs. Added protect ship and ignore
122 * count checkboxes to those dialogs. Changed flag code for
123 * parse_objects. Added unprotect sexpressions
125 * 37 6/30/97 11:36a Hoffoss
126 * Fixed bug with wing reforming.
128 * 36 6/18/97 2:36p Hoffoss
129 * Wing ship numbering starts at 1 instead of 0, and changed form wing to
130 * allow reforming a wing.
132 * 35 6/04/97 12:00a Allender
133 * make wing goal priorities default to -1 so that their priorities get
134 * set more appropriatly in the initial order dialog
136 * 34 5/14/97 4:08p Lawrance
137 * removing my_index from game arrays
139 * 33 5/08/97 10:54a Hoffoss
140 * Fixed bug in reforming wings screwing up ship names.
142 * 32 4/30/97 9:17a Hoffoss
143 * Hotkey for wing set to none when new wing created.
145 * 31 3/28/97 3:19p Hoffoss
148 * 30 3/20/97 3:55p Hoffoss
149 * Major changes to how dialog boxes initialize (load) and update (save)
150 * their internal data. This should simplify things and create less
153 * 29 3/17/97 4:29p Hoffoss
154 * Automated player's wing flaging as a starting player wing.
156 * 28 3/12/97 12:40p Hoffoss
157 * Fixed bugs in wing object management functions, several small additions
158 * and rearrangements.
160 * 27 3/04/97 6:27p Hoffoss
161 * Changes to Fred to handle new wing structure.
163 * 26 2/28/97 11:31a Hoffoss
164 * Implemented modeless dialog saving and restoring, and changed some
167 * 25 2/27/97 3:16p Allender
168 * major wing structure enhancement. simplified wing code. All around
169 * better wing support
171 * 24 2/27/97 2:31p Hoffoss
172 * Changed wing create code to bash ship's cues to false.
174 * 23 2/24/97 5:38p Hoffoss
175 * Added dialog box to name a wing at creation, code to change ship names
176 * to match wing name, and code to maintain these ship names.
178 * 22 2/20/97 4:03p Hoffoss
179 * Several ToDo items: new reinforcement clears arrival cue, reinforcement
180 * control from ship and wing dialogs, show grid toggle.
182 * 21 2/17/97 5:28p Hoffoss
183 * Checked RCS headers, added them were missing, changing description to
184 * something better, etc where needed.
192 #include "fredview.h"
200 #include "management.h"
201 #include "linklist.h"
204 #include "createwingdlg.h"
205 #include "management.h"
207 #define MULTI_WING 999999
210 #define new DEBUG_NEW
212 static char THIS_FILE[] = __FILE__;
215 int already_deleting_wing = 0;
217 void remove_player_from_wing(int player, int min = 1);
219 // Finds a free wing slot (i.e. unused)
224 for (i=0; i<MAX_WINGS; i++)
225 if (!Wings[i].wave_count)
231 int check_wing_dependencies(int wing_num)
235 name = Wings[wing_num].name;
236 return reference_handler(name, REF_TYPE_WING, -1);
239 void mark_wing(int wing)
244 SDL_assert(Wings[wing].special_ship >= 0 && Wings[wing].special_ship < Wings[wing].wave_count);
245 set_cur_object_index(wing_objects[wing][Wings[wing].special_ship]);
246 for (i=0; i<Wings[wing].wave_count; i++)
247 mark_object(wing_objects[wing][i]);
250 // delete a whole wing, also deleting it's ships if necessary.
251 int delete_wing(int wing_num, int bypass)
255 if (already_deleting_wing)
258 r = check_wing_dependencies(wing_num);
262 already_deleting_wing = 1;
263 for (i=0; i<Num_reinforcements; i++)
264 if (!stricmp(Wings[wing_num].name, Reinforcements[i].name)) {
265 delete_reinforcement(i);
269 invalidate_references(Wings[wing_num].name, REF_TYPE_WING);
271 total = Wings[wing_num].wave_count;
272 for (i=0; i<total; i++)
273 delete_object(wing_objects[wing_num][i]);
276 Wings[wing_num].wave_count = 0;
277 if (cur_wing == wing_num)
278 set_cur_wing(cur_wing = -1); // yes, one '=' is correct.
280 free_sexp2(Wings[wing_num].arrival_cue);
281 free_sexp2(Wings[wing_num].departure_cue);
285 already_deleting_wing = 0;
289 // delete a whole wing, leaving ships intact but wingless.
290 void remove_wing(int wing_num)
295 if (check_wing_dependencies(wing_num))
298 Ship_editor_dialog.bypass_errors = Wing_editor_dialog.bypass_errors = 1;
299 Ship_editor_dialog.update_data(0);
300 total = Wings[wing_num].wave_count;
301 for (i=0; i<total; i++) {
302 ptr = &Objects[wing_objects[wing_num][i]];
303 if (ptr->type == OBJ_SHIP)
304 remove_ship_from_wing(ptr->instance);
305 else if (ptr->type == OBJ_START)
306 remove_player_from_wing(ptr->instance);
309 SDL_assert(!Wings[wing_num].wave_count);
310 Ship_editor_dialog.initialize_data(1);
311 Ship_editor_dialog.bypass_errors = Wing_editor_dialog.bypass_errors = 0;
313 if (cur_wing == wing_num) {
314 set_cur_wing(cur_wing = -1); // yes, one '=' is correct.
320 // Takes a ship out of a wing, deleting wing if that was the only ship in it.
321 void remove_ship_from_wing(int ship, int min)
324 int i, wing, end, obj;
326 wing = Ships[ship].wingnum;
328 if (Wings[wing].wave_count == min) {
329 Wings[wing].wave_count = 0;
333 i = Wings[wing].wave_count;
336 if (wing_objects[wing][i] == Ships[ship].objnum)
339 SDL_assert(i != -1); // Error, object should be in wing.
340 if (Wings[wing].special_ship == i)
341 Wings[wing].special_ship = 0;
343 // if not last element, move last element to position to fill gap
345 obj = wing_objects[wing][i] = wing_objects[wing][end];
346 Wings[wing].ship_index[i] = Wings[wing].ship_index[end];
347 if (Objects[obj].type == OBJ_SHIP) {
348 sprintf(buf, "%s %d", Wings[wing].name, i + 1);
349 rename_ship(Wings[wing].ship_index[i], buf);
353 Wings[wing].wave_count--;
354 if (Wings[wing].wave_count && (Wings[wing].threshold >= Wings[wing].wave_count))
355 Wings[wing].threshold = Wings[wing].wave_count - 1;
358 Ships[ship].wingnum = -1;
362 // reset ship name to non-wing default ship name
363 sprintf(buf, "%s %d", Ship_info[Ships[ship].ship_info_index].name, ship);
364 rename_ship(ship, buf);
367 // Takes a player out of a wing, deleting wing if that was the only ship in it.
368 void remove_player_from_wing(int player, int min)
370 remove_ship_from_wing(player, min);
373 // Forms a wing from marked objects
377 int i, ship, wing = -1, waypoints = 0, count = 0, illegal_ships = 0;
378 int friendly, hostile, leader;
382 if (!query_valid_object())
385 leader = cur_object_index;
386 ptr = GET_FIRST(&obj_used_list);
387 while (ptr != END_OF_LIST(&obj_used_list)) {
388 if ((ptr->type == OBJ_SHIP) && (ptr->flags & OF_MARKED)) {
394 i = Ships[ptr->instance].wingnum;
409 if (count > MAX_SHIPS_PER_WING) {
410 sprintf(msg, "You have too many ships marked!\n"
411 "A wing is limited to %d ships total", MAX_SHIPS_PER_WING);
413 Fred_main_wnd->MessageBox(msg, "Error", MB_ICONEXCLAMATION);
417 if ((wing >= 0) && (wing != MULTI_WING)) {
418 sprintf(msg, "Do you want to reform wing \"%s\"?", Wings[wing].name);
419 i = Fred_main_wnd->MessageBox(msg, "Query", MB_YESNOCANCEL);
426 else { // must be IDYES
427 for (i=Wings[wing].wave_count-1; i>=0; i--) {
428 ptr = &Objects[wing_objects[wing][i]];
431 remove_ship_from_wing(ptr->instance, 0);
435 remove_player_from_wing(ptr->instance, 0);
439 Int3(); // shouldn't be in a wing!
443 SDL_assert(!Wings[wing].wave_count);
451 wing = find_free_wing();
452 Wings[wing].num_waves = 1;
453 Wings[wing].threshold = 0;
454 Wings[wing].arrival_location = Wings[wing].departure_location = 0;
455 Wings[wing].arrival_distance = 0;
456 Wings[wing].arrival_anchor = -1;
457 Wings[wing].arrival_cue = Locked_sexp_true;
458 Wings[wing].departure_cue = Locked_sexp_false;
459 Wings[wing].hotkey = -1;
460 Wings[wing].flags = 0;
462 for (i=0; i<MAX_AI_GOALS; i++) {
463 Wings[wing].ai_goals[i].ai_mode = AI_GOAL_NONE;
464 Wings[wing].ai_goals[i].priority = -1; // this sets up the priority field to be like ships
468 Fred_main_wnd->MessageBox("Too many wings, can't create more!",
469 "Error", MB_ICONEXCLAMATION);
474 if (dlg.DoModal() == IDCANCEL)
477 string_copy(Wings[wing].name, dlg.m_name, NAME_LENGTH - 1);
481 ptr = GET_FIRST(&obj_used_list);
482 while (ptr != END_OF_LIST(&obj_used_list)) {
483 if (ptr->flags & OF_MARKED) {
484 // if ((ptr->type == OBJ_START) && (ptr->instance)) {
486 // unmark_object(OBJ_INDEX(ptr));
488 // } else if (ptr->type == OBJ_WAYPOINT) {
489 if (ptr->type == OBJ_WAYPOINT) {
491 unmark_object(OBJ_INDEX(ptr));
493 } else if (ptr->type == OBJ_SHIP) {
494 switch (ship_query_general_type(ptr->instance))
496 case SHIP_TYPE_FIGHTER_BOMBER:
497 case SHIP_TYPE_CRUISER:
498 case SHIP_TYPE_AWACS:
499 case SHIP_TYPE_GAS_MINER:
500 case SHIP_TYPE_CORVETTE:
501 case SHIP_TYPE_FREIGHTER:
502 case SHIP_TYPE_CAPITAL:
503 case SHIP_TYPE_TRANSPORT:
504 case SHIP_TYPE_SUPERCAP:
509 unmark_object(OBJ_INDEX(ptr));
517 // if this wing is a player starting wing, automatically set the hotkey for this wing
518 for (i = 0; i < MAX_STARTING_WINGS; i++ ) {
519 if ( !stricmp(Wings[wing].name, Starting_wing_names[i]) ) {
520 Wings[wing].hotkey = i;
525 count = friendly = hostile = 0;
526 if (Objects[Ships[Player_start_shipnum].objnum].flags & OF_MARKED)
529 ptr = GET_FIRST(&obj_used_list);
530 while (ptr != END_OF_LIST(&obj_used_list)) {
531 if (ptr->flags & OF_MARKED) {
532 if ((ptr->type == OBJ_START) && (ptr->instance == Player_start_shipnum))
533 i = 0; // player 1 start always goes to front of the wing
537 SDL_assert((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START));
538 ship = ptr->instance;
539 if (Ships[ship].wingnum != -1) {
540 if (ptr->type == OBJ_SHIP)
541 remove_ship_from_wing(ship);
543 remove_player_from_wing(ptr->instance);
546 sprintf(msg, "%s %d", Wings[wing].name, i + 1);
547 rename_ship(ship, msg);
548 if (Ships[ship].team == TEAM_FRIENDLY)
550 else if ((Ships[ship].team == TEAM_HOSTILE) || (Ships[ship].team == TEAM_NEUTRAL))
553 Wings[wing].ship_index[i] = ship;
554 Ships[ship].wingnum = wing;
555 if (Ships[ship].arrival_cue >= 0)
556 free_sexp2(Ships[ship].arrival_cue);
558 Ships[ship].arrival_cue = Locked_sexp_false;
559 if (Ships[ship].departure_cue >= 0)
560 free_sexp2(Ships[ship].departure_cue);
562 Ships[ship].departure_cue = Locked_sexp_false;
564 wing_objects[wing][i] = OBJ_INDEX(ptr);
565 if (OBJ_INDEX(ptr) == leader)
566 Wings[wing].special_ship = i;
572 if (!count) // this should never happen, so if it does, needs to be fixed now.
573 Error(LOCATION, "No valid ships were selected to form wing from");
575 Wings[wing].wave_count = count;
579 // Fred_main_wnd->MessageBox("Multi-player starting points can't be part of a wing!\n"
580 // "All marked multi-player starting points were ignored",
581 // "Error", MB_ICONEXCLAMATION);
584 Fred_main_wnd->MessageBox("Waypoints can't be part of a wing!\n"
585 "All marked waypoints were ignored",
586 "Error", MB_ICONEXCLAMATION);
589 Fred_main_wnd->MessageBox("Some ship types aren't allowed to be in a wing.\n"
590 "All marked ships of these types were ignored",
591 "Error", MB_ICONEXCLAMATION);
593 if (friendly && hostile)
594 Fred_main_wnd->MessageBox("Both hostile and friendly ships in same wing", "Warning");
600 /////////////////////////////////////////////////////////////////////////////////////
601 // Old stuff down there..
603 #define MAX_WING_VECTORS 8 // 8 vectors per wing formation. Possible to have more
604 // than 8 ships. A vector is where a ship is located relative
605 // to the leader. Other ships can be located at the same
606 // vector relative to another member. So wing formation
607 // size is not limited by this constant.
608 #define MAX_WING_FORMATIONS 8 // 8 different kinds of wing formations
610 typedef struct formation {
612 vector offsets[MAX_WING_VECTORS];
615 formation Wing_formations[MAX_WING_FORMATIONS];
617 //wing Wings[MAX_WINGS];
619 int Wings_initialized = 0;
621 void initialize_wings(void)
623 if (Wings_initialized)
626 Wings_initialized = 1;
628 Wing_formations[0].num_vectors = 2;
630 Wing_formations[0].offsets[0].x = -5.0f;
631 Wing_formations[0].offsets[0].y = +1.0f;
632 Wing_formations[0].offsets[0].z = -5.0f;
634 Wing_formations[0].offsets[1].x = +5.0f;
635 Wing_formations[0].offsets[1].y = +1.0f;
636 Wing_formations[0].offsets[1].z = -5.0f;
639 void create_wings_from_objects(void)
643 for (i=0; i<MAX_WINGS; i++)
644 Wings[i].wave_count= 0;
646 for (i=0; i<MAX_OBJECTS; i++)
647 if (Objects[i].type != OBJ_NONE)
648 if (get_wingnum(i) != -1) {
649 int wingnum = get_wingnum(i);
651 SDL_assert((wingnum >= 0) && (wingnum < MAX_WINGS));
652 SDL_assert(Wings[wingnum].wave_count < MAX_SHIPS_PER_WING);
653 // JEH strcpy(Wings[wingnum].ship_names[Wings[wingnum].count++], i;
658 int get_free_objnum(void)
662 for (i=1; i<MAX_OBJECTS; i++)
663 if (Objects[i].type == OBJ_NONE)
670 // wing_type is the type of wing from the Wing_formations array to create.
671 // leader_index is the index in Objects of the leader object. This object must
672 // have a position and an orientation.
673 // *wingmen is a list of indices of existing ships to be added to the wing.
674 // The wingmen list is terminated by -1.
675 // max_size is the maximum number of ships to add to the wing
676 // fill_flag is set if more ships are to be added to fill out the wing to max_size
677 void create_wing(int wing_type, int leader_index, int *wingmen, int max_size, int fill_flag)
679 int num_placed, num_vectors, cur_vec_index;
680 object *lobjp = &Objects[leader_index];
683 int wing_list[MAX_OBJECTS];
688 SDL_assert((wing_type >= 0) && (wing_type < MAX_WING_FORMATIONS));
689 SDL_assert(Wing_formations[wing_type].num_vectors > 0);
690 SDL_assert(Wing_formations[wing_type].num_vectors < MAX_WING_VECTORS);
692 SDL_assert(Objects[leader_index].type != OBJ_NONE);
693 SDL_assert(max_size < MAX_SHIPS_PER_WING);
696 wingp = &Wing_formations[wing_type];
697 num_vectors = wingp->num_vectors;
700 vm_copy_transpose_matrix(&rotmat, &lobjp->orient);
702 while (num_placed < max_size) {
706 if (*wingmen == -1) {
710 curobj = get_free_objnum();
711 SDL_assert(curobj != -1);
712 Objects[curobj].type = lobjp->type;
713 SDL_assert(Wings[cur_wing].wave_count < MAX_SHIPS_PER_WING);
714 // JEH Wings[cur_wing].ship_list[Wings[cur_wing].count] = curobj;
715 Wings[cur_wing].wave_count++;
720 Objects[curobj] = *lobjp;
721 vm_vec_rotate(&wvec, &wingp->offsets[cur_vec_index], &rotmat);
722 cur_vec_index = (cur_vec_index + 1) % num_vectors;
724 if (num_placed < num_vectors)
727 parent = &Objects[wing_list[num_placed - num_vectors]];
729 wing_list[num_placed] = curobj;
731 vm_vec_add(&Objects[curobj].pos, &parent->pos, &wvec);
739 // cur_object_index becomes the leader.
740 // If count == -1, then all objects of wing cur_wing get added to the wing.
741 // If count == +n, then n objects are added to the wing.
742 void test_form_wing(int count)
744 int i, wingmen[MAX_OBJECTS];
749 SDL_assert(cur_object_index != -1);
750 SDL_assert(Objects[cur_object_index].type != OBJ_NONE);
751 SDL_assert(get_wingnum(cur_object_index) != -1);
752 get_wingnum(cur_object_index);
756 for (i=1; i<MAX_OBJECTS; i++)
757 if ((get_wingnum(i) == cur_wing) && (Objects[i].type != OBJ_NONE))
758 if (i != cur_object_index)
770 set_wingnum(cur_object_index, cur_wing);
771 create_wing(0, cur_object_index, wingmen, j, fill_flag);