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