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 file by guru3/tank program 00026 00027 */ 00028 00029 #include "gStatistics.h" 00030 #include "tConfiguration.h" 00031 00032 //0 is flat file 00033 //1 will be database 00034 static int statOutputType = 0; 00035 static tSettingItem<int> sot_ci("STAT_OUTPUT", statOutputType); 00036 00037 gStatistics* gStats; 00038 00039 gStatistics::gStatistics() 00040 { 00041 highscores = new gStatList("highscores", statOutputType); 00042 won_rounds = new gStatList("won_rounds", statOutputType); 00043 won_matches = new gStatList("won_matches", statOutputType); 00044 // ladder = new gStatList("ladder", statOutputType); 00045 kills = new gStatList("kills", statOutputType); 00046 deaths = new gStatList("deaths", statOutputType); 00047 } 00048 00049 gStatistics::~gStatistics() 00050 { 00051 delete highscores; 00052 delete won_rounds; 00053 delete won_matches; 00054 // delete ladder; 00055 delete kills; 00056 delete deaths; 00057 } 00058 00059 /* original code stripped out to be replaced with the new stuff.... 00060 00061 class gHighscoresBase{ 00062 int id; 00063 static tList<gHighscoresBase> highscoreList; 00064 protected: 00065 tArray<tString> highName; 00066 00067 char* highscore_file; 00068 tOutput desc; 00069 int maxSize; 00070 00071 // find the player at position i 00072 ePlayerNetID *online(int p){ 00073 for (int i=se_PlayerNetIDs.Len()-1;i>=0;i--) 00074 if (se_PlayerNetIDs(i)->IsHuman() && !strcmp(se_PlayerNetIDs(i)->GetUserName(),highName[p])) 00075 return se_PlayerNetIDs(i); 00076 00077 return NULL; 00078 } 00079 00080 // i is the active player, j the passive one 00081 virtual void swap_entries(int i,int j){ 00082 Swap(highName[i],highName[j]); 00083 00084 // swap: i is now the passive player 00085 00086 // send the poor guy who just dropped a message 00087 ePlayerNetID *p=online(i); 00088 if (p){ 00089 tColoredString name; 00090 name << *p << tColoredString::ColorString(1,.5,.5); 00091 00092 tOutput message; 00093 message.SetTemplateParameter(1, static_cast<const char *>(name)); 00094 message.SetTemplateParameter(2, static_cast<const char *>(highName[j])); 00095 message.SetTemplateParameter(3, i+1); 00096 message.SetTemplateParameter(4, desc); 00097 00098 if (i<j) 00099 message << "$league_message_rose" ; 00100 else 00101 message << "$league_message_dropped" ; 00102 00103 message << "\n"; 00104 00105 tString s; 00106 s << message; 00107 sn_ConsoleOut(s,p->Owner()); 00108 // con << message; 00109 } 00110 00111 } 00112 00113 void load_Name(std::istream &s,int i){ 00114 char c=' '; 00115 while (s.good() && !s.eof() && isspace(c)) 00116 s.get(c); 00117 s.putback(c); 00118 00119 // read and filter name 00120 tString name; 00121 name.ReadLine( s ); 00122 ePlayerNetID::FilterName( name, highName[i] ); 00123 } 00124 00125 void save_Name(std::ostream &s,int i){ 00126 s << highName[i]; 00127 } 00128 00129 00130 public: 00131 00132 virtual void Save()=0; 00133 virtual void Load()=0; 00134 00135 // returns if i should stay above j 00136 virtual bool inorder(int i,int j)=0; 00137 00138 void sort(){ 00139 // since single score items travel up the 00140 // score ladder, this is the most efficient sort algorithm: 00141 00142 for(int i=1;i<highName.Len();i++) 00143 for(int j=i;j>=1 && !inorder(j-1,j);j--) 00144 swap_entries(j,j-1); 00145 } 00146 00147 00148 int checkPos(int found){ 00149 // move him up 00150 int newpos=found; 00151 while(newpos>0 && !inorder(newpos-1,newpos)){ 00152 swap_entries(newpos,newpos-1); 00153 newpos--; 00154 } 00155 00156 // move him down 00157 while(newpos<highName.Len()-1 && !inorder(newpos,newpos+1)){ 00158 swap_entries(newpos,newpos+1); 00159 newpos++; 00160 } 00161 00162 //Save(); 00163 00164 return newpos; 00165 } 00166 00167 int Find(const char *name,bool force=false){ 00168 int found=highName.Len(); 00169 for(int i=found-1;i>=0 ;i--) 00170 if(highName[i].Len()<=1 || !strcmp(highName[i],name)){ 00171 found=i; 00172 } 00173 00174 if (force && highName[found].Len()<=1) 00175 highName[found]=name; 00176 00177 return found; 00178 } 00179 00180 gHighscoresBase(char *name,char *sd,int max=0) 00181 :id(-1),highscore_file(name),desc(sd),maxSize(max){ 00182 highscoreList.Add(this,id); 00183 } 00184 00185 virtual ~gHighscoresBase(){ 00186 highscoreList.Remove(this,id); 00187 } 00188 00189 virtual void greet_this(ePlayerNetID *p,tOutput &o){ 00190 // tOutput o; 00191 00192 int f=Find(p->GetUserName())+1; 00193 int l=highName.Len(); 00194 00195 o.SetTemplateParameter(1, f); 00196 o.SetTemplateParameter(2, l); 00197 o.SetTemplateParameter(3, desc); 00198 00199 if (l>=f) 00200 o << "$league_message_greet"; 00201 else 00202 o << "$league_message_greet_new"; 00203 00204 // s << o; 00205 } 00206 00207 static void Greet(ePlayerNetID *p,tOutput &o){ 00208 o << "$league_message_greet_intro"; 00209 for(int i=highscoreList.Len()-1;i>=0;i--){ 00210 highscoreList(i)->greet_this(p,o); 00211 if (i>1) 00212 o << "$league_message_greet_sep" << " "; 00213 if (i==1) 00214 o << " " << "$league_message_greet_lastsep" << " "; 00215 } 00216 o << ".\n"; 00217 } 00218 00219 static void SaveAll(){ 00220 if ( !tRecorder::IsRunning() ) 00221 for(int i=highscoreList.Len()-1;i>=0;i--) 00222 highscoreList(i)->Save(); 00223 } 00224 00225 static void LoadAll(){ 00226 if ( !tRecorder::IsRunning() ) 00227 for(int i=highscoreList.Len()-1;i>=0;i--) 00228 highscoreList(i)->Load(); 00229 } 00230 }; 00231 00232 00233 tString GreetHighscores() 00234 { 00235 tOutput o; 00236 gHighscoresBase::Greet(eCallbackGreeting::Greeted(),o); 00237 tString s; 00238 s << o; 00239 return s; 00240 } 00241 00242 static eCallbackGreeting g(GreetHighscores); 00243 00244 tList<gHighscoresBase> gHighscoresBase::highscoreList; 00245 00246 template<class T>class highscores: public gHighscoresBase{ 00247 protected: 00248 tArray<T> high_score; 00249 00250 virtual void swap_entries(int i,int j){ 00251 Swap(high_score[i],high_score[j]); 00252 gHighscoresBase::swap_entries(i,j); 00253 } 00254 00255 public: 00256 virtual void Load(){ 00257 std::ifstream s; 00258 00259 if ( tDirectories::Var().Open ( s, highscore_file ) ) 00260 { 00261 int i=0; 00262 while (s.good() && !s.eof()) 00263 { 00264 s >> high_score[i]; 00265 load_Name(s,i); 00266 // con << highName[i] << " " << high_score[i] << '\n'; 00267 i++; 00268 } 00269 } 00270 } 00271 00272 // returns if i should stay above j 00273 virtual bool inorder(int i,int j) 00274 { 00275 return (highName[j].Len()<=1 || high_score[i]>=high_score[j]); 00276 } 00277 00278 00279 virtual void Save(){ 00280 std::ofstream s; 00281 00282 sort(); 00283 00284 if ( tDirectories::Var().Open ( s, highscore_file ) ) 00285 { 00286 int i=0; 00287 int max=high_score.Len(); 00288 if (maxSize && max>maxSize) 00289 max=maxSize; 00290 while (highName[i].Len()>1 && i<max){ 00291 tString mess; 00292 mess << high_score[i]; 00293 mess.SetPos(10, false ); 00294 s << mess; 00295 save_Name(s,i); 00296 s << '\n'; 00297 i++; 00298 //std::cout << mess; 00299 //save_Name(std::cout,i); 00300 //std::cout << '\n'; 00301 } 00302 } 00303 } 00304 00305 void checkPos(int found,const tString &name,T score){ 00306 tOutput message; 00307 // find the name in the list 00308 bool isnew=false; 00309 00310 message.SetTemplateParameter(1, name); 00311 message.SetTemplateParameter(2, desc); 00312 message.SetTemplateParameter(3, score); 00313 00314 if (highName[found].Len()<=1){ 00315 highName[found]=name; 00316 message << "$highscore_message_entered"; 00317 high_score[found]=score; 00318 isnew=true; 00319 } 00320 else if (score>high_score[found]){ 00321 message << "$highscore_message_improved"; 00322 high_score[found]=score; 00323 } 00324 else 00325 return; 00326 00327 // move him up 00328 int newpos=gHighscoresBase::checkPos(found); 00329 00330 message.SetTemplateParameter(4, newpos + 1); 00331 00332 if (newpos!=found || isnew) 00333 if (newpos==0) 00334 message << "$highscore_message_move_top"; 00335 else 00336 message << "$highscore_message_move_pos"; 00337 else 00338 if (newpos==0) 00339 message << "$highscore_message_stay_top"; 00340 else 00341 message << "$highscore_message_stay_pos"; 00342 00343 message << "\n"; 00344 00345 ePlayerNetID *p=online(newpos); 00346 //con << message; 00347 if (p) 00348 sn_ConsoleOut(tString(message),p->Owner()); 00349 //Save(); 00350 } 00351 00352 void Add( ePlayerNetID* player,T AddScore) 00353 { 00354 tASSERT( player ); 00355 tString const & name = player->GetUserName(); 00356 int f=Find(name,true); 00357 checkPos(f,name,AddScore+high_score[f]); 00358 } 00359 00360 void Add( eTeam* team,T AddScore) 00361 { 00362 if ( team->NumHumanPlayers() > 0 ) 00363 { 00364 for ( int i = team->NumPlayers() - 1 ; i>=0; --i ) 00365 { 00366 ePlayerNetID* player = team->Player( i ); 00367 if ( player->IsHuman() ) 00368 { 00369 this->Add( player, AddScore ); 00370 } 00371 } 00372 } 00373 } 00374 00375 void Check(const ePlayerNetID* player,T score){ 00376 tASSERT( player ); 00377 tString name = player->GetUserName(); 00378 int len = high_score.Len(); 00379 if (len<=0 || score>high_score[len-1]){ 00380 // find the name in the list 00381 int found=Find(name,true); 00382 checkPos(found,name,score); 00383 } 00384 } 00385 00386 highscores(char *name,char *sd,int max=0) 00387 :gHighscoresBase(name,sd,max){ 00388 // Load(); 00389 } 00390 00391 virtual ~highscores(){ 00392 // Save(); 00393 } 00394 }; 00395 00396 00397 00398 static REAL ladder_min_bet=1; 00399 static tSettingItem<REAL> ldd_mb("LADDER_MIN_BET", 00400 ladder_min_bet); 00401 00402 static REAL ladder_perc_bet=10; 00403 static tSettingItem<REAL> ldd_pb("LADDER_PERCENT_BET", 00404 ladder_perc_bet); 00405 00406 static REAL ladder_tax=1; 00407 static tSettingItem<REAL> ldd_tex("LADDER_TAX", 00408 ladder_tax); 00409 00410 static REAL ladder_lose_perc_on_load=.2; 00411 static tSettingItem<REAL> ldd_lpl("LADDER_LOSE_PERCENT_ON_LOAD", 00412 ladder_lose_perc_on_load); 00413 00414 static REAL ladder_lose_min_on_load=.2; 00415 static tSettingItem<REAL> ldd_lml("LADDER_LOSE_MIN_ON_LOAD", 00416 ladder_lose_min_on_load); 00417 00418 static REAL ladder_gain_extra=1; 00419 static tSettingItem<REAL> ldd_ga("LADDER_GAIN_EXTRA", 00420 ladder_gain_extra); 00421 00422 00423 class ladder: public highscores<REAL>{ 00424 public: 00425 virtual void Load(){ 00426 highscores<REAL>::Load(); 00427 00428 sort(); 00429 00430 int end=highName.Len(); 00431 00432 for(int i=highName.Len()-1;i>=0;i--){ 00433 00434 // make them lose some points 00435 00436 REAL loss=ladder_lose_perc_on_load*high_score[i]*.01; 00437 if (loss<ladder_lose_min_on_load) 00438 loss=ladder_lose_min_on_load; 00439 high_score[i]-=loss; 00440 00441 if (high_score[i]<0) 00442 end=i; 00443 } 00444 00445 // remove the bugggers with less than 0 points 00446 highName.SetLen(end); 00447 high_score.SetLen(end); 00448 } 00449 00450 void checkPos(int found,const tString &name,REAL score){ 00451 tOutput message; 00452 00453 message.SetTemplateParameter(1, name); 00454 message.SetTemplateParameter(2, desc); 00455 message.SetTemplateParameter(3, score); 00456 00457 // find the name in the list 00458 bool isnew=false; 00459 if (highName[found].Len()<=1){ 00460 highName[found]=name; 00461 message << "$ladder_message_entered"; 00462 high_score[found]=score; 00463 isnew=true; 00464 } 00465 else{ 00466 REAL diff=score-high_score[found]; 00467 message.SetTemplateParameter(5, static_cast<float>(fabs(diff))); 00468 00469 if (diff>0) 00470 message << "$ladder_message_gained"; 00471 else 00472 message << "$ladder_message_lost"; 00473 00474 high_score[found]=score; 00475 } 00476 00477 // move him up 00478 int newpos=gHighscoresBase::checkPos(found); 00479 00480 message.SetTemplateParameter(4, newpos + 1); 00481 00482 if (newpos!=found || isnew) 00483 if (newpos==0) 00484 message << "$ladder_message_move_top"; 00485 else 00486 message << "$ladder_message_move_pos"; 00487 else 00488 if (newpos==0) 00489 message << "$ladder_message_stay_top"; 00490 else 00491 message << "$ladder_message_stay_pos"; 00492 00493 message << "\n"; 00494 00495 ePlayerNetID *p=online(newpos); 00496 // con << message; 00497 if (p){ 00498 sn_ConsoleOut(tString(message),p->Owner()); 00499 } 00500 00501 // Save(); 00502 } 00503 00504 void Add(const tString &name,REAL AddScore){ 00505 int found=Find(name,true); 00506 checkPos(found,name,AddScore+high_score[found]); 00507 } 00508 00509 // ladder mechanics: what happens if someone wins? 00510 void winner( eTeam *winningTeam ){ 00511 tASSERT( winningTeam ); 00512 00513 // AI won? bail out. 00514 if ( winningTeam->NumHumanPlayers() <= 0 ) 00515 { 00516 return; 00517 } 00518 00519 // only do something in multiplayer mode 00520 int i; 00521 int count=0; 00522 00523 tArray<ePlayerNetID*> active; 00524 00525 for(i=se_PlayerNetIDs.Len()-1;i>=0;i--) 00526 { 00527 ePlayerNetID* p=se_PlayerNetIDs(i); 00528 00529 // only take enemies of the current winners (and the winners themselves) into the list 00530 if (p->IsHuman() && ( p->CurrentTeam() == winningTeam || eTeam::Enemies( winningTeam, p ) ) ) 00531 { 00532 count++; 00533 active[active.Len()] = p; 00534 } 00535 } 00536 00537 // only one active player? quit. 00538 if ( active.Len() <= 1 ) 00539 { 00540 return; 00541 } 00542 00543 // collect the bets 00544 tArray<int> nums; 00545 tArray<REAL> bet; 00546 00547 REAL pot=0; 00548 00549 for(i=active.Len()-1;i>=0;i--){ 00550 00551 nums[i]=Find(active(i)->GetUserName(),true); 00552 00553 if (high_score[nums[i]]<0) 00554 high_score[nums[i]]=0; 00555 00556 bet[i]=high_score[nums[i]]*ladder_perc_bet*.01; 00557 if (bet[i]<ladder_min_bet) 00558 bet[i]=ladder_min_bet; 00559 pot+=bet[i]; 00560 } 00561 00562 pot-=pot*ladder_tax*.01; // you have to pay to the bank :-) 00563 00564 // now bet[i] tells us how much player nums[i] has betted 00565 // and pot is the overall bet. Add something to it, prop to 00566 // the winners ping+ping charity: 00567 00568 // take the bet from the losers and give it to the winner 00569 for(i=active.Len()-1; i>=0; i--) 00570 { 00571 ePlayerNetID* player = active(i); 00572 if(player->CurrentTeam() == winningTeam ) 00573 { 00574 REAL pc=player->ping + player->pingCharity*.001; 00575 if (pc<0) 00576 pc=0; 00577 if (pc>1) // add sensible bounds 00578 pc=1; 00579 00580 REAL potExtra = pc*ladder_gain_extra; 00581 00582 if ( player->Object() && player->Object()->Alive() ) 00583 { 00584 potExtra *= 2.0f; 00585 } 00586 00587 Add(player->GetUserName(), ( pot / winningTeam->NumHumanPlayers() + potExtra ) - bet[i] ); 00588 } 00589 else 00590 { 00591 Add(player->GetUserName(),-bet[i]); 00592 } 00593 } 00594 } 00595 00596 ladder(char *name,char *sd,int max=0) 00597 :highscores<REAL>(name,sd,max){ 00598 // Load(); 00599 } 00600 00601 virtual ~ladder(){ 00602 // Save(); 00603 } 00604 }; 00605 00606 00607 static highscores<int> highscore("highscores.txt","$highscore_description",100); 00608 static highscores<int> highscore_won_rounds("won_rounds.txt", 00609 "$won_rounds_description"); 00610 static highscores<int> highscore_won_matches("won_matches.txt", 00611 "$won_matches_description"); 00612 static ladder highscore_ladder("ladder.txt", 00613 "$ladder_description"); 00614 00615 void check_hs(){ 00616 if (sg_singlePlayer) 00617 if(se_PlayerNetIDs.Len()>0 && se_PlayerNetIDs(0)->IsHuman()) 00618 highscore.Check(se_PlayerNetIDs(0),se_PlayerNetIDs(0)->Score()); 00619 } 00620 00621 gHighscoresBase::SaveAll(); 00622 00623 highscore_won_rounds.Add(eTeam::teams[winner-1] ,1); 00624 highscore_ladder.winner(eTeam::teams(winner-1)); 00625 00626 check_hs(); 00627 00628 highscore_won_matches.Add( winningTeam, 1 ); 00629 00630 check_hs(); 00631 00632 gHighscoresBase::LoadAll(); 00633 00634 gHighscoresBase::SaveAll(); 00635 00636 */