Exemple #1
0
// 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:");
}
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);
	}
}
Exemple #3
0
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);
	}
}
Exemple #4
0
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));
		}
	}
}
Exemple #5
0
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:");
	}
}
Exemple #6
0
// 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));
	}
}
Exemple #7
0
Fleet::Variant::Variant(const DataNode &node)
{
	weight = (node.Size() < 2) ? 1 : static_cast<int>(node.Value(1));
	
	for(const DataNode &child : node)
	{
		int n = 1;
		if(child.Size() > 1 && child.Value(1) >= 1.)
			n = static_cast<int>(child.Value(1));
		ships.insert(ships.end(), n, GameData::Ships().Get(child.Token(0)));
	}
}
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:");
}
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);
    }
}
Exemple #10
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 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 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));
		}
	}
}
Exemple #13
0
// 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)));
}
// 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);
    }
}
Exemple #15
0
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:");
	}
}
Exemple #16
0
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);
	}
}
Exemple #17
0
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);
	}
}
Exemple #18
0
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:");
	}
}
Exemple #19
0
// 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;
        }
    }
}
Exemple #20
0
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';
		}
	}
}
Exemple #21
0
// 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);
}
Exemple #22
0
// 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;
}
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();
}
Exemple #24
0
// 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:");
	}
}
Exemple #25
0
// 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;
		}
	}
}