/* Yarn Spinner is licensed to you under the terms found in the file LICENSE.md. */ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using UnityEngine; #nullable enable namespace Yarn.Unity { [HelpURL("https://docs.yarnspinner.dev/using-yarnspinner-with-unity/importing-yarn-files/yarn-projects")] public sealed class YarnProject : ScriptableObject { [HideInInspector] public byte[]? compiledYarnProgram; [HideInInspector] [NotNull] #pragma warning disable CS8618 public Localization baseLocalization; #pragma warning restore CS8618 [SerializeField] [HideInInspector] public SerializableDictionary localizations = new SerializableDictionary(); public LineMetadata? lineMetadata; [HideInInspector] public LocalizationType localizationType; /// /// The cached result of deserializing . /// private Program? cachedProgram = null; /// /// The names of all nodes contained within the . /// public string[] NodeNames { get { return Program.Nodes.Keys.ToArray(); } } [System.Serializable] public struct ShadowTableEntry { public string sourceLineID; public string[] shadowMetadata; } [System.Serializable] public class ShadowTableDictionary : SerializableDictionary { } /// /// The cached result of reading the default values from the . /// private Dictionary? initialValues; /// /// The default values of all declared or inferred variables in the . Organised by their name as written in the yarn /// files. /// public Dictionary InitialValues { get { if (initialValues != null) { return initialValues; } initialValues = new Dictionary(); foreach (var pair in Program.InitialValues) { var value = pair.Value; switch (value.ValueCase) { case Yarn.Operand.ValueOneofCase.StringValue: { initialValues[pair.Key] = value.StringValue; break; } case Yarn.Operand.ValueOneofCase.BoolValue: { initialValues[pair.Key] = value.BoolValue; break; } case Yarn.Operand.ValueOneofCase.FloatValue: { initialValues[pair.Key] = value.FloatValue; break; } default: { Debug.LogWarning($"{pair.Key} is of an invalid type: {value.ValueCase}"); break; } } } return initialValues; } } // ok assumption is that this can be lazy loaded and then kept around as // not every node has headers you care about but some will and be read A // LOT so we will fill a dict on request and just keep it around is // somewhat unnecessary as people can get this out themselves if they // want but I think peeps will wanna use headers like a dictionary so we // will do the transformation for you private Dictionary>> nodeHeaders = new Dictionary>>(); /// /// Gets the headers for the requested node. /// /// /// The first time this is called, the values are extracted from and cached inside . Future /// calls will then return the cached values. /// [System.Obsolete("Use " + nameof(Dialogue) + "." + nameof(Dialogue.GetHeaders), true)] public Dictionary> GetHeaders(string nodeName) { throw new System.NotImplementedException(); } /// /// Gets a Localization given a locale code. /// /// The locale code to find a for. /// The Localization if one is found for the locale ; /// otherwise. public Localization GetLocalization(string localeCode) { // If localeCode is null, we use the base localization. if (localeCode == null) { return baseLocalization; } if (localizations.TryGetValue(localeCode, out var result)) { return result; } // We didn't find a localization. Fall back to the Base // localization. return baseLocalization; } /// /// Returns a list of all line and option IDs within the requested nodes /// /// /// This is intended to be used either to precache multiple nodes worth /// of lines or for debugging /// /// the names of all nodes whos line IDs you /// covet /// The ids of all lines and options in the requested public IEnumerable GetLineIDsForNodes(IEnumerable nodes) { var ids = new List(); foreach (var node in nodes) { var lines = Program.LineIDsForNode(node); if (lines != null) { ids.AddRange(lines); } } return ids; } /// /// Gets the Yarn Program stored in this project. /// /// /// The first time this is called, the program stored in is deserialized and cached. Future /// calls to this method will return the cached value. /// public Program Program { get { if (cachedProgram == null) { cachedProgram = Program.Parser.ParseFrom(compiledYarnProgram); } return cachedProgram; } } private void Awake() { // We have to invalidate the cache on Awake. Note that this cannot // be done through the importer (e.g., with a setter method that // sets compiledYarnProgram and invalidates cachedProgram) because // the YarnProject the importer accesses is NOT the same object as // the one currently loaded in the editor. (You can tell by // comparing their HashCodes) If there are other sources that can // change the value of compiledYarnProgram aside from the importer // in runtime, maybe we can add such a method, but until then, this // is sufficient. cachedProgram = null; } } public enum LocalizationType { YarnInternal, Unity, } }