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

Rules.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 "Creation.h"
00029 
00030 #include <stdlib.h>
00031 
00032 #ifdef _DEBUG
00033 #define new DEBUG_NEW
00034 #endif
00035 
00036 long Rules::MaxTechType = 0;
00037 unsigned long Rules::MaxTechLevel = 0;
00038 long Rules::MaxHabType = 0;
00039 long Rules::MaxMinType = 0;
00040 long Rules::MaxMineType = 0;
00041 unsigned long Rules::MaxFleets = 512;
00042 long Rules::PopEQ1kT = 100;
00043 
00044 // these maps leak, could probably fix it by making them pointers, new'ing and delete'ing them
00045 map<string, long, less<string> > Rules::Consts;
00046 map<string, double, less<string> > Rules::Floats;
00047 map<string, Array<long> *, less<string> > Rules::Arrays;
00048 map<string, Array<double> *, less<string> > Rules::FloatArrays;
00049 
00050 deque<unsigned long> Rules::TechCost;
00051 deque<string> Rules::TechName;
00052 deque<string> Rules::HabName;
00053 deque<string> Rules::MinName;
00054 deque<string> Rules::MineName;
00055 
00056 string * Rules::ModFileName = NULL;
00057 double Rules::ModFileVersion = 0.0;
00058 string * Rules::ModFileCRC = NULL;
00059 
00060 deque<deque<long> *> Rules::mHabOdds;
00061 deque<long> Rules::mMinMC;
00062 deque<long> Rules::mMaxMC;
00063 deque<long> Rules::mHWMinMC;
00064 deque<long> Rules::mHWMaxMC;
00065 deque<long> Rules::mHWFloorMC;
00066 deque<long> Rules::mHWSetupMC;
00067 deque<long> Rules::mHWSetupSM;
00068 
00069 
00070 void Rules::Init()
00071 {
00072 }
00073 
00074 void Rules::Cleanup()
00075 {
00076         int i;
00077         for (i = 0; i < mHabOdds.size(); ++i)
00078                 delete mHabOdds[i];
00079         mHabOdds.clear();
00080 
00081         TechCost.clear();
00082         TechName.clear();
00083         HabName.clear();
00084         MinName.clear();
00085         MineName.clear();
00086 
00087         Consts.clear();
00088         Floats.clear();
00089         map<string, Array<long> *, less<string> >::iterator iml;
00090         for (iml = Arrays.begin(); iml != Arrays.end(); ++iml)
00091                 delete iml->second;
00092         Arrays.clear();
00093 
00094         map<string, Array<double> *, less<string> >::iterator imd;
00095         for (imd = FloatArrays.begin(); imd != FloatArrays.end(); ++imd)
00096                 delete imd->second;
00097         FloatArrays.clear();
00098 
00099         delete ModFileName;
00100         delete ModFileCRC;
00101         mMinMC.clear();
00102         mMaxMC.clear();
00103         mHWMinMC.clear();
00104         mHWMaxMC.clear();
00105         mHWFloorMC.clear();
00106         mHWSetupMC.clear();
00107         mHWSetupSM.clear();
00108 }
00109 
00110 bool Rules::TechScrap(const Planet * planet)
00111 {
00112         if (planet &&
00113                 planet->GetOwner() &&
00114                 planet->GetBaseNumber() >= 0 &&
00115                 !planet->GetOwner()->HasGotTech() &&
00116                 Randodd(GetFloat("TechEventChance")))
00117         {
00118                 return true;
00119         } else
00120                 return false;
00121 }
00122 
00123 TechType Rules::TechFleet(const Player * player, const Fleet * fleet)
00124 {
00125         TechType TechGot = TECH_NONE;
00126 
00127         // Have we gotten tech already?
00128         if (!player->HasGotTech() && Randodd(GetFloat("TechEventChance")))
00129         {
00130                 long Count = 0;
00131                 for (long i = 0; i < Rules::MaxTechType; ++i) { // check each field
00132                         // if it's possible to gain, and we pass a coin toss
00133                         if (player->GetTechLevel(i) < fleet->TechLevel(i) && Randodd(GetFloat("TechFieldChance")))
00134                                 if (Random(++Count) == 0)       // This will select between all that you could get, randomly
00135                                         TechGot = i;
00136                 }
00137 
00138                 if (!TechGot) {
00139                         /* need to redo MT stuff
00140                         for (long i = MIN_MTTECH; i < MAX_MTTECH; ++i)
00141                         {
00142                                 long items = fleet->TechLevel(i);
00143                                 if (items > 25)
00144                                         items = 25;
00145 
00146                                 if (Random(200) < items) {      // half a percent chance per tech item.
00147                                         if (Random(++Count) == 0)       // This will select between all that you could get, randomly
00148                                                 TechGot = i;
00149                                 }
00150                         }
00151                         */
00152                 }
00153         }
00154 
00155         return TechGot;
00156 }
00157 
00158 double Rules::ScrapRecover(const Planet * planet, bool colonize)
00159 {
00160         if (planet == NULL)
00161                 return 1.0 / 3.0;
00162 
00163         double percent;
00164         if (colonize)
00165                 percent = .75;
00166         else if (planet->GetBaseNumber() >= 0) {
00167                 if (planet->GetOwner()->UltimateRecycle())
00168                         percent = .9;
00169                 else
00170                         percent = .8;
00171         } else {
00172                 if (planet->GetOwner() && planet->GetOwner()->UltimateRecycle())
00173                         percent = .45;
00174                 else
00175                         percent = 1.0 / 3.0;
00176         }
00177 
00178         return percent;
00179 }
00180 
00181 long Rules::ScrapResource(const Planet * planet)
00182 {
00183         long percent;
00184         if (!planet || !planet->GetOwner()->UltimateRecycle())
00185                 percent = 0;
00186         else if (planet->GetBaseNumber() >= 0)
00187                 percent = 7000;
00188         else
00189                 percent = 3500;
00190 
00191         return percent;
00192 }
00193 
00194 TechType Rules::TechInvasion(Player * invader, const Player * owner)
00195 {
00196         TechType TechGot = TECH_NONE;
00197 
00198         // Have we gotten tech already?
00199         if (!invader->HasGotTech() && Randodd(GetFloat("TechEventChance")))
00200         {
00201                 long Count = 0;
00202                 for (long i = 0; i < Rules::MaxTechType; ++i) { // check each field
00203                         // if it's possible to gain, and we pass a coin toss
00204                         if (invader->GetTechLevel(i) < owner->GetTechLevel(i) && Randodd(GetFloat("TechFieldChance")))
00205                                 if (Random(++Count) == 0)       // This will select between all that you could get, randomly
00206                                         TechGot = i;
00207                 }
00208         }
00209 
00210         return TechGot;
00211 }
00212 
00213 bool Rules::Stealable(CargoType ct)
00214 {
00215         return ct != POPULATION;
00216 }
00217 
00218 long Rules::CloakValue(long Cloaking, long Mass)
00219 {
00220         long Cloak = Cloaking / Mass;
00221         long percent;
00222         if (Cloak <= 100)
00223                 percent = Cloak / 2;
00224         else {
00225                 Cloak -= 100;
00226                 if (Cloak <= 200)
00227                         percent = 50 + Cloak / 8;
00228                 else {
00229                         Cloak -= 200;
00230                         if (Cloak <= 312)
00231                                 percent = 75 + Cloak / 24;
00232                         else {
00233                                 Cloak -= 312;
00234                                 if (Cloak <= 512)
00235                                         percent = 88 + Cloak/64;
00236                                 else if (Cloak < 768)
00237                                         percent = 96;
00238                                 else if (Cloak < 1000)
00239                                         percent = 97;
00240                                 else
00241                                         percent = 98;
00242                         }
00243                 }
00244         }
00245 
00246         return percent;
00247 }
00248 
00249 double Rules::CalcScanning(double base, long newscan, long count)
00250 {
00251         double Result = base;
00252         if (newscan > 0)
00253                 Result = pow(pow(Result, 4) + pow((double)newscan, 4) * count, 0.25);
00254         else if (newscan == 0 && Result < 0)
00255                 Result = 0;
00256 
00257         return Result;
00258 }
00259 
00260 void Rules::WriteRulesFile(TiXmlNode * node)
00261 {
00262         TiXmlElement rulefile("Rules");
00263         AddString(&rulefile, "File", ModFileName ? ModFileName->c_str() : "");
00264         AddDouble(&rulefile, "Version", ModFileVersion);
00265         AddString(&rulefile, "Verification", ModFileCRC ? ModFileCRC->c_str() : "");
00266         node->InsertEndChild(rulefile);
00267 }
00268 
00269 bool Rules::LoadRules(const TiXmlNode * node, const char * file, const char * verify, double version)
00270 {
00271         if (!node)
00272                 return false;
00273 
00274         ModFileName = new string(file);
00275         ModFileVersion = version;
00276         if (verify)
00277                 ModFileCRC = new string(verify);
00278 
00279         const TiXmlNode * child1;
00280         const TiXmlElement * el;
00281         const char * ptr;
00282 //      string ptr;
00283         long val;
00284         double dval;
00285         for (child1 = node->FirstChild(); child1; child1 = child1->NextSibling()) {
00286                 if (child1->Type() == TiXmlNode::COMMENT)
00287                         continue;
00288 
00289                 el = child1->ToElement();
00290                 if (el == NULL)
00291                         continue;
00292 
00293                 if (stricmp(child1->Value(), "Constant") == 0) {
00294                         // <Constant Name="MaxShipDesigns">16</Constant>
00295                         ptr = el->Attribute("Name");
00296                         val = GetLong(child1);
00297                         Consts.insert(pair<string, long>(ptr, val));
00298                         if (stricmp(ptr, "MaxFleets") == 0)
00299                                 MaxFleets = val;
00300                         else if (stricmp(ptr, "PopEQ1kT") == 0)
00301                                 PopEQ1kT = val;
00302                 } else if (stricmp(child1->Value(), "Float") == 0) {
00303                         // <Float Name="MineDecayRate">0.02</Float>
00304                         ptr = el->Attribute("Name");
00305                         dval = GetDouble(child1);
00306                         Floats.insert(pair<string, double>(ptr, dval));
00307                 } else if (stricmp(child1->Value(), "Array") == 0) {
00308                         // <Array Name="PacketDecay" start="0"><Item>0</Item><Item>1000</Item><Item>2500</Item><Item>5000</Item></Array>
00309                         const char * ptr = el->Attribute("Name");
00310                         if (ptr == NULL)
00311                                 continue;
00312 
00313                         int start;
00314                         el->Attribute("Start", &start);
00315                         Array<long> * arr = new Array<long>(start);
00316                         const TiXmlNode * child2;
00317                         for (child2 = child1->FirstChild("Item"); child2; child2 = child2->NextSibling("Item")) {
00318                                 if (child2->Type() == TiXmlNode::COMMENT)
00319                                         continue;
00320 
00321                                 arr->AddItem(GetLong(child2));
00322                         }
00323                         Arrays.insert(pair<string, Array<long> *>(ptr, arr));
00324                 } else if (stricmp(child1->Value(), "FloatArray") == 0) {
00325                         // <Array Name="PacketDecay" start="0"><Item>0</Item><Item>1000</Item><Item>2500</Item><Item>5000</Item></Array>
00326                         const char * ptr = el->Attribute("Name");
00327                         if (ptr == NULL)
00328                                 continue;
00329 
00330                         int start;
00331                         el->Attribute("Start", &start);
00332                         Array<double> * arr = new Array<double>(start);
00333                         const TiXmlNode * child2;
00334                         for (child2 = child1->FirstChild("Item"); child2; child2 = child2->NextSibling("Item")) {
00335                                 if (child2->Type() == TiXmlNode::COMMENT)
00336                                         continue;
00337 
00338                                 arr->AddItem(GetDouble(child2));
00339                         }
00340                         FloatArrays.insert(pair<string, Array<double> *>(ptr, arr));
00341                 } else if (stricmp(child1->Value(), "TechCost") == 0) {
00342                         // <TechCost Level="1">50</TechCost
00343                         int level = 0;
00344                         el->Attribute("Level", &level);
00345                         val = GetLong(child1);
00346                         if (static_cast<int>(TechCost.size()) < level)
00347                                 TechCost.insert(TechCost.end(), level - TechCost.size(), 0);
00348 
00349                         TechCost[level-1] = val;
00350                         MaxTechLevel = max(MaxTechLevel, static_cast<unsigned long>(level));
00351                 } else if (stricmp(child1->Value(), "TechName") == 0) {
00352                         // <TechName>Energy</TechName>
00353                         TechName.insert(TechName.end(), GetString(child1));
00354                         ++MaxTechType;
00355                 } else if (stricmp(child1->Value(), "Habitat") == 0) {
00356                         HabName.insert(HabName.end(), GetString(child1->FirstChild("Name")));
00357                         deque<long> * hoa;
00358                         hoa = Rules::GetHabOddArray(MaxHabType, true);
00359                         ParseArray(child1->FirstChild("Array"), "TickOdds", "TickNumber", *hoa);
00360                         long sum = 0;
00361                         for (long i = 0; i < hoa->size(); ++i) {
00362                                 sum += (*hoa)[i];
00363                                 (*hoa)[i] = sum;
00364                         }
00365 
00366                         ++MaxHabType;
00367                 } else if (stricmp(child1->Value(), "MinName") == 0) {
00368                         MinName.insert(MinName.end(), GetString(child1));
00369                         ++MaxMinType;
00370                 } else if (stricmp(child1->Value(), "MineName") == 0) {
00371                         MineName.insert(MineName.end(), GetString(child1));
00372                         ++MaxMineType;
00373                 }
00374         }
00375         return true;
00376 }
00377 
00378 long Rules::GetConstant(const string name, long Default /*= 0*/)
00379 {
00380         map<string, long, less<string> >::const_iterator im;
00381         im = Consts.find(name);
00382         if (im == Consts.end())
00383                 return Default;
00384 
00385         return im->second;
00386 }
00387 
00388 double Rules::GetFloat(const string name, double Default /*= 0.0*/)
00389 {
00390         map<string, double, less<string> >::const_iterator im;
00391         im = Floats.find(name);
00392         if (im == Floats.end())
00393                 return Default;
00394 
00395         return im->second;
00396 }
00397 
00398 long Rules::GetArrayValue(const string name, int position)
00399 {
00400         map<string, Array<long> *, less<string> >::const_iterator im;
00401         im = Arrays.find(name);
00402         if (im == Arrays.end())
00403                 return 0;
00404 
00405         return (*im->second)[position];
00406 }
00407 
00408 double Rules::GetArrayFloat(const string name, int position)
00409 {
00410         map<string, Array<double> *, less<string> >::const_iterator im;
00411         im = FloatArrays.find(name);
00412         if (im == FloatArrays.end())
00413                 return 0;
00414 
00415         return (*im->second)[position];
00416 }
00417 
00418 long Rules::TechID(const char * name)
00419 {
00420         if (name == NULL || *name == '\0')
00421                 return -1;
00422 
00423         if (stricmp(name, "Alchemy instead of research") == 0)
00424                 return RESEARCH_ALCHEMY;
00425         else if (stricmp(name, "Same field") == 0)
00426                 return NEXT_SAME;
00427         else if (stricmp(name, "Lowest field") == 0)
00428                 return NEXT_LOW;
00429         else if (stricmp(name, "Cheapest field") == 0)
00430                 return NEXT_CHEAP;
00431 
00432         deque<string>::const_iterator it;
00433         it = find(TechName.begin(), TechName.end(), name);
00434         if (it == TechName.end())
00435                 return TECH_NONE;
00436 
00437         return it - TechName.begin();
00438 }
00439 
00440 const string Rules::GetTechName(long type)
00441 {
00442         if (type == RESEARCH_ALCHEMY)
00443                 return "Alchemy instead of research";
00444         else if (type == NEXT_SAME)
00445                 return "Same field";
00446         else if (type == NEXT_LOW)
00447                 return "Lowest field";
00448         else if (type == NEXT_CHEAP)
00449                 return "Cheapest field";
00450         else if (type >= 0 && type < MaxTechType)
00451                 return TechName[type];
00452         else
00453                 return "BadName";
00454 }
00455 
00456 long Rules::MineralID(const char * name)
00457 {
00458         if (name == NULL || *name == '\0')
00459                 return -1;
00460 
00461         deque<string>::const_iterator it;
00462         it = find(MinName.begin(), MinName.end(), name);
00463         if (it == MinName.end())
00464                 return -1;
00465 
00466         return it - MinName.begin();
00467 }
00468 
00469 long Rules::HabID(const char * name)
00470 {
00471         if (name == NULL || *name == '\0')
00472                 return -1;
00473 
00474         deque<string>::const_iterator it;
00475         it = find(HabName.begin(), HabName.end(), name);
00476         if (it == HabName.end())
00477                 return -1;
00478 
00479         return it - HabName.begin();
00480 }
00481 
00482 long Rules::MineID(const char * name)
00483 {
00484         if (name == NULL || *name == '\0')
00485                 return -1;
00486 
00487         deque<string>::const_iterator it;
00488         it = find(MineName.begin(), MineName.end(), name);
00489         if (it == MineName.end())
00490                 return -1;
00491 
00492         return it - MineName.begin();
00493 }
00494 
00495 void Rules::ReadCargo(const TiXmlNode * node, deque<long> & q, long * pop)
00496 {
00497         deque<long>::iterator li;
00498         if (node == NULL) {
00499                 if (pop)        *pop = 0;
00500                 for (li = q.begin(); li != q.end(); ++li)
00501                         *li = 0;
00502         } else {
00503                 if (pop)        *pop = GetLong(node->FirstChild("Population"));
00504                 ParseArray(node, q, MINERALS);
00505         }
00506 }
00507 
00508 TiXmlElement * Rules::WriteCargo(TiXmlNode * node, const char * name, const deque<long> &q, long pop)
00509 {
00510         deque<long>::const_iterator lmax, lmin;
00511 
00512         lmax = max_element(q.begin(), q.end());
00513         lmin = min_element(q.begin(), q.end());
00514         if (*lmax > 0 || *lmin < 0 || pop != 0 ) {
00515                 TiXmlElement * child;
00516                 child = WriteArray(name, q, MINERALS);
00517                 if (pop != 0)
00518                         AddLong(child, "Population", pop);
00519 
00520                 node->LinkEndChild(child);
00521                 return child;
00522         } else
00523                 return NULL;
00524 }
00525 
00526 long Rules::GetCargoType(const char * name)
00527 {
00528         if (stricmp(name, "Population") == 0 || stricmp(name, "Crew") == 0)
00529                 return POPULATION;
00530         else if (stricmp(name, "Fuel") == 0)
00531                 return FUEL;
00532         else if (stricmp(name, "Resources") == 0)
00533                 return RESOURCES;
00534         else {
00535                 long result = MineralID(name);
00536                 if (result < 0) {
00537                         Message * mess = TheGame->AddMessage("Error: Bad cargo type");
00538                         mess->AddItem("", name);
00539                 }
00540 
00541                 return result;
00542         }
00543 }
00544 
00545 const string Rules::GetCargoName(long type)
00546 {
00547         if (type == POPULATION)
00548                 return "Population";
00549         else if (type == FUEL)
00550                 return "Fuel";
00551         else {
00552                 if (type >= 0 && type < MaxMinType)
00553                         return MinName[type];
00554                 else
00555                         return "BadName";
00556         }
00557 }
00558 
00559 TiXmlElement * Rules::WriteArray(const char * node, const deque<long> & q, long Type)
00560 {
00561         if (Type == MINERALS)
00562                 return WriteArray(node, "Mineral", "Name", q, &MinName);
00563         else if (Type == TECHS)
00564                 return WriteArray(node, "Tech", "Name", q, &TechName);
00565         else if (Type == HABS)
00566                 return WriteArray(node, "Hab", "Name", q, &HabName);
00567         else if (Type == MINES)
00568                 return WriteArray(node, "Mine", "Type", q, &MineName);
00569         else {
00570                 assert(false);
00571                 return NULL;
00572         }
00573 }
00574 
00575 TiXmlElement * Rules::WriteArrayFloat(const char * node, const deque<double> & q, long Type)
00576 {
00577         if (Type == MINERALS)
00578                 return WriteArrayFloat(node, "Mineral", "Name", q, &MinName);
00579         else if (Type == TECHS)
00580                 return WriteArrayFloat(node, "Tech", "Name", q, &TechName);
00581         else if (Type == HABS)
00582                 return WriteArrayFloat(node, "Hab", "Name", q, &HabName);
00583         else if (Type == MINES)
00584                 return WriteArrayFloat(node, "Mine", "Type", q, &MineName);
00585         else {
00586                 assert(false);
00587                 return NULL;
00588         }
00589 }
00590 
00591 TiXmlElement * Rules::WriteArrayBool(const char * node, const char * name, const char * Attrib, const deque<bool> & arr, const deque<string> * desc /*= NULL*/)
00592 {
00593         TiXmlElement * result;
00594         result = new TiXmlElement(node);
00595         for (unsigned int i = 0; i < arr.size(); ++i) {
00596                 TiXmlElement txe(name);
00597                 if (desc == NULL)
00598                         txe.SetAttribute(Attrib, i+1);
00599                 else
00600                         txe.SetAttribute(Attrib, (*desc)[i]);
00601 
00602                 TiXmlText txt(arr[i] ? "True" : "False");
00603                 txe.InsertEndChild(txt);
00604                 result->InsertEndChild(txe);
00605         }
00606         return result;
00607 }
00608 
00609 TiXmlElement * Rules::WriteArray(const char * node, const char * name, const char * Attrib, const deque<long> & arr, const deque<string> * desc /*= NULL*/)
00610 {
00611         TiXmlElement * result;
00612         result = new TiXmlElement(node);
00613         for (unsigned int i = 0; i < arr.size(); ++i) {
00614                 TiXmlElement txe(name);
00615                 if (desc == NULL)
00616                         txe.SetAttribute(Attrib, i+1);
00617                 else
00618                         txe.SetAttribute(Attrib, (*desc)[i]);
00619 
00620                 TiXmlText txt(Long2String(arr[i]));
00621                 txe.InsertEndChild(txt);
00622                 result->InsertEndChild(txe);
00623         }
00624         return result;
00625 }
00626 
00627 TiXmlElement * Rules::WriteArrayFloat(const char * node, const char * name, const char * Attrib, const deque<double> & arr, const deque<string> * desc /*= NULL*/)
00628 {
00629         TiXmlElement * result;
00630         result = new TiXmlElement(node);
00631         for (unsigned int i = 0; i < arr.size(); ++i) {
00632                 TiXmlElement txe(name);
00633                 if (desc == NULL)
00634                         txe.SetAttribute(Attrib, i+1);
00635                 else
00636                         txe.SetAttribute(Attrib, (*desc)[i]);
00637 
00638                 TiXmlText txt(Float2String(arr[i]));
00639                 txe.InsertEndChild(txt);
00640                 result->InsertEndChild(txe);
00641         }
00642         return result;
00643 }
00644 
00645 bool Rules::ParseArray(const TiXmlNode * node, deque<long> & q, long Type)
00646 {
00647         if (Type == MINERALS)
00648                 return ParseArray(node, "Mineral", "Name", q, &MinName);
00649         else if (Type == TECHS)
00650                 return ParseArray(node, "Tech", "Name", q, &TechName);
00651         else if (Type == HABS)
00652                 return ParseArray(node, "Hab", "Name", q, &HabName);
00653         else if (Type == MINES)
00654                 return ParseArray(node, "Mine", "Type", q, &MineName);
00655         else
00656                 return false;
00657 }
00658 
00659 bool Rules::ParseArrayFloat(const TiXmlNode * node, deque<double> & q, long Type)
00660 {
00661         if (Type == MINERALS)
00662                 return ParseArrayFloat(node, "Mineral", "Name", q, &MinName);
00663         else if (Type == TECHS)
00664                 return ParseArrayFloat(node, "Tech", "Name", q, &TechName);
00665         else if (Type == HABS)
00666                 return ParseArrayFloat(node, "Hab", "Name", q, &HabName);
00667         else if (Type == MINES)
00668                 return ParseArrayFloat(node, "Mine", "Type", q, &MineName);
00669         else
00670                 return false;
00671 }
00672 
00673 bool Rules::ParseArrayBool(const TiXmlNode * node, const char * name, const char * Attrib, deque<bool> & arr, const deque<string> * desc /*= NULL*/)
00674 {
00675         if (!node)
00676                 return false;
00677 
00678         bool IsOK = true;
00679         const TiXmlNode * child;
00680         for (child = node->FirstChild(name); child; child = child->NextSibling(name)) {
00681                 const TiXmlElement * el = child->ToElement();
00682                 if (el == NULL) {
00683                         Message * mess = TheGame->AddMessage("Error: Invalid element in ParseArrayBool");
00684                         mess->AddItem("", name);
00685                         IsOK = false;
00686                         continue;
00687                 }
00688 
00689                 unsigned int index;
00690                 if (desc == NULL) {
00691                         el->Attribute(Attrib, (int*)(&index));
00692                 } else {
00693                         const char * type;
00694                         type = el->Attribute(Attrib);
00695                         if (type == NULL) {
00696                                 Message * mess = TheGame->AddMessage("Error: Invalid attribute in ParseArrayBool");
00697                                 mess->AddItem(name, Attrib);
00698                                 IsOK = false;
00699                                 continue;
00700                         }
00701 
00702                         deque<string>::const_iterator in;
00703                         in = find(desc->begin(), desc->end(), type);
00704                         if (in == desc->end()) {
00705                                 Message * mess = TheGame->AddMessage("Error: Invalid attribute in ParseArrayBool");
00706                                 mess->AddItem(name, Attrib);
00707                                 IsOK = false;
00708                                 continue;
00709                         }
00710 
00711                         index = in - desc->begin() + 1;
00712                 }
00713 
00714                 if (index < 1 || index > arr.size()) {
00715                         Message * mess = TheGame->AddMessage("Error: Invalid index in ParseArrayBool");
00716                         mess->AddLong(name, index);
00717                         IsOK = false;
00718                         continue;
00719                 }
00720 
00721                 arr[index-1] = GetBool(child);
00722         }
00723 
00724         return IsOK;
00725 }
00726 
00727 bool Rules::ParseArray(const TiXmlNode * node, const char * name, const char * Attrib, deque<long> & arr, const deque<string> * desc /*= NULL*/)
00728 {
00729         if (!node)
00730                 return false;
00731 
00732         bool IsOK = true;
00733         const TiXmlNode * child;
00734         for (child = node->FirstChild(name); child; child = child->NextSibling(name)) {
00735                 const TiXmlElement * el = child->ToElement();
00736                 if (el == NULL) {
00737                         Message * mess = TheGame->AddMessage("Error: Invalid element in ParseArray");
00738                         mess->AddItem("", name);
00739                         IsOK = false;
00740                         continue;
00741                 }
00742 
00743                 unsigned int index;
00744                 if (desc == NULL) {
00745                         el->Attribute(Attrib, (int*)(&index));
00746                 } else {
00747                         const char * type;
00748                         type = el->Attribute(Attrib);
00749                         if (type == NULL) {
00750                                 Message * mess = TheGame->AddMessage("Error: Invalid attribute in ParseArray");
00751                                 mess->AddItem(name, Attrib);
00752                                 IsOK = false;
00753                                 continue;
00754                         }
00755 
00756                         deque<string>::const_iterator in;
00757                         in = find(desc->begin(), desc->end(), type);
00758                         if (in == desc->end()) {
00759                                 Message * mess = TheGame->AddMessage("Error: Invalid attribute in ParseArray");
00760                                 mess->AddItem(name, Attrib);
00761                                 IsOK = false;
00762                                 continue;
00763                         }
00764 
00765                         index = in - desc->begin() + 1;
00766                 }
00767 
00768                 if (index < 1 || index > arr.size()) {
00769                         Message * mess = TheGame->AddMessage("Error: Invalid index in ParseArray");
00770                         mess->AddLong(name, index);
00771                         IsOK = false;
00772                         continue;
00773                 }
00774 
00775                 arr[index-1] = GetLong(child);
00776         }
00777 
00778         return IsOK;
00779 }
00780 
00781 bool Rules::ParseArrayFloat(const TiXmlNode * node, const char * name, const char * Attrib, deque<double> & arr, const deque<string> * desc /*= NULL*/)
00782 {
00783         if (!node)
00784                 return false;
00785 
00786         bool IsOK = true;
00787         const TiXmlNode * child;
00788         for (child = node->FirstChild(name); child; child = child->NextSibling(name)) {
00789                 const TiXmlElement * el = child->ToElement();
00790                 if (el == NULL) {
00791                         Message * mess = TheGame->AddMessage("Error: Invalid element in ParseArray");
00792                         mess->AddItem("", name);
00793                         IsOK = false;
00794                         continue;
00795                 }
00796 
00797                 unsigned int index;
00798                 if (desc == NULL) {
00799                         el->Attribute(Attrib, (int*)(&index));
00800                 } else {
00801                         const char * type;
00802                         type = el->Attribute(Attrib);
00803                         if (type == NULL) {
00804                                 Message * mess = TheGame->AddMessage("Error: Invalid attribute in ParseArray");
00805                                 mess->AddItem(name, Attrib);
00806                                 IsOK = false;
00807                                 continue;
00808                         }
00809 
00810                         deque<string>::const_iterator in;
00811                         in = find(desc->begin(), desc->end(), type);
00812                         if (in == desc->end()) {
00813                                 Message * mess = TheGame->AddMessage("Error: Invalid attribute in ParseArray");
00814                                 mess->AddItem(name, Attrib);
00815                                 IsOK = false;
00816                                 continue;
00817                         }
00818 
00819                         index = in - desc->begin() + 1;
00820                 }
00821 
00822                 if (index < 1 || index > arr.size()) {
00823                         Message * mess = TheGame->AddMessage("Error: Invalid index in ParseArray");
00824                         mess->AddLong(name, index);
00825                         IsOK = false;
00826                         continue;
00827                 }
00828 
00829                 arr[index-1] = GetDouble(child);
00830         }
00831 
00832         return IsOK;
00833 }
00834 
00835 long Rules::RandomHab(HabType ht)
00836 {
00837         deque<long> * hoa = GetHabOddArray(ht, false);
00838 
00839         if (hoa == NULL) {
00840                 // serious problem
00841                 return 50;
00842         }
00843 
00844         long rnd = Random((*hoa)[hoa->size() - GetConstant("MinHabValue")]) + 1;
00845 
00846         // now do a binary search: g is the guess, hig is the prior guess on the high side
00847         // log is the prior guess on the low side
00848         long hig = GetConstant("MaxHabValue") - GetConstant("MinHabValue");
00849         long log = 0;
00850         long g = (log + hig) / 2;       // This is here to remove a warning, not really needed
00851         bool done = false;
00852 
00853         while (!done) {
00854                 g = (log + hig) / 2;
00855 
00856                 if (g > 0 && rnd <= (*hoa)[g-1])
00857                         hig = g - 1;
00858                 else if (g < hig && rnd > (*hoa)[g])
00859                         log = g + 1;
00860                 else
00861                         done = true;
00862         }
00863 
00864         return g + GetConstant("MinHabValue");
00865 }
00866 
00867 // regenerate hab till it matches the race's hab
00868 long Rules::GetSecondHab(HabType ht, const Player * owner)
00869 {
00870         long Result;
00871         long count = 0;
00872         do {
00873                 Result = RandomHab(ht);
00874         } while (count++ < 1000 &&
00875                         owner->HabCenter(ht) >= 0 &&
00876                                 (Result < owner->HabCenter(ht) - owner->HabWidth(ht) ||
00877                                 Result > owner->HabCenter(ht) + owner->HabWidth(ht))
00878                         );
00879 
00880         return Result;
00881 }
00882 
00883 bool Rules::ParseMinSettings(const TiXmlNode * node)
00884 {
00885         const TiXmlNode * child;
00886         mMinMC.insert(mMinMC.begin(), Rules::MaxMinType, 1);
00887         mMaxMC.insert(mMaxMC.begin(), Rules::MaxMinType, 120);
00888         mHWMinMC.insert(mHWMinMC.begin(), Rules::MaxMinType, 30);
00889         mHWMaxMC.insert(mHWMaxMC.begin(), Rules::MaxMinType, 120);
00890         mHWFloorMC.insert(mHWFloorMC.begin(), Rules::MaxMinType, 30);
00891 
00892         child = node->FirstChild("AllWorlds");
00893         if (!child) {
00894                 Message * mess = TheGame->AddMessage("Error: Missing section");
00895                 mess->AddItem("Section", "Mineral settings - AllWorlds");
00896                 return false;
00897         }
00898 
00899         if (!ParseArray(child->FirstChild("Minimum"), mMinMC, MINERALS))
00900                 return false;
00901 
00902         if (!ParseArray(child->FirstChild("Maximum"), mMaxMC, MINERALS))
00903                 return false;
00904 
00905         child = node->FirstChild("HomeWorlds");
00906         if (!child) {
00907                 Message * mess = TheGame->AddMessage("Error: Missing section");
00908                 mess->AddItem("Section", "Mineral settings - HomeWorlds");
00909                 return false;
00910         }
00911 
00912         if (!ParseArray(child->FirstChild("Minimum"), mHWMinMC, MINERALS))
00913                 return false;
00914 
00915         if (!ParseArray(child->FirstChild("Maximum"), mHWMaxMC, MINERALS))
00916                 return false;
00917 
00918         if (!ParseArray(child->FirstChild("HWFloor"), mHWFloorMC, MINERALS))
00919                 return false;
00920 
00921         return true;
00922 }
00923 
00924 void Rules::WriteMinSettings(TiXmlNode * node)
00925 {
00926         TiXmlElement minset("MineralSettings");
00927 
00928         TiXmlElement All("AllWorlds");
00929         All.LinkEndChild(Rules::WriteArray("Minimum", mMinMC, MINERALS));
00930         All.LinkEndChild(Rules::WriteArray("Maximum", mMaxMC, MINERALS));
00931         minset.InsertEndChild(All);
00932 
00933         TiXmlElement HWs("HomeWorlds");
00934         HWs.LinkEndChild(Rules::WriteArray("Minimum", mHWMinMC, MINERALS));
00935         HWs.LinkEndChild(Rules::WriteArray("Maximum", mHWMaxMC, MINERALS));
00936         minset.InsertEndChild(HWs);
00937 
00938         minset.LinkEndChild(Rules::WriteArray("HWFloor", mHWFloorMC, MINERALS));
00939         node->InsertEndChild(minset);
00940 }
00941 /*
00942     <MineralSettings>
00943 <!-- AKA BMM -->
00944         <AllWorlds>
00945                         <Minimum>
00946                                 <Mineral Name="Ironium">1</Mineral>
00947                                 <Mineral Name="Boranium">1</Mineral>
00948                                 <Mineral Name="Germanium">1</Mineral>
00949                         </Minimum>
00950                         <Maximum>
00951                                 <Mineral Name="Ironium">120</Mineral>
00952                                 <Mineral Name="Boranium">120</Mineral>
00953                                 <Mineral Name="Germanium">120</Mineral>
00954                         </Maximum>
00955         </AllWorlds>
00956         <HomeWorlds>
00957                         <Minimum>
00958                                 <Mineral Name="Ironium">1</Mineral>
00959                                 <Mineral Name="Boranium">1</Mineral>
00960                                 <Mineral Name="Germanium">1</Mineral>
00961                         </Minimum>
00962                         <Maximum>
00963                                 <Mineral Name="Ironium">120</Mineral>
00964                                 <Mineral Name="Boranium">120</Mineral>
00965                                 <Mineral Name="Germanium">120</Mineral>
00966                         </Maximum>
00967                 </HomeWorlds>
00968         <HWFloor>
00969             <Mineral Name="Ironium">30</Mineral>
00970             <Mineral Name="Boranium">30</Mineral>
00971             <Mineral Name="Germanium">30</Mineral>
00972         </HWFloor>
00973     </MineralSettings>
00974 */
00975 deque<long> * Rules::GetHabOddArray(HabType ht, bool Create /*= false*/)
00976 {
00977         while (Create && ht >= mHabOdds.size()) {
00978                 deque<long> * hoa;
00979                 hoa = new deque<long>;
00980                 hoa->insert(hoa->begin(), 99, 1);
00981                 mHabOdds.push_back(hoa);
00982         }
00983 
00984         if (ht >= mHabOdds.size())
00985                 return NULL;
00986         else
00987                 return mHabOdds[ht];
00988 }
00989 
00990 // All HWs start with the same mineral concentrations and stockpiles
00991 long Rules::GetHWMC(long mintype)
00992 {
00993         if (mHWSetupMC.size() == 0) {
00994                 mHWSetupMC.insert(mHWSetupMC.begin(), Rules::MaxMinType, 1);
00995                 for (int i = 0; i < Rules::MaxMinType; ++i) {
00996                         if (Random(100) < Rules::HWMinMC(i))
00997                                 mHWSetupMC[i] = Rules::HWMinMC(i);
00998                         else {
00999                                 long die1 = (Rules::HWMaxMC(i) - Rules::HWMinMC(i)) / 2;
01000                                 long die2 = (Rules::HWMaxMC(i) - Rules::HWMinMC(i) + 1) / 2;
01001                                 mHWSetupMC[i] = Random(die1) + Random(die2) + Rules::HWMinMC(i) + 1;
01002                         }
01003 
01004                         if (mHWSetupMC[i] < mHWFloorMC[i])
01005                                 mHWSetupMC[i] = mHWFloorMC[i];
01006                 }
01007         }
01008 
01009         return mHWSetupMC[mintype];
01010 }
01011 
01012 long Rules::GetHWStartMinerals(long mintype)
01013 {
01014         long lMax;
01015         long lMin = 165;
01016         if (TheGame->GetCreation() && mHWSetupSM.size() == 0) {
01017                 mHWSetupSM.insert(mHWSetupSM.begin(), Rules::MaxMinType, 1);
01018                 for (int i = 0; i < Rules::MaxMinType; ++i) {
01019                         lMax = Rules::GetHWMC(i) * 11;
01020                         if (lMax < 500)
01021                                 lMax = 500;
01022 
01023                         mHWSetupSM[i] = Random(lMin, lMax);
01024                 }
01025         }
01026 
01027 // leave as 'unfinished' will generate a warning to this line, easier for ide to jump right here
01028 #pragma unfinished(__FILE__ " : Rules::GetHWStartMinerals needs to set starting minerals correctly")
01029 
01030         return mHWSetupSM[mintype];
01031 }
01032 
01033 long Rules::GetSWStartMinerals(long /*mintype*/)
01034 {
01035         if (TheGame->GetCreation())
01036                 return Random(TheGame->GetCreation()->mSWMinMin, TheGame->GetCreation()->mSWMaxMin);
01037         else
01038                 return 0;
01039 }
01040 
01041 double Rules::OverGateRange(long range, long dist)
01042 {
01043         if (range == -1 || range >= dist)
01044                 return 0;
01045         else
01046                 return double(dist - range) / 4.0 * range;
01047 }
01048 
01049 double Rules::OverGateMass(long cap1, long cap2, long mass)
01050 {
01051         double safe = 1.0;
01052         if (cap1 != -1 && cap1 < mass)
01053                 safe = 1.0 - double(mass - cap1) / 4.0 * cap1;
01054         if (cap2 != -1 && cap2 < mass)
01055                 safe *= 1.0 - double(mass - cap2) / 4.0 * cap2;
01056 
01057         return 1.0 - safe;
01058 }

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