diff --git a/README.md b/README.md index 3b6b9bd..466adab 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Topiary.Unity -Unity Integration for the narrative scripting tool [topiary](https://github.com/peartreegames/topiary) with the [C# bindings](https://github.com/peartreegames/topiary-csharp). +Unity Integration for the dialogue scripting tool [topiary](https://github.com/peartreegames/topiary) with the [C# bindings](https://github.com/peartreegames/topiary-csharp). -Checkout the [syntax](https://github.com/peartreegames/topiary/blob/main/docs/syntax.md) file if you're new to writing with Topi. +Checkout the [syntax](https://peartree.games/topiary/docs/syntax) file if you're new to writing with Topi. ## Installation @@ -93,25 +93,17 @@ public static class DialogueFunctions // ex .topi file: // extern const playAnim = |name, clip| {} // playAnim("Player", "Laugh") - [Topi("playAnim"), Preserve] - public static void PlayAnim(TopiValue speakerName, TopiValue animClip) + [Topi("playAnim", 2)] + [MonoPInvokeCallback(typeof(Delegates.ExternFunctionDelegate))] + public static TopiValue PlayAnim(IntPtr argsPtr, byte count) { - if (!Conversation.Speakers.TryGetValue(speakerName.String, out var speaker)) return; - speaker.GetComponent().PlayAnim(animClip.String); - } - - [Topi("triggerAnim"), Preserve] - public static void TriggerAnim(TopiValue speakerName, TopiValue animClip) - { - if (!Conversation.Speakers.TryGetValue(speakerName.String, out var speaker)) return; - speaker.GetComponent().TriggerAnim(animClip.String); - } - - [Topi("stopAnim"), Preserve] - public static void StopAnim(TopiValue speakerName, TopiValue animClip) - { - if (!Conversation.Speakers.TryGetValue(speakerName.String, out var speaker)) return; - speaker.GetComponent().StopAnim(animClip.String); + var args = TopiValue.CreateArgs(argsPtr, count); + var speakerName = args[0]; + var animClip = args[1]; + if (!Conversation.Speakers.TryGetValue(speakerName.String, out var topi) || + !topi.TryGetComponent(out Speaker speaker)) return default; + speaker.PlayAnim(animClip.String); + return default; } } ``` diff --git a/Runtime/Conversation.cs b/Runtime/Conversation.cs index 5031e9d..6fb4c79 100644 --- a/Runtime/Conversation.cs +++ b/Runtime/Conversation.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.InteropServices; using AOT; using PeartreeGames.Evt.Variables; using UnityEngine; @@ -22,7 +23,6 @@ public class Conversation : MonoBehaviour public string[] Tags => tags; private TopiSpeaker _previousSpeaker; - private List _evtReferences; public static event Action OnCreated; public static event Action OnStart; public static event Action OnEnd; @@ -30,6 +30,8 @@ public class Conversation : MonoBehaviour public static event Action OnChoices; public static readonly Dictionary Speakers = new(); public static readonly Dictionary Conversations = new(); + private static readonly Dictionary Variables = new(); + private static Delegates.Subscriber _subscriber; public static void AddSpeaker(TopiSpeaker speaker) => Speakers[speaker.name] = speaker; public static void RemoveSpeaker(TopiSpeaker speaker) => Speakers.Remove(speaker.name); @@ -123,6 +125,9 @@ public IEnumerator Play() Log("[Topiary.Unity] Invalid Dialogue", Library.Severity.Warn); yield break; } + + _subscriber = ValueChanged; + Dialogue.Library.SetSubscriberCallback(Dialogue.VmPtr, Marshal.GetFunctionPointerForDelegate(_subscriber)); Dialogue.Library.SetDebugSeverity(logs); yield return StartCoroutine(LoadAddressableTopiValues()); State.Inject(Dialogue); @@ -134,7 +139,7 @@ public IEnumerator Play() { Dialogue?.Run(); } - catch (System.Runtime.InteropServices.SEHException ex) + catch (SEHException ex) { Log($"Caught an SEHException: {ex}", Library.Severity.Error); break; @@ -155,39 +160,65 @@ public IEnumerator Play() UnloadAddressableTopiValues(); } - + [MonoPInvokeCallback(typeof(Delegates.Subscriber))] + public static void ValueChanged(string name, ref TopiValue value) + { + if (Variables.TryGetValue(name, out var variable)) + { + switch (value.tag) + { + case TopiValue.Tag.Bool when variable is EvtTopiBool b: + b.Value = value.Bool; + break; + case TopiValue.Tag.Number when variable is EvtTopiInt i: + i.Value = value.Int; + break; + case TopiValue.Tag.Number when variable is EvtTopiFloat f: + f.Value = value.Float; + break; + case TopiValue.Tag.String when variable is EvtTopiString s: + s.Value = value.String; + break; + } + } + } + private IEnumerator LoadAddressableTopiValues() { var ao = Addressables.LoadResourceLocationsAsync(new List {"Topiary", "Evt"}, Addressables.MergeMode.Intersection); yield return ao; var list = ao.Result; - _evtReferences = new List(); foreach (var item in list) { var key = item.PrimaryKey; if (!_data.ExternsSet.Contains(key)) continue; var aoEvt = Addressables.LoadAssetAsync(key); yield return aoEvt; - - EvtTopiReference evtRef = aoEvt.Result switch + + var topiName = aoEvt.Result switch { - EvtTopiBool b => new EvtBoolReference(this, b), - EvtTopiFloat f => new EvtFloatReference(this, f), - EvtTopiInt i => new EvtIntReference(this, i), - EvtTopiString s => new EvtStringReference(this, s), + EvtTopiBool b => b.Name, + EvtTopiFloat f => f.Name, + EvtTopiInt i => i.Name, + EvtTopiString s => s.Name, _ => null }; - if (evtRef == null) continue; - _evtReferences.Add(evtRef); + if (topiName == null) + { + Addressables.Release(aoEvt); + continue; + } + Variables[topiName] = aoEvt.Result; } } private void UnloadAddressableTopiValues() { - if (_evtReferences == null) return; - foreach (var reference in _evtReferences) reference?.Dispose(); - _evtReferences.Clear(); + foreach (var kvp in Variables) + { + Addressables.Release(kvp.Value); + } } } } \ No newline at end of file diff --git a/Runtime/Evt/EvtTopiReference.cs b/Runtime/Evt/EvtTopiReference.cs deleted file mode 100644 index f5a78a1..0000000 --- a/Runtime/Evt/EvtTopiReference.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; - -namespace PeartreeGames.Topiary.Unity -{ - public abstract class EvtTopiReference : IDisposable - { - private protected readonly Conversation _conversation; - - protected EvtTopiReference(Conversation conversation) - { - _conversation = conversation; - } - - public abstract void Dispose(); - } - public abstract class EvtTopiReference : EvtTopiReference - { - private protected readonly EvtTopiVariable _evtVariable; - - public EvtTopiReference(Conversation conversation, EvtTopiVariable evtVariable) : base(conversation) - { - _evtVariable = evtVariable; - _evtVariable.OnEvt += OnValueChanged; - _conversation.Dialogue.Subscribe(_evtVariable.Name); - } - - public override void Dispose() - { - _evtVariable.OnEvt -= OnValueChanged; - _conversation.Dialogue.Unsubscribe(_evtVariable.Name); - } - - protected abstract void OnValueChanged(T value); - - protected abstract void OnTopiValueChanged(ref TopiValue topiValue); - } - - public class EvtIntReference : EvtTopiReference - { - public EvtIntReference(Conversation conversation, EvtTopiVariable evtVariable) : base( - conversation, evtVariable) - { - conversation.Dialogue.Set(evtVariable.Name, evtVariable.Value); - } - - protected override void OnValueChanged(int value) - { - var name = _evtVariable.Name; - var current = _conversation.Dialogue.GetValue(name); - if (current.tag != TopiValue.Tag.Number) return; - _conversation.Dialogue.Set(name, value); - } - - protected override void OnTopiValueChanged(ref TopiValue topiValue) - { - if (topiValue.tag != TopiValue.Tag.Number) return; - _evtVariable.Value = topiValue.Int; - } - } - - public class EvtFloatReference : EvtTopiReference - { - public EvtFloatReference(Conversation conversation, EvtTopiVariable evtVariable) : - base( - conversation, evtVariable) - { - conversation.Dialogue.Set(evtVariable.Name, evtVariable.Value); - } - - protected override void OnValueChanged(float value) - { - var name = _evtVariable.Name; - var current = _conversation.Dialogue.GetValue(name); - if (current.tag != TopiValue.Tag.Number) return; - _conversation.Dialogue.Set(name, value); - } - - protected override void OnTopiValueChanged(ref TopiValue topiValue) - { - if (topiValue.tag != TopiValue.Tag.Number) return; - _evtVariable.Value = topiValue.Float; - } - } - - - public class EvtBoolReference : EvtTopiReference - { - public EvtBoolReference(Conversation conversation, EvtTopiVariable evtVariable) : - base( - conversation, evtVariable) - { - conversation.Dialogue.Set(evtVariable.Name, evtVariable.Value); - } - - protected override void OnValueChanged(bool value) - { - var name = _evtVariable.Name; - var current = _conversation.Dialogue.GetValue(name); - if (current.tag != TopiValue.Tag.Bool) return; - _conversation.Dialogue.Set(name, value); - } - - protected override void OnTopiValueChanged(ref TopiValue topiValue) - { - if (topiValue.tag != TopiValue.Tag.Bool) return; - _evtVariable.Value = topiValue.Bool; - } - } - - public class EvtStringReference : EvtTopiReference - { - public EvtStringReference(Conversation conversation, EvtTopiVariable evtVariable) : - base( - conversation, evtVariable) - { - conversation.Dialogue.Set(evtVariable.Name, evtVariable.Value); - } - - protected override void OnValueChanged(string value) - { - var name = _evtVariable.Name; - var current = _conversation.Dialogue.GetValue(name); - if (current.tag != TopiValue.Tag.String) return; - _conversation.Dialogue.Set(name, value); - } - - protected override void OnTopiValueChanged(ref TopiValue topiValue) - { - if (topiValue.tag != TopiValue.Tag.String) return; - _evtVariable.Value = topiValue.String; - } - } -} \ No newline at end of file diff --git a/Runtime/Evt/EvtTopiReference.cs.meta b/Runtime/Evt/EvtTopiReference.cs.meta deleted file mode 100644 index 75bcfb7..0000000 --- a/Runtime/Evt/EvtTopiReference.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 18ed7f3a4f6d48b9a758569e4f423fb3 -timeCreated: 1705551381 \ No newline at end of file