Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List

Game.cpp

00001 /*
00002 Copyright 2003 - 2005 Elliott Kleinrock, Dan Neely, Kurt W. Over, Damon Domjan
00003 
00004 This file is part of FreeStars, a free clone of the Stars! game.
00005 
00006 FreeStars is free software; you can redistribute it and/or modify
00007 it under the terms of the GNU General Public License as published by
00008 the Free Software Foundation; either version 2 of the License, or
00009 (at your option) any later version.
00010 
00011 FreeStars is distributed in the hope that it will be useful,
00012 but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 GNU General Public License for more details.
00015 
00016 You should have received a copy of the GNU General Public License
00017 along with FreeStars; if not, write to the Free Software
00018 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 The full GPL Copyright notice should be in the file COPYING.txt
00021 
00022 Contact:
00023 Email Elliott at 9jm0tjj02@sneakemail.com
00024 */
00025 
00026 #include "FSServer.h"
00027 
00028 #include "Hull.h"
00029 #include "RacialTrait.h"
00030 #include "Packet.h"
00031 
00032 #include "Creation.h"
00033 
00034 #include <stdlib.h>
00035 #include <time.h>
00036 
00037 #ifdef _DEBUG
00038 #define new DEBUG_NEW
00039 #endif
00040 
00041 Game * TheGame;
00042 
00043 Game::Game()
00044 {
00045         TurnPhase = 0;
00046         mCreation = NULL;
00047         mNumberOfPlayers = 0;
00048         mWHMin = 0;
00049         mWHMax = 0;
00050         mWHMinDistance = 0;
00051 
00052         VCWorlds = 0;
00053         VCTechLevel = 0;
00054         VCTechCount = 0;
00055         VCScore = 0;
00056         VCTimes2nd = 0.0;
00057         VCResources = 0;
00058         VCCapShips = 0;
00059         VCHighScoreAt = 0;
00060         VCCount = 0;
00061         VCStart = 0;
00062         mGameID = 0;
00063 }
00064 
00065 Game::~Game()
00066 {
00067         delete mCreation;
00068 
00069         deque<Component *>::iterator i1;
00070         for (i1 = mComponents.begin(); i1 != mComponents.end(); ++i1)
00071                 delete *i1;
00072         mComponents.clear();
00073 
00074         deque<Player *>::iterator i3;
00075         for (i3 = mPlayers.begin(); i3 != mPlayers.end(); ++i3)
00076                 delete *i3;
00077         mPlayers.clear();
00078 
00079         deque<RacialTrait *>::iterator i2;
00080         for (i2 = mPRTs.begin(); i2 != mPRTs.end(); ++i2)
00081                 delete *i2;
00082         mPRTs.clear();
00083 
00084         for (i2 = mLRTs.begin(); i2 != mLRTs.end(); ++i2)
00085                 delete *i2;
00086         mLRTs.clear();
00087 
00088         int i;
00089         for (i = 0; i < mMessages.size(); ++i)
00090                 delete mMessages[i];
00091 
00092         for (i = 0; i < mTopObjects.size(); ++i)
00093                 delete mTopObjects[i];
00094 
00095         mOrders.clear();
00096 }
00097 
00098 Message * Game::AddMessage(string type)
00099 {
00100         Message * mess = new Message(type);
00101         mMessages.push_back(mess);
00102         return mess;
00103 }
00104 
00105 void Game::StoreMessageLocation(const Location * loc)
00106 {
00107         int i;
00108         for (i = 0; i < mMessages.size(); ++i)
00109                 mMessages[i]->StoreMessageLocation(loc, NULL);
00110 
00111         for (i = 0; i < mNumberOfPlayers; ++i)
00112                 mPlayers[i]->StoreMessageLocation(loc);
00113 }
00114 
00115 bool Game::LoadCreation(const TiXmlNode * options)
00116 {
00117         const char * ptr;
00118         if (!options)
00119                 return false;
00120 
00121         // Slow Tech Advances game option
00122         ptr = GetString(options->FirstChild("TechAdvances"));
00123         if (ptr == NULL || strcmp(ptr, "Normal") == 0)
00124                 TechFactor = 1.0;
00125         else if (strcmp(ptr, "Slow") == 0)
00126                 TechFactor = 2.0;
00127         else {
00128                 double tmp = atof(ptr);
00129                 if (tmp > 0 && tmp <= 100)
00130                         TechFactor = tmp;
00131                 else
00132                         TechFactor = 1.0;
00133         }
00134 
00135         // No Random Events game option
00136         const TiXmlNode * child1;
00137         const TiXmlNode * child2;
00138         const TiXmlNode * child3;
00139         const TiXmlText * text;
00140 
00141         if (!TheGalaxy->ParseSize(options->FirstChild("Size")))
00142                 return false;
00143 
00144         RandomEventsStart = 0;
00145         child1 = options->FirstChild("RandomEvents");
00146         if (!child1)
00147                 mRandomEvents = RE_NONE;
00148         else {
00149                 child2 = child1->FirstChild("Start");
00150                 if (child2)
00151                         RandomEventsStart = GetLong(child2);
00152 
00153                 mRandomEvents = RE_NONE;
00154                 for (child2 = child1->FirstChild("Event"); child2; child2 = child2->NextSibling("Event")) {
00155                         child3 = child2->FirstChild();
00156                         if (!child3)
00157                                 continue;
00158 
00159                         if (child3->Type() != TiXmlNode::TEXT)
00160                                 continue;
00161 
00162                         text = child3->ToText();
00163                         if (strcmp(text->Value(), "Mystery Trader") == 0)
00164                                 mRandomEvents |= RE_MT;
00165                         else if (strcmp(text->Value(), "Artifacts") == 0)
00166                                 mRandomEvents |= RE_ARTIFACT;
00167                         else if (strcmp(text->Value(), "Comets") == 0)
00168                                 mRandomEvents |= RE_COMET;
00169                         else if (strcmp(text->Value(), "Wormholes") == 0) {
00170                                 mRandomEvents |= RE_WORMHOLE;
00171                                 child3 = child1->FirstChild("Wormholes");
00172                                 mWHMin = GetLong(child3->FirstChild("MinimumPairs"));
00173                                 mWHMax = GetLong(child3->FirstChild("MaximumPairs"));
00174                                 mWHMinDistance = GetLong(child3->FirstChild("MinDistance"));
00175                                 mWHMinDistance = max(mWHMinDistance, 10L);
00176                         } else if (strcmp(text->Value(), "All") == 0) {
00177                                 mRandomEvents = RE_ALL;
00178                                 break;
00179                         }
00180                 }
00181         }
00182 
00183         PublicScoreStart = 0;
00184         child1 = options->FirstChild("PublicScores");
00185         if (!child1)
00186                 PublicScore = PPS_RANK;
00187         else {
00188                 PublicScore = PPS_NONE;
00189                 PublicScoreStart = 20;
00190                 child2 = child1->FirstChild("Start");
00191                 if (child2)
00192                         PublicScoreStart = GetLong(child2);
00193 
00194                 for (child2 = child1->FirstChild("Category"); child2; child2 = child2->NextSibling("Category")) {
00195                         child3 = child2->FirstChild();
00196                         if (!child3)
00197                                 continue;
00198 
00199                         if (child3->Type() != TiXmlNode::TEXT)
00200                                 continue;
00201 
00202                         text = child3->ToText();
00203                         if (strcmp(text->Value(), "Planets") == 0)
00204                                 PublicScore |= PPS_PLANET;
00205                         else if (strcmp(text->Value(), "Bases") == 0)
00206                                 PublicScore |= PPS_BASES;
00207                         else if (strcmp(text->Value(), "Unarmed Ships") == 0)
00208                                 PublicScore |= PPS_UNARM;
00209                         else if (strcmp(text->Value(), "Escort Ships") == 0)
00210                                 PublicScore |= PPS_ESCORT;
00211                         else if (strcmp(text->Value(), "Capital Ships") == 0)
00212                                 PublicScore |= PPS_CAPSHIP;
00213                         else if (strcmp(text->Value(), "Tech Levels") == 0)
00214                                 PublicScore |= PPS_TECH;
00215                         else if (strcmp(text->Value(), "Resources") == 0)
00216                                 PublicScore |= PPS_RESOURCE;
00217                         else if (strcmp(text->Value(), "Score") == 0)
00218                                 PublicScore |= PPS_SCORE;
00219                         else if (strcmp(text->Value(), "Rank") == 0)
00220                                 PublicScore |= PPS_RANK;
00221                         else if (strcmp(text->Value(), "All") == 0) {
00222                                 PublicScore = PPS_ALL;
00223                                 break;
00224                         }
00225                 }
00226         }
00227 
00228         child1 = options->FirstChild("VictoryConditions");
00229         if (child1) {
00230                 VCCount = GetLong(child1->FirstChild("WinnerConditions"));
00231                 VCStart = GetLong(child1->FirstChild("MinimumYears"));
00232                 VCWorlds = GetLong(child1->FirstChild("ControledWorlds"));
00233                 child2 = child1->FirstChild("TechLevels");
00234                 if (child2) {
00235                         VCTechLevel = GetLong(child2->FirstChild("Level"));
00236                         VCTechCount = GetLong(child2->FirstChild("Number"));
00237                 }
00238                 VCScore = GetLong(child1->FirstChild("Score"));
00239                 VCTimes2nd = GetDouble(child1->FirstChild("OverSecond"));
00240                 VCResources = GetLong(child1->FirstChild("Resources"));
00241                 VCCapShips = GetLong(child1->FirstChild("CapShips"));
00242                 VCHighScoreAt = GetLong(child1->FirstChild("HighestScoreAt"));
00243         }
00244 
00245         child1 = options->FirstChild("MineralSettings");
00246         if (!child1) {
00247                 Message * mess = TheGame->AddMessage("Error: Missing section");
00248                 mess->AddItem("Section", "MineralSettings");
00249                 return false;
00250         }
00251 
00252         Rules::ParseMinSettings(child1);
00253 
00254         return true;
00255 }
00256 
00257 bool Game::LoadRacialTraits(const TiXmlNode * node)
00258 {
00259         if (!node)
00260                 return false;
00261 
00262         const TiXmlNode * child;
00263         for (child = node->FirstChild("PrimaryRacialTrait"); child != NULL; child = child->NextSibling("PrimaryRacialTrait")) {
00264                 mPRTs.insert(mPRTs.end(), RacialTrait::ParseNode(child));
00265         }
00266 
00267         for (child = node->FirstChild("LesserRacialTrait"); child != NULL; child = child->NextSibling("LesserRacialTrait")) {
00268                 mLRTs.insert(mLRTs.end(), RacialTrait::ParseNode(child));
00269         }
00270 
00271         return true;
00272 }
00273 
00274 bool Game::LoadStartShips(const TiXmlNode * node)
00275 {
00276         if (!node)
00277                 return false;
00278 
00279         const TiXmlNode * child;
00280         RacialTrait * rt;
00281         for (child = node->FirstChild("PrimaryRacialTrait"); child != NULL; child = child->NextSibling("PrimaryRacialTrait")) {
00282                 rt = const_cast<RacialTrait *>(ParsePRT(GetString(child->FirstChild("Name"))));
00283                 if (rt == NULL) {
00284                         return false;
00285                 }
00286                 rt->ParseStartShips(child);
00287         }
00288 
00289         for (child = node->FirstChild("LesserRacialTrait"); child != NULL; child = child->NextSibling("LesserRacialTrait")) {
00290                 rt = const_cast<RacialTrait *>(ParseLRT(GetString(child->FirstChild("Name"))));
00291                 if (rt == NULL) {
00292                         return false;
00293                 }
00294                 rt->ParseStartShips(child);
00295         }
00296 
00297         return true;
00298 }
00299 
00300 bool Game::LoadComponents(const TiXmlNode * node)
00301 {
00302         if (!node)
00303                 return false;
00304 
00305         if (!Component::LoadComponents(node, mComponents))
00306                 return false;
00307 
00308 #ifdef DEBUG
00309         deque<Component *>::const_iterator iter;
00310         Component * temp;
00311         Hull * thull;
00312 
00313         for (iter = mComponents.begin(); iter != mComponents.end(); ++iter) {
00314                 temp = *iter;
00315                 thull = dynamic_cast<Hull *>(temp);
00316                 if (thull)
00317                         thull->CheckSlots();
00318         }
00319 #endif // DEBUG
00320 
00321         return true;
00322 }
00323 
00324 unsigned long GetRandomSeed()
00325 {
00326         return (unsigned)time(NULL);
00327 }
00328 
00329 void Game::SetFileLocation(const char * hostfile)
00330 {
00331         mFileLoc = hostfile;
00332         int pos = max(int(mFileLoc.find_last_of('/')), int(mFileLoc.find_last_of('\\')));
00333         mFileName = mFileLoc.substr(pos+1);
00334         mFileName.erase(mFileName.find_last_of('.'));
00335         mFileLoc.erase(pos+1);
00336 }
00337 
00338 bool Game::LoadHostFile(const char * hostfile)
00339 {
00340         SetFileLocation(hostfile);
00341 
00342         TiXmlDocument doc(hostfile);
00343         doc.SetCondenseWhiteSpace(false);
00344         if (!doc.LoadFile()) {
00345                 Message * mess = TheGame->AddMessage("Error: Cannot open file");
00346                 mess->AddItem("File name", hostfile);
00347                 mess->AddItem("TinyXML error description", doc.ErrorDesc());
00348                 mess->AddLong("TinyXML error row", doc.ErrorRow());
00349                 mess->AddLong("TinyXML error column", doc.ErrorCol());
00350                 return false;
00351         }
00352 
00353         const TiXmlNode * hf;
00354         const TiXmlNode * node;
00355 
00356         hf = doc.FirstChild("HostFile");
00357         if (!hf) {
00358                 Message * mess = TheGame->AddMessage("Error: Missing section");
00359                 mess->AddItem("File name", hostfile);
00360                 mess->AddItem("Section", "HostFile");
00361                 return false;
00362         }
00363 
00364         if (!Game::CheckMetaInfo(hf, hostfile, HOSTFILEVERSION))
00365                 return false;
00366 
00367         mGameID = GetLong(hf->FirstChild("GameID"));
00368         if (mGameID == 0) {
00369                 Message * mess = TheGame->AddMessage("Error: Invalid Game ID");
00370                 mess->AddLong("Game ID", mGameID);
00371                 return false;
00372         }
00373 
00374         node = hf->FirstChild("Turn");
00375         Turn = GetLong(node);
00376         if (Turn < 1) {
00377                 Message * mess = TheGame->AddMessage("Error: Turn too low");
00378                 mess->AddLong("Turn", Turn);
00379                 return false;
00380         }
00381 
00382         ++Turn; // This is a new turn
00383         mNumberOfPlayers = GetLong(hf->FirstChild("NumberOfPlayers"));
00384         if (mNumberOfPlayers < 1) {
00385                 TheGame->AddMessage("Error: Less then 1 player");
00386                 return false;
00387         }
00388 
00389         unsigned long seed;
00390         seed = GetLong(hf->FirstChild("Seed"));
00391         if (seed == 0)
00392                 seed = GetRandomSeed();
00393         // set seed to what is defined in the .hst file if there, mostly for testing
00394         init_genrand(seed);
00395 
00396         if (!LoadXYFile())
00397                 return false;
00398 
00399         for (node = hf->FirstChild("Player"); node; node = node->NextSibling("Player")) {
00400                 const TiXmlNode * child2 = node->FirstChild("PlayerNumber");
00401                 unsigned int p = GetLong(child2);
00402                 if (p < 1 || p > NumberPlayers() || p != mPlayers.size()+1) {
00403                         Message * mess = TheGame->AddMessage("Error: Invalid player number");
00404                         mess->AddLong("", p);
00405                         return false;
00406                 }
00407 
00408                 deque<Player *>::iterator pi;
00409                 pi = mPlayers.insert(mPlayers.end(), new Player(p));
00410                 if (!(*pi)->ParseNode(node, false))
00411                         return false;
00412         }
00413 
00414         if (mNumberOfPlayers != mPlayers.size())
00415                 return false;
00416 
00417         // Load after getting # of players, will cause problems with the CanSee array otherwise
00418         node = hf->FirstChild("Galaxy");
00419         if (!node) {
00420                 Message * mess = TheGame->AddMessage("Error: Missing section");
00421                 mess->AddItem("File name", hostfile);
00422                 mess->AddItem("Section", "Galaxy");
00423                 return false;
00424         }
00425         if (!TheGalaxy->ParseNode(node))
00426                 return false;
00427 
00428         return true;
00429 }
00430 
00431 bool Game::LoadPlayerFile(const char * playerfile)
00432 {
00433         SetFileLocation(playerfile);
00434 
00435         string file = playerfile;
00436         int pos = file.find_last_of('.');
00437         mCurrentPlayer = atol(file.substr(pos+2).c_str());
00438 
00439         TiXmlDocument doc(playerfile);
00440         doc.SetCondenseWhiteSpace(false);
00441         if (!doc.LoadFile()) {
00442                 Message * mess = TheGame->AddMessage("Error: Cannot open file");
00443                 mess->AddItem("File name", playerfile);
00444                 mess->AddItem("TinyXML error description", doc.ErrorDesc());
00445                 mess->AddLong("TinyXML error row", doc.ErrorRow());
00446                 mess->AddLong("TinyXML error column", doc.ErrorCol());
00447                 return false;
00448         }
00449 
00450         const TiXmlNode * ptf;
00451         const TiXmlNode * node;
00452 
00453         ptf = doc.FirstChild("PlayerTurnFile");
00454         if (!ptf) {
00455                 Message * mess = TheGame->AddMessage("Error: Missing section");
00456                 mess->AddItem("File name", playerfile);
00457                 mess->AddItem("Section", "PlayerTurnFile");
00458                 return false;
00459         }
00460 
00461         if (!Game::CheckMetaInfo(ptf, playerfile, TURNFILEVERSION))
00462                 return false;
00463 
00464         mGameID = GetLong(ptf->FirstChild("GameID"));
00465         if (mGameID == 0) {
00466                 Message * mess = TheGame->AddMessage("Error: Invalid Game ID");
00467                 mess->AddLong("Game ID", mGameID);
00468                 return false;
00469         }
00470 
00471         node = ptf->FirstChild("Turn");
00472         Turn = GetLong(node);
00473         if (Turn < 1) {
00474                 Message * mess = TheGame->AddMessage("Error: Turn too low");
00475                 mess->AddLong("Turn", Turn);
00476                 return false;
00477         }
00478 
00479         mNumberOfPlayers = GetLong(ptf->FirstChild("NumberOfPlayers"));
00480         if (mNumberOfPlayers < 1) {
00481                 TheGame->AddMessage("Error: Less then 1 player");
00482                 return false;
00483         }
00484 
00485         if (!LoadXYFile())
00486                 return false;
00487 
00488         mPlayers.insert(mPlayers.begin(), mNumberOfPlayers, NULL);
00489         for (node = ptf->FirstChild("Player"); node; node = node->NextSibling("Player")) {
00490                 const TiXmlNode * child2 = node->FirstChild("PlayerNumber");
00491                 unsigned int p = GetLong(child2);
00492                 if (p < 1 || p > NumberPlayers()) {
00493                         Message * mess = TheGame->AddMessage("Error: Invalid player number");
00494                         mess->AddLong("", p);
00495                         return false;
00496                 }
00497 
00498                 mPlayers[p-1] = new Player(p);
00499                 if (!mPlayers[p-1]->ParseNode(node, p != mCurrentPlayer))
00500                         return false;
00501         }
00502 
00503         if (mNumberOfPlayers != mPlayers.size())
00504                 return false;
00505 
00506         if (mPlayers[mCurrentPlayer-1] == NULL)
00507                 return false;
00508 
00509         // Load after getting # of players, will cause problems with the CanSee array otherwise
00510         node = ptf->FirstChild("Galaxy");
00511         if (!node) {
00512                 Message * mess = TheGame->AddMessage("Error: Missing section");
00513                 mess->AddItem("File name", playerfile);
00514                 mess->AddItem("Section", "Galaxy");
00515                 return false;
00516         }
00517         if (!TheGalaxy->ParseNode(node))
00518                 return false;
00519 
00520         mPlayers[mCurrentPlayer-1]->ParseMessages(ptf->FirstChild("Messages"));
00521 
00522         file[pos+1] = 'x';
00523         mPlayers[mCurrentPlayer-1]->SetWriteXFile();
00524         mPlayers[mCurrentPlayer-1]->OpenOrdersFile(file.c_str());
00525 
00526         return true;
00527 }
00528 
00529 bool Game::CheckMetaInfo(const TiXmlNode * node, const char * file, double fileversion)
00530 {
00531         const TiXmlNode * mn = node->FirstChild("MetaInfo");
00532         if (!mn) {
00533                 Message * mess = TheGame->AddMessage("Error: Missing section");
00534                 mess->AddItem("File name", file);
00535                 mess->AddItem("Section", "MetaInfo");
00536                 return false;
00537         }
00538 
00539         double meta = GetDouble(mn->FirstChild("FileVersion"));
00540         if (meta > fileversion + epsilon || meta < fileversion - epsilon) {
00541                 Message * mess = TheGame->AddMessage("Error: Invalid FileVersion");
00542                 mess->AddItem("File name", file);
00543                 mess->AddItem("FileVersion", GetString(node->FirstChild("FileVersion")));
00544                 return false;
00545         }
00546 
00547         meta = GetDouble(mn->FirstChild("FreeStarsVersion"));
00548         if (meta > FREESTARSVERSION + epsilon || meta < FREESTARSVERSION - epsilon) {
00549                 Message * mess = TheGame->AddMessage("Error: Invalid FreeStarsVersion");
00550                 mess->AddItem("File name", file);
00551                 mess->AddItem("FreeStarsVersion", GetString(node->FirstChild("FreeStarsVersion")));
00552                 return false;
00553         }
00554 
00555         return true;
00556 }
00557 
00558 bool Game::LoadDefFile(const char * deffile)
00559 {
00560         SetFileLocation(deffile);
00561 
00562         // generate a random game ID with a different seed then the rest of the game uses
00563         init_genrand(GetRandomSeed());
00564         do mGameID = genrand_int32();
00565         while (mGameID == 0);
00566 
00567         TiXmlDocument doc(deffile);
00568         doc.SetCondenseWhiteSpace(false);
00569         if (!doc.LoadFile()) {
00570                 Message * mess = TheGame->AddMessage("Error: Cannot open file");
00571                 mess->AddItem("File name", deffile);
00572                 return false;
00573         }
00574 
00575         const TiXmlNode * df;
00576         const TiXmlNode * node;
00577 
00578         mCreation = new Creation;
00579 
00580         df = doc.FirstChild("DefFile");
00581         if (!df) {
00582                 Message * mess = TheGame->AddMessage("Error: Missing section");
00583                 mess->AddItem("File name", deffile);
00584                 mess->AddItem("Section", "DefFile");
00585                 return false;
00586         }
00587 
00588         if (!CheckMetaInfo(df, deffile, HOSTFILEVERSION))
00589                 return false;
00590 
00591         int seedc = 0;
00592         int seeds = GetLong(df->FirstChild("Seeds"));
00593         if (seeds <= 18)
00594                 seeds = 18;
00595         else if (seeds > 600)
00596                 seeds = 600;
00597 
00598         unsigned long * seed = new unsigned long[seeds];
00599         node = df->FirstChild("Seed");
00600         if (node == NULL)
00601                 seed[seedc++] = GetRandomSeed();
00602         else {
00603                 for (; node != NULL; node = node->NextSibling("Seed")) {
00604                         seed[seedc] = GetLong(node);
00605                         if (seed[seedc] == 0)
00606                                 seed[seedc] = GetRandomSeed();
00607 
00608                         ++seedc;
00609                 }
00610         }
00611 
00612         node = df->FirstChild("Turn");
00613         Turn = GetLong(node);
00614         if (Turn < 1) {
00615                 Message * mess = TheGame->AddMessage("Error: Turn too low");
00616                 mess->AddLong("Turn", Turn);
00617                 delete [] seed;
00618                 return false;
00619         }
00620 
00621         node = df->FirstChild("Rules");
00622         if (!node) {
00623                 Message * mess = TheGame->AddMessage("Error: Missing section");
00624                 mess->AddItem("File name", deffile);
00625                 mess->AddItem("Section", "Rules");
00626                 delete [] seed;
00627                 return false;
00628         }
00629 
00630         if (!LoadRules(GetString(node->FirstChild("File")), "", 0.0, false)) {
00631                 delete [] seed;
00632                 return false;
00633         }
00634 
00635         unsigned long randomize;
00636         for (node = df->FirstChild("RaceFile"); node != NULL; node = node->NextSibling("RaceFile")) {
00637                 Player * p = new Player(mPlayers.size() + 1);
00638                 mPlayers.insert(mPlayers.end(), p);
00639                 string file;
00640                 file = mFileLoc;
00641                 file += GetString(node);
00642                 randomize = p->CreateFromFile(file.c_str());
00643                 if (randomize < 0) {
00644                         delete [] seed;
00645                         return false;
00646                 } else if (seedc < seeds)
00647                         seed[seedc++] = randomize;
00648         }
00649 
00650         mNumberOfPlayers = mPlayers.size();
00651         int i;
00652         for (i = 0; i < mNumberOfPlayers; ++i)
00653                 mPlayers[i]->SetupRelations();
00654 
00655         // do not ask for random numbers before this point, execpt for the game ID above
00656         // set seed to what is defined in the .def file and player race files
00657         init_by_array(seed, seedc);
00658         delete [] seed;
00659 
00660         const TiXmlNode * cre;
00661         cre = df->FirstChild("Creation");
00662         if (!LoadCreation(cre))
00663                 return false;
00664 
00665         if (cre == NULL || !mCreation->LoadCreation(cre->FirstChild("UniverseSetup")))
00666                 return false;
00667 
00668         string NameFile;
00669         NameFile = mFileLoc;
00670         NameFile += GetString(df->FirstChild("PlanetNames"));
00671         if (!mCreation->LoadNames(NameFile.c_str()))
00672                 return false;
00673 
00674         // if they include a galaxy use that
00675         if (df->FirstChild("Galaxy") != NULL) {
00676                 if (!TheGalaxy->ParseNode(df->FirstChild("Galaxy")))
00677                         return false;
00678         }
00679 
00680         if (mCreation->mWorlds > TheGalaxy->GetPlanetCount()) {
00681                 // make one
00682                 TheGalaxy->Build(mCreation);
00683         }
00684 
00685         PlacePlayers();
00686 
00687         return true;
00688 }
00689 
00690 void Game::PlacePlayers()
00691 {
00692         deque<long> PNums(NumberPlayers());
00693         unsigned int i;
00694 
00695         for (i = 0; i < NumberPlayers(); ++i)
00696                 PNums[i] = i+1;
00697 
00698         // MS random_shuffle has a bug so use this one instead
00699         Random_Shuffle(PNums.begin(), PNums.end());
00700 
00701         for (i = 0; i < NumberPlayers(); ++i) {
00702                 TheGalaxy->PlacePlayer(NCGetPlayer(PNums[i]));
00703         }
00704 
00705         // for all left over home worlds
00706         Planet * hw;
00707         while ((hw = TheGame->GetCreation()->GetNextHW()) != NULL) {
00708                 hw->SetBaseNumber(-1);
00709         }
00710 }
00711 
00712 bool Game::LoadRules(const char * file, const char * verify, double version, bool checkver)
00713 {
00714         string File;
00715         File = "rules/";
00716         File += file;
00717 
00718         if (checkver) {
00719 //              if (!CheckCRC(File.c_str(), verify))
00720 //                      return false;
00721         } else {
00722 //              verify = GetCRC(File.c_str());
00723         }
00724 
00725         TiXmlDocument doc(File);
00726         doc.SetCondenseWhiteSpace(false);
00727         if (!doc.LoadFile()) {
00728                 Message * mess = TheGame->AddMessage("Error: Cannot open file");
00729                 mess->AddItem("File name", File);
00730                 return false;
00731         }
00732 
00733         const TiXmlNode * rd;
00734         const TiXmlNode * node;
00735 
00736         rd = doc.FirstChild("RulesDefination");
00737         if (!rd) {
00738                 Message * mess = TheGame->AddMessage("Error: Missing section");
00739                 mess->AddItem("File name", File);
00740                 mess->AddItem("Section", "RulesDefination");
00741                 return false;
00742         }
00743 
00744         node = rd->FirstChild("MetaInfo");
00745         if (!node) {
00746                 Message * mess = TheGame->AddMessage("Error: Missing section");
00747                 mess->AddItem("File name", File);
00748                 mess->AddItem("Section", "MetaInfo");
00749                 return false;
00750         }
00751 
00752         double meta = GetDouble(node->FirstChild("FileVersion"));
00753         if (meta > RULESFILEVERSION + epsilon || meta < RULESFILEVERSION - epsilon) {
00754                 Message * mess = TheGame->AddMessage("Warning: Incorrect FileVersion");
00755                 mess->AddItem("File name", File);
00756                 mess->AddItem("FileVersion", GetString(node->FirstChild("FileVersion")));
00757                 return false;
00758         }
00759 
00760         meta = GetDouble(node->FirstChild("RulesVersion"));
00761         if (checkver) {
00762                 if (meta > version + epsilon || meta < version - epsilon) {
00763                         Message * mess = TheGame->AddMessage("Warning: Incorrect RulesVersion");
00764                         mess->AddItem("File name", File);
00765                         mess->AddItem("RulesVersion", GetString(node->FirstChild("RulesVersion")));
00766                         return false;
00767                 }
00768         } else {
00769                 version = meta;
00770         }
00771 
00772         if (!Rules::LoadRules(rd->FirstChild("Rules"), file, verify, version))
00773                 return false;
00774 
00775         if (!LoadRacialTraits(rd->FirstChild("RacialTraits")))
00776                 return false;
00777 
00778         if (!LoadComponents(rd->FirstChild("Components")))
00779                 return false;
00780 
00781         if (mCreation) {
00782                 if (!LoadStartShips(rd->FirstChild("RacialTraits")))
00783                         return false;
00784         }
00785 
00786         return true;
00787 }
00788 
00789 bool Game::LoadXYFile()
00790 {
00791         string File;
00792         File = mFileLoc;
00793         File += mFileName;
00794         File += ".xy";
00795 
00796         TiXmlDocument doc(File);
00797         doc.SetCondenseWhiteSpace(false);
00798         if (!doc.LoadFile()) {
00799                 Message * mess = TheGame->AddMessage("Error: Cannot open file");
00800                 mess->AddItem("File name", File);
00801                 return false;
00802         }
00803 
00804         const TiXmlNode * xy;
00805         const TiXmlNode * node;
00806 
00807         xy = doc.FirstChild("XYFile");
00808         if (!xy) {
00809                 Message * mess = TheGame->AddMessage("Error: Missing section");
00810                 mess->AddItem("File name", File);
00811                 mess->AddItem("Section", "XYFile");
00812                 return false;
00813         }
00814 
00815         if (!Game::CheckMetaInfo(xy, File.c_str(), XYFILEVERSION))
00816                 return false;
00817 
00818         long id = GetLong(xy->FirstChild("GameID"));
00819         if (id != TheGame->GetGameID()) {
00820                 Message * mess = TheGame->AddMessage("Error: Missmatched Game IDs");
00821                 mess->AddLong("Host file GameID", mGameID);
00822                 mess->AddLong("XY file GameID", id);
00823                 return false;
00824         }
00825 
00826         node = xy->FirstChild("Rules");
00827         if (!node) {
00828                 Message * mess = TheGame->AddMessage("Error: Missing section");
00829                 mess->AddItem("File name", File);
00830                 mess->AddItem("Section", "Rules");
00831                 return false;
00832         }
00833 
00834         if (!LoadRules( GetString(node->FirstChild("File")),
00835                                         GetString(node->FirstChild("Verification")),
00836                                         GetDouble(node->FirstChild("Version")), true))
00837         {
00838                 return false;
00839         }
00840 
00841         if (!LoadCreation(xy->FirstChild("Creation")))
00842                 return false;
00843 
00844         if (!TheGalaxy->ParseNode(xy->FirstChild("Galaxy")))
00845                 return false;
00846 
00847         return true;
00848 }
00849 
00850 void Game::WriteXYFile()
00851 {
00852         cout << "Writing XYfile File" << endl;
00853 
00854         TiXmlDocument doc;
00855         doc.SetCondenseWhiteSpace(false);
00856 
00857         TiXmlDeclaration decl("1.0", "", "yes");
00858         doc.InsertEndChild(decl);
00859 
00860         TiXmlElement * XYFile = new TiXmlElement("XYFile");
00861         TiXmlElement * MetaInfo = new TiXmlElement("MetaInfo");
00862         AddDouble(MetaInfo, "FreeStarsVersion", FREESTARSVERSION);
00863         AddDouble(MetaInfo, "FileVersion", HOSTFILEVERSION);
00864         XYFile->LinkEndChild(MetaInfo);
00865         AddLong(XYFile, "GameID", TheGame->GetGameID());
00866         Rules::WriteRulesFile(XYFile);
00867 
00868         TiXmlElement * cre = new TiXmlElement("Creation");
00869         AddString(cre, "GameName", Name.c_str());
00870         AddDouble(cre, "TechAdvances", TechFactor);
00871 
00872         TiXmlElement * size = new TiXmlElement("Size");
00873         AddLong(size, "MinX", TheGalaxy->MinX());
00874         AddLong(size, "MaxX", TheGalaxy->MaxX());
00875         AddLong(size, "MinY", TheGalaxy->MinY());
00876         AddLong(size, "MaxY", TheGalaxy->MaxY());
00877         cre->LinkEndChild(size);
00878 
00879         TiXmlElement * re = new TiXmlElement("RandomEvents");
00880         AddLong(re, "Start", RandomEventsStart);
00881         if (mRandomEvents == RE_ALL)
00882                 AddString(re, "Event", "All");
00883         else {
00884                 if (mRandomEvents & RE_MT)
00885                         AddString(re, "Event", "Mystery Trader");
00886                 if (mRandomEvents & RE_ARTIFACT)
00887                         AddString(re, "Event", "Artifacts");
00888                 if (mRandomEvents & RE_COMET)
00889                         AddString(re, "Event", "Comets");
00890                 if (mRandomEvents & RE_WORMHOLE) {
00891                         AddString(re, "Event", "Wormholes");
00892                         TiXmlElement * wh = new TiXmlElement("Wormholes");
00893                         AddLong(wh, "MinimumPairs", mWHMin);
00894                         AddLong(wh, "MaximumPairs", mWHMax);
00895                         AddLong(wh, "MinDistance", mWHMinDistance);
00896                         re->LinkEndChild(wh);
00897                 }
00898         }
00899         cre->LinkEndChild(re);
00900 
00901         TiXmlElement * ps = new TiXmlElement("PublicScores");
00902         AddLong(ps, "Start", PublicScoreStart);
00903         if (PublicScore == PPS_ALL)
00904                 AddString(ps, "Category", "All");
00905         else {
00906                 if (mRandomEvents & PPS_PLANET)
00907                         AddString(ps, "Category", "Planets");
00908                 if (mRandomEvents & PPS_BASES)
00909                         AddString(ps, "Category", "Bases");
00910                 if (mRandomEvents & PPS_UNARM)
00911                         AddString(ps, "Category", "Unarmed Ships");
00912                 if (mRandomEvents & PPS_ESCORT)
00913                         AddString(ps, "Category", "Escort Ships");
00914                 if (mRandomEvents & PPS_CAPSHIP)
00915                         AddString(ps, "Category", "Capital Ships");
00916                 if (mRandomEvents & PPS_TECH)
00917                         AddString(ps, "Category", "Tech Levels");
00918                 if (mRandomEvents & PPS_RESOURCE)
00919                         AddString(ps, "Category", "Resources");
00920                 if (mRandomEvents & PPS_SCORE)
00921                         AddString(ps, "Category", "Score");
00922                 if (mRandomEvents & PPS_RANK)
00923                         AddString(ps, "Category", "Rank");
00924         }
00925         cre->LinkEndChild(ps);
00926 
00927         TiXmlElement * vc = new TiXmlElement("VictoryConditions");
00928         AddLong(vc, "WinnerConditions", VCCount);
00929         AddLong(vc, "MinimumYears", VCStart);
00930         if (VCWorlds > 0)
00931                 AddLong(vc, "ControledWorlds", VCWorlds);
00932         if (VCTechLevel > 0) {
00933                 TiXmlElement * tl = new TiXmlElement("TechLevels");
00934                 AddLong(tl, "Level", VCTechLevel);
00935                 AddLong(tl, "Number", VCTechCount);
00936                 vc->LinkEndChild(tl);
00937         }
00938         if (VCScore > 0)
00939                 AddLong(vc, "Score", VCScore);
00940         if (VCTimes2nd > 0.0)
00941                 AddDouble(vc, "OverSecond", VCTimes2nd);
00942         if (VCResources > 0)
00943                 AddLong(vc, "Resources", VCResources);
00944         if (VCCapShips > 0)
00945                 AddLong(vc, "CapShips", VCCapShips);
00946         if (VCHighScoreAt > 0)
00947                 AddLong(vc, "HighestScoreAt", VCHighScoreAt);
00948         cre->LinkEndChild(vc);
00949 
00950         Rules::WriteMinSettings(cre);
00951         XYFile->LinkEndChild(cre);
00952 
00953         TiXmlElement * galaxy = new TiXmlElement("Galaxy");
00954         TheGalaxy->WriteXYFile(galaxy);
00955         XYFile->LinkEndChild(galaxy);
00956         doc.LinkEndChild(XYFile);
00957 
00958 // For debug, save to a new file,
00959         string File;
00960 
00961         File = mFileLoc;
00962         File += mFileName;
00963         File += ".xy";
00964         doc.SaveFile(File);
00965 }
00966 
00967 bool Game::WriteHostFile()
00968 {
00969         cout << "Writing Host File" << endl;
00970 
00971         deque<Player *>::const_iterator pi;
00972 
00973         TiXmlDocument doc;
00974         doc.SetCondenseWhiteSpace(false);
00975 
00976         TiXmlDeclaration decl("1.0", "", "yes");
00977         doc.InsertEndChild(decl);
00978 
00979         TiXmlElement HostFile("HostFile");
00980         TiXmlElement MetaInfo("MetaInfo");
00981         AddDouble(&MetaInfo, "FreeStarsVersion", FREESTARSVERSION);
00982         AddDouble(&MetaInfo, "FileVersion", HOSTFILEVERSION);
00983         HostFile.InsertEndChild(MetaInfo);
00984 
00985         AddLong(&HostFile, "GameID", TheGame->GetGameID());
00986         AddLong(&HostFile, "Turn", Turn);
00987         AddLong(&HostFile, "NumberOfPlayers", mNumberOfPlayers);
00988 
00989         for (pi = mPlayers.begin(); pi != mPlayers.end(); ++pi) {
00990                 TiXmlElement player("Player");
00991                 (*pi)->WriteNode(&player, NULL);
00992 
00993                 HostFile.InsertEndChild(player);
00994         }
00995 
00996         TiXmlElement galaxy("Galaxy");
00997         TheGalaxy->WriteNode(&galaxy, NULL);
00998         HostFile.InsertEndChild(galaxy);
00999 
01000         TiXmlElement eMess("ErrorMessages");
01001         for (unsigned int i = 0; i < mMessages.size(); ++i)
01002                 mMessages[i]->WriteNode(&eMess);
01003 
01004         for (pi = mPlayers.begin(); pi != mPlayers.end(); ++pi) {
01005                 TiXmlElement player("Player");
01006                 AddLong(&player, "PlayerNumber", (*pi)->GetID());
01007                 (*pi)->WriteMessages(&player, "Error");
01008                 eMess.InsertEndChild(player);
01009         }
01010         HostFile.InsertEndChild(eMess);
01011 
01012         doc.InsertEndChild(HostFile);
01013 
01014 // For debug, save to a new file,
01015         string File;
01016 
01017         File = mFileLoc;
01018         File += mFileName;
01019         File += ".hstnew";
01020         doc.SaveFile(File);
01021 
01022 //      if (rename(File.c_str(), Backup.c_str()) != 0)
01023 //              return false;
01024 
01025         return true;
01026 }
01027 
01028 void Game::UpdateLoadBy()
01029 {
01030         int i, j, l;    // loop counters
01031         CargoHolder * other;
01032         bool pickpocket;
01033 
01034         // for all objects
01035         for (i = 0; i < mTopObjects.size(); ++i) for (j = 0; j < mTopObjects[i]->size(); ++j) {
01036                 Fleet * fleet = dynamic_cast<Fleet *>(mTopObjects[i]->at(j));
01037                 if (fleet == NULL)
01038                         continue;
01039                 
01040                 pickpocket = fleet->CanStealShip();
01041 
01042                 // check everything here at the same location
01043                 for (l = 0; l < mTopObjects[i]->size(); ++l) {
01044                         other = mTopObjects[i]->at(l);
01045                         if (fleet->GetOwner() != other->GetOwner()) {
01046                                 if (l == 0 && dynamic_cast<Planet *>(other) != NULL) {
01047                                         if (fleet->CanStealPlanet() || (fleet->GetFirstOrder()->GetType() == OT_REMOTEMINE && other->GetOwner() == NULL))
01048                                                 other->SetCanLoadBy(fleet->GetOwner());
01049                                 } else if (pickpocket)
01050                                         other->SetCanLoadBy(fleet->GetOwner());
01051                         }
01052                 }
01053         }
01054 }
01055 
01056 void Game::UpdateSeen()
01057 {
01058 /*
01059 for all topobjects
01060  for each obj
01061   reset seen
01062 for all topobjects
01063  for each obj
01064   get space scan & pen scan
01065   see everything here
01066   for all topobjects not here
01067    calc dist
01068     for each obj
01069      calc cloakdist
01070      if cloakdist < pen scan
01071       set cansee
01072      else if cloakdist < space scan
01073       if not in orbit or obj type is mine or packet
01074        set cansee
01075 */
01076 
01077         // new
01078         int i, j, k, l; // loop counters
01079         long pen;
01080         long space;
01081         double mradius;
01082         CargoHolder * viewer;
01083         CargoHolder * other;
01084         long inorbit;
01085         Planet * planet = NULL;
01086         MineField * mine;
01087         double dist, cloakdist;
01088 
01089         // for all objects
01090         for (i = 0; i < mTopObjects.size(); ++i) for (j = 0; j < mTopObjects[i]->size(); ++j) {
01091                 // get space scan & pen scan
01092                 viewer = mTopObjects[i]->at(j);
01093                 mine = dynamic_cast<MineField *>(viewer);
01094                 if (mine != NULL)
01095                         mradius = mine->GetRadius();
01096                 else
01097                         mradius = -1;
01098 
01099                 pen = viewer->GetScanPen();
01100                 space = viewer->GetScanSpace();
01101 
01102                 if (pen < 0 && space < 0 && mradius < 0)        // if viewer can't see, stop now
01103                         continue;
01104 
01105                 if (space >= 0) {
01106                         // see everything else at the same location
01107                         for (l = 0; l < mTopObjects[i]->size(); ++l) if (j != l) {
01108                                 other = mTopObjects[i]->at(l);
01109                                 if (viewer->GetOwner() != other->GetOwner())
01110                                         other->SetSeenBy(viewer->GetOwner()->GetID()-1, pen >= 0 ? SEEN_PENSCAN : SEEN_SCANNER);
01111                         }
01112                 }
01113 
01114                 if (pen <= 0 && space <= 0 && mradius < epsilon)        // if viewer can't see beyond current position, stop now
01115                         continue;
01116 
01117                 // for all topobjects not here
01118                 for (k = 0; k < mTopObjects.size(); ++k) if (k != i) {
01119                         // calc dist
01120                         dist = viewer->Distance(*mTopObjects[k]->at(0));
01121                         if (dist > pen && dist > space) // if we can't see it uncloaked, stop now
01122                                 continue;
01123 
01124                         inorbit = -1;
01125                         planet = NULL;
01126                         // if dist < pen scan
01127                         // for each obj
01128 
01129                         for (l = 0; l < mTopObjects[k]->size(); ++l) {
01130                                 other = mTopObjects[k]->at(l);
01131                                 if (l == 0) {
01132                                         planet = dynamic_cast<Planet *>(other);
01133                                         inorbit = planet != NULL;
01134                                 }
01135 
01136                                 if (dist < mradius) {
01137                                         if (space >= 0 || pen >= 0)
01138                                                 viewer->SetSeenBy(other->GetOwner()->GetID()-1, SEEN_SCANNER);
01139 
01140                                         if (!planet && viewer->GetOwner()->MineFieldScanning() && Random(100) > other->GetCloak(viewer->GetOwner(), false))
01141                                                 other->SetSeenBy(viewer->GetOwner()->GetID()-1, SEEN_SCANNER);
01142                                 }
01143 
01144                                 cloakdist = dist / (1.0 - other->GetCloak(viewer->GetOwner(), true) * viewer->GetMaxTachyon());
01145                                 if (cloakdist < pen) {
01146                                         // check for (possibly cloaked) bases
01147                                         if (planet != NULL && planet->GetOwner() && planet->GetBaseNumber() >= 0) {
01148                                                 cloakdist = dist/ (1.0 - Rules::CloakValue(planet->GetBaseDesign()->GetCloaking(), planet->GetBaseDesign()->GetMass()) * viewer->GetMaxTachyon());
01149                                                 if (cloakdist < pen)
01150                                                         planet->SetSeenBy(viewer->GetOwner()->GetID()-1, SEEN_PENSCAN | SEEN_HULL);
01151                                         }
01152                                         // check cansee
01153                                         other->SetSeenBy(viewer->GetOwner()->GetID()-1, (l == 0 && planet) ? SEEN_PENSCAN : (SEEN_PENSCAN | SEEN_HULL));
01154                                 } else {
01155                                         cloakdist = dist / (1.0 - other->GetCloak(viewer->GetOwner(), false) * viewer->GetMaxTachyon());
01156                                         if (cloakdist < space) {
01157                                                 if (inorbit == -1)
01158                                                         inorbit = dynamic_cast<Planet *>(mTopObjects[k]->at(0)) != NULL ? 1 : 0;
01159                                                 // if not in orbit or obj type is mine or packet
01160                                                 if (inorbit == 0 ||
01161                                                         dynamic_cast<MineField *>(other) != NULL ||
01162                                                         dynamic_cast<Packet *>(other) != NULL)
01163                                                 {
01164                                                         // check cansee
01165                                                         other->SetSeenBy(viewer->GetOwner()->GetID()-1, SEEN_SCANNER);
01166                                                 }
01167                                         }
01168                                 }
01169                         }
01170                 }
01171         }
01172 }
01173 
01174 void Game::ResetSeen()
01175 {
01176         int i, j;
01177         // for all objects
01178         for (i = 0; i < mTopObjects.size(); ++i) for (j = 0; j < mTopObjects[i]->size(); ++j)
01179                 mTopObjects[i]->at(j)->ResetSeen();
01180 
01181         for (i = 0; i < mPlayers.size(); ++i)
01182                 mPlayers[i]->ResetSeen();
01183 }
01184 
01185 bool Game::WritePlayerFiles()
01186 {
01187         cout << "Writing Player Files" << endl;
01188 
01189         deque<Player *>::const_iterator pi;
01190         for (pi = mPlayers.begin(); pi != mPlayers.end(); ++pi) {
01191                 TiXmlDocument doc;
01192                 doc.SetCondenseWhiteSpace(false);
01193 
01194                 TiXmlDeclaration decl("1.0", "", "yes");
01195                 doc.InsertEndChild(decl);
01196 
01197                 TiXmlElement TurnFile("PlayerTurnFile");
01198 
01199                 TiXmlElement MetaInfo("MetaInfo");
01200                 AddDouble(&MetaInfo, "FreeStarsVersion", FREESTARSVERSION);
01201                 AddDouble(&MetaInfo, "FileVersion", TURNFILEVERSION);
01202                 TurnFile.InsertEndChild(MetaInfo);
01203 
01204                 AddLong(&TurnFile, "GameID", TheGame->GetGameID());
01205                 AddLong(&TurnFile, "Turn", Turn);
01206                 AddLong(&TurnFile, "NumberOfPlayers", mNumberOfPlayers);
01207 
01208                 TiXmlElement player("Player");
01209                 (*pi)->WriteNode(&player, *pi);
01210                 TurnFile.InsertEndChild(player);
01211 
01212                 TiXmlElement galaxy("Galaxy");
01213                 TheGalaxy->WriteNode(&galaxy, *pi);
01214                 TurnFile.InsertEndChild(galaxy);
01215 
01216                 deque<Player *>::const_iterator pi2;
01217                 for (pi2 = mPlayers.begin(); pi2 != mPlayers.end(); ++pi2) {
01218                         if (*pi != *pi2) {
01219                                 TiXmlElement player("Player");
01220                                 if ((*pi2)->WriteNode(&player, *pi))
01221                                         TurnFile.InsertEndChild(player);
01222                         }
01223                 }
01224 
01225                 TiXmlElement eMess("Messages");
01226                 (*pi)->WriteMessages(&eMess, "");
01227                 TurnFile.InsertEndChild(eMess);
01228 
01229                 doc.InsertEndChild(TurnFile);
01230 
01231                 char buf[1024];
01232                 sprintf(buf, "%s%s.m%ldnew", mFileLoc.c_str(), mFileName.c_str(), long(pi - mPlayers.begin() + 1));
01233                 doc.SaveFile(buf);
01234         }
01235 
01236 //      if (rename(File.c_str(), Backup.c_str()) != 0)
01237 //              return false;
01238 
01239         return true;
01240 }
01241 
01242 bool Game::LoadTurns()
01243 {
01244         deque<long> PNums(NumberPlayers());
01245         unsigned int i;
01246 
01247         for (i = 0; i < NumberPlayers(); ++i)
01248                 PNums[i] = i;
01249 
01250         // MS random_shuffle has a bug so use this one instead
01251         Random_Shuffle(PNums.begin(), PNums.end());
01252 
01253         for (i = 0; i < NumberPlayers(); ++i) {
01254                 if (!ProcessWaypoints(i))
01255                         return false;
01256         }
01257 
01258         for (i = 0; i < NumberPlayers(); ++i) {
01259                 if (!ProcessOrders(PNums[i]))
01260                         return false;
01261         }
01262 
01263         if (!AssignWaypoints())
01264                 return false;
01265 
01266         return true;
01267 }
01268 
01269 bool Game::ProcessWaypoints(long pnumber)
01270 {
01271         string File;
01272         File = mFileLoc;
01273         File += mFileName;
01274         File += ".x" + Long2String(pnumber+1);
01275 
01276         Player * player = mPlayers[pnumber];
01277 
01278         TiXmlDocument doc(File.c_str());
01279         doc.SetCondenseWhiteSpace(false);
01280         if (!doc.LoadFile()) {
01281                 Message * mess = TheGame->AddMessage("Error: Cannot open file");
01282                 mess->AddItem("File name", File);
01283                 return false;
01284         }
01285 
01286         const TiXmlNode * orders;
01287         const TiXmlNode * node;
01288         orders = doc.FirstChild("OrdersFile");
01289         if (!orders) {
01290                 Message * mess = TheGame->AddMessage("Error: Missing section");
01291                 mess->AddItem("File name", File);
01292                 mess->AddItem("Section", "OrdersFile");
01293                 return false;
01294         }
01295 
01296         if (!Game::CheckMetaInfo(orders, File.c_str(), ORDERSFILEVERSION))
01297                 return false;
01298 
01299         long id = GetLong(orders->FirstChild("GameID"));
01300         if (id != TheGame->GetGameID()) {
01301                 Message * mess = TheGame->AddMessage("Error: Missmatched Game IDs");
01302                 mess->AddLong("Host file GameID", mGameID);
01303                 mess->AddLong("Orders file GameID", id);
01304                 return false;
01305         }
01306 
01307         node = orders->FirstChild("PlayerNo");
01308         if (GetLong(node) != pnumber+1) {
01309                 Message * mess = player->AddMessage("Error: Invalid player number");
01310                 mess->AddLong("", GetLong(node));
01311                 mess->AddItem("File name", File);
01312                 return false;
01313         }
01314 
01315         node = orders->FirstChild("Turn");
01316         if (GetLong(node) != Turn-1) {
01317                 Message * mess = player->AddMessage("Error: Wrong year number in turn file");
01318                 mess->AddLong("Turn specified", GetLong(node));
01319                 mess->AddLong("Actual turn", Turn);
01320                 return false;
01321         }
01322 
01323         // process all orders
01324         for (node = orders->FirstChild("Waypoints"); node; node = node->NextSibling("Waypoints")) {
01325                 WayOrderList & wol = *mOrders.insert(mOrders.end(),WayOrderList());
01326                 wol.SetFleet(GetLong(node->FirstChild("Fleet")));
01327                 wol.ParseNode(node, player);
01328         }
01329 
01330         return true;
01331 }
01332 
01333 bool Game::AssignWaypoints()
01334 {
01335         Player * p;
01336         Fleet * f;
01337         for (int i = 0; i < mOrders.size(); ++i) {
01338                 p = NCGetPlayer(mOrders[i].GetPlayer());
01339                 if (p == NULL)
01340                         return false;
01341 
01342                 f = p->NCGetFleet(mOrders[i].GetFleet());
01343                 if (f == NULL)
01344                         return false;
01345 
01346                 f->ChangeWaypoints(mOrders[i]);
01347         }
01348 
01349         return true;
01350 }
01351 
01352 bool Game::ProcessOrders(long pnumber)
01353 {
01354         const TiXmlNode * node;
01355 
01356         string File;
01357         File = mFileLoc;
01358         File += mFileName;
01359         File += ".x" + Long2String(pnumber+1);
01360 
01361         TiXmlDocument doc(File.c_str());
01362         doc.SetCondenseWhiteSpace(false);
01363         if (!doc.LoadFile()) {
01364                 Message * mess = mPlayers[pnumber]->AddMessage("Error: Cannot open file");
01365                 mess->AddItem("File name", File);
01366                 return false;
01367         }
01368 
01369         const TiXmlNode * orders;
01370         orders = doc.FirstChild("OrdersFile");
01371         if (!orders) {
01372                 Message * mess = mPlayers[pnumber]->AddMessage("Error: Missing section");
01373                 mess->AddItem("File name", File);
01374                 mess->AddItem("Section", "OrdersFile");
01375                 return false;
01376         }
01377 
01378         long id = GetLong(orders->FirstChild("GameID"));
01379         if (id != TheGame->GetGameID()) {
01380                 Message * mess = TheGame->AddMessage("Error: Missmatched Game IDs");
01381                 mess->AddLong("Host file GameID", mGameID);
01382                 mess->AddLong("Orders file GameID", id);
01383                 return false;
01384         }
01385 
01386         node = orders->FirstChild("PlayerNo");
01387         if (GetLong(node) != pnumber+1) {
01388                 Message * mess = mPlayers[pnumber]->AddMessage("Error: Invalid player number");
01389                 mess->AddLong("", GetLong(node));
01390                 mess->AddItem("File name", File);
01391                 return false;
01392         }
01393 
01394         node = orders->FirstChild("Turn");
01395         if (GetLong(node) != Turn-1) {
01396                 Message * mess = mPlayers[pnumber]->AddMessage("Error: Wrong year number in turn file");
01397                 mess->AddLong("Turn specified", GetLong(node));
01398                 mess->AddLong("Actual turn", Turn);
01399                 return false;
01400         }
01401 
01402         mPlayers[pnumber]->ParseOrders(orders);
01403 
01404         return true;
01405 }
01406 
01407 Player * Game::NCGetPlayer(unsigned int n)
01408 {
01409         if (n < 1 || n > mPlayers.size())
01410                 return NULL;
01411 
01412         return mPlayers[n-1];
01413 }
01414 
01415 const RacialTrait * Game::ParsePRT(const char * str) const
01416 {
01417         if (str != NULL && *str != '\0') {
01418                 deque<RacialTrait *>::const_iterator iter;
01419                 for (iter = mPRTs.begin(); iter != mPRTs.end(); ++iter) {
01420                         if (stricmp((*iter)->Name().c_str(), str) == 0)
01421                                 return *iter;
01422                 }
01423         }
01424 
01425         return NULL;
01426 }
01427 
01428 const RacialTrait * Game::ParseLRT(const char * str) const
01429 {
01430         if (str != NULL && *str != '\0') {
01431                 deque<RacialTrait *>::const_iterator iter;
01432                 for (iter = mLRTs.begin(); iter != mLRTs.end(); ++iter) {
01433                         if (stricmp((*iter)->Name().c_str(), str) == 0)
01434                                 return *iter;
01435                 }
01436         }
01437 
01438         return NULL;
01439 }
01440 
01441 const Component * Game::ParseComponent(const char * name) const
01442 {
01443         if (name != NULL && *name != '\0') {
01444                 deque<Component *>::const_iterator iter;
01445                 for (iter = mComponents.begin(); iter != mComponents.end(); ++iter) {
01446                         if (stricmp((*iter)->GetName().c_str(), name) == 0)
01447                                 return *iter;
01448                 }
01449         }
01450 
01451         return NULL;
01452 }
01453 
01454 const Component * Game::GetBestComp(const Player * player, const char * ValueType, bool CheckRad) const
01455 {
01456         long vt = Component::ParseSubType(ValueType, false);
01457         if (vt < 0)
01458                 return NULL;
01459         else
01460                 return GetBestComp(player, vt, CheckRad);
01461 }
01462 
01463 const Component * Game::GetBestComp(const Player * player, long vt, bool CheckRad) const
01464 {
01465         long temp;
01466         long Score = 0;
01467         const Component * Result = NULL;
01468 
01469         deque<Component *>::const_iterator iter;
01470         for (iter = mComponents.begin(); iter != mComponents.end(); ++iter) {
01471 
01472                 if (!(*iter)->IsBuildable(player))
01473                         continue;
01474 
01475                 if (CheckRad && player->RadDamage((*iter)->GetRadiation()) > 0.0)
01476                         continue;
01477 
01478                 temp = Component::GetScore(*iter, vt);
01479                 if (temp > Score) {
01480                         Result = *iter;
01481                         Score = temp;
01482                 }
01483         }
01484 
01485         return Result;
01486 }
01487 
01488 bool Game::ProcessTurn()
01489 {
01490         deque<long> PNums(NumberPlayers());
01491         unsigned int i;
01492 
01493         for (i = 0; i < NumberPlayers(); ++i)
01494                 PNums[i] = i;
01495 
01496         // MS random_shuffle has a bug so use thise one instead
01497         Random_Shuffle(PNums.begin(), PNums.end());
01498 
01499         // Scrapping Fleets -- in numeric order so players can coordinate tech scrapping
01500         TurnPhase = TP_SCRAPFLEET;
01501         for (i = 0; i < NumberPlayers(); ++i)
01502                 mPlayers[i]->ForEachFleet(Fleet::FTScrap, true);
01503 
01504         // Waypoint zero tasks -- in random order
01505         TurnPhase = TP_WAY0_UNLOAD;
01506         for (i = 0; i < NumberPlayers(); ++i)
01507                 mPlayers[PNums[i]]->ForEachFleet(Fleet::FTUnload0, true);
01508 
01509         TurnPhase = TP_WAY0_COLONIZE;
01510         for (i = 0; i < NumberPlayers(); ++i)
01511                 mPlayers[PNums[i]]->ForEachFleet(Fleet::FTColonize0, true);
01512 
01513         TurnPhase = TP_WAY0_INVADE;
01514         TheGalaxy->ResolveInvasions();
01515 
01516         TurnPhase = TP_WAY0_LOAD;
01517         for (i = 0; i < NumberPlayers(); ++i) {
01518                 mPlayers[PNums[i]]->ForEachFleet(Fleet::FTLoad0, false);
01519                 mPlayers[PNums[i]]->ForEachFleet(Fleet::FTLoad0, true);         // do dunnage loads
01520         }
01521 
01522         // Check for Tech gains
01523         TurnPhase = TP_TECH_CHECK1;
01524         for (i = 0; i < NumberPlayers(); ++i)
01525                 mPlayers[i]->CheckTechGain();
01526 
01527         // TODO Mystery Trader Moves
01528         TurnPhase = TP_MT_MOVE;
01529 
01530         // Packets
01531         TurnPhase = TP_PACKETS1;
01532         TheGalaxy->MovePackets(false);
01533 
01534         // Check for dead worlds
01535         TurnPhase = TP_DEADCHECK1;
01536         TheGalaxy->DeadCheck();
01537 
01538         // Fleets move, hit mines
01539         TurnPhase = TP_MOVEMENT;
01540         long Left = 0;
01541         long Last = -1;
01542 
01543         // move fleets till only cycles left
01544         while (Left != Last) {
01545                 for (i = 0; i < NumberPlayers(); ++i) {
01546                         Last = Left;
01547                         // ForEach will return the number of fleets that need more work
01548                         // so keep calling it till there same number of fleets needing work for two runs
01549                         Left = mPlayers[i]->ForEachFleet(Fleet::FTMove, true);
01550                 }
01551         }
01552 
01553         // TODO Move cycles of fleets following each other
01554         TurnPhase = TP_MOVEMENT_CYCLE;
01555 
01556         // Inner Strength colonists grow -- in order cause it doesn't matter
01557         TurnPhase = TP_FREIGHTER_GROW;
01558         for (i = 0; i < NumberPlayers(); ++i) {
01559                 if (mPlayers[i]->FreighterReproduction() > epsilon)
01560                         mPlayers[i]->ForEachFleet(Fleet::FTFreighterReproduction, true);
01561         }
01562 
01563         // Salvage decay
01564         TurnPhase = TP_SALVAGEDECAY;
01565         TheGalaxy->DecaySalvage();
01566 
01567         // TODO Wormholes shift
01568         TurnPhase = TP_WORMHOLES;
01569         TheGalaxy->JiggleWormholes();
01570 
01571         // Reset seen objects, must be before battles (maybe before mines detonate) and after movement
01572         ResetSeen();
01573 
01574         // TODO SD Minefields detonate
01575         TurnPhase = TP_MINES_DETONATE;
01576 
01577         // Mining
01578         this->TurnPhase = TP_MINING;
01579         if (!TheGalaxy->Mine())
01580                 return false;
01581 
01582         deque<Player *>::iterator pi;
01583         // AR's Remote mining their own worlds, happens right after normal mining
01584         for (i = 0; i < NumberPlayers(); ++i) {
01585                 if (mPlayers[i]->ARTechType() >= 0)
01586                         mPlayers[i]->ForEachFleet(Fleet::FTRemoteMine, true);
01587         }
01588 
01589         // Production and Research
01590         this->TurnPhase = TP_PRODUCTION;
01591         TheGalaxy->DoProduction();
01592 
01593         // Check for Tech gains
01594         TurnPhase = TP_TECH_CHECK2;
01595         for (i = 0; i < NumberPlayers(); ++i)
01596                 mPlayers[i]->AddProductionTech();
01597 
01598         // SS Spy bonus obtained
01599         TurnPhase = TP_SPYBONUS;
01600         for (i = 0; i < NumberPlayers(); ++i) {
01601                 if (mPlayers[i]->SpyTechBonus() > epsilon)
01602                         TheGalaxy->GainSpyTech(mPlayers[i]);
01603         }
01604 
01605         // Refuel fleets
01606         for (i = 0; i < NumberPlayers(); ++i)
01607                 mPlayers[i]->ForEachFleet(Fleet::FTRefuel, true);
01608 
01609         //  Population grows/dies
01610         this->TurnPhase = TP_POPULATION;
01611         TheGalaxy->GrowPop();
01612 
01613         // Packets that just launched move
01614         TurnPhase = TP_INSTA_PACKETS;
01615         TheGalaxy->MovePackets(true);
01616 
01617         // TODO Random events
01618         TurnPhase = TP_RANDOM_EVENTS;
01619 
01620         // Check for dead worlds
01621         TurnPhase = TP_DEADCHECK2;
01622         TheGalaxy->DeadCheck();
01623 
01624         // TODO Fleet battles
01625         TurnPhase = TP_BATTLES;
01626         for (i = 0; i < NumberPlayers(); ++i) {
01627                 mPlayers[i]->DoBattles();
01628         }
01629 
01630         // Remote mine uninhabited worlds, in random player order
01631         TurnPhase = TP_REMOTE_MINE;
01632         for (i = 0; i < NumberPlayers(); ++i)
01633                 mPlayers[PNums[i]]->ForEachFleet(Fleet::FTRemoteMine, false);
01634 
01635         // TODO Meet MT
01636         TurnPhase = TP_MEET_MT;
01637 
01638         // Bombing
01639         TurnPhase = TP_BOMBING;
01640         TheGalaxy->DoBombing();
01641 
01642         // Starbase and fleet repair
01643         TurnPhase = TP_REPAIR;
01644         TheGalaxy->RepairBases();
01645         for (i = 0; i < NumberPlayers(); ++i)
01646                 mPlayers[i]->ForEachFleet(Fleet::FTRepair, false);
01647 
01648         // Waypoint 1 tasks
01649         TurnPhase = TP_WAY1_UNLOAD;
01650         for (i = 0; i < NumberPlayers(); ++i)
01651                 mPlayers[i]->ForEachFleet(Fleet::FTCheckWaypoint, true);
01652 
01653         for (i = 0; i < NumberPlayers(); ++i)
01654                 mPlayers[PNums[i]]->ForEachFleet(Fleet::FTUnload1, true);
01655 
01656         TurnPhase = TP_WAY1_COLONIZE;
01657         for (i = 0; i < NumberPlayers(); ++i)
01658                 mPlayers[PNums[i]]->ForEachFleet(Fleet::FTColonize1, true);
01659 
01660         TurnPhase = TP_WAY1_INVADE;
01661         TheGalaxy->ResolveInvasions();
01662 
01663         // Check for robber barron scanners and remote miners ability to load from unowned things
01664         // this check must be after colonizations(invasions), and before loads
01665         UpdateLoadBy();
01666 
01667         TurnPhase = TP_WAY1_LOAD;
01668         for (i = 0; i < NumberPlayers(); ++i) {
01669                 mPlayers[PNums[i]]->ForEachFleet(Fleet::FTLoad1, false);
01670                 mPlayers[PNums[i]]->ForEachFleet(Fleet::FTLoad1, true);         // do dunnage loads
01671         }
01672 
01673         // Check for Tech gains
01674         TurnPhase = TP_TECH_CHECK3;
01675         for (i = 0; i < NumberPlayers(); ++i)
01676                 mPlayers[i]->CheckTechGain();
01677 
01678         // Check for dead worlds
01679         TurnPhase = TP_DEADCHECK3;
01680         TheGalaxy->DeadCheck();
01681 
01682         // TODO Mine Laying
01683         TurnPhase = TP_MINELAYING;
01684 
01685         // TODO Minefield Decay
01686         TurnPhase = TP_MINEDECAY;
01687 
01688         // TODO Mine sweeping
01689         TurnPhase = TP_MINESWEEP;
01690 
01691         // TODO Fleet Transfer -- be sure to update CanLoadBy if the ship transferred has the capability to steal minerals
01692         TurnPhase = TP_TRANSFER;
01693 
01694         // Fleet Merges
01695         TurnPhase = TP_MERGE;
01696         for (i = 0; i < NumberPlayers(); ++i)
01697                 mPlayers[i]->ForEachFleet(Fleet::FTMerge, true);
01698 
01699         // Instaforming
01700         TurnPhase = TP_INSTA_TERRAFORM;
01701         TheGalaxy->Instaform();
01702 
01703         // Remote Terraforming
01704         TurnPhase = TP_REMOTE_TERRAFORM;
01705         for (i = 0; i < NumberPlayers(); ++i)
01706                 mPlayers[i]->ForEachFleet(Fleet::FTRemoteTerraform, true);
01707 
01708         // clear old orders
01709         for (i = 0; i < NumberPlayers(); ++i)
01710                 mPlayers[i]->ForEachFleet(Fleet::FTClearWaypoint, true);
01711 
01712         // Update seen things, must be before patrol, best to be as late as possible, after transfer at least
01713         UpdateSeen();
01714         TheGalaxy->AdjustWormholeTraverse();
01715 
01716         // TODO Patrol
01717         TurnPhase = TP_PATROL;
01718 //      for (i = 0; i < NumberPlayers(); ++i)
01719 //              mPlayers[i]->ForEachFleet(Fleet::FTPatrol, true);
01720 
01721         return true;
01722 }
01723 
01724 void Game::InitialSeen()
01725 {
01726         ResetSeen();
01727         UpdateSeen();
01728 }
01729 
01730 long Game::GetTerraLimit(const Player * player, HabType ht)
01731 {
01732         long temp;
01733         long Score = 0;
01734 
01735         deque<Component *>::const_iterator iter;
01736         for (iter = mComponents.begin(); iter != mComponents.end(); ++iter) {
01737                 if ((*iter)->GetType() != CT_TERRAFORM)
01738                         continue;
01739 
01740                 if (!(*iter)->IsBuildable(player))
01741                         continue;
01742 
01743                 if ((*iter)->GetTerraType() == ht || (*iter)->GetTerraType() == -1) {
01744                         temp = (*iter)->GetTerraLimit();
01745                         if (temp > Score)
01746                                 Score = temp;
01747                 }
01748         }
01749 
01750         return Score;
01751 }
01752 
01753 double Game::ClosestMinefield(deque<MineField *> *pm, const Location * loc, long dist)
01754 {
01755         double Result = TheGalaxy->MaxX() + TheGalaxy->MaxY();
01756 
01757         return Result;
01758 }
01759 
01760 double Game::ClosestMinefield(deque<MineField *> *pm, const Location * loc, double px, double py, deque<MineField *> *inmine/*= NULL*/)
01761 {
01762         double Result = TheGalaxy->MaxX() + TheGalaxy->MaxY();
01763 
01764         return Result;
01765 }
01766 
01767 deque<CargoHolder *> * Game::GetClosestTop(int x, int y, long max/*= 0*/)
01768 {
01769         if (mTopObjects.size() <= 0)
01770                 return NULL;
01771 
01772         double lowestdist = mTopObjects[0]->at(0)->Distance(x, y);
01773         double dist;
01774         int closest = 0;
01775         int j;
01776         for (j = 1; j < mTopObjects.size(); ++j) {
01777                 dist = mTopObjects[j]->at(0)->Distance(x, y);
01778                 if (dist < lowestdist) {
01779                         lowestdist = dist;
01780                         closest = j;
01781                 }
01782 
01783                 if (dist < epsilon)
01784                         break;
01785         }
01786 
01787         if (max <= 0 || lowestdist < max)
01788                 return mTopObjects[closest];
01789         else
01790                 return NULL;
01791 }
01792 
01793 void Game::AddAlsoHere(CargoHolder * loc)
01794 {
01795         int j;
01796         for (j = 0; j < mTopObjects.size(); ++j) {
01797                 if (loc->IsWith(*mTopObjects[j]->at(0)))
01798                         break;
01799         }
01800 
01801         if (j < mTopObjects.size()) {
01802                 assert(find(mTopObjects[j]->begin(), mTopObjects[j]->end(), loc) == mTopObjects[j]->end());
01803                 mTopObjects[j]->push_back(loc);
01804                 loc->SetAlsoHere(mTopObjects[j]);
01805         } else {
01806                 mTopObjects.push_back(new deque<CargoHolder *>(1, loc));
01807                 loc->SetAlsoHere(mTopObjects[mTopObjects.size()-1]);
01808         }
01809 }
01810 
01811 void Game::RemoveAlsoHere(CargoHolder * loc)
01812 {
01813         deque<CargoHolder *>::iterator i;
01814         deque<CargoHolder *> * deq = loc->GetAlsoHere();
01815 
01816         i = find(deq->begin(), deq->end(), loc);
01817         deq->erase(i);
01818 
01819         if (deq->size() == 0) {
01820                 deque<deque<CargoHolder *> *>::iterator i2;
01821                 i2 = find(mTopObjects.begin(), mTopObjects.end(), deq);
01822                 mTopObjects.erase(i2);
01823         }
01824 }
01825 
01826 void Game::MoveAlsoHere(CargoHolder * loc)
01827 {
01828         deque<CargoHolder *> * deq = loc->GetAlsoHere();
01829 
01830         if (loc->IsWith(*deq->at(0)))
01831                 return;
01832 
01833         RemoveAlsoHere(loc);
01834         AddAlsoHere(loc);
01835 }

Generated on Mon Aug 8 21:33:43 2005 for Freestars by  doxygen 1.4.2-20050421