00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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
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;
00117
00118 long TechDif = Rules::MaxTechLevel;
00119 for (long i = 0; i < Rules::MaxTechType; ++i) {
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;
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
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);
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;
00314 }
00315
00316 TheGame->RemoveAlsoHere(gone);
00317 TheGame->StoreMessageLocation(gone);
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
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
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
00416
00417
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
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
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];
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
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
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
00639 for (node = orders->FirstChild(); node; node = node->NextSibling()) {
00640 if (node->Type() == TiXmlNode::COMMENT)
00641 continue;
00642
00643
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
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
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
00792 Message * mess = AddMessage("Error: Missing destination in transfer order", owned);
00793 if (other && other->GetOwner() == this)
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
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
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)
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
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
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
01024
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
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
01055
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
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
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
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
01308 return;
01309 }
01310
01311 if (planet->GetOwner() == NULL) {
01312 planet->CreateHW(this);
01313 } else if (planet->GetOwner() != this) {
01314
01315 return;
01316 }
01317
01318 mHasHW = true;
01319
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
01329 return;
01330 }
01331
01332 if (second->GetOwner() == NULL) {
01333 second->CreateSecondWorld(homeworld);
01334 } else if (second->GetOwner() != this) {
01335
01336 return;
01337 }
01338
01339
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)
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 }