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