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