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