Пример #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:");
}
Пример #2
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:");
}
Пример #3
0
// Add a label, pointing to whatever node is created next.
void Conversation::AddLabel(const string &label, const DataNode &node)
{
	if(labels.find(label) != labels.end())
	{
		node.PrintTrace("Conversation: label \"" + label + "\" is used more than once:");
		return;
	}
	
	// If there are any unresolved references to this label, we can now set
	// their indices correctly.
	auto range = unresolved.equal_range(label);
	
	for(auto it = range.first; it != range.second; ++it)
		nodes[it->second.first].data[it->second.second].second = nodes.size();
	
	unresolved.erase(range.first, range.second);
	
	// Remember what index this label points to.
	labels[label] = nodes.size();
}
Пример #4
0
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();
}
Пример #5
0
// 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();
}