src/engine/eTeam.cpp

Go to the documentation of this file.
00001 /*
00002  
00003 *************************************************************************
00004  
00005 ArmageTron -- Just another Tron Lightcycle Game in 3D.
00006 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
00007  
00008 **************************************************************************
00009  
00010 This program is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 as published by the Free Software Foundation; either version 2
00013 of the License, or (at your option) any later version.
00014  
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019  
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00023 
00024 ***************************************************************************
00025  
00026 */
00027 
00028 #include "eTeam.h"
00029 #include "tSysTime.h"
00030 #include "rFont.h"
00031 #include "nConfig.h"
00032 
00033 #include <set>
00034 
00035 #define TEAMCOLORS 8
00036 
00037 static unsigned short se_team_rgb[TEAMCOLORS][3]=
00038 { {  4,  8, 15 } , // blue
00039     { 15, 15,  4 } , // gold
00040     { 15,  4,  4 } , // red
00041     {  4, 15,  4 } , // green
00042     { 15,  4, 15 } , // violet
00043     {  4, 15, 15 } , // ugly green
00044     { 15, 15, 15 } , // white
00045     {  7,  7,  7 }   // black
00046 };
00047 
00048 static tString se_team_name[TEAMCOLORS]=
00049 {
00050     tString("$team_name_blue"),
00051     tString("$team_name_gold"),
00052     tString("$team_name_red"),
00053     tString("$team_name_green"),
00054     tString("$team_name_violet"),
00055     tString("$team_name_ugly"),
00056     tString("$team_name_white"),
00057     tString("$team_name_black")
00058 };
00059 
00060 // class that creates config items for one team
00061 // TEAM_(NAME|RED|GREEN|BLUE)_X
00062 class eTeamColorConfig {
00063     typedef tSettingItem<tString> nameConf;
00064     typedef tSettingItem<unsigned short int> colorConf;
00065     colorConf *m_red, *m_green, *m_blue;
00066     nameConf *m_name;
00067     static int teamCount;
00068 public:
00069     eTeamColorConfig() {
00070         std::ostringstream name(""), red(""), green(""), blue("");
00071         name  << "TEAM_NAME_"  << teamCount + 1;
00072         red   << "TEAM_RED_"   << teamCount + 1;
00073         green << "TEAM_GREEN_" << teamCount + 1;
00074         blue  << "TEAM_BLUE_"  << teamCount + 1;
00075         m_name  = new nameConf (name .str().c_str(), se_team_name[teamCount]);
00076         m_red   = new colorConf(red  .str().c_str(), se_team_rgb [teamCount][0]);
00077         m_green = new colorConf(green.str().c_str(), se_team_rgb [teamCount][1]);
00078         m_blue  = new colorConf(blue .str().c_str(), se_team_rgb [teamCount][2]);
00079         ++teamCount;
00080     }
00081     ~eTeamColorConfig() {
00082         delete m_name;
00083         delete m_red;
00084         delete m_green;
00085         delete m_blue;
00086     }
00087 };
00088 
00089 int eTeamColorConfig::teamCount = 0;
00090 
00091 static eTeamColorConfig se_team_config[TEAMCOLORS];
00092 
00094 inline static tColoredStringProxy ColorString( const eTeam * t )
00095 {
00096     return tColoredStringProxy( t->R()/15.0f, t->G()/15.0f, t->B()/15.0f );
00097 }
00098 
00099 nNOInitialisator<eTeam> eTeam_init(220,"eTeam");
00100 
00101 nDescriptor &eTeam::CreatorDescriptor() const{
00102     return eTeam_init;
00103 }
00104 
00105 int  eTeam::minTeams=0;                                 // minimum nuber of teams
00106 int  eTeam::maxTeams=30;                        // maximum nuber of teams
00107 int  eTeam::minPlayers=0;                       // minimum number of players per team
00108 int  eTeam::maxPlayers=3;                       // maximum number of players per team
00109 int  eTeam::maxImbalance=2;                     // maximum difference of player numbers
00110 bool eTeam::balanceWithAIs=true;                // use AI players to balance the teams?
00111 bool eTeam::enforceRulesOnQuit=false;   // if the quitting of one player unbalances the teams, enforce the rules by redistributing
00112 
00113 tList<eTeam> eTeam::teams;              //  list of all teams
00114 
00115 static bool newTeamAllowed;             // is it allowed to create a new team currently?
00116 
00117 static nSettingItem<bool> se_newTeamAllowed("NEW_TEAM_ALLOWED", newTeamAllowed );
00118 
00119 static bool se_allowTeamNameColor  = true; // allow to name a team after a color
00120 static bool se_allowTeamNamePlayer = true; // allow to name a team after the leader
00121 static bool se_allowTeamNameLeader = false; // allow to name a team by it's leader
00122 
00123 static tSettingItem<bool> se_allowTeamNameColorConfig("ALLOW_TEAM_NAME_COLOR", se_allowTeamNameColor );
00124 static tSettingItem<bool> se_allowTeamNamePlayerConfig("ALLOW_TEAM_NAME_PLAYER", se_allowTeamNamePlayer );
00125 static tSettingItem<bool> se_allowTeamNameCustomConfig("ALLOW_TEAM_NAME_LEADER", se_allowTeamNameLeader );
00126 
00127 // update all internal information
00128 void eTeam::UpdateStaticFlags()
00129 {
00130     bool newTeamAllowedCurrent = teams.Len() >= maxTeams;
00131 
00132     if ( newTeamAllowedCurrent != newTeamAllowed )
00133     {
00134         se_newTeamAllowed.Set( newTeamAllowedCurrent );
00135 
00136         for (int i = teams.Len() - 1; i>=0; --i)
00137             teams(i)->Update();
00138     }
00139 }
00140 
00141 //update internal properties ( player count )
00142 void eTeam::UpdateProperties()
00143 {
00144     //  bool change = false;
00145     if ( nCLIENT != sn_GetNetState() )
00146     {
00147         //if ( maxPlayersLocal != maxPlayers )
00148         //{
00149         maxPlayersLocal = maxPlayers;
00150         //                      change = true;
00151         //}
00152 
00153         //if ( maxImbalanceLocal != maxImbalance )
00154         //{
00155         maxImbalanceLocal = maxImbalance;
00156         //                      change = true;
00157         //}
00158 
00159         //              if ( change )
00160         //              {
00161         //              }
00162     }
00163 
00164     numHumans = 0;
00165     numAIs = 0;
00166     int i;
00167     for ( i = players.Len()-1; i>=0; --i )
00168     {
00169         if ( players(i)->IsHuman() )
00170         {
00171             if ( players(i)->IsActive() )
00172                 ++numHumans;
00173         }
00174         else
00175             ++numAIs;
00176     }
00177 
00178     if ( nSERVER == sn_GetNetState() )
00179         RequestSync();
00180 }
00181 
00182 // update name and color
00183 void eTeam::UpdateAppearance()
00184 {
00185     unsigned short oldr = r, oldg = g, oldb = b;
00186 
00187     ePlayerNetID* oldest = OldestHumanPlayer();
00188     if ( !oldest )
00189     {
00190         oldest = OldestAIPlayer();
00191     }
00192     /* Logic:
00193           No more voting about teamnames.
00194           The teamname of the player who has been on the team for the longest time
00195           is used for a team!
00196 
00197           Color Teamname:
00198                 -more than 1 player in the current team
00199                         -oldest player's custom teamname is empty
00200                         -if "Player Settings->Player 1->Name Team After Player" is off
00201           Custom Teamname:
00202                 -oldest player's custom teamname is NOT empty
00203           Non-Color Teamname (e.g. "Player 1's Team"):
00204                 -more than 1 player in the current team
00205                         -oldest/first player's custom teamname is empty
00206                         -if "Player Settings->Player 1->Name Team After Player" is on
00207     */
00208     bool nameTeamColor = players.Len() > 1 && (!oldest || oldest->teamname.Len()<=1 || !oldest->nameTeamAfterMe);
00209 
00210     //  if ( !IsHuman() )
00211     //          nameTeamColor = false;
00212     if ( !se_allowTeamNameColor )
00213         nameTeamColor = false;
00214     if ( !se_allowTeamNamePlayer )
00215         nameTeamColor = true;
00216 
00217     nameTeamColor = NameTeamAfterColor ( nameTeamColor );
00218 
00219     tString updateName;
00220     if ( oldest )
00221     {
00222         if ( nameTeamColor )
00223         {
00224             // team name determined by color
00225             tOutput newname;
00226             newname << &se_team_name[ colorID ][0];
00227 
00228             updateName = newname;
00229 
00230             r = se_team_rgb[colorID][0];
00231             g = se_team_rgb[colorID][1];
00232             b = se_team_rgb[colorID][2];
00233         }
00234         else
00235         {
00236             // let oldest own the team
00237             if ( players.Len() > 1 )
00238             {
00239                 if ( oldest->IsHuman() )
00240                 {
00241                     // did the player set a custom teamname ?
00242                     if (se_allowTeamNameLeader && oldest->teamname.Len()>1)
00243                     {
00244                         // Use player's custom teamname
00245                         updateName = oldest->teamname;
00246                     }
00247                     else
00248                     {
00249                         // name team after first/oldest player
00250                         tOutput newname;
00251                         newname.SetTemplateParameter( 1, oldest->GetName() );
00252                         newname << "$team_owned_by";
00253                         updateName = newname;
00254                     }
00255                 }
00256                 else
00257                 {
00258                     // name team after oldest bot
00259                     tOutput newname;
00260                     newname.SetTemplateParameter( 1, oldest->GetName() );
00261                     newname << "$team_ai";
00262                     updateName = newname;
00263                 }
00264             }
00265             else
00266             {
00267                 // did the player set a custom teamname ?
00268                 if (oldest->teamname.Len()>1)
00269                     // use custom teamname
00270                     updateName = oldest->teamname;
00271                 else
00272                     // use player name as teamname
00273                     updateName = oldest->GetUserName();
00274             }
00275 
00276             r = oldest->r;
00277             g = oldest->g;
00278             b = oldest->b;
00279 
00280             // update colored player names
00281             if ( sn_GetNetState() != nSERVER )
00282             {
00283                 for ( int i = players.Len()-1; i>=0; --i )
00284                 {
00285                     players(i)->UpdateName();
00286                 }
00287             }
00288         }
00289     }
00290     else
00291     {
00292         // empty team
00293         updateName = tOutput("$team_empty");
00294         r = g = b = 7;
00295     }
00296 
00297     // if the name has been changed then update it
00298     if (name!=updateName)
00299     {
00300         // only display a message if
00301         // the oldest player changed the name of the team
00302         // the server also sets the teamname sometimes
00303         if(sn_GetNetState()!=nCLIENT && oldest)
00304         {
00305             tOutput message;
00306             tColoredString name;
00307             name << *oldest;
00308             name << tColoredString::ColorString(1,1,1);
00309             message.SetTemplateParameter(1, name);
00310 
00311             tColoredString resetColor;
00312             resetColor << tColoredString::ColorString(r,g,b);
00313             resetColor << updateName;
00314             resetColor << tColoredString::ColorString(1,1,1);
00315             message.SetTemplateParameter(2, resetColor);
00316             message << "$team_renamed";
00317             sn_ConsoleOut(message);
00318         }
00319         name = updateName;
00320     }
00321 
00322     if ( nSERVER == sn_GetNetState() )
00323         RequestSync();
00324 
00325     // update team members if the color changed
00326     if ( oldr != r || oldg != g || oldb != b )
00327     {
00328         for ( int i = players.Len() - 1; i >= 0; --i )
00329         {
00330             players(i)->UpdateName();
00331         }
00332     }
00333 }
00334 
00335 //update internal properties ( player count )
00336 void eTeam::Update()
00337 {
00338     UpdateProperties();
00339     UpdateAppearance();
00340 }
00341 
00342 // sets the lock status
00343 void eTeam::SetLocked( bool locked )
00344 {
00345     if ( locked && !locked_ )
00346     {
00347         sn_ConsoleOut( tOutput( "$invite_team_locked", Name() ) );
00348     }
00349     if ( !locked && locked_ )
00350     {
00351         sn_ConsoleOut( tOutput( "$invite_team_unlocked", Name() ) );
00352     }
00353 
00354     locked_ = locked;
00355 }
00356 
00357 // returns the lock status
00358 bool eTeam::IsLocked() const
00359 {
00360     return locked_;
00361 }
00362 
00363 // invite the player to join
00364 void eTeam::Invite( ePlayerNetID * player )
00365 {
00366     tASSERT( player );
00367     if ( !IsInvited( player ) )
00368     {
00369         sn_ConsoleOut( tOutput( "$invite_team_invite", player->GetColoredName(), Name() ) );
00370     }
00371     player->invitations_.insert( this );
00372 }
00373 
00374 // revoke an invitation
00375 void eTeam::UnInvite( ePlayerNetID * player )
00376 {
00377     tASSERT( player );
00378     if ( player->CurrentTeam() == this )
00379     {
00380         sn_ConsoleOut( tOutput( "$invite_team_kick", player->GetColoredName(), Name() ) );
00381         player->SetTeam(0);
00382     }
00383     else
00384     {
00385         sn_ConsoleOut( tOutput( "$invite_team_uninvite", player->GetColoredName(), Name() ) );
00386     }
00387     player->invitations_.erase( this );
00388 }
00389 
00390 // check if a player is invited
00391 bool eTeam::IsInvited( ePlayerNetID const * player ) const
00392 {
00393     return player->invitations_.find( const_cast< eTeam * >( this ) ) != player->invitations_.end();
00394 }
00395 
00396 void eTeam::AddScore ( int s )
00397 {
00398     score += s;
00399 
00400     if ( nSERVER == sn_GetNetState() )
00401         RequestSync();
00402 }
00403 
00404 void eTeam::ResetScore ( )
00405 {
00406     score = 0;
00407 
00408     if ( nSERVER == sn_GetNetState() )
00409         RequestSync();
00410 }
00411 
00412 void eTeam::SetScore ( int s )
00413 {
00414     score = s;
00415 
00416     if ( nSERVER == sn_GetNetState() )
00417         RequestSync();
00418 }
00419 
00420 void eTeam::AddScore(int points,
00421                      const tOutput& reasonwin,
00422                      const tOutput& reasonlose)
00423 {
00424     if (points==0)
00425         return;
00426 
00427     // delegate to player if this is a one-player team
00428     if ( players.Len() == 1 && maxPlayersLocal == 1 )
00429     {
00430         players[0]->AddScore( points, reasonwin, reasonlose );
00431         return;
00432     }
00433 
00434     score += points;
00435 
00436     tOutput message;
00437     message.SetTemplateParameter(1, tColoredString::RemoveColors(name));
00438     message.SetTemplateParameter(2, points > 0 ? points : -points);
00439 
00440     if (points>0)
00441     {
00442         if (reasonwin.IsEmpty())
00443             message << "$player_win_default";
00444         else
00445             message.Append(reasonwin);
00446     }
00447     else
00448     {
00449         if (reasonlose.IsEmpty())
00450             message << "$player_lose_default";
00451         else
00452             message.Append(reasonlose);
00453     }
00454 
00455     sn_ConsoleOut(message);
00456     RequestSync(true);
00457 
00458     se_SaveToScoreFile(message);
00459 }
00460 
00461 void eTeam::SwapTeamsNo(int a,int b){
00462     if (0>a || teams.Len()<=a)
00463         return;
00464     if (0>b || teams.Len()<=b)
00465         return;
00466     if (a==b)
00467         return;
00468 
00469     eTeam *A=teams(a);
00470     eTeam *B=teams(b);
00471 
00472     teams(b)=A;
00473     teams(a)=B;
00474     A->listID=b;
00475     B->listID=a;
00476 }
00477 
00478 void eTeam::SortByScore(){
00479     // bubble sort (AAARRGGH! but good for lists that change not much)
00480 
00481     bool inorder=false;
00482     while (!inorder){
00483         inorder=true;
00484         int i;
00485         for (i=teams.Len()-2;i>=0;i--)
00486             if (teams(i)->score < teams(i+1)->score){
00487                 SwapTeamsNo(i,i+1);
00488                 inorder=false;
00489             }
00490     }
00491 }
00492 
00493 tString eTeam::Ranking( int MAX, bool cut ){
00494     SortByScore();
00495 
00496     tString ret;
00497 
00498     if (teams.Len()>0){
00499         ret.SetPos(2, cut );
00500         ret << tOutput("$team_scoretable_name");
00501         ret.SetPos(25, cut );
00502         ret << tOutput("$team_scoretable_score");
00503         ret.SetPos(32, cut );
00504         ret << tOutput("$team_scoretable_members");
00505         ret.SetPos(41, cut );
00506         ret << tOutput("$team_scoretable_alive");
00507         ret << "\n";
00508 
00509         int max = teams.Len();
00510         if ( max > MAX && MAX > 0 )
00511         {
00512             max = MAX ;
00513         }
00514         for (int i=0;i<max;i++){
00515             tColoredString line;
00516             eTeam *t = teams(i);
00517             line << ColorString(t);
00518             tString name = t->Name();
00519             //name.RemoveHex();
00520             name.SetPos( 24, cut );
00521 
00522             line.SetPos(2, false );
00523             line << name;
00524             line.SetPos(25, false );
00525             line << t->score;
00526             line.SetPos(32, false );
00527             line << t->NumPlayers();
00528             line.SetPos(41, false);
00529             int alive=t->AlivePlayers();
00530             line << alive;
00531             ret << line << "\n";
00532         }
00533         if ( max < teams.Len() )
00534         {
00535             ret << " ...\n";
00536         }
00537     }
00538     // else
00539     //    ret << tOutput("$team_scoretable_nobody");
00540     return ret;
00541 }
00542 float eTeam::RankingGraph( float y, int MAX ){
00543     SortByScore();
00544 
00545     tColoredString ret;
00546 
00547     if (teams.Len()>0){
00548         tColoredString name;
00549         name << tColoredString::ColorString(1.,.5,.5)
00550         << tOutput("$team_scoretable_name");
00551         DisplayText(-.7, y, .06, name.c_str(), sr_fontScoretable, -1);
00552         tColoredString score;
00553         score << tOutput("$team_scoretable_score");
00554         DisplayText(-.3, y, .06, score.c_str(), sr_fontScoretable, -1);
00555         tColoredString members;
00556         members << tOutput("$team_scoretable_members");
00557         DisplayText(-.1, y, .06, members.c_str(), sr_fontScoretable, -1);
00558         tColoredString alive;
00559         alive << tOutput("$team_scoretable_alive");
00560         DisplayText(.3, y, .06, alive.c_str(), sr_fontScoretable, -1);
00561         y-=.06;
00562 
00563         int max = teams.Len();
00564         if ( max > MAX && MAX > 0 )
00565         {
00566             max = MAX ;
00567         }
00568         for(int i=0;i<max;i++){
00569             eTeam *t = teams(i);
00570             tColoredString name;
00571             name << ColorString(t) << t->Name();
00572             DisplayText(-.7, y, .06, name.c_str(), sr_fontScoretable, -1);
00573             tColoredString score;
00574             score << t->score;
00575             DisplayText(-.3, y, .06, score.c_str(), sr_fontScoretable, -1);
00576             tColoredString members;
00577             members << t->NumPlayers();
00578             DisplayText(-.1, y, .06, members.c_str(), sr_fontScoretable, -1);
00579             tColoredString alive;
00580             int alivep=t->AlivePlayers();
00581             if(alivep)
00582                 alive << tColoredString::ColorString(0,1,0);
00583             else
00584                 alive << tColoredString::ColorString(1,0,0);
00585             alive << alivep;
00586             DisplayText(.3, y, .06, alive.c_str(), sr_fontScoretable, -1);
00587             y-=.06;
00588         }
00589         if ( max < teams.Len() )
00590         {
00591             DisplayText(-.7, y, .06, "...", sr_fontScoretable, -1);
00592             y-=.06;
00593         }
00594     }
00595     return y;
00596 }
00597 
00598 
00599 // get the number of human players on the team
00600 int     eTeam::NumHumanPlayers  (               ) const
00601 {
00602     return numHumans;
00603 }
00604 
00605 static int imbalance = 1;
00606 
00607 // get the number of human players on the team
00608 int     eTeam::NumAIPlayers     (               ) const
00609 {
00610     return numAIs;
00611 }
00612 
00613 // make sure the limits on team number and such are met
00614 void eTeam::EnforceConstraints()
00615 {
00616     if ( maxImbalance < 1 )
00617         maxImbalance = 1;
00618 
00619     if ( minTeams > maxTeams )
00620         minTeams = maxTeams;
00621 
00622     Enforce( minTeams, maxTeams, maxImbalance );
00623 
00624     // reset imbalance count so players may try to switch teams in the next round
00625     if ( imbalance <= 0 )
00626         imbalance=0;
00627 }
00628 
00629 // make sure the limits on team number and such are met
00630 void eTeam::Enforce( int minTeams, int maxTeams, int maxImbalance)
00631 {
00632     if ( maxTeams < 1 )
00633         maxTeams = 1;
00634 
00635     /*
00636     // z-man: disabled for new "respect maxTeams and maxPlayers setting" logic
00637     if ( maxPlayers * maxTeams < se_PlayerNetIDs.Len() )
00638     {
00639         maxPlayers = ( se_PlayerNetIDs.Len()/maxTeams ) + 1;
00640     }
00641     */
00642 
00643     // nothing to be done on the clients
00644     if ( nCLIENT == sn_GetNetState() )
00645         return;
00646 
00647     if ( maxImbalance < 1 )
00648         maxImbalance = 1;
00649 
00650     if ( minTeams > maxTeams )
00651         minTeams = maxTeams;
00652 
00653     if ( minPlayers > maxPlayers )
00654         minPlayers = maxPlayers;
00655 
00656     bool balance = false;
00657 
00658     int giveUp = 10;
00659     while ( !balance && giveUp-- > 0 )
00660     {
00661         balance = true;
00662 
00663         // find the max and min number of players per team and the
00664         eTeam *max = NULL, *min = NULL, *ai = NULL;
00665         int    maxP = minPlayers, minP = 100000;
00666         bool minLocked = false;
00667 
00668         int numTeams = 0;
00669         int numHumanTeams = 0;
00670 
00671         int i;
00672         for ( i = teams.Len()-1; i>=0; --i )
00673         {
00674             eTeam *t = teams(i);
00675 
00676             if ( t->BalanceThisTeam() )
00677             {
00678                 int humans = t->NumHumanPlayers();
00679 
00680                 numTeams++;
00681 
00682                 if ( humans > 0 )
00683                     numHumanTeams++;
00684                 else
00685                     ai = t;
00686 
00687                 if ( humans > maxP )
00688                 {
00689                     maxP = humans;
00690                     max  = t;
00691                 }
00692 
00693                 // prefer unlocked teams as elimination victims, and of course smaller teams
00694                 if ( ( humans > 0 || t->NumPlayers() == 0 ) && humans < minP && ( minLocked || !t->IsLocked() ) )
00695                 {
00696                     minP = humans;
00697                     min  = t;
00698                     minLocked = t->IsLocked();
00699                 }
00700             }
00701         }
00702 
00703         if ( ( numTeams > maxTeams && min ) || ( numTeams > minTeams && ai ) )
00704         {
00705             // too many teams. Destroy the smallest team.
00706             // better: destroy the AI team
00707             if ( ai )
00708                 min = ai;
00709 
00710             for ( i = min->NumPlayers()-1; i>=0; --i )
00711             {
00712                 // one player from the dismantled team.
00713                 tJUST_CONTROLLED_PTR< ePlayerNetID > pni = min->Player(i);
00714 
00715                 // just ignore AIs, they get removed later by the "balance with AIs" code once it notices all humans are gone from this team
00716                 if ( !pni->IsHuman() )
00717                 {
00718                     continue;
00719                 }
00720 
00721                 // find the second smallest team:
00722                 eTeam* second = NULL;
00723                 int secondMinP = maxPlayers; // the number of humans on that team
00724                 for ( int j = teams.Len()-1; j>=0; --j )
00725                 {
00726                     eTeam *t = teams(j);
00727 
00728                     if ( t->BalanceThisTeam() )
00729                     {
00730                         int humans = t->NumHumanPlayers();
00731 
00732                         if ( humans < secondMinP && t != min )
00733                         {
00734                             secondMinP = humans;
00735                             second = t;
00736                         }
00737                     }
00738                 }
00739 
00740                 if ( second )
00741                 {
00742                     // put the player into the second smallest team, overriding balancing settings (they're likely to be in the way )
00743                     int imbBackup = second->maxImbalanceLocal;
00744                     second->maxImbalanceLocal = 99999;
00745                     pni->SetTeamForce( 0 );
00746                     pni->UpdateTeamForce();
00747                     pni->SetTeamForce( second );
00748                     pni->UpdateTeamForce();
00749                     second->maxImbalanceLocal = imbBackup;
00750 
00751                     balance = false;
00752                 }
00753                 else
00754                 {
00755                     // no room, kick the player out
00756                     pni->SetTeamForce( NULL );
00757                     pni->UpdateTeamForce();
00758 
00759                     balance = false;
00760                 }
00761             }
00762         }
00763         else if ( numTeams < minTeams )
00764         {
00765             // too few teams. Create a new one
00766             eTeam *newTeam = tNEW( eTeam );
00767             teams.Add( newTeam, newTeam->listID );
00768             newTeam->UpdateProperties();
00769 
00770             balance = false;
00771         }
00772         else if ( ( ( maxP - maxImbalance > minP || maxP > maxPlayers ) && minP < maxPlayers ) || ( minP == 0 && maxP > 1 ) )
00773         {
00774             // teams are unbalanced; move one player from the strongest team to the weakest
00775             if ( max )
00776             {
00777                 ePlayerNetID* unluckyOne = max->YoungestHumanPlayer();
00778                 unluckyOne->SetTeamForce( min );
00779                 unluckyOne->UpdateTeamForce();
00780                 balance = false;
00781             }
00782         }
00783         else if ( maxP > maxPlayers )
00784         {
00785             // teams too large. create a new team and put the last joiner of the strongest team in
00786             eTeam* newTeam = tNEW( eTeam );
00787             if ( max )
00788             {
00789                 ePlayerNetID* unluckyOne = max->YoungestHumanPlayer();
00790                 unluckyOne->SetTeamForce( newTeam );
00791                 unluckyOne->UpdateTeamForce();
00792 
00793                 balance = false;
00794             }
00795         }
00796     }
00797 }
00798 
00799 // static tList<eTeam> se_ColoredTeams;
00800 
00801 static eTeam * se_ColoredTeams[TEAMCOLORS]={0,0,0,0,0,0,0,0};
00802 
00803 // inquire or set the ability to use a color as a team name
00804 bool eTeam::NameTeamAfterColor ( bool wish )
00805 {
00806     if ( wish && colorID < 0 )
00807     {
00808         for ( int i = 0; i < TEAMCOLORS; ++i )
00809         {
00810             if ( !se_ColoredTeams[i] )
00811             {
00812                 se_ColoredTeams[i] = this;
00813                 colorID = i;
00814                 return true;
00815             }
00816         }
00817     }
00818 
00819     if ( !wish && colorID >= 0 )
00820     {
00821         se_ColoredTeams[ colorID ] = 0;
00822         colorID = -1;
00823     }
00824 
00825     return colorID >= 0;
00826 }
00827 
00828 // register a player
00829 void eTeam::AddPlayer    ( ePlayerNetID* player )
00830 {
00831     tASSERT( player );
00832 
00833     tJUST_CONTROLLED_PTR< eTeam > keepalive( this );
00834 
00835     if ( ! PlayerMayJoin( player ) )
00836         return;
00837 
00838     tJUST_CONTROLLED_PTR< eTeam > oldTeam( player->currentTeam );
00839     tString oldTeamName("Old Team (BUG)");
00840     if ( player->currentTeam )
00841     {
00842         oldTeamName = oldTeam->Name();
00843         player->currentTeam->RemovePlayerDirty( player );
00844         oldTeam->UpdateProperties();
00845         oldTeam->UpdateAppearance();
00846     }
00847 
00848     players.Add( player, player->teamListID );
00849     // bool teamChange = player->currentTeam;
00850     player->currentTeam = this;
00851     player->timeJoinedTeam = tSysTimeFloat();
00852 
00853     UpdateProperties();
00854 
00855     // print the new entry
00856     if ( players.Len() <= 1 )
00857     {
00858         UpdateAppearance();
00859 
00860         /*
00861         // print creation message
00862         tOutput message;
00863         message.SetTemplateParameter(1, player->GetName() );
00864         message.SetTemplateParameter(2, Name() );
00865         message << "$player_creates_team";
00866 
00867         sn_ConsoleOut( message );
00868         */
00869     }
00870 
00871     // anounce joining if there are is more than one member now or if the team is color-named
00872     if ( sn_GetNetState() != nCLIENT )
00873     {
00874         // get colored player name
00875         tColoredString playerName;
00876         playerName << *player << tColoredString::ColorString(.5,1,.5);
00877 
00878         // tString playerNameNoColor = tColoredString::RemoveColors( player->GetName() );
00879 
00880         if ( ( players.Len() > 1 || colorID >= 0 ) && IsHuman() )
00881         {
00882             if ( oldTeam && oldTeam->players.Len() >= 1 )
00883             {
00884                 sn_ConsoleOut( tOutput( "$player_changes_team",
00885                                         playerName,
00886                                         Name(),
00887                                         oldTeamName ) );
00888             }
00889             else
00890             {
00891                 // print join message
00892                 sn_ConsoleOut( tOutput( "$player_joins_team_start",
00893                                         playerName,
00894                                         Name() ) );
00895             }
00896         }
00897         else if ( oldTeam )
00898         {
00899             // or at least the leaving of the old team
00900             if ( oldTeam->players.Len() > 0 )
00901                 sn_ConsoleOut( tOutput( "$player_leaves_team",
00902                                         playerName,
00903                                         oldTeamName ) );
00904         }
00905         else
00906         {
00907             // announce a generic join
00908             sn_ConsoleOut( tOutput( "$player_entered_game", playerName ) );
00909         }
00910     }
00911 
00912     if ( listID < 0 )
00913     {
00914         teams.Add ( this, listID );
00915     }
00916 
00917     player->UpdateName();
00918 }
00919 
00920 // register a player the dirty way
00921 void eTeam::AddPlayerDirty   ( ePlayerNetID* player )
00922 {
00923     tASSERT( player );
00924 
00925     if ( player->currentTeam )
00926     {
00927         player->currentTeam->RemovePlayerDirty ( player );
00928     }
00929 
00930     players.Add( player, player->teamListID );
00931     player->currentTeam = player->nextTeam = this;
00932     player->timeJoinedTeam = tSysTimeFloat();
00933 
00934     if ( listID < 0 )
00935     {
00936         teams.Add ( this, listID );
00937     }
00938 
00939     player->UpdateName();
00940 }
00941 
00942 // deregister a player
00943 void eTeam::RemovePlayerDirty ( ePlayerNetID* player )
00944 {
00945     tASSERT( player );
00946     tASSERT( player->currentTeam == this );
00947 
00948     // remove player without shuffling the list
00949     for ( int i = players.Len()-2; i >= player->teamListID; --i )
00950     {
00951         ePlayerNetID * shuffle = players(i);
00952         players.Remove( shuffle, shuffle->teamListID );
00953         players.Add   ( shuffle, shuffle->teamListID );
00954     }
00955     tASSERT ( player->teamListID == players.Len()-1 );
00956 
00957     // now player has been shuffled to the back of the list without disturbing
00958     // the order of the other players and can be removed
00959     players.Remove ( player, player->teamListID );
00960     player->currentTeam = NULL;
00961 
00962     // remove team from list
00963     if ( listID >= 0 && players.Len() == 0 )
00964     {
00965         teams.Remove( this, listID );
00966 
00967         // don't forget the colored team list
00968         if ( colorID >= 0 )
00969         {
00970             se_ColoredTeams[ colorID ] = 0;
00971             colorID = -1;
00972         }
00973     }
00974 }
00975 
00976 // deregister a player
00977 void eTeam::RemovePlayer ( ePlayerNetID* player )
00978 {
00979     tCONTROLLED_PTR( eTeam ) safety;
00980     safety = this;                                              // avoid premature destruction of this team
00981 
00982     RemovePlayerDirty( player );
00983 
00984     player->UpdateName();
00985 
00986     // get colored player name
00987     tColoredString playerName;
00988     playerName << *player << tColoredString::ColorString(1,.5,.5);
00989 
00990     if ( sn_GetNetState() != nCLIENT )
00991     {
00992         if ( players.Len() > 0 || colorID >= 0  )
00993         {
00994             sn_ConsoleOut( tOutput( "$player_leaves_team",
00995                                     playerName,
00996                                     Name() ) );
00997         }
00998         else
00999         {
01000             // announce a generic leave
01001             sn_ConsoleOut( tOutput( "$player_leaving_game", playerName ) );
01002         }
01003     }
01004 
01005     UpdateProperties();
01006 
01007     // trigger enforcement of strong constraints on next balancing if the player is quitting
01008     if ( enforceRulesOnQuit && 0 == player->nextTeam && nCLIENT != sn_GetNetState() )
01009         imbalance = -10;
01010 }
01011 
01012 
01013 // see if the given player may join this team
01014 bool eTeam::PlayerMayJoin( const ePlayerNetID* player ) const
01015 {
01016     // a player who is already on the team can "join" the team
01017     if (player->currentTeam==this)
01018         return true;
01019 
01020     // AI players are always allowed to join, the logic that tries to put the AI into
01021     // this team is responsible for checking
01022     if ( !player->IsHuman() )
01023         return true;
01024 
01025     // suspended players cannot join
01026     if ( player->GetSuspended() > 0 )
01027         return false;
01028 
01029     // check for invitations. Not with those shoes!
01030     if ( IsLocked() && !IsInvited( player ) )
01031     {
01032         return false;
01033     }
01034 
01035     int maxInb = maxImbalanceLocal;
01036 
01037     int minP = 10000; // minimum number of humans in a team after the player left
01038     if ( bool(player) && bool(player->currentTeam) )
01039     {
01040         minP = player->currentTeam->NumHumanPlayers() - 1;
01041 
01042         // allow leaving a team if it vanishes and the number of teams does not shrink below the minimum team count
01043         if ( minP == 0 && teams.Len() > minTeams )
01044             minP = 10000;
01045     }
01046 
01047     for ( int i = teams.Len()-1; i>=0; --i )
01048     {
01049         eTeam *t = teams(i);
01050 
01051         if ( t->BalanceThisTeam() )
01052         {
01053             int humans = t->NumHumanPlayers();
01054 
01055             if ( humans < minP )
01056             {
01057                 minP = humans;
01058             }
01059         }
01060     }
01061 
01062     int maxPlayers = maxPlayersLocal;
01063 
01064     // we must have room           and the joining must not cause huge imbalance
01065     if ( numHumans < maxPlayers && ( sn_GetNetState() != nSERVER || minP + maxInb > numHumans ) )
01066         return true;
01067 
01068     // always allow circular swapping of players
01069     {
01070         std::set< eTeam const * > swapTargets; // teams players from this team want to swap into (recursively, if someone wants to swap to B and someone else from B wants to swap to C, C is on the list, too)
01071         swapTargets.insert( this );
01072 
01073         bool goon = true;
01074         while ( goon )
01075         {
01076             goon = false;
01077             for ( std::set< eTeam const * >::iterator iter = swapTargets.begin(); iter != swapTargets.end(); ++iter )
01078             {
01079                 eTeam const * team = *iter;
01080                 for ( int i = team->players.Len()-1; i>=0; --i )
01081                 {
01082                     ePlayerNetID * otherPlayer = team->players(i);
01083                     eTeam * swapTeam = otherPlayer->NextTeam();
01084                     if ( swapTeam && swapTeam != otherPlayer->CurrentTeam() && swapTargets.find( swapTeam ) == swapTargets.end() )
01085                     {
01086                         goon = true;
01087                         swapTargets.insert( swapTeam );
01088 
01089                         // early return if we find a closed swap chain
01090                         if ( swapTeam == player->CurrentTeam() )
01091                             return true;
01092                     }
01093                 }
01094             }
01095         }
01096     }
01097 
01098     // sorry, no way
01099     return false;
01100 }
01101 
01102 
01103 // is it allowed to create a new team?
01104 bool eTeam::NewTeamAllowed      ()
01105 {
01106     return teams.Len() < maxTeams;
01107 }
01108 
01109 // if this flag is set, the center player is the boss of a team.
01110 // if it isn't set, the oldest player is boss.
01111 static bool se_centerPlayerIsBoss=true;
01112 static tSettingItem<bool> se_centerPlayerIsBossConf("TEAM_CENTER_IS_BOSS", se_centerPlayerIsBoss );
01113 
01114 // the oldest player
01115 ePlayerNetID*   eTeam::OldestPlayer     (               ) const
01116 {
01117     ePlayerNetID* ret = NULL;
01118 
01119     for (int i= players.Len(); i>=0; i--)
01120     {
01121         ePlayerNetID* p = players(i);
01122         if (!ret || ret->timeJoinedTeam > p->timeJoinedTeam || se_centerPlayerIsBoss )
01123         {
01124             ret = p;
01125         }
01126     }
01127 
01128     return ret;
01129 }
01130 
01131 // the oldest human player
01132 ePlayerNetID*   eTeam::OldestHumanPlayer(               ) const
01133 {
01134     ePlayerNetID* ret = NULL;
01135 
01136     for (int i= players.Len()-1; i>=0; i--)
01137     {
01138         ePlayerNetID* p = players(i);
01139         if ( p->IsHuman() && ( !ret || ret->timeJoinedTeam > p->timeJoinedTeam || se_centerPlayerIsBoss ) )
01140         {
01141             ret = p;
01142         }
01143     }
01144 
01145     return ret;
01146 }
01147 
01148 // the oldest AI player
01149 ePlayerNetID*   eTeam::OldestAIPlayer   (               ) const
01150 {
01151     ePlayerNetID* ret = NULL;
01152 
01153     for (int i= players.Len()-1; i>=0; i--)
01154     {
01155         ePlayerNetID* p = players(i);
01156         if ( ( !p->IsHuman() ) && ( !ret || ret->timeJoinedTeam > p->timeJoinedTeam || se_centerPlayerIsBoss ) )
01157         {
01158             ret = p;
01159         }
01160     }
01161 
01162     return ret;
01163 }
01164 
01165 // the youngest player
01166 ePlayerNetID*   eTeam::YoungestPlayer   (               ) const
01167 {
01168     ePlayerNetID* ret = NULL;
01169 
01170     for (int i= players.Len(); i>=0; i--)
01171     {
01172         ePlayerNetID* p = players(i);
01173         if (!ret || ret->timeJoinedTeam < p->timeJoinedTeam )
01174         {
01175             ret = p;
01176         }
01177     }
01178 
01179     return ret;
01180 }
01181 
01182 // the youngest human player
01183 ePlayerNetID*   eTeam::YoungestHumanPlayer(             ) const
01184 {
01185     ePlayerNetID* ret = NULL;
01186 
01187     for (int i= players.Len()-1; i>=0; i--)
01188     {
01189         ePlayerNetID* p = players(i);
01190         if ( p->IsHuman() && ( !ret || ret->timeJoinedTeam < p->timeJoinedTeam ) )
01191         {
01192             ret = p;
01193         }
01194     }
01195 
01196     return ret;
01197 }
01198 
01199 // the youngest AI player
01200 ePlayerNetID*   eTeam::YoungestAIPlayer (               ) const
01201 {
01202     ePlayerNetID* ret = NULL;
01203 
01204     for (int i= players.Len()-1; i>=0; i--)
01205     {
01206         ePlayerNetID* p = players(i);
01207         if ( ( !p->IsHuman() ) && ( !ret || ret->timeJoinedTeam < p->timeJoinedTeam ) )
01208         {
01209             ret = p;
01210         }
01211     }
01212 
01213     return ret;
01214 }
01215 
01216 // is anyone still alive?
01217 bool eTeam::Alive ( ) const
01218 {
01219     for (int i= players.Len()-1; i>=0; --i)
01220     {
01221         ePlayerNetID* p = players(i);
01222         if ( p->Object() && p->Object()->Alive() )
01223         {
01224             return true;
01225         }
01226     }
01227 
01228     return false;
01229 }
01230 
01231 // how many of the current players are currently alive?
01232 int eTeam::AlivePlayers ( ) const
01233 {
01234     int ret = 0;
01235     for (int i= players.Len()-1; i>=0; --i)
01236     {
01237         ePlayerNetID* p = players(i);
01238         if ( p->Object() && p->Object()->Alive() )
01239         {
01240             ret++;
01241         }
01242     }
01243 
01244     return ret;
01245 }
01246 
01247 
01248 // print out an understandable name in to s
01249 void eTeam::PrintName(tString &s) const
01250 {
01251     s << "Team " << name;
01252 }
01253 
01254 
01255 
01256 // we must not transmit an object that contains pointers to non-transmitted objects.
01257 // this function is supposed to check that.
01258 bool eTeam::ClearToTransmit(int user) const
01259 {
01260     return true;
01261 }
01262 
01263 
01264 // syncronisation functions:
01265 
01266 // store sync message in m
01267 void eTeam::WriteSync(nMessage &m)
01268 {
01269     m << r;
01270     m << g;
01271     m << b;
01272     m << name;
01273     m << maxPlayersLocal;
01274     m << maxImbalanceLocal;
01275     m << score;
01276 }
01277 
01278 
01279 // guess what
01280 void eTeam::ReadSync(nMessage &m)
01281 {
01282     m >> r;
01283     m >> g;
01284     m >> b;
01285     m >> name;
01286     m >> maxPlayersLocal;
01287     m >> maxImbalanceLocal;
01288     m >> score;
01289 
01290     // update colored player names
01291     if ( sn_GetNetState() != nSERVER )
01292     {
01293         for ( int i = players.Len()-1; i>=0; --i )
01294         {
01295             players(i)->UpdateName();
01296         }
01297     }
01298 }
01299 
01300 
01301 // is the message newer than the last accepted sync
01302 bool eTeam::SyncIsNew(nMessage &m)
01303 {
01304     return true;
01305 }
01306 
01307 
01308 // the extra information sent on creation:
01309 // store sync message in m
01310 // the information written by this function should
01311 // be read from the message in the "message"- connstructor
01312 void eTeam::WriteCreate(nMessage &m)
01313 {
01314     nNetObject::WriteCreate(m);
01315 }
01316 
01317 
01318 // control functions:
01319 // receives the control message. the data written to the message created
01320 // by *NewControlMessage() can be read directly from m.
01321 void eTeam::ReceiveControlNet(nMessage &m)
01322 {
01323 }
01324 
01325 
01326 
01327 // con/desstruction
01328 // default constructor
01329 eTeam::eTeam()
01330         :colorID(-1),listID(-1)
01331 {
01332     score = 0;
01333     locked_ = false;
01334     maxPlayersLocal = maxPlayers;
01335     maxImbalanceLocal = maxImbalance;
01336     r = g = b = 32; // initialize color so it will be updated, guaranteed
01337     Update();
01338 }
01339 
01340 
01341 // remote constructor
01342 eTeam::eTeam(nMessage &m)
01343         :nNetObject( m ),
01344         colorID(-1),listID(-1)
01345 {
01346     score = 0;
01347     locked_ = false;
01348     maxPlayersLocal = maxPlayers;
01349     maxImbalanceLocal = maxImbalance;
01350     r = g = b = 32; // initialize color so it will be updated, guaranteed
01351     Update();
01352 }
01353 
01354 // destructor
01355 eTeam::~eTeam()
01356 {
01357     if ( listID >= 0 )
01358         teams.Remove( this, listID );
01359 
01360     if ( colorID >= 0 )
01361     {
01362         se_ColoredTeams[ colorID ] = 0;
01363         colorID = -1;
01364     }
01365 
01366     // revoke all invitations
01367     for ( int i = se_PlayerNetIDs.Len()-1; i >= 0; --i )
01368     {
01369         se_PlayerNetIDs(i)->invitations_.erase( this );
01370     }
01371 }
01372 // *******************************************************************************
01373 // *
01374 // *    Enemies
01375 // *
01376 // *******************************************************************************
01382 // *******************************************************************************
01383 
01384 bool eTeam::Enemies( eTeam const * team, ePlayerNetID const * player )
01385 {
01386     // nonexistant parties can't be enemies
01387     if (!player || !team)
01388         return false;
01389 
01390     // check if the player is a team member
01391     if ( player->CurrentTeam() == team )
01392         return false;
01393 
01394     // check if the player has a team
01395     if ( !player->currentTeam )
01396         return false;
01397 
01398     // go through player list; the player is an enemy if he is at least enemy with one of the menbers
01399     for (int i = team->players.Len()-1; i>=0; --i)
01400         if ( ePlayerNetID::Enemies( team->players(i), player ) )
01401             return true;
01402 
01403     return false;
01404 }
01405 
01406 // *******************************************************************************
01407 // *
01408 // *    Enemies
01409 // *
01410 // *******************************************************************************
01416 // *******************************************************************************
01417 
01418 bool eTeam::Enemies( eTeam const * team1, eTeam const * team2 )
01419 {
01420     // nonexistant parties can't be enemies
01421     if (!team1 || !team2 || team1 == team2)
01422         return false;
01423 
01424     // go through player list; if one is an enemy, so is the team
01425     for (int i = team2->players.Len()-1; i>=0; --i)
01426         if ( Enemies( team1, team2->players(i) ) )
01427             return true;
01428 
01429     return false;
01430 }
01431 
01432 // *******************************************************************************
01433 // *
01434 // *    SwapPlayers
01435 // *
01436 // *******************************************************************************
01441 // *******************************************************************************
01442 
01443 void eTeam::SwapPlayers( ePlayerNetID * player1, ePlayerNetID * player2 )
01444 {
01445     tASSERT( player1 );
01446     tASSERT( player2 );
01447 
01448     // swap IDs
01449     int id3 = player1->teamListID;
01450     player1->teamListID = player2->teamListID;
01451     player2->teamListID = id3;
01452 
01453     // adjust pointers from teams
01454     eTeam * team2 = player1->CurrentTeam();
01455     eTeam * team1 = player2->CurrentTeam();
01456 
01457     if ( team2 )
01458         team2->players[player2->teamListID] = player2;
01459     if ( team1 )
01460         team1->players[player1->teamListID] = player1;
01461 
01462     // swap teams
01463     player1->currentTeam = team1;
01464     player2->currentTeam = team2;
01465 
01466     // swap next teams (if current teams differ)
01467     team1 = player2->NextTeam();
01468     team2 = player1->NextTeam();
01469     if ( player1->currentTeam != player2->currentTeam )
01470     {
01471         player1->nextTeam = team1;
01472         player2->nextTeam = team2;
01473     }
01474 }
01475 
01476 // *******************************************************************************
01477 // *
01478 // *    Shuffle
01479 // *
01480 // *******************************************************************************
01485 // *******************************************************************************
01486 
01487 void eTeam::Shuffle( int startID, int stopID )
01488 {
01489     tASSERT( 0 <= startID && startID < players.Len() );
01490     tASSERT( 0 <= stopID && stopID < players.Len() );
01491 
01492     if ( startID == stopID )
01493         return;
01494 
01495     tOutput message( "$team_shuffle", players[startID]->GetName(), startID+1, stopID+1 );
01496     sn_ConsoleOut( message );
01497 
01498     // simply swap the one player over all the players in between.
01499     while ( startID < stopID )
01500     {
01501         SwapPlayers( players[startID], players[startID+1] );
01502         startID++;
01503     }
01504     while ( startID > stopID )
01505     {
01506         SwapPlayers( players[startID], players[startID-1] );
01507         startID--;
01508     }
01509 }
01510 
01511 

Generated on Sat Mar 15 22:55:47 2008 for Armagetron Advanced by  doxygen 1.5.4