]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_pmsg.cpp
silence compiler warnings (mostly shadow variables)
[taylor/freespace2.git] / src / network / multi_pmsg.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/Network/multi_pmsg.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * $Log$
16  * Revision 1.6  2005/10/02 09:30:10  taylor
17  * sync up rest of big-endian network changes.  it should at least be as good as what's in FS2_Open now, only better :)
18  *
19  * Revision 1.5  2004/06/11 01:38:28  tigital
20  * byte-swapping changes for bigendian systems
21  *
22  * Revision 1.4  2002/06/09 04:41:23  relnev
23  * added copyright header
24  *
25  * Revision 1.3  2002/05/26 20:22:48  theoddone33
26  * Most of network/ works
27  *
28  * Revision 1.2  2002/05/07 03:16:47  theoddone33
29  * The Great Newline Fix
30  *
31  * Revision 1.1.1.1  2002/05/03 03:28:10  root
32  * Initial import.
33  * 
34  * 
35  * 7     3/10/99 6:50p Dave
36  * Changed the way we buffer packets for all clients. Optimized turret
37  * fired packets. Did some weapon firing optimizations.
38  * 
39  * 6     3/09/99 6:24p Dave
40  * More work on object update revamping. Identified several sources of
41  * unnecessary bandwidth.
42  * 
43  * 5     2/24/99 2:25p Dave
44  * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
45  * bug for dogfight more.
46  * 
47  * 4     11/19/98 8:03a Dave
48  * Full support for D3-style reliable sockets. Revamped packet lag/loss
49  * system, made it receiver side and at the lowest possible level.
50  * 
51  * 3     11/17/98 11:12a Dave
52  * Removed player identification by address. Now assign explicit id #'s.
53  * 
54  * 2     10/07/98 10:53a Dave
55  * Initial checkin.
56  * 
57  * 1     10/07/98 10:50a Dave
58  * 
59  * 18    6/13/98 9:32p Mike
60  * Kill last character in file which caused "Find in Files" to report the
61  * file as "not a text file."
62  * 
63  * 17    6/13/98 6:01p Hoffoss
64  * Externalized all new (or forgot to be added) strings to all the code.
65  * 
66  * 16    6/13/98 3:19p Hoffoss
67  * NOX()ed out a bunch of strings that shouldn't be translated.
68  * 
69  * 15    5/13/98 6:54p Dave
70  * More sophistication to PXO interface. Changed respawn checking so
71  * there's no window for desynchronization between the server and the
72  * clients.
73  * 
74  * 14    5/08/98 5:05p Dave
75  * Go to the join game screen when quitting multiplayer. Fixed mission
76  * text chat bugs. Put mission type symbols on the create game list.
77  * Started updating standalone gui controls.
78  * 
79  * 13    5/02/98 5:38p Dave
80  * Put in new tracker API code. Put in ship information on mp team select
81  * screen. Make standalone server name permanent. Fixed standalone server
82  * text messages.
83  * 
84  * 12    4/23/98 6:18p Dave
85  * Store ETS values between respawns. Put kick feature in the text
86  * messaging system. Fixed text messaging system so that it doesn't
87  * process or trigger ship controls. Other UI fixes.
88  * 
89  * 11    4/22/98 4:59p Allender
90  * new multiplayer dead popup.  big changes to the comm menu system for
91  * team vs. team.  Start of debriefing stuff for team vs. team  Make form
92  * on my wing work with individual ships who have high priority orders
93  * 
94  * 10    4/16/98 6:35p Dave
95  * Display more informative prompt when typing in a text message.
96  * 
97  * 9     4/06/98 10:24p Dave
98  * Fixed up Netgame.respawn for the standalone case.
99  * 
100  * 8     4/05/98 3:30p Dave
101  * Print netplayer messages in brighter green on the hud, with
102  * accompanying sound. Echo netplayer messages on sending machine. Fixed
103  * standalone sequencing bug where host never get the "enter mission"
104  * button.
105  * 
106  * 7     4/04/98 4:22p Dave
107  * First rev of UDP reliable sockets is done. Seems to work well if not
108  * overly burdened.
109  * 
110  * 6     4/03/98 1:03a Dave
111  * First pass at unreliable guaranteed delivery packets.
112  * 
113  * 5     4/02/98 5:50p Dave
114  * Put in support for standard comm messages to get sent to netplayers as
115  * well as ai ships. Make critical button presses not get evaluated on the
116  * observer.
117  * 
118  * 4     4/01/98 5:56p Dave
119  * Fixed a messaging bug which caused msg_all mode in multiplayer not to
120  * work. Compile out a host of multiplayer options not available in the
121  * demo.
122  * 
123  * 3     3/27/98 11:57a Dave
124  * Put in expression checking for text messages.
125  * 
126  * 2     3/25/98 2:16p Dave
127  * Select random default image for newly created pilots. Fixed several
128  * multi-pause messaging bugs. Begin work on online help for multiplayer
129  * keys.
130  * 
131  * 1     3/19/98 5:04p Dave
132  *  
133  * 
134  * $NoKeywords: $
135  */
136
137 #include <ctype.h>
138 #include "multi.h"
139 #include "multimsgs.h"
140 #include "multiutil.h"
141 #include "multi_pmsg.h"
142 #include "multi_kick.h"
143 #include "gamesnd.h"
144 #include "hud.h"
145 #include "hudmessage.h"
146 #include "hudsquadmsg.h"
147 #include "key.h"
148 #include "timer.h"
149 #include "ship.h"
150
151 // ----------------------------------------------------------------------------------
152 // MULTI MESSAGING DEFINES/VARS
153 //
154
155 // if a key is down less than this time, fire up the test messaging system, otherwise fire up the voice messaging system
156 #define MULTI_MSG_KEYDOWN_WAIT                          325                                                     // in ms
157
158 // sound to play before displaying incoming text messages in-mission
159 #define MULTI_MSG_TEXT_SOUND                                    SND_CUE_VOICE
160
161 // max length of a string we'll allow players to send
162 #define MULTI_MSG_MAX_LEN                                               75
163
164 // current message processing mode
165 int Multi_msg_mode = MULTI_MSG_NONE;
166
167 // timestamp for timing keydown
168 int Multi_msg_stamp = -1;
169
170 // flag indicating if there is _still_ a key down for the current message mode
171 int Multi_msg_repeat_flag = 0;
172
173 // timestamp set when we leave messaging mode, use to keep eating keys for a short period of time
174 int Multi_msg_eat_stamp = -1;
175
176 // text message input vars
177 int Multi_msg_text_enter = 0;
178 char Multi_msg_text[MULTI_MSG_MAX_TEXT_LEN+1];
179
180 // command defines - all these commands must be followed by a ":" so that we can easily tokenize and recognize
181 // it as a command instead of a word. they also must be immediately at the beginning of a text string
182 // SO : kick dave        would not work
183 //      kick: dave       would work
184 // Finally, if no command is found but there is a ":", it uses the text before the : as an expression to 
185 // lookup players to route the text to
186 #define MULTI_MSG_CMD_COUNT                                     1                                                               // # of commands
187 #define MULTI_MSG_CMD_KICK                                              0                                                               // kick command
188
189 //XSTR:OFF
190 const char *Multi_msg_commands[MULTI_MSG_CMD_COUNT] = {                                         // commands themselves
191         "kick"  
192 };
193 //XSTR:ON
194
195 // process an entered line of text and maybe perform a command on it (return 1 if an action was performed, 0 if not)
196 int multi_msg_check_command(char *str);
197
198 // perform the passed command (MULTI_MSG_CMD_* define) with the passed string argument
199 void multi_msg_perform_command(int command,char *param);
200
201
202 // ----------------------------------------------------------------------------------
203 // MULTI MESSAGING FUNCTIONS
204 //
205
206 // called when a messaging key has been detected as being pressed
207 void multi_msg_key_down(int mode)
208 {               
209         // keep eating keys for a short period of time
210         if((Multi_msg_eat_stamp != -1) && !timestamp_elapsed(Multi_msg_eat_stamp)){
211                 return;
212         }       
213
214         // if our player flags are marked as being in msg mode, don't do anything
215         if(Player->flags & PLAYER_FLAGS_MSG_MODE){
216                 return;
217         }       
218
219         // if there already is a keydown
220         if(Multi_msg_mode != MULTI_MSG_NONE){
221                 // if it is the same as the current mode, set the "still down" flag
222                 if((mode == Multi_msg_mode) && !Multi_msg_text_enter){                  
223                         Multi_msg_repeat_flag = 1;
224                 }
225
226                 // return here
227                 return;
228         }
229
230         // otherwise set the message mode and set the timestamp
231         Multi_msg_mode = mode;
232         Multi_msg_repeat_flag = 1;
233         Multi_msg_stamp = timestamp(MULTI_MSG_KEYDOWN_WAIT);
234 }
235
236 // returns true when messaging system has determined that we should be messaging with voice
237 int multi_msg_voice_record()
238 {
239         return ((Multi_msg_mode != MULTI_MSG_NONE) && timestamp_elapsed(Multi_msg_stamp) && Multi_msg_repeat_flag && !Multi_msg_text_enter) ? 1 : 0;
240 }
241
242 // general processing function to do things like timing keydown, etc. call from multi_do_frame()
243 void multi_msg_process()
244 {
245         // keep eating keys for a short period of time
246         if((Multi_msg_eat_stamp != -1) && timestamp_elapsed(Multi_msg_eat_stamp)){
247                 Multi_msg_eat_stamp = -1;
248                 return;
249         }       
250         
251         // if we don't currently have a valid mode set, don't do anything
252         if(Multi_msg_mode == MULTI_MSG_NONE){
253                 return;
254         }
255
256         // if the key has been released
257         if(!Multi_msg_repeat_flag && (Multi_msg_stamp != -1) && !Multi_msg_text_enter){
258                 // if the timestamp had not yet elapsed, fire up the text messaging system
259                 // this is the equivalent of a (TAP)
260                 if(!timestamp_elapsed(Multi_msg_stamp) && !Multi_msg_text_enter){
261                         // fire up text messaging system here
262                         Multi_msg_text_enter = 1;                       
263                         memset(Multi_msg_text,0,MULTI_MSG_MAX_TEXT_LEN+1);
264                 } else {
265                         Multi_msg_mode = MULTI_MSG_NONE;
266                         Multi_msg_stamp = -1;           
267                 }               
268         }
269         
270         // unset the repeat flag every frame
271         Multi_msg_repeat_flag = 0;
272 }
273
274 // get the current messaging mode
275 int multi_msg_mode()
276 {
277         return Multi_msg_mode;
278 }
279
280 // return 0 or 1 if in text chat mode or not
281 int multi_msg_text_mode()
282 {
283         return Multi_msg_text_enter;
284 }
285
286 // process a text string entered by the local player
287 void multi_msg_eval_text_msg()
288 {
289         int player_index;
290         
291         // if its a 0 length string, don't do anything
292         if(strlen(Multi_msg_text) <= 0){
293                 return;
294         }
295
296         // evaluate any special commands here
297         if(multi_msg_check_command(Multi_msg_text)){
298                 return;
299         }
300
301         // get the player if in MSG_TARGET mode
302         if(Multi_msg_mode == MULTI_MSG_TARGET){
303                 if(Player_ai->target_objnum != -1){                     
304                         player_index = multi_find_player_by_object(&Objects[Player_ai->target_objnum]);
305                         if(player_index != -1){
306                                 // send the chat packet
307                                 send_game_chat_packet(Net_player, Multi_msg_text, Multi_msg_mode, &Net_players[player_index]);
308
309                                 // echo the message locally
310                                 multi_msg_display_mission_text(Multi_msg_text, MY_NET_PLAYER_NUM);
311                         }
312                 }
313         }
314         // all other modes
315         else {
316                 // send the chat packet
317                 send_game_chat_packet(Net_player, Multi_msg_text, Multi_msg_mode, NULL);
318
319                 // echo the message locally
320                 multi_msg_display_mission_text(Multi_msg_text, MY_NET_PLAYER_NUM);
321         }
322 }
323
324 // maybe process a keypress in text messaging mode, return true if the key was processed
325 int multi_msg_text_process(int k)
326 {
327         char str[2];
328
329         // keep eating keys for a short period of time
330         if((Multi_msg_eat_stamp != -1) && !timestamp_elapsed(Multi_msg_eat_stamp)){
331                 return 1;
332         }       
333         
334         // if we're not in text message mode, return 0
335         if(!Multi_msg_text_enter){
336                 return 0;
337         }
338
339         switch(k){
340         // cancel the message
341         case SDLK_ESCAPE:
342                 multi_msg_text_flush();                         
343                 break;
344
345         // send the message
346         case SDLK_RETURN:
347                 multi_msg_eval_text_msg();              
348                 multi_msg_text_flush();                                         
349                 break;
350
351         // backspace
352         case SDLK_BACKSPACE:
353                 if(strlen(Multi_msg_text) > 0){
354                         Multi_msg_text[strlen(Multi_msg_text)-1] = '\0';
355                 }
356                 break;
357
358         // ignore these individual keys
359         case SDLK_LSHIFT + KEY_SHIFTED:
360         case SDLK_RSHIFT + KEY_SHIFTED:
361         case SDLK_LALT + KEY_SHIFTED:
362         case SDLK_RALT + KEY_SHIFTED:
363         case SDLK_LCTRL + KEY_SHIFTED:
364         case SDLK_RCTRL + KEY_SHIFTED:
365                 break;
366
367         // stick other printable characters onto the text
368         default :                                       
369                 // if we're not already at the maximum length
370                 if(strlen(Multi_msg_text) < MULTI_MSG_MAX_LEN){
371                         int key_text = key_get_text_input();
372
373                         if (key_text < 0) {
374                                 key_text = 255;
375                         }
376
377                         str[0] = (char)key_text;
378                         str[1] = '\0';
379                         SDL_strlcat(Multi_msg_text, str, SDL_arraysize(Multi_msg_text));
380                 }
381                 break;
382         }
383
384         return 1;
385 }
386
387 // return 0 or 1 if there is multi text to be rendered (filling in txt if necessary)
388 int multi_msg_message_text(char *txt, const int txt_len)
389 {
390         // if we're not in text message mode, return 0
391         if(!Multi_msg_text_enter){
392                 return 0;
393         }
394
395         // put the target of the message at the front of the string
396         switch(Multi_msg_mode){
397         // messaging all players
398         case MULTI_MSG_ALL:
399                 SDL_strlcpy(txt, XSTR("ALL : ", 694), txt_len);
400                 break;
401
402         // messaging friendly players
403         case MULTI_MSG_FRIENDLY:
404                 SDL_strlcpy(txt, XSTR("FRIENDLY : ", 695), txt_len);
405                 break;
406
407         // messaging hostile players
408         case MULTI_MSG_HOSTILE:
409                 SDL_strlcpy(txt, XSTR("HOSTILE : ", 696), txt_len);
410                 break;
411
412         // messaging targeted ship
413         case MULTI_MSG_TARGET:
414                 SDL_strlcpy(txt, XSTR("TARGET : ", 697), txt_len);
415                 break;  
416
417         default :
418                 Int3();
419         }       
420         SDL_strlcat(txt, Multi_msg_text, txt_len);
421         SDL_strlcat(txt, "_", txt_len);
422         return 1;
423 }
424
425 // display ingame,inmission message text
426 void multi_msg_display_mission_text(const char *msg, int player_index)
427 {
428         // play a cue voice sound
429         snd_play(&Snds[MULTI_MSG_TEXT_SOUND]);
430
431         if(MULTI_STANDALONE(Net_players[player_index])){
432                 HUD_sourced_printf(HUD_SOURCE_NETPLAYER,"%s %s",XSTR("<SERVER>",698),msg);                      
433         } else {
434                 HUD_sourced_printf(HUD_SOURCE_NETPLAYER,"%s : %s",Net_players[player_index].player->callsign,msg);                      
435         }
436 }
437
438 // if the passed net_player's callsign matches the reg expression of the passed expr
439 int multi_msg_matches_expr(net_player *player, const char *expr)
440 {
441         char callsign[CALLSIGN_LEN+1];
442         int len,idx;
443
444         // some error checking
445         if((player == NULL) || (expr == NULL) || (strlen(expr) <= 0)){
446                 return 0;
447         }
448
449         // get the completely lowercase callsign
450         memset(callsign,0,CALLSIGN_LEN+1);
451         len = strlen(player->player->callsign);
452         for(idx=0;idx<len;idx++){
453                 callsign[idx] = (char)tolower(player->player->callsign[idx]);
454         }
455
456         // see if this guy's callsign matches the expr
457         len = strlen(expr);
458         for(idx=0;idx<len;idx++){
459                 // look for non-matching characters
460                 if(callsign[idx] != expr[idx]){
461                         return 0;
462                 }
463         }
464                                 
465         // matches
466         return 1;
467 }
468
469 // if text input mode is active, clear it
470 void multi_msg_text_flush()
471 {
472         Multi_msg_text_enter = 0;
473         Multi_msg_mode = MULTI_MSG_NONE;
474         Multi_msg_stamp = -1;           
475
476         // keep eating keys for a short period of time and unset any used control bits
477         Multi_msg_eat_stamp = timestamp(350);
478         control_config_clear_used_status();
479         key_flush();
480 }
481
482
483 // -----------------------------------------------------------------------------------
484 // MULTI MESSAGE COMMAND FUNCTIONS
485 //
486
487 // process an entered line of text and maybe perform a command on it (return 1 if an action was performed, 0 if not)
488 int multi_msg_check_command(char *str)
489 {
490         int idx;
491         char *prefix,*predicate,param[MULTI_MSG_MAX_TEXT_LEN+1];
492
493         // look for a colon
494         if(strstr(str,":") == NULL){
495                 return 0;
496         }
497
498         // try and find a command prefix
499         prefix = NULL;  
500         prefix = strtok(str,":");
501         if(prefix == NULL){
502                 return 0;
503         }
504
505         // get all the text after the message
506         predicate = NULL;
507         predicate = strtok(NULL, NOX("\n\0"));
508         if(predicate == NULL){
509                 return 0;
510         } 
511         
512         // store the text as the actual parameter
513         SDL_strlcpy(param, predicate, SDL_arraysize(param));
514         drop_leading_white_space(param);
515
516         // go through all existing commands and see what we can do
517         for(idx=0;idx<MULTI_MSG_CMD_COUNT;idx++){
518                 if(!SDL_strcasecmp(prefix,Multi_msg_commands[idx])){
519                         // perform the command
520                         multi_msg_perform_command(idx,param);
521
522                         // return true
523                         return 1;
524                 }
525         }
526         
527         // apply the results as a general expression, if we're in message all mode
528         if(Multi_msg_mode == MULTI_MSG_ALL){
529                 SDL_strlcpy(Multi_msg_text, param, SDL_arraysize(Multi_msg_text));
530
531                 // send the chat packet
532                 send_game_chat_packet(Net_player, Multi_msg_text, MULTI_MSG_EXPR,NULL, prefix);
533
534                 // echo the message locally
535                 multi_msg_display_mission_text(Multi_msg_text, MY_NET_PLAYER_NUM);
536
537                 // return true
538                 return 1;
539         }       
540         
541         // no commands performed
542         return 0;
543 }
544
545 // perform the passed command (MULTI_MSG_CMD_* define) with the passed string argument
546 void multi_msg_perform_command(int command,char *param)
547 {       
548         // we may eventually want to split each of these cases into its own function to make things neater
549         switch(command){
550         // kick a player
551         case MULTI_MSG_CMD_KICK:
552                 int np_index = multi_find_player_by_callsign(param);
553                 if(np_index != -1){
554                         multi_kick_player(np_index);
555                 }
556                 break;
557         }
558 }
559
560
561 // -----------------------------------------------------------------------------------
562 // MULTI SQUADMATE MESSAGING FUNCTIONS
563 //
564
565 //XSTR:OFF
566
567 const char *Multi_msg_subsys_name[SUBSYSTEM_MAX] = {
568         "None",
569         "Engine",
570         "Turret",
571 #ifndef MAKE_FS1
572         "Bridge",
573 #endif
574         "Radar",
575         "Navigation",
576         "Communication",
577         "Weapons",
578         "Sensors",
579         "Solar Array",
580         "Unknown"
581 };
582
583 //XSTR:ON
584
585 // display a squadmsg order directed towards _me_
586 void multi_msg_show_squadmsg(net_player *source,int command,ushort target_sig,int subsys_type)
587 {
588         char hud_string[255];
589         char temp_string[100];                  
590         int should_display;
591         object *target_obj;
592
593         // clear the strings
594         memset(hud_string,0,255);
595         memset(temp_string,0,100);      
596
597         // add the message header
598         SDL_snprintf(hud_string,SDL_arraysize(hud_string),XSTR("ORDER FROM <%s> : ",699),source->player->callsign);
599
600         // get the target obj if possible
601         target_obj = NULL;
602         target_obj = multi_get_network_object(target_sig);      
603
604         should_display = 1;
605
606         // add the command specific text
607         switch(command){
608         // attack my target
609         case ATTACK_TARGET_ITEM :
610                 if((target_obj != NULL) && (target_obj->type == OBJ_SHIP)){
611                         SDL_snprintf(temp_string,SDL_arraysize(temp_string),XSTR("Attack %s",700),Ships[target_obj->instance].ship_name);
612                         SDL_strlcat(hud_string, temp_string, SDL_arraysize(hud_string));
613                 } else {
614                         should_display = 0;
615                 }
616                 break;
617
618         // disable my target
619         case DISABLE_TARGET_ITEM:
620                 if((target_obj != NULL) && (target_obj->type == OBJ_SHIP)){
621                         SDL_snprintf(temp_string,SDL_arraysize(temp_string),XSTR("Disable %s",701),Ships[target_obj->instance].ship_name);
622                         SDL_strlcat(hud_string, temp_string, SDL_arraysize(hud_string));
623                 } else {
624                         should_display = 0;
625                 }
626                 break;
627
628         // protect my target
629         case PROTECT_TARGET_ITEM:
630                 if((target_obj != NULL) && (target_obj->type == OBJ_SHIP)){
631                         SDL_snprintf(temp_string,SDL_arraysize(temp_string),XSTR("Protect %s",702),Ships[target_obj->instance].ship_name);
632                         SDL_strlcat(hud_string, temp_string, SDL_arraysize(hud_string));
633                 } else {
634                         should_display = 0;
635                 }
636                 break;
637
638         // ignore my target
639         case IGNORE_TARGET_ITEM:
640                 if((target_obj != NULL) && (target_obj->type == OBJ_SHIP)){
641                         SDL_snprintf(temp_string,SDL_arraysize(temp_string),XSTR("Ignore %s",703),Ships[target_obj->instance].ship_name);
642                         SDL_strlcat(hud_string, temp_string, SDL_arraysize(hud_string));
643                 } else {
644                         should_display = 0;
645                 }
646                 break;  
647
648         // disarm my target
649         case DISARM_TARGET_ITEM:
650                 if((target_obj != NULL) && (target_obj->type == OBJ_SHIP)){
651                         SDL_snprintf(temp_string,SDL_arraysize(temp_string),XSTR("Disarm %s",704),Ships[target_obj->instance].ship_name);
652                         SDL_strlcat(hud_string, temp_string, SDL_arraysize(hud_string));
653                 } else {
654                         should_display = 0;
655                 }
656                 break;  
657
658         // disable subsystem on my target
659         case DISABLE_SUBSYSTEM_ITEM:
660                 if((target_obj != NULL) && (target_obj->type == OBJ_SHIP) && (subsys_type != -1) && (subsys_type != 0)){
661                         SDL_snprintf(temp_string,SDL_arraysize(temp_string),XSTR("Disable subsystem %s on %s",705),Multi_msg_subsys_name[subsys_type],Ships[target_obj->instance].ship_name);
662                         SDL_strlcat(hud_string, temp_string, SDL_arraysize(hud_string));
663                 } else {
664                         should_display = 0;
665                 }
666                 break;
667
668         // form on my wing
669         case FORMATION_ITEM:            
670                 SDL_strlcat(hud_string, XSTR("Form on my wing",706), SDL_arraysize(hud_string));
671                 break;
672
673         // cover me
674         case COVER_ME_ITEM:
675                 SDL_strlcat(hud_string, XSTR("Cover me",707), SDL_arraysize(hud_string));
676                 break;
677
678         // engage enemy
679         case ENGAGE_ENEMY_ITEM:
680                 SDL_strlcat(hud_string, XSTR("Engage enemy!",708), SDL_arraysize(hud_string));
681                 break;
682
683         default :
684                 should_display =0;
685                 break;
686         }
687
688         // print it out
689         if(should_display){
690                 HUD_printf(hud_string);
691         }
692 }
693
694 // evaluate if the given netplayer exists in the passed wingnum
695 int multi_msg_player_in_wing(int wingnum,net_player *pl)
696 {
697         int idx;
698         
699         // if this guy doesn't have a valid ship, bail 
700         if((pl->player->objnum == -1) || (Objects[pl->player->objnum].type != OBJ_SHIP)){
701                 return 0;
702         }
703
704         // look through all ships in the wing
705         for(idx=0;idx<Wings[wingnum].current_count;idx++){
706                 // if we found a match
707                 if(Wings[wingnum].ship_index[idx] == Objects[pl->player->objnum].instance){
708                         return 1;
709                 }
710         }
711
712         return 0;
713 }
714
715 // evaluate if the given netplayer is flying the passed shipnum
716 int multi_msg_player_in_ship(int shipnum,net_player *pl)
717 {
718         // if we found a matching ship
719         if((pl->player->objnum != -1) && (Objects[pl->player->objnum].type == OBJ_SHIP) && (shipnum == Objects[pl->player->objnum].instance)){
720                 return 1;
721         }
722
723         // not a matching ship
724         return 0;
725 }
726
727 // send a squadmsg packet to a player
728 void multi_msg_send_squadmsg_packet(net_player *target,net_player *source,int command,ushort net_sig,int subsys_type)
729 {
730         ubyte data[100];                
731         char s_val;
732         int packet_size;
733
734         SDL_assert(source != NULL);
735         SDL_assert(target != NULL);
736         if((source == NULL) || (target == NULL)){
737                 return;
738         }
739
740         // build the header
741         BUILD_HEADER(SQUADMSG_PLAYER);
742
743         // add the command and targeting data   
744         ADD_INT(command);
745
746         // add the id of the guy sending the order
747         ADD_SHORT(source->player_id);
748
749         // net signature
750         ADD_USHORT(net_sig);
751         
752         // targeted subsytem (or -1 if none)
753         s_val = (char)subsys_type;
754         ADD_DATA(s_val);        
755
756         // send to the player   
757         multi_io_send_reliable(target, data, packet_size);
758 }
759
760 // evaluate if a wing SQUADMATE MESSAGE command should be sent to a player
761 // return 0 if at least one ai ship got the order, 1 if only players
762 int multi_msg_eval_wing_squadmsg(int wingnum,int command,ai_info *aif, int player_num)
763 {
764         int idx;
765         ushort net_sig;
766         int subsys_type;
767         int sent_count;
768
769         // get the index of the sender
770         if(player_num == -1)
771                 player_num = MY_NET_PLAYER_NUM;
772
773         // get the target information
774         if(aif->target_objnum == -1){
775                 net_sig = 0;
776         } else {
777                 net_sig = Objects[aif->target_objnum].net_signature;
778         }
779
780         if((aif->targeted_subsys == NULL) || (aif->targeted_subsys->system_info == NULL)){
781                 subsys_type = -1;
782         } else {
783                 subsys_type = aif->targeted_subsys->system_info->type;
784         }
785
786         // go through all netplayers and find all matched
787         sent_count = Wings[wingnum].current_count;
788         for(idx=0;idx<MAX_PLAYERS;idx++){
789                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){                   
790                         // if he is in the wing, send him the message
791                         if(multi_msg_player_in_wing(wingnum,&Net_players[idx])){
792                                 // if this was the sender himself, just decrement the count
793                                 if(idx == player_num){
794                                         sent_count--;
795                                         continue;
796                                 }
797
798                                 // if its me, just display locally
799                                 if(&Net_players[idx] == Net_player){
800                                         multi_msg_show_squadmsg(&Net_players[player_num],command,net_sig,subsys_type);                                          
801                                         sent_count--;
802                                 }
803                                 // otherwise send it to who is supposed to get it
804                                 else {                                  
805                                         multi_msg_send_squadmsg_packet(&Net_players[idx],&Net_players[player_num],command,net_sig,subsys_type);
806                                         sent_count--;
807                                 }
808                         }
809                 }
810         }
811
812         // if all the ships which got the message were players, return 1
813         return !sent_count;
814 }
815
816 // evaluate if a ship SQUADMATE MESSAGE command should be sent to a player
817 // return 0 if not sent to a netplayer, 1 if it was
818 int multi_msg_eval_ship_squadmsg(int shipnum,int command,ai_info *aif, int player_num)
819 {
820         int idx;
821         ushort net_sig;
822         int subsys_type;
823
824         // get the index of the sender
825         if ( player_num == -1 )
826                 player_num = MY_NET_PLAYER_NUM;
827
828         // get the target information
829         if(aif->target_objnum == -1){
830                 net_sig = 0;
831         } else {
832                 net_sig = Objects[aif->target_objnum].net_signature;
833         }
834
835         if((aif->targeted_subsys == NULL) || (aif->targeted_subsys->system_info == NULL)){
836                 subsys_type = -1;
837         } else {
838                 subsys_type = aif->targeted_subsys->system_info->type;
839         }
840
841         // go through all netplayers and find all matched
842         for(idx=0;idx<MAX_PLAYERS;idx++){
843                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (idx != player_num)){
844                         // if he is in the ship, send him the message
845                         if(multi_msg_player_in_ship(shipnum,&Net_players[idx])){
846                                 // if its me, just display locall
847                                 if(&Net_players[idx] == Net_player){                            
848                                         multi_msg_show_squadmsg(&Net_players[player_num],command,net_sig,subsys_type);                                          
849
850                                         return 1;
851                                 }
852                                 // otherwise send it to who is supposed to get it
853                                 else {
854                                         multi_msg_send_squadmsg_packet(&Net_players[idx],&Net_players[player_num],command,net_sig,subsys_type);
855                                         return 1;
856                                 }                                                                                                       
857                         }
858                 }
859         }
860
861         // this will let the messaging system show a response to the sender of the packet
862         return 0;
863 }
864
865 // process incoming squadmate messaging info 
866 void multi_msg_process_squadmsg_packet(unsigned char *data, header *hinfo)
867 {       
868         int command;
869         ushort net_sig;
870         short source_id;
871         int source_index;
872         char s_val;
873         int offset = HEADER_LENGTH;
874
875         // get all packet data
876         GET_INT(command);
877         GET_SHORT(source_id);
878         GET_USHORT(net_sig);
879         GET_DATA(s_val);
880         PACKET_SET_SIZE();
881
882         // determine who the order is from
883         source_index = find_player_id(source_id);
884         if(source_index == -1){
885                 nprintf(("Network","Received squadmsg order packet from unknown player!!\n"));
886                 return;
887         }
888
889         // display the squadmessage somehow
890         multi_msg_show_squadmsg(&Net_players[source_index],command,net_sig,(int)s_val);
891 }