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