]> icculus.org git repositories - taylor/freespace2.git/blob - src/fred2/wing.cpp
The Great Newline Fix
[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.2  2002/05/07 03:16:44  theoddone33
11  * The Great Newline Fix
12  *
13  * Revision 1.1.1.1  2002/05/03 03:28:09  root
14  * Initial import.
15  *
16  * 
17  * 4     7/09/99 5:54p Dave
18  * Seperated cruiser types into individual types. Added tons of new
19  * briefing icons. Campaign screen.
20  * 
21  * 3     3/01/99 10:00a Dave
22  * Fxied several dogfight related stats bugs.
23  * 
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.
28  * 
29  * 1     10/07/98 3:02p Dave
30  * 
31  * 1     10/07/98 3:00p Dave
32  * 
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.
37  * 
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.
41  * 
42  * 57    12/29/97 4:55p Johnson
43  * Added some fixes.
44  * 
45  * 56    11/21/97 11:46a Johnson
46  * Changed code to delete reinforcement wings when the wing is deleted.
47  * 
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
52  * 
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.
56  * 
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.
60  * 
61  * 52    9/11/97 4:04p Hoffoss
62  * Fixed bug in Fred: disband wing wasn't making sure the ship editor's
63  * data was synced.
64  * 
65  * 51    9/06/97 2:13p Mike
66  * Replace support for TEAM_NEUTRAL
67  * 
68  * 50    9/02/97 3:44p Johnson
69  * Fixed bugs with thresholds not being lowered.
70  * 
71  * 49    9/02/97 3:24p Johnson
72  * Fixed bug: ships removed from wings will lower threshold if required.
73  * 
74  * 48    8/30/97 9:52p Hoffoss
75  * Implemented arrival location, distance, and anchor in Fred.
76  * 
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.
80  * 
81  * 46    8/16/97 6:44p Hoffoss
82  * Changes to allow any player to be in a wing.
83  * 
84  * 45    8/16/97 4:51p Hoffoss
85  * Fixed bugs with wing deletion and removing ships from a wing.
86  * 
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.
90  * 
91  * 43    8/12/97 1:55a Hoffoss
92  * Made extensive changes to object reference checking and handling for
93  * object deletion call.
94  * 
95  * 42    8/10/97 4:24p Hoffoss
96  * Added warning when creating a wing if wing is mixed.
97  * 
98  * 41    8/08/97 1:31p Hoffoss
99  * Added syncronization protection to cur_object_index changes.
100  * 
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).
104  * 
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.
108  * 
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
113  * 
114  * 37    6/30/97 11:36a Hoffoss
115  * Fixed bug with wing reforming.
116  * 
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.
120  * 
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
124  * 
125  * 34    5/14/97 4:08p Lawrance
126  * removing my_index from game arrays
127  * 
128  * 33    5/08/97 10:54a Hoffoss
129  * Fixed bug in reforming wings screwing up ship names.
130  * 
131  * 32    4/30/97 9:17a Hoffoss
132  * Hotkey for wing set to none when new wing created.
133  * 
134  * 31    3/28/97 3:19p Hoffoss
135  * Fixed bug.
136  * 
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
140  * problems.
141  * 
142  * 29    3/17/97 4:29p Hoffoss
143  * Automated player's wing flaging as a starting player wing.
144  * 
145  * 28    3/12/97 12:40p Hoffoss
146  * Fixed bugs in wing object management functions, several small additions
147  * and rearrangements.
148  * 
149  * 27    3/04/97 6:27p Hoffoss
150  * Changes to Fred to handle new wing structure.
151  * 
152  * 26    2/28/97 11:31a Hoffoss
153  * Implemented modeless dialog saving and restoring, and changed some
154  * variables names.
155  * 
156  * 25    2/27/97 3:16p Allender
157  * major wing structure enhancement.  simplified wing code.  All around
158  * better wing support
159  * 
160  * 24    2/27/97 2:31p Hoffoss
161  * Changed wing create code to bash ship's cues to false.
162  * 
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.
166  * 
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.
170  * 
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.
174  *
175  * $NoKeywords: $
176  */
177
178 #include "stdafx.h"
179 #include "fred.h"
180 #include "freddoc.h"
181 #include "fredview.h"
182 #include "3d.h"
183 #include "physics.h"
184 #include "object.h"
185 #include "editor.h"
186 #include "ailocal.h"
187 #include "ship.h"
188 #include "vecmat.h"
189 #include "management.h"
190 #include "linklist.h"
191 #include "mainfrm.h"
192 #include "wing.h"
193 #include "createwingdlg.h"
194 #include "management.h"
195
196 #define MULTI_WING      999999
197
198 #ifdef _DEBUG
199 #define new DEBUG_NEW
200 #undef THIS_FILE
201 static char THIS_FILE[] = __FILE__;
202 #endif
203
204 int already_deleting_wing = 0;
205
206 void remove_player_from_wing(int player, int min = 1);
207
208 // Finds a free wing slot (i.e. unused)
209 int find_free_wing()
210 {
211         int i;
212
213         for (i=0; i<MAX_WINGS; i++)
214                 if (!Wings[i].wave_count)
215                         return i;
216
217         return -1;
218 }
219
220 int check_wing_dependencies(int wing_num)
221 {
222         char *name;
223
224         name = Wings[wing_num].name;
225         return reference_handler(name, REF_TYPE_WING, -1);
226 }
227
228 void mark_wing(int wing)
229 {
230         int i;
231
232         unmark_all();
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]);
237 }
238
239 // delete a whole wing, also deleting it's ships if necessary.
240 int delete_wing(int wing_num, int bypass)
241 {
242         int i, r, total;
243
244         if (already_deleting_wing)
245                 return 0;
246
247         r = check_wing_dependencies(wing_num);
248         if (r)
249                 return r;
250
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);
255                         break;
256                 }
257
258         invalidate_references(Wings[wing_num].name, REF_TYPE_WING);
259         if (!bypass) {
260                 total = Wings[wing_num].wave_count;
261                 for (i=0; i<total; i++)
262                         delete_object(wing_objects[wing_num][i]);
263         }
264
265         Wings[wing_num].wave_count = 0;
266         if (cur_wing == wing_num)
267                 set_cur_wing(cur_wing = -1);  // yes, one '=' is correct.
268
269         free_sexp2(Wings[wing_num].arrival_cue);
270         free_sexp2(Wings[wing_num].departure_cue);
271
272         num_wings--;
273         set_modified();
274         already_deleting_wing = 0;
275         return 0;
276 }
277
278 // delete a whole wing, leaving ships intact but wingless.
279 void remove_wing(int wing_num)
280 {
281         int i, total;
282         object *ptr;
283
284         if (check_wing_dependencies(wing_num))
285                 return;
286
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);
296         }
297
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;
301
302         if (cur_wing == wing_num) {
303                 set_cur_wing(cur_wing = -1);  // yes, one '=' is correct.
304         }
305
306         set_modified();
307 }
308
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)
311 {
312         char buf[256];
313         int i, wing, end, obj;
314
315         wing = Ships[ship].wingnum;
316         if (wing != -1) {
317                 if (Wings[wing].wave_count == min) {
318                         Wings[wing].wave_count = 0;
319                         delete_wing(wing);
320
321                 } else {
322                         i = Wings[wing].wave_count;
323                         end = i - 1;
324                         while (i--)
325                                 if (wing_objects[wing][i] == Ships[ship].objnum)
326                                         break;
327
328                         Assert(i != -1);  // Error, object should be in wing.
329                         if (Wings[wing].special_ship == i)
330                                 Wings[wing].special_ship = 0;
331
332                         // if not last element, move last element to position to fill gap
333                         if (i != end) {
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);
339                                 }
340                         }
341
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;
345                 }
346
347                 Ships[ship].wingnum = -1;
348         }
349
350         set_modified();
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);
354 }
355
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)
358 {
359         remove_ship_from_wing(player, min);
360 }
361
362 // Forms a wing from marked objects
363 int create_wing()
364 {
365         char msg[1024];
366         int i, ship, wing = -1, waypoints = 0, count = 0, illegal_ships = 0;
367         int friendly, hostile, leader;
368         object *ptr;
369         create_wing_dlg dlg;
370
371         if (!query_valid_object())
372                 return -1;
373
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)) {
378                         count++;
379                         i = -1;
380                         switch (ptr->type) {
381                                 case OBJ_SHIP:
382                                 case OBJ_START:
383                                         i = Ships[ptr->instance].wingnum;
384                                         break;
385                         }
386
387                         if (i >= 0) {
388                                 if (wing < 0)
389                                         wing = i;
390                                 else if (wing != i)
391                                         wing = MULTI_WING;
392                         }
393                 }
394
395                 ptr = GET_NEXT(ptr);
396         }
397
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);
401
402                 Fred_main_wnd->MessageBox(msg, "Error", MB_ICONEXCLAMATION);
403                 return -1;
404         }
405
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);
409                 if (i == IDCANCEL)
410                         return -1;
411
412                 else if (i == IDNO)
413                         wing = -1;
414
415                 else {  // must be IDYES
416                         for (i=Wings[wing].wave_count-1; i>=0; i--) {
417                                 ptr = &Objects[wing_objects[wing][i]];
418                                 switch (ptr->type) {
419                                         case OBJ_SHIP:
420                                                 remove_ship_from_wing(ptr->instance, 0);
421                                                 break;
422
423                                         case OBJ_START:
424                                                 remove_player_from_wing(ptr->instance, 0);
425                                                 break;
426
427                                         default:
428                                                 Int3();  // shouldn't be in a wing!
429                                 }
430                         }
431
432                         Assert(!Wings[wing].wave_count);
433                         num_wings--;
434                 }
435
436         } else
437                 wing = -1;
438
439         if (wing < 0) {
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;
450
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
454                 }
455
456                 if (wing < 0) {
457                         Fred_main_wnd->MessageBox("Too many wings, can't create more!",
458                                 "Error", MB_ICONEXCLAMATION);
459
460                         return -1;
461                 }
462
463                 if (dlg.DoModal() == IDCANCEL)
464                         return -1;
465
466                 string_copy(Wings[wing].name, dlg.m_name, NAME_LENGTH - 1);
467         }
468
469         set_cur_indices(-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)) {
474 //                              starts++;
475 //                              unmark_object(OBJ_INDEX(ptr));
476
477 //                      } else if (ptr->type == OBJ_WAYPOINT) {
478                         if (ptr->type == OBJ_WAYPOINT) {
479                                 waypoints++;
480                                 unmark_object(OBJ_INDEX(ptr));
481
482                         } else if (ptr->type == OBJ_SHIP) {
483                                 switch (ship_query_general_type(ptr->instance))
484                                 {
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:
494                                                 break;
495
496                                         default:
497                                                 illegal_ships++;
498                                                 unmark_object(OBJ_INDEX(ptr));
499                                 }
500                         }
501                 }
502
503                 ptr = GET_NEXT(ptr);
504         }
505
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;
510                         break;
511                 }
512         }
513
514         count = friendly = hostile = 0;
515         if (Objects[Ships[Player_start_shipnum].objnum].flags & OF_MARKED)
516                 count = 1;
517
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
523                         else
524                                 i = count++;
525
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);
531                                 else
532                                         remove_player_from_wing(ptr->instance);
533                         }
534
535                         sprintf(msg, "%s %d", Wings[wing].name, i + 1);
536                         rename_ship(ship, msg);
537                         if (Ships[ship].team == TEAM_FRIENDLY)
538                                 friendly = 1;
539                         else if ((Ships[ship].team == TEAM_HOSTILE) || (Ships[ship].team == TEAM_NEUTRAL))
540                                 hostile = 1;
541
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);
546
547                         Ships[ship].arrival_cue = Locked_sexp_false;
548                         if (Ships[ship].departure_cue >= 0)
549                                 free_sexp2(Ships[ship].departure_cue);
550
551                         Ships[ship].departure_cue = Locked_sexp_false;
552
553                         wing_objects[wing][i] = OBJ_INDEX(ptr);
554                         if (OBJ_INDEX(ptr) == leader)
555                                 Wings[wing].special_ship = i;
556                 }
557
558                 ptr = GET_NEXT(ptr);
559         }
560
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");
563
564         Wings[wing].wave_count = count;
565         num_wings++;
566
567 //      if (starts)
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);
571
572         if (waypoints)
573                 Fred_main_wnd->MessageBox("Waypoints can't be part of a wing!\n"
574                         "All marked waypoints were ignored",
575                         "Error", MB_ICONEXCLAMATION);
576
577         if (illegal_ships)
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);
581
582         if (friendly && hostile)
583                 Fred_main_wnd->MessageBox("Both hostile and friendly ships in same wing", "Warning");
584
585         mark_wing(wing);
586         return 0;
587 }
588
589 /////////////////////////////////////////////////////////////////////////////////////
590 // Old stuff down there..
591
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
598
599 typedef struct formation {
600         int             num_vectors;
601         vector  offsets[MAX_WING_VECTORS];
602 } formation;
603
604 formation Wing_formations[MAX_WING_FORMATIONS];
605
606 //wing  Wings[MAX_WINGS];
607
608 int     Wings_initialized = 0;
609
610 void initialize_wings(void)
611 {
612         if (Wings_initialized)
613                 return;
614
615         Wings_initialized = 1;
616
617         Wing_formations[0].num_vectors = 2;
618         
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;
622
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;
626 }
627
628 void create_wings_from_objects(void)
629 {
630         int     i;
631
632         for (i=0; i<MAX_WINGS; i++)
633                 Wings[i].wave_count= 0;
634
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);
639                                 
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;
643                         }
644
645 }
646
647 int get_free_objnum(void)
648 {
649         int     i;
650
651         for (i=1; i<MAX_OBJECTS; i++)
652                 if (Objects[i].type == OBJ_NONE)
653                         return i;
654
655         return -1;
656 }
657
658 //      Create a wing.
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)
667 {
668         int                     num_placed, num_vectors, cur_vec_index;
669         object          *lobjp = &Objects[leader_index];
670         formation       *wingp;
671         object          *parent;
672         int                     wing_list[MAX_OBJECTS];
673         matrix          rotmat;
674
675         initialize_wings();
676
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);
680
681         Assert(Objects[leader_index].type != OBJ_NONE);
682         Assert(max_size < MAX_SHIPS_PER_WING);
683
684         num_placed = 0;
685         wingp = &Wing_formations[wing_type];
686         num_vectors = wingp->num_vectors;
687         cur_vec_index = 0;
688         parent = lobjp;
689         vm_copy_transpose_matrix(&rotmat, &lobjp->orient);
690
691         while (num_placed < max_size) {
692                 vector  wvec;
693                 int             curobj;
694
695                 if (*wingmen == -1) {
696                         if (!fill_flag)
697                                 break;
698                         else {
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++;
705                         }
706                 } else
707                         curobj = *wingmen++;
708
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;
712                 
713                 if (num_placed < num_vectors)
714                         parent = lobjp;
715                 else
716                         parent = &Objects[wing_list[num_placed - num_vectors]];
717                 
718                 wing_list[num_placed] = curobj;
719
720                 vm_vec_add(&Objects[curobj].pos, &parent->pos, &wvec);
721
722                 num_placed++;
723         }
724
725 }
726
727 //      Create a wing.
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)
732 {
733         int     i, wingmen[MAX_OBJECTS];
734         int     j, fill_flag;
735
736         j = 0;
737
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);
742
743         wingmen[0] = -1;
744
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)
748                                 wingmen[j++] = i;
749         
750         wingmen[j] = -1;
751
752         fill_flag = 0;
753
754         if (count > 0) {
755                 fill_flag = 1;
756                 j += count;
757         }
758
759         set_wingnum(cur_object_index, cur_wing);
760         create_wing(0, cur_object_index, wingmen, j, fill_flag);
761 }