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

Player.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 "Battle.h"
00030 #include "Bombing.h"
00031 #include "RacialTrait.h"
00032 #include "Creation.h"
00033 #include "Order.h"
00034 
00035 #ifdef _DEBUG
00036 #define new DEBUG_NEW
00037 #endif
00038 
00039 Player::Player(int id) : mID(id)
00040 {
00041         mTechLevel.insert(mTechLevel.begin(), Rules::MaxTechType, -1);
00042         mTempTechLevel.insert(mTempTechLevel.begin(), Rules::MaxTechType, 0);
00043         mTechProgress.insert(mTechProgress.begin(), Rules::MaxTechType, -1);
00044         mTerraLimit.insert(mTerraLimit.begin(), Rules::MaxHabType, 0);
00045         mShipDesigns.insert(mShipDesigns.begin(), Rules::GetConstant("MaxShipDesigns"), NULL);
00046         mBaseDesigns.insert(mBaseDesigns.begin(), Rules::GetConstant("MaxBaseDesigns"), NULL);
00047         mFleets.insert(mFleets.begin(), Rules::MaxFleets, NULL);
00048         mDefaultPayTax = false;
00049         mBattlePlans.insert(mBattlePlans.begin(), Rules::GetConstant("MaxBattlePlans"), NULL);
00050         mHasHW = false;
00051         mWriteXFile = false;
00052         mUnsavedChanges = 0;
00053         mMO = NULL;
00054 }
00055 
00056 Player::~Player()
00057 {
00058         int i;
00059         for (i = 0; i < mDefaultQ.size(); ++i)
00060                 delete mDefaultQ[i];
00061 
00062         for (i = 0; i < mFleets.size(); ++i)
00063                 delete mFleets[i];
00064 
00065         mFleets.clear();
00066 
00067         for (i = 0; i < mShipDesigns.size(); ++i)
00068                 delete mShipDesigns[i];
00069 
00070         for (i = 0; i < mBaseDesigns.size(); ++i)
00071                 delete mBaseDesigns[i];
00072 
00073         for (i = 0; i < mMessages.size(); ++i)
00074                 delete mMessages[i];
00075 
00076         for (i = 0; i < mBattlePlans.size(); ++i)
00077                 delete mBattlePlans[i];
00078 
00079         for (i = 0; i < mOrders.size(); ++i)
00080                 delete mOrders[i];
00081 }
00082 
00083 Message * Player::AddMessage(string type, const Location * loc)
00084 {
00085         Message * mess = new Message(type, loc);
00086         mMessages.push_back(mess);
00087         return mess;
00088 }
00089 
00090 Message * Player::AddMessage(string type)
00091 {
00092         // only for very bad error messages
00093         Message * mess = new Message(type);
00094         mMessages.push_back(mess);
00095         return mess;
00096 }
00097 
00098 void Player::StoreMessageLocation(const Location * loc)
00099 {
00100         for (int i = 0; i < mMessages.size(); ++i)
00101                 mMessages[i]->StoreMessageLocation(loc, this);
00102 }
00103 
00104 void Player::WriteMessages(TiXmlNode * node, const char * type)
00105 {
00106         for (unsigned int i = 0; i < mMessages.size(); ++i) {
00107                 if (type == NULL || *type == '\0' || mMessages[i]->IsType(type)) {
00108                         mMessages[i]->WriteNode(node);
00109                 }
00110         }
00111 }
00112 
00113 double Player::Miniturize(const Component * comp) const
00114 {
00115         if (comp->GetType() & (CT_PLANETARY & ~CT_PLANET_SPECIAL))
00116                 return 1.0;     // execpt for special items, planetary stuff does not miniturize
00117 
00118         long TechDif = Rules::MaxTechLevel;
00119         for (long i = 0; i < Rules::MaxTechType; ++i) { // check each field
00120                 if (comp->GetTech(i) >= 0)
00121                         TechDif = min(TechDif, GetTechLevel(i) - comp->GetTech(i));
00122         }
00123 
00124         if (TechDif == Rules::MaxTechLevel) {
00125                 Message * mess = TheGame->AddMessage("Item with no required tech");
00126                 mess->AddItem("Component name", comp->GetName());
00127         }
00128 
00129         return Race::Miniturize(TechDif);
00130 }
00131 
00132 bool Player::GainTechLevel(TechType tech)
00133 {
00134         bool Result = false;
00135         mTechProgress[tech] -= TechCost(tech);
00136 
00137         if (TheGame->GetTurnPhase() == TP_PRODUCTION)
00138                 mTempTechLevel[tech]++;
00139         else
00140                 mTechLevel[tech]++;
00141 
00142         Message * mess = AddMessage("Gain tech level");
00143         mess->AddItem("Tech field", Rules::GetTechName(tech));
00144         mess->AddLong("New Tech Level", mTechLevel[tech] + mTempTechLevel[tech]);
00145         if (mResearchField == tech) {
00146                 switch (mResearchNext) {
00147                 case NEXT_SAME:
00148                         break;
00149                 case NEXT_LOW:
00150                         {
00151                                 mResearchField = 0;
00152                                 for (long t2 = 1; t2 < Rules::MaxTechType; ++t2) {
00153                                         if ((mTechLevel[t2] + mTempTechLevel[t2]) < (mTechLevel[mResearchField] + mTempTechLevel[mResearchField]))
00154                                                 mResearchField = t2;
00155                                 }
00156                                 break;
00157                         }
00158                 case RESEARCH_ALCHEMY:
00159                         mResearchField = RESEARCH_ALCHEMY;
00160                         Result = true;  // report left over resources
00161                         break;
00162                 case NEXT_CHEAP:
00163                         {
00164                                 mResearchField = 0;
00165                                 long lowcost = TechCost(0);
00166                                 long tempcost;
00167                                 for (long t2 = 1; t2 < Rules::MaxTechType; ++t2) {
00168                                         tempcost = TechCost(t2);
00169                                         if (tempcost < lowcost) {
00170                                                 mResearchField = t2;
00171                                                 lowcost = tempcost;
00172                                         }
00173                                 }
00174                                 break;
00175                         }
00176                 default:
00177                         assert(mResearchNext >= 0 && mResearchNext < Rules::MaxTechType);
00178                         mResearchField = mResearchNext;
00179                         mResearchNext = NEXT_SAME;
00180                         break;
00181                 }
00182 
00183                 mess->AddItem("Next tech field", Rules::GetTechName(mResearchField));
00184         }
00185 
00186         return Result;
00187 }
00188 
00189 // return the amount of left over resources
00190 long Player::GainTech(long TechGain, TechType tech)
00191 {
00192         if (tech < 0 || tech >= Rules::MaxTechType)
00193                 return TechGain;
00194 
00195         long Result = 0;
00196         if (TheGame->GetTurnPhase() == TP_PRODUCTION) {
00197                 if (mResearchField == RESEARCH_ALCHEMY)
00198                         return TechGain;
00199 
00200                 long cost;
00201                 bool more = true;
00202                 long resources = TechGain;
00203                 while (more) {
00204                         cost = TechCost(mResearchField) - mTechProgress[mResearchField];
00205                         if (resources < cost) {
00206                                 more = false;
00207                                 mTechProgress[mResearchField] += resources;
00208                                 TheGalaxy->TechSpent(resources, tech);
00209                         } else {
00210                                 more = true;
00211                                 mTechProgress[mResearchField] += cost;
00212                                 TheGalaxy->TechSpent(cost, tech);
00213                                 resources -= cost;
00214                                 if (GainTechLevel(mResearchField)) {
00215                                         more = false;
00216                                         Result = resources;
00217                                 }
00218                         }
00219                 }
00220         } else
00221                 mTechProgress[tech] += TechGain;
00222 
00223         return Result;
00224 }
00225 
00226 void Player::GainSpyTech(long TechGain, TechType tech)
00227 {
00228         Message * mess = AddMessage("Spy bonus");
00229         mess->AddItem("Tech field gain", Rules::GetTechName(tech));
00230         mess->AddLong("Tech reseource gain", TechGain);
00231         GainTech(TechGain, tech);
00232         while (mTechProgress[tech] >= TechCost(tech)) {
00233                 GainTechLevel(tech);
00234         }
00235 }
00236 
00237 void Player::CheckTechGain()
00238 {
00239         for (long tech = 0; tech < Rules::MaxTechType; ++tech) {
00240                 while (mTechProgress[tech] >= TechCost(tech))
00241                         GainTechLevel(tech);
00242         }
00243 }
00244 
00245 void Player::AddProductionTech()
00246 {
00247         for (long tech = 0; tech < Rules::MaxTechType; ++tech) {
00248                 mTechLevel[tech] += mTempTechLevel[tech];
00249                 mTempTechLevel[tech] = 0;
00250         }
00251 }
00252 
00253 long Player::TechCost(TechType tech) const
00254 {
00255         assert(tech >= 0 && tech < Rules::MaxTechType);
00256         if (tech < 0 || tech >= Rules::MaxTechType)
00257                 return 0;
00258 
00259         if (mTechLevel[tech] + mTempTechLevel[tech] == Rules::MaxTechLevel)
00260                 return 0;
00261 
00262         long TotalCost = Rules::TechCost[mTechLevel[tech]+mTempTechLevel[tech]];
00263         for (long i = 0; i < Rules::MaxTechType; ++i) {
00264                 if (i != tech)
00265                         TotalCost += 10 * (mTechLevel[i] + mTempTechLevel[i]);
00266         }
00267 
00268         TotalCost = long(TotalCost * mTechCostFactor[tech] + .5);
00269         TotalCost = long(TotalCost * TheGame->GetTechFactor() + .5);    // 1.0 for normal games, 2.0 for slow tech games
00270 
00271         return TotalCost;
00272 }
00273 
00274 Fleet * Player::NCGetFleet(unsigned long n)
00275 {
00276         if (n < 1 || n > mFleets.size())
00277                 return NULL;
00278 
00279         return mFleets[n-1];
00280 }
00281 
00282 Fleet * Player::GetFleetCreate(unsigned long n, const CargoHolder &loc)
00283 {
00284         if (n < 0 || n > mFleets.size())
00285                 return NULL;
00286 
00287         if (mFleets[n-1] == NULL)
00288                 mFleets[n-1] = new Fleet(n, loc);
00289 
00290         return mFleets[n-1];
00291 }
00292 
00293 const Ship * Player::GetShipDesign(unsigned long n) const
00294 {
00295         if (n <= 0 || n > mShipDesigns.size())
00296                 return NULL;
00297         
00298         return mShipDesigns[n-1];
00299 }
00300 
00301 const Ship * Player::GetBaseDesign(unsigned long n) const
00302 {
00303         if (n < 0 || n >= mBaseDesigns.size())
00304                 return NULL;
00305 
00306         return mBaseDesigns[n];
00307 }
00308 
00309 void Player::DeleteFleet(Fleet * gone)
00310 {
00311         if (gone != mFleets[gone->GetID()-1]) {
00312                 assert(false);
00313                 return; // serious error;
00314         }
00315 
00316         TheGame->RemoveAlsoHere(gone);
00317         TheGame->StoreMessageLocation(gone);    // make any messages referring to this fleet point to it's last location
00318         mFleets[gone->GetID()-1] = NULL;
00319         delete gone;
00320 }
00321 
00322 const BattlePlan * Player::GetBattlePlan(unsigned long num) const
00323 {
00324         if (num < 0 || num >= mBattlePlans.size())
00325                 return mBattlePlans[0];
00326         else
00327                 return mBattlePlans[num];
00328 }
00329 
00330 bool Player::ParseNode(const TiXmlNode * node, bool other)
00331 {
00332         if (stricmp(node->Value(), "Player") != 0)
00333                 return false;
00334 
00335         mID = GetLong(node->FirstChild("PlayerNumber"));
00336         if (mID < 1 || mID > TheGame->NumberPlayers()) {
00337                 Message * mess = TheGame->AddMessage("Error: Invalid player number");
00338                 mess->AddLong("", mID);
00339                 return false;
00340         }
00341 
00342         // setup relations
00343         mRelations.erase(mRelations.begin(), mRelations.end());
00344         mRelations.insert(mRelations.begin(), TheGame->NumberPlayers(), PR_NEUTRAL);
00345         Rules::ParseArray(node->FirstChild("Relations"), "Race", "IDNumber", mRelations);
00346         if (!ParseCommon(node))
00347                 return false;
00348 
00349         const TiXmlNode * child;
00350         for (child = node->FirstChild(); child; child = child->NextSibling()) {
00351                 if (child->Type() == TiXmlNode::COMMENT)
00352                         continue;
00353 
00354                 if (stricmp(child->Value(), "PlayerNumber") == 0) {
00355                         // skip, already done.
00356                 } else if (!other && stricmp(child->Value(), "Password") == 0) {
00357                 } else if (stricmp(child->Value(), "RaceDefinition") == 0) {
00358                         if (!Race::ParseNode(child, other))
00359                                 return false;
00360                 } else if (stricmp(child->Value(), "ResearchTax") == 0) {
00361                 } else if (stricmp(child->Value(), "ResearchField") == 0) {
00362                 } else if (stricmp(child->Value(), "ResearchNext") == 0) {
00363                 } else if (stricmp(child->Value(), "TechLevels") == 0) {
00364                 } else if (!other && stricmp(child->Value(), "BattlePlan") == 0) {
00365                 } else if (!other && stricmp(child->Value(), "ProductionQueue") == 0) {
00366                 } else if (!other && stricmp(child->Value(), "DefaultPayTax") == 0) {
00367                 } else if (!other && stricmp(child->Value(), "Relations") == 0) {
00368                         if (!Rules::ParseArray(child, mTechLevel, TECHS))
00369                                 return false;
00370                 } else if (!other && stricmp(child->Value(), "TechProgress") == 0) {
00371                         if (!Rules::ParseArray(child, mTechProgress, TECHS))
00372                                 return false;
00373                 } else if (stricmp(child->Value(), "PlanetarySpaceScan") == 0) {
00374                         mScanSpace = GetLong(child);
00375                 } else if (stricmp(child->Value(), "PlanetaryPenScan") == 0) {
00376                         mScanPen = GetLong(child);
00377                 } else if (stricmp(child->Value(), "DefenseValue") == 0) {
00378                         mDefenseValue = GetDouble(child);
00379                 } else if (stricmp(child->Value(), "TerraformLimit") == 0) {
00380                         if (!Rules::ParseArray(child, mTerraLimit, HABS))
00381                                 return false;
00382                 } else if (stricmp(child->Value(), "ShipDesign") == 0) {
00383                         const TiXmlElement * tie = child->ToElement();
00384                         int num;
00385                         tie->Attribute("IDNumber", &num);
00386                         if (num <= 0 || num > Rules::GetConstant("MaxShipDesigns")) {
00387                                 Message * mess = TheGame->AddMessage("Error: Invalid ship slot number");
00388                                 mess->AddLong("", num);
00389                         } else {
00390                                 num--;
00391                                 if (mShipDesigns[num] == NULL)
00392                                         mShipDesigns[num] = new Ship();
00393                                 if (!mShipDesigns[num]->ParseNode(child, this, other)) {
00394                                         delete mShipDesigns[num];
00395                                         mShipDesigns[num] = NULL;
00396                                 }
00397                         }
00398                 } else if (stricmp(child->Value(), "BaseDesign") == 0) {
00399                         const TiXmlElement * tie = child->ToElement();
00400                         int num;
00401                         tie->Attribute("IDNumber", &num);
00402                         if (num <= 0 || num > Rules::GetConstant("MaxBaseDesigns")) {
00403                                 Message * mess = TheGame->AddMessage("Error: Invalid base slot number");
00404                                 mess->AddLong("", num);
00405                         } else {
00406                                 num--;
00407                                 if (mBaseDesigns[num] == NULL)
00408                                         mBaseDesigns[num] = new Ship();
00409                                 if (!mBaseDesigns[num]->ParseNode(child, this, other)) {
00410                                         delete mBaseDesigns[num];
00411                                         mBaseDesigns[num] = NULL;
00412                                 }
00413                         }
00414                 } else if (stricmp(child->Value(), "Minefield") == 0) {
00415 //                      if (!(*mMineFields.insert(mMineFields.begin()))->ParseNode(child, this))
00416 //                              return false;
00417 //                      TheGame->AddAlsoHere();
00418                 } else if (stricmp(child->Value(), "Fleet") == 0) {
00419                         ParseFleet(child, other);
00420                 } else {
00421                         Message * mess = TheGame->AddMessage("Warning: Unknown section");
00422                         mess->AddItem("Section name", child->Value());
00423                         mess->AddItem("Player", this);
00424                 }
00425         }
00426 
00427         return true;
00428 }
00429 
00430 bool Player::ParseCommon(const TiXmlNode * node)
00431 {
00432         // setup relations
00433         mRelations.erase(mRelations.begin(), mRelations.end());
00434         mRelations.insert(mRelations.begin(), TheGame->NumberPlayers(), PR_NEUTRAL);
00435         Rules::ParseArray(node->FirstChild("Relations"), "Race", "IDNumber", mRelations);
00436 
00437         const TiXmlNode * child;
00438         for (child = node->FirstChild(); child; child = child->NextSibling()) {
00439                 if (child->Type() == TiXmlNode::COMMENT)
00440                         continue;
00441 
00442                 if (stricmp(child->Value(), "Password") == 0) {
00443                         // skipping passwords for now
00444                 } else if (stricmp(child->Value(), "ResearchTax") == 0) {
00445                         ParseResearchTax(child);
00446                 } else if (stricmp(child->Value(), "ResearchField") == 0) {
00447                         SetResearchField(ParseResearchField(child));
00448                 } else if (stricmp(child->Value(), "ResearchNext") == 0) {
00449                         SetResearchNext(ParseResearchField(child));
00450                 } else if (stricmp(child->Value(), "BattlePlan") == 0) {
00451                         int num;
00452                         child->ToElement()->Attribute("IDNumber", &num);
00453                         if (num < 0 || num >= Rules::GetConstant("MaxBattlePlans")) {
00454                                 Message * mess = TheGame->AddMessage("Error: Invalid battle plan number");
00455                                 mess->AddLong("", num);
00456                         } else {
00457                                 if (mBattlePlans[num] == NULL)
00458                                         mBattlePlans[num] = new BattlePlan(false);
00459                                 else
00460                                         delete mBattlePlans[num];       // this shouldn't happen
00461 
00462                                 mBattlePlans[num]->ParseNode(child, this);
00463                         }
00464                 } else if (stricmp(child->Value(), "ProductionQueue") == 0) {
00465                         SetProduction(ProdOrder::ParseNode(child, NULL, this));
00466                 } else if (stricmp(child->Value(), "DefaultPayTax") == 0) {
00467                         mDefaultPayTax = GetBool(child);
00468                 }
00469         }
00470 
00471         return true;
00472 }
00473 
00474 void Player::ParseResearchTax(const TiXmlNode * node)
00475 {
00476         double tax = mResearchTax;
00477 
00478         tax = GetDouble(node);
00479         if (tax < 0.0) {
00480                 Message * mess = TheGame->AddMessage("Error: Invalid tech tax");
00481                 mess->AddFloat("tax", tax);
00482                 tax = 0.0;
00483         }
00484         if (tax > 1.0) {
00485                 Message * mess = TheGame->AddMessage("Error: Invalid tech tax");
00486                 mess->AddFloat("tax", tax);
00487                 tax = 1.0;
00488         }
00489         SetResearchTax(tax);
00490 }
00491 
00492 long Player::ParseResearchField(const TiXmlNode * node)
00493 {
00494         long field = Rules::TechID(GetString(node));
00495         if (field == TECH_NONE) {
00496                 Message * mess = TheGame->AddMessage("Error: Invalid tech type");
00497                 mess->AddItem("Research field", GetString(node));
00498         }
00499 
00500         return field;
00501 }
00502 
00503 void Player::ParseMessages(const TiXmlNode * node)
00504 {
00505         if (node == NULL)
00506                 return;
00507 
00508         const TiXmlNode * child;
00509         Message * mess;
00510         for (child = node->FirstChild("Message"); child != NULL; child = child->NextSibling("Message")) {
00511                 mess = new Message();
00512                 if (mess->ParseNode(child))
00513                         mMessages.push_back(mess);
00514                 else
00515                         delete mess;
00516         }
00517 }
00518 
00519 bool Player::WriteNode(TiXmlNode * node, const Player * viewer) const
00520 {
00521         bool Result = false;
00522 
00523         AddLong(node, "PlayerNumber", GetID());
00524         if (viewer == NULL || viewer == this) {
00525                 Result = true;
00526                 TiXmlElement race("RaceDefinition");
00527                 Race::WriteNode(&race);
00528                 node->InsertEndChild(race);
00529 
00530 //              AddString(player, "Password", );
00531                 AddDouble(node, "ResearchTax", mResearchTax);
00532                 AddString(node, "ResearchField", Rules::GetTechName(mResearchField).c_str());
00533                 AddString(node, "ResearchNext", Rules::GetTechName(mResearchNext).c_str());
00534 
00535                 node->LinkEndChild(Rules::WriteArray("TechLevels", mTechLevel, TECHS));
00536                 node->LinkEndChild(Rules::WriteArray("TechProgress", mTechProgress, TECHS));
00537                 AddLong(node, "PlanetarySpaceScan", mScanSpace);
00538                 AddLong(node, "PlanetaryPenScan", mScanPen);
00539                 AddDouble(node, "DefenseValue", mDefenseValue);
00540                 node->LinkEndChild(Rules::WriteArray("Relations", "Race", "IDNumber", mRelations));
00541                 node->LinkEndChild(Rules::WriteArray("TerraformLimit", mTerraLimit, HABS));
00542 
00543                 int i;
00544                 for (i = 0; i < mBattlePlans.size(); ++i) {
00545                         if (mBattlePlans[i] && mBattlePlans[i]->IsDefined())
00546                                 WriteBattlePlan(node, i);
00547                 }
00548 
00549                 // Default Production Queue and if new planets should pay tax or not
00550                 ProdOrder::WriteNode(node, mDefaultQ);
00551                 AddBool(node, "DefaultPayTax", mDefaultPayTax);
00552         }
00553 
00554         deque<Ship *>::const_iterator sdi;
00555         for (sdi = mShipDesigns.begin(); sdi != mShipDesigns.end(); ++sdi) {
00556                 if (*sdi == NULL)
00557                         continue;
00558                 if (viewer == NULL || viewer == this || (*sdi)->SeenDesign(viewer->GetID()-1) || (*sdi)->SeenHull(viewer->GetID()-1)) {
00559                         TiXmlElement SD("ShipDesign");
00560                         SD.SetAttribute("IDNumber", sdi - mShipDesigns.begin() + 1);
00561                         (*sdi)->WriteNode(&SD, viewer == NULL, viewer == this, viewer == NULL || (*sdi)->SeenDesign(viewer->GetID()-1));
00562                         node->InsertEndChild(SD);
00563                         Result = true;
00564                 }
00565         }
00566         for (sdi = mBaseDesigns.begin(); sdi != mBaseDesigns.end(); ++sdi) {
00567                 if (*sdi == NULL)
00568                         continue;
00569                 if (viewer == NULL || viewer == this || (*sdi)->SeenDesign(viewer->GetID()-1) || (*sdi)->SeenHull(viewer->GetID()-1)) {
00570                         TiXmlElement SD("BaseDesign");
00571                         SD.SetAttribute("IDNumber", sdi - mBaseDesigns.begin() + 1);
00572                         (*sdi)->WriteNode(&SD, viewer == NULL, viewer == this, viewer == NULL || (*sdi)->SeenDesign(viewer->GetID()-1));
00573                         node->InsertEndChild(SD);
00574                         Result = true;
00575                 }
00576         }
00577 
00578         deque<Fleet *>::const_iterator fi;
00579         for (fi = mFleets.begin(); fi != mFleets.end(); ++fi) {
00580                 if (*fi != NULL && (viewer == NULL || viewer == this || (*fi)->SeenBy(viewer))) {
00581                         TiXmlElement fleet("Fleet");
00582                         (*fi)->WriteNode(&fleet, viewer);
00583                         node->InsertEndChild(fleet);
00584                         Result = true;
00585                 }
00586         }
00587 
00588         if (viewer != NULL && viewer != this) {
00589                 TiXmlElement race("RaceDefinition");
00590                 if (mSeenHab[viewer->GetID()-1]) {
00591                         Race::WriteHabs(&race);
00592                         Result = true;
00593                 }
00594 
00595                 if (Result) {
00596                         AddString(&race, "SingularName", mSingularName.c_str());
00597                         AddString(&race, "PluralName", mPluralName.c_str());
00598                         AddLong(&race, "RaceEmblem", mRaceEmblem);
00599                         node->InsertEndChild(race);
00600                 }
00601         }
00602 
00603         return Result;
00604 }
00605 
00606 void Player::ParseFleet(const TiXmlNode * node, bool other)
00607 {
00608         const TiXmlElement * tie = node->ToElement();
00609         int num;
00610         tie->Attribute("IDNumber", &num);
00611 
00612         if (num < 1 || num >= Rules::MaxFleets) {
00613                 Message * mess = TheGame->AddMessage("Error: Invalid fleet number");
00614                 mess->AddLong("", num);
00615         } else {
00616                 --num;
00617                 if (mFleets[num] == NULL)
00618                         mFleets[num] = new Fleet();
00619                 if (!mFleets[num]->ParseNode(node, this, other)) {
00620                         delete mFleets[num];
00621                         mFleets[num] = NULL;
00622                 } else
00623                         TheGame->AddAlsoHere(mFleets[num]);
00624         }
00625 }
00626 
00627 void Player::ParseOrders(const TiXmlNode * orders)
00628 {
00629         const TiXmlNode * node;
00630         const TiXmlNode * child;
00631         const char * ptr;
00632 
00633         Planet * pfrom;
00634         Planet * pto;
00635         Fleet * ffrom;
00636         Fleet * fto;
00637 
00638         // process all orders
00639         for (node = orders->FirstChild(); node; node = node->NextSibling()) {
00640                 if (node->Type() == TiXmlNode::COMMENT)
00641                         continue;
00642 
00643                 // already checked, skip it here
00644                 if (stricmp(node->Value(), "PlayerNo") == 0) {
00645                 } else if (stricmp(node->Value(), "Turn") == 0) {
00646                 } else if (stricmp(node->Value(), "GameID") == 0) {
00647                 } else if (stricmp(node->Value(), "MetaInfo") == 0) {
00648 
00649                 } else if (stricmp(node->Value(), "StartMultipleOrder") == 0) {
00650                         StartMultipleOrder();
00651                 } else if (stricmp(node->Value(), "EndMultipleOrder") == 0) {
00652                         EndMultipleOrder();
00653                 } else if (stricmp(node->Value(), "ProductionQueue") == 0) {
00654                         ptr = GetString(node->FirstChild("Planet"));
00655                         if (stricmp(ptr, "Default") == 0) {
00656                                 SetProduction(ProdOrder::ParseNode(node, NULL, this));
00657                         } else {
00658                                 pto = TheGalaxy->GetPlanet(ptr);
00659                                 if (pto != NULL && pto->GetOwner() == this)
00660                                         pto->ParseProduction(node);
00661                                 else {
00662                                         Message * mess = AddMessage("Error: Invalid planet");
00663                                         mess->AddItem("", ptr);
00664                                         mess->AddItem("Where", "production queue");
00665                                         continue;
00666                                 }
00667                         }
00668                 } else if (stricmp(node->Value(), "SetPayTax") == 0) {
00669                         ptr = GetString(node->FirstChild("Planet"));
00670                         pto = TheGalaxy->GetPlanet(ptr);
00671                         if (pto != NULL && pto->GetOwner() == this)
00672                                 pto->SetPayTax(GetBool(node->FirstChild("PayTax")));
00673                         else {
00674                                 Message * mess = AddMessage("Error: Invalid planet");
00675                                 mess->AddItem("", ptr);
00676                                 mess->AddItem("Where", "PayTax");
00677                                 continue;
00678                         }
00679                 } else if (stricmp(node->Value(), "DefaultPayTax") == 0) {
00680                         SetPayTax(GetBool(node));
00681                 } else if (stricmp(node->Value(), "ChangePassword") == 0) {
00682                         // TODO to be done later
00683                 } else if (stricmp(node->Value(), "ResearchTax") == 0) {
00684                         ParseResearchTax(node);
00685                 } else if (stricmp(node->Value(), "ResearchField") == 0) {
00686                         SetResearchField(ParseResearchField(node));
00687                 } else if (stricmp(node->Value(), "ResearchNext") == 0) {
00688                         SetResearchNext(ParseResearchField(node));
00689                 } else if (stricmp(node->Value(), "SetPacketSpeed") == 0) {
00690                         pfrom = TheGalaxy->GetPlanet(GetString(node->FirstChild("Planet")));
00691                         if (!pfrom || pfrom->GetOwner() != this) {
00692                                 Message * mess = AddMessage("Error: Invalid planet");
00693                                 mess->AddItem("", GetString(node->FirstChild("Planet")));
00694                                 mess->AddItem("Where", "SetPacketSpeed");
00695                                 continue;
00696                         }
00697 
00698                         pfrom->SetPacketSpeed(GetLong(node->FirstChild("PacketSpeed")));
00699                 } else if (stricmp(node->Value(), "SetPacketDestination") == 0) {
00700                         pfrom = TheGalaxy->GetPlanet(GetString(node->FirstChild("Planet")));
00701                         if (!pfrom || pfrom->GetOwner() != this) {
00702                                 Message * mess = AddMessage("Error: Invalid planet");
00703                                 mess->AddItem("", GetString(node->FirstChild("Planet")));
00704                                 mess->AddItem("Where", "SetPacketDestination");
00705                                 continue;
00706                         }
00707 
00708                         pto = TheGalaxy->GetPlanet(GetString(node->FirstChild("PacketDestination")));
00709                         if (!pto) {
00710                                 Message * mess = AddMessage("Error: Invalid planet");
00711                                 mess->AddItem("", GetString(node->FirstChild("PacketDestination")));
00712                                 mess->AddItem("Where", "PacketDestination");
00713                                 continue;
00714                         }
00715                         pfrom->SetPacketDest(pto);
00716                 } else if (stricmp(node->Value(), "SetRouteDestination") == 0) {
00717                         pfrom = TheGalaxy->GetPlanet(GetString(node->FirstChild("From")));
00718                         if (!pfrom || pfrom->GetOwner() != this) {
00719                                 Message * mess = AddMessage("Error: Invalid planet");
00720                                 mess->AddItem("", GetString(node->FirstChild("From")));
00721                                 mess->AddItem("Where", "route from");
00722                                 continue;
00723                         }
00724 
00725                         pto = TheGalaxy->GetPlanet(GetString(node->FirstChild("RouteDestination")));
00726                         if (!pto) {
00727                                 Message * mess = AddMessage("Error: Invalid planet");
00728                                 mess->AddItem("", GetString(node->FirstChild("RouteDestination")));
00729                                 mess->AddItem("Where", "route to");
00730                                 continue;
00731                         }
00732                         pfrom->SetRoute(pto);
00733                 } else if (stricmp(node->Value(), "SplitMerge") == 0) {
00734                         // TODO split/merge fleets undo/redo and following
00735                         child = node->FirstChild("FromFleet");
00736                         ffrom = NCGetFleet(GetLong(child));
00737                         if (!ffrom) {
00738                                 Message * mess = AddMessage("Error: Invalid fleet number in split/merge");
00739                                 mess->AddLong("", GetLong(child));
00740                                 continue;
00741                         }
00742                         child = node->FirstChild("ToFleet");
00743                         fto = GetFleetCreate(GetLong(child), *ffrom);
00744                         if (!fto) {
00745                                 AddMessage("Error: Cannot create fleet for split");
00746                                 continue;
00747                         }
00748 
00749                         if (!ffrom->IsWith(*fto)) {
00750                                 Message * mess = AddMessage("Error: merge at seperate locations", ffrom);
00751                                 mess->AddItem("Merge to", fto);
00752                                 continue;
00753                         }
00754 
00755                         child = node->FirstChild("Ship");
00756                         if (child == NULL)
00757                                 ffrom->MergeTo(fto);
00758 
00759                         for (; child; child = child->NextSibling("Ship"))
00760                         {
00761                                 const Ship * design = GetShipDesign(GetLong(child->FirstChild("Design")));
00762                                 if (design == NULL)
00763                                 {
00764                                         Message * mess = AddMessage("Error: Invalid ship number");
00765                                         mess->AddLong("", GetLong(child->FirstChild("Design")));
00766                                         continue;
00767                                 }
00768                                 int number = GetLong(child->FirstChild("Number"));
00769                                 int damaged = GetLong(child->FirstChild("Damaged"));
00770                                 ffrom->MergeTo(fto, design, number, damaged);
00771                         }
00772 
00773                         if (ffrom->IsEmpty()) {
00774                                 DeleteFleet(ffrom);
00775                         }
00776 
00777                         if (fto->IsEmpty()) {
00778                                 DeleteFleet(fto);
00779                         }
00780                 } else if (stricmp(node->Value(), "Transfer") == 0) {
00781                         child = node->FirstChild("Owned");
00782                         CargoHolder * owned = ParseTransport(child, NULL);
00783                         if (!owned) {
00784                                 AddMessage("Error: Missing source in transfer order");
00785                                 continue;
00786                         }
00787 
00788                         child = node->FirstChild("Other");
00789                         CargoHolder * other =  ParseTransport(child, owned);
00790                         if (!other || !owned->IsWith(*other)) {
00791                                 // same message so you can't learn about others fleets by sending spurious transfer orders
00792                                 Message * mess = AddMessage("Error: Missing destination in transfer order", owned);
00793                                 if (other && other->GetOwner() == this) // if you own it in a seperate location send more information
00794                                         mess->AddItem("Transfer destination", other);
00795                                 continue;
00796                         }
00797 
00798                         child = node->FirstChild("Cargo");
00799                         if (child == NULL) {
00800                                 AddMessage("Error: Invalid cargo type");
00801                                 continue;
00802                         }
00803 
00804                         deque<long> cargo;
00805                         cargo.insert(cargo.begin(), Rules::MaxMinType, 0);
00806                         long pop;
00807                         long fuel;
00808                         Rules::ReadCargo(child, cargo, &pop);
00809                         fuel = GetLong(child->FirstChild("Fuel"));
00810                         TransferCargo(owned, other, pop, fuel, cargo);
00811                 } else if (stricmp(node->Value(), "BattlePlan") == 0) {
00812                         int num;
00813                         node->ToElement()->Attribute("IDNumber", &num);
00814                         if (num < 0 || num >= Rules::GetConstant("MaxBattlePlans")) {
00815                                 Message * mess = AddMessage("Error: Invalid battle plan number");
00816                                 mess->AddLong("", num);
00817                         } else {
00818                                 BattlePlan * bp = new BattlePlan(false);
00819                                 bp->ParseNode(node, this);
00820                                 ChangeBattlePlan(bp, num);
00821                         }
00822                 } else if (stricmp(node->Value(), "Relations") == 0) {
00823                         // setup relations
00824                         deque<long> rel;
00825                         rel.insert(rel.begin(), TheGame->NumberPlayers(), PR_NEUTRAL);
00826                         Rules::ParseArray(node, "Race", "IDNumber", rel);
00827                         SetRelations(rel);
00828                 } else if (stricmp(node->Value(), "SetRepeat") == 0) {
00829                         ffrom = NCGetFleet(GetLong(node->FirstChild("Fleet")));
00830                         if (ffrom)
00831                                 ffrom->SetRepeat(GetBool(node->FirstChild("Repeat")));
00832                 } else if (stricmp(node->Value(), "Waypoints") == 0) {
00833                         // Already done for host processing, so skip it here.
00834                         if (mWriteXFile) {
00835                                 WayOrderList wol;
00836                                 wol.SetFleet(GetLong(node->FirstChild("Fleet")));
00837                                 wol.ParseNode(node, this);
00838 
00839                                 Fleet * f = NCGetFleet(GetLong(node->FirstChild("Fleet")));
00840                                 if (f == NULL)
00841                                         continue;
00842 
00843                                 f->ChangeWaypoints(wol);
00844                         }
00845                 } else {
00846                         Message * mess = AddMessage("Error: Invalid .x file command");
00847                         mess->AddItem("", node->Value());
00848                 }
00849         }
00850 }
00851 
00852 CargoHolder * Player::ParseTransport(const TiXmlNode * node, const CargoHolder * owned)
00853 {
00854         CargoHolder * ch;
00855         const TiXmlNode * child;
00856 
00857         child = node->FirstChild("Planet");
00858         if (child != NULL) {
00859                 ch = TheGalaxy->GetPlanet(GetString(child));
00860                 if (owned == NULL) {
00861                         if (this == ch->GetOwner())
00862                                 return ch;
00863                 } else if (this == ch->GetOwner())
00864                         return ch;
00865                 else
00866                         return TheGalaxy->GetJettison(owned, ch);
00867 
00868                 Message * mess = AddMessage("Error: Not owner", ch);
00869                 mess->AddItem("Where", "cargo transfer");
00870                 return NULL;
00871         }
00872 
00873         child = node->FirstChild("Fleet");
00874         if (child != NULL) {
00875                 long f = GetLong(child);
00876                 if (owned != NULL) {
00877                         Player * p = TheGame->NCGetPlayer(GetLong(node->FirstChild("Owner")));
00878                         if (!p) {
00879                                 Message * mess = AddMessage("Error: Invalid player number");
00880                                 mess->AddLong("", GetLong(child));
00881                                 mess->AddItem("Where", "Cargo Transfer");
00882                                 return NULL;
00883                         }
00884 
00885                         ch = TheGalaxy->GetJettison(owned, p->NCGetFleet(f));
00886                 } else
00887                         ch = NCGetFleet(f);
00888 
00889                 return ch;
00890         }
00891 
00892         if (owned != NULL) {
00893                 child = node->FirstChild("Space");
00894                 if (child != NULL) {
00895                         return TheGalaxy->GetJettison(owned, NULL);
00896                 }
00897 
00898                 child = node->FirstChild("Packet");
00899                 if (child != NULL) {
00900                         return NULL;
00901                 }
00902         }
00903 
00904         return NULL;
00905 }
00906 
00907 void Player::TransferCargo(CargoHolder * owned, CargoHolder * other, long pop, long fuel, deque<long> & cargo, bool write/*= true*/)
00908 {
00909         if (pop < 0) {
00910                 pop = -pop;
00911                 other->TransferCargo(owned, POPULATION, &pop, this);
00912                 pop = -pop;
00913         } else
00914                 owned->TransferCargo(other, POPULATION, &pop, this);
00915 
00916         if (fuel < 0) {
00917                 fuel = -fuel;
00918                 other->TransferCargo(owned, FUEL, &fuel, this);
00919                 fuel = -fuel;
00920         } else
00921                 owned->TransferCargo(other, FUEL, &fuel, this);
00922 
00923         for (int i = 0; i < Rules::MaxMinType; ++i) {
00924                 if (cargo[i] < 0) {
00925                         cargo[i] = -cargo[i];
00926                         other->TransferCargo(owned, i, &cargo[i], this);
00927                         cargo[i] = -cargo[i];
00928                 } else
00929                         owned->TransferCargo(other, i, &cargo[i], this);
00930         }
00931 
00932         if (write && mWriteXFile)
00933                 AddOrder(new TransportOrder(this, owned, other, pop, fuel, cargo));
00934 }
00935 
00936 long Player::ForEachFleet(Fleet::FuncType func, bool arg)
00937 {
00938         long Result = 0;
00939         for (long i = 0; i < Rules::MaxFleets; ++i) {
00940                 if (mFleets[i] != NULL)
00941                         Result += mFleets[i]->Process(func, arg) ? 1 : 0;
00942         }
00943 
00944         return Result;
00945 }
00946 
00947 void Player::BuildShips(Planet * planet, long Type, long number)
00948 {
00949         long f = -1;
00950         bool newfleet = false;
00951         for (long i = 0; i < Rules::MaxFleets; ++i) {
00952                 if (mFleets[i] == NULL) {
00953                         f = i;
00954                         break;
00955                 } else if (f == -1 && mFleets[i]->InOrbit() == planet)
00956                         f = i;
00957         }
00958 
00959         Message * mess;
00960         if (f < 0) {
00961                 mess = AddMessage("Production lost due to too many fleets", planet);
00962         } else {
00963                 if (mFleets[f] == NULL) {
00964                         mFleets[f] = new Fleet(f+1, *planet);
00965                         mess = AddMessage("New ships built into new fleet", mFleets[f]);
00966                         newfleet = true;
00967                 } else {
00968                         mess = AddMessage("New ships built into existing fleet", mFleets[f]);
00969                 }
00970 
00971                 mFleets[f]->AddShips(Type, number);
00972 
00973                 // Ships are always built with full fuel tanks
00974                 mFleets[f]->AdjustFuel(mShipDesigns[Type-1]->GetFuelCapacity() * number);
00975         }
00976 
00977         if (newfleet) {
00978                 mFleets[f]->SetStartOrders(planet);
00979                 TheGame->AddAlsoHere(mFleets[f]);
00980         }
00981 
00982         mess->AddItem("Ship name", GetShipDesign(Type)->GetName());
00983         mess->AddLong("Number built", number);
00984 
00985         mShipDesigns[Type-1]->IncrementBuilt(number);
00986 }
00987 
00988 void Player::ResetTerraLimits()
00989 {
00990         mTerraLimit.clear();
00991         mTerraLimit.insert(mTerraLimit.begin(), Rules::MaxHabType, 0);
00992 }
00993 
00994 void Player::SetTerraLimit(HabType ht, long limit)
00995 {
00996         if (ht == -1) {
00997                 for (ht = 0; ht < Rules::MaxHabType; ++ht)
00998                         mTerraLimit[ht] = max(limit, mTerraLimit[ht]);
00999         } else
01000                 mTerraLimit[ht] = max(limit, mTerraLimit[ht]);
01001 }
01002 
01003 // clear Seen flags
01004 void Player::ResetSeen()
01005 {
01006         unsigned long i;
01007         for (i = 0; i < mShipDesigns.size(); ++i) {
01008                 if (mShipDesigns[i] != NULL)
01009                         mShipDesigns[i]->ResetSeen();
01010         }
01011 
01012         for (i = 0; i < mBaseDesigns.size(); ++i) {
01013                 if (mBaseDesigns[i] != NULL)
01014                         mBaseDesigns[i]->ResetSeen();
01015         }
01016 
01017         mSeenHab.empty();
01018         mSeenHab.insert(mSeenHab.begin(), TheGame->NumberPlayers(), false);
01019 }
01020 
01021 void Player::DoBattles()
01022 {
01023         // there is probably a better way to find who is fighting where, but
01024         // battle order needs to be consistant, so it has to go by player # and then fleet #
01025         unsigned int i;
01026         for (i = 0; i < Rules::MaxFleets; ++i) {
01027                 if (mFleets[i] != NULL && !mFleets[i]->AlreadyFought()) {
01028                         Battle bat(*mFleets[i]);
01029 //                      bat.AddFleet(mFleets[i]); Do not do this, it makes the next function have to check to see if the fleet has already been added
01030                         Planet * planet;
01031                         planet = TheGalaxy->GetPlanet(mFleets[i]);
01032                         bat.SetPlanet(planet);
01033                         bat.AddFleets();
01034                         bat.Resolve();
01035                 }
01036         }
01037 }
01038 
01039 void Player::AddBattleFleets(Battle * bat)
01040 {
01041         unsigned int i;
01042         for (i = 0; i < Rules::MaxFleets; ++i) {
01043                 if (mFleets[i] != NULL && !mFleets[i]->AlreadyFought() && mFleets[i]->IsWith(*bat))
01044                         bat->AddFleet(mFleets[i]);
01045         }
01046 }
01047 
01048 bool Player::AddBombingFleets(Bombing * bom, const Player* owner, const long bomb_type) const
01049 {
01050         bool added = false;
01051         unsigned int i;
01052         for (i = 0; i < Rules::MaxFleets; ++i)
01053         {
01054                 //See if Fleet has orders to bomb
01055                 //mFleets[i]->GetFirstOrder()
01056                 if (mFleets[i]->IsWith(*bom) && mFleets[i]->GetBattlePlan()->WillFight(this,owner))
01057                 {
01058                         switch(bomb_type)
01059                         {
01060                                 case BT_NORMAL:
01061                                         mFleets[i] != NULL && mFleets[i]->CanNormalBomb();
01062                                         bom->AddNormalFleet(mFleets[i]);
01063                                         added = true;
01064                                 break;
01065                                 case BT_SMART:
01066                                         mFleets[i] != NULL && mFleets[i]->CanSmartBomb();
01067                                         bom->AddSmartFleet(mFleets[i]);
01068                                         added = true;
01069                                 break;
01070                                 case BT_TERRA:
01071                                         mFleets[i] != NULL && mFleets[i]->CanTerraBomb();
01072                                         bom->AddTerraFleet(mFleets[i]);
01073                                         added = true;
01074                                 break;
01075                                 
01076                         }
01077                 }
01078         }
01079         return added;
01080 }
01081 
01082 void Player::ClearBattleEnemies()
01083 {
01084         mBattleEnemy.clear();
01085         mBattleEnemy.insert(mBattleEnemy.begin(), TheGame->NumberPlayers(), false);
01086         mInBattle = false;
01087 }
01088 
01089 void Player::SetSeenDesign(long p, long design, bool base)
01090 {
01091         if (base)
01092                 mBaseDesigns[design]->SetSeenDesign(p, true);
01093         else
01094                 mShipDesigns[design]->SetSeenDesign(p, true);
01095 }
01096 
01097 void Player::SetSeenHull(long p, long design, bool base)
01098 {
01099         if (base)
01100                 mBaseDesigns[design]->SetSeenHull(p, true);
01101         else
01102                 mShipDesigns[design]->SetSeenHull(p, true);
01103 }
01104 
01105 long Player::GetMineFieldID() const
01106 {
01107         long Result;
01108         bool retry;
01109         long trys = 0;
01110 
01111         do {
01112                 Result = genrand_int32();
01113                 retry = Result == 0;
01114                 deque<MineField *>::const_iterator mfi;
01115                 for (mfi = mMineFields.begin(); !retry && mfi != mMineFields.end(); ++mfi) {
01116                         if (Result == (*mfi)->GetID()) {
01117                                 retry = true;
01118                                 break;
01119                         }
01120                 }
01121         } while (retry && trys++ < 10);
01122 
01123         assert(!retry);
01124         return Result;
01125 }
01126 
01127 long Player::CreateFromFile(const char * file)
01128 {
01129         TiXmlDocument doc(file);
01130         doc.SetCondenseWhiteSpace(false);
01131         if (!doc.LoadFile()) {
01132                 Message * mess = TheGame->AddMessage("Error: Cannot open file");
01133                 mess->AddItem("", file);
01134                 return -1;
01135         }
01136 
01137         const TiXmlNode * node;
01138 
01139         node = doc.FirstChild("RaceDefinition");
01140         if (!node) {
01141                 Message * mess = TheGame->AddMessage("Error: Missing section");
01142                 mess->AddItem("File name", file);
01143                 mess->AddItem("Section", "RaceDefinition");
01144                 return -1;
01145         }
01146 
01147         if (!Game::CheckMetaInfo(node, file, RACEFILEVERSION))
01148                 return -1;
01149 
01150         if (!Race::ParseNode(node, false))
01151                 return -1;
01152 
01153         if (!ParseCommon(node))
01154                 return -1;
01155 
01156         if (GetBattlePlan(0) == NULL) {
01157                 Message * mess = TheGame->AddMessage("Error: No default battle plan");
01158                 mess->AddItem("File name", file);
01159                 return -1;
01160         }
01161         // copy password
01162 
01163         mResearchTax = 0.0;
01164         if (ARTechType() > 0)
01165                 mResearchField = ARTechType();
01166         else
01167                 mResearchField = 0;
01168 
01169         mResearchNext = NEXT_CHEAP;
01170         int i;
01171 
01172         for (i = 0; i < Rules::MaxTechType; ++i) {
01173                 mTechProgress[i] = 0;
01174                 mTechLevel[i] = PRTStartTech(i);
01175                 // if the start at 3(4) box is checked, tech is expensive, and it's currently less then the start level
01176                 if (StartAt() && (TechCostFactor(i) > 1.75 - epsilon) && mTechLevel[i] < StartAtBonus())
01177                         mTechLevel[i] = StartAtBonus();
01178 
01179                 mTechLevel[i] += LRTStartTech(i);
01180         }
01181 
01182         AddMessage("Game starting");
01183 
01184         // tech and LRT dependant
01185         const Component * comp;
01186         comp = TheGame->GetBestComp(this, "PlanetScanSpace", false);
01187         mScanSpace = comp == NULL ? 0 : comp->GetScanSpace();
01188         comp = TheGame->GetBestComp(this, "PlanetScanPen", false);
01189         mScanPen = comp == NULL ? 0 : comp->GetScanPen();
01190         comp = TheGame->GetBestComp(this, "DefPower", false);
01191         mDefenseValue = comp == NULL ? 0 : comp->GetDefPower();
01192         for (i = 0; i < Rules::MaxHabType; ++i)
01193                 mTerraLimit[i] = TheGame->GetTerraLimit(this, i);
01194 
01195         return GetLong(node->FirstChild("Randomize"), 1200) % 10000;
01196 }
01197 
01198 void Player::SetupRelations()
01199 {
01200         mRelations.erase(mRelations.begin(), mRelations.end());
01201         mRelations.insert(mRelations.begin(), TheGame->NumberPlayers(), PR_NEUTRAL);
01202 }
01203 
01204 void Player::AddStartShips(Planet * planet, bool HomeWorld)
01205 {
01206         const RacialTrait * rt;
01207         rt = GetPRT();
01208         int i;
01209 
01210         for (i = 0; i < rt->StartShipCount(); ++i) {
01211                 AddStartShips(rt, i, planet, HomeWorld);
01212         }
01213 
01214         for (int j = 0; j < GetLRTCount(); ++j) {
01215                 rt = GetLRT(j);
01216                 for (i = 0; i < rt->StartShipCount(); ++i) {
01217                         AddStartShips(rt, i, planet, HomeWorld);
01218                 }
01219         }
01220 }
01221 
01222 void Player::AddStartShips(const RacialTrait * rt, int i, Planet * planet, bool HomeWorld)
01223 {
01224         if (rt->StartShipCount() <= i)
01225                 return;
01226 
01227         const Ship * basicDesign = rt->GetStartDesign(i);
01228         if ((basicDesign->GetCannotBuild() == NULL || !basicDesign->GetCannotBuild()->IsBuildable(this)) && basicDesign->IsValidDesign(this)) {
01229                 Ship * upgrade = new Ship();
01230                 upgrade->CopyDesign(rt->GetStartDesign(i), false);
01231                 if (!(upgrade->GetHull()->GetHullType() & HC_BASE))
01232                         upgrade->Upgrade(this);
01233 
01234                 long designNumber;
01235                 const Ship * design;
01236                 if (upgrade->GetHull()->GetHullType() & HC_BASE) {
01237                         design = GetExistingBaseDesign(upgrade);
01238                         if (design == NULL) {
01239                                 for (designNumber = 0; designNumber < mBaseDesigns.size(); ++designNumber) {
01240                                         if (mBaseDesigns[designNumber] == NULL) {
01241                                                 mBaseDesigns[designNumber] = upgrade;
01242                                                 break;
01243                                         }
01244                                 }
01245                                 design = upgrade;
01246                         } else {
01247                                 designNumber = GetBaseDesign(design);
01248                                 delete upgrade;
01249                         }
01250                 } else {
01251                         design = GetExistingDesign(upgrade);
01252                         if (design == NULL) {
01253                                 for (designNumber = 0; designNumber < mBaseDesigns.size(); ++designNumber) {
01254                                         if (mShipDesigns[designNumber] == NULL) {
01255                                                 mShipDesigns[designNumber] = upgrade;
01256                                                 break;
01257                                         }
01258                                 }
01259                                 design = upgrade;
01260                         } else {
01261                                 designNumber = GetShipNumber(design);
01262                                 delete upgrade;
01263                         }
01264                 }
01265 
01266                 if (design->GetHull()->GetHullType() & HC_BASE) {
01267                         if (HomeWorld && rt->GetStartCount(i) > 0 && planet->GetBaseNumber() == -2)
01268                                 planet->SetBaseNumber(designNumber);
01269 
01270                         if (!HomeWorld && rt->GetStart2ndCount(i) > 0)
01271                                 planet->SetBaseNumber(designNumber);
01272                 } else {
01273                         if (HomeWorld && rt->GetStartCount(i) > 0)
01274                                 BuildShips(planet, designNumber+1, rt->GetStartCount(i));
01275 
01276                         if (!HomeWorld && rt->GetStart2ndCount(i) > 0)
01277                                 BuildShips(planet, designNumber+1, rt->GetStart2ndCount(i));
01278                 }
01279         }
01280 }
01281 
01282 const Ship * Player::GetExistingDesign(const Ship * check) const
01283 {
01284         deque<Ship *>::const_iterator s;
01285         for (s = mShipDesigns.begin(); s != mShipDesigns.end(); ++s) {
01286                 if (*s != NULL && *check == **s)
01287                         return *s;
01288         }
01289 
01290         return NULL;
01291 }
01292 
01293 const Ship * Player::GetExistingBaseDesign(const Ship * check) const
01294 {
01295         deque<Ship *>::const_iterator s;
01296         for (s = mBaseDesigns.begin(); s != mBaseDesigns.end(); ++s) {
01297                 if (*s != NULL && *check == **s)
01298                         return *s;
01299         }
01300 
01301         return NULL;
01302 }
01303 
01304 void Player::PlaceHW(Planet * planet)
01305 {
01306         if (planet == NULL) {
01307                 // error
01308                 return;
01309         }
01310 
01311         if (planet->GetOwner() == NULL) {
01312                 planet->CreateHW(this);
01313         } else if (planet->GetOwner() != this) {
01314                 // error
01315                 return;
01316         }
01317 
01318         mHasHW = true;
01319         // adjust based on player settings
01320         planet->AdjustHW(this);
01321         AddMessage("Homeworld is", planet);
01322         AddStartShips(planet, true);
01323 }
01324 
01325 void Player::PlaceSW(Planet * second, Planet * homeworld)
01326 {
01327         if (second == NULL || homeworld == NULL) {
01328                 // error
01329                 return;
01330         }
01331 
01332         if (second->GetOwner() == NULL) {
01333                 second->CreateSecondWorld(homeworld);
01334         } else if (second->GetOwner() != this) {
01335                 // error
01336                 return;
01337         }
01338 
01339         // adjust based on player settings
01340         second->AdjustSecondWorld(this);
01341         AddStartShips(second, false);
01342 }
01343 
01344 void Player::LoadFleets()
01345 {
01346         int i;
01347         for (i = 0; i < mFleets.size(); ++i) {
01348                 if (mFleets[i] != NULL)
01349                         TheGame->AddAlsoHere(mFleets[i]);
01350         }
01351 }
01352 
01353 void Player::LoadMinefields()
01354 {
01355         int i;
01356         for (i = 0; i < mMineFields.size(); ++i)
01357                 TheGame->AddAlsoHere(mMineFields[i]);
01358 }
01359 
01360 bool Player::OpenOrdersFile()
01361 {
01362         string File;
01363         File = TheGame->GetFileLoc();
01364         File += TheGame->GetFileName();
01365         File += ".x" + Long2String(GetID() + 1);
01366 
01367         return OpenOrdersFile(File.c_str());
01368 }
01369 
01370 bool Player::SaveXFile()
01371 {
01372         TiXmlDocument doc;
01373 
01374         doc.SetCondenseWhiteSpace(false);
01375 
01376         TiXmlDeclaration decl("1.0", "", "yes");
01377         doc.InsertEndChild(decl);
01378 
01379         TiXmlElement node("OrdersFile");
01380 
01381         TiXmlElement MetaInfo("MetaInfo");
01382         AddDouble(&MetaInfo, "FreeStarsVersion", FREESTARSVERSION);
01383         AddDouble(&MetaInfo, "FileVersion", ORDERSFILEVERSION);
01384         node.InsertEndChild(MetaInfo);
01385 
01386         AddLong(&node, "GameID", TheGame->GetGameID());
01387         AddLong(&node, "Turn", TheGame->GetTurn());
01388         AddLong(&node, "PlayerNo", GetID());
01389         for (int i = 0; i < mOrders.size(); ++i)
01390                 mOrders[i]->WriteNode(&node);
01391 
01392         doc.InsertEndChild(node);
01393 
01394         char buf[1024];
01395         sprintf(buf, "%s%s.x%ldnew", TheGame->GetFileLoc().c_str(), TheGame->GetFileName().c_str(), GetID());
01396         bool saved = doc.SaveFile(buf);
01397         if (saved)
01398                 mUnsavedChanges = 0;
01399 
01400         return saved;
01401 }
01402 
01403 bool Player::OpenOrdersFile(const char * file)
01404 {
01405         TiXmlDocument doc(file);
01406         doc.SetCondenseWhiteSpace(false);
01407         if (!doc.LoadFile())
01408                 return false;
01409 
01410         const TiXmlNode * orders;
01411         orders = doc.FirstChild("OrdersFile");
01412         if (!orders) {
01413                 Message * mess = AddMessage("Error: Missing section");
01414                 mess->AddItem("File name", file);
01415                 mess->AddItem("Section", "OrdersFile");
01416                 return false;
01417         }
01418 
01419         long id = GetLong(orders->FirstChild("GameID"));
01420         if (id != TheGame->GetGameID()) {
01421                 Message * mess = AddMessage("Error: Missmatched Game IDs");
01422                 mess->AddLong("Host file GameID", TheGame->GetGameID());
01423                 mess->AddLong("Orders file GameID", id);
01424                 return false;
01425         }
01426 
01427         if (!Game::CheckMetaInfo(orders, file, ORDERSFILEVERSION))
01428                 return false;
01429 
01430         const TiXmlNode * child;
01431         child = orders->FirstChild("PlayerNo");
01432         if (GetLong(child) != GetID()) {
01433                 Message * mess = AddMessage("Error: Invalid player number");
01434                 mess->AddLong("", GetLong(child));
01435                 mess->AddItem("File name", file);
01436                 return false;
01437         }
01438 
01439         child = orders->FirstChild("Turn");
01440         if (GetLong(child) != TheGame->GetTurn()) {
01441                 Message * mess = AddMessage("Error: Wrong year number in turn file");
01442                 mess->AddLong("Turn specified", GetLong(child));
01443                 mess->AddLong("Actual turn", TheGame->GetTurn());
01444                 return false;
01445         }
01446 
01447         ParseOrders(orders);
01448         mUnsavedChanges = 0;
01449 
01450         return true;
01451 }
01452 
01453 void Player::StartMultipleOrder()
01454 {
01455         if (!mWriteXFile)
01456                 return;
01457 
01458         if (mMO != NULL) {
01459                 assert(false);
01460                 EndMultipleOrder();
01461         }
01462 
01463         mMO = new MultipleOrder();
01464         AddOrder(mMO);
01465 }
01466 
01467 void Player::EndMultipleOrder()
01468 {
01469         mMO = NULL;
01470 }
01471 
01472 void Player::AddOrder(Order * o)
01473 {
01474         if (!mWriteXFile)
01475                 return;
01476 
01477         if (mMO == NULL || mMO == o) {
01478                 int i;
01479                 for (i = mOrders.size() - 1; i >= 0; --i) {
01480                         if (mOrders[i]->IsUndone()) {
01481                                 delete mOrders[i];
01482                                 mOrders.pop_back();
01483                         } else
01484                                 break;
01485                 }
01486 
01487                 mOrders.push_back(o);
01488                 mUnsavedChanges++;
01489         } else
01490                 mMO->AddOrder(o);
01491 }
01492 
01493 bool Player::UndoOrder(int pos/*= -1*/)
01494 {
01495         bool last = false;
01496         if (pos < 0) {
01497                 last = true;
01498                 for (pos = mOrders.size() - 1; pos >= 0; --pos) {
01499                         if (!mOrders[pos]->IsUndone())
01500                                 break;
01501                 }
01502 
01503                 if (pos < 0)
01504                         return false;
01505         }
01506 
01507         if (!mOrders[pos]->Undo())
01508                 return false;
01509 
01510         mUnsavedChanges--;
01511 
01512         if (!last) {
01513                 delete mOrders[pos];
01514                 mOrders.erase(mOrders.begin() + pos);
01515         }
01516         
01517         return true;
01518 }
01519 
01520 bool Player::RedoOrder()
01521 {
01522         int pos;
01523         for (pos = 0; pos < mOrders.size(); ++pos) {
01524                 if (mOrders[pos]->IsUndone())
01525                         break;
01526         }
01527 
01528         if (pos >= mOrders.size())
01529                 return false;
01530 
01531         if (!mOrders[pos]->Redo())
01532                 return false;
01533 
01534         mUnsavedChanges++;
01535         return true;
01536 }
01537 
01538 void Player::TestUndoRedo()
01539 {
01540         int size = mOrders.size();
01541 
01542         int i;
01543         for (i = 0; i < size; ++i)
01544                 UndoOrder();
01545 
01546         for (i = 0; i < size; ++i)
01547                 RedoOrder();
01548 }
01549 
01550 void Player::SetResearchTax(double tax)
01551 {
01552         if ((mResearchTax > tax + epsilon || mResearchTax < tax - epsilon) && mWriteXFile)
01553                 AddOrder(new TypedOrder<double>(&mResearchTax, AddDouble, "ResearchTax"));
01554 
01555         mResearchTax = tax;
01556 }
01557 
01558 void Player::SetResearchField(long current)
01559 {
01560         if (mResearchField != current && mWriteXFile)
01561                 AddOrder(new TypedOrder<long>(&mResearchField, "ResearchField", Rules::GetTechName));
01562 
01563         mResearchField = current;
01564 }
01565 
01566 void Player::SetResearchNext(long next)
01567 {
01568         if (mResearchNext != next && mWriteXFile)
01569                 AddOrder(new TypedOrder<long>(&mResearchNext, "ResearchNext", Rules::GetTechName));
01570 
01571         mResearchNext = next;
01572 }
01573 
01574 void Player::ChangeBattlePlan(BattlePlan * bp, int num)
01575 {
01576         if (mWriteXFile)
01577                 AddOrder(new BattlePlanOrder(mBattlePlans[num], num, this));
01578         else
01579                 delete mBattlePlans[num];
01580 
01581         mBattlePlans[num] = bp;
01582 }
01583 
01584 void Player::UndoBattlePlan(BattlePlanOrder * bpo)
01585 {
01586         mBattlePlans[bpo->GetNumber()] = bpo->GetBattlePlan(mBattlePlans[bpo->GetNumber()]);
01587 }
01588 
01589 TiXmlNode * Player::WriteBattlePlan(TiXmlNode * node, int num) const
01590 {
01591         if (mBattlePlans[num] != NULL) {
01592                 TiXmlElement * BPO = new TiXmlElement("BattlePlan");
01593                 BPO->SetAttribute("IDNumber", num);
01594                 mBattlePlans[num]->WriteNode(BPO);
01595                 node->LinkEndChild(BPO);
01596                 return BPO;
01597         } else
01598                 return NULL;
01599 }
01600 
01601 void Player::SetRelations(const deque<long> rel)
01602 {
01603         if (mWriteXFile)
01604                 AddOrder(new RelationsOrder(&mRelations));
01605 
01606         assert(rel.size() == mRelations.size());
01607         mRelations = rel;
01608 }
01609 
01610 void Player::SetProduction(const deque<ProdOrder *> & ords)
01611 {
01612         if (mWriteXFile)
01613                 AddOrder(new ProductionOrder("Default", &mDefaultQ));
01614         else {
01615                 for (int i = 0; i < mDefaultQ.size(); ++i)
01616                         delete mDefaultQ[i];
01617         }
01618 
01619         mDefaultQ = ords;
01620 }
01621 
01622 void Player::SetPayTax(bool paytax)
01623 {
01624         if (paytax != mDefaultPayTax && mWriteXFile)
01625                 AddOrder(new TypedOrder<bool>(&mDefaultPayTax, AddBool, "DefaultPayTax"));
01626 
01627         mDefaultPayTax = paytax;
01628 }
01629 
01630 void Player::ParseWaypoints(const TiXmlNode * node)
01631 {
01632         WayOrderList wol;
01633         wol.SetFleet(GetLong(node->FirstChild("Fleet")));
01634         wol.ParseNode(node, this);
01635 
01636         NCGetFleet(wol.GetFleet())->ChangeWaypoints(wol);
01637 }

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