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