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

ProdOrder.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 "Packet.h"
00029 #include "Order.h"
00030 
00031 #ifdef _DEBUG
00032 #define new DEBUG_NEW
00033 #endif
00034 
00035 #ifdef _DEBUG
00036 #define new DEBUG_NEW
00037 #endif
00038 
00039 ProdOrder::ProdOrder()
00040 : Type(0), Amount(0)
00041 {
00042         init();
00043 }
00044 
00045 ProdOrder::ProdOrder(long type, long amount)
00046 : Type(type), Amount(amount)
00047 {
00048         init();
00049 }
00050 
00051 ProdOrder::~ProdOrder()
00052 {
00053 }
00054 
00055 void ProdOrder::init()
00056 {
00057 }
00058 
00059 ProdOrder * ProdOrder::Copy() const
00060 {
00061         if (typeid(*this) == typeid(POShip))
00062                 return new POShip(dynamic_cast<const POShip &>(*this));
00063         else if (typeid(*this) == typeid(POBase))
00064                 return new POBase(dynamic_cast<const POBase &>(*this));
00065         else if (typeid(*this) == typeid(POPlanetary))
00066                 return new POPlanetary(dynamic_cast<const POPlanetary &>(*this));
00067         else if (typeid(*this) == typeid(POPacket))
00068                 return new POPacket(dynamic_cast<const POPacket &>(*this));
00069         else if (typeid(*this) == typeid(POTerraform))
00070                 return new POTerraform(dynamic_cast<const POTerraform &>(*this));
00071         else if (typeid(*this) == typeid(POAuto))
00072                 return new POAuto(dynamic_cast<const POAuto &>(*this));
00073         else {
00074 //              assert(false);
00075                 return NULL;
00076         }
00077 }
00078 
00079 deque<ProdOrder *> ProdOrder::ParseNode(const TiXmlNode * node, Planet * planet, Player * player /*= NULL*/, bool TrustPartials /*= false*/)
00080 {
00081         assert(planet != NULL || player != NULL);
00082         assert(node != NULL);
00083 
00084         if (player == NULL)
00085                 player = planet->NCGetOwner();
00086 
00087         const TiXmlNode * child;
00088 
00089         deque<ProdOrder *> neword;
00090         ProdOrder * po = NULL;
00091         long type;
00092         long num;
00093 
00094         for (child = node->FirstChild(); child; child = child->NextSibling()) {
00095                 if (child->Type() == TiXmlNode::COMMENT)
00096                         continue;
00097 
00098                 po = NULL;
00099                 if (stricmp(child->Value(), "Planet") == 0) {
00100                         continue; // skip
00101                 } else if (stricmp(child->Value(), "Ship") == 0) {
00102                         type = GetLong(child->FirstChild("Design"));
00103                         if (type <= 0 || type > Rules::GetConstant("MaxShipDesigns")) {
00104                                 Message * mess = player->AddMessage("Error: Invalid ship design type number");
00105                                 mess->AddLong("", type);
00106                                 continue;
00107                         }
00108                         num = GetLong(child->FirstChild("Number"));
00109                         po = new POShip(type, num);
00110                         po->CheckPartials(planet, child, TrustPartials);
00111                 } else if (stricmp(child->Value(), "Base") == 0) {
00112                         type = GetLong(child->FirstChild("Design"));
00113                         if (type <= 0 || type > Rules::GetConstant("MaxBaseDesigns")) {
00114                                 Message * mess = player->AddMessage("Error: Invalid base design");
00115                                 mess->AddLong("", type);
00116                                 continue;
00117                         }
00118                         po = new POBase(type);
00119                         po->CheckPartials(planet, child, TrustPartials);
00120                 } else if (stricmp(child->Value(), "Factories") == 0) {
00121                         num = GetLong(child->FirstChild("Number"));
00122                         po = new POPlanetary(POP_FACTS, num);
00123                         po->CheckPartials(planet, child, TrustPartials);
00124                 } else if (stricmp(child->Value(), "Mines") == 0) {
00125                         num = GetLong(child->FirstChild("Number"));
00126                         po = new POPlanetary(POP_MINES, num);
00127                         po->CheckPartials(planet, child, TrustPartials);
00128                 } else if (stricmp(child->Value(), "Defenses") == 0) {
00129                         num = GetLong(child->FirstChild("Number"));
00130                         po = new POPlanetary(POP_DEFS, num);
00131                         po->CheckPartials(planet, child, TrustPartials);
00132                 } else if (stricmp(child->Value(), "Alchemy") == 0) {
00133                         num = GetLong(child->FirstChild("Number"));
00134                         po = new POPlanetary(POP_ALCHEMY, num);
00135                         po->CheckPartials(planet, child, TrustPartials);
00136                 } else if (stricmp(child->Value(), "Scanner") == 0) {
00137                         po = new POPlanetary(POP_SCANNER, 1);
00138                         po->CheckPartials(planet, child, TrustPartials);
00139                 } else if (stricmp(child->Value(), "Packet") == 0) {
00140                         num = GetLong(child->FirstChild("Number"));
00141                         const char * ptr = GetString(child->FirstChild("Type"));
00142                         long t;
00143                         if (stricmp(ptr, "Mixed") == 0)
00144                                 t = -1;
00145                         else {
00146                                 t = Rules::MineralID(GetString(child->FirstChild("Type")));
00147                                 if (t < 0) {
00148                                         Message * mess = player->AddMessage("Error: Invalid packet type");
00149                                         mess->AddItem("", GetString(child->FirstChild("Type")));
00150                                         continue;
00151                                 }
00152                         }
00153 
00154                         po = new POPacket(t, num);
00155                         po->CheckPartials(planet, child, TrustPartials);
00156                 } else if (stricmp(child->Value(), "Terraform") == 0) {
00157                         num = GetLong(child->FirstChild("Number"), 1);
00158                         po = new POTerraform(POP_TERRAFORM, num);
00159                         po->CheckPartials(planet, child, TrustPartials);
00160                 } else if (stricmp(child->Value(), "AutoMine") == 0) {
00161                         num = GetLong(child);
00162                         po = new POAuto(POP_MINES, num);
00163                 } else if (stricmp(child->Value(), "AutoFactory") == 0) {
00164                         num = GetLong(child);
00165                         po = new POAuto(POP_FACTS, num);
00166                 } else if (stricmp(child->Value(), "AutoDefense") == 0) {
00167                         num = GetLong(child);
00168                         po = new POAuto(POP_DEFS, num);
00169                 } else if (strnicmp(child->Value(), "AutoAlchemy", 11) == 0) {
00170                         po = new POAuto(POP_ALCHEMY, 1);        // on or off, no number required
00171                 } else if (stricmp(child->Value(), "AutoMinTerra") == 0) {
00172                         num = GetLong(child);
00173                         po = new POAuto(POP_MINTERRA, num);
00174                 } else if (stricmp(child->Value(), "AutoMaxTerra") == 0) {
00175                         num = GetLong(child);
00176                         po = new POAuto(POP_MAXTERRA, num);
00177                 } else if (stricmp(child->Value(), "AutoPacket") == 0) {
00178                         num = GetLong(child);
00179                         po = new POAuto(POP_MIXEDPACKET, num);
00180                 } else {
00181                         Message * mess = player->AddMessage("Warning: Unknown production entry");
00182                         mess->AddItem("", child->Value());
00183                         continue;
00184                 }
00185 
00186                 neword.insert(neword.end(), po);
00187         }
00188 
00189         return neword;
00190 }
00191 
00192 TiXmlNode * ProdOrder::WriteNode(TiXmlNode * node, const deque<ProdOrder *> & ords)
00193 {
00194         TiXmlElement * PQ = new TiXmlElement("ProductionQueue");
00195         for (int i = 0; i < ords.size(); ++i)
00196                 ords[i]->WriteNode(node);
00197 
00198         node->LinkEndChild(PQ);
00199         return PQ;
00200 }
00201 
00202 TiXmlNode * ProdOrder::WriteNode(TiXmlNode * node) const
00203 {
00204         return Partial.WriteCosts(node, "Partial");
00205 }
00206 
00207 // <Partial><Resources>4</Resources><Mineral Name="Ironium">2<Mineral>...</Partial>
00208 
00209 // <Partial>\([0-9]*\) \([0-9]*\) \([0-9]*\) \([0-9]*\) [0-9]*</Partial>
00210 
00211 // <Partial><Resources>\1</Resources><Mineral Name="Ironium">\2</Mineral><Mineral Name="Boranium">\3</Mineral><Mineral Name="Germanium">\4</Mineral></Partial>
00212 
00213 void ProdOrder::CheckPartials(Planet * planet, const TiXmlNode * node, bool TrustPartials)
00214 {
00215         if (node == NULL || planet == NULL)
00216                 return;
00217 
00218         Partial.ReadCosts(node->FirstChild("Partial"));
00219         if (TrustPartials)
00220                 return;
00221 
00222         deque<ProdOrder *>::iterator iter;
00223         for (iter = planet->mProductionQ.begin(); iter != planet->mProductionQ.end(); ++iter)
00224         {
00225                 if (typeid(*iter) == typeid(this) && Type == (*iter)->Type)
00226                 {
00227                         if(Partial == (*iter)->Partial)
00228                         {
00229                                 break; // have a match
00230                         }
00231                         
00232                                 /*
00233                         // Check Resources
00234                         if (Partial[RESOURCES] != (*iter)->Partial[RESOURCES])
00235                                 continue;       // not equal stop now
00236                         
00237                         // Check Crew
00238                         if (Partial[POPULATION] != (*iter)->Partial[POPULATION])
00239                                 continue;       // not equal stop now
00240 
00241                         
00242                         // Check Minerals
00243                         for (int i = 0; i < Rules::MaxMinType; ++i)
00244                         {
00245                                 if (Partial[i] == (*iter)->Partial[i])
00246                                         continue; // not equal stop now
00247                         }
00248 
00249                         
00250                         if (i > Rules::MaxMinType)
00251                         // if we didn't find a problem, this is a match
00252                         break;*/
00253                 }
00254         }
00255 
00256         if (iter == planet->mProductionQ.end()) {
00257                 // if we don't have a match, use no partial production
00258                 Message * mess = planet->NCGetOwner()->AddMessage("Warning: Incorrect partial values", planet);
00259                 mess->AddItem("Item being built", TypeToString());
00260                 mess->AddLong("Number ordered", Amount);
00261                 Partial.Zero();
00262         } else {
00263                 // zero remembered partial, so it cannot be duplicated
00264                 (*iter)->Partial.Zero();
00265         }
00266 }
00267 
00268 /*
00269 Object variables used:
00270 Amount: number of items to build
00271 Partial: work previously done
00272 
00273 Parameters:
00274 cost: cost of one item
00275 planet: where it's being built
00276 resources: how many resources are still available, update as used
00277 AutoAlchem: true if the prior item is AutoAlchem, set to false when done
00278 maxbuild: max # of items to build, -1 for auto items (which means use Amount)
00279 
00280 locals:
00281 Build: Number to build, including partials
00282 fb: fraction part of Build, for partial completion
00283 lBuilt: count of items built, pass to Built function to actually build them
00284 Auto: true if in an auto item, false otherwise
00285 
00286 
00287 Build = max(Amount, resources&minerals + Partial / cost)
00288 res&min -= int(Build) * cost - Partial
00289 if Build < Amount
00290         Partial = fraction(Build) * cost
00291         res&min -= fraction(Build) * cost
00292 
00293 amount=1
00294 cost = 9r 3g
00295 4 res
00296 Build= 4/9
00297 Partial = 4, 1 3/9 -> 4 2
00298 
00299 2g
00300 Build= 2/3
00301 Partial = 6 2
00302 
00303 cost = 3r 9g
00304 2 res
00305 Build= 2/3
00306 Partial = 2, 6
00307 
00308 2g
00309 Build= 2/9
00310 Partial = 0 2 -> which means that no work is done, and the g shouldn't get spent either
00311 4g
00312 Build= 4/9
00313 Partial = 1 4
00314 
00315 cost = 5r 2g
00316 1r
00317 Build = 1/5
00318 Partial = 1 1
00319 1g
00320 Build = 1/2
00321 Partial = 2 1
00322 */
00323 
00324 bool ProdOrder::DoProduce(const Cost & cost, Planet * planet, long * resources, bool * AutoAlchemy, long maxbuild /*= -1*/)
00325 {
00326         // maxbuild should only be provided for auto build items
00327         assert(maxbuild == -1 || dynamic_cast<POAuto *>(this) != NULL || dynamic_cast<POTerraform *>(this) != NULL);
00328 
00329         Player * owner = planet->NCGetOwner();
00330         CargoType ct;
00331 
00332         double Build;
00333         int lBuilt = 0;
00334         bool Auto = false;
00335         if (maxbuild > 0)
00336                 Auto = true;
00337         else
00338                 maxbuild = Amount;
00339 
00340         // Partial completion is limited to cost
00341         Partial.SetResources(min(cost.GetResources(), Partial.GetResources()));
00342         Partial.SetCrew(min(cost.GetCrew(), Partial.GetCrew()));
00343         for (ct = 0; ct < Rules::MaxMinType; ++ct)
00344                 Partial[ct] = min(cost[ct], Partial[ct]);
00345 
00346         do {
00347                 // Add partial to stockpile
00348                 if (Partial.GetResources() > 0) {
00349                         *resources += Partial.GetResources();
00350                         planet->AdjustPopulation(Partial.GetCrew());
00351                         for (ct = 0; ct < Rules::MaxMinType; ++ct)
00352                                 planet->AdjustAmounts(ct, Partial[ct]);
00353 
00354                         Partial.Zero();
00355                 }
00356 
00357                 // How much can we get done?
00358                 // Build = max(maxbuild, resources&minerals + Partial / cost)
00359                 Build = maxbuild;
00360                 if (cost.GetResources() > 0)
00361                         Build = min(Build, double(*resources) / cost.GetResources());
00362 
00363                 if (cost.GetCrew() > 0)
00364                         Build = min(Build, double(planet->GetPopulation()) / cost.GetCrew());
00365 
00366                 for (ct = 0; ct < Rules::MaxMinType; ++ct)
00367                         if (cost[ct] > 0)
00368                                 Build = min(Build, double(planet->GetContain(ct)) / cost[ct]);
00369 
00370                 // res&min -= int(Build) * cost - Partial
00371                 if (long(Build) >= 1) {
00372                         *resources -= long(Build) * cost.GetResources();
00373                         planet->AdjustPopulation(-(long(Build) * cost.GetCrew()));
00374                         for (ct = 0; ct < Rules::MaxMinType; ++ct)
00375                                 planet->AdjustAmounts(ct, -(long(Build) * cost[ct]));
00376                 }
00377 
00378                 lBuilt += long(Build);
00379 
00380                 maxbuild -= long(Build);
00381 
00382                 if (maxbuild > 0) {
00383                         if (!Auto)
00384                                 BuildPartial(cost, planet, resources, Build);
00385 
00386                         // Build alchemy
00387                         if ((*AutoAlchemy || (!Auto && owner->GetResearchField() == RESEARCH_ALCHEMY)) && *resources > 0) {
00388                                 if (*resources >= long(Rules::GetConstant("AlchemyCost") * owner->ComponentCostFactor(CT_ALCHEMY))) {
00389                                         planet->BuildAlchemy(1);
00390                                         *resources -= long(Rules::GetConstant("AlchemyCost") * owner->ComponentCostFactor(CT_ALCHEMY));
00391                                         for (CargoType ct = 0; ct < Rules::MaxMinType; ++ct)
00392                                                 planet->AdjustAmounts(ct, 1);
00393                                 } else {
00394                                         POPlanetary * alchem = new POPlanetary(POP_ALCHEMY, 1);
00395                                         alchem->Partial.SetResources(*resources);
00396                                         *resources = 0;
00397                                         planet->mProductionQ.push_front(alchem);
00398                                 }
00399                         }
00400                 }
00401         } while (maxbuild > 0 && *resources > 0 && (*AutoAlchemy || (!Auto && owner->GetResearchField() == RESEARCH_ALCHEMY)));
00402 
00403         if (maxbuild > 0 && *resources > 0) { // AutoAlchemy is false
00404                 if (Auto) {
00405                         BuildPartial(cost, planet, resources, Build);
00406                         if (Partial.GetResources() > 0) {       // if resources are 0, then no work is done, and don't allocate anything else to it yet
00407                                 ProdOrder * item = NULL;
00408                                 if (Type >= POP_MIXEDPACKET && Type <= POP_MIXEDPACKET + Rules::MaxMinType) {
00409                                         item = new POPacket(Type - POP_MIXEDPACKET - 1, 1);
00410                                 } else if (Type == POP_MINTERRA || Type == POP_MAXTERRA)
00411                                         item = new POTerraform(POP_TERRAFORM, 1);
00412                                 else if (Type == POP_TERRAFORM)
00413                                         item = NULL;
00414                                 else    // should only be POP_FACTS, POP_MINES, or POP_DEFS
00415                                         item = new POPlanetary(Type, 1);
00416 
00417                                 if (item != NULL) {
00418                                         item->Partial = Partial;
00419                                         Partial.Zero();
00420                                         planet->mProductionQ.push_front(item);
00421                                 }
00422                         }
00423                 } else {
00424                         // left over resources go to research
00425                         owner->GainTech(*resources);
00426                         *resources = 0;
00427                 }
00428         }
00429 
00430         if (!Auto)      Amount = maxbuild;
00431         Built(planet, lBuilt);  // Actually build them
00432         *AutoAlchemy = false;   // always turn off auto-alchemy
00433         return(!Auto && Amount == 0);           // if we're done, delete it
00434 }
00435 
00436 void ProdOrder::BuildPartial(const Cost & cost, Planet * planet, long * resources, double Build)
00437 {
00438         // Partial = fraction(Build) * cost
00439         double dummy;
00440         double fb = modf(Build, &dummy);
00441 
00442         // Can't spend resources till all needed raw materials are on hand.
00443         // for example: 3res 9iron item, with 4 iron on hand, since minerals must be spent before resources, you can only do 1r 4i so far
00444         // another example 5res 4iron with 3 iron you can put in 3res 3iron. If limited to 2res you do 2res 2iron
00445         Partial.Zero();
00446         Partial.SetResources(long(fb * cost.GetResources()));
00447         *resources -= Partial.GetResources();
00448         if (Partial.GetResources() > 0) {       // if resources are 0, then no work is done, and don't allocate anything else to it yet
00449                 // Crew doesn't get on till it's all done.
00450                 for (CargoType ct = 0; ct < Rules::MaxMinType; ++ct) {
00451                         Partial[ct] = long(fb * cost[ct] + 1.0 - epsilon);
00452                         planet->AdjustAmounts(ct, -Partial[ct]);
00453                 }
00454         }
00455 }
00456 
00457 POAuto::~POAuto()
00458 {
00459 }
00460 
00461 TiXmlNode * POAuto::WriteNode(TiXmlNode * node) const
00462 {
00463         string str;
00464         str = TypeToString();
00465 
00466         if (str.empty())
00467                 TheGame->AddMessage("Error: Invalid auto procution order");
00468         else
00469                 AddLong(node, str.c_str(), Amount);
00470 
00471         return node;
00472 }
00473 
00474 string POAuto::TypeToString() const
00475 {
00476         string str;
00477         if (Type == POP_MINES)
00478                 str = "AutoMine";
00479         else if (Type == POP_FACTS)
00480                 str = "AutoFactory";
00481         else if (Type == POP_DEFS)
00482                 str = "AutoDefense";
00483         else if (Type == POP_ALCHEMY)
00484                 str = "AutoAlchemy";
00485         else if (Type == POP_MIXEDPACKET)
00486                 str = "AutoPacket";
00487         else if (Type == POP_MINTERRA)
00488                 str = "AutoMinTerra";
00489         else if (Type == POP_MAXTERRA)
00490                 str = "AutoMaxTerra";
00491         else
00492                 TheGame->AddMessage("Error: Invalid auto procution order");
00493 
00494         return str;
00495 }
00496 
00497 bool POAuto::Produce(Planet * planet, long * resources, bool * AutoAlchemy)
00498 {
00499         const Player * owner = planet->GetOwner();
00500 
00501         if (Type == POP_MINES) {
00502                 if (owner->ARTechType() >= 0) {
00503                         // AR trying to build planetary stuff, send message and delete from queue
00504                         return true;
00505                 }
00506 
00507                 long maxbuild = min(Amount, planet->MaxMines() - planet->GetMines());
00508                 if (maxbuild > 0)
00509                         DoProduce(owner->MineCost(), planet, resources, AutoAlchemy, maxbuild);
00510 
00511                 // auto itmes never get deleted fromt the queue
00512         } else if (Type == POP_FACTS) {
00513                 if (owner->ARTechType() >= 0) {
00514                         // AR trying to build planetary stuff, send message and delete from queue
00515                         return true;
00516                 }
00517 
00518                 long maxbuild = min(Amount, planet->MaxFactories() - planet->GetFactories());
00519                 if (maxbuild > 0)
00520                         DoProduce(owner->FactoryCost(), planet, resources, AutoAlchemy, maxbuild);
00521 
00522                 // auto itmes never get deleted fromt the queue
00523         } else if (Type == POP_DEFS) {
00524                 if (owner->ARTechType() >= 0) {
00525                         // AR trying to build planetary stuff, send message and delete from queue
00526                         return true;
00527                 }
00528 
00529                 long maxbuild = min(Amount, planet->MaxDefenses() - planet->GetDefenses());
00530                 if (maxbuild > 0) {
00531                         assert(Component::DefenseCost());
00532                         Cost cost;
00533                         cost.SetResources(long(Component::DefenseCost()->GetResources() * owner->ComponentCostFactor(CT_DEFENSE) + .5));
00534                         cost.SetCrew(long(Component::DefenseCost()->GetCrew() * owner->ComponentCostFactor(CT_DEFENSE) + .5));
00535                         for (CargoType ct = 0; ct < Rules::MaxMinType; ++ct)
00536                                 cost[ct] = long((*Component::DefenseCost())[ct] * owner->ComponentCostFactor(CT_DEFENSE) + .5);
00537 
00538                         DoProduce(cost, planet, resources, AutoAlchemy, maxbuild);
00539                 }
00540 
00541                 // auto itmes never get deleted fromt the queue
00542         } else if (Type == POP_ALCHEMY) {
00543                 *AutoAlchemy = true;
00544                 // auto itmes never get deleted fromt the queue
00545         } else if (Type == POP_MINTERRA) {
00546                 // auto itmes never get deleted fromt the queue
00547         } else if (Type == POP_MAXTERRA) {
00548                 // auto itmes never get deleted fromt the queue
00549         } else if (Type >= POP_MIXEDPACKET && Type <= POP_MIXEDPACKET + Rules::MaxMinType) {
00550                 if (POPacket::CheckPacket(planet))
00551                         DoProduce(planet->GetPacketCost(Type - POP_MIXEDPACKET - 1), planet, resources, AutoAlchemy, Amount);
00552                 // auto itmes never get deleted fromt the queue
00553         } else {
00554                 Message * mess = planet->NCGetOwner()->AddMessage("Error: Invalid Production type", planet);
00555                 mess->AddLong("Auto", Type);
00556                 return true;    // delete invalid items
00557                 
00558         }
00559 
00560         return false;   // auto itmes never get deleted fromt the queue
00561 }
00562 
00563 
00564 POBase::~POBase()
00565 {
00566 }
00567 
00568 TiXmlNode * POBase::WriteNode(TiXmlNode * node) const
00569 {
00570         TiXmlElement POB("Base");
00571         AddLong(&POB, "Design", Type);
00572         ProdOrder::WriteNode(&POB);
00573         node->InsertEndChild(POB);
00574 
00575         return node;
00576 }
00577 
00578 string POBase::TypeToString() const
00579 {
00580         string str;
00581         str = "Base design ";
00582         str += Long2String(Type);
00583         return str;
00584 }
00585 
00586 bool POBase::Produce(Planet * planet, long * resources, bool * AutoAlchemy)
00587 {
00588         Cost cost;
00589 
00590         const Player * owner = planet->GetOwner();
00591         if (planet->GetBaseNumber() == Type)    // we've already got one
00592                 return true;
00593 
00594         // check for invalid designs
00595         if (!owner->GetShipDesign(Type) || !owner->GetShipDesign(Type)->IsValidDesign(owner)) {
00596                 Message * mess = planet->NCGetOwner()->AddMessage("Error: Invalid Production type", planet);
00597                 mess->AddLong("Base", Type);
00598                 return true;
00599         }
00600 
00601         if (planet->GetBaseNumber() > 0)
00602                 cost = owner->GetBaseDesign(Type)->GetCost(owner, planet->GetBaseDesign(), planet);
00603         else
00604                 cost = owner->GetBaseDesign(Type)->GetCost(owner);
00605 
00606         return DoProduce(cost, planet, resources, AutoAlchemy);
00607 }
00608 
00609 void POBase::Built(Planet * planet, long number)
00610 {
00611         assert(number == 1);
00612         planet->SetBaseNumber(Type);
00613         Message * mess = planet->NCGetOwner()->AddMessage("Base built", planet);
00614         mess->AddLong("BaseDesign", Type);
00615 }
00616 
00617 
00618 POPacket::~POPacket()
00619 {
00620 }
00621 
00622 TiXmlNode * POPacket::WriteNode(TiXmlNode * node) const
00623 {
00624         TiXmlElement POP("Packet");
00625         if (Type == POP_MIXEDPACKET)
00626                 AddString(&POP, "Type", "Mixed");
00627         else
00628                 AddString(&POP, "Type", Rules::GetCargoName(Type - POP_MIXEDPACKET - 1).c_str());
00629         AddLong(&POP, "Number", Amount);
00630         ProdOrder::WriteNode(&POP);
00631         node->InsertEndChild(POP);
00632 
00633         return node;
00634 }
00635 
00636 string POPacket::TypeToString() const
00637 {
00638         string str;
00639         str = "Packet ";
00640         str += Long2String(Type - POP_MIXEDPACKET - 1);
00641         return str;
00642 }
00643 
00644 bool POPacket::CheckPacket(Planet * planet)
00645 {
00646         if (planet->GetBaseNumber() < 0 || planet->GetBaseDesign()->GetDriverSpeed() <= 0) {
00647                 planet->NCGetOwner()->AddMessage("Error: Packet without driver", planet);
00648                 return false;
00649         } else if (planet->GetPacketSpeed() > planet->GetBaseDesign()->GetDriverSpeed() + Rules::GetConstant("MaxPacketOverFling")) {
00650                 Message * mess = planet->NCGetOwner()->AddMessage("Error: Invalid packet speed", planet);
00651                 mess->AddLong("Driver safe speed", planet->GetBaseDesign()->GetDriverSpeed());
00652                 mess->AddLong("Packet speed", planet->GetPacketSpeed());
00653                 return false;
00654         } else if (planet->GetPacketDest() == NULL) {
00655                 planet->NCGetOwner()->AddMessage("Error: Packet without destination", planet);
00656                 return false;
00657         } else
00658                 return true;
00659 }
00660 
00661 bool POPacket::Produce(Planet * planet, long * resources, bool * AutoAlchemy)
00662 {
00663         if (!CheckPacket(planet))
00664                 return true;
00665 
00666         return DoProduce(planet->GetPacketCost(Type - POP_MIXEDPACKET - 1), planet, resources, AutoAlchemy);
00667 }
00668 
00669 
00670 POPlanetary::~POPlanetary()
00671 {
00672 }
00673 
00674 TiXmlNode * POPlanetary::WriteNode(TiXmlNode * node) const
00675 {
00676         string type = TypeToString();
00677 
00678         if (type.empty())
00679                 return node;
00680 
00681         TiXmlElement POP(type.c_str());
00682         AddLong(&POP, "Number", Amount);
00683         ProdOrder::WriteNode(&POP);
00684         node->InsertEndChild(POP);
00685 
00686         return node;
00687 }
00688 
00689 string POPlanetary::TypeToString() const
00690 {
00691         string str;
00692         switch (Type) {
00693         case POP_FACTS:
00694                 str = "Factories";
00695                 break;
00696         case POP_MINES:
00697                 str = "Mines";
00698                 break;
00699         case POP_DEFS:
00700                 str = "Defenses";
00701                 break;
00702         case POP_ALCHEMY:
00703                 str = "Alchemy";
00704                 break;
00705         case POP_SCANNER:
00706                 str = "Scanner";
00707                 break;
00708         default:
00709                 TheGame->AddMessage("Error: Invalid planetary procution order");
00710                 break;
00711         }
00712 
00713         return str;
00714 }
00715 
00716 bool POPlanetary::Produce(Planet * planet, long * resources, bool * AutoAlchemy)
00717 {
00718         const Player * owner = planet->GetOwner();
00719         if (Type == POP_FACTS) {
00720                 if (owner->ARTechType() >= 0) {
00721                         // AR trying to build planetary stuff, send message and delete from queue
00722                         return true;
00723                 }
00724 
00725                 long max = planet->MaxFactories();
00726                 if (Amount + planet->GetFactories() > max) {
00727                         // send message too
00728                         Amount = max - planet->GetFactories();
00729                 }
00730 
00731                 return DoProduce(owner->FactoryCost(), planet, resources, AutoAlchemy);
00732         } else if (Type == POP_MINES) {
00733                 if (owner->ARTechType() >= 0) {
00734                         // AR trying to build planetary stuff, send message and delete from queue
00735                         return true;
00736                 }
00737 
00738                 long max = planet->MaxMines();
00739                 if (Amount + planet->GetMines() > max) {
00740                         // send message too
00741                         Amount = max - planet->GetMines();
00742                 }
00743 
00744                 return DoProduce(owner->MineCost(), planet, resources, AutoAlchemy);
00745         } else if (Type == POP_DEFS) {
00746                 if (owner->ARTechType() >= 0) {
00747                         // AR trying to build planetary stuff, send message and delete from queue
00748                         return true;
00749                 }
00750 
00751                 long max = planet->MaxDefenses();
00752                 if (Amount + planet->GetDefenses() > max) {
00753                         // send message too
00754                         Amount = max - planet->GetDefenses();
00755                 }
00756 
00757                 assert(Component::DefenseCost());
00758                 Cost cost;
00759                 cost.SetResources(long(Component::DefenseCost()->GetResources() * owner->ComponentCostFactor(CT_DEFENSE) + .5));
00760                 cost.SetCrew(long(Component::DefenseCost()->GetCrew() * owner->ComponentCostFactor(CT_DEFENSE) + .5));
00761                 for (CargoType ct = 0; ct < Rules::MaxMinType; ++ct)
00762                         cost[ct] = long((*Component::DefenseCost())[ct] * owner->ComponentCostFactor(CT_DEFENSE) + .5);
00763 
00764                 return DoProduce(cost, planet, resources, AutoAlchemy);
00765         } else if (Type == POP_ALCHEMY) {
00766                 Cost cost;
00767                 cost.SetResources(long(Rules::GetConstant("AlchemyCost") * owner->ComponentCostFactor(CT_ALCHEMY) + .5));
00768                 cost.SetCrew(0);
00769                 for (CargoType ct = 0; ct < Rules::MaxMinType; ++ct)
00770                         cost[ct] = 0;
00771 
00772                 return DoProduce(cost, planet, resources, AutoAlchemy);
00773         } else if (Type == POP_SCANNER) {
00774                 if (owner->ARTechType() >= 0) {
00775                         // AR trying to build planetary stuff, send message and delete from queue
00776                         return true;
00777                 }
00778 
00779                 assert(Amount == 1);
00780                 if (planet->GetScanner()) {
00781                         // send message too
00782                         return true;
00783                 }
00784 
00785                 assert(Component::ScannerCost());
00786                 Cost cost;
00787                 cost.SetResources(long(Component::ScannerCost()->GetResources() * owner->ComponentCostFactor(CT_PLANSCAN) + .5));
00788                 cost.SetCrew(long(Component::ScannerCost()->GetCrew() * owner->ComponentCostFactor(CT_PLANSCAN) + .5));
00789                 for (CargoType ct = 0; ct < Rules::MaxMinType; ++ct)
00790                         cost[ct] = long((*Component::ScannerCost())[ct] * owner->ComponentCostFactor(CT_PLANSCAN) + .5);
00791 
00792                 return DoProduce(cost, planet, resources, AutoAlchemy);
00793         } else {
00794                 Message * mess = planet->NCGetOwner()->AddMessage("Error: Invalid Production type", planet);
00795                 mess->AddLong("Planetary", Type);
00796                 return true;
00797         }
00798 }
00799 
00800 void POPlanetary::Built(Planet * planet, long number)
00801 {
00802         if (Type >= POP_MIXEDPACKET && Type <= POP_MIXEDPACKET + Rules::MaxMinType) {
00803                 Packet * p;
00804                 p = new Packet(*planet, planet->GetPacketSpeed(), planet->GetBaseDesign()->GetDriverSpeed(), planet->GetPacketDest());
00805                 if (Type == POP_MIXEDPACKET) {
00806                         for (long i = 0; i < Rules::MaxMinType; ++i)
00807                                 p->AdjustAmounts(i, planet->GetOwner()->PacketSizeMixed() * number);
00808                 } else {
00809                         p->AdjustAmounts(Type - POP_MIXEDPACKET - 1, planet->GetOwner()->PacketSizeOneMin() * number);
00810                 }
00811 
00812                 TheGalaxy->AddPacket(p, planet);
00813         } else if (Type == POP_FACTS) {
00814                 planet->BuildFactories(number);
00815         } else if (Type == POP_MINES) {
00816                 planet->BuildMines(number);
00817         } else if (Type == POP_DEFS) {
00818                 planet->BuildDefenses(number);
00819         } else if (Type == POP_ALCHEMY) {
00820                 planet->BuildAlchemy(number);
00821                 for (CargoType ct = 0; ct < Rules::MaxMinType; ++ct)
00822                         planet->AdjustAmounts(ct, number);
00823         } else if (Type == POP_SCANNER) {
00824                 planet->NCGetOwner()->AddMessage("Built scanner", planet);
00825                 assert(number == 1);
00826                 planet->BuildScanner();
00827         }
00828 }
00829 
00830 
00831 POShip::~POShip()
00832 {
00833 }
00834 
00835 TiXmlNode * POShip::WriteNode(TiXmlNode * node) const
00836 {
00837         TiXmlElement POS("Ship");
00838         AddLong(&POS, "Design", Type);
00839         AddLong(&POS, "Number", Amount);
00840         ProdOrder::WriteNode(&POS);
00841         node->InsertEndChild(POS);
00842         return node;
00843 }
00844 
00845 string POShip::TypeToString() const
00846 {
00847         string str;
00848         str = "Ship design ";
00849         str += Long2String(Type);
00850         return str;
00851 }
00852 
00853 bool POShip::Produce(Planet * planet, long * resources, bool * AutoAlchemy)
00854 {
00855         const Player * owner = planet->GetOwner();
00856 
00857         // check for invalid designs
00858         if (!owner->GetShipDesign(Type) || !owner->GetShipDesign(Type)->IsValidDesign(owner)) {
00859                 Message * mess = planet->NCGetOwner()->AddMessage("Error: Invalid Production type", planet);
00860                 mess->AddLong("Ship", Type);
00861                 return true;
00862         }
00863 
00864         if (planet->GetBaseNumber() < 0 ||
00865                 (planet->GetBaseDesign()->GetDock() >= 0 &&
00866                         owner->GetShipDesign(Type)->GetMass() > planet->GetBaseDesign()->GetDock()))
00867         {
00868                 Message * mess = planet->NCGetOwner()->AddMessage("Warning: Ship too big for dock", planet);
00869                 mess->AddItem("Ship design", owner->GetShipDesign(Type)->GetName());
00870                 mess->AddLong("dock capacity", planet->GetBaseDesign()->GetDock());
00871                 return true;
00872         }
00873 
00874         return DoProduce(owner->GetShipDesign(Type)->GetCost(owner), planet, resources, AutoAlchemy);
00875 }
00876 
00877 void POShip::Built(Planet * planet, long number)
00878 {
00879         Player * player = planet->NCGetOwner();
00880         player->BuildShips(planet, Type, number);
00881 }
00882 
00883 
00884 POTerraform::~POTerraform()
00885 {
00886 }
00887 
00888 TiXmlNode * POTerraform::WriteNode(TiXmlNode * node) const
00889 {
00890         if (Type == POP_MINTERRA)
00891                 AddLong(node, "AutoMinTerra", Amount);
00892         else if (Type == POP_MAXTERRA)
00893                 AddLong(node, "AutoMaxTerra", Amount);
00894         else {
00895                 TiXmlElement POT("Terraform");
00896                 AddLong(&POT, "Number", Amount);
00897                 ProdOrder::WriteNode(&POT);
00898                 node->InsertEndChild(POT);
00899         }
00900 
00901         return node;
00902 }
00903 
00904 string POTerraform::TypeToString() const
00905 {
00906         string str;
00907         if (Type == POP_MINTERRA)
00908                 str = "AutoMinTerra";
00909         else if (Type == POP_MAXTERRA)
00910                 str = "AutoMaxTerra";
00911         else
00912                 str = "Terraform";
00913 
00914         return str;
00915 }
00916 
00917 bool POTerraform::Produce(Planet * planet, long * resources, bool * AutoAlchemy)
00918 {
00919         Player * owner = planet->NCGetOwner();
00920         double cf = owner->ComponentCostFactor(CT_TERRAFORM);
00921         if (cf <= epsilon)
00922                 return true;    // CA's can't buy terraforming
00923 
00924         Cost c;
00925 
00926         // TODO: I think production of terraforming needs work, also messages
00927         long tempAmt = Amount;
00928         while (Amount > 0 && *resources > 0) {
00929                 if (Type == POP_MINTERRA && planet->GetPopulation() <= planet->GetMaxPop() && planet->GetOwner()->HabFactor(planet) >= 0)
00930                         return false;
00931 
00932                 c.Zero();
00933                 owner->ResetTerraLimits();
00934                 for (unsigned long i = 0; i < TheGame->GetComponents().size(); ++i) {
00935                         // Is it terraforming? can we build it? Will it help? Is it cheaper?
00936                         if (TheGame->GetComponents()[i]->GetType() == CT_TERRAFORM &&
00937                                 TheGame->GetComponents()[i]->IsBuildable(owner) &&
00938                                 planet->CanTerraform(TheGame->GetComponents()[i]) &&
00939                                 (mResCost == 0 || mResCost >= TheGame->GetComponents()[i]->GetCost().GetResources()))
00940                         {
00941                                 owner->SetTerraLimit(TheGame->GetComponents()[i]->GetTerraType(), TheGame->GetComponents()[i]->GetTerraLimit());
00942                                 c = TheGame->GetComponents()[i]->GetCost();
00943                         }
00944                 }
00945 
00946                 if (c.GetResources() == 0) {
00947                         if (Type == POP_TERRAFORM)
00948                                 return true;    // this player cannot terraform yet
00949                         else
00950                                 break;
00951                 }
00952 
00953                 c *= cf;
00954 
00955                 if (DoProduce(c, planet, resources, AutoAlchemy) == true && Type == POP_TERRAFORM)
00956                         return true;
00957         }
00958 
00959         if (Type == POP_TERRAFORM)
00960                 return Amount == 0;
00961         else {
00962                 Amount = tempAmt;
00963                 return false;
00964         }
00965 }
00966 
00967 void POTerraform::Built(Planet * planet, long number)
00968 {
00969         assert(number == 1);
00970         planet->Terraform(planet->GetOwner(), true);
00971         Amount -= number;
00972 }

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