void System::LoadObject(const DataNode &node, Set<Planet> &planets, int parent) { int index = objects.size(); objects.push_back(StellarObject()); StellarObject &object = objects.back(); object.parent = parent; bool isAdded = (node.Token(0) == "add"); if(node.Size() >= 2 + isAdded) { Planet *planet = planets.Get(node.Token(1 + isAdded)); object.planet = planet; planet->SetSystem(this); } for(const DataNode &child : node) { if(child.Token(0) == "sprite" && child.Size() >= 2) { object.LoadSprite(child); object.isStar = !child.Token(1).compare(0, 5, "star/"); if(!object.isStar) { object.isStation = !child.Token(1).compare(0, 14, "planet/station"); object.isMoon = (!object.isStation && parent >= 0 && !objects[parent].IsStar()); } } else if(child.Token(0) == "distance" && child.Size() >= 2) object.distance = child.Value(1); else if(child.Token(0) == "period" && child.Size() >= 2) object.speed = 360. / child.Value(1); else if(child.Token(0) == "offset" && child.Size() >= 2) object.offset = child.Value(1); else if(child.Token(0) == "object") LoadObject(child, planets, index); else child.PrintTrace("Skipping unrecognized attribute:"); } }
// Load a system's description. void System::Load(const DataNode &node, Set<Planet> &planets) { if(node.Size() < 2) return; name = node.Token(1); // If this is truly a change and not just being called by Load(), these sets // of objects will be entirely replaced by any new ones, rather than adding // the new ones on to the end of the list: bool resetLinks = !links.empty(); bool resetAsteroids = !asteroids.empty(); bool resetFleets = !fleets.empty(); bool resetObjects = !objects.empty(); for(const DataNode &child : node) { if(child.Token(0) == "pos" && child.Size() >= 3) position.Set(child.Value(1), child.Value(2)); else if(child.Token(0) == "government" && child.Size() >= 2) government = GameData::Governments().Get(child.Token(1)); else if(child.Token(0) == "link") { if(resetLinks) { resetLinks = false; links.clear(); } if(child.Size() >= 2) links.push_back(GameData::Systems().Get(child.Token(1))); } else if(child.Token(0) == "habitable" && child.Size() >= 2) habitable = child.Value(1); else if(child.Token(0) == "asteroids") { if(resetAsteroids) { resetAsteroids = false; asteroids.clear(); } if(child.Size() >= 4) asteroids.emplace_back(child.Token(1), child.Value(2), child.Value(3)); } else if(child.Token(0) == "trade" && child.Size() >= 3) trade[child.Token(1)].SetBase(child.Value(2)); else if(child.Token(0) == "fleet") { if(resetFleets) { resetFleets = false; fleets.clear(); } if(child.Size() >= 3) fleets.emplace_back(GameData::Fleets().Get(child.Token(1)), child.Value(2)); } else if(child.Token(0) == "object") { if(resetObjects) { for(StellarObject &object : objects) if(object.GetPlanet()) { Planet *planet = planets.Get(object.GetPlanet()->Name()); planet->RemoveSystem(this); } resetObjects = false; objects.clear(); } LoadObject(child, planets); } else child.PrintTrace("Skipping unrecognized attribute:"); } // Set planet messages based on what zone they are in. for(StellarObject &object : objects) { if(object.message || object.planet) continue; const StellarObject *root = &object; while(root->parent >= 0) root = &objects[root->parent]; static const string STAR = "You cannot land on a star!"; static const string HOTPLANET = "This planet is too hot to land on."; static const string COLDPLANET = "This planet is too cold to land on."; static const string UNINHABITEDPLANET = "This planet is uninhabited."; static const string HOTMOON = "This moon is too hot to land on."; static const string COLDMOON = "This moon is too cold to land on."; static const string UNINHABITEDMOON = "This moon is uninhabited."; static const string STATION = "This station cannot be docked with."; double fraction = root->distance / habitable; if(object.IsStar()) object.message = &STAR; else if (object.IsStation()) object.message = &STATION; else if (object.IsMoon()) { if(fraction < .5) object.message = &HOTMOON; else if(fraction >= 2.) object.message = &COLDMOON; else object.message = &UNINHABITEDMOON; } else { if(fraction < .5) object.message = &HOTPLANET; else if(fraction >= 2.) object.message = &COLDPLANET; else object.message = &UNINHABITEDPLANET; } } }
// Load a system's description. void System::Load(const DataNode &node, Set<Planet> &planets) { if(node.Size() < 2) return; name = node.Token(1); // For the following keys, if this data node defines a new value for that // key, the old values should be cleared (unless using the "add" keyword). set<string> shouldOverwrite = {"asteroids", "attributes", "fleet", "link", "object"}; for(const DataNode &child : node) { // Check for the "add" or "remove" keyword. bool add = (child.Token(0) == "add"); bool remove = (child.Token(0) == "remove"); if((add || remove) && child.Size() < 2) { child.PrintTrace("Skipping " + child.Token(0) + " with no key given:"); continue; } // Get the key and value (if any). const string &key = child.Token((add || remove) ? 1 : 0); int valueIndex = (add || remove) ? 2 : 1; bool hasValue = (child.Size() > valueIndex); const string &value = child.Token(hasValue ? valueIndex : 0); // Check for conditions that require clearing this key's current value. // "remove <key>" means to clear the key's previous contents. // "remove <key> <value>" means to remove just that value from the key. bool removeAll = (remove && !hasValue); // If this is the first entry for the given key, and we are not in "add" // or "remove" mode, its previous value should be cleared. bool overwriteAll = (!add && !remove && shouldOverwrite.count(key)); overwriteAll |= (!add && !remove && key == "minables" && shouldOverwrite.count("asteroids")); // Clear the data of the given type. if(removeAll || overwriteAll) { // Clear the data of the given type. if(key == "government") government = nullptr; else if(key == "music") music.clear(); else if(key == "attributes") attributes.clear(); else if(key == "link") links.clear(); else if(key == "asteroids" || key == "minables") asteroids.clear(); else if(key == "haze") haze = nullptr; else if(key == "trade") trade.clear(); else if(key == "fleet") fleets.clear(); else if(key == "object") { // Make sure any planets that were linked to this system know // that they are no longer here. for(StellarObject &object : objects) if(object.GetPlanet()) planets.Get(object.GetPlanet()->TrueName())->RemoveSystem(this); objects.clear(); } // If not in "overwrite" mode, move on to the next node. if(overwriteAll) shouldOverwrite.erase(key == "minables" ? "asteroids" : key); else continue; } // Handle the attributes which can be "removed." if(!hasValue && key != "object") { child.PrintTrace("Expected key to have a value:"); continue; } else if(key == "attributes") { if(remove) for(int i = valueIndex; i < child.Size(); ++i) attributes.erase(child.Token(i)); else for(int i = valueIndex; i < child.Size(); ++i) attributes.insert(child.Token(i)); } else if(key == "link") { if(remove) links.erase(GameData::Systems().Get(value)); else links.insert(GameData::Systems().Get(value)); } else if(key == "asteroids") { if(remove) { for(auto it = asteroids.begin(); it != asteroids.end(); ++it) if(it->Name() == value) { asteroids.erase(it); break; } } else if(child.Size() >= 4) asteroids.emplace_back(value, child.Value(valueIndex + 1), child.Value(valueIndex + 2)); } else if(key == "minables") { const Minable *type = GameData::Minables().Get(value); if(remove) { for(auto it = asteroids.begin(); it != asteroids.end(); ++it) if(it->Type() == type) { asteroids.erase(it); break; } } else if(child.Size() >= 4) asteroids.emplace_back(type, child.Value(valueIndex + 1), child.Value(valueIndex + 2)); } else if(key == "fleet") { const Fleet *fleet = GameData::Fleets().Get(value); if(remove) { for(auto it = fleets.begin(); it != fleets.end(); ++it) if(it->Get() == fleet) { fleets.erase(it); break; } } else fleets.emplace_back(fleet, child.Value(valueIndex + 1)); } // Handle the attributes which cannot be "removed." else if(remove) { child.PrintTrace("Cannot \"remove\" a specific value from the given key:"); continue; } else if(key == "pos" && child.Size() >= 3) position.Set(child.Value(valueIndex), child.Value(valueIndex + 1)); else if(key == "government") government = GameData::Governments().Get(value); else if(key == "music") music = value; else if(key == "habitable") habitable = child.Value(valueIndex); else if(key == "belt") asteroidBelt = child.Value(valueIndex); else if(key == "haze") haze = SpriteSet::Get(value); else if(key == "trade" && child.Size() >= 3) trade[value].SetBase(child.Value(valueIndex + 1)); else if(key == "object") LoadObject(child, planets); else child.PrintTrace("Skipping unrecognized attribute:"); } // Set planet messages based on what zone they are in. for(StellarObject &object : objects) { if(object.message || object.planet) continue; const StellarObject *root = &object; while(root->parent >= 0) root = &objects[root->parent]; static const string STAR = "You cannot land on a star!"; static const string HOTPLANET = "This planet is too hot to land on."; static const string COLDPLANET = "This planet is too cold to land on."; static const string UNINHABITEDPLANET = "This planet is uninhabited."; static const string HOTMOON = "This moon is too hot to land on."; static const string COLDMOON = "This moon is too cold to land on."; static const string UNINHABITEDMOON = "This moon is uninhabited."; static const string STATION = "This station cannot be docked with."; double fraction = root->distance / habitable; if(object.IsStar()) object.message = &STAR; else if (object.IsStation()) object.message = &STATION; else if (object.IsMoon()) { if(fraction < .5) object.message = &HOTMOON; else if(fraction >= 2.) object.message = &COLDMOON; else object.message = &UNINHABITEDMOON; } else { if(fraction < .5) object.message = &HOTPLANET; else if(fraction >= 2.) object.message = &COLDPLANET; else object.message = &UNINHABITEDPLANET; } } }
// Load a planet's description from a file. void Planet::Load(const DataNode &node, const Set<Sale<Ship>> &ships, const Set<Sale<Outfit>> &outfits) { if(node.Size() < 2) return; name = node.Token(1); // If this planet has been loaded before, these sets of items should be // reset if they are also defined here, instead of appending to them: bool resetAttributes = !attributes.empty(); bool resetDescription = !description.empty(); bool resetSpaceport = !spaceport.empty(); for(const DataNode &child : node) { if(child.Token(0) == "landscape" && child.Size() >= 2) landscape = SpriteSet::Get(child.Token(1)); else if(child.Token(0) == "attributes") { if(resetAttributes) { resetAttributes = false; attributes.clear(); } for(int i = 1; i < child.Size(); ++i) attributes.insert(child.Token(i)); } else if(child.Token(0) == "description" && child.Size() >= 2) { if(resetDescription) { resetDescription = false; description.clear(); } if(!description.empty() && !child.Token(1).empty() && child.Token(1)[0] > ' ') description += '\t'; description += child.Token(1); description += '\n'; } else if(child.Token(0) == "spaceport" && child.Size() >= 2) { if(child.Token(1) == "clear") spaceport.clear(); else { if(resetSpaceport) { resetSpaceport = false; spaceport.clear(); } if(!spaceport.empty() && !child.Token(1).empty() && child.Token(1)[0] > ' ') spaceport += '\t'; spaceport += child.Token(1); spaceport += '\n'; } } else if(child.Token(0) == "shipyard" && child.Size() >= 2) { if(child.Token(1) == "clear") shipSales.clear(); else shipSales.push_back(ships.Get(child.Token(1))); } else if(child.Token(0) == "outfitter" && child.Size() >= 2) { if(child.Token(1) == "clear") outfitSales.clear(); else outfitSales.push_back(outfits.Get(child.Token(1))); } else if(child.Token(0) == "government" && child.Size() >= 2) government = GameData::Governments().Get(child.Token(1)); else if(child.Token(0) == "required reputation" && child.Size() >= 2) requiredReputation = child.Value(1); else if(child.Token(0) == "bribe" && child.Size() >= 2) bribe = child.Value(1); else if(child.Token(0) == "security" && child.Size() >= 2) security = child.Value(1); else if(child.Token(0) == "tribute" && child.Size() >= 2) { tribute = child.Value(1); for(const DataNode &grand : child) { if(grand.Token(0) == "threshold" && grand.Size() >= 2) defenseThreshold = grand.Value(1); else if(grand.Token(0) == "fleet" && grand.Size() >= 3) { defenseCount = (grand.Size() >= 3 ? grand.Value(2) : 1); defenseFleet = GameData::Fleets().Get(grand.Token(1)); } else grand.PrintTrace("Skipping unrecognized attribute:"); } } else child.PrintTrace("Skipping unrecognized attribute:"); } }