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

Fleet.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 "Stack.h"
00029 #include "TempFleet.h"
00030 #include "Hull.h"
00031 #include "Order.h"
00032 #include "Wormhole.h"
00033 
00034 #include <stdlib.h>
00035 #include <algorithm>
00036 #include <functional>
00037 
00038 #ifdef _DEBUG
00039 #define new DEBUG_NEW
00040 #endif
00041 
00042 
00043 Fleet::Fleet(int id, const CargoHolder &loc) : CargoHolder(loc), mStartPos(loc)
00044 {
00045         mID = id;
00046         Init();
00047 }
00048 
00049 Fleet::Fleet()
00050 {
00051         Init();
00052 }
00053 
00054 void Fleet::Init()
00055 {
00056         mName.erase();
00057         ResetDefaults();        // Set all calculated values to defaults
00058         mCanLoadBy.clear();
00059         mCanLoadBy.insert(mCanLoadBy.begin(), TheGame->NumberPlayers(), false);
00060         mRepeatOrders = false;
00061 
00062         mRepairRate = 2;        // how much this fleet will repair this turn
00063         mBattlePlan = 0;
00064         mRepeatOrders = false;
00065         mAlreadyFought = false;
00066         mHasMoved = false;
00067         mChasing = NULL;
00068         mDistMoved = 0;
00069         mFuel = 0;      // unknown
00070 }
00071 
00072 Fleet::~Fleet()
00073 {
00074         mStacks.clear();
00075         mChasers.clear();
00076         int i;
00077         for (i = 0; i < mOrders.size(); ++i)
00078                 delete mOrders[i];
00079 }
00080 
00081 void Fleet::ResetDefaults()
00082 {
00083         // set all Calculated Values to defaults
00084         CVCost.Zero();
00085         ReCost = 0;
00086         CVScanPen = -2;
00087         CVScanSpace = -1;
00088         CVMass = -1;
00089 
00090         CVCloaking = -1;
00091         CVFuelCapacity = -1;
00092         CVFuelGen = -1;
00093         CVMass = -1;
00094         CCalcMaxMass = false;
00095         CCalcMaxTachyon = false;
00096         CVCargoCapacity = -1;
00097         CVMines = -1;
00098         CVSweeping = -1;
00099 
00100         CCalcMaxDampener = false;
00101         CCalcMaxRepairRate = false;
00102         CVMinSafeSpeed = -1;
00103         CVMinMaxSpeed = -1;
00104         CVMinFreeSpeed = -1;
00105         CVMinBattleSpeed = -1;
00106 
00107         CVColonize = -1;
00108         CVStealShip = -1;
00109         CVStealPlanet = -1;
00110         CVShoot = -1;
00111         
00112         CCalcNormalKillper = false;
00113         CVMinKill = CVInstKill = -1;
00114         CVJumpGate = -1;
00115         
00116         CVNormalBomb = CVSmartBomb = CVTerraBomb = -1;
00117 
00118         CVMineAmount.erase(CVMineAmount.begin(), CVMineAmount.end());
00119         CVMineAmount.insert(CVMineAmount.begin(), MT_MAXIMUM, -1);
00120 
00121         CVTechLevel.erase(CVTechLevel.begin(), CVTechLevel.end());
00122         CVTechLevel.insert(CVTechLevel.begin(), Rules::MaxTechType, -1);
00123 
00124         CVRadiation.erase(CVRadiation.begin(), CVRadiation.end());
00125         CVRadiation.insert(CVRadiation.begin(), Rules::MaxHabType, -1);
00126 }
00127 
00128 bool Fleet::ParseNode(const TiXmlNode * node, Player * player, bool other)
00129 {
00130         if (!CargoHolder::ParseNode(node, player))
00131                 return false;
00132 
00133         mStartPos = *this;
00134         const TiXmlNode * child1;
00135         const char * ptr;
00136 
00137         if (mID < 1 || mID > (int)Rules::MaxFleets)
00138                 return false;
00139 
00140         ptr = GetString(node->FirstChild("Name"));
00141         if (ptr != NULL)
00142                 mName = ptr;
00143 
00144         child1 = node->FirstChild("Contains");
00145         if (child1 != NULL)
00146                 mFuel = GetLong(child1->FirstChild("Fuel"));
00147 
00148         if (!other) {
00149                 mBattlePlan = GetLong(node->FirstChild("BattlePlan"));
00150                 if (mBattlePlan < 0 || mBattlePlan >= Rules::GetConstant("MaxBattlePlans"))
00151                         return false;
00152 
00153                 mRepeatOrders = GetBool(node->FirstChild("Repeat"));
00154 
00155                 Rules::ParseArrayBool(node->FirstChild("CanLoadBy"), "Race", "Number", mCanLoadBy);
00156         }
00157 
00158         for (child1 = node->FirstChild("Stack"); child1; child1 = child1->NextSibling("Stack")) {
00159                 Stack a;
00160                 unsigned long c = GetLong(child1->FirstChild("ShipCount"));
00161                 a.SetCount(c);
00162                 unsigned long d = GetLong(child1->FirstChild("ShipDesign"));
00163                 a.SetDesign(player->GetShipDesign(d));
00164                 mStacks.push_back(a);
00165                 mStacks.back().ParseNode(child1, player);
00166                 mStacks.back().SetFleetIn(this);
00167         }
00168 
00169         if (!other) {
00170                 WayOrderList ords;
00171                 ords.SetFleet(GetID());
00172                 ords.ParseNode(node, player);
00173                 ChangeWaypoints(ords);
00174                 mBattlePlan = GetLong(node->FirstChild("BattlePlan"));
00175         }
00176 
00177         return true;
00178 }
00179 
00180 TiXmlNode * Fleet::WriteNode(TiXmlNode * node, const Player * viewer) const
00181 {
00182         CargoHolder::WriteNode(node, viewer);
00183 
00184         if (viewer == NULL || viewer == GetOwner()) {
00185                 TiXmlNode * child1;
00186                 child1 = node->FirstChild("Contains");
00187                 if (child1 == NULL) {
00188                         child1 = new TiXmlElement("Contains");
00189                         node->LinkEndChild(child1);
00190                 }
00191                 AddLong(child1, "Fuel", mFuel);
00192 
00193                 AddString(node, "Name", mName.c_str());
00194                 AddLong(node, "BattlePlan", mBattlePlan);
00195                 AddBool(node, "Repeat", mRepeatOrders);
00196                 for (int i = 0; i < mOrders.size(); ++i)
00197                         mOrders[i]->WriteNode(node);
00198         }
00199         if (viewer == NULL)
00200                 node->LinkEndChild(Rules::WriteArrayBool("CanLoadBy", "Race", "Number", mCanLoadBy));
00201 
00202         AddLong(node, "Mass", GetMass());
00203         deque<Stack>::const_iterator si;
00204         for (si = mStacks.begin(); si != mStacks.end(); ++si) {
00205                 TiXmlElement stack("Stack");
00206                 si->WriteNode(&stack, GetOwner(), viewer);
00207                 node->InsertEndChild(stack);
00208         }
00209 
00210         return node;
00211 }
00212 
00213 const string Fleet::GetName(const Player * viewer) const
00214 {
00215         if (viewer == GetOwner() && !mName.empty())
00216                 return mName;
00217         else
00218         {
00219                 string str;
00220                 const Ship * MostShips = NULL;
00221                 long count = 0;
00222 
00223                 // loop through ships
00224                 deque<Stack>::const_iterator iter;
00225                 for (iter = mStacks.begin(); iter != mStacks.end(); ++iter) {
00226                         if (iter->GetCount() > count) {
00227                                 count = iter->GetCount();
00228                                 MostShips = iter->GetDesign();
00229                         }
00230                 }
00231 
00232                 str = MostShips->GetName();
00233                 if (str.empty()) {
00234                         str = MostShips->GetHull()->GetName();
00235                         for (int i = 0; i < Rules::GetConstant("MaxShipDesigns"); ++i) {
00236                                 if (GetOwner()->GetShipDesign(i) == MostShips) {
00237                                         str += "(" + Long2String(i) + ")";
00238                                         break;
00239                                 }
00240                         }
00241                 }
00242 
00243                 if (viewer != GetOwner())
00244                         str = GetOwner()->GetSingleName() + " " + str;
00245 
00246                 if (mStacks.size() > 1)
00247                         str += '+';
00248                 str += " #" + Long2String(mID);
00249                 return str;
00250         }
00251 }
00252 
00253 const BattlePlan * Fleet::GetBattlePlan() const
00254 {
00255         return mOwner->GetBattlePlan(mBattlePlan);
00256 }
00257 
00258 bool Fleet::Process(FuncType func, bool arg)
00259 {
00260         switch (func) {
00261         case FTScrap:
00262                 if (mOrders.size() > 0 && GetFirstOrder()->GetType() == OT_SCRAP)
00263                         Scrap(false);
00264                 return false;
00265         case FTRemoteMine:
00266                 RemoteMine(arg);
00267                 return false;
00268         case FTUnload0:
00269                 ProcessTransport(false, arg);
00270                 return false;
00271         case FTColonize0:
00272                 Colonize();
00273                 return false;
00274         case FTLoad0:
00275                 ProcessTransport(true, arg);
00276                 return false;
00277         case FTMove:
00278                 if (!mHasMoved)
00279                         return Move();
00280                 else
00281                         return false;
00282         case FTFreighterReproduction:
00283                 FreighterReproduction();
00284                 return false;
00285         case FTRefuel:
00286                 Refuel();
00287                 return false;
00288         case FTCheckWaypoint:
00289                 CheckWaypoint();
00290                 return false;
00291         case FTUnload1:
00292                 if (mDoneWaypoint)
00293                         ProcessTransport(false, arg);
00294                 return false;
00295         case FTColonize1:
00296                 if (mDoneWaypoint)
00297                         Colonize();
00298                 return false;
00299         case FTLoad1:
00300                 if (mDoneWaypoint)
00301                         ProcessTransport(true, arg);
00302                 return false;
00303         case FTMerge:
00304                 CheckMerge();
00305                 return false;
00306         case FTClearWaypoint:
00307                 ClearWaypoint();
00308                 return false;
00309         case FTRemoteTerraform:
00310                 RemoteTerraform(false);
00311                 return false;
00312         case FTRepair:
00313                 Repair();
00314                 return false;
00315         default:
00316                 return false;
00317         }
00318 }
00319 
00320 void Fleet::CheckWaypoint()
00321 {
00322         if (mOrders.size() >= 2 && *this == *mOrders[1]->GetLocation()) {
00323                 delete mOrders[0];
00324                 mOrders.pop_front();
00325                 mDoneWaypoint = true;
00326         } else
00327                 mDoneWaypoint = false;
00328 
00329         // set current order location to current location
00330         // but, don't reset location if location is a fleet (so you can continue following some one)
00331         Fleet * f = dynamic_cast<Fleet *>(mOrders[0]->NCGetLocation());
00332         if (f != NULL) {
00333                 if (!IsWith(*f)) {
00334                         mOrders[0]->SetLocation(new Location(*this), true);
00335                         WayOrder * wo = new WayOrder(f);
00336                         wo->SetType(OT_NONE);
00337                         wo->SetSpeed(GetBestSpeed(this, f, OT_PATROL));
00338                         mOrders.insert(mOrders.end(), wo);
00339                 }
00340         } else {
00341                 if (dynamic_cast<Planet *>(mAlsoHere->at(0)) != NULL)
00342                         mOrders[0]->SetLocation(mAlsoHere->at(0), false);
00343                 else
00344                         mOrders[0]->SetLocation(new Location(*this), true);
00345         }
00346 }
00347 
00348 void Fleet::ClearWaypoint()
00349 {
00350         switch (mOrders[0]->GetType()) {
00351         case OT_NONE:
00352         case OT_COLONIZE:
00353         case OT_MERGE:
00354         case OT_TRANSFER:
00355         case OT_TRANSPORT:
00356                 mOrders[0]->SetType(OT_NONE);
00357                 break;
00358         case OT_REMOTEMINE:
00359                 break;
00360         case OT_SCRAP:
00361                 break;
00362         case OT_ROUTE:
00363                 if (mOrders.size() < 2)
00364                         SetNextRoute(InOrbit());
00365 
00366                 mOrders[0]->SetType(OT_NONE);
00367                 break;
00368         case OT_LAYMINE:
00369                 // decrement # of years
00370                 break;
00371         case OT_PATROL:
00372                 // find closest fleet to target
00373                 break;
00374         default:
00375                 break;
00376         }
00377 }
00378 
00379 void Fleet::RemoteTerraform(bool bomb)
00380 {
00381         Planet * planet = InOrbit();
00382         if (planet != NULL &&
00383                 planet->GetOwner() != NULL &&
00384                 (planet->GetOwner()->GetRelations(GetOwner()) > PR_NEUTRAL || planet->GetBaseDesign() < 0))
00385         {
00386                 planet->RemoteTerraform(this, bomb);
00387         }
00388 }
00389 
00390 void Fleet::Repair()
00391 {
00392         if (mAlreadyFought || mRepairRate == 0)
00393                 return;
00394 
00395         double rate = Rules::GetFloat("RepairMoved", 0.01);     // rate for moving
00396         if (mRepairRate == 2) {
00397                 rate = Rules::GetFloat("RepairStill", 0.02);    // sitting still
00398                 Planet * p = InOrbit();
00399                 if (p != NULL) {
00400                         rate = Rules::GetFloat("RepairOrbit", 0.03);    // while in orbit
00401                         if (GetOwner() == p->GetOwner()) {
00402                                 rate = Rules::GetFloat("RepairOrbitOwned", 0.05);       // of your own planet
00403                                 if (p->GetBaseDesign() != NULL)
00404                                         rate = p->GetBaseDesign()->GetRepairRate();     // with a base
00405                         }
00406                 }
00407         }
00408 
00409         // Apply IS bonus
00410         rate *= GetOwner()->RepairFactor();
00411 
00412         // Add Fuel transport bonus
00413         rate += GetMaxRepairRate();
00414 
00415         long repair;
00416         for (int i = 0; i < mStacks.size(); ++i) {
00417                 if (mStacks[i].GetDamage() > 0) {
00418                         repair = max(1L, long(mStacks[i].GetDesign()->GetArmor(GetOwner()) * rate + .5));
00419                         mStacks[i].SetDamage(max(0L, mStacks[i].GetDamage() - repair));
00420                 }
00421         }
00422 }
00423 
00424 void Fleet::ProcessTransport(bool load, bool dunnage)
00425 {
00426         if (mOrders.size() == 0 || mOrders[0]->GetType() != OT_TRANSPORT)
00427                 return;
00428 
00429         WayOrderTransport * order = dynamic_cast<WayOrderTransport *>(mOrders[0]);
00430         if (!IsWith(*order->GetLocation()))
00431                 return;
00432 
00433         CargoHolder * dest = dynamic_cast<CargoHolder *>(order->NCGetLocation());
00434         if (dest == NULL)
00435                 return;
00436 
00437         for (int i = FUEL; i < Rules::MaxMinType; ++i) {
00438                 if (load)
00439                         ProcessLoad(dest, order->GetAction(i), i, order->GetValue(i), dunnage);
00440                 else
00441                         ProcessUnload(dest, order->GetAction(i), i, order->GetValue(i));
00442         }
00443 }
00444 
00445 void Fleet::Colonize()
00446 {
00447         if (mOrders.size() == 0 || mOrders[0]->GetType() != OT_COLONIZE)
00448                 return;
00449 
00450         if (!CanColonize()) {
00451                 NCGetOwner()->AddMessage("Colonization failed - no colonization pod", this);
00452                 return;
00453         }
00454 
00455         Planet * target = InOrbit();
00456         if (target == NULL) {
00457                 NCGetOwner()->AddMessage("Colonization failed - not in orbit", this);
00458                 return;
00459         }
00460 
00461         if (target->GetPopulation() > 0) {
00462                 Message * mess = NCGetOwner()->AddMessage("Colonization failed - world occupied", this);
00463                 mess->AddItem("World being colonized", target);
00464                 mess->AddItem("Owner", target->GetOwner());
00465                 return;
00466         }
00467 
00468         if (GetPopulation() <= 0) {
00469                 Message * mess = NCGetOwner()->AddMessage("Colonization failed - no colonists", this);
00470                 mess->AddItem("World being colonized", target);
00471                 return;
00472         }
00473 
00474         target->Invade(NCGetOwner(), GetPopulation() + GetCost().GetCrew());
00475         Scrap(true);
00476 }
00477 
00478 void Fleet::CheckMerge()
00479 {
00480         if (mOrders.size() == 0 || mOrders[0]->GetType() != OT_MERGE)
00481                 return;
00482 
00483         Fleet * f = dynamic_cast<Fleet *>(mOrders[0]->NCGetLocation());
00484         if (f == NULL) {
00485                 NCGetOwner()->AddMessage("Merge failed - destination is not a fleet", this);
00486                 return;
00487         }
00488 
00489         if (f->GetOwner() != GetOwner()) {
00490                 NCGetOwner()->AddMessage("Merge failed - destination fleet is not owned", this);
00491                 return;
00492         }
00493 
00494         MergeTo(f);
00495 }
00496 
00497 void Fleet::Scrap(bool colonize)
00498 {
00499         TechType TechGot = TECH_NONE;
00500         Salvage * salvage = NULL;
00501         Planet * planet = InOrbit();
00502 
00503         //Tech first:
00504         //Is ship in orbit with base
00505         if (planet && Rules::TechScrap(planet)) {
00506                 // regular tech
00507                 TechGot = Rules::TechFleet(planet->GetOwner(), this);
00508 
00509                 // TODO MT tech from scrapping
00510         }
00511 
00512         // calc minerals
00513         if (planet) {
00514                 double percent = Rules::ScrapRecover(planet, colonize);
00515                 long tempMins = 0;
00516                 for (int i = 0; i < Rules::MaxMinType; ++i)
00517                 {
00518                         long temp = GetContain(i);
00519                         temp += long(GetCost()[i] * percent + .5);
00520                         planet->AdjustAmounts(i, temp);
00521                         tempMins += temp;
00522                 }
00523 
00524                 if (GetOwner() == planet->GetOwner()) {
00525                         planet->AdjustAmounts(POPULATION, GetPopulation());
00526                         planet->AdjustPopulation(GetCost().GetCrew());  // recover crew too
00527                 }
00528         } else {
00529                 Salvage * salvage = TheGalaxy->AddSalvage(*this);
00530                 double percent = Rules::ScrapRecover(NULL, false);
00531                 for (int i = 0; i < Rules::MaxMinType; ++i) {
00532                         long temp = GetContain(i);
00533                         temp += long(GetCost()[i] * percent + .5);
00534                         salvage->AdjustAmounts(i, temp);
00535                 }
00536         }
00537 
00538         int URGain = 0;
00539         // calc resources
00540         if (!colonize && planet) {
00541                 int percent = Rules::ScrapResource(planet);
00542                 if (percent > 0) {
00543                         long temp = GetCost().GetResources() * percent;
00544                         planet->AddScrapRes(temp);
00545                         URGain = (temp*planet->GetResources())/(temp+planet->GetResources());
00546                 }
00547         }
00548 
00549         string str2;
00550         // send message to the fleet owner
00551         // - note that both the fleet owner and the planet owner get a message, even if they are the same player
00552 //      if (!planet || planet->GetOwner() != GetOwner())        // add this line to eliminate a (mostly) duplicate message
00553         {
00554                 // This is not the same as classic Stars!, that passes the fleet as part of the
00555                 // message and if you reuse the fleet, it points at the new fleet - I didn't think that was important to preserve - EK
00556                 str2 = GetName(GetOwner());
00557                 Message * mess;
00558                 if (colonize) {
00559                         mess = NCGetOwner()->AddMessage("Colonize attempt", planet);
00560                         mess->AddItem("Fleet name", GetName(GetOwner()));
00561                 } else if (planet) {
00562                         mess = NCGetOwner()->AddMessage("Scrap fleet at planet", planet);
00563                         mess->AddItem("Fleet name", GetName(GetOwner()));
00564                         mess->AddLong("UR resource gain", URGain);
00565                 } else {
00566                         mess = NCGetOwner()->AddMessage("Scrap fleet in space", salvage);
00567                         mess->AddItem("Fleet name", GetName(GetOwner()));
00568                 }
00569         }
00570 
00571         // send message to the planet owner and give tech
00572         if (!colonize && planet)
00573         {
00574                 str2.erase();
00575                 if (planet->GetOwner() != GetOwner()) {
00576                         str2 = GetOwner()->GetSingleName();
00577                         str2 += " ";
00578                 }
00579                 str2 += "Fleet #" + Long2String(mID);
00580 
00581                 Message * mess;
00582                 mess = planet->NCGetOwner()->AddMessage("Fleet scrapped at planet", planet);
00583                 mess->AddItem("Fleet name", str2);
00584                 mess->AddLong("UR resource gain", URGain);
00585                 if (TechGot >= 0) {
00586                         mess->AddItem("Tech field gained", Rules::GetTechName(TechGot));
00587                         long res = planet->GetOwner()->TechCost(TechGot);
00588                         mess->AddLong("Tech resources gained", res);
00589                         planet->NCGetOwner()->SetGotTech(true);
00590                         planet->NCGetOwner()->GainTech(TechGot, res);
00591                 }
00592         }
00593 
00594         KillFleet();
00595 }
00596 
00597 void Fleet::KillFleet()
00598 {
00599         const Planet * planet = InOrbit();
00600         Player * player;
00601         const Fleet * fleet;
00602 
00603         for (int i = 0; i < mChasers.size(); ++i) {
00604                 player = TheGame->NCGetPlayer(mChasers[i].player);
00605                 if (player != NULL) {
00606                         fleet = player->GetFleet(mChasers[i].fleet);
00607                         Message * mess = player->AddMessage("Fleet has vanished", fleet);
00608                         if (planet == NULL)
00609                                 mess->AddItem("Last location", new Location(*this), true);
00610                         else
00611                                 mess->AddItem("Last location", planet);
00612                 }
00613         }
00614 
00615         NCGetOwner()->DeleteFleet(this);
00616 }
00617 
00618 void Fleet::FreighterReproduction()
00619 {
00620         assert(GetOwner()->FreighterReproduction() > epsilon);
00621 
00622         if (GetPopulation() <= 0)
00623                 return;
00624 
00625         long Grow = long(GetPopulation() * GetOwner()->FreighterReproduction() * GetOwner()->GrowthRate()) / Rules::PopEQ1kT;
00626         long Over = 0;
00627         Planet * p = NULL;
00628 
00629         if (GetCargoMass() + Grow > GetCargoCapacity())
00630                 Over = (GetCargoCapacity() - GetCargoMass()) * Rules::PopEQ1kT;
00631 
00632         Grow = Grow * Rules::PopEQ1kT - Over;
00633         AdjustPopulation(Grow);
00634         if (Over) {
00635                 p = InOrbit();
00636                 if (p != NULL && p->GetOwner() == GetOwner())
00637                         p->AdjustPopulation(Over);
00638                 else
00639                         Over = 0;       // if not in orbit of an owned world, overflow is lost
00640         }
00641 
00642         if (Grow > 0 || Over > 0) {
00643                 Message * mess = NCGetOwner()->AddMessage("Freighter growth", this);
00644                 mess->AddLong("Growth", Grow);
00645                 if (Over > 0) {
00646                         mess->AddLong("Overflow amount", Over);
00647                         mess->AddItem("Overflow world", p);
00648                 }
00649         }
00650 }
00651 
00652 // return true if the fleet isn't done moving yet (following a fleet that hasn't finished moving)
00653 bool Fleet::Move()
00654 {
00655         if (mHasMoved)  // should never happen
00656                 return false;
00657 
00658         if (mOrders.size() <= 1 || IsWith(*mOrders[1]->GetLocation()) || mOrders[1]->GetSpeed() == 0) {
00659                 mHasMoved = true;
00660                 return false;
00661         }
00662 
00663         const Planet * destp;
00664         // check recieving gate first
00665         destp = dynamic_cast<const Planet *>(mOrders[1]->GetLocation());
00666 
00667         if (destp == NULL && !mOrders[1]->GetLocation()->SeenBy(GetOwner())) {
00668                 NCGetOwner()->AddMessage("Error: Targeting unseen", this);
00669                 return false;
00670         }
00671 
00672         if (Randodd(GetOwner()->EngineFailure(mOrders[1]->GetSpeed()))) {
00673                 Message * mess = NCGetOwner()->AddMessage("Engines failed");
00674                 mess->AddItem("", this);
00675                 mHasMoved = true;
00676                 return false;
00677         }
00678 
00679         // is fleet gating?
00680         if (mOrders[1]->GetSpeed() == -1) {
00681                 mHasMoved = true;
00682                 const Component * send = NULL;
00683                 const Component * recieve = NULL;
00684                 Planet * p;
00685                 // check recieving gate first
00686                 if (destp != NULL && destp->GetBaseNumber() >= 0 && destp->GetOwner()->GetRelations(GetOwner()) >= PR_FRIEND)
00687                         recieve = destp->GetBaseDesign()->GetGate();
00688 
00689                 p = InOrbit();
00690                 if (p != NULL && p->GetBaseNumber() >= 0 && p->GetOwner()->GetRelations(GetOwner()) >= PR_FRIEND)
00691                         send = p->GetBaseDesign()->GetGate();
00692 
00693                 if (send == NULL && CanJumpGate())
00694                         send = recieve;
00695                 else if (send && recieve && GetCargoMass() > 0 && !GetOwner()->GateCargo()) {
00696                         // Unload
00697                         if (send && recieve && p->GetOwner() == GetOwner()) {
00698                                 Message * mess = NCGetOwner()->AddMessage("Can't gate cargo - cargo dropped");
00699                                 mess->AddItem("", this);
00700                                 mess->AddItem("Cargo dropped on", p);
00701                                 for (int i = 0; i < Rules::MaxMinType; ++i) {
00702                                         p->AdjustAmounts(i, GetContain(i));
00703                                         AdjustAmounts(i, -GetContain(i));
00704                                 }
00705 
00706                                 p->AdjustAmounts(POPULATION, GetPopulation());
00707                                 AdjustAmounts(POPULATION, -GetPopulation());
00708                         } else {
00709                                 Message * mess = NCGetOwner()->AddMessage("Can't gate cargo - gate aborted");
00710                                 mess->AddItem("", this);
00711                                 return false;
00712                         }
00713                 }
00714 
00715                 if (send == NULL) {
00716                         Message * mess = NCGetOwner()->AddMessage("No sending gate");
00717                         mess->AddItem("", this);
00718                         if (p != NULL)
00719                                 mess->AddItem("Sending", p);
00720                         return false;
00721                 }
00722 
00723                 if (recieve == NULL) {
00724                         Message * mess = NCGetOwner()->AddMessage("No recieving gate");
00725                         mess->AddItem("", this);
00726                         mess->AddItem("Destination", mOrders[1]->GetLocation());
00727                         return false;
00728                 }
00729 
00730                 // range overgate damage
00731                 double rdam = Rules::OverGateRange(send->GetGateRange(), long(Distance(*mOrders[1]->GetLocation())));
00732                 if (rdam > 1.0 - epsilon) {
00733                         Message * mess = NCGetOwner()->AddMessage("Gate too far");
00734                         mess->AddItem("", this);
00735                         mess->AddItem("Destination", mOrders[1]->GetLocation());
00736                         return false;
00737                 }
00738 
00739                 // mass overgate damage, total damage, and void chance
00740                 double mdam, tdam, vodds;
00741                 bool massOK = true;
00742                 bool checkdam = rdam > epsilon;
00743                 for (int i = 0; i < mStacks.size(); ++i) {
00744                         mdam = Rules::OverGateMass(send->GetGateMass(), recieve->GetGateMass(), mStacks[i].GetDesign()->GetMass());
00745                         if (mdam > 1.0 - epsilon)
00746                                 massOK = false;
00747                         else if (mdam > epsilon)
00748                                 checkdam = true;
00749                 }
00750 
00751                 if (!massOK) {
00752                         Message * mess = NCGetOwner()->AddMessage("Ships too massive for gate");
00753                         mess->AddItem("", this);
00754                         mess->AddItem("Destination", mOrders[1]->GetLocation());
00755                         return false;
00756                 }
00757 
00758                 if (checkdam) {
00759                         long adam;      // armor damage
00760                         Message * mess = NCGetOwner()->AddMessage("Ships damaged by overgating");
00761                         mess->AddItem("", this);
00762                         for (int i = 0; i < mStacks.size(); ++i) {
00763                                 mdam = Rules::OverGateMass(send->GetGateMass(), recieve->GetGateMass(), mStacks[i].GetDesign()->GetMass());
00764                                 tdam = min(0.98, mdam + rdam - mdam * rdam);
00765                                 vodds = (1.0 - (1.0 - rdam) * (1.0 - tdam)) / 3.0 * GetOwner()->OvergateLossFactor();
00766                                 adam = long(tdam * mStacks[i].GetDesign()->GetArmor(GetOwner()) + .5);
00767                                 long lost = mStacks[i].DamageAllShips(adam);
00768                                 if (mStacks[i].GetCount() > 0 && vodds > epsilon) {
00769                                         // any survivors face the void
00770                                         for (int j = 0; j < mStacks[i].GetCount(); ++j) {
00771                                                 if (Randodd(vodds))
00772                                                         lost++;
00773                                         }
00774                                 }
00775                                 if (lost > 0) {
00776                                         mess->AddItem("Ships lost name", mStacks[i].GetDesign()->GetName());
00777                                         mess->AddLong("Ships lost number", lost);
00778                                         if (mStacks[i].KillShips(lost, false)) {
00779                                                 mStacks.erase(mStacks.begin()+i);
00780                                                 i--;    // adjust loop counter if the whole stack is gone
00781                                         }
00782                                         ResetDefaults();
00783                                 }
00784                         }
00785 
00786                         if (GetShipCount() == 0) {
00787                                 KillFleet();
00788                                 return false;
00789                         }
00790                 }
00791 
00792                 // fleet arrives
00793                 SetLocation(*destp);
00794                 Refuel(destp);
00795                 TheGame->MoveAlsoHere(this);
00796                 mRepairRate = 0;
00797                 return false;
00798         }       // if gating
00799 
00800         if (mChasing && !mChasing->mHasMoved)
00801                 return true;
00802 
00803         double dist = Distance(*mOrders[1]->GetLocation());
00804         // find the min speed needed to get to go the distance (MM reduction & mine field hit odds)
00805         long speed = mOrders[1]->GetSpeed();
00806         speed--;
00807         while (dist < speed * speed)
00808                 speed--;
00809         speed++;
00810 
00811         Location dest = *mOrders[1]->GetLocation();
00812         if (long(dist) > speed * speed) {
00813                 MoveToward(*this, dest, &mPX, &mPY, speed * speed);
00814                 dist = Distance(mPX, mPY);
00815         } else {
00816                 mPX = dest.GetPosX() + epsilon;
00817                 mPY = dest.GetPosY() + epsilon;
00818         }
00819 
00820         double fuelU = GetFuelUsage(speed);
00821         if (GetFuel() < long(fuelU * dist +.5)) {
00822                 dist = GetFuel() / fuelU;
00823                 mOrders[1]->SetSpeed(GetMinFreeSpeed());        // for next turn
00824                 Message * mess = NCGetOwner()->AddMessage("Out of fuel, slowing down");
00825                 mess->AddItem("", this);
00826                 MoveToward(*this, dest, &mPX, &mPY, long(dist + .5));
00827                 dist = Distance(mPX, mPY);
00828         }
00829 
00830         mRepairRate = 1;
00831         double safe;
00832         safe = TheGame->ClosestMinefield(&mPossibleMines, this, mPX, mPY);
00833         if (safe > dist || mPossibleMines.size() == 0) {
00834                 SetLocation(long(mPX), long(mPY));
00835                 AdjustFuel(-long(fuelU * Distance(mStartPos) + .5));
00836                 mHasMoved = true;
00837                 if (IsWith(*(mOrders[1]->GetLocation()))) {
00838                         const Wormhole * wh = dynamic_cast<const Wormhole *>(mOrders[1]->GetLocation());
00839                         if (wh != NULL && wh->GetAttached() != NULL) {
00840                                 SetLocation(*wh->GetAttached());
00841                                 wh->Enter(GetID());
00842                                 wh->GetAttached()->Exit(GetID());
00843                         }
00844                 }
00845 
00846                 TheGame->MoveAlsoHere(this);
00847                 return false;
00848         } else {
00849                 while (safe > 2.0 && dest.Distance(mPX, mPY) > 1.0) {
00850                         MoveToward(mStartPos, dest, &mPX, &mPY, long(safe));    // move closer
00851                         safe = TheGame->ClosestMinefield(&mPossibleMines, this, mPX, mPY);
00852                 }
00853 
00854                 if (dest.Distance(mPX, mPY) > 1.0) {
00855                         while (!Move1LY(dest, speed))
00856                                 ;
00857                 }
00858 
00859                 SetLocation(long(mPX), long(mPY));
00860                 AdjustFuel(long(fuelU * Distance(mStartPos) + .5));
00861 
00862                 if (IsWith(*(mOrders[1]->GetLocation()))) {
00863                         const Wormhole * wh = dynamic_cast<const Wormhole *>(mOrders[1]->GetLocation());
00864                         if (wh != NULL && wh->GetAttached() != NULL) {
00865                                 SetLocation(*wh->GetAttached());
00866                                 wh->Enter(GetID());
00867                                 wh->GetAttached()->Exit(GetID());
00868                         }
00869                 }
00870 
00871                 mHasMoved = true;
00872                 TheGame->MoveAlsoHere(this);
00873                 return false;
00874         }
00875 }
00876 
00877 bool Fleet::Move1LY(Location & dest, long speed)
00878 {
00879         mRepairRate = 1;
00880         mDistMoved++;
00881         MoveToward(mStartPos, dest, &mPX, &mPY, mDistMoved);
00882         deque<bool> mineType;
00883         mineType.insert(mineType.begin(), Rules::MaxMineType, false);
00884         for (int i = 0; i < mPossibleMines.size(); i++) {
00885                 if (!mineType[mPossibleMines[i]->GetMineType()]) {
00886                         mineType[mPossibleMines[i]->GetMineType()] = true;
00887                         if (mPossibleMines[i]->TestCollide(*this, speed)) {
00888                                 mPossibleMines[i]->ReduceFieldCollision();
00889                                 TakeMinefieldDamage(mPossibleMines[i]);
00890                                 return false;
00891                         }
00892                 }
00893         }
00894 
00895         if (dest.Distance(mPX, mPY) > 1.0)
00896                 return true;
00897         else
00898                 return false;
00899 }
00900 
00901 bool Fleet::CanLoadBy(const Player * player) const
00902 {
00903         return mCanLoadBy[player->GetID() - 1];
00904 }
00905 
00906 void Fleet::SetCanLoadBy(const Player * player)
00907 {
00908         mCanLoadBy[player->GetID()-1] = true;
00909 }
00910 
00911 long Fleet::GetCloak(const Player *, bool) const
00912 {
00913         return Rules::CloakValue(GetCloaking(), GetMass() + (GetOwner()->CloakCargo() ? 0 : GetCargoMass()));
00914 }
00915 
00916 long Fleet::GetFuelNeeded() const       // fuel needed to make it to the next waypoint (waypoint 1)
00917 {
00918         if (mOrders.size() < 1)
00919                 return 0;
00920         else
00921                 return long(GetFuelUsage(mOrders[1]->GetSpeed()) * Distance(*mOrders[1]->GetLocation()) + .5);
00922 }
00923 
00924 void Fleet::AdjustFuel(long amount)
00925 {
00926         mFuel += amount;
00927         if (mFuel < 0)
00928                 mFuel = 0;
00929         
00930         if (mFuel > GetFuelCapacity())
00931                 mFuel = GetFuelCapacity();
00932 }
00933 
00934 void Fleet::Refuel(const Planet * p)
00935 {
00936         if (p && p->GetBaseNumber() >= 0 && p->GetBaseDesign()->CanRefuel() && p->GetOwner()->GetRelations(GetOwner()) >= PR_FRIEND)
00937                 mFuel = GetFuelCapacity();
00938 }
00939 
00940 double Fleet::GetFuelUsage(long speed) const
00941 {
00942         double MinUsage = -1;
00943         double MinUsageDone = 0;
00944         long MinUsageCapacity = 0;
00945         double Result = 0;
00946         long CargoLeft = GetCargoMass();
00947 
00948         // first calc everything where we know it's cargo
00949         deque<Stack>::const_iterator iter;
00950         for (iter = mStacks.begin(); iter != mStacks.end(); ++iter) {
00951                 if (iter->GetDesign()->GetFreeSpeed() >= speed) {
00952                         // ships going at free speed or less don't care about mass, will carry maximum cargo
00953                         Result += iter->GetDesign()->GetFuelUsage(speed) * iter->GetCount();
00954                         if (CargoLeft > 0)
00955                                 CargoLeft -= min(CargoLeft, iter->GetDesign()->GetCargoCapacity() * iter->GetCount());
00956                 } else if (GetCargoMass() == GetCargoCapacity()) {
00957                         // if fleet is packed full, everything goes with full loads
00958                         Result += iter->GetDesign()->GetFuelUsage(speed) *
00959                                         iter->GetCount() *
00960                                         (iter->GetDesign()->GetMass() + iter->GetDesign()->GetCargoCapacity()) *
00961                                         GetOwner()->FuelFactor();
00962                         CargoLeft -= iter->GetDesign()->GetCargoCapacity() * iter->GetCount();
00963                 } else {
00964                         // potentially partially full ships, calc with empty load, add fuel for cargo later
00965                         Result += iter->GetDesign()->GetFuelUsage(speed) *
00966                                         iter->GetCount() *
00967                                         iter->GetDesign()->GetMass() *
00968                                         GetOwner()->FuelFactor();
00969                 }
00970         }
00971 
00972         // pay for any left over cargo
00973         while (CargoLeft > 0) {
00974                 for (iter = mStacks.begin(); iter != mStacks.end(); ++iter) {
00975                         if (iter->GetDesign()->GetFreeSpeed() <= speed)
00976                                 ;       // already done
00977                         else if (iter->GetDesign()->GetCargoCapacity() == 0)
00978                                 ;       // can't carry cargo, fuel for hull already done
00979                         else {
00980                                 double usage = iter->GetDesign()->GetFuelUsage(speed);
00981                                 if (usage > MinUsageDone && (MinUsage == -1 || usage < MinUsage)) {
00982                                         MinUsage = usage;
00983                                         MinUsageCapacity = iter->GetDesign()->GetCargoCapacity() * iter->GetCount();
00984                                 }
00985                         }
00986                 }
00987 
00988                 Result += MinUsage * MinUsageCapacity * GetOwner()->FuelFactor();
00989                 CargoLeft -= min(CargoLeft, MinUsageCapacity);
00990                 MinUsageDone = MinUsage;
00991                 MinUsage = -1;
00992         }
00993 
00994         return Result / 200.0;
00995 }
00996 
00997 void Fleet::MergeTo(Fleet * to)
00998 {
00999         // merge ships
01000         to->AddChaser(mChasers);
01001         deque<Stack>::iterator iter;
01002         for (iter = mStacks.begin(); iter != mStacks.end(); ++iter) {
01003                 to->Merge(*iter, iter->GetCount(), iter->GetDamaged(), mID);
01004                 mStacks.erase(iter);
01005         }
01006 
01007         // move cargo and fuel
01008         for (int i = FUEL; i < Rules::MaxMinType; ++i) {
01009                 to->AdjustAmounts(i, GetContain(i));
01010                 AdjustAmounts(i, -GetContain(i));
01011         }
01012 }
01013 
01014 void Fleet::MergeTo(Fleet * to, const Ship * design, long number, long damaged)
01015 {
01016         Stack* stack = NULL; // stack to be merged
01017         for (deque<Stack>::iterator iter = mStacks.begin(); iter != mStacks.end(); ++iter)
01018         {
01019                 if (design == iter->GetDesign())
01020                 {
01021                         stack  = &(*iter);
01022                         break;
01023                 }
01024         }
01025 
01026         if (stack == NULL)
01027         {
01028                 Message * mess = NCGetOwner()->AddMessage("Error: Split or merge a design not in the fleet", this);
01029                 mess->AddItem("Ship design", design->GetName());
01030                 return;
01031         }
01032 
01033         if (number > stack->GetCount()) {
01034                 Message * mess = NCGetOwner()->AddMessage("Error: Split or merge too many ships", this);
01035                 mess->AddItem("Ship design", design->GetName());
01036                 mess->AddLong("Attempted transfer", number);
01037                 number = stack->GetCount();
01038                 mess->AddLong("Actual transfer", number);
01039         }
01040 
01041         if (damaged > stack->GetDamaged()) {
01042                 Message * mess = NCGetOwner()->AddMessage("Error: Split or merge too many damaged ships", this);
01043                 mess->AddItem("Ship design", design->GetName());
01044                 mess->AddLong("Attempted transfer", damaged);
01045                 damaged = stack->GetDamaged();
01046                 mess->AddLong("Actual transfer", damaged);
01047         }
01048 
01049         // 20 ships, 15 damaged, transfer 8, damaged needs to be at least 3
01050         if (damaged < stack->GetDamaged() + number - stack->GetCount()) {
01051                 Message * mess = NCGetOwner()->AddMessage("Error: Split or merge not enough damaged ships", this);
01052                 mess->AddItem("Ship design", design->GetName());
01053                 mess->AddLong("Attempted transfer", damaged);
01054                 damaged = stack->GetDamaged() + number - stack->GetCount();
01055                 mess->AddLong("Actual transfer", damaged);
01056         }
01057         
01058         // original capacity of fleet
01059         long original_capacity = GetCargoCapacity();
01060 
01061         // perform merge
01062         to->Merge(*stack, number, damaged, mID);
01063         to->AddChaser(mChasers);
01064 
01065         // handle cargo
01066         long capacity = stack->GetDesign()->GetCargoCapacity() * number;
01067         double ratio = double(capacity)/double(original_capacity);
01068         long amt;
01069         if (capacity > 0)
01070         {       
01071                 // Population
01072                 amt = GetContain(POPULATION);
01073                 if(amt != 0)
01074                 {
01075                         // Only in Multiples of PopEQ1kT;
01076                         amt = long(amt * ratio) / Rules::PopEQ1kT;
01077                         amt *= Rules::PopEQ1kT;
01078                 
01079                         to->AdjustAmounts(POPULATION, amt);
01080                         AdjustAmounts(POPULATION, -amt);
01081                 }
01082                 
01083                 // Minerals
01084                 for (int i = 0; i < Rules::MaxMinType; ++i)
01085                 {
01086                         amt = GetContain(i);
01087                         if(amt != 0)
01088                         {
01089                                 amt = long(amt * ratio);
01090 
01091                                 to->AdjustAmounts(i, amt);
01092                                 AdjustAmounts(i, -amt);
01093                         }
01094                 }
01095         }
01096 
01097         // handle fuel
01098         original_capacity = GetFuelCapacity();
01099         capacity = stack->GetDesign()->GetFuelCapacity() * number;
01100         ratio = double(capacity)/double(original_capacity);
01101         amt = long(GetFuel() * ratio);
01102         to->AdjustAmounts(FUEL, amt);
01103         AdjustAmounts(FUEL, -amt);
01104 
01105         ResetDefaults();
01106 
01107         stack->SetCount(stack->GetCount() - number);
01108         stack->SetDamaged(stack->GetDamaged() - damaged);
01109         if (stack->GetCount() <= 0)
01110                 remove_if(mStacks.begin(),mStacks.end(),bind2nd(equal_to<Stack>(),*stack));
01111 }
01112 
01113 void Fleet::Merge(Stack &stack, long number, long damaged, long Origin)
01114 {
01115         ResetDefaults();
01116         deque<Stack>::iterator iter;
01117         for (iter = mStacks.begin(); iter != mStacks.end(); ++iter) {
01118                 if (iter->GetDesign() == stack.GetDesign())
01119                         break;
01120         }
01121 
01122         if (iter == mStacks.end()) {
01123                 iter = mStacks.insert(iter, Stack());
01124                 iter->SetDesign(stack.GetDesign());
01125         }
01126 
01127         // damage
01128         long newdamage = 0;
01129         long Dcount = iter->GetDamaged() + damaged;
01130         if (Dcount > 0)
01131                 newdamage = (iter->GetDamaged() * iter->GetDamage() + damaged * stack.GetDamage() + Dcount/2) / Dcount;
01132 
01133         iter->SetCount(iter->GetCount() + number);
01134         iter->SetDamaged(iter->GetDamaged() + damaged);
01135         iter->SetDamage(newdamage);
01136 
01137         // the idea is that each stack of ships will contain an array holding the number of ships that came from which fleet
01138         deque<Stack::Origin>::iterator i2;
01139         long nleft = number;
01140         long dleft = damaged;
01141         for (i2 = stack.mOrigins.begin(); i2 != stack.mOrigins.end(); ++i2) {
01142                 long n = long(0.5 + double(i2->ships) * number / stack.GetCount());
01143                 long d = long(0.5 + double(i2->damaged) * damaged / stack.GetDamaged());
01144                 if (n < d)
01145                         n = d;
01146 
01147                 iter->AddFromFleet(i2->fleet, n, d);
01148                 i2->ships -= n;
01149                 i2->damaged -= n;
01150                 nleft -= n;
01151                 dleft -= d;
01152         }
01153 
01154         iter->AddFromFleet(Origin, nleft, dleft);
01155 }
01156 
01157 void Fleet::AddShips(long Type, long number)
01158 {
01159         deque<Stack>::iterator iter;
01160         for (iter = mStacks.begin(); iter != mStacks.end(); ++iter) {
01161                 if (iter->GetDesign() == GetOwner()->GetShipDesign(Type))
01162                         break;
01163         }
01164 
01165         if (iter == mStacks.end()) {
01166                 iter = mStacks.insert(iter, Stack());
01167                 iter->SetDesign(GetOwner()->GetShipDesign(Type));
01168         }
01169 
01170         iter->SetCount(iter->GetCount() + number);
01171 }
01172 
01173 void Fleet::ResetSeen()
01174 {
01175         CargoHolder::ResetSeen();
01176 
01177         mCanLoadBy.clear();
01178         mCanLoadBy.insert(mCanLoadBy.begin(), TheGame->NumberPlayers(), false);
01179 }
01180 
01181 void Fleet::SetSeenBy(long p, bool seen)
01182 {
01183         mSeenBy[p] = seen;
01184 
01185         SetSeenDesign(p, seen, TheGame->GetPlayer(p+1)->ScanDesign());
01186 }
01187 
01188 void Fleet::SetSeenDesign(long p, bool seen, bool design)
01189 {
01190         deque<Stack>::iterator iter;
01191         for (iter = mStacks.begin(); iter != mStacks.end(); ++iter) {
01192                 if (design)
01193                         const_cast<Ship *>(iter->GetDesign())->SetSeenDesign(p, seen);
01194                 else
01195                         const_cast<Ship *>(iter->GetDesign())->SetSeenHull(p, seen);
01196         }
01197 }
01198 
01199 void Fleet::ChangeWaypoints(WayOrderList & wol)
01200 {
01201         if (GetOwner()->WriteXFile())
01202                 NCGetOwner()->AddOrder(new WaypointOrder(GetID(), &mOrders));
01203         else {
01204                 for (int i = 0; i < mOrders.size(); ++i)
01205                         delete mOrders[i];
01206         }
01207 
01208         wol.SetNoDelete();
01209         mOrders = wol.GetOrders();
01210 }
01211 
01212 void Fleet::SetRepeat(bool repeat)
01213 {
01214         if (repeat != mRepeatOrders && GetOwner()->WriteXFile())
01215                 NCGetOwner()->AddOrder(new TypedOrder<bool>(&mRepeatOrders, AddBool, "Repeat", "Fleet", Long2String(GetID()).c_str()));
01216 
01217         mRepeatOrders = repeat;
01218 }
01219 
01220 void Fleet::SetBattlePlan(long bp)
01221 {
01222         if (bp != mBattlePlan && GetOwner()->WriteXFile())
01223                 NCGetOwner()->AddOrder(new TypedOrder<long>(&mBattlePlan, AddLong, "BattlePlan"));
01224 
01225         mBattlePlan = bp;
01226 }
01227 
01228 void Fleet::SetStartOrders(Planet * planet)
01229 {
01230         WayOrder * wo = new WayOrder(planet);
01231         mOrders.insert(mOrders.begin(), wo);
01232         SetNextRoute(planet);
01233 }
01234 
01235 void Fleet::SetNextRoute(Planet * planet)
01236 {
01237         if (planet && planet->GetRoute() != NULL) {
01238                 WayOrder * wo = new WayOrder(const_cast<Planet *>(planet->GetRoute()));
01239                 wo->SetType(OT_ROUTE);
01240                 wo->SetSpeed(GetBestSpeed(planet, planet->GetRoute(), OT_ROUTE));
01241                 mOrders.insert(mOrders.end(), wo);
01242         }
01243 }
01244 
01245 long Fleet::GetBestSpeed(const Location * L1, const Location * L2, OrderType ot)
01246 {
01247         long dist = long(L1->Distance(*L2));
01248 
01249         const Planet * p1 = dynamic_cast<const Planet *>(L1);
01250         const Planet * p2 = dynamic_cast<const Planet *>(L2);
01251 
01252         bool oneway = (ot == OT_COLONIZE) || CanColonize();
01253 
01254         if (p2 && p2->GetBaseNumber() >= 0) {
01255                 if (p2->GetBaseDesign()->CanRefuel())
01256                         oneway = true;
01257 
01258                 if (GetCargoMass() == 0 || GetOwner()->GateCargo()) {
01259                         if (p1 && p1->GetBaseNumber() >= 0) {
01260                                 const Component * gate = p1->GetBaseDesign()->GetGate();
01261                                 if (gate->GetGateMass() >= GetMaxMass() && gate->GetRange() >= dist) {
01262                                         gate = p2->GetBaseDesign()->GetGate();
01263                                         if (gate->GetGateMass() >= GetMaxMass())
01264                                                 return -1;      // gate
01265                                 }
01266                         }
01267                 }
01268         }
01269 
01270         long speed;
01271         double fu;
01272 
01273         // use battle speed as minimum speed, battle speed is normally the highest speed at which the engine is safe at 120% fuel efficiency
01274         for (speed = GetMinSafeSpeed(); speed > GetMinBattleSpeed(); --speed) {
01275                 fu = GetFuelUsage(speed) * dist * (oneway ? 1 : 2);
01276                 if (fu <= GetFuel())
01277                         break;
01278         }
01279 
01280         return speed;
01281 }
01282 
01283 void Fleet::AddChaser(unsigned long p, unsigned long f)
01284 {
01285         assert(p != GetOwner()->GetID());
01286         deque<Chaser>::iterator iter;
01287         for (iter = mChasers.begin(); iter != mChasers.end(); ++iter)
01288                 if (iter->player == p && iter->fleet == f)
01289                         break;
01290 
01291         if (iter == mChasers.end()) {
01292                 iter = mChasers.insert(mChasers.end(),Chaser());
01293                 iter->fleet = f;
01294                 iter->player = p;
01295         }
01296 }
01297 
01298 void Fleet::AddChaser(const deque<Chaser> &w2)
01299 {
01300         deque<Chaser>::const_iterator iter;
01301         for (iter = w2.begin(); iter != w2.end(); ++iter)
01302                 AddChaser(iter->player, iter->fleet);
01303 }
01304 
01305 void Fleet::TakeMinefieldDamage(MineField * field)
01306 {
01307         int dpse;       // damage per standard engine
01308         int dpre;       // damage per ram scoop engine
01309         int temp;
01310         long ReportedDamage = 0;
01311         long ReportedKills = 0;
01312 
01313         dpse = Rules::GetArrayValue("MineFleetDamage", field->GetMineType());
01314         temp = Rules::GetArrayValue("MineShipDamage", field->GetMineType());
01315         if (dpse < temp * GetShipCount())
01316                 dpse = temp * GetShipCount();
01317 
01318         dpre = Rules::GetArrayValue("MineFleetRamDamage", field->GetMineType());
01319         temp = Rules::GetArrayValue("MineShipRamDamage", field->GetMineType());
01320         if (dpre < temp * GetShipCount())
01321                 dpre = temp * GetShipCount();
01322 
01323         if (dpse > 0 || dpre > 0) {
01324                 for (int i = 0; i < mStacks.size(); ++i) {
01325                         if (mStacks[i].GetDesign()->GetFreeSpeed() > 1)
01326                                 temp = dpre;
01327                         else
01328                                 temp = dpse;
01329 
01330                         temp *= mStacks[i].GetDesign()->GetEngines();
01331                         ReportedDamage += temp * mStacks[i].GetCount();
01332                         temp -= min(mStacks[i].GetDesign()->GetShield(GetOwner()), long((temp + 1) / 2));
01333                         long lost = mStacks[i].DamageAllShips(temp);
01334                         ReportedKills += lost;
01335                         if (mStacks[i].KillShips(lost, true)) {
01336                                 mStacks.erase(mStacks.begin()+i);
01337                                 i--;    // adjust loop counter if the whole stack is gone
01338                         }
01339                         ResetDefaults();
01340                 }
01341         }
01342 
01343         Message * mess;
01344         mess = NCGetOwner()->AddMessage("Your fleet hit mine");
01345         mess->AddItem("", this);
01346         mess->AddLong("Damage", ReportedDamage);
01347         mess->AddLong("Kills", ReportedKills);
01348         if (ReportedKills > 0)
01349                 mess->AddItem("Salvage location", new Location(*this), true);
01350         mess = field->NCGetOwner()->AddMessage("Fleet hit your mine");
01351         mess->AddItem("", field);
01352         mess->AddLong("Damage", ReportedDamage);
01353         mess->AddLong("Kills", ReportedKills);
01354         if (ReportedKills > 0)
01355                 mess->AddItem("Salvage location", new Location(*this), true);
01356 }
01357 
01358 void Fleet::RemoteMine(bool ARmining)
01359 {
01360         if (mOrders.size() < 1 || mOrders[0]->GetType() != OT_REMOTEMINE)
01361                 return;
01362 
01363         if (!ARmining && mStartPos != *this)    // Remote mining doesn't happen the same turn a fleet moves
01364                 return;
01365 
01366         Planet * planet = InOrbit();
01367         if (planet == NULL)     // Attempt to remote mine space, fleet is probably enroute, ignore
01368                 return;
01369 
01370         if (planet->GetOwner() != NULL) {
01371                 if (GetOwner()->ARTechType() >= 0 && GetOwner() == planet->GetOwner()) {
01372                         if (!ARmining)
01373                                 return; // AR remote mining his own world, done previously, ignore silently
01374                 } else {
01375                         // Warning message about remote mining an occupied world
01376                         Message * mess;
01377                         mess = NCGetOwner()->AddMessage("Warning: Remote mining an owned world", this);
01378                         mess->AddItem("", planet);
01379                         mess->AddItem("Owner", planet->GetOwner());
01380                         return;
01381                 }
01382         }
01383 
01384         if (GetMines() == 0) {
01385                 // Warning message about remote mining orders without a remote mining capable fleet
01386                 Message * mess;
01387                 mess = NCGetOwner()->AddMessage("Warning: Remote mining without mining ability", this);
01388                 mess->AddItem("", planet);
01389                 return;
01390         }
01391 
01392         planet->Mine(min(GetMines(), Rules::GetConstant("MaxRemoteMining")), GetOwner());
01393 }
01394 
01395 long Fleet::GetShipCount() const
01396 {
01397         long Result = 0;
01398         deque<Stack>::const_iterator iter;
01399         for (iter = mStacks.begin(); iter != mStacks.end(); ++iter)
01400                 Result += iter->GetCount();
01401 
01402         return Result;
01403 }
01404 
01405 long Fleet::TechLevel(TechType tech) const
01406 {
01407         if (tech >= Rules::MaxTechType || CVTechLevel[tech] == -1) {
01408                 // loop through ships
01409                 long Level = 0;
01410                 deque<Stack>::const_iterator iter;
01411 
01412                 if (tech < Rules::MaxTechType) {
01413                         for (iter = mStacks.begin(); iter != mStacks.end(); ++iter) {
01414                                 Level = max(Level, iter->GetDesign()->TechLevel(tech));
01415                         }
01416                 } else {
01417                         for (iter = mStacks.begin(); iter != mStacks.end(); ++iter)
01418                                 Level += iter->GetDesign()->TechLevel(tech);
01419                 }
01420                 if (tech < Rules::MaxTechType)
01421                         const_cast<Fleet *>(this)->CVTechLevel[tech] = Level;
01422 
01423                 return Level;
01424         } else
01425                 return CVTechLevel[tech];
01426 }
01427 
01428 const Cost & Fleet::GetCost() const
01429 {
01430         if (ReCost <= 0 || (TheGame->GetTurnPhase() > TP_TECH_CHECK1 && ReCost < TP_TECH_CHECK1) || (TheGame->GetTurnPhase() > TP_TECH_CHECK2 && ReCost < TP_TECH_CHECK2) || (TheGame->GetTurnPhase() > TP_TECH_CHECK3 && ReCost < TP_TECH_CHECK3)) {
01431                 const_cast<Fleet *>(this)->ReCost = TheGame->GetTurnPhase();
01432                 // loop through ships
01433                 deque<Stack>::const_iterator iter;
01434 
01435                 const_cast<Fleet *>(this)->CVCost.Zero();
01436                 for (iter = mStacks.begin(); iter != mStacks.end(); ++iter) {
01437                         for (CargoType ct = RESOURCES; ct < Rules::MaxMinType; ++ct) {
01438                                 const_cast<Fleet *>(this)->CVCost[ct] += iter->GetDesign()->GetCost(GetOwner())[ct] * iter->GetCount();
01439                         }
01440                 }
01441         }
01442 
01443         return CVCost;
01444 }
01445 
01446 /*
01447 bool Fleet::CanSteal(CargoType ct, CargoHolder * dest) const
01448 {
01449         // loop through ships
01450         bool Result = false;
01451         deque<Stack>::const_iterator iter;
01452 
01453         for (iter = mStacks.begin(); !Result && iter != mStacks.end(); ++iter)
01454                 Result = iter->GetDesign()->CanSteal(ct, dest);
01455 
01456         return Result;
01457 }
01458 */
01459 
01460 long lGetScanPen(long v, const Stack & s, const Player * player)
01461 {
01462         return max(v, s.GetDesign()->GetScanPen(player));
01463 }
01464 
01465 long Fleet::GetScanPen() const
01466 {
01467         if (CVScanPen == -2) {
01468                 const_cast<Fleet *>(this)->CVScanPen =
01469                                 accumulate(     mStacks.begin(),
01470                                                         mStacks.end(),
01471                                                         0,
01472                                                         lGetScanPen,
01473                                                         GetOwner());
01474         }
01475 
01476         return CVScanPen;
01477 }
01478 
01479 long lGetScanSpace(long v, const Stack & s, const Player * player)
01480 {
01481         return max(v, s.GetDesign()->GetScanSpace(player));
01482 }
01483 
01484 long Fleet::GetScanSpace() const
01485 {
01486         if (CVScanSpace != -1) {
01487                 const_cast<Fleet *>(this)->CVScanSpace =
01488                                 accumulate(     mStacks.begin(),
01489                                                         mStacks.end(),
01490                                                         0,
01491                                                         lGetScanSpace,
01492                                                         GetOwner());
01493 
01494                 const_cast<Fleet *>(this)->CVScanSpace = long(CVScanSpace * GetOwner()->SpaceScanFactor());
01495         }
01496 
01497         return CVScanSpace;
01498 }
01499 
01500 long lGetTerraPower(long v, const Stack & s, long type)
01501 {
01502         return v + s.GetDesign()->GetTerraPower(type);
01503 }
01504 
01505 //      to ask for +terra: type is: 0x12 to initial (if deterraformed for some reason) 0x22 to tech of ship owner
01506 //      to ask for -terra: type is: 0x11 to initial, 0x21 to tech of ship owner
01507 long Fleet::GetTerraPower(long type) const
01508 {
01509         return accumulate(      mStacks.begin(),
01510                                                 mStacks.end(),
01511                                                 0,
01512                                                 lGetTerraPower,
01513                                                 type);
01514 }
01515 
01516 long lGetMineAmount(long v, const Stack & s, long type)
01517 {
01518         return v + s.GetDesign()->GetMineAmount(type);
01519 }
01520 
01521 long Fleet::GetMineAmount(long type) const
01522 {
01523         if (CVMineAmount[type-1] == -1) {
01524                 long Result = accumulate(mStacks.begin(),
01525                                                                 mStacks.end(),
01526                                                                 0,
01527                                                                 lGetMineAmount,
01528                                                                 type);
01529                 const_cast<Fleet *>(this)->CVMineAmount[type-1] = Result;
01530         }
01531         return CVMineAmount[type-1];
01532 }
01533 
01534 double Fleet::RadDamage() const
01535 {
01536         double Damage = 0.0;
01537         for (HabType ht = 0; ht < Rules::MaxHabType; ++ht) {
01538                 if (DoesRadiate(ht))
01539                         Damage += GetOwner()->RadDamage(ht);
01540         }
01541 
01542         return Damage;
01543 }
01544 
01545 bool Fleet::DoesRadiate(HabType ht) const
01546 {
01547         if (CVRadiation[ht] != -1)
01548                 return CVRadiation[ht] ? true : false;
01549         const_cast<Fleet *>(this)->CVRadiation[ht] = 1;
01550         for (deque<Stack>::const_iterator i = mStacks.begin(); i != mStacks.end(); ++i)
01551                 if (i->GetDesign()->DoesRadiate(ht))
01552                         return true;
01553         const_cast<Fleet *>(this)->CVRadiation[ht] = 0;
01554         return false;
01555 }
01556 
01557 //Macros for the rest of the functions:
01558 
01559 // Get the sum of all ships in the fleet
01560 // define a local function that adds current total, and component capability * count
01561 // make a member function that accumulates all slots using the local function, and initial value of the hull capability
01562 #define GET_FLEET_SUM(Function)                                                         \
01563         long lGet##Function(long v, const Stack & s)                    \
01564         {                                                                                                               \
01565                 return v + s.GetDesign()->Get##Function() * s.GetCount();       \
01566         }                                                                                                               \
01567                                                                                                                         \
01568         long Fleet::Get##Function() const                                               \
01569         {                                                                                                               \
01570                 if (CV##Function == -1)                                                         \
01571                         const_cast<Fleet *>(this)->CV##Function = accumulate(mStacks.begin(), mStacks.end(), 0, lGet##Function);        \
01572                 return CV##Function;                                                            \
01573         }
01574 #define GET_FLEET_SUMD(Function)                                                        \
01575         double lGet##Function(double v, const Stack & s)                \
01576         {                                                                                                               \
01577                 return v + s.GetDesign()->Get##Function() * s.GetCount();       \
01578         }                                                                                                               \
01579                                                                                                                         \
01580         double Fleet::Get##Function() const                                             \
01581         {                                                                                                               \
01582                 if (!CCalc##Function) {                                                         \
01583                         const_cast<Fleet *>(this)->CCalc##Function = true;\
01584                         const_cast<Fleet *>(this)->CV##Function = accumulate(mStacks.begin(), mStacks.end(), 0, lGet##Function);        \
01585                 }                                                                                                       \
01586                 return CV##Function;                                                            \
01587         }
01588 // Get the max value of all ships in the fleet
01589 #define GET_FLEET_MAXD(Function)                                                        \
01590         double lGetMax##Function(double v, const Stack & s)             \
01591         {                                                                                                               \
01592                 return max(v, s.GetDesign()->Get##Function());          \
01593         }                                                                                                               \
01594                                                                                                                         \
01595         double Fleet::GetMax##Function() const                                  \
01596         {                                                                                                               \
01597                 if (!CCalcMax##Function) {                                                      \
01598                         const_cast<Fleet *>(this)->CCalcMax##Function = true;\
01599                         const_cast<Fleet *>(this)->CVMax##Function = accumulate(mStacks.begin(), mStacks.end(), 0, lGetMax##Function);  \
01600                 }                                                                                                       \
01601                 return CVMax##Function;                                                         \
01602         }
01603 // Get the max value of all ships in the fleet
01604 #define GET_FLEET_MAX(Function)                                                 \
01605         long lGetMax##Function(long v, const Stack & s)         \
01606         {                                                                                                               \
01607                 return max(v, s.GetDesign()->Get##Function());          \
01608         }                                                                                                               \
01609                                                                                                                         \
01610         long Fleet::GetMax##Function() const                                    \
01611         {                                                                                                               \
01612                 if (!CCalcMax##Function) {                                                      \
01613                         const_cast<Fleet *>(this)->CCalcMax##Function = true;\
01614                         const_cast<Fleet *>(this)->CVMax##Function = accumulate(mStacks.begin(), mStacks.end(), 0, lGetMax##Function);  \
01615                 }                                                                                                       \
01616                 return CVMax##Function;                                                         \
01617         }
01618 // Get the min value of all ships in the fleet
01619 #define GET_FLEET_MIN(Function)                                                         \
01620         long lGetMin##Function(long v, const Stack & s)                 \
01621         {       /* return min but more then 0 */                                        \
01622                 return v > 0 ? min(v, s.GetDesign()->Get##Function()) : s.GetDesign()->Get##Function();         \
01623         }                                                                                                               \
01624                                                                                                                         \
01625         long Fleet::GetMin##Function() const                                    \
01626         {                                                                                                               \
01627                 if (CVMin##Function == -1)                                                      \
01628                         const_cast<Fleet *>(this)->CVMin##Function = accumulate(mStacks.begin(), mStacks.end(), 0, lGetMin##Function);  \
01629                 return CVMin##Function;                                                         \
01630         }
01631 // Get true if any ship in the fleet has the capability, false otherwise
01632 #define GET_FLEET_HAS(Function)                                                         \
01633         bool Fleet::Can##Function() const                                               \
01634         {                                                                                                               \
01635                 if (CV##Function != -1)                                                         \
01636                         return CV##Function ? true : false;                             \
01637                 const_cast<Fleet *>(this)->CV##Function = 1;                    \
01638                 for (deque<Stack>::const_iterator i = mStacks.begin(); i != mStacks.end(); ++i) \
01639                         if (i->GetDesign()->Can##Function())                    \
01640                                 return true;                                                            \
01641                 const_cast<Fleet *>(this)->CV##Function = 0;                    \
01642                 return false;                                                                           \
01643         }
01644 
01645 GET_FLEET_SUM(Cloaking)
01646 GET_FLEET_SUM(FuelCapacity)
01647 GET_FLEET_SUM(FuelGen)
01648 GET_FLEET_SUM(Mass)
01649 GET_FLEET_MAX(Mass)
01650 GET_FLEET_MAXD(Tachyon)
01651 GET_FLEET_SUM(CargoCapacity)
01652 GET_FLEET_SUM(Mines)
01653 GET_FLEET_SUM(Sweeping)
01654 
01655 GET_FLEET_MAXD(Dampener)
01656 GET_FLEET_MAXD(RepairRate)
01657 GET_FLEET_MIN(SafeSpeed)
01658 GET_FLEET_MIN(MaxSpeed)
01659 GET_FLEET_MIN(FreeSpeed)
01660 GET_FLEET_MIN(BattleSpeed)
01661 
01662 GET_FLEET_HAS(Colonize)
01663 GET_FLEET_HAS(StealShip)
01664 GET_FLEET_HAS(StealPlanet)
01665 GET_FLEET_HAS(Shoot)
01666 GET_FLEET_HAS(NormalBomb)
01667 GET_FLEET_HAS(TerraBomb)
01668 GET_FLEET_HAS(SmartBomb)
01669 GET_FLEET_HAS(JumpGate)
01670 
01671 GET_FLEET_SUMD(NormalKillper)
01672 //GET_FLEET_SUM(SmartKillper)
01673 GET_FLEET_SUM(MinKill)
01674 GET_FLEET_SUM(InstKill)

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