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