2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Hud/HUDsquadmsg.cpp $
15 * File to control sqaudmate messaging
18 * Revision 1.5 2005/08/12 08:52:32 taylor
19 * various GCC4 warning fixes
21 * Revision 1.4 2002/07/13 06:46:48 theoddone33
24 * Revision 1.3 2002/06/09 04:41:21 relnev
25 * added copyright header
27 * Revision 1.2 2002/05/07 03:16:45 theoddone33
28 * The Great Newline Fix
30 * Revision 1.1.1.1 2002/05/03 03:28:09 root
34 * 16 9/06/99 10:45a Andsager
35 * Add freighter to player override of protected status.
37 * 15 9/06/99 10:32a Andsager
38 * Allow attack of protected fighter, bomber, freighters, by player's
41 * 14 8/26/99 8:51p Dave
42 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
44 * 13 7/30/99 10:31p Dave
45 * Added comm menu to the configurable hud files.
47 * 12 7/09/99 5:54p Dave
48 * Seperated cruiser types into individual types. Added tons of new
49 * briefing icons. Campaign screen.
51 * 11 6/16/99 10:20a Dave
52 * Added send-message-list sexpression.
54 * 10 6/09/99 9:53a Andsager
55 * 1st pass at grey menu items when no ships/wings/fighters accepting
58 * 9 4/23/99 12:01p Johnson
61 * 8 4/16/99 5:54p Dave
62 * Support for on/off style "stream" weapons. Real early support for
63 * target-painting lasers.
65 * 7 3/30/99 5:40p Dave
66 * Fixed reinforcements for TvT in multiplayer.
68 * 6 3/28/99 5:58p Dave
69 * Added early demo code. Make objects move. Nice and framerate
70 * independant, but not much else. Don't use yet unless you're me :)
72 * 5 1/07/99 9:07a Jasen
75 * 4 12/28/98 3:17p Dave
76 * Support for multiple hud bitmap filenames for hi-res mode.
78 * 3 12/21/98 5:02p Dave
79 * Modified all hud elements to be multi-resolution friendly.
81 * 2 10/07/98 10:53a Dave
84 * 1 10/07/98 10:49a Dave
86 * 198 9/11/98 2:05p Allender
87 * make reinforcements work correctly in multiplayer games. There still
88 * may be a team vs team issue that I haven't thought of yet :-(
90 * 197 8/28/98 3:28p Dave
91 * EMP effect done. AI effects may need some tweaking as required.
93 * 196 8/25/98 1:48p Dave
94 * First rev of EMP effect. Player side stuff basically done. Next comes
97 * 195 6/30/98 2:17p Dave
98 * Revised object update system. Removed updates for all weapons. Put
99 * button info back into control info packet.
101 * 194 6/09/98 10:31a Hoffoss
102 * Created index numbers for all xstr() references. Any new xstr() stuff
103 * added from here on out should be added to the end if the list. The
104 * current list count can be found in FreeSpace.cpp (search for
107 * 193 5/26/98 11:54a Allender
108 * fix multiplayer problems and sexpression crash
110 * 192 5/24/98 4:27p Allender
111 * fix bug when determine who could message in multiplayer
113 * 191 5/23/98 2:34a Lawrance
114 * Fix problems with HUD squad messaging, don't save/restore bindings
116 * 190 5/22/98 12:41a Allender
117 * don't save/restore key presses in comm menu
119 * 189 5/21/98 9:38p Allender
120 * don't save/clear key bindings
122 * 188 5/21/98 3:32p Allender
123 * don't allow comm menu in observer mode
125 * 187 5/19/98 12:19p Mike
128 * 186 5/18/98 10:08a Lawrance
129 * increase MSG_KEY_EAT_TIME to 300ms
131 * 185 5/18/98 12:41a Allender
132 * fixed subsystem problems on clients (i.e. not reporting properly on
133 * damage indicator). Fixed ingame join problem with respawns. minor
136 * 184 5/13/98 5:08p Allender
137 * fix code in which sometimes wings wouldn't respond in multiplayer when
138 * doing a message all fighters
140 * 183 5/08/98 4:38p Allender
141 * always allow player ships to count when counting fighters for
142 * messaging. Terran command will now always issue the shooting at
145 * 182 5/08/98 2:11p Mike
146 * Add "/Repair Subsys" to "Rearm" option in Comm menu.
148 * 181 5/06/98 2:57p Allender
149 * always allow rearm ship to be called in
151 * 180 5/05/98 2:04a Mike
152 * Fix bug in support ship code.
154 * 179 5/05/98 1:41a Mike
155 * Improve support ship availability.
157 * 178 5/04/98 12:59a Allender
158 * players who are traitors shouldn't be allowed to rearm or use messaging
161 * 177 5/04/98 12:39a Allender
162 * make page up and page down only active when > 10 items on menu
164 * 176 4/29/98 10:56p Allender
165 * don't allow shortcuts in mutliplayer when player cannot message (except
168 * 175 4/23/98 10:06a Allender
169 * don't use the word "player" in event log for rearm event. Send
170 * shipname instead (players only)
172 * 174 4/23/98 9:15a Allender
173 * make rearm shortcut work for clients
175 * 173 4/23/98 1:49a Allender
176 * major rearm/repair fixes for multiplayer. Fixed respawning of AI ships
177 * to not respawn until 5 seconds after they die. Send escort information
180 * 172 4/22/98 4:59p Allender
181 * new multiplayer dead popup. big changes to the comm menu system for
182 * team vs. team. Start of debriefing stuff for team vs. team Make form
183 * on my wing work with individual ships who have high priority orders
185 * 171 4/21/98 12:15a Allender
186 * don't allow observers to use shortcut messaging keys
188 * 170 4/20/98 12:36a Mike
189 * Make team vs. team work when player is hostile. Several targeting
192 * 169 4/13/98 12:51p Allender
193 * made countermeasure succeed indicator work in multiplayer. Make rearm
194 * shortcut work more appropriately.
196 * 168 4/10/98 2:42p Johnson
197 * (from allender) when sending wing command, don't assert if ship to
198 * send message not found -- don't send message. Allow rearm message
199 * shortcut even if comm destroyed
201 * 167 4/10/98 2:39p Johnson
203 * 166 4/10/98 12:47p Allender
204 * changed working on replay popup. Don't reference repair in comm menu.
205 * Added Shift-R for repair me
207 * 165 4/09/98 12:35p Allender
208 * disallow messaging to departing wings and departing/dying ships
210 * 164 4/08/98 4:06p Allender
211 * make selection of wing Player team based, not TEAM_FRIENDLY.
213 * 163 4/07/98 5:30p Lawrance
214 * Player can't send/receive messages when comm is destroyed. Garble
215 * messages when comm is damaged.
217 * 162 4/07/98 1:53p Lawrance
218 * Fix uninitialized data bug.
220 * 161 4/06/98 12:11a Allender
221 * prevent the comm menu keys from being held over after menu goes away
223 * 160 4/05/98 3:06p Allender
224 * don't allow ships/wings to act on orders which they shouldn't receive
226 * 159 4/03/98 12:17a Allender
227 * new sexpression to detect departed or destroyed. optionally disallow
228 * support ships. Allow docking with escape pods
230 * 158 4/02/98 5:50p Dave
231 * Put in support for standard comm messages to get sent to netplayers as
232 * well as ai ships. Make critical button presses not get evaluated on the
239 #include "freespace.h"
245 #include "hudtarget.h"
247 #include "hudsquadmsg.h"
248 #include "controlsconfig.h"
251 #include "missionparse.h"
253 #include "linklist.h"
254 #include "missionlog.h"
255 #include "missionmessage.h"
256 #include "hudtarget.h"
259 #include "missionparse.h"
260 #include "multimsgs.h"
261 #include "multiutil.h"
263 #include "hudtargetbox.h"
264 #include "multi_pmsg.h"
265 #include "subsysdamage.h"
268 // defines for different modes in the squad messaging system
270 #define SM_MODE_TYPE_SELECT 1 //am I going to message a ship or a wing
271 #define SM_MODE_SHIP_SELECT 2 //choosing actual ship
272 #define SM_MODE_WING_SELECT 3 //choosing actual wing
273 #define SM_MODE_SHIP_COMMAND 4 //which command to send to a ship
274 #define SM_MODE_WING_COMMAND 5 //which command to send to a wing
275 #define SM_MODE_REINFORCEMENTS 6 //call for reinforcements
276 #define SM_MODE_REPAIR_REARM 7 //repair/rearm player ship
277 #define SM_MODE_REPAIR_REARM_ABORT 8 //abort repair/rearm of player ship
278 #define SM_MODE_ALL_FIGHTERS 9 //message all fighters/bombers
280 #define DEFAULT_MSG_TIMEOUT (8 * 1000) // number of seconds * 1000 to get milliseconds
281 #define MSG_KEY_EAT_TIME (300)
283 static int Squad_msg_mode; // current mode that the messaging system is in
284 static int Msg_key_used; // local variable which tells if the key being processed
285 // with the messaging system was actually used
286 static int Msg_key; // global which indicates which key was currently pressed
287 static int Msg_mode_timestamp;
288 static int Msg_instance; // variable which holds ship/wing instance to send the message to
289 static int Msg_shortcut_command; // holds command when using a shortcut key
290 static int Msg_target_objnum; // id of the current target of the player
291 static ship_subsys *Msg_targeted_subsys;// pointer to current subsystem which is targeted
293 static int Msg_enemies; // tells us whether or not to message enemy ships or friendlies
296 static int Msg_eat_key_timestamp; // used to temporarily "eat" keys
298 // defined to position the messaging box
299 int Mbox_item_h[GR_NUM_RESOLUTIONS] = {
303 int Mbox_item_xoffset[GR_NUM_RESOLUTIONS] = {
308 // top of the message box gauge
309 int Mbox_top_coords[GR_NUM_RESOLUTIONS][2] = {
318 int Mbox_bmap_coords[GR_NUM_RESOLUTIONS][2] = {
327 // squadmsg menu pgup and pgdn
328 int Menu_pgup_coords[GR_NUM_RESOLUTIONS][2] = {
336 int Menu_pgdn_coords[GR_NUM_RESOLUTIONS][2] = {
346 // following defines/vars are used to build menus that are used in messaging mode
348 typedef struct mmode_item {
349 int instance; // instance in Ships/Wings array of this menu item
350 int active; // active items are in bold text -- inactive items greyed out
351 char text[NAME_LENGTH]; // text to display on the menu
354 #define MAX_MENU_ITEMS 50 // max number of items in the menu
355 #define MAX_MENU_DISPLAY 10 // max number that can be displayed
357 mmode_item MsgItems[MAX_MENU_ITEMS];
358 int Num_menu_items = -1; // number of items for a message menu
359 int First_menu_item= -1; // index of first item in the menu
362 // following set of vars/defines are used to store/restore key bindings for keys that
363 // are used in messaging mode
365 // array to temporarily store key bindings that will be in use for the messaging
367 typedef struct key_store {
368 int option_num; // which element in the Control_config array is this
369 int id; // which id (1 or 2) is this key.
370 int key_value; // which key value to put there.
373 #define MAX_KEYS_NO_SCROLL 10
374 #define MAX_KEYS_USED 12 // maximum number of keys used for the messaging system
376 key_store key_save[MAX_KEYS_USED]; // array to save the key information during messaging mode
377 int num_keys_saved = 0; // number of keys that are saved.
379 // next array is the array of MAX_KEYS_USED size which are the keys to use for messaging mode
381 int keys_used[] = { SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6, SDLK_7, SDLK_8, SDLK_9, SDLK_0,
382 SDLK_PAGEUP, SDLK_PAGEDOWN };
387 // following are defines and character strings that are used as part of messaging mode
389 #define TYPE_SHIP_ITEM 0
390 #define TYPE_WING_ITEM 1
391 #define TYPE_ALL_FIGHTERS_ITEM 2
392 #define TYPE_REINFORCEMENT_ITEM 3
393 #define TYPE_REPAIR_REARM_ITEM 4
394 #define TYPE_REPAIR_REARM_ABORT_ITEM 5
396 #define NUM_TYPE_SELECT 6
398 const char *type_select_str(int n)
400 #if NUM_TYPE_SELECT != 6
401 #error type_select_Str is not up to date
406 return XSTR( "Ships", 293);
408 return XSTR( "Wings", 294);
409 case TYPE_ALL_FIGHTERS_ITEM:
410 return XSTR( "All Fighters", 295);
411 case TYPE_REINFORCEMENT_ITEM:
412 return XSTR( "Reinforcements", 296);
413 case TYPE_REPAIR_REARM_ITEM:
414 return XSTR( "Rearm/Repair Subsys", 297);
415 case TYPE_REPAIR_REARM_ABORT_ITEM:
416 return XSTR( "Abort Rearm", 298);
422 // data structure to hold character string of commands for comm menu
423 typedef struct comm_order {
424 int value; // used to match which command to display on the menu
427 // note: If you change this table at all, keep it in sync with version in IgnoreOrdersDlg.cpp
428 // Also make sure you update comm_order_menu_text below this.
429 // Also make sure you update MAX_SHIP_ORDERS in HUDsquadmsg.h
430 comm_order Comm_orders[MAX_SHIP_ORDERS] = {
431 { ATTACK_TARGET_ITEM },
432 { DISABLE_TARGET_ITEM },
433 { DISARM_TARGET_ITEM },
434 { DISABLE_SUBSYSTEM_ITEM },
435 { PROTECT_TARGET_ITEM },
436 { IGNORE_TARGET_ITEM },
439 { ENGAGE_ENEMY_ITEM },
440 { CAPTURE_TARGET_ITEM },
441 { REARM_REPAIR_ME_ITEM },
442 { ABORT_REARM_REPAIR_ITEM },
446 // Text to display on the menu
447 // Given an index into the Comm_orders array, return the text associated with it.
448 // MUST BE 1:1 with Comm_orders.
449 const char *comm_order_menu_text(int index)
452 case 0: return XSTR( "Destroy my target", 299); break;
453 case 1: return XSTR( "Disable my target", 300); break;
454 case 2: return XSTR( "Disarm my target", 301); break;
455 case 3: return XSTR( "Destroy subsystem", 302); break;
456 case 4: return XSTR( "Protect my target", 303); break;
457 case 5: return XSTR( "Ignore my target", 304); break;
458 case 6: return XSTR( "Form on my wing", 305); break;
459 case 7: return XSTR( "Cover me", 306); break;
460 case 8: return XSTR( "Engage enemy", 307); break;
461 case 9: return XSTR( "Capture my target", 308); break;
462 case 10: return XSTR( "Rearm me", 309); break;
463 case 11: return XSTR( "Abort rearm", 310); break;
464 case 12: return XSTR( "Depart", 311); break;
471 // Text to display on the messaging menu when using the shortcut keys
472 const char *comm_order_hotkey_text( int index )
476 for (i = 0; i < MAX_SHIP_ORDERS; i++ ) {
477 if ( Comm_orders[i].value == index )
478 return comm_order_menu_text(i);
485 // a define of who can receive message
486 #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)
488 int squadmsg_history_index = 0;
489 squadmsg_history Squadmsg_history[SQUADMSG_HISTORY_MAX];
491 // used for Message box gauge
492 #define NUM_MBOX_FRAMES 3
494 static hud_frames Mbox_gauge[NUM_MBOX_FRAMES];
495 static int Mbox_frames_loaded = 0;
496 static const char *Mbox_fnames[GR_NUM_RESOLUTIONS][NUM_MBOX_FRAMES] =
500 "message1", // top part of menu
501 "message2", // middle part
502 "message3" // bottom part
505 "message1", // top part of menu
506 "message2", // middle part
507 "message3" // bottom part
512 static int Mbox_title_coord[GR_NUM_RESOLUTIONS][2] = {
520 static int Mbox_item_coord[GR_NUM_RESOLUTIONS][2] = {
529 // define for trapping messages send to "all fighters"
530 #define MESSAGE_ALL_FIGHTERS -999
532 // forward declarations
533 void hud_add_issued_order(const char *name, int order, const char *target);
534 int hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip = NULL );
535 int hud_squadmsg_ship_order_valid( int shipnum, int order );
537 // function to set up variables needed when messaging mode is started
538 void hud_squadmsg_start()
542 //if ( num_keys_saved < 0 ) // save the keys if they haven't been saved yet
543 hud_squadmsg_save_keys();
548 for (i=0; i<num_keys_saved; i++)
549 clear_key_binding ( (short) key_save[i].key_value ); // removes all mention of this key from Control_config
552 Num_menu_items = -1; // reset the menu items
554 Squad_msg_mode = SM_MODE_TYPE_SELECT; // start off at the base state
555 Msg_mode_timestamp = timestamp(DEFAULT_MSG_TIMEOUT); // initialize our timer to bogus value
556 Msg_shortcut_command = -1; // assume no shortcut key being used
557 Msg_target_objnum = Player_ai->target_objnum; // save the players target object number
558 Msg_targeted_subsys = Player_ai->targeted_subsys; // save the players currently targted subsystem
560 Msg_enemies = 0; // tells us if we are messaging enemy ships
563 snd_play( &Snds[SND_SQUADMSGING_ON] );
566 // functions which will restore all of the key binding stuff when messaging mode is done
567 void hud_squadmsg_end()
573 // move through all keys saved and restore their orignal values.
574 for ( i=0; i<num_keys_saved; i++ ) {
576 Control_config[ksp->option_num].key_id = (short) ksp->key_value;
580 if ( message_is_playing() == FALSE )
581 snd_play( &Snds[SND_SQUADMSGING_OFF] );
584 // function which returns true if there are fighters/bombers on the players team
586 int hud_squadmsg_count_fighters( )
592 // set up the team to compare for messaging. In debug versions, we will allow messaging to enemies
593 //team = TEAM_FRIENDLY;
594 team = Player_ship->team;
597 team = opposing_team_mask(Player_ship->team);
600 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
601 if ( objp->type != OBJ_SHIP )
604 shipp = &Ships[objp->instance];
605 // check fighter is accepting orders
606 if (shipp->orders_accepted != 0) {
607 // be sure ship is on correct team, not the player, and is a fighter/bomber
608 if ( (shipp->team == team) && (objp != Player_obj) && (Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) ) {
618 // function which counts the number of ships available for messaging. Used to determine if
619 // we should grey out a menu or allow a shortcut command to apply. parameter "flag" is used
620 // to tell us whether or not we should add the ship to a menu item or not. We include the
621 // flag so that we don't have to have conditions for messaging ships/wings in two places.
622 int hud_squadmsg_count_ships( int add_to_menu )
629 // set up the team to compare for messaging. In debug versions, we will allow messaging to enemies
630 //team = TEAM_FRIENDLY;
631 team = Player_ship->team;
634 team = opposing_team_mask(Player_ship->team);
638 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
640 shipp = &Ships[Objects[so->objnum].instance];
641 SDL_assert ( shipp->objnum != -1 );
643 // ships must be able to receive a message
644 if ( !(Ship_info[shipp->ship_info_index].flags & CAN_MESSAGE) )
647 // must be on the same team
648 if ( shipp->team != team )
651 // departing or dying ships cannot be on list
652 if ( shipp->flags & (SF_DEPARTING|SF_DYING) )
655 // MULTI - changed to allow messaging of netplayers
657 // cannot be my ship or an instructor
658 if ( (&Objects[so->objnum] == Player_obj) || is_instructor(&Objects[so->objnum]) )
661 // ship must be accepting ship type orders
662 if ( shipp->orders_accepted == 0)
665 // if it is a player ship, we must be in multiplayer
666 if ( (Objects[so->objnum].flags & OF_PLAYER_SHIP) && !(Game_mode & GM_MULTIPLAYER) )
669 // if a messaging shortcut, be sure this ship can process the order
670 if ( Msg_shortcut_command != -1 ) {
671 if ( !(shipp->orders_accepted & Msg_shortcut_command) )
673 else if ( !hud_squadmsg_ship_order_valid(Objects[so->objnum].instance, Msg_shortcut_command) )
679 SDL_assert ( Num_menu_items < MAX_MENU_ITEMS );
680 SDL_strlcpy( MsgItems[Num_menu_items].text, shipp->ship_name, SDL_arraysize(MsgItems[0].text) );
681 MsgItems[Num_menu_items].instance = SHIP_INDEX(shipp);
682 MsgItems[Num_menu_items].active = 1;
688 // if adding to the menu and we have > 10 items, then don't allow page up and page down to be used.
689 if ( add_to_menu && (Num_menu_items > MAX_MENU_DISPLAY) )
690 hud_squadmsg_save_keys(1);
694 // routine to return true if a wing should be put onto the messaging menu
695 int hud_squadmsg_wing_valid( wing *wingp, int team )
697 // int player_count, j;
699 // a couple of special cases to account for before adding to count (or to menu). The wing gone
700 // flags is firm indication to skip this particular wing. Also, skip if enemy wing
701 if ( (wingp->flags & WF_WING_GONE) || (wingp->current_count == 0) )
704 // departing wings don't get attention either
705 if ( wingp->flags & WF_WING_DEPARTING )
708 // sanity check on ship_index field -- if check is successful, then check the team.
709 SDL_assert (wingp->ship_index[0] != -1 );
710 if ( Ships[wingp->ship_index[0]].team != team )
713 // if this wing is the players wing, and there is only one ship in the wing, then skip past it
714 if ( (Ships[Player_obj->instance].wingnum == WING_INDEX(wingp)) && (wingp->current_count == 1) )
717 // check if wing commander is accepting orders
718 if ( Ships[wingp->ship_index[0]].orders_accepted == 0)
721 // if doing a message shortcut is being used, be sure the wing can "accept" the command. Only need
722 // to look at the first ship in the wing.
723 if ( Msg_shortcut_command != -1 ) {
724 if ( !(Ships[wingp->ship_index[0]].orders_accepted & Msg_shortcut_command) )
727 // MULTI - changed to allow messaging of netplayers
728 // don't count wings where all ships are player ships
731 for ( j = 0; j < wingp->current_count; j++ ) {
732 if ( Objects[Ships[wingp->ship_index[j]].objnum].flags & OF_PLAYER_SHIP )
735 if ( player_count == wingp->current_count )
742 // function like above, except for wings
743 int hud_squadmsg_count_wings( int add_to_menu )
748 // set up the team to compare for messaging. In debug versions, we will allow messaging to enemies
749 //team = TEAM_FRIENDLY;
750 team = Player_ship->team;
753 team = opposing_team_mask(Player_ship->team);
758 // add the player starting wings first
759 for ( i = 0; i < MAX_STARTING_WINGS; i++ ) {
762 wingnum = Starting_wings[i];
766 if ( hud_squadmsg_wing_valid(&Wings[wingnum], team) ) {
769 SDL_assert ( Num_menu_items < MAX_MENU_ITEMS );
770 SDL_strlcpy( MsgItems[Num_menu_items].text, Wings[wingnum].name, SDL_arraysize(MsgItems[0].text) );
771 MsgItems[Num_menu_items].instance = wingnum;
772 MsgItems[Num_menu_items].active = 1;
778 for ( i = 0; i < num_wings; i++ ) {
779 // if this wing is a player starting wing, skip it since we added it above
780 for ( j = 0; j < MAX_STARTING_WINGS; j++ ) {
781 if ( i == Starting_wings[j] )
784 if ( j < MAX_STARTING_WINGS )
787 if ( hud_squadmsg_wing_valid(&Wings[i], team) ) {
790 SDL_assert ( Num_menu_items < MAX_MENU_ITEMS );
791 SDL_strlcpy( MsgItems[Num_menu_items].text, Wings[i].name, SDL_arraysize(MsgItems[0].text) );
792 MsgItems[Num_menu_items].instance = i;
793 MsgItems[Num_menu_items].active = 1;
802 // function to set the current submode in message mode -- also resets variables that
803 // should be reset inbetween submodes
804 void hud_squadmsg_do_mode( int mode )
806 Squad_msg_mode = mode;
811 void hud_squadmsg_page_down()
813 if ( (First_menu_item + MAX_MENU_DISPLAY) < Num_menu_items ) {
814 First_menu_item += MAX_MENU_DISPLAY;
815 SDL_assert ( First_menu_item < Num_menu_items );
819 void hud_squadmsg_page_up()
821 if ( First_menu_item > 0 ) {
822 First_menu_item -= MAX_MENU_DISPLAY;
823 SDL_assert (First_menu_item >= 0 );
827 int hud_squadmsg_get_total_keys()
831 num_keys_used = MAX_KEYS_NO_SCROLL;
832 if ( Num_menu_items > MAX_MENU_DISPLAY )
833 num_keys_used = MAX_KEYS_USED;
835 return num_keys_used;
838 // function called from high level keyboard read code to give the squadmsg code a key.
839 // return 1 is the key was used by the messaging code, 0 otherwise
840 int hud_squadmsg_read_key( int k )
842 int i, key_found, num_keys_used;
844 num_keys_used = hud_squadmsg_get_total_keys();
846 if ( !(Player->flags & PLAYER_FLAGS_MSG_MODE) ) {
847 // check to see if any messaging keys are still down for some length of time
848 // after messaging is over. Return true for a while.
849 if ( !timestamp_elapsed(Msg_eat_key_timestamp) ) {
850 for (i = 0; i < num_keys_used; i++ ) {
851 if ( key_pressed(keys_used[i]) )
860 for (i = 0; i < num_keys_used; i++ ) {
861 if ( k == keys_used[i] ) {
862 if ( key_down_count(k) ) {
867 if ( key_pressed(k) ) {
871 // key_down_count(k);
882 // function which reads the keyboard array and determines if a menu key has been hit
883 int hud_squadmsg_get_key()
885 int k, i, num_keys_used;
893 num_keys_used = hud_squadmsg_get_total_keys();
895 // if the emp effect is active, never accept keypresses
896 if(emp_active_local()){
900 for ( i = 0; i < num_keys_used; i++ ) {
901 if ( k == keys_used[i] ) {
902 Msg_key_used = 1; // this variable will extend the timer
904 // use a timestamp to prevent top level key code from possibly reprocessing this key
905 Msg_eat_key_timestamp = timestamp(MSG_KEY_EAT_TIME);
906 if ( k == SDLK_PAGEDOWN ) { // pageup and pagedown scroll the menu -- deal with these seperately!!
907 hud_squadmsg_page_down();
909 } else if ( k == SDLK_PAGEUP ) {
910 hud_squadmsg_page_up();
912 } else if ( k == SDLK_ESCAPE ) {
913 hud_squadmsg_toggle();
915 } else if ( (i < Num_menu_items) && (Squad_msg_mode == SM_MODE_REINFORCEMENTS) ) // return any key if selecting reinforcement
918 // play general fail sound if inactive item hit.
919 else if ( (i < Num_menu_items) && !(MsgItems[i].active) )
920 gamesnd_play_iface(SND_GENERAL_FAIL);
922 else if ( (i < Num_menu_items) && (MsgItems[i].active) ) // only return keys that are associated with menu items
926 Msg_key_used = 0; // if no #-key pressed for visible item, break and allow timer to
927 break; // to continue as if no key was pressed
935 // function which will essentially print out the contents of the current state of the messaging
936 // menu. Parameters will be a title. The menu items and the number of items will be
937 // in global vars since they don't get recomputed every frame.
938 void hud_squadmsg_display_menu( const char *title )
940 int bx, by, sx, sy, i, nitems, none_valid, messaging_allowed;
942 // hud_set_bright_color();
943 hud_set_gauge_color(HUD_MESSAGE_BOX, HUD_C_BRIGHT);
945 gr_string(Mbox_title_coord[gr_screen.res][0], Mbox_title_coord[gr_screen.res][1], title);
948 if ( Num_menu_items < MAX_MENU_DISPLAY )
949 nitems = Num_menu_items;
951 if ( First_menu_item == 0 ) // First_menu_item == 0 means first page of items
952 nitems = MAX_MENU_DISPLAY;
953 else if ( (Num_menu_items - First_menu_item) <= MAX_MENU_DISPLAY ) // check if remaining items fit on one page
954 nitems = Num_menu_items - First_menu_item;
956 nitems = MAX_MENU_DISPLAY;
960 sx = Mbox_item_coord[gr_screen.res][0];
961 sy = Mbox_item_coord[gr_screen.res][1];
962 bx = Mbox_bmap_coords[gr_screen.res][0]; // global x-offset where bitmap gets drawn
963 by = Mbox_bmap_coords[gr_screen.res][1]; // global y-offset where bitmap gets drawn
965 none_valid = 1; // variable to tell us whether all items in the menu are valid or not
967 // use another variable to tell us whether we can message or not.
968 messaging_allowed = 1;
969 if ( (Game_mode & GM_MULTIPLAYER) && !multi_can_message(Net_player) ){
970 messaging_allowed = 0;
973 for ( i = 0; i < nitems; i++ ) {
975 char *text = MsgItems[First_menu_item+i].text;
977 // blit the background
978 // hud_set_default_color();
979 hud_set_gauge_color(HUD_MESSAGE_BOX);
980 if ( Mbox_gauge[1].first_frame >= 0 ) {
981 GR_AABITMAP(Mbox_gauge[1].first_frame, bx, by);
983 by += Mbox_item_h[gr_screen.res];
985 // set the text color
986 if ( MsgItems[First_menu_item+i].active ) {
987 // hud_set_bright_color();
988 hud_set_gauge_color(HUD_MESSAGE_BOX, HUD_C_BRIGHT);
991 dim_index = min(5, HUD_color_alpha - 2);
992 if ( dim_index < 0 ) {
995 gr_set_color_fast(&HUD_color_defaults[dim_index]);
998 hud_set_gauge_color(HUD_MESSAGE_BOX, HUD_C_DIM);
1001 // first do the number
1002 item_num = (i+1) % MAX_MENU_DISPLAY;
1003 emp_hud_printf(sx, sy, EG_SQ1 + i, NOX("%1d."), item_num );
1006 emp_hud_string(sx+Mbox_item_xoffset[gr_screen.res], sy, EG_SQ1 + i, text);
1008 sy += Mbox_item_h[gr_screen.res];
1010 // if we have at least one item active, then set the variable so we don't display any
1011 // message about no active items
1012 if ( MsgItems[First_menu_item+i].active )
1016 // maybe draw an extra line in to make room for [pgdn], or for the 'no active items'
1018 if ( !messaging_allowed || none_valid || ((First_menu_item + nitems) < Num_menu_items) || (Msg_shortcut_command != -1) ) {
1019 // blit the background
1020 // hud_set_default_color();
1021 hud_set_gauge_color(HUD_MESSAGE_BOX);
1022 if ( Mbox_gauge[1].first_frame >= 0 ) {
1024 GR_AABITMAP(Mbox_gauge[1].first_frame, bx, by);
1026 by += Mbox_item_h[gr_screen.res];
1029 // draw the bottom of the frame
1030 // hud_set_default_color();
1031 hud_set_gauge_color(HUD_MESSAGE_BOX);
1032 if ( Mbox_gauge[2].first_frame >= 0 ) {
1034 GR_AABITMAP(Mbox_gauge[2].first_frame, bx, by);
1037 // determine if we should put the text "[more]" at top or bottom to indicate you can page up or down
1038 hud_targetbox_start_flash(TBOX_FLASH_SQUADMSG);
1039 hud_targetbox_maybe_flash(TBOX_FLASH_SQUADMSG);
1040 if ( First_menu_item > 0 ) {
1041 gr_printf( Menu_pgup_coords[gr_screen.res][0], Menu_pgup_coords[gr_screen.res][1], XSTR( "[pgup]", 312) );
1044 if ( (First_menu_item + nitems) < Num_menu_items ) {
1045 gr_printf( Menu_pgdn_coords[gr_screen.res][0], Menu_pgdn_coords[gr_screen.res][1], XSTR( "[pgdn]", 313));
1048 if ( messaging_allowed ) {
1050 gr_printf( sx, by - Mbox_item_h[gr_screen.res] + 2, XSTR( "No valid items", 314));
1051 } else if ( !none_valid && (Msg_shortcut_command != -1) ){
1052 gr_printf( sx, by - Mbox_item_h[gr_screen.res] + 2, "%s", comm_order_hotkey_text(Msg_shortcut_command));
1055 // if this player is not allowed to message, then display message saying so
1056 gr_printf( sx, by - Mbox_item_h[gr_screen.res] + 2, XSTR( "Not allowed to message", 315));
1061 // function to return true or false if the given ship can rearm, or be repaired
1062 int hud_squadmsg_can_rearm( ship *shipp )
1064 // player ships which turns traitor cannot rearm
1065 if ( (shipp == Player_ship) && (Player_ship->team == TEAM_TRAITOR) )
1068 // 5/6/98 -- MWA Decided to always be able to call in support.
1072 // calls for repair/rearm of the player ship. Checks for the presense of the support
1073 // ship and does the appropriate action if found
1074 void hud_squadmsg_repair_rearm( int toggle_state, object *objp)
1079 int multi_player_num;
1081 // this is essentially a check for multiplayer server/client mode
1082 // in multiplayer mode, the server may have to issue this command when received from a client
1085 multi_player_num = -1;
1088 multi_player_num = multi_find_player_by_object(objp);
1089 SDL_assert(multi_player_num != -1);
1092 // see if player is already scheduled on arriving support ship. If so, issues appripriate
1094 if ( is_support_allowed(tobj) ) {
1095 if ( mission_is_repair_scheduled( tobj ) ) {
1096 message_send_builtin_to_player( MESSAGE_REARM_ON_WAY, NULL, MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, multi_player_num, -1 );
1098 robjnum = hud_support_find_closest(OBJ_INDEX(tobj));
1099 if ( robjnum != -1 ) {
1100 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 );
1102 // request a rearm. Next function returns -1 if ship is warping in, objnum of repair ship otherwise
1103 robjnum = ai_issue_rearm_request( tobj );
1104 if ( robjnum != -1) {
1105 robjp = &Objects[robjnum];
1106 message_send_builtin_to_player( MESSAGE_ON_WAY, &Ships[robjp->instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, multi_player_num, -1 );
1109 // if we are in this part of the if statment, a support ship has been warped in to
1110 // service us. Issue appropriate message
1111 message_send_builtin_to_player( MESSAGE_REARM_WARP, NULL, MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, multi_player_num, -1 );
1114 mission_log_add_entry(LOG_PLAYER_REARM, Ships[tobj->instance].ship_name, NULL);
1119 //if ( multi_player_num == -1 ) // only do the hud display if it is for me!
1120 // hud_support_view_start();
1123 hud_squadmsg_toggle(); // take us out of message mode
1126 // function which gets called from keyboard code to issues a shortcut command for rearming.
1127 void hud_squadmsg_rearm_shortcut()
1129 if ( !hud_squadmsg_can_rearm(Player_ship) )
1132 // multiplayer clients need to send this message to the server
1133 if ( MULTIPLAYER_CLIENT ) {
1134 send_player_order_packet(SQUAD_MSG_SHIP, 0, REARM_REPAIR_ME_ITEM);
1138 hud_squadmsg_repair_rearm(0);
1141 // code which is called when a player aborts his rearm request
1142 void hud_squadmsg_repair_rearm_abort( int toggle_state, object *obj)
1148 // this is essentially a check for multiplayer server/client mode
1149 // in multiplayer mode, the server may have to issue this command when received from a client
1155 // try to abort the request. We shoudln't be in this function unless we are actually
1156 // queued for repair. Send a message from support ship if the support ship is in the mission
1157 ai_abort_rearm_request( tobj );
1159 // move the next statements outside of the above if-statement. Seems like this place
1160 // is the right place, since we want to change state of the messaging system regardless
1161 // of what happened above.
1163 hud_squadmsg_toggle(); // take us out of message mode
1166 // returns 1 if an order is valid for a ship. Applies to things like departure when engines are blown, etc.
1167 int hud_squadmsg_ship_order_valid( int shipnum, int order )
1169 // disabled ships can't depart.
1170 if ( (order == DEPART_ITEM) && (Ships[shipnum].flags & SF_DISABLED) )
1176 // returns true or false if the Players target is valid for the given order
1177 // find_order is true when we need to search the comm_orders array for the order entry. We have
1178 // to do this action in some cases since all we know is the actual "value" of the order
1179 int hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip )
1181 int target_objnum, i;
1182 ship *shipp, *ordering_shipp;
1188 // find the comm_menu item for this command
1190 for (i = 0; i < MAX_SHIP_ORDERS; i++ ) {
1191 if ( Comm_orders[i].value == order )
1194 SDL_assert( i < MAX_SHIP_ORDERS );
1198 // orders which don't operate on targets are always valid
1199 if ( !(Comm_orders[order].value & TARGET_MESSAGES) )
1202 target_objnum = aip->target_objnum;
1204 // order isn't valid if there is no player target
1205 if ( target_objnum == -1 ) {
1209 objp = &Objects[target_objnum];
1211 ordering_shipp = &Ships[aip->shipnum];
1213 // target isn't a ship, then return 0
1214 if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_WEAPON) )
1217 // if it's a weapon, then it needs to be a WIF_BOMB weapon. Only attack order valid, and only
1218 // valid on bombs not on the player's team
1219 if ( objp->type == OBJ_WEAPON ) {
1220 if ( (Comm_orders[order].value == ATTACK_TARGET_ITEM )
1221 && (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB)
1222 && (Weapons[objp->instance].team != ordering_shipp->team) )
1229 SDL_assert( objp->type == OBJ_SHIP );
1231 shipp = &Ships[objp->instance];
1233 // if target is a navbouy, return 0
1234 if ( Ship_info[shipp->ship_info_index].flags & SIF_NAVBUOY ){
1238 // if we are messaging a ship, and that ship is our target, no target type orders are ever active
1239 if ( (Squad_msg_mode == SM_MODE_SHIP_COMMAND) && (target_objnum == Msg_instance) ){
1243 // if the order is a disable order or depart, and the ship is disabled, order isn't active
1244 if ( (Comm_orders[order].value == DISABLE_TARGET_ITEM) && (shipp->flags & SF_DISABLED) ){
1248 // same as above except for disabled.
1249 if ( (Comm_orders[order].value == DISARM_TARGET_ITEM) && ((shipp->subsys_info[SUBSYSTEM_TURRET].num > 0) && (shipp->subsys_info[SUBSYSTEM_TURRET].current_hits == 0.0f)) ){
1253 // if order is disable subsystem, and no subsystem targeted or no hits, then order not valid
1254 if ( (Comm_orders[order].value == DISABLE_SUBSYSTEM_ITEM) && ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) ){
1258 // check based on target's and player's team
1259 if ( (shipp->team == ordering_shipp->team) && (Comm_orders[order].value & FRIENDLY_TARGET_MESSAGES) ){
1261 } else if ( (shipp->team != ordering_shipp->team) && (Comm_orders[order].value & ENEMY_TARGET_MESSAGES) ){
1268 // function to send an order to all fighters/bombers.
1269 void hud_squadmsg_send_to_all_fighters( int command, int player_num )
1272 ship *shipp, *ordering_shipp;
1273 int i, send_message, to_everyone, do_ship;
1276 // quick short circuit here because of actually showing comm menu even though you cannot message.
1277 // just a safety net.
1278 if ( (Game_mode & GM_MULTIPLAYER) && (player_num != -1) ) {
1279 if ( !multi_can_message(&Net_players[player_num]) ) {
1284 // check for multiplayer mode
1285 if((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
1286 send_player_order_packet(SQUAD_MSG_ALL, 0, command);
1290 // to_everyone tells us whether the command should apply to all ships, or just to fighets/bombers.
1291 // when true, command goes to *all* friendlies.
1292 send_message = 1; // internal flag to dictate who sends message
1297 if ( player_num != -1 )
1298 aip = &Ai_info[Ships[Objects[Net_players[player_num].player->objnum].instance].ai_index];
1300 SDL_assert( aip->shipnum != -1 );
1301 ordering_shipp = &Ships[aip->shipnum];
1303 if ( command == IGNORE_TARGET_ITEM ) {
1305 // if we were messaging a ship directly, set flag to send no messages. We will send one
1306 // specifically from the ship player is ordering
1307 if ( (Msg_instance != MESSAGE_ALL_FIGHTERS) && (Squad_msg_mode == SM_MODE_SHIP_COMMAND) ) {
1313 for ( i = 0; i < num_wings; i++ ) {
1316 if ( (Wings[i].flags & WF_WING_GONE) || (Wings[i].current_count == 0) )
1319 if ( Wings[i].flags & WF_WING_DEPARTING )
1322 // get the first ship on the wing list and look at it's team and then it's type
1323 shipnum = Wings[i].ship_index[0];
1324 SDL_assert( shipnum != -1 );
1325 shipp = &Ships[shipnum];
1327 // can't message if not on players team
1328 if ( shipp->team != ordering_shipp->team )
1331 // can't message if ship not fighter/bomber if the command isn't to everyone.
1332 if ( !to_everyone && !(Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) )
1335 // don't send the command if the "wing" won't accept the command. We do this by looking at
1336 // the set of orders accepted for the first ship in the wing.
1337 if ( !(command & shipp->orders_accepted) )
1340 // send the command to the wing
1341 if ( Wings[i].current_count > 1 ) {
1342 if ( hud_squadmsg_send_wing_command(i, command, send_message, player_num) ) {
1348 // now find any friendly fighter/bomber ships not in wings
1349 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
1350 if ( objp->type != OBJ_SHIP )
1353 // don't send messge to ships not on player's team, or that are in a wing.
1354 shipp = &Ships[objp->instance];
1355 if ( (shipp->team != ordering_shipp->team) || (shipp->wingnum != -1) )
1358 // don't send message to non fighter wings
1359 if ( !to_everyone && !(Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) )
1362 // skip departing/dying ships
1363 if ( shipp->flags & (SF_DEPARTING|SF_DYING) )
1366 // don't send command if ship won't accept if
1367 if ( !(command & shipp->orders_accepted) )
1370 if ( hud_squadmsg_send_ship_command(objp->instance, command, send_message, player_num) ) {
1375 // we might send the ship command again if we are ignoring a target, and the guy
1376 // we ordered directly is a ship -- we want the response to come directly from the
1379 SDL_assert( Msg_instance != MESSAGE_ALL_FIGHTERS );
1380 hud_squadmsg_send_ship_command( Msg_instance, command, 1 );
1384 // Check if any enemy ships are in the mission
1385 int hud_squadmsg_enemies_present()
1390 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1391 shipp = &Ships[Objects[so->objnum].instance];
1392 if ( shipp->team != Player_ship->team )
1399 #define OVERRIDE_PROTECT_SHIP_TYPE (SIF_FIGHTER|SIF_BOMBER|SIF_FREIGHTER|SIF_TRANSPORT)
1400 // function which sends a message to a specific ship. This routine can be called from one of two
1401 // places. Either after selecting a ship when using a hotkey, or after selecting a command when
1402 // using the entire messaging menu system
1404 // if local and addr are non-null, it means the function is being called by the (multiplayer) server in response to
1405 // a PLAYER_COMMAND_PACKET
1406 int hud_squadmsg_send_ship_command( int shipnum, int command, int send_message, int player_num )
1409 int ai_mode, ai_submode; // ai mode and submode needed for ship commands
1410 char *target_shipname; // ship number of possible targets
1412 int target_team, ship_team; // team id's for the ship getting message and any target the player has
1413 ship *ordering_shipp;
1415 // quick short circuit here because of actually showing comm menu even though you cannot message.
1416 // just a safety net.
1417 if ( (Game_mode & GM_MULTIPLAYER) && (player_num != -1) ) {
1418 if ( !multi_can_message(&Net_players[player_num]) ) {
1423 // check for multiplayer mode
1424 if((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1425 send_player_order_packet(SQUAD_MSG_SHIP, shipnum, command);
1429 ai_mode = AI_GOAL_NONE; // needs to be initialized
1430 ai_submode = -1234567;
1433 if ( player_num != -1 ){
1434 ainfo = &Ai_info[Ships[Objects[Net_players[player_num].player->objnum].instance].ai_index];
1437 SDL_assert( ainfo->shipnum != -1 );
1438 ordering_shipp = &Ships[ainfo->shipnum];
1440 // a shortcut to save on repetitive coding. If the order is a 'target' order, make the default
1441 // mesage be "no target"
1442 message = MESSAGE_NOSIR;
1443 if ( (command & TARGET_MESSAGES) && (ainfo->target_objnum == -1) )
1444 message = MESSAGE_NO_TARGET;
1446 if ( hud_squadmsg_is_target_order_valid(command, 1, ainfo) ) {
1448 target_shipname = NULL;
1450 if ( ainfo->target_objnum != -1) {
1451 if ( Objects[ainfo->target_objnum].type == OBJ_SHIP ) {
1452 if ( ainfo->target_objnum != Ships[shipnum].objnum ) {
1453 target_shipname = Ships[Objects[ainfo->target_objnum].instance].ship_name; // I think this is right
1454 target_team = Ships[Objects[ainfo->target_objnum].instance].team;
1459 SDL_assert ( ainfo->shipnum != -1 );
1460 ship_team = Ships[ainfo->shipnum].team; // team of the ship issuing the message
1462 switch ( command ) { // value of k matches the #defines for ship messages
1463 case ATTACK_TARGET_ITEM:
1464 if ( Objects[ainfo->target_objnum].type == OBJ_SHIP ) {
1465 SDL_assert( target_shipname );
1466 SDL_assert( ship_team != target_team );
1468 // Orders to override protect
1469 if (Ship_info[Ships[Objects[ainfo->target_objnum].instance].ship_info_index].flags & OVERRIDE_PROTECT_SHIP_TYPE) {
1470 Objects[ainfo->target_objnum].flags &= ~OF_PROTECTED;
1473 ai_mode = AI_GOAL_CHASE;
1474 ai_submode = SM_ATTACK;
1475 } else if ( Objects[ainfo->target_objnum].type == OBJ_WEAPON ) {
1476 ai_mode = AI_GOAL_CHASE_WEAPON;
1477 ai_submode = Objects[ainfo->target_objnum].instance; // store the instance of the weapon -- ai goals code will deal with it
1480 message = MESSAGE_ATTACK_TARGET;
1483 case DISABLE_TARGET_ITEM:
1484 SDL_assert( target_shipname );
1485 SDL_assert( ship_team != target_team );
1487 // Orders to override protect
1488 if (Ship_info[Ships[Objects[ainfo->target_objnum].instance].ship_info_index].flags & OVERRIDE_PROTECT_SHIP_TYPE) {
1489 Objects[ainfo->target_objnum].flags &= ~OF_PROTECTED;
1492 ai_mode = AI_GOAL_DISABLE_SHIP;
1493 ai_submode = -SUBSYSTEM_ENGINE;
1494 message = MESSAGE_DISABLE_TARGET;
1497 case DISARM_TARGET_ITEM:
1498 SDL_assert( target_shipname );
1499 SDL_assert( ship_team != target_team );
1501 // Orders to override protect
1502 if (Ship_info[Ships[Objects[ainfo->target_objnum].instance].ship_info_index].flags & OVERRIDE_PROTECT_SHIP_TYPE) {
1503 Objects[ainfo->target_objnum].flags &= ~OF_PROTECTED;
1506 ai_mode = AI_GOAL_DISARM_SHIP;
1507 ai_submode = -SUBSYSTEM_TURRET;
1508 message = MESSAGE_DISARM_TARGET;
1511 case DISABLE_SUBSYSTEM_ITEM:
1512 SDL_assert( target_shipname );
1513 SDL_assert( ship_team != target_team );
1514 SDL_assert( ainfo->targeted_subsys != NULL );
1515 SDL_assert( ainfo->targeted_subsys->current_hits > 0.0f);
1517 // Orders to override protect
1518 if (Ship_info[Ships[Objects[ainfo->target_objnum].instance].ship_info_index].flags & OVERRIDE_PROTECT_SHIP_TYPE) {
1519 Objects[ainfo->target_objnum].flags &= ~OF_PROTECTED;
1522 ai_mode = AI_GOAL_DESTROY_SUBSYSTEM;
1523 ai_submode = ship_get_subsys_index( &Ships[Objects[ainfo->target_objnum].instance], ainfo->targeted_subsys->system_info->subobj_name );
1524 message = MESSAGE_ATTACK_TARGET;
1527 case CAPTURE_TARGET_ITEM:
1528 SDL_assert( target_shipname );
1529 SDL_assert( ship_team != target_team );
1531 SDL_assert(ainfo->target_objnum > -1);
1533 Objects[ainfo->target_objnum].flags |= OF_PROTECTED;
1535 ai_mode = AI_GOAL_DOCK;
1536 ai_submode = AIS_DOCK_0;
1537 message = MESSAGE_DOCK_YES;
1540 case PROTECT_TARGET_ITEM:
1542 // AL 31-3-98: Can't protect self... this can happen if all fighters
1543 // are told to protect another friendly ship
1544 if ( ainfo->target_objnum == Ships[shipnum].objnum ) {
1548 SDL_assert( target_shipname );
1549 SDL_assert( ship_team == target_team );
1551 ai_mode = AI_GOAL_GUARD;
1552 ai_submode = AIS_GUARD_PATROL;
1553 message = MESSAGE_YESSIR;
1556 case IGNORE_TARGET_ITEM:
1557 SDL_assert( target_shipname );
1558 SDL_assert( ship_team != target_team );
1560 ai_mode = AI_GOAL_IGNORE;
1562 message = MESSAGE_YESSIR;
1565 case FORMATION_ITEM:
1566 message = MESSAGE_YESSIR;
1567 target_shipname = ordering_shipp->ship_name;
1568 ai_mode = AI_GOAL_FORM_ON_WING;
1573 ai_mode = AI_GOAL_GUARD;
1574 ai_submode = AIS_GUARD_PATROL;
1575 target_shipname = ordering_shipp->ship_name;
1576 message = MESSAGE_YESSIR;
1579 case ENGAGE_ENEMY_ITEM:
1580 ai_mode = AI_GOAL_CHASE_ANY;
1581 ai_submode = SM_ATTACK;
1582 // if no enemies present, use the affirmative, instead of engaging enemies message
1583 if ( hud_squadmsg_enemies_present() )
1584 message = MESSAGE_YESSIR;
1586 message = MESSAGE_ENGAGE;
1587 target_shipname = NULL;
1591 ai_mode = AI_GOAL_WARP;
1593 message = MESSAGE_WARP_OUT;
1596 // the following are support ship options!!!
1597 case REARM_REPAIR_ME_ITEM:
1598 if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (player_num != -1) ){
1599 hud_squadmsg_repair_rearm(0,&Objects[Net_players[player_num].player->objnum]);
1601 hud_squadmsg_repair_rearm(0); // note we return right away. repair/rearm code handles messaging, etc
1605 case ABORT_REARM_REPAIR_ITEM:
1606 if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (player_num != -1) ){
1607 hud_squadmsg_repair_rearm_abort(0,&Objects[Net_players[player_num].player->objnum]);
1609 hud_squadmsg_repair_rearm_abort(0); // note we return right away. repair/rearm code handles messaging, etc
1613 case STAY_NEAR_ME_ITEM:
1614 case STAY_NEAR_TARGET_ITEM: // can't attack anything on same team
1616 // cannot stay near a hostile ship(?)
1617 if ( (command == STAY_NEAR_TARGET_ITEM) && (ship_team != target_team) )
1620 ai_mode = AI_GOAL_STAY_NEAR_SHIP;
1622 message = MESSAGE_YESSIR;
1623 if ( command == STAY_NEAR_ME_ITEM )
1624 target_shipname = ordering_shipp->ship_name;
1627 case KEEP_SAFE_DIST_ITEM:
1628 ai_mode = AI_GOAL_KEEP_SAFE_DISTANCE;
1630 message = MESSAGE_YESSIR;
1634 Int3(); // get Allender -- illegal message
1639 // handle case of messaging one ship. Deal with messaging all fighters next.
1640 if ( ai_mode != AI_GOAL_NONE ) {
1641 SDL_assert(ai_submode != -1234567);
1642 ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, ai_mode, ai_submode, target_shipname, &Ai_info[Ships[shipnum].ai_index] );
1643 if( player_num == -1 )
1644 hud_add_issued_order(Ships[shipnum].ship_name, command, target_shipname);
1648 // if we're in multiplayer mode, and we're the server, determine if this virtual squadmate order should be
1649 // sent to other players in the game as an actual "order"
1650 if((Game_mode & GM_MULTIPLAYER) && (message != MESSAGE_NOSIR)){
1651 // if the multi_msg system processed and sent this order to a player, we should not play a response
1652 if(multi_msg_eval_ship_squadmsg(shipnum,command,ainfo,player_num)){
1657 // this is the _response_
1658 if ( send_message ){
1659 message_send_builtin_to_player( message, &Ships[shipnum], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_ANYTIME, 0, 0, player_num, -1 );
1662 return send_message;
1665 // function to send a command to a wing. Like above function, called from one of two places
1667 // if local and addr are non-null, it means the function is being called by the (multiplayer) server in response to
1668 // a PLAYER_COMMAND_PACKET
1670 // returns whether or not a message was sent
1671 int hud_squadmsg_send_wing_command( int wingnum, int command, int send_message, int player_num )
1674 int ai_mode, ai_submode; // ai mode and submode needed for ship commands
1675 char *target_shipname; // ship number of possible targets
1676 int message_sent, message;
1677 int target_team = -1, wing_team = -1; // team for the wing and the player's target
1678 ship *ordering_shipp;
1680 // quick short circuit here because of actually showing comm menu even though you cannot message.
1681 // just a safety net.
1682 if ( (Game_mode & GM_MULTIPLAYER) && (player_num != -1) ) {
1683 if ( !multi_can_message(&Net_players[player_num]) ) {
1688 // check for multiplayer mode
1689 if((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1690 send_player_order_packet(SQUAD_MSG_WING, wingnum,command);
1694 ai_mode = AI_GOAL_NONE; // needs to be initialized
1695 ai_submode = -1234567;
1698 if ( player_num != -1 )
1699 ainfo = &Ai_info[Ships[Objects[Net_players[player_num].player->objnum].instance].ai_index];
1701 SDL_assert( ainfo->shipnum != -1 );
1702 ordering_shipp = &Ships[ainfo->shipnum];
1704 // get the shipnum of the ship the player has targeted. Used in enough places to do this just
1705 // once. If the ship targeted is part of the wing that was messages -- bail out!!!
1707 // a shortcut to save on repetative coding
1708 message = MESSAGE_NOSIR;
1709 if ( (command & TARGET_MESSAGES) && (ainfo->target_objnum == -1) )
1710 message = MESSAGE_NO_TARGET;
1712 if ( hud_squadmsg_is_target_order_valid(command, 1, ainfo) ) {
1714 target_shipname = NULL;
1715 if ( ainfo->target_objnum != -1) {
1716 if ( Objects[ainfo->target_objnum].type == OBJ_SHIP ) {
1717 target_shipname = Ships[Objects[ainfo->target_objnum].instance].ship_name; // I think this is right
1719 target_team = Ships[Objects[ainfo->target_objnum].instance].team;
1724 SDL_assert ( ainfo->shipnum != -1 );
1725 SDL_assert ( (wingnum >= 0) && (wingnum < num_wings) );
1727 // get the team for the wing
1728 SDL_assert ( Wings[wingnum].ship_index[0] != -1 );
1730 wing_team = Ships[Wings[wingnum].ship_index[0]].team;
1733 switch ( command ) { // value of k matches the #defines for ship messages
1734 case ATTACK_TARGET_ITEM:
1735 if ( Objects[ainfo->target_objnum].type == OBJ_SHIP ) {
1736 SDL_assert( target_shipname );
1737 SDL_assert( wing_team != target_team );
1738 if ( (Ships[Objects[ainfo->target_objnum].instance].wingnum != -1) && (Ships[Objects[ainfo->target_objnum].instance].wingnum == wingnum) ) {
1739 message = MESSAGE_NOSIR;
1740 ai_mode = AI_GOAL_NONE;
1742 ai_mode = AI_GOAL_CHASE;
1743 ai_submode = SM_ATTACK;
1744 message = MESSAGE_ATTACK_TARGET;
1746 } else if ( Objects[ainfo->target_objnum].type == OBJ_WEAPON ) {
1747 ai_mode = AI_GOAL_CHASE_WEAPON;
1748 ai_submode = Objects[ainfo->target_objnum].instance; // store the instance of the weapon -- ai goals code will deal with it
1749 message = MESSAGE_ATTACK_TARGET;
1755 case DISABLE_TARGET_ITEM:
1756 SDL_assert( target_shipname );
1757 SDL_assert( wing_team != target_team );
1759 ai_mode = AI_GOAL_DISABLE_SHIP;
1760 ai_submode = -SUBSYSTEM_ENGINE;
1761 message = MESSAGE_DISABLE_TARGET;
1764 case DISARM_TARGET_ITEM:
1765 SDL_assert( target_shipname );
1766 SDL_assert( wing_team != target_team );
1768 ai_mode = AI_GOAL_DISARM_SHIP;
1769 ai_submode = -SUBSYSTEM_TURRET;
1770 message = MESSAGE_DISARM_TARGET;
1773 case DISABLE_SUBSYSTEM_ITEM:
1774 SDL_assert( target_shipname );
1775 SDL_assert( wing_team != target_team );
1776 SDL_assert( ainfo->targeted_subsys != NULL );
1777 SDL_assert( ainfo->targeted_subsys->current_hits > 0.0f);
1779 ai_mode = AI_GOAL_DESTROY_SUBSYSTEM;
1780 ai_submode = ship_get_subsys_index( &Ships[Objects[ainfo->target_objnum].instance], ainfo->targeted_subsys->system_info->subobj_name );
1781 message = MESSAGE_ATTACK_TARGET;
1785 case PROTECT_TARGET_ITEM:
1786 SDL_assert( target_shipname );
1787 SDL_assert( wing_team == target_team );
1789 ai_mode = AI_GOAL_GUARD;
1790 ai_submode = AIS_GUARD_PATROL;
1791 message = MESSAGE_YESSIR;
1794 case IGNORE_TARGET_ITEM:
1795 SDL_assert( target_shipname );
1796 SDL_assert( wing_team != target_team );
1798 ai_mode = AI_GOAL_IGNORE;
1799 ai_submode = 0; // actually, a don't care.
1800 message = MESSAGE_YESSIR;
1803 case FORMATION_ITEM:
1804 message = MESSAGE_YESSIR;
1805 target_shipname = ordering_shipp->ship_name;
1806 ai_mode = AI_GOAL_FORM_ON_WING;
1811 ai_mode = AI_GOAL_GUARD;
1812 ai_submode = AIS_GUARD_PATROL;
1813 target_shipname = ordering_shipp->ship_name;
1814 message = MESSAGE_YESSIR;
1817 case ENGAGE_ENEMY_ITEM:
1818 ai_mode = AI_GOAL_CHASE_ANY;
1819 ai_submode = SM_ATTACK;
1820 if ( hud_squadmsg_enemies_present() )
1821 message = MESSAGE_YESSIR;
1823 message = MESSAGE_ENGAGE;
1824 target_shipname = NULL;
1828 ai_mode = AI_GOAL_WARP;
1830 message = MESSAGE_WARP_OUT;
1831 Wings[wingnum].flags |= WF_DEPARTURE_ORDERED;
1834 case REARM_REPAIR_ME_ITEM:
1835 case ABORT_REARM_REPAIR_ITEM:
1836 case STAY_NEAR_ME_ITEM:
1837 case STAY_NEAR_TARGET_ITEM:
1838 case KEEP_SAFE_DIST_ITEM:
1842 Int3(); // get Allender -- illegal message
1847 if ( ai_mode != AI_GOAL_NONE ) {
1848 SDL_assert(ai_submode != -1234567);
1849 ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, ai_mode, ai_submode, target_shipname, wingnum );
1853 // if we're in multiplayer mode, and we're the server, determine if this virtual squadmate order should be
1854 // sent to other players in the game as an actual "order"
1855 if((Game_mode & GM_MULTIPLAYER) && (message != MESSAGE_NOSIR)){
1856 // if there's at least one ai ship which got the command, let the response come through
1857 if(multi_msg_eval_wing_squadmsg(wingnum,command,ainfo,player_num)){
1862 // this is the _response_
1864 if ( send_message ) {
1867 // get a random ship in the wing to send the message to the player
1868 ship_num = ship_get_random_ship_in_wing( wingnum, SHIP_GET_NO_PLAYERS );
1870 // in multiplayer, its possible that all ships in a wing are players. so we'll just send from a random ship
1872 ship_num = ship_get_random_ship_in_wing(wingnum);
1875 // only send message if ship is found. There appear to be cases where all ships
1876 // in a wing die in the same frame causing the wing to appear valid in the message
1877 // menu, but the get_random_ship* functions won't return dying ships.
1878 if ( ship_num != -1 ) {
1879 message_send_builtin_to_player( message, &Ships[ship_num], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_ANYTIME, 0, 0, player_num, -1 );
1884 return message_sent;
1888 // return number of available reinforcements, 0 if none available
1889 int hud_squadmsg_reinforcements_available(int team)
1893 for (i = 0; i < Num_reinforcements; i++) {
1897 if ( Reinforcements[i].num_uses >= Reinforcements[i].uses ){
1902 if ( team != ship_get_reinforcement_team(i) ){
1906 // check the arrival cue sexpression of the ship/wing of this reinforcement. If known
1907 // false, it doesn't count either
1908 if ( (wingnum = wing_name_lookup(Reinforcements[i].name, 1)) != -1 ) {
1909 SDL_assert ( Wings[wingnum].arrival_cue >= 0 );
1910 if ( Sexp_nodes[Wings[wingnum].arrival_cue].value == SEXP_KNOWN_FALSE ){
1916 p_objp = mission_parse_get_arrival_ship( Reinforcements[i].name );
1917 if ( p_objp != NULL ) {
1918 if ( Sexp_nodes[p_objp->arrival_cue].value == SEXP_KNOWN_FALSE ){
1922 Int3(); // allender says bogus! reinforcement should be here since it wasn't a wing!
1932 // function to put up window in upper right to allow for player to select the type
1933 // of entity to select for a message (i.e. a wing or a ship)
1934 void hud_squadmsg_type_select( )
1938 First_menu_item = 0;
1941 for (i=0; i<NUM_TYPE_SELECT; i++ ) {
1942 SDL_strlcpy( MsgItems[i].text, type_select_str(i), SDL_arraysize(MsgItems[0].text) );
1943 MsgItems[i].active = 1; // assume active
1945 Num_menu_items = NUM_TYPE_SELECT;
1948 // check to see if the players team is TEAM_TRAITOR. If so, then he is a "traitor", and will not
1949 // be able to do anything from this menu
1950 if ( Player_ship->team == TEAM_TRAITOR ) {
1951 for (i = 0; i < MAX_MENU_ITEMS; i++ )
1952 MsgItems[i].active = 0;
1956 // based on ship counts, wing counts, shortcut active, grey out possible menu choices
1957 if ( !hud_squadmsg_count_ships(0) )
1958 MsgItems[TYPE_SHIP_ITEM].active = 0;
1960 if ( !hud_squadmsg_count_wings(0) )
1961 MsgItems[TYPE_WING_ITEM].active = 0;
1963 // check to be sure that we have some fighters/bombers on the players team that we
1965 if ( !hud_squadmsg_count_fighters() ){
1966 MsgItems[TYPE_ALL_FIGHTERS_ITEM].active = 0;
1969 if ((Player_ship != NULL) && !hud_squadmsg_reinforcements_available(Player_ship->team)) {
1970 MsgItems[TYPE_REINFORCEMENT_ITEM].active = 0;
1973 MsgItems[TYPE_REPAIR_REARM_ITEM].active = 1; // this item will always be available (I think)
1974 MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 0;
1977 // If the player ship communications are severely damaged, then the player
1978 // will only be able to call for repair/rearm ships
1980 // also, only allow support ship if this player is not allowed to messaage.
1981 if ( (hud_communications_state(Player_ship) != COMM_OK) || ((Game_mode & GM_MULTIPLAYER) && !multi_can_message(Net_player)) ) {
1982 for ( i = 0; i < MAX_MENU_ITEMS; i++ ){
1983 MsgItems[i].active = 0;
1986 MsgItems[TYPE_REPAIR_REARM_ITEM].active = 1;
1989 // check to see if the player is awaiting repair or being repaired. Active the abort and inactive the repair items
1990 // check to see if the player is scheduled to be repaired by incoming ship
1991 if ( Ai_info[Ships[Player_obj->instance].ai_index].ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
1992 MsgItems[TYPE_REPAIR_REARM_ITEM].active = 0;
1993 MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 1;
1994 } else if ( mission_is_repair_scheduled(Player_obj) ) {
1995 MsgItems[TYPE_REPAIR_REARM_ITEM].active = 0;
1996 MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 1;
1999 // if no support available, can't call one in
2000 if ( !is_support_allowed(Player_obj) ) {
2001 MsgItems[TYPE_REPAIR_REARM_ITEM].active = 0;
2002 MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 0;
2005 // de-activate the rearm/repair item if the player has a full load of missiles and
2006 // all subsystems at full strength. We will only check if this item hasn't been marked
2007 // inactive because of some other reason
2008 if ( MsgItems[TYPE_REPAIR_REARM_ITEM].active ) {
2010 if ( !hud_squadmsg_can_rearm(Player_ship) ){
2011 MsgItems[TYPE_REPAIR_REARM_ITEM].active = 0;
2015 // if using keyboard shortcut, these items are always inactive
2016 if ( Msg_shortcut_command != -1 ) {
2017 MsgItems[TYPE_REPAIR_REARM_ITEM].active = 0;
2018 MsgItems[TYPE_REINFORCEMENT_ITEM].active = 0;
2019 MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 0;
2023 hud_squadmsg_display_menu( XSTR( "Message What", 316) );
2024 k = hud_squadmsg_get_key();
2025 if ( k != -1 ) { // when k != -1, we have a key that associates with menu item
2026 SDL_assert ( k < Num_menu_items );
2027 if ( k == TYPE_SHIP_ITEM ){
2028 hud_squadmsg_do_mode( SM_MODE_SHIP_SELECT );
2029 } else if ( k == TYPE_WING_ITEM ) {
2030 hud_squadmsg_do_mode( SM_MODE_WING_SELECT );
2031 } else if ( k == TYPE_ALL_FIGHTERS_ITEM ) {
2032 hud_squadmsg_do_mode( SM_MODE_ALL_FIGHTERS );
2035 if ( Msg_shortcut_command == -1 ) {
2036 if ( k == TYPE_REINFORCEMENT_ITEM ) {
2037 hud_squadmsg_do_mode( SM_MODE_REINFORCEMENTS );
2038 player_set_next_all_alone_msg_timestamp();
2039 } else if ( k == TYPE_REPAIR_REARM_ITEM ){
2040 hud_squadmsg_do_mode( SM_MODE_REPAIR_REARM );
2041 } else if ( k == TYPE_REPAIR_REARM_ABORT_ITEM ) {
2042 hud_squadmsg_do_mode( SM_MODE_REPAIR_REARM_ABORT );
2048 // function to display a list of ships to send a command to
2049 void hud_squadmsg_ship_select()
2053 if ( Num_menu_items == -1 ) {
2055 hud_squadmsg_count_ships( 1 );
2058 hud_squadmsg_display_menu( XSTR( "Select Ship", 317) );
2059 k = hud_squadmsg_get_key();
2060 if ( k != -1 ) { // if true, we have selected a ship.
2061 if ( Msg_shortcut_command == -1 ) {
2062 Msg_instance = MsgItems[First_menu_item + k].instance; // store the instance id in a global
2063 hud_squadmsg_do_mode( SM_MODE_SHIP_COMMAND ); // and move to a new mode
2065 // we must convert the Msg_shortcut_command value to a value that the message
2066 // system normally uses to select a command. Since the menu
2067 SDL_assert( Msg_shortcut_command != IGNORE_TARGET_ITEM );
2068 hud_squadmsg_send_ship_command( MsgItems[First_menu_item+k].instance, Msg_shortcut_command, 1 );
2069 hud_squadmsg_toggle();
2075 // function to display a list of ships to send a command to
2076 void hud_squadmsg_wing_select()
2080 if ( Num_menu_items == -1 ) {
2082 hud_squadmsg_count_wings( 1 );
2085 hud_squadmsg_display_menu( XSTR( "Select Wing", 318) );
2086 k = hud_squadmsg_get_key();
2087 if ( k != -1 ) { // if true, we have selected a ship.
2088 if ( Msg_shortcut_command == -1 ) { // do normal menu stuff when no hoykey active
2089 Msg_instance = MsgItems[First_menu_item + k].instance; // store the instance id in a global
2090 hud_squadmsg_do_mode( SM_MODE_WING_COMMAND ); // and move to a new mode
2092 SDL_assert( Msg_shortcut_command != IGNORE_TARGET_ITEM );
2093 hud_squadmsg_send_wing_command( MsgItems[First_menu_item+k].instance, Msg_shortcut_command, 1 );
2094 hud_squadmsg_toggle();
2100 // code which gives an order to all fighters/bombers. If there is a message shortcut active, then
2101 // make that order apply to all fighters/bombers. Otherwise, move to the ship_command menu
2102 void hud_squadmsg_msg_all_fighters()
2104 if ( Msg_shortcut_command == -1 ) {
2105 Msg_instance = MESSAGE_ALL_FIGHTERS;
2106 hud_squadmsg_do_mode( SM_MODE_SHIP_COMMAND );
2108 hud_squadmsg_send_to_all_fighters( Msg_shortcut_command );
2109 hud_squadmsg_toggle();
2113 // called to actually bring in a reinforcement. For single player games, always gets called.
2114 // for multiplayer games, always called on the server side. Clients should never get here
2115 void hud_squadmsg_call_reinforcement(int reinforcement_num, int player_num)
2121 rp = &Reinforcements[reinforcement_num];
2123 // safety net mainly for multiplayer servers in case some odd data desync occurs between
2124 // server and clients
2125 if ( MULTIPLAYER_MASTER && (rp->num_uses == rp->uses) ) {
2129 // check to see if the reinforcement called was a wing.
2130 for (i = 0; i < num_wings; i++ ) {
2131 if ( !SDL_strcasecmp(rp->name, Wings[i].name) ) {
2132 // found a wingname. Call the parse function to create all the ships in this wing
2133 // we must set the arrival cue of the wing to true, otherwise, this won't work!!
2134 Wings[i].flags &= ~WF_REINFORCEMENT;
2135 Wings[i].flags |= WF_RESET_REINFORCEMENT;
2137 // set up the arrival delay. If it is 0, then make is some random number of seconds
2138 delay = rp->arrival_delay;
2140 delay = (int)(frand() * 3.0) + 3;
2141 Wings[i].arrival_delay = timestamp(delay * 1000);
2146 // if we found no wing name that matched the reinforcement name, then look for a ship
2148 if ( i == num_wings ) {
2149 p_objp = mission_parse_get_arrival_ship( rp->name );
2151 // by resetting the reinforcement flag, we will allow code which normally handles arrivals
2152 // to make this reinforcement arrive. Doing so keeps the data structures clean.
2153 p_objp->flags &= ~P_SF_REINFORCEMENT;
2155 // set up the arrival delay
2156 delay = rp->arrival_delay;
2158 delay = (int)(frand() * 3.0) + 3; // between 3 and 6 seconds to arrive
2159 p_objp->arrival_delay = timestamp(delay * 1000);
2161 Int3(); // get allender -- I don't think that this can happen!!!!
2166 // increment the number of times this is used. Incremented here on single player and multiplayer
2167 // server side only. Clients keep track of own count when they actually call something in.
2170 // commented out on 9/9/98 because these messages simply are not used
2172 // now play a message (if there is one to play) for this reinforcement arrival. The first for loop
2173 // determine how many messages there are to play, since the array is packet. Then, if >= 1 message
2174 // to play, play one
2175 for (i = 0; i < MAX_REINFORCEMENT_MESSAGES; i++ )
2176 if ( !strlen(rp->yes_messages[i]) )
2180 // message_send_to_player( rp->yes_messages[myrand() % i], rp->name, MESSAGE_PRIORITY_NORMAL, HUD_SOURCE_FRIENDLY );
2183 mission_log_add_entry(LOG_PLAYER_REINFORCEMENT, rp->name, NULL);
2186 // function to display a list of reinforcements available to the player
2187 void hud_squadmsg_reinforcement_select()
2192 if ( Num_menu_items == -1 ) {
2194 for (i = 0; i < Num_reinforcements; i++) {
2195 rp = &Reinforcements[i];
2197 // don't put reinforcements onto the list that have already been used up.
2198 if (rp->num_uses == rp->uses) {
2202 // don't put items which are not on my team
2203 if((Player_ship != NULL) && (ship_get_reinforcement_team(i) != Player_ship->team)){
2207 SDL_assert ( Num_menu_items < MAX_MENU_ITEMS );
2208 SDL_strlcpy( MsgItems[Num_menu_items].text, rp->name, SDL_arraysize(MsgItems[0].text) );
2209 MsgItems[Num_menu_items].instance = i;
2210 MsgItems[Num_menu_items].active = 0;
2212 if ( rp->flags & RF_IS_AVAILABLE ) {
2213 MsgItems[Num_menu_items].active = 1;
2220 // hud_squadmsg_display_menu( "Select Reinforcement" );
2221 hud_squadmsg_display_menu( XSTR( "Select Ship/Wing", 319) ); // AL 11-14-97: Reinforcement didn't fit, so using this for now
2222 k = hud_squadmsg_get_key();
2226 hud_squadmsg_toggle(); // take us out of message mode
2228 rnum = MsgItems[First_menu_item + k].instance;
2230 // check to see if trying to call a reinforcement not yet available. If so, maybe play message, but
2232 if ( MsgItems[First_menu_item + k].active == 0 ) {
2236 // in single player, or a multiplayer master, call in the reinforcement. Clients send a packet to the
2238 if ( MULTIPLAYER_CLIENT ) {
2239 Reinforcements[rnum].num_uses++; // increment this variable here since clients need to maintain a valid count
2240 send_player_order_packet(SQUAD_MSG_REINFORCEMENT, rnum, 0);
2242 hud_squadmsg_call_reinforcement(rnum);
2247 // function to display list of commands for a ship
2248 void hud_squadmsg_ship_command()
2251 int i, orders, default_orders;
2253 // when adding ship commands, we must look at the type of ship, and what messages that
2254 // ship allows. First, place all messages that are possible onto the menu, then
2256 // see if messaging all ships or just one. Messaging all ships will mean all default orders
2257 // show on comm menu.
2258 if ( Msg_instance != MESSAGE_ALL_FIGHTERS ) {
2259 orders = Ships[Msg_instance].orders_accepted;
2260 default_orders = ship_get_default_orders_accepted( &Ship_info[Ships[Msg_instance].ship_info_index] );
2263 default_orders = FIGHTER_MESSAGES;
2264 orders = default_orders;
2267 First_menu_item = 0;
2269 for ( i = 0; i < MAX_SHIP_ORDERS; i++ ) {
2270 // check to see if the comm order should even be added to the menu -- if so, then add it
2271 // the order will be activated if the bit is set for the ship.
2272 if ( default_orders & Comm_orders[i].value ) {
2273 SDL_assert ( Num_menu_items < MAX_MENU_ITEMS );
2274 SDL_strlcpy( MsgItems[Num_menu_items].text, comm_order_menu_text(i), SDL_arraysize(MsgItems[0].text) );
2275 MsgItems[Num_menu_items].instance = Comm_orders[i].value;
2276 MsgItems[Num_menu_items].active = 0;
2277 // check the bit to see if the command is active
2278 if ( orders & Comm_orders[i].value )
2279 MsgItems[Num_menu_items].active = 1;
2281 // if the order cannot be carried out by the ship, then item should be inactive
2282 if ( (Msg_instance != MESSAGE_ALL_FIGHTERS) && !hud_squadmsg_ship_order_valid( Msg_instance, Comm_orders[i].value ) )
2283 MsgItems[Num_menu_items].active = 0;
2285 // do some other checks to possibly gray out other items.
2286 // if no target, remove any items which are associated with the players target
2287 if ( !hud_squadmsg_is_target_order_valid(i, 0) )
2288 MsgItems[Num_menu_items].active = 0;
2290 // if messaging all fighters, see if we should gray out the order if no one will accept it,
2291 // or modify the text if only some of the ships will accept it
2292 if ( Msg_instance == MESSAGE_ALL_FIGHTERS ) {
2295 int partial_accept, all_accept; // value which tells us what to do with menu item
2297 all_accept = Comm_orders[i].value;
2299 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2301 // don't send messge to ships not on player's team, or that are in a wing.
2302 shipp = &Ships[Objects[so->objnum].instance];
2303 if ( shipp->team != Player_ship->team )
2306 // don't send message to non fighter wings
2307 if ( !(Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) )
2310 all_accept &= shipp->orders_accepted; // 'and'ing will either keep this bit set or zero it properly
2311 partial_accept |= (shipp->orders_accepted & Comm_orders[i].value); // 'or'ing will tell us if at least one accepts
2314 if ( !all_accept ) {
2315 // either modify the text if a partial accept, or grey it out if no one accepts
2316 if ( partial_accept ) {
2317 SDL_strlcat( MsgItems[Num_menu_items].text, XSTR( "(*)", 320), SDL_arraysize(MsgItems[0].text) );
2319 MsgItems[Num_menu_items].active = 0;
2328 hud_squadmsg_display_menu( XSTR( "What Command", 321) );
2329 k = hud_squadmsg_get_key();
2331 // when we get a valid goal, we must add the goal to the ai ship's goal list
2334 SDL_assert ( k < Num_menu_items );
2335 // when messaging all fighters or ignoring target, call the send_to_all_fighters routine
2336 if ( (Msg_instance != MESSAGE_ALL_FIGHTERS) && (MsgItems[k].instance != IGNORE_TARGET_ITEM) )
2337 hud_squadmsg_send_ship_command( Msg_instance, MsgItems[k].instance, 1 );
2339 hud_squadmsg_send_to_all_fighters( MsgItems[k].instance );
2340 hud_squadmsg_toggle();
2344 // function to display list of command for a wing
2345 void hud_squadmsg_wing_command()
2349 int default_orders, i, orders;
2351 // when adding commands for wings, we will look at all of the ships in the wing, and create
2352 // the order list from that set of ships. In the future, we may want to do something else....
2354 wingp = &Wings[Msg_instance];
2356 // or together all of the orders for all the ships in the wing
2358 for ( i = 0; i < wingp->current_count; i++ ) {
2359 orders = ship_get_default_orders_accepted( &Ship_info[Ships[wingp->ship_index[i]].ship_info_index] );
2360 default_orders |= orders;
2362 default_orders &= ~CAPTURE_TARGET_ITEM; // we cannot capture any target with a wing.
2365 orders = Ships[wingp->ship_index[0]].orders_accepted; // get the orders that the first ship in the wing will accept
2366 for ( i = 0; i < MAX_SHIP_ORDERS; i++ ) {
2367 // add the set of default orders to the comm menu. We will currently allow all messages
2368 // to be available in the wing.
2369 if ( default_orders & Comm_orders[i].value ) {
2370 SDL_assert ( Num_menu_items < MAX_MENU_ITEMS );
2371 SDL_strlcpy( MsgItems[Num_menu_items].text, comm_order_menu_text(i), SDL_arraysize(MsgItems[0].text) );
2372 MsgItems[Num_menu_items].instance = Comm_orders[i].value;
2373 MsgItems[Num_menu_items].active = 0;
2375 // possibly grey out the menu item depending on whether or not the "wing" will accept this order
2376 // the "wing" won't accept the order if the first ship in the wing doesn't accept it.
2377 if ( orders & Comm_orders[i].value )
2378 MsgItems[Num_menu_items].active = 1;
2380 // do some other checks to possibly gray out other items.
2381 // if no target, remove any items which are associated with the players target
2382 if ( !hud_squadmsg_is_target_order_valid(i, 0) )
2383 MsgItems[Num_menu_items].active = 0;
2389 hud_squadmsg_display_menu( XSTR( "What Command", 321) );
2390 k = hud_squadmsg_get_key();
2393 // ignore target gets sent to everyone.
2394 if ( MsgItems[k].instance != IGNORE_TARGET_ITEM )
2395 hud_squadmsg_send_wing_command( Msg_instance, MsgItems[k].instance, 1 );
2397 hud_squadmsg_send_to_all_fighters( MsgItems[k].instance );
2398 hud_squadmsg_toggle();
2404 //----------------------------------------------------------
2405 // external entry points below!!!!
2407 // when starting messaging mode, we must remove old bindings from the
2408 // keys that are used for messaging mode (which will get restored when
2409 // messaging mode is done).
2411 // this code below will get called only the key config changes (from ControlsConfig.cpp)
2412 // or if the bindings haven't been saved yet. This code doesn't remove the bindings
2413 // but just sets up the array so that the bindings can be removed when messaging
2416 // do_scroll indicates whether we should save the page up and page down keys
2417 void hud_squadmsg_save_keys( int do_scroll )
2424 for ( j=0; j<MAX_KEYS_USED; j++ ) {
2425 for ( i=0; Control_config[i].text[0]; i++ ) { // the text field in this structure is empty at the end of the config list
2426 if ( Control_config[i].key_id == keys_used[j] ) { // this is true if we have a match
2428 // if we are not saving scrolling keys and we are trying to match page up and page down
2430 if ( !do_scroll && ((keys_used[j] == KEY_PAGEDOWN) || (keys_used[j] == KEY_PAGEUP)) )
2433 SDL_assert( num_keys_saved < MAX_KEYS_USED );
2434 key_save[num_keys_saved].option_num = i;
2435 key_save[num_keys_saved].key_value = keys_used[j];
2437 break; // done with this key -- move to next.
2444 // function is called once per mission start. Initializes those values
2445 // which only need to be inited once per mission.
2446 void hud_init_squadmsg( void )
2450 if ( !Mbox_frames_loaded ) {
2451 for ( i = 0; i < NUM_MBOX_FRAMES; i++ ) {
2452 Mbox_gauge[i].first_frame = bm_load_animation(Mbox_fnames[gr_screen.res][i], &Mbox_gauge[i].num_frames);
2453 if ( Mbox_gauge[i].first_frame == -1 ) {
2454 Warning(LOCATION, "Could not load in ani: %s\n", Mbox_fnames[gr_screen.res][i]);
2458 Mbox_frames_loaded = 1;
2461 Msg_eat_key_timestamp = timestamp(0);
2464 // external entry point into code which changes the messaging mode based on the
2465 // previous player flag value. I thought it better to isolate all system changes
2467 void hud_squadmsg_toggle()
2469 // if the emp effect is active, always ignore this
2470 if(emp_active_local()){
2474 // if entering this mode, must setup messaging system. Don't start squadmessging if
2475 // the player is dead.
2476 if ( !(Player->flags & PLAYER_FLAGS_MSG_MODE) ) {
2477 if ( Game_mode & GM_DEAD ){
2480 if ( (Game_mode & GM_MULTIPLAYER) && NETPLAYER_IS_OBSERVER(Net_player) ){
2483 hud_squadmsg_start();
2488 Player->flags ^= PLAYER_FLAGS_MSG_MODE;
2492 // extern entry point to allow messaging of enemies
2493 void hud_enemymsg_toggle()
2495 hud_squadmsg_toggle();
2496 // if we just entered message mode, turn on var that says to message enemies
2497 if ( Player->flags & PLAYER_FLAGS_MSG_MODE )
2502 // external entry point into code when a keyboard shortcut is used for a command
2503 // we are passed in an ID for the command to set internal variables. This command
2504 // will be used in place of the last menu in the messaging code
2505 void hud_squadmsg_shortcut( int command )
2507 // check if the communications system is capable of sending a message
2508 if ( (hud_communications_state(Player_ship, 1) != COMM_OK) && (command != REARM_REPAIR_ME_ITEM) ) {
2512 // observers in multiplayer games cannot have this active either
2513 if ( (Game_mode & GM_MULTIPLAYER) && NETPLAYER_IS_OBSERVER(Net_player) )
2516 // in multiplayer and I cannot message, don't allow anything except calling in for rearm
2517 if ( (Game_mode & GM_MULTIPLAYER) && !multi_can_message(Net_player) && (command != REARM_REPAIR_ME_ITEM) )
2518 gamesnd_play_error_beep();
2520 // player ships which turns traitor cannot rearm
2521 if ( Player_ship->team == TEAM_TRAITOR )
2524 if ( Player->flags & PLAYER_FLAGS_MSG_MODE ) // we are already in messaging mode -- maybe do sometime more interesting?
2526 hud_squadmsg_toggle();
2527 Msg_shortcut_command = command; // save the command for later use
2528 if ( Msg_shortcut_command == CAPTURE_TARGET_ITEM ) // some commands only apply to wings or ships
2529 Squad_msg_mode = SM_MODE_SHIP_SELECT; // -- don't offer choice
2530 else if ( Msg_shortcut_command == IGNORE_TARGET_ITEM ) { // ignoring target applied to all ships
2531 hud_squadmsg_toggle(); // turns off mode which was turned on above
2532 hud_squadmsg_send_to_all_fighters( Msg_shortcut_command );
2536 // external entry point which is called when the player hits a selection key (1-0) while in messaging
2537 // mode. If we are in messaging mode, send the shortcut command to the ships that are part of the
2538 // passed in selection set. If there is no shortcut command, do nothing. Returns 1 if the key
2539 // was used, else 0. This return value is used to tell the key control system that it should
2540 // call the normal targeting selection stuff.
2541 int hud_squadmsg_hotkey_select( int k )
2543 htarget_list *hitem, *plist;
2547 SDL_assert ( Player->flags & PLAYER_FLAGS_MSG_MODE );
2549 if ( Msg_shortcut_command == -1 )
2552 SDL_assert ( (k >= 0) && (k < MAX_KEYED_TARGETS) );
2553 plist = &(Player->keyed_targets[k]);
2555 if ( EMPTY(plist) ) // be sure that we have at least one ship in the list
2559 // for each ship in the selection set list, send the shortcut command that the player
2560 // previously entered. Be sure to check that we are not trying to send a command to
2562 for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) {
2564 SDL_assert ( objp->type == OBJ_SHIP );
2565 if ( Ships[objp->instance].team != TEAM_FRIENDLY )
2568 // be sure that this ship can accept this command
2569 // DDOI - I changed this , to & which seeems to be in keeping
2570 // with similar code.
2571 if ( !(Msg_shortcut_command & Ships[objp->instance].orders_accepted) )
2574 hud_squadmsg_send_ship_command( objp->instance, Msg_shortcut_command, send_message );
2578 hud_squadmsg_toggle(); // turn off messaging mode
2583 // the next function is called once a frame when the player is messaging someone
2584 // in his squad. After a period of 5 seconds of inactivity (i.e. no keypress to
2585 // select something in the menu), the menu will disappear. This function will only
2586 // get called if the player flag PLAYER_FLAG_MSG_MODE is set. Parameter is the key
2587 // that was hit this frame
2589 int hud_squadmsg_do_frame( )
2593 SDL_assert ( Player->flags & PLAYER_FLAGS_MSG_MODE ); // be sure that messaging mode is set!!!
2595 // if the player is now dead, or the timestamp elapsed, then get out of messaging mode.
2596 if ( (Game_mode & GM_DEAD) || timestamp_elapsed(Msg_mode_timestamp) ) {
2597 hud_squadmsg_toggle();
2603 // check the player's current target. Change in target resets the timer
2605 if ( Msg_target_objnum != Player_ai->target_objnum ) {
2606 Msg_target_objnum = Player_ai->target_objnum;
2610 if ( Msg_targeted_subsys != Player_ai->targeted_subsys ) {
2611 Msg_targeted_subsys = Player_ai->targeted_subsys;
2615 // setup color/font info
2616 // hud_set_default_color();
2617 hud_set_gauge_color(HUD_MESSAGE_BOX);
2619 // check for multiplayer mode - this is really a special case checker for support ship requesting and aborting
2620 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)){
2621 // send the correct packet
2622 if(Squad_msg_mode == SM_MODE_REPAIR_REARM)
2623 send_player_order_packet(SQUAD_MSG_SHIP, 0, REARM_REPAIR_ME_ITEM);
2625 send_player_order_packet(SQUAD_MSG_SHIP, 0, ABORT_REARM_REPAIR_ITEM);
2627 // make sure to toggle the mode off
2628 hud_squadmsg_toggle();
2633 // draw top of frame
2634 if ( Mbox_gauge[0].first_frame >= 0 ) {
2635 GR_AABITMAP(Mbox_gauge[0].first_frame, Mbox_top_coords[gr_screen.res][0], Mbox_top_coords[gr_screen.res][1]);
2638 switch( Squad_msg_mode ) {
2640 case SM_MODE_TYPE_SELECT:
2641 hud_squadmsg_type_select();
2644 case SM_MODE_SHIP_SELECT:
2645 hud_squadmsg_ship_select();
2648 case SM_MODE_WING_SELECT:
2649 hud_squadmsg_wing_select();
2652 case SM_MODE_SHIP_COMMAND:
2653 hud_squadmsg_ship_command();
2656 case SM_MODE_WING_COMMAND:
2657 hud_squadmsg_wing_command();
2660 case SM_MODE_REINFORCEMENTS:
2661 hud_squadmsg_reinforcement_select();
2664 case SM_MODE_REPAIR_REARM:
2665 //if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (addr != NULL)){
2666 // hud_squadmsg_repair_rearm(1,&Objects[Net_players[player_num].player->objnum]);
2668 hud_squadmsg_repair_rearm(1); // note we return right away. repair/rearm code handles messaging, etc
2672 case SM_MODE_REPAIR_REARM_ABORT:
2673 //if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (addr != NULL)){
2674 // hud_squadmsg_repair_rearm_abort(1,&Objects[Net_players[player_num].player->objnum]);
2676 hud_squadmsg_repair_rearm_abort(1); // note we return right away. repair/rearm code handles messaging, etc
2680 case SM_MODE_ALL_FIGHTERS:
2681 hud_squadmsg_msg_all_fighters();
2685 Int3(); // get allender -- invalid mode in messaging system
2690 // be sure to reset the clip region
2691 HUD_reset_clip(); // JAS: Is this needed?
2693 if ( Msg_key_used || target_changed ) {
2694 Msg_mode_timestamp = timestamp(DEFAULT_MSG_TIMEOUT);
2700 void hud_add_issued_order(const char *name, int order, const char *target)
2702 Squadmsg_history[squadmsg_history_index].ship = get_parse_name_index(name);
2703 Squadmsg_history[squadmsg_history_index].order = order;
2705 Squadmsg_history[squadmsg_history_index].target = get_parse_name_index(target);
2707 Squadmsg_history[squadmsg_history_index].target = -1;
2709 squadmsg_history_index++;
2710 if (squadmsg_history_index >= SQUADMSG_HISTORY_MAX)
2711 squadmsg_history_index = 0;
2714 int hud_query_order_issued(const char *name, const char *order, const char *target)
2716 int i, o=-1, ship, t;
2718 ship = get_parse_name_index(name);
2721 t = get_parse_name_index(target);
2723 for (i=0; i<MAX_SHIP_ORDERS; i++)
2724 if (!SDL_strcasecmp(order, comm_order_menu_text(i)) )
2725 o = Comm_orders[i].value;
2727 SDL_assert(i < MAX_SHIP_ORDERS);
2728 for (i=0; i<SQUADMSG_HISTORY_MAX; i++)
2729 if (Squadmsg_history[i].order == o)
2730 if (ship == Squadmsg_history[i].ship)
2731 if (Squadmsg_history[i].target == t)
2738 void hudsquadmsg_page_in()
2742 for ( i = 0; i < NUM_MBOX_FRAMES; i++ ) {
2743 bm_page_in_aabitmap( Mbox_gauge[i].first_frame, Mbox_gauge[i].num_frames );