]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_team.cpp
byte-swapping changes for bigendian systems
[taylor/freespace2.git] / src / network / multi_team.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_team.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * $Log$
16  * Revision 1.4  2004/06/11 01:45:13  tigital
17  * byte-swapping changes for bigendian systems
18  *
19  * Revision 1.3  2002/06/09 04:41:24  relnev
20  * added copyright header
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  * 13    9/08/99 1:59p Dave
30  * Nailed the problem with assigning teams improperly in TvT. Basic
31  * problem was that clients were sending team update packets with bogus
32  * info that the server was willingly accepting, thereby blowing away his
33  * own info.
34  * 
35  * 12    8/22/99 1:55p Dave
36  * Cleaned up host/team-captain leaving code.
37  * 
38  * 11    8/22/99 1:19p Dave
39  * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
40  * which d3d cards are detected.
41  * 
42  * 10    4/09/99 2:21p Dave
43  * Multiplayer beta stuff. CD checking.
44  * 
45  * 9     3/10/99 6:50p Dave
46  * Changed the way we buffer packets for all clients. Optimized turret
47  * fired packets. Did some weapon firing optimizations.
48  * 
49  * 8     3/09/99 6:24p Dave
50  * More work on object update revamping. Identified several sources of
51  * unnecessary bandwidth.
52  * 
53  * 7     2/17/99 2:11p Dave
54  * First full run of squad war. All freespace and tracker side stuff
55  * works.
56  * 
57  * 6     2/11/99 3:08p Dave
58  * PXO refresh button. Very preliminary squad war support.
59  * 
60  * 5     11/19/98 8:03a Dave
61  * Full support for D3-style reliable sockets. Revamped packet lag/loss
62  * system, made it receiver side and at the lowest possible level.
63  * 
64  * 4     11/17/98 11:12a Dave
65  * Removed player identification by address. Now assign explicit id #'s.
66  * 
67  * 3     11/05/98 5:55p Dave
68  * Big pass at reducing #includes
69  * 
70  * 2     10/07/98 10:53a Dave
71  * Initial checkin.
72  * 
73  * 1     10/07/98 10:50a Dave
74  * 
75  * 18    6/13/98 3:19p Hoffoss
76  * NOX()ed out a bunch of strings that shouldn't be translated.
77  * 
78  * 17    5/26/98 7:33p Dave
79  * Once extra check for TvT oktocommit. Tested against all cases.
80  * 
81  * 16    5/22/98 9:35p Dave
82  * Put in channel based support for PXO. Put in "shutdown" button for
83  * standalone. UI tweaks for TvT
84  * 
85  * 15    5/15/98 5:16p Dave
86  * Fix a standalone resetting bug.Tweaked PXO interface. Display captaincy
87  * status for team vs. team. Put in asserts to check for invalid team vs.
88  * team situations.
89  * 
90  * 14    5/03/98 7:04p Dave
91  * Make team vs. team work mores smoothly with standalone. Change how host
92  * interacts with standalone for picking missions. Put in a time limit for
93  * ingame join ship select. Fix ingame join ship select screen for Vasudan
94  * ship icons.
95  * 
96  * 13    4/22/98 5:53p Dave
97  * Large reworking of endgame sequencing. Updated multi host options
98  * screen for new artwork. Put in checks for host or team captains leaving
99  * midgame.
100  * 
101  * 12    4/15/98 5:03p Dave
102  * Put in a rough countdown to mission start on final sync screen. Fixed
103  * several team vs. team bugs on the ship/team select screen.
104  * 
105  * 11    4/14/98 12:57a Dave
106  * Made weapon select screen show netplayer names above ships. Fixed pilot
107  * info popup to show the status of pilot images more correctly.
108  * 
109  * 10    4/08/98 2:51p Dave
110  * Fixed pilot image xfer once again. Solidify team selection process in
111  * pre-briefing multiplayer.
112  * 
113  * 9     4/04/98 4:22p Dave
114  * First rev of UDP reliable sockets is done. Seems to work well if not
115  * overly burdened.
116  * 
117  * 8     4/03/98 1:03a Dave
118  * First pass at unreliable guaranteed delivery packets.
119  * 
120  * 7     3/31/98 4:51p Dave
121  * Removed medals screen and multiplayer buttons from demo version. Put in
122  * new pilot popup screen. Make ships in mp team vs. team have proper team
123  * ids. Make mp respawns a permanent option saved in the player file.
124  * 
125  * 6     3/15/98 4:17p Dave
126  * Fixed oberver hud problems. Put in handy netplayer macros. Reduced size
127  * of network orientation matrices.
128  * 
129  * 5     3/12/98 5:45p Dave
130  * Put in new observer HUD. Made it possible for observers to join at the
131  * beginning of a game and follow it around as an observer full-time.
132  * 
133  * 4     3/10/98 4:26p Dave
134  * Second pass at furball mode. Fixed several team vs. team bugs.
135  * 
136  * 3     3/09/98 5:54p Dave
137  * Fixed stats to take asteroid hits into account. Polished up UI stuff in
138  * team select. Finished up pilot info popup. Tracked down and fixed
139  * double click bug.
140  * 
141  * 2     3/03/98 8:55p Dave
142  * Finished pre-briefing team vs. team support.
143  * 
144  * 1     3/03/98 5:09p Dave
145  *  
146  * $NoKeywords: $
147  */
148
149 #include "freespace.h"
150 #include "linklist.h"
151 #include "multimsgs.h"
152 #include "multiutil.h"
153 #include "multi_team.h"
154 #include "multi_endgame.h"
155 #include "multi_pmsg.h"
156 #include "multi.h"
157
158 // ------------------------------------------------------------------------------------
159 // MULTIPLAYER TEAMPLAY DEFINES/VARS
160 //
161
162 // packet codes
163 #define MT_CODE_TEAM_UPDATE                     0                                                       // send a full team update on a player-per-player basis
164 #define MT_CODE_TEAM_REQUEST                    1                                                       // a request sent to the host to be be on a given team
165
166 //XSTR:OFF
167
168 char *Multi_team0_names[4] = {                                                                  // ships on team 0 (TEAM_FRIENDLY)
169         "alpha 1", "alpha 2", "alpha 3", "alpha 4"
170 };
171 char *Multi_team1_names[4] = {                                                                  // ships on team 1 (TEAM_HOSTILE)
172         "zeta 1", "zeta 2", "zeta 3", "zeta 4"
173 };
174
175 // score for teams 0 and 1 for this mission
176 int Multi_team0_score = 0;
177 int Multi_team1_score = 0;
178
179
180 // ------------------------------------------------------------------------------------
181 // MULTIPLAYER TEAMPLAY FORWARD DECLARATIONS
182 //
183
184 // process a request to change a team
185 void multi_team_process_team_change_request(net_player *pl,net_player *who_from,int team);
186
187 // send a packet to the host requesting to change my team 
188 void multi_team_send_team_request(net_player *pl,int team);
189
190 // have the netgame host assign default teams
191 void multi_team_host_assign_default_teams();
192
193 // check to make sure all teams have a proper captain.
194 void multi_team_sync_captains();
195
196 // process a team update packet, return bytes processed
197 int multi_team_process_team_update(ubyte *data);
198
199
200 // ------------------------------------------------------------------------------------
201 // MULTIPLAYER TEAMPLAY FUNCTIONS
202 //
203
204 // call before level load (pre-sync)
205 void multi_team_level_init()
206 {
207         // score for teams 0 and 1 for this mission
208         Multi_team0_score = 0;
209         Multi_team1_score = 0;
210 }
211
212 // call to determine who won the sw match, -1 == tie, 0 == team 0, 1 == team 1
213 int multi_team_winner()
214 {
215         // determine who won
216
217         // tie situation
218         if(Multi_team0_score == Multi_team1_score){
219                 return -1;
220         }
221
222         // team 0 won
223         if(Multi_team0_score > Multi_team1_score){
224                 return 0;
225         }
226
227         // team 1 must have won
228         return 1;
229 }
230
231 // call to add score to a team
232 void multi_team_maybe_add_score(int points, int team)
233 {
234         // if we're not in multiplayer mode
235         if(!(Game_mode & GM_MULTIPLAYER)){
236                 return;
237         }
238
239         // if not squad war
240         if(!(Netgame.type_flags & NG_TYPE_TEAM)){
241                 return;
242         }
243
244         // if i'm not the server of the game, bail here
245         if(!MULTIPLAYER_MASTER){
246                 return;
247         }
248
249         // add team score
250         switch(team){
251         case 0:
252                 nprintf(("Network", "TVT : adding %d points to team 0 (total == %d)\n", points, points + Multi_team0_score));
253                 Multi_team0_score += points;
254                 break;
255
256         case 1:
257                 nprintf(("Network", "TVT : adding %d points to team 1 (total == %d)\n", points, points + Multi_team1_score));
258                 Multi_team1_score += points;
259                 break;
260         }
261 }
262
263 // reset all players and assign them to default teams
264 void multi_team_reset()
265 {
266         int idx;
267
268         nprintf(("Network","MULTI TEAM : resetting\n"));
269         
270         // unset everyone's captaincy and locked flags  
271         for(idx=0;idx<MAX_PLAYERS;idx++){
272                 if(MULTI_CONNECTED(Net_players[idx])){
273                         Net_players[idx].p_info.team = 0;
274                         Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_LOCKED | NETINFO_FLAG_TEAM_CAPTAIN);
275                 }
276         }
277
278         // host should divvy up the teams, send a reset notice to all players and assign captains
279         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){         
280                 // divvy up the teams here
281                 multi_team_host_assign_default_teams();         
282         }       
283 }
284
285 // set the captaincy status of this player
286 void multi_team_set_captain(net_player *pl,int set)
287 {
288         // only the host should ever get here!
289         Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
290
291         // set the player flags as being a captain and notify everyone else of this
292         if(set){
293                 nprintf(("Network","MULTI TEAM : Server setting player %s team %d captain\n",pl->player->callsign,pl->p_info.team));
294                 pl->flags |= NETINFO_FLAG_TEAM_CAPTAIN;
295         } else {
296                 nprintf(("Network","MULTI TEAM : Server unsetting player %s as team %d captain\n",pl->player->callsign,pl->p_info.team));
297                 pl->flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
298         }       
299 }
300
301 // set the team of this given player (if called by the host, the player becomes locked, cnad only the host can modify him from thereon)
302 void multi_team_set_team(net_player *pl,int team)
303 {       
304         // if i'm the server of the game, do it now
305         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){         
306                 nprintf(("Network","MULTI TEAM : Server/Host setting player %s to team %d and locking\n",pl->player->callsign,team));
307
308                 pl->p_info.team = team;
309
310                 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
311                         pl->flags |= NETINFO_FLAG_TEAM_LOCKED;
312                 }
313                 pl->flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);              
314                                                 
315                 // check to see if the resulting team rosters need a captain
316                 multi_team_sync_captains();
317         } else {
318                 nprintf(("Network","MULTI TEAM : Sending team change request to server\n"));
319                 multi_team_send_team_request(pl,team);
320         }
321
322         // verify that we have valid team stuff
323         multi_team_verify();
324 }
325
326 // process a request to change a team
327 void multi_team_process_team_change_request(net_player *pl,net_player *who_from,int team)
328 {
329         // if this player has already been locked, don't do anything
330         if((pl->flags & NETINFO_FLAG_TEAM_LOCKED) && !(who_from->flags & NETINFO_FLAG_GAME_HOST)){
331                 nprintf(("Network","MULTI TEAM : Server ignoring team change request because player is locked\n"));
332                 return;
333         } 
334
335         // otherwise set the team for the player and send an update
336         nprintf(("Network","MULTI TEAM : Server changing player %s to team %d from client request\n",pl->player->callsign,team));
337         pl->p_info.team = team;
338         pl->flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);                                      
339
340         // if this came from the host, lock the player down
341         if(who_from->flags & NETINFO_FLAG_GAME_HOST){
342                 pl->flags |= NETINFO_FLAG_TEAM_LOCKED;
343         }
344
345         // check to see if the resulting team rosters need a captain
346         multi_team_sync_captains();     
347
348         // send a team update
349         multi_team_send_update();
350
351         // verify that we have valid team stuff
352         multi_team_verify();
353 }
354
355 // have the netgame host assign default teams
356 void multi_team_host_assign_default_teams()
357 {
358         int player_count,idx;
359         int team0_count;
360         
361         // first determine how many players there are in the game
362         player_count = 0;
363         for(idx=0;idx<MAX_PLAYERS;idx++){
364                 // count anyone except the standalone server (if applicable)
365                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx])){
366                         player_count++;
367                 }
368         }
369
370         // determine how many players should be stuck on team 0
371         if(player_count < 2){
372                 team0_count = 1;
373         } else {
374                 team0_count = player_count / 2;
375         }
376
377         // assign the players to team 0
378         idx = 0;
379         while(team0_count > 0){
380                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx])){
381                         Net_players[idx].p_info.team = 0;
382                         Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_LOCKED);
383                         Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
384                         team0_count--;
385                 }
386
387                 idx++;
388         }
389
390         // assign the remaining players to team 1
391         while(idx < MAX_PLAYERS){
392                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx])){
393                         Net_players[idx].p_info.team = 1;
394                         Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_LOCKED);
395                         Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);                 
396                 }               
397                 idx++;  
398         }               
399
400         // sync captains up
401         multi_team_sync_captains();
402
403         // send a full update to all players
404         multi_team_send_update();
405
406         // verify that we have valid team stuff
407         multi_team_verify();
408 }
409
410 // check to see if the team this player on needs a captain and assign him if so
411 void multi_team_sync_captains()
412 {
413         int idx;
414         int team0_cap,team1_cap;
415
416         // if I'm not the server, bail
417         if(!MULTIPLAYER_MASTER){                
418                 return;
419         }
420         
421         // determine if any team now needs a captain    
422         team0_cap = 0;
423         team1_cap = 0;
424         for(idx=0;idx<MAX_PLAYERS;idx++){
425                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN)){
426                         switch(Net_players[idx].p_info.team){
427                         case 0 :
428                                 team0_cap = 1;
429                                 break;
430                         case 1 : 
431                                 team1_cap = 1;
432                                 break;
433                         }
434                 }
435         }
436
437         // if team 0 needs a captain, get one
438         if(!team0_cap){
439                 for(idx=0;idx<MAX_PLAYERS;idx++){
440                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].p_info.team == 0) && !(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN)){
441                                 multi_team_set_captain(&Net_players[idx],1);
442                                 break;
443                         }
444                 }
445         }
446
447         // if team 1 needs a captain, get one
448         if(!team1_cap){
449                 for(idx=0;idx<MAX_PLAYERS;idx++){
450                         if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].p_info.team == 1) && !(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN)){
451                                 multi_team_set_captain(&Net_players[idx],1);
452                                 break;
453                         }
454                 }
455         }                       
456 }
457
458 // is it ok for the host to hit commit
459 int multi_team_ok_to_commit()
460 {
461         int team0_captains,team1_captains;
462         int team0_count,team1_count;
463         int idx;                
464
465         // verify that we have valid team stuff
466         multi_team_verify();
467         
468         // check to see if both teams have a captain
469         team0_captains = 0;
470         team1_captains = 0;
471
472         for(idx=0;idx<MAX_PLAYERS;idx++){
473                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx])){
474                         // if this is team 0's captain
475                         if((Net_players[idx].p_info.team == 0) && (Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN)){
476                                 team0_captains++;
477                         } else if((Net_players[idx].p_info.team == 1) && (Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN)){
478                                 team1_captains++;
479                         }
480                 }
481         }
482
483         // check to see if both teams has <= 4 players
484         multi_team_get_player_counts(&team0_count,&team1_count);
485         team0_count = ((team0_count <= 4) && (team0_count > 0)) ? 1 : 0;
486         team1_count = ((team1_count <= 4) && (team1_count > 0)) ? 1 : 0;
487
488         return ((team0_captains == 1) && (team1_captains == 1) && team0_count && team1_count) ? 1 : 0;
489 }
490
491 // handle a player drop
492 void multi_team_handle_drop()
493 {               
494         int idx;
495         int team0_cap, team1_cap;
496
497         // if I'm not the server, bail
498         if(!MULTIPLAYER_MASTER){
499                 return;
500         }
501         
502         // if we're not in a team vs team situation, we don't care
503         if(!(Netgame.type_flags & NG_TYPE_TEAM)){
504                 return;
505         }
506
507         // is either team at 0 players, ingame?
508         if(Game_mode & GM_IN_MISSION){
509                 int team0_count, team1_count;
510                 multi_team_get_player_counts(&team0_count, &team1_count);
511                 if(team0_count <= 0){
512                         multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_TEAM0_EMPTY);
513                         return;
514                 }
515                 if(team1_count <= 0){
516                         multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_TEAM1_EMPTY);
517                         return;
518                 }
519         }
520
521         // check to see if a team captain has left the game             
522         team0_cap = 0;
523         team1_cap = 0;
524         for(idx=0;idx<MAX_PLAYERS;idx++){
525                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN)){
526                         switch(Net_players[idx].p_info.team){
527                         case 0 :
528                                 team0_cap = 1;
529                                 break;
530                         case 1 : 
531                                 team1_cap = 1;
532                                 break;
533                         }
534                 }
535         }
536         
537         // if we have lost a team captain and we're not in the forming state, abort the game
538         if((!team0_cap || !team1_cap) && (Netgame.game_state != NETGAME_STATE_FORMING)){
539                 // if we're in-mission, just sync up captains
540                 if(Game_mode & GM_IN_MISSION){
541                         // sync up captains
542                         multi_team_sync_captains();                     
543
544                         // send a team update
545                         multi_team_send_update();
546
547                         // find the player who is the new captain
548                         int team_check = team0_cap ? 1 : 0;
549                         for(idx=0; idx<MAX_PLAYERS; idx++){
550                                 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN) && (Net_players[idx].p_info.team == team_check)){                  
551                                         send_host_captain_change_packet(Net_players[idx].player_id, 1);
552                                 }
553                         }
554                         return;
555                 }
556                 
557                 // quit the game
558                 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CAPTAIN_LEFT);
559         } 
560         // otherwise sync things up and be done with it.
561         else {
562                 // sync up team captains        
563                 multi_team_sync_captains();
564         
565                 // send a team update
566                 multi_team_send_update();
567
568                 // verify that we have valid team stuff
569                 multi_team_verify();
570         }       
571 }
572
573 // handle a player join
574 void multi_team_handle_join(net_player *pl)
575 {
576         int team0_count,team1_count,idx,team_select;
577         
578         // if we're not in a team vs team situation, we don't care
579         if(!(Netgame.type_flags & NG_TYPE_TEAM)){
580                 return;
581         }       
582         
583         // if the joining player is joining as an observer, don't put him on a team
584         if(pl->flags & NETINFO_FLAG_OBSERVER){
585                 return;
586         }
587
588         // only the host should ever do this
589         Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
590         if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
591                 return;
592         }
593
594         // count the # of players on each time
595         team0_count = 0;
596         team1_count = 0;
597         for(idx=0;idx<MAX_PLAYERS;idx++){
598                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx])){
599                         // player is on team 0
600                         if(Net_players[idx].p_info.team == 0){
601                                 team0_count++;
602                         }
603                         // player is on team 1
604                         else if(Net_players[idx].p_info.team == 1){
605                                 team1_count++;
606                         } 
607                         // some other case - should never happen
608                         else {
609                                 Int3();
610                         }
611                 }
612         }
613
614         // determine what team he should be on
615         if((team0_count == team1_count) || (team0_count < team1_count)){
616                 team_select = 0;
617         } else {
618                 team_select = 1;
619         }
620
621         // place him on the team, but don't lock him yet
622         multi_team_set_team(pl,team_select);
623         pl->flags &= ~(NETINFO_FLAG_TEAM_LOCKED);               
624
625         // send a team update
626         multi_team_send_update();
627
628         // verify that we have valid team stuff
629         multi_team_verify();
630 }
631
632 // set all ships in the mission to be marked as the proper team (TEAM_HOSTILE, TEAM_FRIENLY)
633 void multi_team_mark_all_ships()
634 {
635         ship_obj *moveup;       
636         
637    // look through all ships in the mission
638         moveup = GET_FIRST(&Ship_obj_list);
639         while(moveup!=END_OF_LIST(&Ship_obj_list)){
640                 multi_team_mark_ship(&Ships[Objects[moveup->objnum].instance]);         
641
642                 moveup = GET_NEXT(moveup);
643         }       
644
645         // verify that we have valid team stuff
646         multi_team_verify();
647 }
648
649 // set the proper team for the passed in ship
650 void multi_team_mark_ship(ship *sp)
651 {       
652         int idx;
653         int team_num;
654
655         // no team found yet
656         team_num = -1;
657                 
658         // look through team 0
659         for(idx=0;idx<4;idx++){
660                 if(!stricmp(sp->ship_name,Multi_team0_names[idx])){
661                         team_num = 0;
662                         break;
663                 }
664         }
665
666         // look through team 1 if necessary
667         if(team_num < 0){
668                 for(idx=0;idx<4;idx++){
669                         if(!stricmp(sp->ship_name,Multi_team1_names[idx])){
670                                 team_num = 1;
671                                 break;
672                         }
673                 }
674         }
675
676         // if we found a team
677         switch(team_num){
678         case 0 :
679                 sp->team = TEAM_FRIENDLY;
680                 break;
681         case 1 :
682                 sp->team = TEAM_HOSTILE;
683                 break;
684         }
685
686         // verify that we have valid team stuff
687         multi_team_verify();
688 }
689
690 // host locks all players into their teams
691 void multi_team_host_lock_all()
692 {
693         int idx;
694
695         // lock all players down
696         for(idx=0;idx<MAX_PLAYERS;idx++){
697                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx])){
698                         Net_players[idx].flags |= NETINFO_FLAG_TEAM_LOCKED;
699                 }
700         }
701
702         // verify that we have valid team stuff
703         multi_team_verify();
704 }
705
706 // get the player counts for team 0 and team 1 (NULL values are valid)
707 void multi_team_get_player_counts(int *team0, int *team1)
708 {
709         int idx;
710
711         // initialize the values
712         if(team0 != NULL){
713                 (*team0) = 0;
714         }
715         if(team1 != NULL){
716                 (*team1) = 0;
717         }
718
719         // check all players
720         for(idx=0;idx<MAX_PLAYERS;idx++){
721                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx])){
722                         if((Net_players[idx].p_info.team == 0) && (team0 != NULL)){
723                                 (*team0)++;
724                         } else if((Net_players[idx].p_info.team == 1) && (team1 != NULL)){
725                                 (*team1)++;
726                         }
727                 }
728         }
729 }
730
731 // report on the winner/loser of the game via chat text
732 #define SEND_AND_DISPLAY(mesg)          do { send_game_chat_packet(Net_player, mesg, MULTI_MSG_ALL, NULL, NULL, 1); multi_display_chat_msg(mesg, 0, 0); } while(0);
733 void multi_team_report()
734 {
735         char report[400] = "";  
736
737         // a little header
738         SEND_AND_DISPLAY("----****");   
739
740         // display scores
741         sprintf(report, XSTR("<Team 1 had %d points>", 1275), Multi_team0_score);
742         SEND_AND_DISPLAY(report);
743         sprintf(report, XSTR("<Team 2 had %d points>", 1276), Multi_team1_score);
744         SEND_AND_DISPLAY(report);
745
746         // display winner
747         switch(multi_team_winner()){
748         case -1:
749                 SEND_AND_DISPLAY(XSTR("<Match was a tie>", 1277));
750                 break;
751
752         case 0:         
753                 SEND_AND_DISPLAY(XSTR("<Team 1 (green) is the winner>", 1278));
754                 break;
755
756         case 1:                         
757                 SEND_AND_DISPLAY(XSTR("<Team 2 (red) is the winner>", 1279));
758                 break;
759         }
760         // a little header
761         SEND_AND_DISPLAY("----****");   
762 }
763
764
765 // ------------------------------------------------------------------------------------
766 // MULTIPLAYER TEAMPLAY PACKET HANDLERS
767 //
768
769 // process an incoming team update packet
770 void multi_team_process_packet(unsigned char *data, header *hinfo)
771 {
772         ubyte code;     
773         int player_index;
774         int offset = HEADER_LENGTH;             
775
776         // find out who is sending this data    
777         player_index = find_player_id(hinfo->id);       
778
779         // get the packet opcode
780         GET_DATA(code);
781
782         // take action absed upon the opcode    
783         switch((int)code){
784         // a request to set the team for a player
785         case MT_CODE_TEAM_REQUEST:
786                 ushort player_id;
787                 int req_index,req_team;
788
789                 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
790
791                 // get the packet data
792                 GET_DATA_U16(player_id);
793                 GET_DATA_S32(req_team);
794
795                 // if i'm the host of the game, process here            
796                 req_index = find_player_id(player_id);
797                 if(req_index == -1){
798                         nprintf(("Network","Could not find player to process team change request !\n"));
799                 } else {
800                         multi_team_process_team_change_request(&Net_players[req_index],&Net_players[player_index],req_team);
801                 }               
802                 break;
803         
804         // a full team update
805         case MT_CODE_TEAM_UPDATE:
806                 offset += multi_team_process_team_update(data+offset);
807                 break;  
808         }       
809         
810         PACKET_SET_SIZE();
811
812         // verify that we have valid team stuff
813         multi_team_verify();
814 }
815
816 // send a packet to the host requesting to change my team 
817 void multi_team_send_team_request(net_player *pl, int team)
818 {
819         ubyte data[MAX_PACKET_SIZE],code;
820         int packet_size = 0;
821
822         // build the header and add the opcode
823         BUILD_HEADER(TEAM_UPDATE);
824         code = MT_CODE_TEAM_REQUEST;
825         ADD_DATA(code);
826
827         // add the address of the guy we want to change
828         ADD_DATA_S16(pl->player_id);
829
830         // add the team I want to be on
831         ADD_DATA_S32(team);
832
833         // send to the server of the game (will be routed to host if in a standalone situation) 
834         multi_io_send_reliable(Net_player, data, packet_size);
835 }
836
837 // send a full update on a player-per-player basis (should call this to update all players after other relevant function calls)
838 void multi_team_send_update()
839 {
840         ubyte data[MAX_PACKET_SIZE],val,stop;
841         int idx;
842         int packet_size = 0;
843
844         // if I'm not the server, bail
845         if(!MULTIPLAYER_MASTER){
846                 return;
847         }
848
849         // first, verify that we have valid settings
850         multi_team_verify();
851
852         // build the header and add the opcode
853         BUILD_HEADER(TEAM_UPDATE);
854         val = MT_CODE_TEAM_UPDATE;
855         ADD_DATA(val);
856
857         // add the info for all players;
858         for(idx=0;idx<MAX_PLAYERS;idx++){
859                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
860                         // add a stop byte
861                         stop = 0x0;
862                         ADD_DATA(stop);
863
864                         // add this guy's id
865                         ADD_DATA_S16(Net_players[idx].player_id);
866
867                         // pack all his data into a byte
868                         val = 0x0;
869                         
870                         // set bit 0 if he's on team 1
871                         if(Net_players[idx].p_info.team == 1){
872                                 val |= (1<<0);
873                         }
874
875                         // set bit 1 if he's a team captain
876                         if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
877                                 val |= (1<<1);
878                         }
879                         ADD_DATA(val);
880                 }
881         }
882
883         // add the final stop byte
884         stop = 0xff;
885         ADD_DATA(stop);
886
887         // if i'm the server, I should broadcast to this to all players, otherwise I should send it to the standalone
888         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){         
889                 multi_io_send_to_all_reliable(data, packet_size);
890         } else {
891                 multi_io_send_reliable(Net_player, data, packet_size);
892         }
893 }       
894
895 // process a team update packet, return bytes processed
896 int multi_team_process_team_update(ubyte *data)
897 {
898         ubyte stop,flags;
899         short player_id;
900         int player_index;
901         int offset = 0; 
902
903         // if I'm the server, bail
904         Assert(!MULTIPLAYER_MASTER);
905         
906         // process all players
907         GET_DATA(stop);
908         while(stop != 0xff){
909                 // get the net address and flags for the guy
910                 GET_DATA_S16(player_id);
911                 GET_DATA(flags);
912
913                 // do a player lookup
914                 if(!MULTIPLAYER_MASTER){
915                         player_index = find_player_id(player_id);
916                         if(player_index != -1){
917                                 // set his team correctly
918                                 if(flags & (1<<0)){
919                                         Net_players[player_index].p_info.team = 1;
920                                 } else {
921                                         Net_players[player_index].p_info.team = 0;
922                                 }
923
924                                 // set his captaincy flag correctly
925                                 Net_players[player_index].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
926                                 if(flags & (1<<1)){
927                                         Net_players[player_index].flags |= NETINFO_FLAG_TEAM_CAPTAIN;
928                                 }
929                         }
930                 }
931
932                 // get the next stop byte
933                 GET_DATA(stop);
934         }
935
936         // verify that we have valid team stuff
937         multi_team_verify();
938         
939         // return bytes processed
940         return offset;
941 }
942
943 // verify that we have valid team stuff
944 void multi_team_verify()
945 {
946 #ifndef NDEBUG
947         int team0_count,team0_cap;
948         int team1_count,team1_cap;
949         int idx;
950
951         // determine how many players we have on team 0 and if they have a captain
952         team0_count = 0;
953         team0_cap = 0;
954         for(idx=0;idx<MAX_PLAYERS;idx++){
955                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
956                         // if he's on team 0
957                         if(Net_players[idx].p_info.team == 0){
958                                 team0_count++;
959
960                                 // if he's a captain
961                                 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
962                                         team0_cap++;
963                                 }
964                         }
965                 }
966         }
967         // if the team has members
968         if(team0_count > 0){
969                 // make sure it also has a captain
970                 Assert(team0_cap > 0);
971
972                 // make sure it only has 1 captain
973                 Assert(team0_cap == 1);
974         }
975
976         // determine how many players we have on team 1 and if they have a captain
977         team1_count = 0;
978         team1_cap = 0;
979         for(idx=0;idx<MAX_PLAYERS;idx++){
980                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
981                         // if he's on team 1
982                         if(Net_players[idx].p_info.team == 1){
983                                 team1_count++;
984
985                                 // if he's a captain
986                                 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
987                                         team1_cap++;
988                                 }
989                         }
990                 }
991         }
992         // if the team has members
993         if(team1_count > 0){
994                 // make sure it also has a captain
995                 Assert(team1_cap > 0);
996
997                 // make sure it only has 1 captain
998                 Assert(team1_cap == 1);
999         }
1000 #endif
1001 }
1002