mirror of
https://github.com/Ratstail91/Mementos.git
synced 2025-11-29 18:44:28 +11:00
207 lines
8.2 KiB
C#
207 lines
8.2 KiB
C#
using System.Collections.Generic;
|
|
|
|
namespace Ink.Parsed
|
|
{
|
|
[System.Flags]
|
|
public enum SequenceType
|
|
{
|
|
Stopping = 1, // default
|
|
Cycle = 2,
|
|
Shuffle = 4,
|
|
Once = 8
|
|
}
|
|
|
|
public class Sequence : Parsed.Object
|
|
{
|
|
|
|
public List<Parsed.Object> sequenceElements;
|
|
public SequenceType sequenceType;
|
|
|
|
public Sequence (List<ContentList> elementContentLists, SequenceType sequenceType)
|
|
{
|
|
this.sequenceType = sequenceType;
|
|
this.sequenceElements = new List<Parsed.Object> ();
|
|
|
|
foreach (var elementContentList in elementContentLists) {
|
|
|
|
var contentObjs = elementContentList.content;
|
|
|
|
Parsed.Object seqElObject = null;
|
|
|
|
// Don't attempt to create a weave for the sequence element
|
|
// if the content list is empty. Weaves don't like it!
|
|
if (contentObjs == null || contentObjs.Count == 0)
|
|
seqElObject = elementContentList;
|
|
else
|
|
seqElObject = new Weave (contentObjs);
|
|
|
|
this.sequenceElements.Add (seqElObject);
|
|
AddContent (seqElObject);
|
|
}
|
|
}
|
|
|
|
// Generate runtime code that looks like:
|
|
//
|
|
// chosenIndex = MIN(sequence counter, num elements) e.g. for "Stopping"
|
|
// if chosenIndex == 0, divert to s0
|
|
// if chosenIndex == 1, divert to s1 [etc]
|
|
//
|
|
// - s0:
|
|
// <content for sequence element>
|
|
// divert to no-op
|
|
// - s1:
|
|
// <content for sequence element>
|
|
// divert to no-op
|
|
// - s2:
|
|
// empty branch if using "once"
|
|
// divert to no-op
|
|
//
|
|
// no-op
|
|
//
|
|
public override Runtime.Object GenerateRuntimeObject ()
|
|
{
|
|
var container = new Runtime.Container ();
|
|
container.visitsShouldBeCounted = true;
|
|
container.countingAtStartOnly = true;
|
|
|
|
_sequenceDivertsToResove = new List<SequenceDivertToResolve> ();
|
|
|
|
// Get sequence read count
|
|
container.AddContent (Runtime.ControlCommand.EvalStart ());
|
|
container.AddContent (Runtime.ControlCommand.VisitIndex ());
|
|
|
|
bool once = (sequenceType & SequenceType.Once) > 0;
|
|
bool cycle = (sequenceType & SequenceType.Cycle) > 0;
|
|
bool stopping = (sequenceType & SequenceType.Stopping) > 0;
|
|
bool shuffle = (sequenceType & SequenceType.Shuffle) > 0;
|
|
|
|
var seqBranchCount = sequenceElements.Count;
|
|
if (once) seqBranchCount++;
|
|
|
|
// Chosen sequence index:
|
|
// - Stopping: take the MIN(read count, num elements - 1)
|
|
// - Once: take the MIN(read count, num elements)
|
|
// (the last one being empty)
|
|
if (stopping || once) {
|
|
//var limit = stopping ? seqBranchCount-1 : seqBranchCount;
|
|
container.AddContent (new Runtime.IntValue (seqBranchCount-1));
|
|
container.AddContent (Runtime.NativeFunctionCall.CallWithName ("MIN"));
|
|
}
|
|
|
|
// - Cycle: take (read count % num elements)
|
|
else if (cycle) {
|
|
container.AddContent (new Runtime.IntValue (sequenceElements.Count));
|
|
container.AddContent (Runtime.NativeFunctionCall.CallWithName ("%"));
|
|
}
|
|
|
|
// Shuffle
|
|
if (shuffle) {
|
|
|
|
// Create point to return to when sequence is complete
|
|
var postShuffleNoOp = Runtime.ControlCommand.NoOp();
|
|
|
|
// When visitIndex == lastIdx, we skip the shuffle
|
|
if ( once || stopping )
|
|
{
|
|
// if( visitIndex == lastIdx ) -> skipShuffle
|
|
int lastIdx = stopping ? sequenceElements.Count - 1 : sequenceElements.Count;
|
|
container.AddContent(Runtime.ControlCommand.Duplicate());
|
|
container.AddContent(new Runtime.IntValue(lastIdx));
|
|
container.AddContent(Runtime.NativeFunctionCall.CallWithName("=="));
|
|
|
|
var skipShuffleDivert = new Runtime.Divert();
|
|
skipShuffleDivert.isConditional = true;
|
|
container.AddContent(skipShuffleDivert);
|
|
|
|
AddDivertToResolve(skipShuffleDivert, postShuffleNoOp);
|
|
}
|
|
|
|
// This one's a bit more complex! Choose the index at runtime.
|
|
var elementCountToShuffle = sequenceElements.Count;
|
|
if (stopping) elementCountToShuffle--;
|
|
container.AddContent (new Runtime.IntValue (elementCountToShuffle));
|
|
container.AddContent (Runtime.ControlCommand.SequenceShuffleIndex ());
|
|
if (once || stopping) container.AddContent(postShuffleNoOp);
|
|
}
|
|
|
|
container.AddContent (Runtime.ControlCommand.EvalEnd ());
|
|
|
|
// Create point to return to when sequence is complete
|
|
var postSequenceNoOp = Runtime.ControlCommand.NoOp();
|
|
|
|
// Each of the main sequence branches, and one extra empty branch if
|
|
// we have a "once" sequence.
|
|
for (var elIndex=0; elIndex<seqBranchCount; elIndex++) {
|
|
|
|
// This sequence element:
|
|
// if( chosenIndex == this index ) divert to this sequence element
|
|
// duplicate chosen sequence index, since it'll be consumed by "=="
|
|
container.AddContent (Runtime.ControlCommand.EvalStart ());
|
|
container.AddContent (Runtime.ControlCommand.Duplicate ());
|
|
container.AddContent (new Runtime.IntValue (elIndex));
|
|
container.AddContent (Runtime.NativeFunctionCall.CallWithName ("=="));
|
|
container.AddContent (Runtime.ControlCommand.EvalEnd ());
|
|
|
|
// Divert branch for this sequence element
|
|
var sequenceDivert = new Runtime.Divert ();
|
|
sequenceDivert.isConditional = true;
|
|
container.AddContent (sequenceDivert);
|
|
|
|
Runtime.Container contentContainerForSequenceBranch;
|
|
|
|
// Generate content for this sequence element
|
|
if ( elIndex < sequenceElements.Count ) {
|
|
var el = sequenceElements[elIndex];
|
|
contentContainerForSequenceBranch = (Runtime.Container)el.runtimeObject;
|
|
}
|
|
|
|
// Final empty branch for "once" sequences
|
|
else {
|
|
contentContainerForSequenceBranch = new Runtime.Container();
|
|
}
|
|
|
|
contentContainerForSequenceBranch.name = "s" + elIndex;
|
|
contentContainerForSequenceBranch.InsertContent(Runtime.ControlCommand.PopEvaluatedValue(), 0);
|
|
|
|
// When sequence element is complete, divert back to end of sequence
|
|
var seqBranchCompleteDivert = new Runtime.Divert ();
|
|
contentContainerForSequenceBranch.AddContent (seqBranchCompleteDivert);
|
|
container.AddToNamedContentOnly (contentContainerForSequenceBranch);
|
|
|
|
// Save the diverts for reference resolution later (in ResolveReferences)
|
|
AddDivertToResolve (sequenceDivert, contentContainerForSequenceBranch);
|
|
AddDivertToResolve (seqBranchCompleteDivert, postSequenceNoOp);
|
|
}
|
|
|
|
container.AddContent (postSequenceNoOp);
|
|
|
|
return container;
|
|
}
|
|
|
|
void AddDivertToResolve(Runtime.Divert divert, Runtime.Object targetContent)
|
|
{
|
|
_sequenceDivertsToResove.Add( new SequenceDivertToResolve() {
|
|
divert = divert,
|
|
targetContent = targetContent
|
|
});
|
|
}
|
|
|
|
public override void ResolveReferences(Story context)
|
|
{
|
|
base.ResolveReferences (context);
|
|
|
|
foreach (var toResolve in _sequenceDivertsToResove) {
|
|
toResolve.divert.targetPath = toResolve.targetContent.path;
|
|
}
|
|
}
|
|
|
|
class SequenceDivertToResolve
|
|
{
|
|
public Runtime.Divert divert;
|
|
public Runtime.Object targetContent;
|
|
}
|
|
List<SequenceDivertToResolve> _sequenceDivertsToResove;
|
|
}
|
|
}
|
|
|