void MissionAction::Load(const DataNode &node, const string &missionName) { if(node.Size() >= 2) trigger = node.Token(1); if(node.Size() >= 3) system = node.Token(2); for(const DataNode &child : node) { if(child.Token(0) == "dialog") { for(int i = 1; i < child.Size(); ++i) { if(!dialogText.empty()) dialogText += "\n\t"; dialogText += child.Token(i); } for(const DataNode &grand : child) for(int i = 0; i < grand.Size(); ++i) { if(!dialogText.empty()) dialogText += "\n\t"; dialogText += grand.Token(i); } } else if(child.Token(0) == "conversation" && child.HasChildren()) conversation.Load(child); else if(child.Token(0) == "conversation" && child.Size() > 1) stockConversation = GameData::Conversations().Get(child.Token(1)); else if(child.Token(0) == "outfit" && child.Size() >= 2) { int count = (child.Size() < 3 ? 1 : static_cast<int>(child.Value(2))); gifts[GameData::Outfits().Get(child.Token(1))] = count; } else if(child.Token(0) == "require" && child.Size() >= 2) gifts[GameData::Outfits().Get(child.Token(1))] = 0; else if(child.Token(0) == "payment") { if(child.Size() == 1) paymentMultiplier += 150; if(child.Size() >= 2) payment += child.Value(1); if(child.Size() >= 3) paymentMultiplier += child.Value(2); } else if(child.Token(0) == "event" && child.Size() >= 2) { int days = (child.Size() >= 3 ? child.Value(2) : 0); events[child.Token(1)] = days; } else if(child.Token(0) == "fail") fail.insert(child.Size() >= 2 ? child.Token(1) : missionName); else conditions.Add(child); } }
void Trade::Load(const DataNode &node) { assert(node.Token(0) == "trade"); for(const DataNode &child : node) { if(child.Token(0) == "commodity" && child.Size() >= 2) { bool isSpecial = (child.Size() < 4); vector<Commodity> &list = (isSpecial ? specialCommodities : commodities); auto it = list.begin(); for( ; it != list.end(); ++it) if(it->name == child.Token(1)) break; if(it == list.end()) it = list.insert(it, Commodity()); it->name = child.Token(1); if(!isSpecial) { it->low = child.Value(2); it->high = child.Value(3); } for(const DataNode &grand : child) it->items.push_back(grand.Token(0)); } else if(child.Token(0) == "clear") commodities.clear(); else child.PrintTrace("Skipping unrecognized attribute:"); } }
void GameData::ReadEconomy(const DataNode &node) { if(!node.Size() || node.Token(0) != "economy") return; vector<string> headings; for(const DataNode &child : node) { if(child.Token(0) == "purchases") { for(const DataNode &grand : child) if(grand.Size() >= 3 && grand.Value(2)) purchases[systems.Get(grand.Token(0))][grand.Token(1)] += grand.Value(2); } else if(child.Token(0) == "system") { headings.clear(); for(int index = 1; index < child.Size(); ++index) headings.push_back(child.Token(index)); } else { System &system = *systems.Get(child.Token(0)); int index = 0; for(const string &commodity : headings) system.SetSupply(commodity, child.Value(++index)); } } }
void Outfit::Load(const DataNode &node) { if(node.Size() >= 2) name = node.Token(1); for(const DataNode &child : node) { if(child.Token(0) == "category" && child.Size() >= 2) category = child.Token(1); else if(child.Token(0) == "flare sprite" && child.Size() >= 2) { flareSprites.emplace_back(Animation(), 1); flareSprites.back().first.Load(child); } else if(child.Token(0) == "flare sound" && child.Size() >= 2) ++flareSounds[Audio::Get(child.Token(1))]; else if(child.Token(0) == "afterburner effect" && child.Size() >= 2) ++afterburnerEffects[GameData::Effects().Get(child.Token(1))]; else if(child.Token(0) == "thumbnail" && child.Size() >= 2) thumbnail = SpriteSet::Get(child.Token(1)); else if(child.Token(0) == "weapon") LoadWeapon(child); else if(child.Token(0) == "description" && child.Size() >= 2) { description += child.Token(1); description += '\n'; } else if(child.Size() >= 2) attributes[child.Token(0)] = child.Value(1); else child.PrintTrace("Skipping unrecognized attribute:"); } }
// Load a government's definition from a file. void Government::Load(const DataNode &node) { if(node.Size() >= 2) name = node.Token(1); for(const DataNode &child : node) { if(child.Token(0) == "swizzle" && child.Size() >= 2) swizzle = child.Value(1); else if(child.Token(0) == "color" && child.Size() >= 4) color = Color(child.Value(1), child.Value(2), child.Value(3)); else if(child.Token(0) == "player reputation" && child.Size() >= 2) initialPlayerReputation = child.Value(1); else if(child.Token(0) == "attitude toward") { for(const DataNode &grand : child) if(grand.Size() >= 2) { const Government *gov = GameData::Governments().Get(grand.Token(0)); attitudeToward.resize(nextID, 0.); attitudeToward[gov->id] = grand.Value(1); } } else if(child.Token(0) == "penalty for") { for(const DataNode &grand : child) if(grand.Size() >= 2) { if(grand.Token(0) == "assist") penaltyFor[ShipEvent::ASSIST] = grand.Value(1); if(grand.Token(0) == "disable") penaltyFor[ShipEvent::DISABLE] = grand.Value(1); if(grand.Token(0) == "board") penaltyFor[ShipEvent::BOARD] = grand.Value(1); if(grand.Token(0) == "capture") penaltyFor[ShipEvent::CAPTURE] = grand.Value(1); if(grand.Token(0) == "destroy") penaltyFor[ShipEvent::DESTROY] = grand.Value(1); if(grand.Token(0) == "atrocity") penaltyFor[ShipEvent::ATROCITY] = grand.Value(1); } } else if(child.Token(0) == "bribe" && child.Size() >= 2) bribe = child.Value(1); else if(child.Token(0) == "fine" && child.Size() >= 2) fine = child.Value(1); else if(child.Token(0) == "death sentence" && child.Size() >= 2) deathSentence = GameData::Conversations().Get(child.Token(1)); else if(child.Token(0) == "friendly hail" && child.Size() >= 2) friendlyHail = GameData::Phrases().Get(child.Token(1)); else if(child.Token(0) == "hostile hail" && child.Size() >= 2) hostileHail = GameData::Phrases().Get(child.Token(1)); } }
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:"); } }
void Galaxy::Load(const DataNode &node) { if(node.Size() >= 2) name = node.Token(1); for(const DataNode &child : node) { if(child.Token(0) == "pos" && child.Size() >= 3) position = QVector2D(child.Value(1), child.Value(2)); else if(child.Token(0) == "sprite" && child.Size() >= 2) sprite = child.Token(1); else unparsed.push_back(child); } }
void DataWriter::Write(const DataNode &node) { for(int i = 0; i < node.Size(); ++i) WriteToken(node.Token(i)); Write(); if(node.begin() != node.end()) { BeginChild(); { for(const DataNode &child : node) Write(child); } EndChild(); } }
void ConditionSet::Add(const DataNode &node) { if(node.Size() == 2) Add(node.Token(0), node.Token(1)); else if(node.Size() == 3) Add(node.Token(0), node.Token(1), node.Value(2)); else if(node.Size() == 1 && node.Token(0) == "never") entries.emplace_back("", "!=", 0); else if(node.Size() == 1 && (node.Token(0) == "and" || node.Token(0) == "or")) { children.emplace_back(); children.back().Load(node); } }
void Phrase::Load(const DataNode &node) { if(node.Token(0) != "phrase") return; words.push_back(vector<vector<string>>()); for(const DataNode &child : node) { if(child.Token(0) == "word") { words.back().push_back(vector<string>()); for(const DataNode &grand : child) words.back().back().push_back(grand.Token(0)); } } }
// Format and add the text from the given node to the given string. void Dialog::ParseTextNode(const DataNode &node, size_t startingIndex, string &text) { for(int i = startingIndex; i < node.Size(); ++i) { if(!text.empty()) text += "\n\t"; text += node.Token(i); } for(const DataNode &child : node) for(int i = 0; i < child.Size(); ++i) { if(!text.empty()) text += "\n\t"; text += child.Token(i); } }
void Personality::Load(const DataNode &node) { flags = 0; for(int i = 1; i < node.Size(); ++i) Parse(node.Token(i)); for(const DataNode &child : node) { if(child.Token(0) == "confusion" && child.Size() >= 2) confusionMultiplier = child.Value(1); else { for(int i = 0; i < child.Size(); ++i) Parse(child.Token(i)); } } }
void Trade::Load(const DataNode &node) { assert(node.Token(0) == "trade"); for(const DataNode &child : node) { if(child.Token(0) == "commodity" && child.Size() >= 4) { commodities.push_back(Commodity()); commodities.back().name = child.Token(1); commodities.back().low = child.Value(2); commodities.back().high = child.Value(3); for(const DataNode &grand : child) commodities.back().items.push_back(grand.Token(0)); } } }
// Load a planet's description from a file. void Planet::Load(const DataNode &node) { if(node.Size() < 2) return; name = node.Token(1); for(const DataNode &child : node) { if(child.Token(0) == "landscape" && child.Size() >= 2) landscape = child.Token(1); else if(child.Token(0) == "attributes") { for(int i = 1; i < child.Size(); ++i) attributes.push_back(child.Token(i)); } else if(child.Token(0) == "description" && child.Size() >= 2) { if(!description.isEmpty() && !child.Token(1).isEmpty() && child.Token(1)[0] > ' ') description += '\t'; description += child.Token(1); description += '\n'; } else if(child.Token(0) == "spaceport" && child.Size() >= 2) { if(!spaceport.isEmpty() && !child.Token(1).isEmpty() && child.Token(1)[0] > ' ') spaceport += '\t'; spaceport += child.Token(1); spaceport += '\n'; } else if(child.Token(0) == "shipyard" && child.Size() >= 2) shipyard.push_back(child.Token(1)); else if(child.Token(0) == "outfitter" && child.Size() >= 2) outfitter.push_back(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 unparsed.push_back(child); } }
void Fleet::Load(const DataNode &node) { if(node.Size() >= 2) fleetName = node.Token(1); // If Load() has already been called once on this fleet, any subsequent // calls will replace the variants instead of adding to them. bool resetVariants = !variants.empty(); for(const DataNode &child : node) { if(child.Token(0) == "government" && child.Size() >= 2) government = GameData::Governments().Get(child.Token(1)); else if(child.Token(0) == "names" && child.Size() >= 2) names = GameData::Phrases().Get(child.Token(1)); else if(child.Token(0) == "fighters" && child.Size() >= 2) fighterNames = GameData::Phrases().Get(child.Token(1)); else if(child.Token(0) == "cargo" && child.Size() >= 2) cargo = static_cast<int>(child.Value(1)); else if(child.Token(0) == "commodities" && child.Size() >= 2) { commodities.clear(); for(int i = 1; i < child.Size(); ++i) commodities.push_back(child.Token(i)); } else if(child.Token(0) == "personality") personality.Load(child); else if(child.Token(0) == "variant") { if(resetVariants) { resetVariants = false; variants.clear(); total = 0; } variants.emplace_back(child); total += variants.back().weight; } else child.PrintTrace("Skipping unrecognized attribute:"); } }
void GameEvent::Load(const DataNode &node) { // If the event has a name, a condition should be automatically created that // represents the fact that this event has occurred. if(node.Size() >= 2) { name = node.Token(1); conditionsToApply.Add("set", "event: " + name); } static const set<string> allowedChanges = { "fleet", "galaxy", "government", "link", "outfitter", "planet", "shipyard", "system", "unlink" }; for(const DataNode &child : node) { const string &key = child.Token(0); if(key == "date" && child.Size() >= 4) date = Date(child.Value(1), child.Value(2), child.Value(3)); else if(key == "unvisit" && child.Size() >= 2) systemsToUnvisit.push_back(GameData::Systems().Get(child.Token(1))); else if(key == "visit" && child.Size() >= 2) systemsToVisit.push_back(GameData::Systems().Get(child.Token(1))); else if(key == "unvisit planet" && child.Size() >= 2) planetsToUnvisit.push_back(GameData::Planets().Get(child.Token(1))); else if(key == "visit planet" && child.Size() >= 2) planetsToVisit.push_back(GameData::Planets().Get(child.Token(1))); else if(allowedChanges.count(key)) changes.push_back(child); else conditionsToApply.Add(child); } }
void GameEvent::Load(const DataNode &node) { // If the event has a name, a condition should be automatically created that // represents the fact that this event has occurred. if(node.Size() >= 2) conditionsToApply.Add("set", "event: " + node.Token(1)); for(const DataNode &child : node) { if(child.Token(0) == "date" && child.Size() >= 4) date = Date(child.Value(1), child.Value(2), child.Value(3)); else if(child.Token(0) == "unvisit" && child.Size() >= 2) systemsToUnvisit.push_back(GameData::Systems().Get(child.Token(1))); else if(child.Token(0) == "system" || child.Token(0) == "planet" || child.Token(0) == "shipyard" || child.Token(0) == "outfitter" || child.Token(0) == "fleet" || child.Token(0) == "government" || child.Token(0) == "link" || child.Token(0) == "unlink") changes.push_back(child); else conditionsToApply.Add(child); } }
void ConditionSet::Add(const DataNode &node) { if(node.Size() == 2) { if(!Add(node.Token(0), node.Token(1))) node.PrintTrace("Unrecognized condition expression:"); } else if(node.Size() == 3) { if(!Add(node.Token(0), node.Token(1), node.Value(2))) node.PrintTrace("Unrecognized condition expression:"); } else if(node.Size() == 1 && node.Token(0) == "never") entries.emplace_back("", "!=", 0); else if(node.Size() == 1 && (node.Token(0) == "and" || node.Token(0) == "or")) { children.emplace_back(); children.back().Load(node); } else node.PrintTrace("Unrecognized condition expression:"); }
// Load depreciation records. void Depreciation::Load(const DataNode &node) { // Check if this is fleet or stock depreciation. isStock = (node.Token(0) == NAME[1]); isLoaded = true; for(const DataNode &child : node) { bool isShip = (child.Token(0) == "ship"); bool isOutfit = (child.Token(0) == "outfit"); if(!(isShip || isOutfit) || child.Size() < 2) continue; // Figure out which record we're modifying. map<int, int> &entry = isShip ? ships[GameData::Ships().Get(child.Token(1))] : outfits[GameData::Outfits().Get(child.Token(1))]; // Load any depreciation records for this item. for(const DataNode &grand : child) if(grand.Size() >= 2) entry[grand.Value(0)] += grand.Value(1); } }
// 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 conversation from file. void Conversation::Load(const DataNode &node) { // Make sure this really is a conversation specification. if(node.Token(0) != "conversation") return; // Free any previously loaded data. nodes.clear(); for(const DataNode &child : node) { if(child.Token(0) == "scene" && child.Size() >= 2) { // A scene always starts a new text node. AddNode(); nodes.back().scene = SpriteSet::Get(child.Token(1)); } else if(child.Token(0) == "label" && child.Size() >= 2) { // You cannot merge text above a label with text below it. if(!nodes.empty()) nodes.back().canMergeOnto = false; AddLabel(child.Token(1), child); } else if(child.Token(0) == "choice") { // Create a new node with one or more choices in it. nodes.emplace_back(true); for(const DataNode &grand : child) { // Store the text of this choice. By default, the choice will // just bring you to the next node in the script. nodes.back().data.emplace_back(grand.Token(0), nodes.size()); nodes.back().data.back().first += '\n'; LoadGotos(grand); } if(nodes.back().data.empty()) { child.PrintTrace("Conversation contains an empty \"choice\" node:"); nodes.pop_back(); } } else if(child.Token(0) == "name") { // A name entry field is just represented as an empty choice node. nodes.emplace_back(true); } else if(child.Token(0) == "branch") { // Don't merge "branch" nodes with any other nodes. nodes.emplace_back(); nodes.back().canMergeOnto = false; nodes.back().conditions.Load(child); // A branch should always specify what node to go to if the test is // true, and may also specify where to go if it is false. for(int i = 1; i <= 2; ++i) { // If no link is provided, just go to the next node. nodes.back().data.emplace_back("", nodes.size()); if(child.Size() > i) { int index = TokenIndex(child.Token(i)); if(!index) Goto(child.Token(i), nodes.size() - 1, i - 1); else if(index < 0) nodes.back().data.back().second = index; } } } else if(child.Token(0) == "apply") { // Don't merge "apply" nodes with any other nodes. AddNode(); nodes.back().canMergeOnto = false; nodes.back().conditions.Load(child); } else { // This is just an ordinary text node. // If the previous node is a choice, or if the previous node ended // in a goto, create a new node. Otherwise, just merge this new // paragraph into the previous node. if(nodes.empty() || !nodes.back().canMergeOnto) AddNode(); // Always append a newline to the end of the text. nodes.back().data.back().first += child.Token(0); nodes.back().data.back().first += '\n'; // Check whether there is a goto attached to this block of text. If // so, future nodes can't merge onto this one. if(LoadGotos(child)) nodes.back().canMergeOnto = false; } } // Display a warning if a label was not resolved. if(!unresolved.empty()) for(const auto &it : unresolved) node.PrintTrace("Conversation contains unrecognized label \"" + it.first + "\":"); // Check for any loops in the conversation. for(const auto &it : labels) { int nodeIndex = it.second; while(nodeIndex >= 0 && Choices(nodeIndex) <= 1) { nodeIndex = NextNode(nodeIndex); if(nodeIndex == it.second) { node.PrintTrace("Conversation contains infinite loop beginning with label \"" + it.first + "\":"); nodes.clear(); return; } } } // Free the working buffers that we no longer need. labels.clear(); unresolved.clear(); }
void Ship::Load(const DataNode &node) { assert(node.Size() >= 2 && node.Token(0) == "ship"); modelName = node.Token(1); if(node.Size() >= 3) base = GameData::Ships().Get(modelName); government = GameData::PlayerGovernment(); equipped.clear(); // Note: I do not clear the attributes list here so that it is permissible // to override one ship definition with another. bool hasEngine = false; bool hasArmament = false; bool hasLicenses = false; bool hasBays = false; bool hasExplode = false; bool hasOutfits = false; bool hasDescription = false; for(const DataNode &child : node) { if(child.Token(0) == "sprite") sprite.Load(child); else if(child.Token(0) == "name" && child.Size() >= 2) name = child.Token(1); else if(child.Token(0) == "attributes") baseAttributes.Load(child); else if(child.Token(0) == "engine" && child.Size() >= 3) { if(!hasEngine) { enginePoints.clear(); hasEngine = true; } enginePoints.emplace_back(child.Value(1), child.Value(2)); } else if(child.Token(0) == "gun" || child.Token(0) == "turret") { if(!hasArmament) { armament = Armament(); hasArmament = true; } const Outfit *outfit = nullptr; Point hardpoint; if(child.Size() >= 3) { hardpoint = Point(child.Value(1), child.Value(2)); if(child.Size() >= 4) outfit = GameData::Outfits().Get(child.Token(3)); } else { if(child.Size() >= 2) outfit = GameData::Outfits().Get(child.Token(1)); } if(outfit) ++equipped[outfit]; if(child.Token(0) == "gun") armament.AddGunPort(hardpoint, outfit); else armament.AddTurret(hardpoint, outfit); } else if(child.Token(0) == "licenses") { if(!hasLicenses) { licenses.clear(); hasLicenses = true; } for(const DataNode &grand : child) licenses.push_back(grand.Token(0)); } else if(child.Token(0) == "never disabled") neverDisabled = true; else if((child.Token(0) == "fighter" || child.Token(0) == "drone") && child.Size() >= 3) { if(!hasBays) { fighterBays.clear(); droneBays.clear(); hasBays = true; } vector<Bay> &bays = (child.Token(0) == "fighter" ? fighterBays : droneBays); bays.emplace_back(child.Value(1), child.Value(2)); } else if(child.Token(0) == "explode" && child.Size() >= 2) { if(!hasExplode) { explosionEffects.clear(); explosionTotal = 0; hasExplode = true; } int count = (child.Size() >= 3) ? child.Value(2) : 1; explosionEffects[GameData::Effects().Get(child.Token(1))] += count; explosionTotal += count; } else if(child.Token(0) == "outfits") { if(!hasOutfits) { outfits.clear(); hasOutfits = true; } for(const DataNode &grand : child) { int count = (grand.Size() >= 2) ? grand.Value(1) : 1; outfits[GameData::Outfits().Get(grand.Token(0))] += count; } } else if(child.Token(0) == "cargo") cargo.Load(child); else if(child.Token(0) == "crew" && child.Size() >= 2) crew = static_cast<int>(child.Value(1)); else if(child.Token(0) == "fuel" && child.Size() >= 2) fuel = child.Value(1); else if(child.Token(0) == "shields" && child.Size() >= 2) shields = child.Value(1); else if(child.Token(0) == "hull" && child.Size() >= 2) hull = child.Value(1); else if(child.Token(0) == "position" && child.Size() >= 3) position = Point(child.Value(1), child.Value(2)); else if(child.Token(0) == "system" && child.Size() >= 2) currentSystem = GameData::Systems().Get(child.Token(1)); else if(child.Token(0) == "planet" && child.Size() >= 2) { zoom = 0.; landingPlanet = GameData::Planets().Get(child.Token(1)); } else if(child.Token(0) == "parked") isParked = true; else if(child.Token(0) == "description" && child.Size() >= 2) { if(!hasDescription) { description.clear(); hasDescription = true; } description += child.Token(1); description += '\n'; } } }
// Load a mission, either from the game data or from a saved game. void Mission::Load(const DataNode &node) { if(node.Size() >= 2) name = node.Token(1); else name = "Unnamed Mission"; for(const DataNode &child : node) { if(child.Token(0) == "name" && child.Size() >= 2) displayName = child.Token(1); else if(child.Token(0) == "description" && child.Size() >= 2) description = child.Token(1); else if(child.Token(0) == "blocked" && child.Size() >= 2) blocked = child.Token(1); else if(child.Token(0) == "deadline" && child.Size() >= 4) { hasDeadline = true; deadline = Date(child.Value(1), child.Value(2), child.Value(3)); } else if(child.Token(0) == "deadline" && child.Size() >= 2) daysToDeadline = child.Value(1); else if(child.Token(0) == "deadline") doDefaultDeadline = true; else if(child.Token(0) == "cargo" && child.Size() >= 3) { cargo = child.Token(1); cargoSize = child.Value(2); if(child.Size() >= 4) cargoLimit = child.Value(3); if(child.Size() >= 5) cargoProb = child.Value(4); for(const DataNode &grand : child) if(grand.Token(0) == "illegal" && grand.Size() >= 2) illegalCargoFine = grand.Value(1); } else if(child.Token(0) == "passengers" && child.Size() >= 2) { passengers = child.Value(1); if(child.Size() >= 3) passengerLimit = child.Value(2); if(child.Size() >= 4) passengerProb = child.Value(3); } else if(child.Token(0) == "invisible") isVisible = false; else if(child.Token(0) == "priority") hasPriority = true; else if(child.Token(0) == "minor") isMinor = true; else if(child.Token(0) == "autosave") autosave = true; else if(child.Token(0) == "job") location = JOB; else if(child.Token(0) == "landing") location = LANDING; else if(child.Token(0) == "assisting") location = ASSISTING; else if(child.Token(0) == "boarding") location = BOARDING; else if(child.Token(0) == "repeat") repeat = (child.Size() == 1 ? 0 : static_cast<int>(child.Value(1))); else if(child.Token(0) == "clearance") { clearance = (child.Size() == 1 ? "auto" : child.Token(1)); clearanceFilter.Load(child); } else if(child.Token(0) == "infiltrating") hasFullClearance = false; else if(child.Token(0) == "to" && child.Size() >= 2) { if(child.Token(1) == "offer") toOffer.Load(child); else if(child.Token(1) == "complete") toComplete.Load(child); else if(child.Token(1) == "fail") toFail.Load(child); } else if(child.Token(0) == "source" && child.Size() >= 2) source = GameData::Planets().Get(child.Token(1)); else if(child.Token(0) == "source") sourceFilter.Load(child); else if(child.Token(0) == "destination" && child.Size() == 2) destination = GameData::Planets().Get(child.Token(1)); else if(child.Token(0) == "destination") destinationFilter.Load(child); else if(child.Token(0) == "waypoint" && child.Size() >= 2) waypoints.insert(GameData::Systems().Get(child.Token(1))); else if(child.Token(0) == "npc") { npcs.push_back(NPC()); npcs.back().Load(child); } else if(child.Token(0) == "on" && child.Size() >= 3 && child.Token(1) == "enter") { MissionAction &action = onEnter[GameData::Systems().Get(child.Token(2))]; action.Load(child, name); } else if(child.Token(0) == "on" && child.Size() >= 2) { static const map<string, Trigger> trigger = { {"complete", COMPLETE}, {"offer", OFFER}, {"accept", ACCEPT}, {"decline", DECLINE}, {"fail", FAIL}, {"visit", VISIT}}; auto it = trigger.find(child.Token(1)); if(it != trigger.end()) actions[it->second].Load(child, name); } } if(displayName.empty()) displayName = name; }
// Load a planet's description from a file. void Planet::Load(const DataNode &node) { if(node.Size() < 2) return; name = node.Token(1); // If this planet has been loaded before, these sets of items should be // reset instead of appending to them: set<string> shouldOverwrite = {"attributes", "description", "spaceport"}; 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); // "<key> clear" is the deprecated way of writing "remove <key>." removeAll |= (!add && !remove && hasValue && value == "clear"); // 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 && !removeAll && shouldOverwrite.count(key)); // Clear the data of the given type. if(removeAll || overwriteAll) { // Clear the data of the given type. if(key == "music") music.clear(); else if(key == "attributes") attributes.clear(); else if(key == "description") description.clear(); else if(key == "spaceport") spaceport.clear(); else if(key == "shipyard") shipSales.clear(); else if(key == "outfitter") outfitSales.clear(); else if(key == "government") government = nullptr; else if(key == "required reputation") requiredReputation = 0.; else if(key == "bribe") bribe = 0.; else if(key == "security") security = 0.; else if(key == "tribute") tribute = 0; // If not in "overwrite" mode, move on to the next node. if(overwriteAll) shouldOverwrite.erase(key); else continue; } // Handle the attributes which can be "removed." if(!hasValue) { 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 == "shipyard") { if(remove) shipSales.erase(GameData::Shipyards().Get(value)); else shipSales.insert(GameData::Shipyards().Get(value)); } else if(key == "outfitter") { if(remove) outfitSales.erase(GameData::Outfitters().Get(value)); else outfitSales.insert(GameData::Outfitters().Get(value)); } // 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 == "landscape") landscape = SpriteSet::Get(value); else if(key == "music") music = value; else if(key == "description" || key == "spaceport") { string &text = (key == "description") ? description : spaceport; if(!text.empty() && !value.empty() && value[0] > ' ') text += '\t'; text += value; text += '\n'; } else if(key == "government") government = GameData::Governments().Get(value); else if(key == "required reputation") requiredReputation = child.Value(valueIndex); else if(key == "bribe") bribe = child.Value(valueIndex); else if(key == "security") security = child.Value(valueIndex); else if(key == "tribute") { tribute = child.Value(valueIndex); bool resetFleets = !defenseFleets.empty(); for(const DataNode &grand : child) { if(grand.Token(0) == "threshold" && grand.Size() >= 2) defenseThreshold = grand.Value(1); else if(grand.Token(0) == "fleet") { if(grand.Size() >= 2 && !grand.HasChildren()) { // Allow only one "tribute" node to define the tribute fleets. if(resetFleets) { defenseFleets.clear(); resetFleets = false; } defenseFleets.insert(defenseFleets.end(), grand.Size() >= 3 ? grand.Value(2) : 1, GameData::Fleets().Get(grand.Token(1)) ); } else grand.PrintTrace("Skipping unsupported tribute fleet definition:"); } else grand.PrintTrace("Skipping unrecognized tribute attribute:"); } } else child.PrintTrace("Skipping unrecognized attribute:"); } static const vector<string> AUTO_ATTRIBUTES = {"spaceport", "shipyard", "outfitter"}; bool autoValues[3] = {!spaceport.empty(), !shipSales.empty(), !outfitSales.empty()}; for(unsigned i = 0; i < AUTO_ATTRIBUTES.size(); ++i) { if(autoValues[i]) attributes.insert(AUTO_ATTRIBUTES[i]); else attributes.erase(AUTO_ATTRIBUTES[i]); } // Precalculate commonly used values that can only change due to Load(). inhabited = (HasSpaceport() || requiredReputation || !defenseFleets.empty()) && !attributes.count("uninhabited"); SetRequiredAttributes(Attributes(), requiredAttributes); }
// Load a set of conditions from the children of this node. void ConditionSet::Load(const DataNode &node) { isOr = (node.Token(0) == "or"); for(const DataNode &child : node) Add(child); }
// Apply the given change to the universe. void GameData::Change(const DataNode &node) { if(node.Token(0) == "fleet" && node.Size() >= 2) fleets.Get(node.Token(1))->Load(node); else if(node.Token(0) == "government" && node.Size() >= 2) governments.Get(node.Token(1))->Load(node); else if(node.Token(0) == "outfitter" && node.Size() >= 2) outfitSales.Get(node.Token(1))->Load(node, outfits); else if(node.Token(0) == "planet" && node.Size() >= 2) planets.Get(node.Token(1))->Load(node, shipSales, outfitSales); else if(node.Token(0) == "shipyard" && node.Size() >= 2) shipSales.Get(node.Token(1))->Load(node, ships); else if(node.Token(0) == "system" && node.Size() >= 2) systems.Get(node.Token(1))->Load(node, planets); else if(node.Token(0) == "link" && node.Size() >= 3) systems.Get(node.Token(1))->Link(systems.Get(node.Token(2))); else if(node.Token(0) == "unlink" && node.Size() >= 3) systems.Get(node.Token(1))->Unlink(systems.Get(node.Token(2))); }
void Conversation::Load(const DataNode &node) { if(node.Token(0) != "conversation") return; if(node.Size() >= 2) identifier = node.Token(1); // Free any previously loaded data. nodes.clear(); for(const DataNode &child : node) { if(child.Token(0) == "scene" && child.Size() >= 2) { nodes.emplace_back(); int next = nodes.size(); nodes.back().data.emplace_back("", next); nodes.back().scene = SpriteSet::Get(child.Token(1)); nodes.back().sceneName = child.Token(1); } else if(child.Token(0) == "label" && child.Size() >= 2) { // You cannot merge text above a label with text below it. if(!nodes.empty()) nodes.back().canMergeOnto = false; AddLabel(child.Token(1), child); } else if(child.Token(0) == "choice") { // Create a new node with one or more choices in it. nodes.emplace_back(true); for(const DataNode &grand : child) { // Store the text of this choice. By default, the choice will // just bring you to the next node in the script. nodes.back().data.emplace_back(grand.Token(0), nodes.size()); nodes.back().data.back().first += '\n'; // If this choice contains a goto, record it. for(const DataNode &great : grand) { int index = TokenIndex(great.Token(0)); if(!index && great.Size() >= 2) Goto(great.Token(1), nodes.size() - 1, nodes.back().data.size() - 1); else if(index < 0) nodes.back().data.back().second = index; else continue; break; } } if(nodes.back().data.empty()) { child.PrintTrace("Conversation contains an empty \"choice\" node:"); nodes.pop_back(); } } else if(child.Token(0) == "name") nodes.emplace_back(true); else if(child.Token(0) == "branch") { nodes.emplace_back(); nodes.back().canMergeOnto = false; nodes.back().conditions.Load(child); for(int i = 1; i <= 2; ++i) { // If no link is provided, just go to the next node. nodes.back().data.emplace_back("", nodes.size()); if(child.Size() > i) { int index = TokenIndex(child.Token(i)); if(!index) Goto(child.Token(i), nodes.size() - 1, i - 1); else if(index < 0) nodes.back().data.back().second = index; } } } else if(child.Token(0) == "apply") { nodes.emplace_back(); nodes.back().canMergeOnto = false; nodes.back().conditions.Load(child); nodes.back().data.emplace_back("", nodes.size()); if(child.Size() > 1) { int index = TokenIndex(child.Token(1)); if(!index) Goto(child.Token(1), nodes.size() - 1, 0); else if(index < 0) nodes.back().data.back().second = index; } } else { // This is just an ordinary text node. // If the previous node is a choice, or if the previous node ended // in a goto, create a new node. Otherwise, just merge this new // paragraph into the previous node. if(nodes.empty() || !nodes.back().canMergeOnto) { nodes.emplace_back(); int next = nodes.size(); nodes.back().data.emplace_back("", next); } nodes.back().data.back().first += child.Token(0); nodes.back().data.back().first += '\n'; // Check if this node contains a "goto". for(const DataNode &grand : child) { int index = TokenIndex(grand.Token(0)); if(!index && grand.Size() >= 2) Goto(grand.Token(1), nodes.size() - 1); else if(index < 0) nodes.back().data.back().second = index; else continue; nodes.back().canMergeOnto = false; break; } } } // Display a warning if a label was not resolved. if(!unresolved.empty()) for(const auto &it : unresolved) node.PrintTrace("Conversation contains unused label \"" + it.first + "\":"); // Check for any loops in the conversation. for(const auto &it : labels) { int nodeIndex = it.second; while(nodeIndex >= 0 && Choices(nodeIndex) <= 1) { nodeIndex = NextNode(nodeIndex); if(nodeIndex == it.second) { node.PrintTrace("Conversation contains infinite loop beginning with label \"" + it.first + "\":"); nodes.clear(); return; } } } // Free the working buffers that we no longer need. labels.clear(); unresolved.clear(); }
// 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:"); } }
// Apply the given change to the universe. void GameData::Change(const DataNode &node) { if(node.Token(0) == "fleet" && node.Size() >= 2) fleets.Get(node.Token(1))->Load(node); else if(node.Token(0) == "galaxy" && node.Size() >= 2) galaxies.Get(node.Token(1))->Load(node); else if(node.Token(0) == "government" && node.Size() >= 2) governments.Get(node.Token(1))->Load(node); else if(node.Token(0) == "outfitter" && node.Size() >= 2) outfitSales.Get(node.Token(1))->Load(node, outfits); else if(node.Token(0) == "planet" && node.Size() >= 2) planets.Get(node.Token(1))->Load(node, shipSales, outfitSales); else if(node.Token(0) == "shipyard" && node.Size() >= 2) shipSales.Get(node.Token(1))->Load(node, ships); else if(node.Token(0) == "system" && node.Size() >= 2) systems.Get(node.Token(1))->Load(node, planets); else if(node.Token(0) == "news" && node.Size() >= 2) news.Get(node.Token(1))->Load(node); else if(node.Token(0) == "link" && node.Size() >= 3) systems.Get(node.Token(1))->Link(systems.Get(node.Token(2))); else if(node.Token(0) == "unlink" && node.Size() >= 3) systems.Get(node.Token(1))->Unlink(systems.Get(node.Token(2))); else node.PrintTrace("Invalid \"event\" data:"); }
// 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; } } }