Ejemplo n.º 1
0
    public static void HandleAttribute(Yarn.Markup.MarkupAttribute attribute, CustomDialogueUI ui)
    {
        switch (attribute.Name)
        {
        case Character:
            SetDialogueGivenLine(attribute, ui);
            break;

        case Wave:
            float speed = attribute.Properties.ContainsKey("s") ? attribute.Properties["s"].FloatValue : 0.5f;
            ui.currentDialogue.tweenText.Wave(
                attribute.Position,
                attribute.Position + attribute.Length,
                speed);
            break;

        case Shake:
            float strength = attribute.Properties.ContainsKey("s") ? attribute.Properties["s"].IntegerValue : 4;
            ui.currentDialogue.tweenText.Shake(
                attribute.Position,
                attribute.Position + attribute.Length,
                strength);
            break;

        case Screenshake:
            float amplitude = attribute.Properties.ContainsKey("a") ? attribute.Properties["a"].FloatValue : 1;
            MainSingleton.Instance.ImpulseSource.m_ImpulseDefinition.m_AmplitudeGain = amplitude;
            MainSingleton.Instance.ImpulseSource.GenerateImpulse();
            break;
        }
    }
Ejemplo n.º 2
0
        /// <summary>
        /// Gets the first attribute with the specified name, if present.
        /// </summary>
        /// <param name="name">The name of the attribute to get.</param>
        /// <param name="attribute">When this method returns, contains the
        /// attribute with the specified name, if the attribute is found;
        /// otherwise, the default <see cref="MarkupAttribute"/>. This
        /// parameter is passed uninitialized.</param>
        /// <returns><see langword="true"/> if the <see
        /// cref="MarkupParseResult"/> contains an attribute with the
        /// specified name; otherwise, <see langword="false"/>.</returns>
        public bool TryGetAttributeWithName(string name, out MarkupAttribute attribute)
        {
            foreach (var a in this.Attributes)
            {
                if (a.Name == name)
                {
                    attribute = a;
                    return(true);
                }
            }

            attribute = default;
            return(false);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Returns the substring of <see cref="Text"/> covered by
        /// <paramref name="attribute"/> Position and Length properties.
        /// </summary>
        /// <remarks>
        /// If the attribute's <see cref="MarkupAttribute.Length"/>
        /// property is zero, this method returns the empty string.
        ///
        /// This method does not check to see if <paramref
        /// name="attribute"/> is an attribute belonging to this
        /// MarkupParseResult. As a result, if you pass an attribute that
        /// doesn't belong, it may describe a range of text that does not
        /// appear in <see cref="Text"/>. If this occurs, an <see
        /// cref="System.IndexOutOfRangeException"/> will be thrown.
        /// </remarks>
        /// <param name="attribute">The attribute to get the text
        /// for.</param>
        /// <returns>The text contained within the attribute.</returns>
        /// <throws cref="System.IndexOutOfRangeException">Thrown when
        /// attribute's <see cref="MarkupAttribute.Position"/> and <see
        /// cref="MarkupAttribute.Length"/> properties describe a range of
        /// text outside the maximum range of <see cref="Text"/>.</throws>
        public string TextForAttribute(MarkupAttribute attribute)
        {
            if (attribute.Length == 0)
            {
                return(string.Empty);
            }

            if (this.Text.Length < attribute.Position + attribute.Length)
            {
                throw new System.IndexOutOfRangeException($"Attribute represents a range not representable by this text. Does this {nameof(MarkupAttribute)} belong to this {nameof(MarkupParseResult)}?");
            }

            return(this.Text.Substring(attribute.Position, attribute.Length));
        }
Ejemplo n.º 4
0
    /// <summary>
    /// Set current DialogueGroup based on line attributes: character name
    /// </summary>
    /// <param name="dialogueLine"></param>
    private static void SetDialogueGivenLine(Yarn.Markup.MarkupAttribute attribute, CustomDialogueUI ui)
    {
        string       name = attribute.Properties["name"].StringValue;
        DialogueType type;

        // Edit below to add more types
        switch (name)
        {
        case "Player":
            type = DialogueType.Player;
            break;

        case "Think":
            type = DialogueType.Think;
            break;

        default:
            type = DialogueType.NPC;
            break;
        }
        ui.SetCurrentDialogue(type, name);
    }
Ejemplo n.º 5
0
        /// <summary>
        /// Deletes an attribute from this markup.
        /// </summary>
        /// <remarks>
        /// This method deletes the range of text covered by <paramref
        /// name="attributeToDelete"/>, and updates the other attributes in
        /// this markup as follows:
        ///
        /// - Attributes that start and end before the deleted attribute
        /// are unmodified.
        ///
        /// - Attributes that start before the deleted attribute and end
        /// inside it are truncated to remove the part overlapping the
        /// deleted attribute.
        ///
        /// - Attributes that have the same position and length as the
        /// deleted attribute are deleted, if they apply to any text.
        ///
        /// - Attributes that start and end within the deleted attribute
        /// are deleted.
        ///
        /// - Attributes that start within the deleted attribute, and end
        /// outside it, have their start truncated to remove the part
        /// overlapping the deleted attribute.
        ///
        /// - Attributes that start after the deleted attribute have their
        /// start point adjusted to account for the deleted text.
        ///
        /// This method does not modify the current object. A new
        /// MarkupParseResult is returned.
        ///
        /// If <paramref name="attributeToDelete"/> is not an attribute of
        /// this <see cref="MarkupParseResult"/>, the behaviour is
        /// undefined.
        /// </remarks>
        /// <param name="attributeToDelete">The attribute to
        /// remove.</param>
        /// <returns>A new MarkupParseResult, with the plain text modified
        /// and an updated collection of attributes.</returns>
        public MarkupParseResult DeleteRange(MarkupAttribute attributeToDelete)
        {
            var newAttributes = new List <MarkupAttribute>();

            // Address the trivial case: if the attribute has a zero
            // length, just create a new markup that doesn't include it.
            // The plain text is left unmodified, because this attribute
            // didn't apply to any text.
            if (attributeToDelete.Length == 0)
            {
                foreach (var a in this.Attributes)
                {
                    if (!a.Equals(attributeToDelete))
                    {
                        newAttributes.Add(a);
                    }
                }

                return(new MarkupParseResult(this.Text, newAttributes));
            }

            var deletionStart = attributeToDelete.Position;
            var deletionEnd   = attributeToDelete.Position + attributeToDelete.Length;

            var editedSubstring = this.Text.Remove(attributeToDelete.Position, attributeToDelete.Length);

            foreach (var existingAttribute in this.Attributes)
            {
                var start = existingAttribute.Position;
                var end   = existingAttribute.Position + existingAttribute.Length;

                if (existingAttribute.Equals(attributeToDelete))
                {
                    // This is the attribute we're deleting. Don't include
                    // it.
                    continue;
                }

                var editedAttribute = existingAttribute;

                if (start <= deletionStart)
                {
                    // The attribute starts before start point of the item
                    // we're deleting.
                    if (end <= deletionStart)
                    {
                        // This attribute is entirely before the item we're
                        // deleting, and will be unmodified.
                    }
                    else if (end <= deletionEnd)
                    {
                        // This attribute starts before the item we're
                        // deleting, and ends inside it. The Position
                        // doesn't need to change, but its Length is
                        // trimmed so that it ends where the deleted
                        // attribute begins.
                        editedAttribute.Length = deletionStart - start;

                        if (existingAttribute.Length > 0 && editedAttribute.Length <= 0)
                        {
                            // The attribute's length has been reduced to
                            // zero. All of the contents it previous had
                            // have been removed, so we will remove the
                            // attribute itself.
                            continue;
                        }
                    }
                    else
                    {
                        // This attribute starts before the item we're
                        // deleting, and ends after it. Its length is
                        // edited to remove the length of the item we're
                        // deleting.
                        editedAttribute.Length -= attributeToDelete.Length;
                    }
                }
                else if (start >= deletionEnd)
                {
                    // The item begins after the item we're deleting. Its
                    // length isn't changing. We just need to offset its
                    // start position.
                    editedAttribute.Position = start - attributeToDelete.Length;
                }
                else if (start >= deletionStart && end <= deletionEnd)
                {
                    // The item is entirely within the item we're deleting.
                    // It will be deleted too - we'll skip including it in
                    // the updated attributes list.
                    continue;
                }
                else if (start >= deletionStart && end > deletionEnd)
                {
                    // The item starts within the item we're deleting, and
                    // ends outside it. We'll adjust the start point so
                    // that it begins at the point where this item and the
                    // item we're deleting stop overlapping.
                    var overlapLength = deletionEnd - start;
                    var newStart      = deletionStart;
                    var newLength     = existingAttribute.Length - overlapLength;

                    editedAttribute.Position = newStart;
                    editedAttribute.Length   = newLength;
                }

                newAttributes.Add(editedAttribute);
            }

            return(new MarkupParseResult(editedSubstring, newAttributes));
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Creates a list of <see cref="MarkupAttribute"/>s from loose
        /// <see cref="MarkupAttributeMarker"/>s.
        /// </summary>
        /// <param name="markers">The collection of markers.</param>
        /// <returns>The list of attributes.</returns>
        /// <throws cref="MarkupParseException">Thrown when a close marker
        /// is encountered, but no corresponding open marker for it
        /// exists.</throws>
        private List <MarkupAttribute> BuildAttributesFromMarkers(List <MarkupAttributeMarker> markers)
        {
            // Using a linked list here because we want to append to the
            // front and be able to walk through it easily
            var unclosedMarkerList = new LinkedList <MarkupAttributeMarker>();

            var attributes = new List <MarkupAttribute>(markers.Count);

            foreach (var marker in markers)
            {
                switch (marker.Type)
                {
                case TagType.Open:
                    // A new marker! Add it to the unclosed list at the
                    // start (because there's a high chance that it
                    // will be closed soon).
                    unclosedMarkerList.AddFirst(marker);
                    break;

                case TagType.Close:
                {
                    // A close marker! Walk back through the
                    // unclosed stack to find the most recent
                    // marker of the same type to find its pair.
                    MarkupAttributeMarker matchedOpenMarker = default;
                    foreach (var openMarker in unclosedMarkerList)
                    {
                        if (openMarker.Name == marker.Name)
                        {
                            // Found a corresponding open!
                            matchedOpenMarker = openMarker;
                            break;
                        }
                    }

                    if (matchedOpenMarker.Name == null)
                    {
                        throw new MarkupParseException($"Unexpected close marker {marker.Name} at position {marker.Position} in line {this.input}");
                    }

                    // This attribute is now closed, so we can
                    // remove the marker from the unmatched list
                    unclosedMarkerList.Remove(matchedOpenMarker);

                    // We can now construct the attribute!
                    var length    = marker.Position - matchedOpenMarker.Position;
                    var attribute = new MarkupAttribute(matchedOpenMarker, length);

                    attributes.Add(attribute);
                }

                break;

                case TagType.SelfClosing:
                {
                    // Self-closing markers create a zero-length
                    // attribute where they appear
                    var attribute = new MarkupAttribute(marker, 0);
                    attributes.Add(attribute);
                }

                break;

                case TagType.CloseAll:
                {
                    // Close all currently open markers

                    // For each marker that we currently have open,
                    // this marker has closed it, so create an
                    // attribute for it
                    foreach (var openMarker in unclosedMarkerList)
                    {
                        var length    = marker.Position - openMarker.Position;
                        var attribute = new MarkupAttribute(openMarker, length);

                        attributes.Add(attribute);
                    }

                    // We've now closed all markers, so we can
                    // clear the unclosed list now
                    unclosedMarkerList.Clear();
                }

                break;
                }
            }

            attributes.Sort(AttributePositionComparison);

            return(attributes);
        }
Ejemplo n.º 7
0
        /// <summary>Parses a line of text, and produces a
        /// <see cref="MarkupParseResult"/> containing the processed
        /// text.</summary>
        /// <param name="input">The text to parse.</param>
        /// <returns>The resulting markup information.</returns>
        internal MarkupParseResult ParseMarkup(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                // We got a null input; return an empty markup parse result
                return(new MarkupParseResult
                {
                    Text = string.Empty,
                    Attributes = new List <MarkupAttribute>(),
                });
            }

            this.input = input.Normalize();

            this.stringReader = new StringReader(this.input);

            var stringBuilder = new StringBuilder();

            var markers = new List <MarkupAttributeMarker>();

            int nextCharacter;

            char lastCharacter = char.MinValue;

            // Read the entirety of the line
            while ((nextCharacter = this.stringReader.Read()) != -1)
            {
                char c = (char)nextCharacter;

                if (c == '\\')
                {
                    // This may be the start of an escaped bracket ("\[" or
                    // "\]"). Peek ahead to see if it is.
                    var nextC = (char)this.stringReader.Peek();

                    if (nextC == '[' || nextC == ']')
                    {
                        // It is! We'll discard this '\', and read the next
                        // character as plain text.
                        c = (char)this.stringReader.Read();
                        stringBuilder.Append(c);
                        this.sourcePosition += 1;
                        continue;
                    }
                    else
                    {
                        // It wasn't an escaped bracket. Continue on, and
                        // parse the '\' as a normal character.
                    }
                }

                if (c == '[')
                {
                    // How long is our current string, in text elements
                    // (i.e. visible glyphs)?
                    this.position = new System.Globalization.StringInfo(stringBuilder.ToString()).LengthInTextElements;

                    // The start of a marker!
                    MarkupAttributeMarker marker = this.ParseAttributeMarker();

                    markers.Add(marker);

                    var hadPrecedingWhitespaceOrLineStart = this.position == 0 || char.IsWhiteSpace(lastCharacter);

                    bool wasReplacementMarker = false;

                    // Is this a replacement marker?
                    if (marker.Name != null && this.markerProcessors.ContainsKey(marker.Name))
                    {
                        wasReplacementMarker = true;

                        // Process it and get the replacement text!
                        var replacementText = this.ProcessReplacementMarker(marker);

                        // Insert it into our final string and update our
                        // position accordingly
                        stringBuilder.Append(replacementText);
                    }

                    bool trimWhitespaceIfAble = false;

                    if (hadPrecedingWhitespaceOrLineStart)
                    {
                        // By default, self-closing markers will trim a
                        // single trailing whitespace after it if there was
                        // preceding whitespace. This doesn't happen if the
                        // marker was a replacement marker, or it has a
                        // property "trimwhitespace" (which must be
                        // boolean) set to false. All markers can opt-in to
                        // trailing whitespace trimming by having a
                        // 'trimwhitespace' property set to true.
                        if (marker.Type == TagType.SelfClosing)
                        {
                            trimWhitespaceIfAble = !wasReplacementMarker;
                        }

                        if (marker.TryGetProperty(TrimWhitespaceProperty, out var prop))
                        {
                            if (prop.Type != MarkupValueType.Bool)
                            {
                                throw new MarkupParseException($"Error parsing line {this.input}: attribute {marker.Name} at position {this.position} has a {prop.Type.ToString().ToLower()} property \"{TrimWhitespaceProperty}\" - this property is required to be a boolean value.");
                            }

                            trimWhitespaceIfAble = prop.BoolValue;
                        }
                    }

                    if (trimWhitespaceIfAble)
                    {
                        // If there's trailing whitespace, and we want to
                        // remove it, do so
                        if (this.PeekWhitespace())
                        {
                            // Consume the single trailing whitespace
                            // character (and don't update position)
                            this.stringReader.Read();
                            this.sourcePosition += 1;
                        }
                    }
                }
                else
                {
                    // plain text! add it to the resulting string and
                    // advance the parser's plain-text position
                    stringBuilder.Append(c);
                    this.sourcePosition += 1;
                }

                lastCharacter = c;
            }

            var attributes = this.BuildAttributesFromMarkers(markers);

            var characterAttributeIsPresent = false;

            foreach (var attribute in attributes)
            {
                if (attribute.Name == CharacterAttribute)
                {
                    characterAttributeIsPresent = true;
                }
            }

            if (characterAttributeIsPresent == false)
            {
                // Attempt to generate a character attribute from the start
                // of the string to the first colon
                var match = EndOfCharacterMarker.Match(this.input);

                if (match.Success)
                {
                    var endRange      = match.Index + match.Length;
                    var characterName = this.input.Substring(0, match.Index);

                    MarkupValue nameValue = new MarkupValue
                    {
                        Type        = MarkupValueType.String,
                        StringValue = characterName,
                    };

                    MarkupProperty nameProperty = new MarkupProperty(CharacterAttributeNameProperty, nameValue);

                    var characterAttribute = new MarkupAttribute(0, 0, endRange, CharacterAttribute, new[] { nameProperty });

                    attributes.Add(characterAttribute);
                }
            }

            return(new MarkupParseResult
            {
                Text = stringBuilder.ToString(),
                Attributes = attributes,
            });
        }