The solution to a non-linear story tracking (may be used for linear as well) is a Node Graph. You already sort of have one, only scripted in a workaround way. If scripted literally, it may also solve an issue of having to script story switches "in place".
Following is a very rough outline, but I hope it should illustrate the idea. I apologize for a messy reply beforehand, but I fear to not have enough spare time to write all this thoroughly.
A Node Graph consists of Nodes. A Node is (in pseudocode):
Code: ags
You may store these nodes any way you prefer, or any way the current scripting/programming language allows. It may be one huge array, or separate Node objects linked to others using pointers (currently impossible in AGS, sadly).
This kind of structure makes it trivial to setup a complex story. You no longer have to make huge "if/else if" lists when deciding what stage to switch to. Instead, you configure the node list at the start of the game (or when loading some data), where you create and link nodes.
Code: ags
And then have a single function, let's say "CompleteNode", that marks particular node as completed and advance to others, found by this node links.
This is where we meet a question of how to track active nodes. The most dumb straightforward way is to make each Node have "Completed" and "Active" parameters. In which case the progression may look like:
Code: ags
This approach is very simple, but it has one issue: we don't have a easily accessed list of active nodes.
(Actually, there's another: a node graph describes a "current position" in itself, which is conceptually wrong in my opinion, but in practice this does not matter so long as there's only one protagonist that may traverse a graph.)
How this may be solved?
Firstly, you may have a literal array of active nodes, which you update in "CompleteNode".
Secondly, following your current idea of having separate "Stories", a Node may have a Story Branch reference (a "Story branch" it belongs to). If it does, then you may have some kind of a Story Branch object, pointing to the active Node:
Code: ags
Then, when testing for the current story stage, you could do something like:
Code: ags
Another approach to tracking active nodes is Node Cursors. Node Cursor (or Story Cursor) is a struct that references current node, and then advanced by "node completed" command, along the links. The problem here is to invent a solution of having multiple cursors restricted to subbranches. So I won't go there now, as above Story references may already suffice (actually, it may be very similar thing).
The biggest advantage of a node graph, in my opinion, is that you don't have to keep the whole story in mind when scripting a reaction to some interaction. Say, if there's an object, and interacting with it advanced a story, all you have to do is to create a new Node for this object and connect it to the story.
Code: ags
Then, when an object is interacted, you simply do "CompleteNode( my_node )", and the whole system will be updated accordingly.
Following is a very rough outline, but I hope it should illustrate the idea. I apologize for a messy reply beforehand, but I fear to not have enough spare time to write all this thoroughly.
A Node Graph consists of Nodes. A Node is (in pseudocode):
// pseudo code!!!
struct Node {
ID - some way to identify it when calling a function, a number, a string, and so on.
Name - a human-readable node name, useful for debugging.
Description - game specific parameters, as become necessary.
Links - list of connected node IDs, or whatever allows to reference them.
There may be separate arrays of Forward links and Backward links too, if you need to let a node know which prerequisite nodes connect to it.
};
You may store these nodes any way you prefer, or any way the current scripting/programming language allows. It may be one huge array, or separate Node objects linked to others using pointers (currently impossible in AGS, sadly).
This kind of structure makes it trivial to setup a complex story. You no longer have to make huge "if/else if" lists when deciding what stage to switch to. Instead, you configure the node list at the start of the game (or when loading some data), where you create and link nodes.
// pseudo code!!!
NodeGraph.CreateNode("John Story 1");
NodeGraph.CreateNode("John Story 2");
NodeGraph.LinkNodes("John Story 1", "John Story 2");
And then have a single function, let's say "CompleteNode", that marks particular node as completed and advance to others, found by this node links.
This is where we meet a question of how to track active nodes. The most dumb straightforward way is to make each Node have "Completed" and "Active" parameters. In which case the progression may look like:
// pseudo code!!!
function CompleteNode( Node node ) {
node.Active = false;
node.Completed = true;
// Check and update each *following* node
for each fwnode in node.ForwardLinks {
bool must_activate = true;
// For each of the forward node, check if all the preceding nodes are completed
for each bwnode in fwnode.BackwardLinks
if (!bwnode.Completed) {
must_activate = false;
}
}
// If all is completed, then activate this forward node
if (must_activate) {
fwnode.Active = true;
}
}
}
This approach is very simple, but it has one issue: we don't have a easily accessed list of active nodes.
(Actually, there's another: a node graph describes a "current position" in itself, which is conceptually wrong in my opinion, but in practice this does not matter so long as there's only one protagonist that may traverse a graph.)
How this may be solved?
Firstly, you may have a literal array of active nodes, which you update in "CompleteNode".
Secondly, following your current idea of having separate "Stories", a Node may have a Story Branch reference (a "Story branch" it belongs to). If it does, then you may have some kind of a Story Branch object, pointing to the active Node:
// pseudo code!!!
function CompleteNode( Node node ) {
<............>
// If all is completed, then activate this forward node
if (must_activate) {
fwnode.Active = true;
StoryBranch story = fwnode.Story;
story.CurrentNode = fwnode;
}
}
Then, when testing for the current story stage, you could do something like:
// pseudo code!!!
function GetStoryNodeID( StoryBranch story ) {
return story.CurrentNode.ID;
}
function DoSomethingForJohn() {
int id = GetStoryNodeID(eStory_John);
if (id == eStory_John_Begins) {
// do this
} else if (id == eStory_John_Continues) {
// do that
}
}
Another approach to tracking active nodes is Node Cursors. Node Cursor (or Story Cursor) is a struct that references current node, and then advanced by "node completed" command, along the links. The problem here is to invent a solution of having multiple cursors restricted to subbranches. So I won't go there now, as above Story references may already suffice (actually, it may be very similar thing).
The biggest advantage of a node graph, in my opinion, is that you don't have to keep the whole story in mind when scripting a reaction to some interaction. Say, if there's an object, and interacting with it advanced a story, all you have to do is to create a new Node for this object and connect it to the story.
// pseudo code!!!
NodeGraph.CreateNode("Yellow Door Node");
NodeGraph.LinkNodes("Yellow Door Node", "John Story 10");
Then, when an object is interacted, you simply do "CompleteNode( my_node )", and the whole system will be updated accordingly.