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