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