]> icculus.org git repositories - taylor/freespace2.git/blob - src/hud/hudsquadmsg.cpp
removed some old stuff
[taylor/freespace2.git] / src / hud / hudsquadmsg.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
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
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Hud/HUDsquadmsg.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * File to control sqaudmate messaging
16  *
17  * $Log$
18  * Revision 1.4  2002/07/13 06:46:48  theoddone33
19  * Warning cleanups
20  *
21  * Revision 1.3  2002/06/09 04:41:21  relnev
22  * added copyright header
23  *
24  * Revision 1.2  2002/05/07 03:16:45  theoddone33
25  * The Great Newline Fix
26  *
27  * Revision 1.1.1.1  2002/05/03 03:28:09  root
28  * Initial import.
29  *
30  * 
31  * 16    9/06/99 10:45a Andsager
32  * Add freighter to player override of protected status.
33  * 
34  * 15    9/06/99 10:32a Andsager
35  * Allow attack of protected fighter, bomber, freighters, by player's
36  * orders.
37  * 
38  * 14    8/26/99 8:51p Dave
39  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
40  * 
41  * 13    7/30/99 10:31p Dave
42  * Added comm menu to the configurable hud files.
43  * 
44  * 12    7/09/99 5:54p Dave
45  * Seperated cruiser types into individual types. Added tons of new
46  * briefing icons. Campaign screen.
47  * 
48  * 11    6/16/99 10:20a Dave
49  * Added send-message-list sexpression.
50  * 
51  * 10    6/09/99 9:53a Andsager
52  * 1st pass at grey menu items when no ships/wings/fighters accepting
53  * orders.
54  * 
55  * 9     4/23/99 12:01p Johnson
56  * Added SIF_HUGE_SHIP
57  * 
58  * 8     4/16/99 5:54p Dave
59  * Support for on/off style "stream" weapons. Real early support for
60  * target-painting lasers.
61  * 
62  * 7     3/30/99 5:40p Dave
63  * Fixed reinforcements for TvT in multiplayer.
64  * 
65  * 6     3/28/99 5:58p Dave
66  * Added early demo code. Make objects move. Nice and framerate
67  * independant, but not much else. Don't use yet unless you're me :)
68  * 
69  * 5     1/07/99 9:07a Jasen
70  * HUD coords
71  * 
72  * 4     12/28/98 3:17p Dave
73  * Support for multiple hud bitmap filenames for hi-res mode.
74  * 
75  * 3     12/21/98 5:02p Dave
76  * Modified all hud elements to be multi-resolution friendly.
77  * 
78  * 2     10/07/98 10:53a Dave
79  * Initial checkin.
80  * 
81  * 1     10/07/98 10:49a Dave
82  * 
83  * 198   9/11/98 2:05p Allender
84  * make reinforcements work correctly in multiplayer games.  There still
85  * may be a team vs team issue that I haven't thought of yet :-(
86  * 
87  * 197   8/28/98 3:28p Dave
88  * EMP effect done. AI effects may need some tweaking as required.
89  * 
90  * 196   8/25/98 1:48p Dave
91  * First rev of EMP effect. Player side stuff basically done. Next comes
92  * AI code.
93  * 
94  * 195   6/30/98 2:17p Dave
95  * Revised object update system. Removed updates for all weapons. Put
96  * button info back into control info packet.
97  * 
98  * 194   6/09/98 10:31a Hoffoss
99  * Created index numbers for all xstr() references.  Any new xstr() stuff
100  * added from here on out should be added to the end if the list.  The
101  * current list count can be found in FreeSpace.cpp (search for
102  * XSTR_SIZE).
103  * 
104  * 193   5/26/98 11:54a Allender
105  * fix multiplayer problems and sexpression crash
106  * 
107  * 192   5/24/98 4:27p Allender
108  * fix bug when determine who could message in multiplayer
109  * 
110  * 191   5/23/98 2:34a Lawrance
111  * Fix problems with HUD squad messaging, don't save/restore bindings
112  * 
113  * 190   5/22/98 12:41a Allender
114  * don't save/restore key presses in comm menu
115  * 
116  * 189   5/21/98 9:38p Allender
117  * don't save/clear key bindings
118  * 
119  * 188   5/21/98 3:32p Allender
120  * don't allow comm menu in observer mode
121  * 
122  * 187   5/19/98 12:19p Mike
123  * Cheat codes!
124  * 
125  * 186   5/18/98 10:08a Lawrance
126  * increase MSG_KEY_EAT_TIME to 300ms
127  * 
128  * 185   5/18/98 12:41a Allender
129  * fixed subsystem problems on clients (i.e. not reporting properly on
130  * damage indicator).  Fixed ingame join problem with respawns.  minor
131  * comm menu stuff
132  * 
133  * 184   5/13/98 5:08p Allender
134  * fix code in which sometimes wings wouldn't respond in multiplayer when
135  * doing a message all fighters
136  * 
137  * 183   5/08/98 4:38p Allender
138  * always allow player ships to count when counting fighters for
139  * messaging.  Terran command will now always issue the shooting at
140  * friendlies message
141  * 
142  * 182   5/08/98 2:11p Mike
143  * Add "/Repair Subsys" to "Rearm" option in Comm menu.
144  * 
145  * 181   5/06/98 2:57p Allender
146  * always allow rearm ship to be called in
147  * 
148  * 180   5/05/98 2:04a Mike
149  * Fix bug in support ship code.
150  * 
151  * 179   5/05/98 1:41a Mike
152  * Improve support ship availability.
153  * 
154  * 178   5/04/98 12:59a Allender
155  * players who are traitors shouldn't be allowed to rearm or use messaging
156  * shortcuts
157  * 
158  * 177   5/04/98 12:39a Allender
159  * make page up and page down only active when > 10 items on menu
160  * 
161  * 176   4/29/98 10:56p Allender
162  * don't allow shortcuts in mutliplayer when player cannot message (except
163  * for rearm repair)
164  * 
165  * 175   4/23/98 10:06a Allender
166  * don't use the word "player" in event log for rearm event.  Send
167  * shipname instead (players only)
168  * 
169  * 174   4/23/98 9:15a Allender
170  * make rearm shortcut work for clients
171  * 
172  * 173   4/23/98 1:49a Allender
173  * major rearm/repair fixes for multiplayer.  Fixed respawning of AI ships
174  * to not respawn until 5 seconds after they die.  Send escort information
175  * to ingame joiners
176  * 
177  * 172   4/22/98 4:59p Allender
178  * new multiplayer dead popup.  big changes to the comm menu system for
179  * team vs. team.  Start of debriefing stuff for team vs. team  Make form
180  * on my wing work with individual ships who have high priority orders
181  * 
182  * 171   4/21/98 12:15a Allender
183  * don't allow observers to use shortcut messaging keys
184  * 
185  * 170   4/20/98 12:36a Mike
186  * Make team vs. team work when player is hostile.  Several targeting
187  * problems.
188  * 
189  * 169   4/13/98 12:51p Allender
190  * made countermeasure succeed indicator work in multiplayer.  Make rearm
191  * shortcut work more appropriately.
192  * 
193  * 168   4/10/98 2:42p Johnson
194  * (from allender)  when sending wing command, don't assert if ship to
195  * send message not found -- don't send message.  Allow rearm message
196  * shortcut even if comm destroyed
197  * 
198  * 167   4/10/98 2:39p Johnson
199  * 
200  * 166   4/10/98 12:47p Allender
201  * changed working on replay popup.  Don't reference repair in comm menu.
202  * Added Shift-R for repair me
203  * 
204  * 165   4/09/98 12:35p Allender
205  * disallow messaging to departing wings and departing/dying ships
206  * 
207  * 164   4/08/98 4:06p Allender
208  * make selection of wing Player team based, not TEAM_FRIENDLY.
209  * 
210  * 163   4/07/98 5:30p Lawrance
211  * Player can't send/receive messages when comm is destroyed.  Garble
212  * messages when comm is damaged.
213  * 
214  * 162   4/07/98 1:53p Lawrance
215  * Fix uninitialized data bug.
216  * 
217  * 161   4/06/98 12:11a Allender
218  * prevent the comm menu keys from being held over after menu goes away
219  * 
220  * 160   4/05/98 3:06p Allender
221  * don't allow ships/wings to act on orders which they shouldn't receive
222  * 
223  * 159   4/03/98 12:17a Allender
224  * new sexpression to detect departed or destroyed.  optionally disallow
225  * support ships.  Allow docking with escape pods 
226  * 
227  * 158   4/02/98 5:50p Dave
228  * Put in support for standard comm messages to get sent to netplayers as
229  * well as ai ships. Make critical button presses not get evaluated on the
230  * observer.
231  *
232  * $NoKeywords: $
233 */
234  
235
236 #include "freespace.h"
237 #include "2d.h"
238 #include "hud.h"
239 #include "ship.h"
240 #include "player.h"
241 #include "key.h"
242 #include "hudtarget.h"
243 #include "timer.h"
244 #include "hudsquadmsg.h"
245 #include "controlsconfig.h"
246 #include "parselo.h"
247 #include "aigoals.h"
248 #include "missionparse.h"
249 #include "sexp.h"
250 #include "linklist.h"
251 #include "missionlog.h"
252 #include "missionmessage.h"
253 #include "hudtarget.h"
254 #include "gamesnd.h"
255 #include "sound.h"
256 #include "missionparse.h"
257 #include "multimsgs.h"
258 #include "multiutil.h"
259 #include "bmpman.h"
260 #include "hudtargetbox.h"
261 #include "multi_pmsg.h"
262 #include "subsysdamage.h"
263 #include "emp.h"
264
265 // defines for different modes in the squad messaging system
266
267 #define SM_MODE_TYPE_SELECT                     1               //am I going to message a ship or a wing
268 #define SM_MODE_SHIP_SELECT                     2               //choosing actual ship
269 #define SM_MODE_WING_SELECT                     3               //choosing actual wing
270 #define SM_MODE_SHIP_COMMAND                    4               //which command to send to a ship
271 #define SM_MODE_WING_COMMAND                    5               //which command to send to a wing
272 #define SM_MODE_REINFORCEMENTS          6               //call for reinforcements
273 #define SM_MODE_REPAIR_REARM                    7               //repair/rearm player ship
274 #define SM_MODE_REPAIR_REARM_ABORT      8               //abort repair/rearm of player ship
275 #define SM_MODE_ALL_FIGHTERS                    9               //message all fighters/bombers
276
277 #define DEFAULT_MSG_TIMEOUT             (8 * 1000)              // number of seconds * 1000 to get milliseconds
278 #define MSG_KEY_EAT_TIME                        (300)
279
280 LOCAL int Squad_msg_mode;                                                       // current mode that the messaging system is in
281 LOCAL int Msg_key_used;                                                         // local variable which tells if the key being processed
282                                                                                                                         // with the messaging system was actually used
283 LOCAL int Msg_key;                                                                      // global which indicates which key was currently pressed
284 LOCAL int Msg_mode_timestamp;
285 LOCAL int Msg_instance;                                         // variable which holds ship/wing instance to send the message to
286 LOCAL int Msg_shortcut_command;                 // holds command when using a shortcut key
287 LOCAL int Msg_target_objnum;                            // id of the current target of the player
288 LOCAL ship_subsys *Msg_targeted_subsys;// pointer to current subsystem which is targeted
289 //#ifndef NDEBUG
290 LOCAL   int Msg_enemies;                                                // tells us whether or not to message enemy ships or friendlies
291 //#endif
292
293 LOCAL int Msg_eat_key_timestamp;                        // used to temporarily "eat" keys
294
295 // defined to position the messaging box
296 int Mbox_item_h[GR_NUM_RESOLUTIONS] = {
297         10, 
298         10
299 };
300 int Mbox_item_xoffset[GR_NUM_RESOLUTIONS] = {
301         17,
302         17
303 };
304
305 // top of the message box gauge
306 int Mbox_top_coords[GR_NUM_RESOLUTIONS][2] = {
307         { // GR_640
308                 445, 5
309         },
310         { // GR_1024
311                 827, 5
312         }
313 };
314
315 int Mbox_bmap_coords[GR_NUM_RESOLUTIONS][2] = {
316         { // GR_640
317                 445, 17
318         },
319         { // GR_1024
320                 827, 17
321         }
322 };
323
324 // squadmsg menu pgup and pgdn
325 int Menu_pgup_coords[GR_NUM_RESOLUTIONS][2] = {
326         { // GR_640
327                 590, 9
328         },
329         { // GR_1024
330                 937, 9
331         }
332 };
333 int Menu_pgdn_coords[GR_NUM_RESOLUTIONS][2] = {
334         { // GR_640
335                 590, 120
336         },
337         { // GR_1024
338                 937, 120
339         }
340 };
341
342 // -----------
343 // following defines/vars are used to build menus that are used in messaging mode
344
345 typedef struct mmode_item {
346         int     instance;                                       // instance in Ships/Wings array of this menu item
347         int     active;                                         // active items are in bold text -- inactive items greyed out
348         char    text[NAME_LENGTH];              // text to display on the menu
349 } mmode_item;
350
351 #define MAX_MENU_ITEMS          50                              // max number of items in the menu
352 #define MAX_MENU_DISPLAY        10                              // max number that can be displayed
353
354 mmode_item MsgItems[MAX_MENU_ITEMS];
355 int Num_menu_items = -1;                                        // number of items for a message menu
356 int First_menu_item= -1;                                                        // index of first item in the menu
357
358 // -----------
359 // following set of vars/defines are used to store/restore key bindings for keys that
360 // are used in messaging mode
361
362 // array to temporarily store key bindings that will be in use for the messaging
363 // system
364 typedef struct key_store {
365         int     option_num;                                     // which element in the Control_config array is this
366         int     id;                                                     // which id (1 or 2) is this key.
367         int     key_value;                                      // which key value to put there.
368 } key_store;
369
370 #define MAX_KEYS_NO_SCROLL      10
371 #define MAX_KEYS_USED           12              // maximum number of keys used for the messaging system
372
373 key_store key_save[MAX_KEYS_USED];              // array to save the key information during messaging mode
374 int num_keys_saved = 0;                                 // number of keys that are saved.
375
376 // next array is the array of MAX_KEYS_USED size which are the keys to use for messaging mode
377
378 int keys_used[] = {     KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0,
379                                                         KEY_PAGEUP, KEY_PAGEDOWN  };
380
381 #define ID1             1
382 #define ID2             2
383
384 // following are defines and character strings that are used as part of messaging mode
385
386 #define TYPE_SHIP_ITEM                                          0
387 #define TYPE_WING_ITEM                                          1
388 #define TYPE_ALL_FIGHTERS_ITEM                  2
389 #define TYPE_REINFORCEMENT_ITEM                 3
390 #define TYPE_REPAIR_REARM_ITEM                  4
391 #define TYPE_REPAIR_REARM_ABORT_ITEM    5
392
393 #define NUM_TYPE_SELECT         6
394
395 char *type_select_str(int n)
396 {
397         #if NUM_TYPE_SELECT != 6 
398         #error type_select_Str is not up to date
399         #endif
400
401         switch(n)       {
402         case TYPE_SHIP_ITEM:
403                 return XSTR( "Ships", 293);
404         case TYPE_WING_ITEM:
405                 return XSTR( "Wings", 294);
406         case TYPE_ALL_FIGHTERS_ITEM:
407                 return XSTR( "All Fighters", 295);
408         case TYPE_REINFORCEMENT_ITEM:
409                 return XSTR( "Reinforcements", 296);
410         case TYPE_REPAIR_REARM_ITEM:
411                 return XSTR( "Rearm/Repair Subsys", 297);
412         case TYPE_REPAIR_REARM_ABORT_ITEM:
413                 return XSTR( "Abort Rearm", 298);
414         }
415         
416         return NULL;    
417 }
418
419 // data structure to hold character string of commands for comm menu
420 typedef struct comm_order {
421         int     value;                          // used to match which command to display on the menu
422 } comm_order;
423
424 // note: If you change this table at all, keep it in sync with version in IgnoreOrdersDlg.cpp
425 // Also make sure you update comm_order_menu_text below this.
426 // Also make sure you update MAX_SHIP_ORDERS in HUDsquadmsg.h
427 comm_order Comm_orders[MAX_SHIP_ORDERS] = {
428         ATTACK_TARGET_ITEM,
429         DISABLE_TARGET_ITEM,
430         DISARM_TARGET_ITEM,
431         DISABLE_SUBSYSTEM_ITEM,
432         PROTECT_TARGET_ITEM,
433         IGNORE_TARGET_ITEM,
434         FORMATION_ITEM,
435         COVER_ME_ITEM,
436         ENGAGE_ENEMY_ITEM,
437         CAPTURE_TARGET_ITEM,
438         REARM_REPAIR_ME_ITEM,
439         ABORT_REARM_REPAIR_ITEM,
440         DEPART_ITEM,
441 };
442
443 // Text to display on the menu
444 // Given an index into the Comm_orders array, return the text associated with it.
445 // MUST BE 1:1 with Comm_orders.
446 char    *comm_order_menu_text(int index)
447 {
448         switch( index ) {
449         case 0: return XSTR( "Destroy my target", 299); break;
450         case 1: return XSTR( "Disable my target", 300); break;
451         case 2: return XSTR( "Disarm my target", 301); break;
452         case 3: return XSTR( "Destroy subsystem", 302); break;
453         case 4: return XSTR( "Protect my target", 303); break;
454         case 5: return XSTR( "Ignore my target", 304); break;
455         case 6: return XSTR( "Form on my wing", 305); break;
456         case 7: return XSTR( "Cover me", 306); break;
457         case 8: return XSTR( "Engage enemy", 307); break;
458         case 9: return XSTR( "Capture my target", 308); break;
459         case 10: return XSTR( "Rearm me", 309); break;
460         case 11: return XSTR( "Abort rearm", 310); break;
461         case 12: return XSTR( "Depart", 311); break;
462         default:
463                 Assert(0);
464         }
465         return NULL;
466 }
467
468 // Text to display on the messaging menu when using the shortcut keys
469 char *comm_order_hotkey_text( int index )
470 {
471         int i;
472
473         for (i = 0; i < MAX_SHIP_ORDERS; i++ ) {
474                 if ( Comm_orders[i].value == index )
475                         return comm_order_menu_text(i);
476         }
477
478         Int3();
479         return NULL;
480 }
481
482 // a define of who can receive message
483 #define CAN_MESSAGE     (SIF_FIGHTER | SIF_BOMBER | SIF_CRUISER | SIF_FREIGHTER | SIF_TRANSPORT | SIF_CAPITAL | SIF_SUPPORT | SIF_SUPERCAP | SIF_DRYDOCK | SIF_GAS_MINER | SIF_AWACS | SIF_CORVETTE)
484
485 int squadmsg_history_index = 0;
486 squadmsg_history Squadmsg_history[SQUADMSG_HISTORY_MAX] = { 0 };
487
488 // used for Message box gauge
489 #define NUM_MBOX_FRAMES         3
490
491 static hud_frames Mbox_gauge[NUM_MBOX_FRAMES];
492 static int Mbox_frames_loaded = 0;
493 static char *Mbox_fnames[GR_NUM_RESOLUTIONS][NUM_MBOX_FRAMES] =
494 {
495 //XSTR:OFF
496         { // GR_640
497                 "message1",             // top part of menu
498                 "message2",             // middle part
499                 "message3"              // bottom part
500         }, 
501         { // GR_1024
502                 "message1",             // top part of menu
503                 "message2",             // middle part
504                 "message3"              // bottom part
505         }
506 //XSTR:ON
507 };
508
509 static int Mbox_title_coord[GR_NUM_RESOLUTIONS][2] = {
510         { // GR_640
511                 447, 6
512         },
513         { // GR_1024
514                 829, 6
515         }
516 };
517 static int Mbox_item_coord[GR_NUM_RESOLUTIONS][2] = {
518         { // GR_640
519                 449, 18
520         },
521         { // GR_1024
522                 831, 18
523         }
524 };
525
526 // define for trapping messages send to "all fighters"
527 #define MESSAGE_ALL_FIGHTERS            -999
528
529 // forward declarations
530 void hud_add_issued_order(char *name, int order, char *target);
531 int hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip = NULL );
532 int hud_squadmsg_ship_order_valid( int shipnum, int order );
533
534 // function to set up variables needed when messaging mode is started
535 void hud_squadmsg_start()
536 {
537 //      int i;
538
539         //if ( num_keys_saved < 0 )  // save the keys if they haven't been saved yet
540         hud_squadmsg_save_keys();
541
542         Msg_key = -1;
543
544 /*
545         for (i=0; i<num_keys_saved; i++)
546                 clear_key_binding ( (short) key_save[i].key_value );                            // removes all mention of this key from Control_config
547 */
548
549         Num_menu_items = -1;                                                                                                    // reset the menu items
550         First_menu_item = 0;
551         Squad_msg_mode = SM_MODE_TYPE_SELECT;                                                   // start off at the base state
552         Msg_mode_timestamp = timestamp(DEFAULT_MSG_TIMEOUT);            // initialize our timer to bogus value
553         Msg_shortcut_command = -1;                                                                                      // assume no shortcut key being used
554         Msg_target_objnum = Player_ai->target_objnum;                           // save the players target object number
555         Msg_targeted_subsys = Player_ai->targeted_subsys;                               // save the players currently targted subsystem
556 #ifndef NDEBUG
557         Msg_enemies = 0;                                                                                                                // tells us if we are messaging enemy ships
558 #endif
559
560         snd_play( &Snds[SND_SQUADMSGING_ON] );
561 }
562
563 // functions which will restore all of the key binding stuff when messaging mode is done
564 void hud_squadmsg_end()
565 {
566 /*
567         int i;
568         key_store *ksp;
569
570         // move through all keys saved and restore their orignal values.
571         for ( i=0; i<num_keys_saved; i++ ) {
572                 ksp = &key_save[i];
573                 Control_config[ksp->option_num].key_id = (short) ksp->key_value;
574         }
575 */
576
577         if ( message_is_playing() == FALSE )
578                 snd_play( &Snds[SND_SQUADMSGING_OFF] );
579 }
580
581 // function which returns true if there are fighters/bombers on the players team
582 // in the mission
583 int hud_squadmsg_count_fighters( )
584 {
585         int count;
586         int team;
587         object *objp;
588         ship *shipp;
589
590         // set up the team to compare for messaging.  In debug versions, we will allow messaging to enemies
591         //team = TEAM_FRIENDLY;
592         team = Player_ship->team;
593 #ifndef NDEBUG
594         if ( Msg_enemies )
595                 team = opposing_team_mask(Player_ship->team);
596 #endif
597
598         count = 0;
599         for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
600                 if ( objp->type != OBJ_SHIP )
601                         continue;
602                 
603                 shipp = &Ships[objp->instance];
604                 // check fighter is accepting orders
605                 if (shipp->orders_accepted != 0) {
606                         // be sure ship is on correct team, not the player, and is a fighter/bomber
607                         if ( (shipp->team == team) && (objp != Player_obj) && (Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) ) {
608                                 return 1;
609                         }
610                 }
611         }
612
613         return 0;
614 }
615
616
617 // function which counts the number of ships available for messaging.  Used to determine if
618 // we should grey out a menu or allow a shortcut command to apply.  parameter "flag" is used
619 // to tell us whether or not we should add the ship to a menu item or not.  We include the
620 // flag so that we don't have to have conditions for messaging ships/wings in two places.
621 int hud_squadmsg_count_ships( int add_to_menu )
622 {
623         int count;
624         int team;
625         ship *shipp;
626         ship_obj *so;
627
628         // set up the team to compare for messaging.  In debug versions, we will allow messaging to enemies
629         //team = TEAM_FRIENDLY;
630         team = Player_ship->team;
631 #ifndef NDEBUG
632         if ( Msg_enemies )
633                 team = opposing_team_mask(Player_ship->team);
634 #endif
635
636         count = 0;
637         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
638                 
639                 shipp = &Ships[Objects[so->objnum].instance];
640                 Assert ( shipp->objnum != -1 );
641
642                 // ships must be able to receive a message
643                 if ( !(Ship_info[shipp->ship_info_index].flags & CAN_MESSAGE) )
644                         continue;
645
646                 // must be on the same team
647                 if ( shipp->team != team )
648                         continue;
649
650                 // departing or dying ships cannot be on list
651                 if ( shipp->flags & (SF_DEPARTING|SF_DYING) )
652                         continue;
653
654                 // MULTI - changed to allow messaging of netplayers
655
656                 // cannot be my ship or an instructor
657                 if ( (&Objects[so->objnum] == Player_obj) || is_instructor(&Objects[so->objnum]) )
658                         continue;
659
660                 // ship must be accepting ship type orders
661                 if ( shipp->orders_accepted == 0)
662                         continue;
663
664                 // if it is a player ship, we must be in multiplayer
665                 if ( (Objects[so->objnum].flags & OF_PLAYER_SHIP) && !(Game_mode & GM_MULTIPLAYER) )
666                         continue;
667
668                 // if a messaging shortcut, be sure this ship can process the order
669                 if ( Msg_shortcut_command != -1 ) {
670                         if ( !(shipp->orders_accepted & Msg_shortcut_command) )
671                                 continue;
672                         else if ( !hud_squadmsg_ship_order_valid(Objects[so->objnum].instance, Msg_shortcut_command) )
673                                 continue;
674                 }
675
676                 count++;
677                 if ( add_to_menu ) {
678                         Assert ( Num_menu_items < MAX_MENU_ITEMS );
679                         strcpy( MsgItems[Num_menu_items].text, shipp->ship_name );
680                         MsgItems[Num_menu_items].instance = SHIP_INDEX(shipp);
681                         MsgItems[Num_menu_items].active = 1;
682                         Num_menu_items++;
683
684                 }
685         }
686
687         // if adding to the menu and we have > 10 items, then don't allow page up and page down to be used.
688         if ( add_to_menu && (Num_menu_items > MAX_MENU_DISPLAY) )
689                 hud_squadmsg_save_keys(1);
690         return count;
691 }
692
693 // routine to return true if a wing should be put onto the messaging menu
694 int hud_squadmsg_wing_valid( wing *wingp, int team )
695 {
696         // int player_count, j;
697
698         // a couple of special cases to account for before adding to count (or to menu).  The wing gone
699         // flags is firm indication to skip this particular wing.  Also, skip if enemy wing
700         if ( (wingp->flags  & WF_WING_GONE) || (wingp->current_count == 0) )
701                 return 0;
702
703         // departing wings don't get attention either
704         if ( wingp->flags & WF_WING_DEPARTING )
705                 return 0;
706
707         // sanity check on ship_index field -- if check is successful, then check the team.
708         Assert (wingp->ship_index[0] != -1 );
709         if ( Ships[wingp->ship_index[0]].team != team )
710                 return 0;
711
712         // if this wing is the players wing, and there is only one ship in the wing, then skip past it
713         if ( (Ships[Player_obj->instance].wingnum == WING_INDEX(wingp)) && (wingp->current_count == 1) )
714                 return 0;
715
716         // check if wing commander is accepting orders
717         if ( Ships[wingp->ship_index[0]].orders_accepted == 0)
718                 return 0;
719
720         // if doing a message shortcut is being used, be sure the wing can "accept" the command.  Only need
721         // to look at the first ship in the wing.
722         if ( Msg_shortcut_command != -1 ) {
723                 if ( !(Ships[wingp->ship_index[0]].orders_accepted & Msg_shortcut_command) )
724                         return 0;
725         }
726         // MULTI - changed to allow messaging of netplayers
727         // don't count wings where all ships are player ships           
728         /*
729         player_count = 0;
730         for ( j = 0; j < wingp->current_count; j++ ) {
731                 if ( Objects[Ships[wingp->ship_index[j]].objnum].flags & OF_PLAYER_SHIP )
732                         player_count++;
733         }
734         if ( player_count == wingp->current_count )
735                 return 0;
736         */
737
738         return 1;
739 }
740
741 // function like above, except for wings
742 int hud_squadmsg_count_wings( int add_to_menu )
743 {
744         int count, i, j;
745         int team;
746
747         // set up the team to compare for messaging.  In debug versions, we will allow messaging to enemies
748         //team = TEAM_FRIENDLY;
749         team = Player_ship->team;
750 #ifndef NDEBUG
751         if ( Msg_enemies )
752                 team = opposing_team_mask(Player_ship->team);
753 #endif
754
755         count = 0;
756
757         // add the player starting wings first
758         for ( i = 0; i < MAX_STARTING_WINGS; i++ ) {
759                 int wingnum;
760
761                 wingnum = Starting_wings[i];
762                 if ( wingnum == -1 )
763                         continue;
764
765                 if ( hud_squadmsg_wing_valid(&Wings[wingnum], team) ) {
766                         count++;
767                         if ( add_to_menu ) {
768                                 Assert ( Num_menu_items < MAX_MENU_ITEMS );
769                                 strcpy( MsgItems[Num_menu_items].text, Wings[wingnum].name );
770                                 MsgItems[Num_menu_items].instance = wingnum;
771                                 MsgItems[Num_menu_items].active = 1;
772                                 Num_menu_items++;
773                         }
774                 }
775         }
776
777         for ( i = 0; i < num_wings; i++ ) {
778                 // if this wing is a player starting wing, skip it since we added it above
779                 for ( j = 0; j < MAX_STARTING_WINGS; j++ ) {
780                         if ( i == Starting_wings[j] )
781                                 break;
782                 }
783                 if ( j < MAX_STARTING_WINGS )
784                         continue;
785
786                 if ( hud_squadmsg_wing_valid(&Wings[i], team) ) {
787                         count++;
788                         if ( add_to_menu ) {
789                                 Assert ( Num_menu_items < MAX_MENU_ITEMS );
790                                 strcpy( MsgItems[Num_menu_items].text, Wings[i].name );
791                                 MsgItems[Num_menu_items].instance = i;
792                                 MsgItems[Num_menu_items].active = 1;
793                                 Num_menu_items++;
794                         }
795                 }
796         }
797         return count;
798 }
799
800
801 // function to set the current submode in message mode -- also resets variables that
802 // should be reset inbetween submodes
803 void hud_squadmsg_do_mode( int mode )
804 {
805         Squad_msg_mode = mode;
806         Num_menu_items = -1;
807         First_menu_item = 0;
808 }
809
810 void hud_squadmsg_page_down()
811 {
812         if ( (First_menu_item + MAX_MENU_DISPLAY) < Num_menu_items ) {
813                 First_menu_item += MAX_MENU_DISPLAY;
814                 Assert ( First_menu_item < Num_menu_items );
815         }
816 }
817
818 void hud_squadmsg_page_up()
819 {
820         if ( First_menu_item > 0 ) {
821                 First_menu_item -= MAX_MENU_DISPLAY;
822                 Assert (First_menu_item >= 0 );
823         }
824 }
825
826 int hud_squadmsg_get_total_keys()
827 {
828         int num_keys_used;
829
830         num_keys_used = MAX_KEYS_NO_SCROLL;
831         if ( Num_menu_items > MAX_MENU_DISPLAY )
832                 num_keys_used = MAX_KEYS_USED;
833
834         return num_keys_used;
835 }
836
837 // function called from high level keyboard read code to give the squadmsg code a key.
838 // return 1 is the key was used by the messaging code, 0 otherwise
839 int hud_squadmsg_read_key( int k )
840 {
841         int i, key_found, num_keys_used;
842
843         num_keys_used = hud_squadmsg_get_total_keys();
844
845         if ( !(Player->flags & PLAYER_FLAGS_MSG_MODE) ) {
846                 // check to see if any messaging keys are still down for some length of time
847                 // after messaging is over.  Return true for a while.
848                 if ( !timestamp_elapsed(Msg_eat_key_timestamp) ) {
849                         for (i = 0; i < num_keys_used; i++ ) {
850                                 if ( keyd_pressed[keys_used[i]] )
851                                         return 1;
852                         }
853                 }
854
855                 return 0;
856         }
857
858         key_found = 0;
859         for (i = 0; i < num_keys_used; i++ ) {
860                 if ( k == keys_used[i] ) {
861                         if ( key_down_count(k) ) {
862                                 Msg_key = k;
863                                 key_found = 1;
864                         }
865
866                         if ( keyd_pressed[k] ) {
867                                 key_found = 1;
868                         }
869
870 //                      key_down_count(k);
871                         break;
872                 }
873         }
874
875         if ( key_found )
876                 return 1;
877
878         return 0;
879 }
880
881 // function which reads the keyboard array and determines if a menu key has been hit
882 int hud_squadmsg_get_key()
883 {
884         int k, i, num_keys_used;
885
886         if ( Msg_key == -1 )
887                 return -1;
888
889         k = Msg_key;
890         Msg_key = -1;
891
892         num_keys_used = hud_squadmsg_get_total_keys();
893
894         // if the emp effect is active, never accept keypresses
895         if(emp_active_local()){
896                 return -1;
897         }
898
899         for ( i = 0; i < num_keys_used; i++ ) {
900                 if ( k == keys_used[i] ) {
901                         Msg_key_used = 1;                                               // this variable will extend the timer
902
903                         // use a timestamp to prevent top level key code from possibly reprocessing this key
904                         Msg_eat_key_timestamp = timestamp(MSG_KEY_EAT_TIME);
905                         if ( k == KEY_PAGEDOWN ) {                      // pageup and pagedown scroll the menu -- deal with these seperately!!
906                                 hud_squadmsg_page_down();
907                                 return -1;
908                         } else if ( k == KEY_PAGEUP ) {
909                                 hud_squadmsg_page_up();
910                                 return -1;
911                         } else if ( k == KEY_ESC ) {
912                                 hud_squadmsg_toggle();
913                                 return -1;
914                         } else if ( (i < Num_menu_items) && (Squad_msg_mode == SM_MODE_REINFORCEMENTS) )                // return any key if selecting reinforcement
915                                 return i;
916
917                         // play general fail sound if inactive item hit.
918                         else if ( (i < Num_menu_items) && !(MsgItems[i].active) )
919                                 gamesnd_play_iface(SND_GENERAL_FAIL);
920
921                         else if ( (i < Num_menu_items) && (MsgItems[i].active) )        // only return keys that are associated with menu items
922                                 return i;
923
924                         else {
925                                 Msg_key_used = 0;                                       // if no #-key pressed for visible item, break and allow timer to 
926                                 break;                                                          // to continue as if no key was pressed
927                         }
928                 }
929         }
930
931         return -1;
932 }
933
934 // function which will essentially print out the contents of the current state of the messaging
935 // menu.  Parameters will be a title.  The menu items and the number of items will be
936 // in global vars since they don't get recomputed every frame.
937 void hud_squadmsg_display_menu( char *title )
938 {
939         int bx, by, sx, sy, i, nitems, none_valid, messaging_allowed;
940
941         // hud_set_bright_color();
942         hud_set_gauge_color(HUD_MESSAGE_BOX, HUD_C_BRIGHT);
943         if ( title ) {
944                 gr_string(Mbox_title_coord[gr_screen.res][0], Mbox_title_coord[gr_screen.res][1], title);
945         }
946
947         if ( Num_menu_items < MAX_MENU_DISPLAY )
948                 nitems = Num_menu_items;
949         else {
950                 if ( First_menu_item == 0 )                                     // First_menu_item == 0 means first page of items
951                         nitems = MAX_MENU_DISPLAY;
952                 else if ( (Num_menu_items - First_menu_item) <= MAX_MENU_DISPLAY )      // check if remaining items fit on one page
953                         nitems = Num_menu_items - First_menu_item;
954                 else {
955                         nitems = MAX_MENU_DISPLAY;
956                 }
957         }
958
959         sx = Mbox_item_coord[gr_screen.res][0];
960         sy = Mbox_item_coord[gr_screen.res][1];
961         bx = Mbox_bmap_coords[gr_screen.res][0];        // global x-offset where bitmap gets drawn
962         by = Mbox_bmap_coords[gr_screen.res][1];                // global y-offset where bitmap gets drawn
963
964         none_valid = 1;         // variable to tell us whether all items in the menu are valid or not
965
966         // use another variable to tell us whether we can message or not.
967         messaging_allowed = 1;
968         if ( (Game_mode & GM_MULTIPLAYER) && !multi_can_message(Net_player) ){
969                 messaging_allowed = 0;
970         }
971
972         for ( i = 0; i < nitems; i++ ) {
973                 int item_num;
974                 char *text = MsgItems[First_menu_item+i].text;
975
976                 // blit the background
977                 // hud_set_default_color();
978                 hud_set_gauge_color(HUD_MESSAGE_BOX);
979                 if ( Mbox_gauge[1].first_frame >= 0 ) {
980                         GR_AABITMAP(Mbox_gauge[1].first_frame, bx, by);                 
981                 }
982                 by += Mbox_item_h[gr_screen.res];
983
984                 // set the text color
985                 if ( MsgItems[First_menu_item+i].active ) {
986                         // hud_set_bright_color();
987                         hud_set_gauge_color(HUD_MESSAGE_BOX, HUD_C_BRIGHT);
988                 } else {
989                         /*
990                         dim_index = min(5, HUD_color_alpha - 2);
991                         if ( dim_index < 0 ) {
992                                 dim_index = 0;
993                         }
994                         gr_set_color_fast(&HUD_color_defaults[dim_index]);
995                         */
996
997                         hud_set_gauge_color(HUD_MESSAGE_BOX, HUD_C_DIM);
998                 }
999
1000                 // first do the number
1001                 item_num = (i+1) % MAX_MENU_DISPLAY;
1002                 emp_hud_printf(sx, sy, EG_SQ1 + i, NOX("%1d."), item_num );             
1003
1004                 // then the text
1005                 emp_hud_string(sx+Mbox_item_xoffset[gr_screen.res], sy, EG_SQ1 + i, text);              
1006
1007                 sy += Mbox_item_h[gr_screen.res];
1008
1009                 // if we have at least one item active, then set the variable so we don't display any
1010                 // message about no active items
1011                 if ( MsgItems[First_menu_item+i].active )
1012                         none_valid = 0;
1013         }
1014
1015         // maybe draw an extra line in to make room for [pgdn], or for the 'no active items'
1016         // display
1017         if ( !messaging_allowed || none_valid || ((First_menu_item + nitems) < Num_menu_items) || (Msg_shortcut_command != -1) ) {
1018                 // blit the background
1019                 // hud_set_default_color();
1020                 hud_set_gauge_color(HUD_MESSAGE_BOX);
1021                 if ( Mbox_gauge[1].first_frame >= 0 ) {
1022
1023                         GR_AABITMAP(Mbox_gauge[1].first_frame, bx, by);                 
1024                 }
1025                 by += Mbox_item_h[gr_screen.res];
1026         }
1027
1028         // draw the bottom of the frame
1029         // hud_set_default_color();
1030         hud_set_gauge_color(HUD_MESSAGE_BOX);
1031         if ( Mbox_gauge[2].first_frame >= 0 ) {
1032
1033                 GR_AABITMAP(Mbox_gauge[2].first_frame, bx, by);         
1034         }
1035
1036         // determine if we should put the text "[more]" at top or bottom to indicate you can page up or down
1037         hud_targetbox_start_flash(TBOX_FLASH_SQUADMSG);
1038         hud_targetbox_maybe_flash(TBOX_FLASH_SQUADMSG);
1039         if ( First_menu_item > 0 ) {
1040                 gr_printf( Menu_pgup_coords[gr_screen.res][0], Menu_pgup_coords[gr_screen.res][1], XSTR( "[pgup]", 312) );
1041         }
1042
1043         if ( (First_menu_item + nitems) < Num_menu_items ) {
1044                 gr_printf( Menu_pgdn_coords[gr_screen.res][0], Menu_pgdn_coords[gr_screen.res][1], XSTR( "[pgdn]", 313));
1045         }
1046
1047         if ( messaging_allowed ) {
1048                 if ( none_valid ){
1049                         gr_printf( sx, by - Mbox_item_h[gr_screen.res] + 2, XSTR( "No valid items", 314));
1050                 } else if ( !none_valid && (Msg_shortcut_command != -1) ){
1051                         gr_printf( sx, by - Mbox_item_h[gr_screen.res] + 2, "%s", comm_order_hotkey_text(Msg_shortcut_command));
1052                 }
1053         } else {
1054                 // if this player is not allowed to message, then display message saying so
1055                 gr_printf( sx, by - Mbox_item_h[gr_screen.res] + 2, XSTR( "Not allowed to message", 315));
1056         }
1057
1058 }
1059
1060 // function to return true or false if the given ship can rearm, or be repaired
1061 int hud_squadmsg_can_rearm( ship *shipp )
1062 {
1063         // player ships which turns traitor cannot rearm
1064         if ( (shipp == Player_ship) && (Player_ship->team == TEAM_TRAITOR) )
1065                 return 0;
1066
1067         // 5/6/98 -- MWA  Decided to always be able to call in support.
1068         return 1;
1069 }
1070
1071 // calls for repair/rearm of the player ship.  Checks for the presense of the support
1072 // ship and does the appropriate action if found
1073 void hud_squadmsg_repair_rearm( int toggle_state, object *objp)
1074 {
1075         int robjnum;
1076         object *robjp;
1077         object *tobj;
1078         int multi_player_num;
1079
1080         // this is essentially a check for multiplayer server/client mode
1081         // in multiplayer mode, the server may have to issue this command when received from a client
1082         if(objp == NULL) {
1083                 tobj = Player_obj;
1084                 multi_player_num = -1;
1085         } else {
1086                 tobj = objp;
1087                 multi_player_num = multi_find_player_by_object(objp);
1088                 Assert(multi_player_num != -1);
1089         }
1090
1091         // see if player is already scheduled on arriving support ship.  If so, issues appripriate
1092         // message and bail
1093         if ( is_support_allowed(tobj) ) {
1094                 if ( mission_is_repair_scheduled( tobj ) ) {
1095                         message_send_builtin_to_player( MESSAGE_REARM_ON_WAY, NULL, MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, multi_player_num, -1 );
1096                 } else {
1097                         robjnum = hud_support_find_closest(OBJ_INDEX(tobj));
1098                         if ( robjnum != -1 ) {
1099                                 message_send_builtin_to_player( MESSAGE_REARM_ON_WAY, &Ships[Objects[robjnum].instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, multi_player_num, -1 );
1100                         } else {
1101                                 // request a rearm.  Next function returns -1 if ship is warping in, objnum of repair ship otherwise
1102                                 robjnum = ai_issue_rearm_request( tobj );
1103                                 if ( robjnum != -1) {
1104                                         robjp = &Objects[robjnum];
1105                                         message_send_builtin_to_player( MESSAGE_ON_WAY, &Ships[robjp->instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, multi_player_num, -1 );
1106
1107                                 } else {
1108                                         // if we are in this part of the if statment, a support ship has been warped in to
1109                                         // service us.  Issue appropriate message
1110                                         message_send_builtin_to_player( MESSAGE_REARM_WARP, NULL, MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, multi_player_num, -1 );
1111                                 }
1112
1113                                 mission_log_add_entry(LOG_PLAYER_REARM, Ships[tobj->instance].ship_name, NULL);
1114                         }
1115                 }
1116         }
1117
1118         //if ( multi_player_num == -1 )         // only do the hud display if it is for me!
1119         //      hud_support_view_start();
1120
1121         if ( toggle_state )
1122                 hud_squadmsg_toggle();                                          // take us out of message mode
1123 }
1124
1125 // function which gets called from keyboard code to issues a shortcut command for rearming.
1126 void hud_squadmsg_rearm_shortcut()
1127 {
1128         if ( !hud_squadmsg_can_rearm(Player_ship) )
1129                 return;
1130
1131         // multiplayer clients need to send this message to the server
1132         if ( MULTIPLAYER_CLIENT ) {
1133                 send_player_order_packet(SQUAD_MSG_SHIP, 0, REARM_REPAIR_ME_ITEM);
1134                 return;
1135         }
1136
1137         hud_squadmsg_repair_rearm(0);
1138 }
1139
1140 // code which is called when a player aborts his rearm request
1141 void hud_squadmsg_repair_rearm_abort( int toggle_state, object *obj)
1142 {
1143 //      ai_info *aip;
1144 //      object *robjp;
1145         object *tobj;
1146
1147         // this is essentially a check for multiplayer server/client mode
1148         // in multiplayer mode, the server may have to issue this command when received from a client
1149         if(obj == NULL)
1150                 tobj = Player_obj;
1151         else
1152                 tobj = obj;
1153
1154         // try to abort the request.  We shoudln't be in this function unless we are actually
1155         // queued for repair.  Send a message from support ship if the support ship is in the mission
1156         ai_abort_rearm_request( tobj );
1157
1158         // move the next statements outside of the above if-statement.  Seems like this place
1159         // is the right place, since we want to change state of the messaging system regardless
1160         // of what happened above.
1161         if ( toggle_state )
1162                 hud_squadmsg_toggle();                                          // take us out of message mode
1163 }
1164
1165 // returns 1 if an order is valid for a ship.  Applies to things like departure when engines are blown, etc.
1166 int hud_squadmsg_ship_order_valid( int shipnum, int order )
1167 {
1168         // disabled ships can't depart.
1169         if ( (order == DEPART_ITEM) && (Ships[shipnum].flags & SF_DISABLED) )
1170                 return 0;
1171
1172         return 1;
1173 }
1174
1175 // returns true or false if the Players target is valid for the given order
1176 // find_order is true when we need to search the comm_orders array for the order entry.  We have
1177 // to do this action in some cases since all we know is the actual "value" of the order
1178 int hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip )
1179 {
1180         int target_objnum, i;
1181         ship *shipp, *ordering_shipp;
1182         object *objp;
1183
1184         if ( aip == NULL )
1185                 aip = Player_ai;
1186
1187         // find the comm_menu item for this command
1188         if ( find_order ) {
1189                 for (i = 0; i < MAX_SHIP_ORDERS; i++ ) {
1190                         if ( Comm_orders[i].value == order )
1191                                 break;
1192                 }
1193                 Assert( i < MAX_SHIP_ORDERS );
1194                 order = i;
1195         }
1196
1197         // orders which don't operate on targets are always valid
1198         if ( !(Comm_orders[order].value & TARGET_MESSAGES) )
1199                 return 1;
1200
1201         target_objnum = aip->target_objnum;
1202
1203         // order isn't valid if there is no player target
1204         if ( target_objnum == -1 ) {
1205                 return 0;
1206         }
1207
1208         objp = &Objects[target_objnum];
1209
1210         ordering_shipp = &Ships[aip->shipnum];
1211
1212         // target isn't a ship, then return 0
1213         if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_WEAPON) )
1214                 return 0;
1215
1216         // if it's a weapon, then it needs to be a WIF_BOMB weapon.  Only attack order valid, and only
1217         // valid on bombs not on the player's team
1218         if ( objp->type == OBJ_WEAPON ) {
1219                 if ( (Comm_orders[order].value == ATTACK_TARGET_ITEM )
1220                         && (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB)
1221                         && (Weapons[objp->instance].team != ordering_shipp->team) )
1222
1223                         return 1;
1224
1225                 return 0;
1226         }
1227
1228         Assert( objp->type == OBJ_SHIP );
1229
1230         shipp = &Ships[objp->instance];
1231
1232         // if target is a navbouy, return 0
1233         if ( Ship_info[shipp->ship_info_index].flags & SIF_NAVBUOY ){
1234                 return 0;
1235         }
1236
1237         // if we are messaging a ship, and that ship is our target, no target type orders are ever active
1238         if ( (Squad_msg_mode == SM_MODE_SHIP_COMMAND) && (target_objnum == Msg_instance) ){
1239                 return 0;
1240         }
1241
1242         // if the order is a disable order or depart, and the ship is disabled, order isn't active
1243         if ( (Comm_orders[order].value == DISABLE_TARGET_ITEM) && (shipp->flags & SF_DISABLED) ){
1244                 return 0;
1245         }
1246
1247         // same as above except for disabled.
1248         if ( (Comm_orders[order].value == DISARM_TARGET_ITEM) && ((shipp->subsys_info[SUBSYSTEM_TURRET].num > 0) && (shipp->subsys_info[SUBSYSTEM_TURRET].current_hits == 0.0f)) ){
1249                 return 0;
1250         }
1251
1252         // if order is disable subsystem, and no subsystem targeted or no hits, then order not valid
1253         if ( (Comm_orders[order].value == DISABLE_SUBSYSTEM_ITEM) && ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) ){
1254                 return 0;
1255         }
1256
1257         // check based on target's and player's team
1258         if ( (shipp->team == ordering_shipp->team) && (Comm_orders[order].value & FRIENDLY_TARGET_MESSAGES) ){
1259                 return 1;
1260         } else if ( (shipp->team != ordering_shipp->team) && (Comm_orders[order].value & ENEMY_TARGET_MESSAGES) ){
1261                 return 1;
1262         } else {
1263                 return 0;
1264         }
1265 }
1266
1267 // function to send an order to all fighters/bombers.
1268 void hud_squadmsg_send_to_all_fighters( int command, int player_num )
1269 {
1270         ai_info *aip;
1271         ship *shipp, *ordering_shipp;
1272         int i, send_message, to_everyone, do_ship;
1273         object *objp;
1274
1275         // quick short circuit here because of actually showing comm menu even though you cannot message.
1276         // just a safety net.
1277         if ( (Game_mode & GM_MULTIPLAYER) && (player_num != -1) ) {
1278                 if ( !multi_can_message(&Net_players[player_num]) ) {
1279                         return;
1280                 }
1281         }
1282
1283         // check for multiplayer mode
1284         if((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
1285                 send_player_order_packet(SQUAD_MSG_ALL, 0, command);
1286                 return;
1287         }
1288
1289         // to_everyone tells us whether the command should apply to all ships, or just to fighets/bombers.
1290         // when true, command goes to *all* friendlies.
1291         send_message = 1;                                                                       // internal flag to dictate who sends message
1292         to_everyone = 0;
1293         do_ship = 0;
1294         aip = Player_ai;
1295
1296         if ( player_num != -1 )
1297                 aip = &Ai_info[Ships[Objects[Net_players[player_num].player->objnum].instance].ai_index];
1298
1299         Assert( aip->shipnum != -1 );
1300         ordering_shipp = &Ships[aip->shipnum];
1301
1302         if ( command == IGNORE_TARGET_ITEM ) {
1303                 to_everyone = 1;
1304                 // if we were messaging a ship directly, set flag to send no messages.  We will send one
1305                 // specifically from the ship player is ordering
1306                 if ( (Msg_instance != MESSAGE_ALL_FIGHTERS) && (Squad_msg_mode == SM_MODE_SHIP_COMMAND) ) {
1307                         do_ship = 1;
1308                         send_message = 0;
1309                 }
1310         }
1311
1312         for ( i = 0; i < num_wings; i++ ) {
1313                 int shipnum;
1314
1315                 if ( (Wings[i].flags & WF_WING_GONE) || (Wings[i].current_count == 0) )
1316                         continue;
1317
1318                 if ( Wings[i].flags & WF_WING_DEPARTING )
1319                         continue;
1320
1321                 // get the first ship on the wing list and look at it's team and then it's type
1322                 shipnum = Wings[i].ship_index[0];
1323                 Assert( shipnum != -1 );
1324                 shipp = &Ships[shipnum];
1325
1326                 // can't message if not on players team
1327                 if ( shipp->team != ordering_shipp->team )
1328                         continue;
1329
1330                 // can't message if ship not fighter/bomber if the command isn't to everyone.
1331                 if ( !to_everyone && !(Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) )
1332                         continue;
1333
1334                 // don't send the command if the "wing" won't accept the command.  We do this by looking at
1335                 // the set of orders accepted for the first ship in the wing.
1336                 if ( !(command & shipp->orders_accepted) )
1337                         continue;
1338
1339                 // send the command to the wing
1340                 if ( Wings[i].current_count > 1 ) {
1341                         if ( hud_squadmsg_send_wing_command(i, command, send_message, player_num) ) {
1342                                 send_message = 0;
1343                         }
1344                 }
1345         }
1346
1347         // now find any friendly fighter/bomber ships not in wings
1348         for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
1349                 if ( objp->type != OBJ_SHIP )
1350                         continue;
1351
1352                 // don't send messge to ships not on player's team, or that are in a wing.
1353                 shipp = &Ships[objp->instance];
1354                 if ( (shipp->team != ordering_shipp->team) || (shipp->wingnum != -1) )
1355                         continue;
1356
1357                 // don't send message to non fighter wings
1358                 if ( !to_everyone && !(Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) )
1359                         continue;
1360
1361                 // skip departing/dying ships
1362                 if ( shipp->flags & (SF_DEPARTING|SF_DYING) )
1363                         continue;
1364
1365                 // don't send command if ship won't accept if
1366                 if ( !(command & shipp->orders_accepted) )
1367                         continue;
1368
1369                 if ( hud_squadmsg_send_ship_command(objp->instance, command, send_message, player_num) ) {
1370                         send_message = 0;
1371                 }
1372         }
1373
1374         // we might send the ship command again if we are ignoring a target, and the guy
1375         // we ordered directly is a ship -- we want the response to come directly from the
1376         // guy we orders
1377         if ( do_ship ) {
1378                 Assert( Msg_instance != MESSAGE_ALL_FIGHTERS );
1379                 hud_squadmsg_send_ship_command( Msg_instance, command, 1 );
1380         }
1381 }
1382
1383 // Check if any enemy ships are in the mission
1384 int hud_squadmsg_enemies_present()
1385 {
1386         ship            *shipp;
1387         ship_obj *so;
1388
1389         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1390                 shipp = &Ships[Objects[so->objnum].instance];
1391                 if ( shipp->team != Player_ship->team )
1392                         return 1;
1393         }
1394
1395         return 0;
1396 }
1397
1398 #define OVERRIDE_PROTECT_SHIP_TYPE      (SIF_FIGHTER|SIF_BOMBER|SIF_FREIGHTER|SIF_TRANSPORT)
1399 // function which sends a message to a specific ship.  This routine can be called from one of two
1400 // places.  Either after selecting a ship when using a hotkey, or after selecting a command when
1401 // using the entire messaging menu system
1402 //
1403 // if local and addr are non-null, it means the function is being called by the (multiplayer) server in response to 
1404 // a PLAYER_COMMAND_PACKET
1405 int hud_squadmsg_send_ship_command( int shipnum, int command, int send_message, int player_num )
1406 {
1407         ai_info *ainfo;
1408         int ai_mode, ai_submode;                                        // ai mode and submode needed for ship commands
1409         char *target_shipname;                                          // ship number of possible targets
1410         int message;
1411         int target_team, ship_team;                             // team id's for the ship getting message and any target the player has
1412         ship *ordering_shipp;
1413         
1414         // quick short circuit here because of actually showing comm menu even though you cannot message.
1415         // just a safety net.
1416         if ( (Game_mode & GM_MULTIPLAYER) && (player_num != -1) ) {
1417                 if ( !multi_can_message(&Net_players[player_num]) ) {
1418                         return 0;
1419                 }
1420         }
1421
1422         // check for multiplayer mode
1423         if((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1424                 send_player_order_packet(SQUAD_MSG_SHIP, shipnum, command);
1425                 return 0;
1426         }
1427
1428         ai_mode = AI_GOAL_NONE;                                                 // needs to be initialized
1429         ai_submode = -1234567;
1430         ainfo = Player_ai;
1431
1432         if ( player_num != -1 ){
1433                 ainfo = &Ai_info[Ships[Objects[Net_players[player_num].player->objnum].instance].ai_index];
1434         }
1435
1436         Assert( ainfo->shipnum != -1 );
1437         ordering_shipp = &Ships[ainfo->shipnum];
1438
1439         // a shortcut to save on repetitive coding.  If the order is a 'target' order, make the default
1440         // mesage be "no target"
1441         message = MESSAGE_NOSIR;
1442         if ( (command & TARGET_MESSAGES) && (ainfo->target_objnum == -1) )
1443                 message = MESSAGE_NO_TARGET;
1444
1445         if ( hud_squadmsg_is_target_order_valid(command, 1, ainfo) ) {
1446
1447                 target_shipname = NULL;
1448                 target_team = -1;
1449                 if ( ainfo->target_objnum != -1) {
1450                         if ( Objects[ainfo->target_objnum].type == OBJ_SHIP ) {
1451                                 if ( ainfo->target_objnum != Ships[shipnum].objnum ) {
1452                                         target_shipname = Ships[Objects[ainfo->target_objnum].instance].ship_name;              // I think this is right
1453                                         target_team = Ships[Objects[ainfo->target_objnum].instance].team;
1454                                 }
1455                         }
1456                 }
1457
1458                 Assert ( ainfo->shipnum != -1 );
1459                 ship_team = Ships[ainfo->shipnum].team;         // team of the ship issuing the message
1460
1461                 switch ( command ) {                                                                    // value of k matches the #defines for ship messages
1462                 case ATTACK_TARGET_ITEM:
1463                         if ( Objects[ainfo->target_objnum].type == OBJ_SHIP ) {
1464                                 Assert( target_shipname );
1465                                 Assert( ship_team != target_team );
1466
1467                                 // Orders to override protect
1468                                 if (Ship_info[Ships[Objects[ainfo->target_objnum].instance].ship_info_index].flags & OVERRIDE_PROTECT_SHIP_TYPE) {
1469                                         Objects[ainfo->target_objnum].flags &= ~OF_PROTECTED;
1470                                 }
1471
1472                                 ai_mode = AI_GOAL_CHASE;
1473                                 ai_submode = SM_ATTACK;
1474                         } else if ( Objects[ainfo->target_objnum].type == OBJ_WEAPON ) {
1475                                 ai_mode = AI_GOAL_CHASE_WEAPON;
1476                                 ai_submode = Objects[ainfo->target_objnum].instance;                    // store the instance of the weapon -- ai goals code will deal with it
1477                         } else
1478                                 Int3();
1479                         message = MESSAGE_ATTACK_TARGET;
1480                         break;
1481
1482                 case DISABLE_TARGET_ITEM:
1483                         Assert( target_shipname );
1484                         Assert( ship_team != target_team );
1485
1486                         // Orders to override protect
1487                         if (Ship_info[Ships[Objects[ainfo->target_objnum].instance].ship_info_index].flags & OVERRIDE_PROTECT_SHIP_TYPE) {
1488                                 Objects[ainfo->target_objnum].flags &= ~OF_PROTECTED;
1489                         }
1490
1491                         ai_mode = AI_GOAL_DISABLE_SHIP;
1492                         ai_submode = -SUBSYSTEM_ENGINE;
1493                         message = MESSAGE_DISABLE_TARGET;
1494                         break;
1495
1496                 case DISARM_TARGET_ITEM:
1497                         Assert( target_shipname );
1498                         Assert( ship_team != target_team );
1499
1500                         // Orders to override protect
1501                         if (Ship_info[Ships[Objects[ainfo->target_objnum].instance].ship_info_index].flags & OVERRIDE_PROTECT_SHIP_TYPE) {
1502                                 Objects[ainfo->target_objnum].flags &= ~OF_PROTECTED;
1503                         }
1504
1505                         ai_mode = AI_GOAL_DISARM_SHIP;
1506                         ai_submode = -SUBSYSTEM_TURRET;
1507                         message = MESSAGE_DISARM_TARGET;
1508                         break;
1509
1510                 case DISABLE_SUBSYSTEM_ITEM:
1511                         Assert( target_shipname );
1512                         Assert( ship_team != target_team );
1513                         Assert( ainfo->targeted_subsys != NULL );
1514                         Assert( ainfo->targeted_subsys->current_hits > 0.0f);
1515
1516                         // Orders to override protect
1517                         if (Ship_info[Ships[Objects[ainfo->target_objnum].instance].ship_info_index].flags & OVERRIDE_PROTECT_SHIP_TYPE) {
1518                                 Objects[ainfo->target_objnum].flags &= ~OF_PROTECTED;
1519                         }
1520
1521                         ai_mode = AI_GOAL_DESTROY_SUBSYSTEM;
1522                         ai_submode = ship_get_subsys_index( &Ships[Objects[ainfo->target_objnum].instance], ainfo->targeted_subsys->system_info->subobj_name );
1523                         message = MESSAGE_ATTACK_TARGET;
1524                         break;
1525
1526                 case CAPTURE_TARGET_ITEM:
1527                         Assert( target_shipname );
1528                         Assert( ship_team != target_team );
1529
1530                         Assert(ainfo->target_objnum > -1);
1531
1532                         Objects[ainfo->target_objnum].flags |= OF_PROTECTED;
1533
1534                         ai_mode = AI_GOAL_DOCK;
1535                         ai_submode = AIS_DOCK_0;
1536                         message = MESSAGE_DOCK_YES;
1537                         break;
1538
1539                 case PROTECT_TARGET_ITEM:
1540
1541                         // AL 31-3-98: Can't protect self... this can happen if all fighters
1542                         //                                      are told to protect another friendly ship
1543                         if ( ainfo->target_objnum == Ships[shipnum].objnum ) {
1544                                 return 0;
1545                         }
1546
1547                         Assert( target_shipname );
1548                         Assert( ship_team == target_team );
1549
1550                         ai_mode = AI_GOAL_GUARD;
1551                         ai_submode = AIS_GUARD_PATROL;
1552                         message = MESSAGE_YESSIR;
1553                         break;
1554
1555                 case IGNORE_TARGET_ITEM:
1556                         Assert( target_shipname );
1557                         Assert( ship_team != target_team );
1558
1559                         ai_mode = AI_GOAL_IGNORE;
1560                         ai_submode = 0;
1561                         message = MESSAGE_YESSIR;
1562                         break;
1563                 
1564                 case FORMATION_ITEM:
1565                         message = MESSAGE_YESSIR;
1566                         target_shipname = ordering_shipp->ship_name;
1567                         ai_mode = AI_GOAL_FORM_ON_WING;
1568                         ai_submode = 0;
1569                         break;
1570                 
1571                 case COVER_ME_ITEM:
1572                         ai_mode = AI_GOAL_GUARD;
1573                         ai_submode = AIS_GUARD_PATROL;
1574                         target_shipname = ordering_shipp->ship_name;
1575                         message = MESSAGE_YESSIR;
1576                         break;
1577                 
1578                 case ENGAGE_ENEMY_ITEM:
1579                         ai_mode = AI_GOAL_CHASE_ANY;
1580                         ai_submode = SM_ATTACK;
1581                         // if no enemies present, use the affirmative, instead of engaging enemies message
1582                         if ( hud_squadmsg_enemies_present() )
1583                                 message = MESSAGE_YESSIR;
1584                         else
1585                                 message = MESSAGE_ENGAGE;
1586                         target_shipname = NULL;
1587                         break;
1588                 
1589                 case DEPART_ITEM:
1590                         ai_mode = AI_GOAL_WARP;
1591                         ai_submode = -1;
1592                         message = MESSAGE_WARP_OUT;
1593                         break;
1594                 
1595                 // the following are support ship options!!!
1596                 case REARM_REPAIR_ME_ITEM:              
1597                         if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (player_num != -1) ){
1598                                 hud_squadmsg_repair_rearm(0,&Objects[Net_players[player_num].player->objnum]);
1599                         } else {
1600                                 hud_squadmsg_repair_rearm(0);                           // note we return right away.  repair/rearm code handles messaging, etc
1601                         }
1602                         return 0;
1603                 
1604                 case ABORT_REARM_REPAIR_ITEM:
1605                         if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (player_num != -1) ){
1606                                 hud_squadmsg_repair_rearm_abort(0,&Objects[Net_players[player_num].player->objnum]);
1607                         } else {
1608                                 hud_squadmsg_repair_rearm_abort(0);             // note we return right away.  repair/rearm code handles messaging, etc
1609                         }
1610                         return 0;
1611                 
1612                 case STAY_NEAR_ME_ITEM:
1613                 case STAY_NEAR_TARGET_ITEM:             // can't attack anything on same team
1614
1615                         // cannot stay near a hostile ship(?)
1616                         if ( (command == STAY_NEAR_TARGET_ITEM) && (ship_team != target_team) )
1617                                 break;
1618
1619                         ai_mode = AI_GOAL_STAY_NEAR_SHIP;
1620                         ai_submode = -1;
1621                         message = MESSAGE_YESSIR;
1622                         if ( command == STAY_NEAR_ME_ITEM )
1623                                 target_shipname = ordering_shipp->ship_name;
1624                         break;
1625                 
1626                 case KEEP_SAFE_DIST_ITEM:
1627                         ai_mode = AI_GOAL_KEEP_SAFE_DISTANCE;
1628                         ai_submode = -1;
1629                         message = MESSAGE_YESSIR;
1630                         break;
1631                 
1632                 default:
1633                         Int3();                                                                         // get Allender -- illegal message
1634                         break;
1635
1636                 }
1637
1638                 // handle case of messaging one ship.  Deal with messaging all fighters next.
1639                 if ( ai_mode != AI_GOAL_NONE ) {
1640                         Assert(ai_submode != -1234567);
1641                         ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, ai_mode, ai_submode, target_shipname, &Ai_info[Ships[shipnum].ai_index] );
1642                         if( player_num == -1 )
1643                                 hud_add_issued_order(Ships[shipnum].ship_name, command, target_shipname);
1644                 }
1645         }
1646
1647         // if we're in multiplayer mode, and we're the server, determine if this virtual squadmate order should be
1648         // sent to other players in the game as an actual "order"
1649         if((Game_mode & GM_MULTIPLAYER) && (message != MESSAGE_NOSIR)){
1650                 // if the multi_msg system processed and sent this order to a player, we should not play a response
1651                 if(multi_msg_eval_ship_squadmsg(shipnum,command,ainfo,player_num)){
1652                         send_message = 0;
1653                 }
1654         }
1655         
1656         // this is the _response_
1657         if ( send_message ){
1658                 message_send_builtin_to_player( message, &Ships[shipnum], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_ANYTIME, 0, 0, player_num, -1 );        
1659         }
1660         
1661         return send_message;
1662 }
1663
1664 // function to send a command to a wing.  Like above function, called from one of two places
1665 //
1666 // if local and addr are non-null, it means the function is being called by the (multiplayer) server in response to 
1667 // a PLAYER_COMMAND_PACKET
1668 //
1669 // returns whether or not a message was sent
1670 int hud_squadmsg_send_wing_command( int wingnum, int command, int send_message, int player_num )
1671 {
1672         ai_info *ainfo;
1673         int ai_mode, ai_submode;                                        // ai mode and submode needed for ship commands
1674         char *target_shipname;                                          // ship number of possible targets
1675         int message_sent, message;
1676         int target_team, wing_team;                             // team for the wing and the player's target
1677         ship *ordering_shipp;
1678
1679         // quick short circuit here because of actually showing comm menu even though you cannot message.
1680         // just a safety net.
1681         if ( (Game_mode & GM_MULTIPLAYER) && (player_num != -1) ) {
1682                 if ( !multi_can_message(&Net_players[player_num]) ) {
1683                         return 0;
1684                 }
1685         }
1686
1687         // check for multiplayer mode
1688         if((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1689                 send_player_order_packet(SQUAD_MSG_WING, wingnum,command);
1690                 return 0;
1691         }
1692
1693         ai_mode = AI_GOAL_NONE;                                                 // needs to be initialized
1694         ai_submode = -1234567;
1695         ainfo = Player_ai;
1696
1697         if ( player_num != -1 )
1698                 ainfo = &Ai_info[Ships[Objects[Net_players[player_num].player->objnum].instance].ai_index];
1699
1700         Assert( ainfo->shipnum != -1 );
1701         ordering_shipp = &Ships[ainfo->shipnum];
1702
1703         // get the shipnum of the ship the player has targeted.  Used in enough places to do this just
1704         // once.  If the ship targeted is part of the wing that was messages -- bail out!!!
1705
1706         // a shortcut to save on repetative coding
1707         message = MESSAGE_NOSIR;
1708         if ( (command & TARGET_MESSAGES) && (ainfo->target_objnum == -1) )
1709                 message = MESSAGE_NO_TARGET;
1710
1711         if ( hud_squadmsg_is_target_order_valid(command, 1, ainfo) ) {
1712
1713                 target_shipname = NULL;
1714                 target_team = -1;
1715                 if ( ainfo->target_objnum != -1) {
1716                         if ( Objects[ainfo->target_objnum].type == OBJ_SHIP ) {
1717                                 target_shipname = Ships[Objects[ainfo->target_objnum].instance].ship_name;              // I think this is right
1718                                 target_team = Ships[Objects[ainfo->target_objnum].instance].team;
1719                         }
1720                 }
1721
1722                 Assert ( ainfo->shipnum != -1 );
1723                 Assert ( (wingnum >= 0) && (wingnum < num_wings) );
1724
1725                 // get the team for the wing
1726                 Assert ( Wings[wingnum].ship_index[0] != -1 );
1727                 wing_team = Ships[Wings[wingnum].ship_index[0]].team;
1728
1729                 switch ( command ) {                                                                    // value of k matches the #defines for ship messages
1730                 case ATTACK_TARGET_ITEM:
1731                         if ( Objects[ainfo->target_objnum].type == OBJ_SHIP ) {
1732                                 Assert( target_shipname );
1733                                 Assert( wing_team != target_team );
1734                                 if ( (Ships[Objects[ainfo->target_objnum].instance].wingnum != -1) && (Ships[Objects[ainfo->target_objnum].instance].wingnum == wingnum) ) {
1735                                         message = MESSAGE_NOSIR;
1736                                         ai_mode = AI_GOAL_NONE;
1737                                 } else {
1738                                         ai_mode = AI_GOAL_CHASE;
1739                                         ai_submode = SM_ATTACK;
1740                                         message = MESSAGE_ATTACK_TARGET;
1741                                 }
1742                         } else if ( Objects[ainfo->target_objnum].type == OBJ_WEAPON ) {
1743                                 ai_mode = AI_GOAL_CHASE_WEAPON;
1744                                 ai_submode = Objects[ainfo->target_objnum].instance;                    // store the instance of the weapon -- ai goals code will deal with it
1745                                 message = MESSAGE_ATTACK_TARGET;
1746                         } else
1747                                 Int3();
1748
1749                         break;
1750
1751                 case DISABLE_TARGET_ITEM:
1752                         Assert( target_shipname );
1753                         Assert( wing_team != target_team );
1754
1755                         ai_mode = AI_GOAL_DISABLE_SHIP;
1756                         ai_submode = -SUBSYSTEM_ENGINE;
1757                         message = MESSAGE_DISABLE_TARGET;
1758                         break;
1759
1760                 case DISARM_TARGET_ITEM:
1761                         Assert( target_shipname );
1762                         Assert( wing_team != target_team );
1763
1764                         ai_mode = AI_GOAL_DISARM_SHIP;
1765                         ai_submode = -SUBSYSTEM_TURRET;
1766                         message = MESSAGE_DISARM_TARGET;
1767                         break;
1768
1769                 case DISABLE_SUBSYSTEM_ITEM:
1770                         Assert( target_shipname );
1771                         Assert( wing_team != target_team );
1772                         Assert( ainfo->targeted_subsys != NULL );
1773                         Assert( ainfo->targeted_subsys->current_hits > 0.0f);
1774
1775                         ai_mode = AI_GOAL_DESTROY_SUBSYSTEM;
1776                         ai_submode = ship_get_subsys_index( &Ships[Objects[ainfo->target_objnum].instance], ainfo->targeted_subsys->system_info->subobj_name );
1777                         message = MESSAGE_ATTACK_TARGET;
1778                         break;
1779
1780
1781                 case PROTECT_TARGET_ITEM:
1782                         Assert( target_shipname );
1783                         Assert( wing_team == target_team );
1784
1785                         ai_mode = AI_GOAL_GUARD;
1786                         ai_submode = AIS_GUARD_PATROL;
1787                         message = MESSAGE_YESSIR;
1788                         break;
1789
1790                 case IGNORE_TARGET_ITEM:
1791                         Assert( target_shipname );
1792                         Assert( wing_team != target_team );
1793
1794                         ai_mode = AI_GOAL_IGNORE;
1795                         ai_submode = 0; //      actually, a don't care.
1796                         message = MESSAGE_YESSIR;
1797                         break;
1798
1799                 case FORMATION_ITEM:
1800                         message = MESSAGE_YESSIR;
1801                         target_shipname = ordering_shipp->ship_name;
1802                         ai_mode = AI_GOAL_FORM_ON_WING;
1803                         ai_submode = 0;
1804                         break;
1805
1806                 case COVER_ME_ITEM:
1807                         ai_mode = AI_GOAL_GUARD;
1808                         ai_submode = AIS_GUARD_PATROL;
1809                         target_shipname = ordering_shipp->ship_name;
1810                         message = MESSAGE_YESSIR;
1811                         break;
1812
1813                 case ENGAGE_ENEMY_ITEM:
1814                         ai_mode = AI_GOAL_CHASE_ANY;
1815                         ai_submode = SM_ATTACK;
1816                         if ( hud_squadmsg_enemies_present() )
1817                                 message = MESSAGE_YESSIR;
1818                         else
1819                                 message = MESSAGE_ENGAGE;
1820                         target_shipname = NULL;
1821                         break;
1822
1823                 case DEPART_ITEM:
1824                         ai_mode = AI_GOAL_WARP;
1825                         ai_submode = -1;
1826                         message = MESSAGE_WARP_OUT;
1827                         Wings[wingnum].flags |= WF_DEPARTURE_ORDERED;
1828                         break;
1829
1830                 case REARM_REPAIR_ME_ITEM:
1831                 case ABORT_REARM_REPAIR_ITEM:
1832                 case STAY_NEAR_ME_ITEM:
1833                 case STAY_NEAR_TARGET_ITEM:
1834                 case KEEP_SAFE_DIST_ITEM:
1835                         return 0;
1836
1837                 default:
1838                         Int3();                                                                         // get Allender -- illegal message
1839                         break;
1840
1841                 }
1842
1843                 if ( ai_mode != AI_GOAL_NONE ) {
1844                         Assert(ai_submode != -1234567);
1845                         ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, ai_mode, ai_submode, target_shipname, wingnum );
1846                 }
1847         }
1848
1849         // if we're in multiplayer mode, and we're the server, determine if this virtual squadmate order should be
1850         // sent to other players in the game as an actual "order"
1851         if((Game_mode & GM_MULTIPLAYER) && (message != MESSAGE_NOSIR)){
1852                 // if there's at least one ai ship which got the command, let the response come through
1853                 if(multi_msg_eval_wing_squadmsg(wingnum,command,ainfo,player_num)){
1854                         send_message = 0;
1855                 }
1856         }
1857
1858         // this is the _response_
1859         message_sent = 0;
1860         if ( send_message ) {
1861                 int ship_num;
1862
1863                 // get a random ship in the wing to send the message to the player              
1864                 ship_num = ship_get_random_ship_in_wing( wingnum, SHIP_GET_NO_PLAYERS );
1865                 
1866                 // in multiplayer, its possible that all ships in a wing are players. so we'll just send from a random ship             
1867                 if(ship_num == -1){
1868                         ship_num = ship_get_random_ship_in_wing(wingnum);
1869                 }
1870                 
1871                 // only send message if ship is found.  There appear to be cases where all ships
1872                 // in a wing die in the same frame causing the wing to appear valid in the message
1873                 // menu, but the get_random_ship* functions won't return dying ships.
1874                 if ( ship_num != -1 ) {
1875                         message_send_builtin_to_player( message, &Ships[ship_num], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_ANYTIME, 0, 0, player_num, -1 );
1876                         message_sent = 1;
1877                 }
1878         }
1879
1880         return message_sent;
1881 }
1882
1883
1884 // return number of available reinforcements, 0 if none available
1885 int hud_squadmsg_reinforcements_available(int team)
1886 {
1887         int i, count = 0;
1888
1889         for (i = 0; i < Num_reinforcements; i++) {
1890                 int wingnum;
1891
1892                 // no more left
1893                 if ( Reinforcements[i].num_uses >= Reinforcements[i].uses ){
1894                         continue;
1895                 }
1896
1897                 // incorrect team
1898                 if ( team != ship_get_reinforcement_team(i) ){
1899                         continue;
1900                 }
1901
1902                 //  check the arrival cue sexpression of the ship/wing of this reinforcement.  If known
1903                 //  false, it doesn't count either
1904                 if ( (wingnum = wing_name_lookup(Reinforcements[i].name, 1)) != -1 ) {
1905                         Assert ( Wings[wingnum].arrival_cue >= 0 );
1906                         if ( Sexp_nodes[Wings[wingnum].arrival_cue].value == SEXP_KNOWN_FALSE ){
1907                                 continue;
1908                         }
1909                 } else {
1910                         p_object *p_objp;
1911
1912                         p_objp = mission_parse_get_arrival_ship( Reinforcements[i].name );
1913                         if ( p_objp != NULL ) {
1914                                 if ( Sexp_nodes[p_objp->arrival_cue].value == SEXP_KNOWN_FALSE ){
1915                                         continue;
1916                                 }
1917                         } else {
1918                                 Int3();                                                 // allender says bogus!  reinforcement should be here since it wasn't a wing!
1919                                 continue;
1920                         }
1921                 }
1922                 count++;
1923         }
1924
1925         return count;
1926 }
1927
1928 // function to put up window in upper right to allow for player to select the type
1929 // of entity to select for a message (i.e. a wing or a ship)
1930 void hud_squadmsg_type_select( )
1931 {
1932         int k, i;
1933
1934         First_menu_item = 0;    
1935
1936         // Add the items
1937         for (i=0; i<NUM_TYPE_SELECT; i++ )      {
1938                 strcpy( MsgItems[i].text, type_select_str(i) );
1939                 MsgItems[i].active = 1;                                         // assume active
1940         }
1941         Num_menu_items = NUM_TYPE_SELECT;
1942
1943
1944         // check to see if the players team is TEAM_TRAITOR.  If so, then he is a "traitor", and will not
1945         // be able to do anything from this menu
1946         if ( Player_ship->team == TEAM_TRAITOR ) {
1947                 for (i = 0; i < MAX_MENU_ITEMS; i++ )
1948                         MsgItems[i].active = 0;
1949                 goto do_main_menu;
1950         }
1951
1952         // based on ship counts, wing counts, shortcut active, grey out possible menu choices
1953         if ( !hud_squadmsg_count_ships(0) )
1954                 MsgItems[TYPE_SHIP_ITEM].active = 0;
1955
1956         if ( !hud_squadmsg_count_wings(0) )
1957                 MsgItems[TYPE_WING_ITEM].active = 0;
1958
1959         // check to be sure that we have some fighters/bombers on the players team that we
1960         // can message
1961         if ( !hud_squadmsg_count_fighters() ){
1962                 MsgItems[TYPE_ALL_FIGHTERS_ITEM].active = 0;
1963         }
1964
1965         if ((Player_ship != NULL) && !hud_squadmsg_reinforcements_available(Player_ship->team)) {
1966                 MsgItems[TYPE_REINFORCEMENT_ITEM].active = 0;
1967         }
1968
1969         MsgItems[TYPE_REPAIR_REARM_ITEM].active = 1;                            // this item will always be available (I think)
1970         MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 0;
1971
1972         // AL: 10/13/97
1973         // If the player ship communications are severely damaged, then the player
1974         // will only be able to call for repair/rearm ships
1975         //
1976         // also, only allow support ship if this player is not allowed to messaage. 
1977         if ( (hud_communications_state(Player_ship) != COMM_OK) || ((Game_mode & GM_MULTIPLAYER) && !multi_can_message(Net_player)) ) {
1978                 for ( i = 0; i < MAX_MENU_ITEMS; i++ ){
1979                         MsgItems[i].active = 0;
1980                 }
1981
1982                 MsgItems[TYPE_REPAIR_REARM_ITEM].active = 1;
1983         }
1984
1985         // check to see if the player is awaiting repair or being repaired.  Active the abort and inactive the repair items
1986         // check to see if the player is scheduled to be repaired by incoming ship
1987         if ( Ai_info[Ships[Player_obj->instance].ai_index].ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
1988                 MsgItems[TYPE_REPAIR_REARM_ITEM].active = 0;
1989                 MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 1;
1990         } else if ( mission_is_repair_scheduled(Player_obj) ) {
1991                 MsgItems[TYPE_REPAIR_REARM_ITEM].active = 0;
1992                 MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 1;
1993         }
1994
1995         // if no support available, can't call one in
1996         if ( !is_support_allowed(Player_obj) ) {
1997                 MsgItems[TYPE_REPAIR_REARM_ITEM].active = 0;
1998                 MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 0;
1999         }
2000
2001         // de-activate the rearm/repair item if the player has a full load of missiles and
2002         // all subsystems at full strength.  We will only check if this item hasn't been marked
2003         // inactive because of some other reason
2004         if ( MsgItems[TYPE_REPAIR_REARM_ITEM].active ) {
2005
2006                 if ( !hud_squadmsg_can_rearm(Player_ship) ){
2007                         MsgItems[TYPE_REPAIR_REARM_ITEM].active = 0;
2008                 }
2009         }
2010
2011         // if using keyboard shortcut, these items are always inactive
2012         if ( Msg_shortcut_command != -1 ) {
2013                 MsgItems[TYPE_REPAIR_REARM_ITEM].active = 0;
2014                 MsgItems[TYPE_REINFORCEMENT_ITEM].active = 0;
2015                 MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 0;
2016         }
2017
2018 do_main_menu:
2019         hud_squadmsg_display_menu( XSTR( "Message What", 316) );
2020         k = hud_squadmsg_get_key();
2021         if ( k != -1 ) {                                                        // when k != -1, we have a key that associates with menu item
2022                 Assert ( k < Num_menu_items );
2023                 if ( k == TYPE_SHIP_ITEM ){
2024                         hud_squadmsg_do_mode( SM_MODE_SHIP_SELECT );
2025                 } else if ( k == TYPE_WING_ITEM ) {
2026                         hud_squadmsg_do_mode( SM_MODE_WING_SELECT );
2027                 } else if ( k == TYPE_ALL_FIGHTERS_ITEM ) {
2028                         hud_squadmsg_do_mode( SM_MODE_ALL_FIGHTERS );
2029                 }
2030                 
2031                 if ( Msg_shortcut_command == -1 ) {
2032                         if ( k == TYPE_REINFORCEMENT_ITEM ) {
2033                                 hud_squadmsg_do_mode( SM_MODE_REINFORCEMENTS );
2034                                 player_set_next_all_alone_msg_timestamp();
2035                         } else if ( k == TYPE_REPAIR_REARM_ITEM ){
2036                                 hud_squadmsg_do_mode( SM_MODE_REPAIR_REARM );
2037                         } else if ( k == TYPE_REPAIR_REARM_ABORT_ITEM ) {
2038                                 hud_squadmsg_do_mode( SM_MODE_REPAIR_REARM_ABORT );
2039                         }
2040                 }
2041         }
2042 }
2043
2044 // function to display a list of ships to send a command to
2045 void hud_squadmsg_ship_select()
2046 {
2047         int k;
2048
2049         if ( Num_menu_items == -1 ) {
2050                 Num_menu_items = 0;
2051                 hud_squadmsg_count_ships( 1 );
2052         }
2053
2054         hud_squadmsg_display_menu( XSTR( "Select Ship", 317) );
2055         k = hud_squadmsg_get_key();
2056         if ( k != -1 ) {                                                // if true, we have selected a ship.
2057                 if ( Msg_shortcut_command == -1 ) {
2058                         Msg_instance = MsgItems[First_menu_item + k].instance;          // store the instance id in a global
2059                         hud_squadmsg_do_mode( SM_MODE_SHIP_COMMAND );                           // and move to a new mode
2060                 } else {
2061                         // we must convert the Msg_shortcut_command value to a value that the message
2062                         // system normally uses to select a command.  Since the menu 
2063                         Assert( Msg_shortcut_command != IGNORE_TARGET_ITEM );
2064                         hud_squadmsg_send_ship_command( MsgItems[First_menu_item+k].instance, Msg_shortcut_command, 1 );
2065                         hud_squadmsg_toggle();
2066                 }
2067         }
2068
2069 }
2070
2071 // function to display a list of ships to send a command to
2072 void hud_squadmsg_wing_select()
2073 {
2074         int k;
2075
2076         if ( Num_menu_items == -1 ) {
2077                 Num_menu_items = 0;
2078                 hud_squadmsg_count_wings( 1 );
2079         }
2080
2081         hud_squadmsg_display_menu( XSTR( "Select Wing", 318) );
2082         k = hud_squadmsg_get_key();
2083         if ( k != -1 ) {                                                // if true, we have selected a ship.
2084                 if ( Msg_shortcut_command == -1 ) {                                                                     // do normal menu stuff when no hoykey active
2085                         Msg_instance = MsgItems[First_menu_item + k].instance;  // store the instance id in a global
2086                         hud_squadmsg_do_mode( SM_MODE_WING_COMMAND );                           // and move to a new mode
2087                 } else {
2088                         Assert( Msg_shortcut_command != IGNORE_TARGET_ITEM );
2089                         hud_squadmsg_send_wing_command( MsgItems[First_menu_item+k].instance, Msg_shortcut_command, 1 );
2090                         hud_squadmsg_toggle();
2091                 }
2092         }
2093
2094 }
2095
2096 // code which gives an order to all fighters/bombers.  If there is a message shortcut active, then
2097 // make that order apply to all fighters/bombers.  Otherwise, move to the ship_command menu
2098 void hud_squadmsg_msg_all_fighters()
2099 {
2100         if ( Msg_shortcut_command == -1 ) {
2101                 Msg_instance = MESSAGE_ALL_FIGHTERS;
2102                 hud_squadmsg_do_mode( SM_MODE_SHIP_COMMAND );
2103         } else {
2104                 hud_squadmsg_send_to_all_fighters( Msg_shortcut_command );
2105                 hud_squadmsg_toggle();
2106         }
2107 }
2108
2109 // called to actually bring in a reinforcement.  For single player games, always gets called.
2110 // for multiplayer games, always called on the server side.  Clients should never get here
2111 void hud_squadmsg_call_reinforcement(int reinforcement_num, int player_num)
2112 {
2113         int i, delay;
2114         reinforcements *rp;
2115         p_object *p_objp;
2116
2117         rp = &Reinforcements[reinforcement_num];
2118
2119         // safety net mainly for multiplayer servers in case some odd data desync occurs between 
2120         // server and clients
2121         if ( MULTIPLAYER_MASTER && (rp->num_uses == rp->uses) ) {
2122                 return;
2123         }
2124
2125         // check to see if the reinforcement called was a wing.
2126         for (i = 0; i < num_wings; i++ ) {
2127                 if ( !stricmp(rp->name, Wings[i].name) ) {
2128                         // found a wingname.  Call the parse function to create all the ships in this wing
2129                         // we must set the arrival cue of the wing to true, otherwise, this won't work!!
2130                         Wings[i].flags &= ~WF_REINFORCEMENT;
2131                         Wings[i].flags |= WF_RESET_REINFORCEMENT;
2132
2133                         // set up the arrival delay.  If it is 0, then make is some random number of seconds
2134                         delay = rp->arrival_delay;
2135                         if ( delay == 0 )
2136                                 delay = (int)(frand() * 3.0) + 3;
2137                         Wings[i].arrival_delay = timestamp(delay * 1000);
2138                         break;
2139                 }
2140         }
2141
2142         // if we found no wing name that matched the reinforcement name, then look for a ship
2143         // of the same name
2144         if ( i == num_wings ) {
2145                 p_objp = mission_parse_get_arrival_ship( rp->name );
2146                 if ( p_objp ) {
2147                         // by resetting the reinforcement flag, we will allow code which normally handles arrivals
2148                         // to make this reinforcement arrive.  Doing so keeps the data structures clean.
2149                         p_objp->flags &= ~P_SF_REINFORCEMENT;
2150
2151                         // set up the arrival delay
2152                         delay = rp->arrival_delay;
2153                         if ( delay == 0 )
2154                                 delay = (int)(frand() * 3.0) + 3;               // between 3 and 6 seconds to arrive
2155                         p_objp->arrival_delay = timestamp(delay * 1000);
2156                 } else {
2157                         Int3();                         // get allender -- I don't think that this can happen!!!!
2158                         return;
2159                 }
2160         }
2161
2162         // increment the number of times this is used.  Incremented here on single player and multiplayer
2163         // server side only.  Clients keep track of own count when they actually call something in.
2164         rp->num_uses++;
2165
2166         // commented out on 9/9/98 because these messages simply are not used
2167         /*
2168         // now play a message (if there is one to play) for this reinforcement arrival.  The first for loop
2169         // determine how many messages there are to play, since the array is packet.  Then, if >= 1 message
2170         // to play, play one
2171         for (i = 0; i < MAX_REINFORCEMENT_MESSAGES; i++ )
2172                 if ( !strlen(rp->yes_messages[i]) )
2173                         break;
2174
2175         //if ( i > 0 )
2176         //      message_send_to_player( rp->yes_messages[myrand() % i], rp->name, MESSAGE_PRIORITY_NORMAL, HUD_SOURCE_FRIENDLY );
2177         */
2178
2179         mission_log_add_entry(LOG_PLAYER_REINFORCEMENT, rp->name, NULL);
2180 }
2181
2182 // function to display a list of reinforcements available to the player
2183 void hud_squadmsg_reinforcement_select()
2184 {
2185         int i, k;
2186         reinforcements *rp;
2187
2188         if ( Num_menu_items == -1 ) {
2189                 Num_menu_items = 0;
2190                 for (i = 0; i < Num_reinforcements; i++) {
2191                         rp = &Reinforcements[i];
2192
2193                         // don't put reinforcements onto the list that have already been used up.
2194                         if ( (rp->num_uses == rp->uses) ){
2195                                 continue;
2196                         }
2197
2198                         // don't put items which are not on my team
2199                         if((Player_ship != NULL) && (ship_get_reinforcement_team(i) != Player_ship->team)){
2200                                 continue;
2201                         } 
2202
2203                         Assert ( Num_menu_items < MAX_MENU_ITEMS );
2204                         strcpy( MsgItems[Num_menu_items].text, rp->name );
2205                         MsgItems[Num_menu_items].instance = i;
2206                         MsgItems[Num_menu_items].active = 0;
2207
2208                         if ( rp->flags & RF_IS_AVAILABLE ) {
2209                                 MsgItems[Num_menu_items].active = 1;
2210                         }
2211
2212                         Num_menu_items++;
2213                 }
2214         }
2215
2216 //      hud_squadmsg_display_menu( "Select Reinforcement" );
2217         hud_squadmsg_display_menu( XSTR( "Select Ship/Wing", 319) );    // AL 11-14-97: Reinforcement didn't fit, so using this for now
2218         k = hud_squadmsg_get_key();
2219         if (k != -1) {
2220                 int rnum;
2221
2222                 hud_squadmsg_toggle();                                          // take us out of message mode
2223
2224                 rnum = MsgItems[First_menu_item + k].instance;
2225
2226                 // check to see if trying to call a reinforcement not yet available.  If so, maybe play message, but
2227                 // definately bail
2228                 if ( MsgItems[First_menu_item + k].active == 0 ) {                                              
2229                         return;
2230                 }
2231
2232                 // in single player, or a multiplayer master, call in the reinforcement.  Clients send a packet to the
2233                 // server
2234                 if ( MULTIPLAYER_CLIENT ) {
2235                         Reinforcements[rnum].num_uses++;                        // increment this variable here since clients need to maintain a valid count
2236                         send_player_order_packet(SQUAD_MSG_REINFORCEMENT, rnum, 0);
2237                 } else {
2238                         hud_squadmsg_call_reinforcement(rnum);
2239                 }
2240         }
2241 }
2242
2243 // function to display list of commands for a ship
2244 void hud_squadmsg_ship_command()
2245 {
2246         int k;
2247         int i, orders, default_orders;
2248
2249         // when adding ship commands, we must look at the type of ship, and what messages that
2250         // ship allows.  First, place all messages that are possible onto the menu, then 
2251
2252         // see if messaging all ships or just one.  Messaging all ships will mean all default orders
2253         // show on comm menu.
2254         if ( Msg_instance != MESSAGE_ALL_FIGHTERS ) {
2255                 orders = Ships[Msg_instance].orders_accepted;
2256                 default_orders = ship_get_default_orders_accepted( &Ship_info[Ships[Msg_instance].ship_info_index] );
2257         } else {
2258
2259                 default_orders = FIGHTER_MESSAGES;
2260                 orders = default_orders;
2261         }
2262
2263         First_menu_item = 0;
2264         Num_menu_items = 0;
2265         for ( i = 0; i < MAX_SHIP_ORDERS; i++ ) {
2266                 // check to see if the comm order should even be added to the menu -- if so, then add it
2267                 // the order will be activated if the bit is set for the ship.
2268                 if ( default_orders & Comm_orders[i].value ) {
2269                         Assert ( Num_menu_items < MAX_MENU_ITEMS );
2270                         strcpy( MsgItems[Num_menu_items].text, comm_order_menu_text(i) );
2271                         MsgItems[Num_menu_items].instance = Comm_orders[i].value;
2272                         MsgItems[Num_menu_items].active = 0;
2273                         // check the bit to see if the command is active
2274                         if ( orders & Comm_orders[i].value )
2275                                 MsgItems[Num_menu_items].active = 1;
2276
2277                         // if the order cannot be carried out by the ship, then item should be inactive
2278                         if ( (Msg_instance != MESSAGE_ALL_FIGHTERS) && !hud_squadmsg_ship_order_valid( Msg_instance, Comm_orders[i].value ) )
2279                                 MsgItems[Num_menu_items].active = 0;
2280
2281                         // do some other checks to possibly gray out other items.
2282                         // if no target, remove any items which are associated with the players target
2283                         if ( !hud_squadmsg_is_target_order_valid(i, 0) )
2284                                 MsgItems[Num_menu_items].active = 0;
2285
2286                         // if messaging all fighters, see if we should gray out the order if no one will accept it,
2287                         // or modify the text if only some of the ships will accept it
2288                         if ( Msg_instance == MESSAGE_ALL_FIGHTERS ) {
2289                                 ship_obj *so;
2290                                 ship *shipp;
2291                                 int partial_accept, all_accept;                 // value which tells us what to do with menu item
2292
2293                                 all_accept = Comm_orders[i].value;
2294                                 partial_accept = 0;
2295                                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2296
2297                                         // don't send messge to ships not on player's team, or that are in a wing.
2298                                         shipp = &Ships[Objects[so->objnum].instance];
2299                                         if ( shipp->team != Player_ship->team )
2300                                                 continue;
2301
2302                                         // don't send message to non fighter wings
2303                                         if ( !(Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) )
2304                                                 continue;
2305
2306                                         all_accept &= shipp->orders_accepted;           // 'and'ing will either keep this bit set or zero it properly
2307                                         partial_accept |= (shipp->orders_accepted & Comm_orders[i].value);      // 'or'ing will tell us if at least one accepts
2308                                 }
2309
2310                                 if ( !all_accept ) {
2311                                         // either modify the text if a partial accept, or grey it out if no one accepts
2312                                         if ( partial_accept ) {
2313                                                 strcat( MsgItems[Num_menu_items].text, XSTR( "(*)", 320) );
2314                                         } else {
2315                                                 MsgItems[Num_menu_items].active = 0;
2316                                         }
2317                                 }
2318                         }
2319
2320                         Num_menu_items++;
2321                 }
2322         }
2323
2324         hud_squadmsg_display_menu( XSTR( "What Command", 321) );
2325         k = hud_squadmsg_get_key();
2326
2327         // when we get a valid goal, we must add the goal to the ai ship's goal list
2328
2329         if ( k != -1 ) {
2330                 Assert ( k < Num_menu_items );
2331                 // when messaging all fighters or ignoring target, call the send_to_all_fighters routine
2332                 if ( (Msg_instance != MESSAGE_ALL_FIGHTERS) && (MsgItems[k].instance != IGNORE_TARGET_ITEM) )
2333                         hud_squadmsg_send_ship_command( Msg_instance, MsgItems[k].instance, 1 );
2334                 else
2335                         hud_squadmsg_send_to_all_fighters( MsgItems[k].instance );
2336                 hud_squadmsg_toggle();
2337         }
2338 }
2339
2340 // function to display list of command for a wing
2341 void hud_squadmsg_wing_command()
2342 {
2343         int k;
2344         wing *wingp;
2345         int default_orders, i, orders;
2346
2347         // when adding commands for wings, we will look at all of the ships in the wing, and create
2348         // the order list from that set of ships.  In the future, we may want to do something else....
2349
2350         wingp = &Wings[Msg_instance];
2351
2352         // or together all of the orders for all the ships in the wing
2353         default_orders = 0;
2354         for ( i = 0; i < wingp->current_count; i++ ) {
2355                 orders = ship_get_default_orders_accepted( &Ship_info[Ships[wingp->ship_index[i]].ship_info_index] );
2356                 default_orders |= orders;
2357         }
2358         default_orders &= ~CAPTURE_TARGET_ITEM;         // we cannot capture any target with a wing.
2359
2360         Num_menu_items = 0;
2361         orders = Ships[wingp->ship_index[0]].orders_accepted;           // get the orders that the first ship in the wing will accept
2362         for ( i = 0; i < MAX_SHIP_ORDERS; i++ ) {
2363                 // add the set of default orders to the comm menu.  We will currently allow all messages
2364                 // to be available in the wing.
2365                 if ( default_orders & Comm_orders[i].value ) {
2366                         Assert ( Num_menu_items < MAX_MENU_ITEMS );
2367                         strcpy( MsgItems[Num_menu_items].text, comm_order_menu_text(i) );
2368                         MsgItems[Num_menu_items].instance = Comm_orders[i].value;
2369                         MsgItems[Num_menu_items].active = 0;
2370
2371                         // possibly grey out the menu item depending on whether or not the "wing" will accept this order
2372                         // the "wing" won't accept the order if the first ship in the wing doesn't accept it.
2373                         if ( orders & Comm_orders[i].value )
2374                                 MsgItems[Num_menu_items].active = 1;
2375
2376                         // do some other checks to possibly gray out other items.
2377                         // if no target, remove any items which are associated with the players target
2378                         if ( !hud_squadmsg_is_target_order_valid(i, 0) )
2379                                 MsgItems[Num_menu_items].active = 0;
2380
2381                         Num_menu_items++;
2382                 }
2383         }
2384
2385         hud_squadmsg_display_menu( XSTR( "What Command", 321) );
2386         k = hud_squadmsg_get_key();
2387         if ( k != -1 ) {
2388
2389                 // ignore target gets sent to everyone.
2390                 if ( MsgItems[k].instance != IGNORE_TARGET_ITEM )
2391                         hud_squadmsg_send_wing_command( Msg_instance, MsgItems[k].instance, 1 );
2392                 else
2393                         hud_squadmsg_send_to_all_fighters( MsgItems[k].instance );
2394                 hud_squadmsg_toggle();
2395         }
2396 }
2397
2398
2399
2400 //----------------------------------------------------------
2401 // external entry points below!!!!
2402
2403 // when starting messaging mode, we must remove old bindings from the
2404 // keys that are used for messaging mode (which will get restored when
2405 // messaging mode is done).
2406
2407 // this code below will get called only the key config changes (from ControlsConfig.cpp)
2408 // or if the bindings haven't been saved yet.  This code doesn't remove the bindings
2409 // but just sets up the array so that the bindings can be removed when messaging
2410 // mode is entered.
2411 //
2412 // do_scroll indicates whether we should save the page up and page down keys
2413 void hud_squadmsg_save_keys( int do_scroll )
2414 {
2415 //      int i, j;
2416
2417         num_keys_saved = 0;
2418
2419 /*
2420         for ( j=0; j<MAX_KEYS_USED; j++ ) {
2421                 for ( i=0; Control_config[i].text[0]; i++ ) {   // the text field in this structure is empty at the end of the config list
2422                         if ( Control_config[i].key_id == keys_used[j] ) {               // this is true if we have a match
2423
2424                                 // if we are not saving scrolling keys and we are trying to match page up and page down
2425                                 // then skip them.
2426                                 if ( !do_scroll && ((keys_used[j] == KEY_PAGEDOWN) || (keys_used[j] == KEY_PAGEUP)) )
2427                                         continue;
2428
2429                                 Assert( num_keys_saved < MAX_KEYS_USED );
2430                                 key_save[num_keys_saved].option_num = i;
2431                                 key_save[num_keys_saved].key_value = keys_used[j];
2432                                 num_keys_saved++;
2433                                 break;  // done with this key -- move to next.
2434                         }
2435                 }
2436         }
2437 */
2438 }
2439
2440 // function is called once per mission start.  Initializes those values
2441 // which only need to be inited once per mission.
2442 void hud_init_squadmsg( void ) 
2443 {
2444         int i;
2445
2446         if ( !Mbox_frames_loaded ) {
2447                 for ( i = 0; i < NUM_MBOX_FRAMES; i++ ) {
2448                         Mbox_gauge[i].first_frame = bm_load_animation(Mbox_fnames[gr_screen.res][i], &Mbox_gauge[i].num_frames);
2449                         if ( Mbox_gauge[i].first_frame == -1 ) {
2450                                 Warning(LOCATION, "Could not load in ani: %s\n", Mbox_fnames[gr_screen.res][i]);
2451                                 return;
2452                         }
2453                 }
2454                 Mbox_frames_loaded = 1;
2455         }
2456
2457         Msg_eat_key_timestamp = timestamp(0);
2458 }
2459
2460 // external entry point into code which changes the messaging mode based on the
2461 // previous player flag value.  I thought it better to isolate all system changes
2462 // in this code.
2463 void hud_squadmsg_toggle()
2464 {
2465         // if the emp effect is active, always ignore this
2466         if(emp_active_local()){
2467                 return;
2468         }
2469
2470         // if entering this mode, must setup messaging system.  Don't start squadmessging if 
2471         // the player is dead.
2472         if ( !(Player->flags & PLAYER_FLAGS_MSG_MODE) ) {
2473                 if ( Game_mode & GM_DEAD ){
2474                         return;
2475                 }
2476                 if ( (Game_mode & GM_MULTIPLAYER) && NETPLAYER_IS_OBSERVER(Net_player) ){
2477                         return;
2478                 }
2479                 hud_squadmsg_start();
2480         } else {
2481                 hud_squadmsg_end();
2482         }
2483
2484         Player->flags ^= PLAYER_FLAGS_MSG_MODE;
2485 }
2486
2487 //#ifndef NDEBUG
2488 // extern entry point to allow messaging of enemies
2489 void hud_enemymsg_toggle()
2490 {
2491         hud_squadmsg_toggle();
2492         // if we just entered message mode, turn on var that says to message enemies
2493         if ( Player->flags & PLAYER_FLAGS_MSG_MODE )
2494                 Msg_enemies = 1;
2495 }
2496 //#endif
2497
2498 // external entry point into code when a keyboard shortcut is used for a command
2499 // we are passed in an ID for the command to set internal variables.  This command
2500 // will be used in place of the last menu in the messaging code
2501 void hud_squadmsg_shortcut( int command )
2502 {
2503         // check if the communications system is capable of sending a message
2504         if ( (hud_communications_state(Player_ship, 1) != COMM_OK) && (command != REARM_REPAIR_ME_ITEM) ) {
2505                 return;
2506         }
2507
2508         // observers in multiplayer games cannot have this active either
2509         if ( (Game_mode & GM_MULTIPLAYER) && NETPLAYER_IS_OBSERVER(Net_player) )
2510                 return;
2511
2512         // in multiplayer and I cannot message, don't allow anything except calling in for rearm
2513         if ( (Game_mode & GM_MULTIPLAYER) && !multi_can_message(Net_player) && (command != REARM_REPAIR_ME_ITEM) )
2514                 gamesnd_play_error_beep();
2515
2516         // player ships which turns traitor cannot rearm
2517         if ( Player_ship->team == TEAM_TRAITOR )
2518                 return;
2519
2520         if ( Player->flags & PLAYER_FLAGS_MSG_MODE )            // we are already in messaging mode -- maybe do sometime more interesting?
2521                 return;
2522         hud_squadmsg_toggle();
2523         Msg_shortcut_command = command;                                                                 // save the command for later use
2524         if ( Msg_shortcut_command == CAPTURE_TARGET_ITEM )                      // some commands only apply to wings or ships
2525                 Squad_msg_mode = SM_MODE_SHIP_SELECT;                                           //  -- don't offer choice
2526         else if ( Msg_shortcut_command == IGNORE_TARGET_ITEM ) {        //  ignoring target applied to all ships
2527                 hud_squadmsg_toggle();                                                                                  // turns off mode which was turned on above
2528                 hud_squadmsg_send_to_all_fighters( Msg_shortcut_command );
2529         }
2530 }
2531
2532 // external entry point which is called when the player hits a selection key (1-0) while in messaging
2533 // mode.  If we are in messaging mode, send the shortcut command to the ships that are part of the
2534 // passed in selection set.  If there is no shortcut command, do nothing.  Returns 1 if the key
2535 // was used, else 0.  This return value is used to tell the key control system that it should
2536 // call the normal targeting selection stuff.
2537 int hud_squadmsg_hotkey_select( int k )
2538 {
2539         htarget_list *hitem, *plist;
2540         int send_message;
2541         object *objp;
2542
2543         Assert ( Player->flags & PLAYER_FLAGS_MSG_MODE );
2544
2545         if ( Msg_shortcut_command == -1 )
2546                 return 0;
2547
2548         Assert ( (k >= 0) && (k < MAX_KEYED_TARGETS) );
2549         plist = &(Player->keyed_targets[k]);
2550
2551         if ( EMPTY(plist) )             // be sure that we have at least one ship in the list
2552                 return 0;
2553
2554         send_message = 1;
2555         // for each ship in the selection set list, send the shortcut command that the player
2556         // previously entered.  Be sure to check that we are not trying to send a command to
2557         // an enemy ship.
2558         for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
2559                 objp = hitem->objp;
2560                 Assert ( objp->type == OBJ_SHIP );
2561                 if ( Ships[objp->instance].team != TEAM_FRIENDLY )
2562                         continue;
2563
2564                 // be sure that this ship can accept this command
2565                 // DDOI - I changed this , to & which seeems to be in keeping
2566                 // with similar code.
2567                 if ( !(Msg_shortcut_command & Ships[objp->instance].orders_accepted) )
2568                         continue;
2569
2570                 hud_squadmsg_send_ship_command( objp->instance, Msg_shortcut_command, send_message );
2571                 send_message  = 0;
2572         }
2573
2574         hud_squadmsg_toggle();                  // turn off messaging mode
2575         return 1;
2576 }
2577
2578
2579 // the next function is called once a frame when the player is messaging someone
2580 // in his squad.  After a period of 5 seconds of inactivity (i.e. no keypress to
2581 // select something in the menu), the menu will disappear.  This function will only
2582 // get called if the player flag PLAYER_FLAG_MSG_MODE is set.  Parameter is the key
2583 // that was hit this frame
2584
2585 int hud_squadmsg_do_frame( )
2586 {
2587         int target_changed;
2588
2589         Assert ( Player->flags & PLAYER_FLAGS_MSG_MODE );               // be sure that messaging mode is set!!!
2590
2591         // if the player is now dead, or the timestamp elapsed, then get out of messaging mode.
2592         if ( (Game_mode & GM_DEAD) || timestamp_elapsed(Msg_mode_timestamp) ) {
2593                 hud_squadmsg_toggle();
2594                 return 0;
2595         }
2596
2597         Msg_key_used = 0;
2598
2599         // check the player's current target.  Change in target resets the timer
2600         target_changed = 0;
2601         if ( Msg_target_objnum != Player_ai->target_objnum ) {
2602                 Msg_target_objnum = Player_ai->target_objnum;
2603                 target_changed = 1;
2604         }
2605
2606         if ( Msg_targeted_subsys != Player_ai->targeted_subsys ) {
2607                 Msg_targeted_subsys = Player_ai->targeted_subsys;
2608                 target_changed = 1;
2609         }
2610
2611         // setup color/font info 
2612         // hud_set_default_color();
2613         hud_set_gauge_color(HUD_MESSAGE_BOX);
2614
2615         // check for multiplayer mode - this is really a special case checker for support ship requesting and aborting
2616         if((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Squad_msg_mode == SM_MODE_REPAIR_REARM || Squad_msg_mode == SM_MODE_REPAIR_REARM_ABORT)){
2617                 char *subsys_name;
2618 //              int who_to_sig;
2619                 ushort net_sig;
2620                 
2621                 // who_to_sig = Objects[Ships[shipnum].objnum].net_signature;
2622                 if(Player_ai->target_objnum != -1)
2623                         net_sig = Objects[Player_ai->target_objnum].net_signature;
2624                 else 
2625                         net_sig = 0;
2626
2627       if ((Player_ai->targeted_subsys != NULL) && (Player_ai->targeted_subsys->current_hits > 0.0f))
2628                         subsys_name = Player_ai->targeted_subsys->system_info->subobj_name;
2629                 else
2630                         subsys_name = NULL;
2631                 
2632                 // send the correct packet
2633                 if(Squad_msg_mode == SM_MODE_REPAIR_REARM)              
2634                         send_player_order_packet(SQUAD_MSG_SHIP, 0, REARM_REPAIR_ME_ITEM);
2635                 else
2636                         send_player_order_packet(SQUAD_MSG_SHIP, 0, ABORT_REARM_REPAIR_ITEM);
2637
2638                 // make sure to toggle the mode off
2639                 hud_squadmsg_toggle();
2640                 
2641                 return 1;
2642         }
2643
2644         // draw top of frame
2645         if ( Mbox_gauge[0].first_frame >= 0 ) {
2646                 GR_AABITMAP(Mbox_gauge[0].first_frame, Mbox_top_coords[gr_screen.res][0], Mbox_top_coords[gr_screen.res][1]);           
2647         }
2648
2649         switch( Squad_msg_mode ) {
2650
2651         case SM_MODE_TYPE_SELECT:
2652                 hud_squadmsg_type_select();
2653                 break;
2654
2655         case SM_MODE_SHIP_SELECT:
2656                 hud_squadmsg_ship_select();
2657                 break;
2658
2659         case SM_MODE_WING_SELECT:
2660                 hud_squadmsg_wing_select();
2661                 break;
2662
2663         case SM_MODE_SHIP_COMMAND:
2664                 hud_squadmsg_ship_command();
2665                 break;
2666
2667         case SM_MODE_WING_COMMAND:
2668                 hud_squadmsg_wing_command();
2669                 break;
2670
2671         case SM_MODE_REINFORCEMENTS:
2672                 hud_squadmsg_reinforcement_select();
2673                 break;          
2674                 
2675         case SM_MODE_REPAIR_REARM:
2676                 //if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (addr != NULL)){
2677                 //      hud_squadmsg_repair_rearm(1,&Objects[Net_players[player_num].player->objnum]);
2678                 //} else {
2679                         hud_squadmsg_repair_rearm(1);                           // note we return right away.  repair/rearm code handles messaging, etc
2680                 //}     
2681                 break;
2682
2683         case SM_MODE_REPAIR_REARM_ABORT:
2684                 //if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (addr != NULL)){
2685                 //      hud_squadmsg_repair_rearm_abort(1,&Objects[Net_players[player_num].player->objnum]);
2686                 //} else {
2687                         hud_squadmsg_repair_rearm_abort(1);             // note we return right away.  repair/rearm code handles messaging, etc
2688                 //}
2689                 break;
2690
2691         case SM_MODE_ALL_FIGHTERS:
2692                 hud_squadmsg_msg_all_fighters();
2693                 break;
2694
2695         default:
2696                 Int3();         // get allender -- invalid mode in messaging system
2697                 break;
2698
2699         }
2700
2701         // be sure to reset the clip region
2702         HUD_reset_clip();               // JAS: Is this needed?
2703
2704         if ( Msg_key_used || target_changed ) {
2705                 Msg_mode_timestamp = timestamp(DEFAULT_MSG_TIMEOUT);
2706                 return 1;
2707         } else
2708                 return 0;
2709 }
2710
2711 void hud_add_issued_order(char *name, int order, char *target)
2712 {
2713         Squadmsg_history[squadmsg_history_index].ship = get_parse_name_index(name);
2714         Squadmsg_history[squadmsg_history_index].order = order;
2715         if (target)
2716                 Squadmsg_history[squadmsg_history_index].target = get_parse_name_index(target);
2717         else
2718                 Squadmsg_history[squadmsg_history_index].target = -1;
2719
2720         squadmsg_history_index++;
2721         if (squadmsg_history_index >= SQUADMSG_HISTORY_MAX)
2722                 squadmsg_history_index = 0;
2723 }
2724
2725 int hud_query_order_issued(char *name, char *order, char *target)
2726 {
2727         int i, o=-1, ship, t;
2728
2729         ship = get_parse_name_index(name);
2730         t = -1;
2731         if (target)
2732                 t = get_parse_name_index(target);
2733
2734         for (i=0; i<MAX_SHIP_ORDERS; i++)
2735                 if (!stricmp(order, comm_order_menu_text(i)) )
2736                         o = Comm_orders[i].value;
2737
2738         Assert(i < MAX_SHIP_ORDERS);
2739         for (i=0; i<SQUADMSG_HISTORY_MAX; i++)
2740                 if (Squadmsg_history[i].order == o)
2741                         if (ship == Squadmsg_history[i].ship)
2742                                 if (Squadmsg_history[i].target == t)
2743                                         return 1;
2744
2745         return 0;
2746 }
2747
2748
2749 void hudsquadmsg_page_in() 
2750 {
2751         int i;
2752
2753         for ( i = 0; i < NUM_MBOX_FRAMES; i++ ) {
2754                 bm_page_in_aabitmap( Mbox_gauge[i].first_frame, Mbox_gauge[i].num_frames );
2755         }
2756 }
2757