void GameData::WriteEconomy(DataWriter &out) { out.Write("economy"); out.BeginChild(); { if(!purchases.empty()) { out.Write("purchases"); out.BeginChild(); for(const auto &pit : purchases) for(const auto &cit : pit.second) out.Write(pit.first->Name(), cit.first, cit.second); out.EndChild(); } out.WriteToken("system"); for(const auto &cit : GameData::Commodities()) out.WriteToken(cit.name); out.Write(); for(const auto &sit : GameData::Systems()) { out.WriteToken(sit.second.Name()); for(const auto &cit : GameData::Commodities()) out.WriteToken(static_cast<int>(sit.second.Supply(cit.name))); out.Write(); } } out.EndChild(); }
// Save depreciation records. void Depreciation::Save(DataWriter &out, int day) const { out.Write(NAME[isStock]); out.BeginChild(); { for(const auto &sit : ships) { out.Write("ship", sit.first->ModelName()); out.BeginChild(); { // If this is a planet's stock, remember how many outfits in // stock are fully depreciated. If it's the player's stock, // anything not recorded is considered fully depreciated, so // there is no reason to save records for those items. for(const auto &it : sit.second) if(isStock || (it.second && it.first > day - MAX_AGE)) out.Write(it.first, it.second); } out.EndChild(); } for(const auto &oit : outfits) { out.Write("outfit", oit.first->Name()); out.BeginChild(); { for(const auto &it : oit.second) if(isStock || (it.second && it.first > day - MAX_AGE)) out.Write(it.first, it.second); } out.EndChild(); } } out.EndChild(); }
void Personality::Save(DataWriter &out) const { out.Write("personality"); out.BeginChild(); { out.Write("confusion", confusionMultiplier); for(const auto &it : TOKEN) if(flags & it.second) out.Write(it.first); } out.EndChild(); }
void Conversation::Save(DataWriter &out) const { if(!identifier.empty()) out.Write("conversation", identifier); else out.Write("conversation"); out.BeginChild(); { for(unsigned i = 0; i < nodes.size(); ++i) { out.Write("label", i); const Node &node = nodes[i]; if(node.scene) out.Write("scene", node.sceneName); if(!node.conditions.IsEmpty()) { if(node.data.size() > 1) out.Write("branch", TokenName(node.data[0].second), TokenName(node.data[1].second)); else out.Write("apply", TokenName(node.data[0].second)); out.BeginChild(); { node.conditions.Save(out); } out.EndChild(); continue; } if(node.isChoice) { out.Write(node.data.empty() ? "name" : "choice"); out.BeginChild(); } for(const auto &it : node.data) { // Break the text up into paragraphs. size_t begin = 0; while(begin != it.first.length()) { size_t pos = it.first.find('\n', begin); if(pos == string::npos) pos = it.first.length(); out.Write(it.first.substr(begin, pos - begin)); if(pos == it.first.length()) break; begin = pos + 1; } int index = it.second; if(index > 0 && static_cast<unsigned>(index) >= nodes.size()) index = -1; WriteToken(index, out); } if(node.isChoice) out.EndChild(); } } out.EndChild(); }
void ConditionSet::Save(DataWriter &out) const { for(const Entry &entry : entries) out.Write(entry.name, entry.op, entry.value); for(const ConditionSet &child : children) { out.Write(child.isOr ? "or" : "and"); out.BeginChild(); { child.Save(out); } out.EndChild(); } }
void Galaxy::Save(DataWriter &file) const { if(name.isEmpty()) file.Write("galaxy"); else file.Write("galaxy", name); file.BeginChild(); { file.Write("pos", position.x(), position.y()); file.Write("sprite", sprite); for(const DataNode &node : unparsed) file.Write(node); } file.EndChild(); }
void GameEvent::Save(DataWriter &out) const { out.Write("event"); out.BeginChild(); { if(date) out.Write("date", date.Day(), date.Month(), date.Year()); conditionsToApply.Save(out); for(const System *system : systemsToUnvisit) if(system && !system->Name().empty()) out.Write("unvisit", system->Name()); for(const Planet *planet : planetsToUnvisit) if(planet && !planet->Name().empty()) out.Write("unvisit planet", planet->Name()); for(const System *system : systemsToVisit) if(system && !system->Name().empty()) out.Write("visit", system->Name()); for(const Planet *planet : planetsToVisit) if(planet && !planet->Name().empty()) out.Write("visit planet", planet->Name()); for(const DataNode &change : changes) out.Write(change); } out.EndChild(); }
void LOADERDECL Pos_ReadDirect() { static_assert(N <= 3, "N > 3 is not sane!"); auto const scale = posScale; DataWriter dst; DataReader src; for (int i = 0; i < 3; ++i) dst.Write(i<N ? PosScale(src.Read<T>(), scale) : 0.f); LOG_VTX(); }
void LOADERDECL TexCoord_ReadDirect() { auto const scale = tcScale[tcIndex]; DataWriter dst; DataReader src; for (int i = 0; i != N; ++i) dst.Write(TCScale(src.Read<T>(), scale)); LOG_TEX<N>(); ++tcIndex; }
void LOADERDECL Pos_ReadIndex() { static_assert(!std::numeric_limits<I>::is_signed, "Only unsigned I is sane!"); static_assert(N <= 3, "N > 3 is not sane!"); auto const index = DataRead<I>(); auto const data = reinterpret_cast<const T*>(cached_arraybases[ARRAY_POSITION] + (index * arraystrides[ARRAY_POSITION])); auto const scale = posScale; DataWriter dst; for (int i = 0; i < 3; ++i) dst.Write(i<N ? PosScale(Common::FromBigEndian(data[i]), scale) : 0.f); LOG_VTX(); }
void LOADERDECL TexCoord_ReadIndex() { static_assert(!std::numeric_limits<I>::is_signed, "Only unsigned I is sane!"); auto const index = DataRead<I>(); auto const data = reinterpret_cast<const T*>(cached_arraybases[ARRAY_TEXCOORD0 + tcIndex] + (index * g_main_cp_state.array_strides[ARRAY_TEXCOORD0 + tcIndex])); auto const scale = tcScale[tcIndex]; DataWriter dst; for (int i = 0; i != N; ++i) dst.Write(TCScale(Common::FromBigEndian(data[i]), scale)); LOG_TEX<N>(); ++tcIndex; }
// Note: the Save() function can assume this is an instantiated mission, not // a template, so it only has to save a subset of the data. void MissionAction::Save(DataWriter &out) const { if(system.empty()) out.Write("on", trigger); else out.Write("on", trigger, system); out.BeginChild(); { if(!dialogText.empty()) { out.Write("dialog"); out.BeginChild(); { // Break the text up into paragraphs. size_t begin = 0; while(true) { size_t pos = dialogText.find("\n\t", begin); if(pos == string::npos) pos = dialogText.length(); out.Write(dialogText.substr(begin, pos - begin)); if(pos == dialogText.length()) break; begin = pos + 2; } } out.EndChild(); } if(!conversation.IsEmpty()) conversation.Save(out); for(const auto &it : gifts) out.Write("outfit", it.first->Name(), it.second); if(payment) out.Write("payment", payment); for(const auto &it : events) out.Write("event", it.first, it.second); for(const auto &name : fail) out.Write("fail", name); conditions.Save(out); } out.EndChild(); }
// Save the cargo manifest to a file. void CargoHold::Save(DataWriter &out) const { bool first = true; for(const auto &it : commodities) if(it.second) { if(first) { out.Write("cargo"); out.BeginChild(); out.Write("commodities"); out.BeginChild(); } first = false; out.Write(it.first, it.second); } if(!first) out.EndChild(); bool firstOutfit = true; for(const auto &it : outfits) if(it.second && !it.first->Name().empty()) { // It is possible this cargo hold contained no commodities, meaning // we must print the opening tag now. if(first) { out.Write("cargo"); out.BeginChild(); } first = false; // If this is the first outfit to be written, print the opening tag. if(firstOutfit) { out.Write("outfits"); out.BeginChild(); } firstOutfit = false; out.Write(it.first->Name(), it.second); } if(!firstOutfit) out.EndChild(); if(!first) out.EndChild(); // Mission cargo is not saved because it is repopulated when the missions // are read rather than when the cargo is read. }
// Write account information to a saved game file. void Account::Save(DataWriter &out) const { out.Write("account"); out.BeginChild(); { out.Write("credits", credits); if(salariesOwed) out.Write("salaries", salariesOwed); out.Write("score", creditScore); out.Write("history"); out.BeginChild(); { for(int64_t worth : history) out.Write(worth); } out.EndChild(); for(const Mortgage &mortgage : mortgages) mortgage.Save(out); } out.EndChild(); }
// Save a mission. It is safe to assume that any mission that is being saved // is already "instantiated," so only a subset of the data must be saved. void Mission::Save(DataWriter &out, const string &tag) const { out.Write(tag, name); out.BeginChild(); { out.Write("name", displayName); if(!description.empty()) out.Write("description", description); if(!blocked.empty()) out.Write("blocked", blocked); if(hasDeadline) out.Write("deadline", deadline.Day(), deadline.Month(), deadline.Year()); if(cargoSize) { out.Write("cargo", cargo, cargoSize); if(illegalCargoFine) { out.BeginChild(); { out.Write("illegal", illegalCargoFine); } out.EndChild(); } } if(passengers) out.Write("passengers", passengers); if(!isVisible) out.Write("invisible"); if(hasPriority) out.Write("priority"); if(isMinor) out.Write("minor"); if(autosave) out.Write("autosave"); if(location == LANDING) out.Write("landing"); if(location == ASSISTING) out.Write("assisting"); if(location == BOARDING) out.Write("boarding"); if(location == JOB) out.Write("job"); if(!clearance.empty()) { out.Write("clearance", clearance); clearanceFilter.Save(out); } if(!hasFullClearance) out.Write("infiltrating"); if(repeat != 1) out.Write("repeat", repeat); if(!toComplete.IsEmpty()) { out.Write("to", "complete"); out.BeginChild(); { toComplete.Save(out); } out.EndChild(); } if(!toFail.IsEmpty()) { out.Write("to", "fail"); out.BeginChild(); { toFail.Save(out); } out.EndChild(); } if(destination) out.Write("destination", destination->Name()); for(const System *system : waypoints) out.Write("waypoint", system->Name()); for(const NPC &npc : npcs) npc.Save(out); // Save all the actions, because this might be an "available mission" that // has not been received yet but must still be included in the saved game. for(const auto &it : actions) it.second.Save(out); for(const auto &it : onEnter) it.second.Save(out); } out.EndChild(); }
void Planet::Save(DataWriter &file) const { file.Write("planet", name); file.BeginChild(); { if(!attributes.empty()) { file.WriteToken("attributes"); for(const QString &it : attributes) file.WriteToken(it); file.Write(); } file.Write("landscape", landscape); // Break the descriptions into paragraphs. for(const QString &str : description.split('\n', QString::SkipEmptyParts)) { file.WriteToken("description"); file.WriteToken(str, '`'); file.Write(); } for(const QString &str : spaceport.split('\n', QString::SkipEmptyParts)) { file.WriteToken("spaceport"); file.WriteToken(str, '`'); file.Write(); } for(const QString &it : shipyard) file.Write("shipyard", it); for(const QString &it : outfitter) file.Write("outfitter", it); if(!std::isnan(requiredReputation)) file.Write("required reputation", requiredReputation); if(!std::isnan(bribe)) file.Write("bribe", bribe); if(!std::isnan(security)) file.Write("security", security); for(const DataNode &node : unparsed) file.Write(node); } file.EndChild(); }
// Save a full description of this ship, as currently configured. void Ship::Save(DataWriter &out) const { out.Write("ship", modelName); out.BeginChild(); { out.Write("name", name); sprite.Save(out); if(neverDisabled) out.Write("never disabled"); out.Write("attributes"); out.BeginChild(); { out.Write("category", baseAttributes.Category()); for(const auto &it : baseAttributes.Attributes()) if(it.second) out.Write(it.first, it.second); } out.EndChild(); out.Write("outfits"); out.BeginChild(); { for(const auto &it : outfits) if(it.first && it.second) { if(it.second == 1) out.Write(it.first->Name()); else out.Write(it.first->Name(), it.second); } } out.EndChild(); cargo.Save(out); out.Write("crew", crew); out.Write("fuel", fuel); out.Write("shields", shields); out.Write("hull", hull); out.Write("position", position.X(), position.Y()); for(const Point &point : enginePoints) out.Write("engine", point.X(), point.Y()); for(const Armament::Weapon &weapon : armament.Get()) { const char *type = (weapon.IsTurret() ? "turret" : "gun"); if(weapon.GetOutfit()) out.Write(type, 2. * weapon.GetPoint().X(), 2. * weapon.GetPoint().Y(), weapon.GetOutfit()->Name()); else out.Write(type, 2. * weapon.GetPoint().X(), 2. * weapon.GetPoint().Y()); } for(const Bay &bay : fighterBays) out.Write("fighter", 2. * bay.point.X(), 2. * bay.point.Y()); for(const Bay &bay : droneBays) out.Write("drone", 2. * bay.point.X(), 2. * bay.point.Y()); for(const auto &it : explosionEffects) if(it.first && it.second) out.Write("explode", it.first->Name(), it.second); if(currentSystem) out.Write("system", currentSystem->Name()); else { shared_ptr<const Ship> parent = GetParent(); if(parent && parent->currentSystem) out.Write("system", parent->currentSystem->Name()); } if(landingPlanet) out.Write("planet", landingPlanet->Name()); if(isParked) out.Write("parked"); } out.EndChild(); }
// Write a conversation to file. void Conversation::Save(DataWriter &out) const { out.Write("conversation"); out.BeginChild(); { for(unsigned i = 0; i < nodes.size(); ++i) { // The original label names are not preserved anywhere. Instead, // the label for every node is just its node index. out.Write("label", i); const Node &node = nodes[i]; if(node.scene) out.Write("scene", node.scene->Name()); if(!node.conditions.IsEmpty()) { // The only thing differentiating a "branch" from an "apply" node // is that a branch has two data entries instead of one. if(node.data.size() > 1) out.Write("branch", TokenName(node.data[0].second), TokenName(node.data[1].second)); else out.Write("apply", TokenName(node.data[0].second)); // Write the condition set as a child of this node. out.BeginChild(); { node.conditions.Save(out); } out.EndChild(); continue; } if(node.isChoice) { out.Write(node.data.empty() ? "name" : "choice"); out.BeginChild(); } for(const auto &it : node.data) { // Break the text up into paragraphs. size_t begin = 0; while(begin < it.first.length()) { // Find the next line break. size_t pos = it.first.find('\n', begin); // Text should always end with a line break, but just in case: if(pos == string::npos) pos = it.first.length(); out.Write(it.first.substr(begin, pos - begin)); // Skip the actual newline character when writing the text out. begin = pos + 1; } // Check what node the conversation goes to after this. int index = it.second; if(index > 0 && static_cast<unsigned>(index) >= nodes.size()) index = Conversation::DECLINE; // Write the node that we go to next after this. WriteToken(index, out); } if(node.isChoice) out.EndChild(); } } out.EndChild(); }