]> icculus.org git repositories - taylor/freespace2.git/blob - src/fred2/wing.cpp
Initial revision
[taylor/freespace2.git] / src / fred2 / wing.cpp
1 /*
2  * $Logfile: /Freespace2/code/Fred2/wing.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Wing management functions for dealing with wing related operations
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:09  root
11  * Initial revision
12  *
13  * 
14  * 4     7/09/99 5:54p Dave
15  * Seperated cruiser types into individual types. Added tons of new
16  * briefing icons. Campaign screen.
17  * 
18  * 3     3/01/99 10:00a Dave
19  * Fxied several dogfight related stats bugs.
20  * 
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.
25  * 
26  * 1     10/07/98 3:02p Dave
27  * 
28  * 1     10/07/98 3:00p Dave
29  * 
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.
34  * 
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.
38  * 
39  * 57    12/29/97 4:55p Johnson
40  * Added some fixes.
41  * 
42  * 56    11/21/97 11:46a Johnson
43  * Changed code to delete reinforcement wings when the wing is deleted.
44  * 
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
49  * 
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.
53  * 
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.
57  * 
58  * 52    9/11/97 4:04p Hoffoss
59  * Fixed bug in Fred: disband wing wasn't making sure the ship editor's
60  * data was synced.
61  * 
62  * 51    9/06/97 2:13p Mike
63  * Replace support for TEAM_NEUTRAL
64  * 
65  * 50    9/02/97 3:44p Johnson
66  * Fixed bugs with thresholds not being lowered.
67  * 
68  * 49    9/02/97 3:24p Johnson
69  * Fixed bug: ships removed from wings will lower threshold if required.
70  * 
71  * 48    8/30/97 9:52p Hoffoss
72  * Implemented arrival location, distance, and anchor in Fred.
73  * 
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.
77  * 
78  * 46    8/16/97 6:44p Hoffoss
79  * Changes to allow any player to be in a wing.
80  * 
81  * 45    8/16/97 4:51p Hoffoss
82  * Fixed bugs with wing deletion and removing ships from a wing.
83  * 
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.
87  * 
88  * 43    8/12/97 1:55a Hoffoss
89  * Made extensive changes to object reference checking and handling for
90  * object deletion call.
91  * 
92  * 42    8/10/97 4:24p Hoffoss
93  * Added warning when creating a wing if wing is mixed.
94  * 
95  * 41    8/08/97 1:31p Hoffoss
96  * Added syncronization protection to cur_object_index changes.
97  * 
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).
101  * 
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.
105  * 
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
110  * 
111  * 37    6/30/97 11:36a Hoffoss
112  * Fixed bug with wing reforming.
113  * 
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.
117  * 
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
121  * 
122  * 34    5/14/97 4:08p Lawrance
123  * removing my_index from game arrays
124  * 
125  * 33    5/08/97 10:54a Hoffoss
126  * Fixed bug in reforming wings screwing up ship names.
127  * 
128  * 32    4/30/97 9:17a Hoffoss
129  * Hotkey for wing set to none when new wing created.
130  * 
131  * 31    3/28/97 3:19p Hoffoss
132  * Fixed bug.
133  * 
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
137  * problems.
138  * 
139  * 29    3/17/97 4:29p Hoffoss
140  * Automated player's wing flaging as a starting player wing.
141  * 
142  * 28    3/12/97 12:40p Hoffoss
143  * Fixed bugs in wing object management functions, several small additions
144  * and rearrangements.
145  * 
146  * 27    3/04/97 6:27p Hoffoss
147  * Changes to Fred to handle new wing structure.
148  * 
149  * 26    2/28/97 11:31a Hoffoss
150  * Implemented modeless dialog saving and restoring, and changed some
151  * variables names.
152  * 
153  * 25    2/27/97 3:16p Allender
154  * major wing structure enhancement.  simplified wing code.  All around
155  * better wing support
156  * 
157  * 24    2/27/97 2:31p Hoffoss
158  * Changed wing create code to bash ship's cues to false.
159  * 
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.
163  * 
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.
167  * 
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.
171  *
172  * $NoKeywords: $
173  */
174
175 #include "stdafx.h"
176 #include "fred.h"
177 #include "freddoc.h"
178 #include "fredview.h"
179 #include "3d.h"
180 #include "physics.h"
181 #include "object.h"
182 #include "editor.h"
183 #include "ailocal.h"
184 #include "ship.h"
185 #include "vecmat.h"
186 #include "management.h"
187 #include "linklist.h"
188 #include "mainfrm.h"
189 #include "wing.h"
190 #include "createwingdlg.h"
191 #include "management.h"
192
193 #define MULTI_WING      999999
194
195 #ifdef _DEBUG
196 #define new DEBUG_NEW
197 #undef THIS_FILE
198 static char THIS_FILE[] = __FILE__;
199 #endif
200
201 int already_deleting_wing = 0;
202
203 void remove_player_from_wing(int player, int min = 1);
204
205 // Finds a free wing slot (i.e. unused)
206 int find_free_wing()
207 {
208         int i;
209
210         for (i=0; i<MAX_WINGS; i++)
211                 if (!Wings[i].wave_count)
212                         return i;
213
214         return -1;
215 }
216
217 int check_wing_dependencies(int wing_num)
218 {
219         char *name;
220
221         name = Wings[wing_num].name;
222         return reference_handler(name, REF_TYPE_WING, -1);
223 }
224
225 void mark_wing(int wing)
226 {
227         int i;
228
229         unmark_all();
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]);
234 }
235
236 // delete a whole wing, also deleting it's ships if necessary.
237 int delete_wing(int wing_num, int bypass)
238 {
239         int i, r, total;
240
241         if (already_deleting_wing)
242                 return 0;
243
244         r = check_wing_dependencies(wing_num);
245         if (r)
246                 return r;
247
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);
252                         break;
253                 }
254
255         invalidate_references(Wings[wing_num].name, REF_TYPE_WING);
256         if (!bypass) {
257                 total = Wings[wing_num].wave_count;
258                 for (i=0; i<total; i++)
259                         delete_object(wing_objects[wing_num][i]);
260         }
261
262         Wings[wing_num].wave_count = 0;
263         if (cur_wing == wing_num)
264                 set_cur_wing(cur_wing = -1);  // yes, one '=' is correct.
265
266         free_sexp2(Wings[wing_num].arrival_cue);
267         free_sexp2(Wings[wing_num].departure_cue);
268
269         num_wings--;
270         set_modified();
271         already_deleting_wing = 0;
272         return 0;
273 }
274
275 // delete a whole wing, leaving ships intact but wingless.
276 void remove_wing(int wing_num)
277 {
278         int i, total;
279         object *ptr;
280
281         if (check_wing_dependencies(wing_num))
282                 return;
283
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);
293         }
294
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;
298
299         if (cur_wing == wing_num) {
300                 set_cur_wing(cur_wing = -1);  // yes, one '=' is correct.
301         }
302
303         set_modified();
304 }
305
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)
308 {
309         char buf[256];
310         int i, wing, end, obj;
311
312         wing = Ships[ship].wingnum;
313         if (wing != -1) {
314                 if (Wings[wing].wave_count == min) {
315                         Wings[wing].wave_count = 0;
316                         delete_wing(wing);
317
318                 } else {
319                         i = Wings[wing].wave_count;
320                         end = i - 1;
321                         while (i--)
322                                 if (wing_objects[wing][i] == Ships[ship].objnum)
323                                         break;
324
325                         Assert(i != -1);  // Error, object should be in wing.
326                         if (Wings[wing].special_ship == i)
327                                 Wings[wing].special_ship = 0;
328
329                         // if not last element, move last element to position to fill gap
330                         if (i != end) {
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);
336                                 }
337                         }
338
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;
342                 }
343
344                 Ships[ship].wingnum = -1;
345         }
346
347         set_modified();
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);
351 }
352
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)
355 {
356         remove_ship_from_wing(player, min);
357 }
358
359 // Forms a wing from marked objects
360 int create_wing()
361 {
362         char msg[1024];
363         int i, ship, wing = -1, waypoints = 0, count = 0, illegal_ships = 0;
364         int friendly, hostile, leader;
365         object *ptr;
366         create_wing_dlg dlg;
367
368         if (!query_valid_object())
369                 return -1;
370
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)) {
375                         count++;
376                         i = -1;
377                         switch (ptr->type) {
378                                 case OBJ_SHIP:
379                                 case OBJ_START:
380                                         i = Ships[ptr->instance].wingnum;
381                                         break;
382                         }
383
384                         if (i >= 0) {
385                                 if (wing < 0)
386                                         wing = i;
387                                 else if (wing != i)
388                                         wing = MULTI_WING;
389                         }
390                 }
391
392                 ptr = GET_NEXT(ptr);
393         }
394
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);
398
399                 Fred_main_wnd->MessageBox(msg, "Error", MB_ICONEXCLAMATION);
400                 return -1;
401         }
402
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);
406                 if (i == IDCANCEL)
407                         return -1;
408
409                 else if (i == IDNO)
410                         wing = -1;
411
412                 else {  // must be IDYES
413                         for (i=Wings[wing].wave_count-1; i>=0; i--) {
414                                 ptr = &Objects[wing_objects[wing][i]];
415                                 switch (ptr->type) {
416                                         case OBJ_SHIP:
417                                                 remove_ship_from_wing(ptr->instance, 0);
418                                                 break;
419
420                                         case OBJ_START:
421                                                 remove_player_from_wing(ptr->instance, 0);
422                                                 break;
423
424                                         default:
425                                                 Int3();  // shouldn't be in a wing!
426                                 }
427                         }
428
429                         Assert(!Wings[wing].wave_count);
430                         num_wings--;
431                 }
432
433         } else
434                 wing = -1;
435
436         if (wing < 0) {
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;
447
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
451                 }
452
453                 if (wing < 0) {
454                         Fred_main_wnd->MessageBox("Too many wings, can't create more!",
455                                 "Error", MB_ICONEXCLAMATION);
456
457                         return -1;
458                 }
459
460                 if (dlg.DoModal() == IDCANCEL)
461                         return -1;
462
463                 string_copy(Wings[wing].name, dlg.m_name, NAME_LENGTH - 1);
464         }
465
466         set_cur_indices(-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)) {
471 //                              starts++;
472 //                              unmark_object(OBJ_INDEX(ptr));
473
474 //                      } else if (ptr->type == OBJ_WAYPOINT) {
475                         if (ptr->type == OBJ_WAYPOINT) {
476                                 waypoints++;
477                                 unmark_object(OBJ_INDEX(ptr));
478
479                         } else if (ptr->type == OBJ_SHIP) {
480                                 switch (ship_query_general_type(ptr->instance))
481                                 {
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:
491                                                 break;
492
493                                         default:
494                                                 illegal_ships++;
495                                                 unmark_object(OBJ_INDEX(ptr));
496                                 }
497                         }
498                 }
499
500                 ptr = GET_NEXT(ptr);
501         }
502
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;
507                         break;
508                 }
509         }
510
511         count = friendly = hostile = 0;
512         if (Objects[Ships[Player_start_shipnum].objnum].flags & OF_MARKED)
513                 count = 1;
514
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
520                         else
521                                 i = count++;
522
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);
528                                 else
529                                         remove_player_from_wing(ptr->instance);
530                         }
531
532                         sprintf(msg, "%s %d", Wings[wing].name, i + 1);
533                         rename_ship(ship, msg);
534                         if (Ships[ship].team == TEAM_FRIENDLY)
535                                 friendly = 1;
536                         else if ((Ships[ship].team == TEAM_HOSTILE) || (Ships[ship].team == TEAM_NEUTRAL))
537                                 hostile = 1;
538
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);
543
544                         Ships[ship].arrival_cue = Locked_sexp_false;
545                         if (Ships[ship].departure_cue >= 0)
546                                 free_sexp2(Ships[ship].departure_cue);
547
548                         Ships[ship].departure_cue = Locked_sexp_false;
549
550                         wing_objects[wing][i] = OBJ_INDEX(ptr);
551                         if (OBJ_INDEX(ptr) == leader)
552                                 Wings[wing].special_ship = i;
553                 }
554
555                 ptr = GET_NEXT(ptr);
556         }
557
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");
560
561         Wings[wing].wave_count = count;
562         num_wings++;
563
564 //      if (starts)
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);
568
569         if (waypoints)
570                 Fred_main_wnd->MessageBox("Waypoints can't be part of a wing!\n"
571                         "All marked waypoints were ignored",
572                         "Error", MB_ICONEXCLAMATION);
573
574         if (illegal_ships)
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);
578
579         if (friendly && hostile)
580                 Fred_main_wnd->MessageBox("Both hostile and friendly ships in same wing", "Warning");
581
582         mark_wing(wing);
583         return 0;
584 }
585
586 /////////////////////////////////////////////////////////////////////////////////////
587 // Old stuff down there..
588
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
595
596 typedef struct formation {
597         int             num_vectors;
598         vector  offsets[MAX_WING_VECTORS];
599 } formation;
600
601 formation Wing_formations[MAX_WING_FORMATIONS];
602
603 //wing  Wings[MAX_WINGS];
604
605 int     Wings_initialized = 0;
606
607 void initialize_wings(void)
608 {
609         if (Wings_initialized)
610                 return;
611
612         Wings_initialized = 1;
613
614         Wing_formations[0].num_vectors = 2;
615         
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;
619
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;
623 }
624
625 void create_wings_from_objects(void)
626 {
627         int     i;
628
629         for (i=0; i<MAX_WINGS; i++)
630                 Wings[i].wave_count= 0;
631
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);
636                                 
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;
640                         }
641
642 }
643
644 int get_free_objnum(void)
645 {
646         int     i;
647
648         for (i=1; i<MAX_OBJECTS; i++)
649                 if (Objects[i].type == OBJ_NONE)
650                         return i;
651
652         return -1;
653 }
654
655 //      Create a wing.
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)
664 {
665         int                     num_placed, num_vectors, cur_vec_index;
666         object          *lobjp = &Objects[leader_index];
667         formation       *wingp;
668         object          *parent;
669         int                     wing_list[MAX_OBJECTS];
670         matrix          rotmat;
671
672         initialize_wings();
673
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);
677
678         Assert(Objects[leader_index].type != OBJ_NONE);
679         Assert(max_size < MAX_SHIPS_PER_WING);
680
681         num_placed = 0;
682         wingp = &Wing_formations[wing_type];
683         num_vectors = wingp->num_vectors;
684         cur_vec_index = 0;
685         parent = lobjp;
686         vm_copy_transpose_matrix(&rotmat, &lobjp->orient);
687
688         while (num_placed < max_size) {
689                 vector  wvec;
690                 int             curobj;
691
692                 if (*wingmen == -1) {
693                         if (!fill_flag)
694                                 break;
695                         else {
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++;
702                         }
703                 } else
704                         curobj = *wingmen++;
705
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;
709                 
710                 if (num_placed < num_vectors)
711                         parent = lobjp;
712                 else
713                         parent = &Objects[wing_list[num_placed - num_vectors]];
714                 
715                 wing_list[num_placed] = curobj;
716
717                 vm_vec_add(&Objects[curobj].pos, &parent->pos, &wvec);
718
719                 num_placed++;
720         }
721
722 }
723
724 //      Create a wing.
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)
729 {
730         int     i, wingmen[MAX_OBJECTS];
731         int     j, fill_flag;
732
733         j = 0;
734
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);
739
740         wingmen[0] = -1;
741
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)
745                                 wingmen[j++] = i;
746         
747         wingmen[j] = -1;
748
749         fill_flag = 0;
750
751         if (count > 0) {
752                 fill_flag = 1;
753                 j += count;
754         }
755
756         set_wingnum(cur_object_index, cur_wing);
757         create_wing(0, cur_object_index, wingmen, j, fill_flag);
758 }