Newer
Older
TheVengeance-Project-IADE-Unity2D / Assets / Ink / InkLibs / InkCompiler / ParsedHierarchy / ConditionalSingleBranch.cs

using System.Collections.Generic;

namespace Ink.Parsed
{
    public class ConditionalSingleBranch : Parsed.Object
    {
        // bool condition, e.g.:
        // { 5 == 4:
        //   - the true branch
        //   - the false branch
        // }
        public bool isTrueBranch { get; set; }

        // When each branch has its own expression like a switch statement,
        // this is non-null. e.g.
        // { x:
        //    - 4: the value of x is four (ownExpression is the value 4)
        //    - 3: the value of x is three
        // }
        public Expression ownExpression { 
            get { 
                return _ownExpression; 
            } 
            set { 
                _ownExpression = value; 
                if (_ownExpression) {
                    AddContent (_ownExpression); 
                }
            }
        }

        // In the above example, match equality of x with 4 for the first branch.
        // This is as opposed to simply evaluating boolean equality for each branch,
        // example when shouldMatchEqualtity is FALSE:
        // {
        //    3 > 2:  This will happen
        //    2 > 3:  This won't happen
        // }
        public bool matchingEquality { get; set; }

        public bool isElse { get; set; }

        public bool isInline { get; set; }

        public Runtime.Divert returnDivert { get; protected set; }

        public ConditionalSingleBranch (List<Parsed.Object> content)
        {
            // Branches are allowed to be empty
            if (content != null) {
                _innerWeave = new Weave (content);
                AddContent (_innerWeave);
            }
        }

        // Runtime content can be summarised as follows:
        //  - Evaluate an expression if necessary to branch on
        //  - Branch to a named container if true
        //       - Divert back to main flow
        //         (owner Conditional is in control of this target point)
        public override Runtime.Object GenerateRuntimeObject ()
        {
            // Check for common mistake, of putting "else:" instead of "- else:"
            if (_innerWeave) {
                foreach (var c in _innerWeave.content) {
                    var text = c as Parsed.Text;
                    if (text) {
                        // Don't need to trim at the start since the parser handles that already
                        if (text.text.StartsWith ("else:")) {
                            Warning ("Saw the text 'else:' which is being treated as content. Did you mean '- else:'?", text);
                        }
                    }
                }
            }
                                           
            var container = new Runtime.Container ();

            // Are we testing against a condition that's used for more than just this
            // branch? If so, the first thing we need to do is replicate the value that's
            // on the evaluation stack so that we don't fully consume it, in case other
            // branches need to use it.
            bool duplicatesStackValue = matchingEquality && !isElse;
            if ( duplicatesStackValue )
                container.AddContent (Runtime.ControlCommand.Duplicate ());

            _conditionalDivert = new Runtime.Divert ();

            // else clause is unconditional catch-all, otherwise the divert is conditional
            _conditionalDivert.isConditional = !isElse;

            // Need extra evaluation?
            if( !isTrueBranch && !isElse ) {

                bool needsEval = ownExpression != null;
                if( needsEval )
                    container.AddContent (Runtime.ControlCommand.EvalStart ());

                if (ownExpression)
                    ownExpression.GenerateIntoContainer (container);

                // Uses existing duplicated value
                if (matchingEquality)
                    container.AddContent (Runtime.NativeFunctionCall.CallWithName ("=="));

                if( needsEval ) 
                    container.AddContent (Runtime.ControlCommand.EvalEnd ()); 
            }

            // Will pop from stack if conditional
            container.AddContent (_conditionalDivert);

            _contentContainer = GenerateRuntimeForContent ();
            _contentContainer.name = "b";

            // Multi-line conditionals get a newline at the start of each branch
            // (as opposed to the start of the multi-line conditional since the condition
            //  may evaluate to false.)
            if (!isInline) {
                _contentContainer.InsertContent (new Runtime.StringValue ("\n"), 0);
            }

            if( duplicatesStackValue || (isElse && matchingEquality) )
                _contentContainer.InsertContent (Runtime.ControlCommand.PopEvaluatedValue (), 0);

            container.AddToNamedContentOnly (_contentContainer);

            returnDivert = new Runtime.Divert ();
            _contentContainer.AddContent (returnDivert);

            return container;
        }

        Runtime.Container GenerateRuntimeForContent()
        {
            // Empty branch - create empty container
            if (_innerWeave == null) {
                return new Runtime.Container ();
            }

            return _innerWeave.rootContainer;
        }

        public override void ResolveReferences (Story context)
        {
            _conditionalDivert.targetPath = _contentContainer.path;

            base.ResolveReferences (context);
        }

        Runtime.Container _contentContainer;
        Runtime.Divert _conditionalDivert;
        Expression _ownExpression;

        Weave _innerWeave;
    }
}