// Copyright (c) Pixel Crushers. All rights reserved. using System.Collections.Generic; using UnityEngine; namespace PixelCrushers.DialogueSystem { public enum BarkGroupQueueLimitMode { NoLimit, StopAtLimit, DropOldestAtLimit } /// /// Manages bark groups specified by BarkGroupMember. /// Adds itself to the Dialogue Manager. /// [AddComponentMenu("")] // Added automatically at runtime. public class BarkGroupManager : MonoBehaviour { public BarkGroupQueueLimitMode queueLimitMode = BarkGroupQueueLimitMode.NoLimit; [Tooltip("Only used if mode is Stop At Limit or Drop Oldest At Limit")] public int queueLimit = 256; private static bool s_applicationIsQuitting = false; private static BarkGroupManager s_instance = null; public static BarkGroupManager instance { get { if (s_applicationIsQuitting) return null; if (s_instance == null) { s_instance = DialogueManager.instance.GetComponent(); if (s_instance == null) { s_instance = DialogueManager.instance.gameObject.AddComponent(); } } return s_instance; } } #if UNITY_2019_3_OR_NEWER && UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] static void InitStaticVariables() { s_applicationIsQuitting = false; s_instance = null; } #endif public Dictionary> groups = new Dictionary>(); private class BarkRequest { public BarkGroupMember member; public Transform listener; public string conversation; public BarkHistory barkHistory; public int entryID; public string barkText; public string sequence; public AbstractBarkUI barkUI; public float delayTime; public bool isPlaying = false; public BarkRequest(string conversation, BarkGroupMember member, Transform listener, BarkHistory barkHistory, int entryID = -1, float delayTime = -1) { this.member = member; this.listener = listener; this.conversation = conversation; this.barkHistory = barkHistory; this.entryID = entryID; this.barkText = null; this.sequence = null; this.barkUI = GetBarkUI(member); this.delayTime = GetDelayTime(member, delayTime); } public BarkRequest(string barkText, BarkGroupMember member, Transform listener, string sequence, float delayTime = -1) { this.member = member; this.listener = listener; this.conversation = null; this.barkHistory = null; this.entryID = -1; this.barkText = barkText; this.sequence = sequence; this.barkUI = GetBarkUI(member); this.delayTime = GetDelayTime(member, delayTime); } private AbstractBarkUI GetBarkUI(BarkGroupMember member) { if (member == null) return null; var dialogueActor = member.GetComponentInChildren(); if (dialogueActor != null && dialogueActor.barkUISettings.barkUI != null) return dialogueActor.barkUISettings.barkUI; return member.GetComponentInChildren(); } private float GetDelayTime(BarkGroupMember member, float delayTime) { if (delayTime >= 0) return delayTime; return (member == null) ? 0 : Random.Range(member.minDelayBetweenQueuedBarks, member.maxDelayBetweenQueuedBarks); } } private Dictionary> queues = new Dictionary>(); private void OnApplicationQuit() { s_applicationIsQuitting = true; } /// /// Registers a bark group member. /// public void AddToGroup(string groupId, BarkGroupMember member) { if (string.IsNullOrEmpty(groupId) || member == null) return; if (!groups.ContainsKey(groupId)) groups.Add(groupId, new HashSet()); groups[groupId].Add(member); } /// /// Unregisters a bark group member. /// public void RemoveFromGroup(string groupId, BarkGroupMember member) { if (string.IsNullOrEmpty(groupId) || member == null) return; if (!groups.ContainsKey(groupId)) return; if (!groups[groupId].Contains(member)) return; groups[groupId].Remove(member); if (groups[groupId].Count == 0) groups.Remove(groupId); } /// /// Hides all bark members' bark UIs and clears any queued barks. /// public void CancelAllBarks() { foreach (var queue in queues.Values) { queue.Clear(); } foreach (var group in groups.Values) { foreach (var member in group) { if (member != null) member.CancelBark(); } } } /// /// Hides other members' barks if they're playing. /// Pass null to hide all members' barks. /// public void MutexBark(string groupId, BarkGroupMember member) { if (string.IsNullOrEmpty(groupId)) return; if (!groups.ContainsKey(groupId)) return; foreach (var other in groups[groupId]) { if (other == member) continue; other.CancelBark(); } } /// /// Barks with group awareness. /// /// Conversation to bark from. /// Barker. /// Bark target. /// Bark history. /// Omit/zero to use the member's random delay settings; if nonzero, use this delay time. public void GroupBark(string conversation, BarkGroupMember member, Transform listener, BarkHistory barkHistory, float delayTime = 0) { if (member == null || !member.queueBarks) { DialogueManager.Bark(conversation, (member != null) ? member.transform : null, listener, barkHistory); } else { Enqueue(new BarkRequest(conversation, member, listener, barkHistory, -1, delayTime)); } } /// /// Barks with group awareness. /// /// Conversation to bark from. /// Barker. /// Bark target. /// Bark history. /// Omit/zero to use the member's random delay settings; if nonzero, use this delay time. public void GroupBark(string conversation, BarkGroupMember member, Transform listener, int entryID, float delayTime = 0) { if (member == null || !member.queueBarks) { DialogueManager.Bark(conversation, (member != null) ? member.transform : null, listener, entryID); } else { Enqueue(new BarkRequest(conversation, member, listener, null, entryID, delayTime)); } } /// /// Barks with group awareness. /// /// Text to bark. /// Barker. /// Bark target. /// Optional sequence to play during the bark. /// Omit/zero to use the member's random delay settings; if nonzero, use this delay time. public void GroupBarkString(string barkText, BarkGroupMember member, Transform listener, string sequence, float delayTime = 0) { if (member == null || !member.queueBarks) { DialogueManager.BarkString(barkText, (member != null) ? member.transform : null, listener, sequence); } else { Enqueue(new BarkRequest(barkText, member, listener, sequence, delayTime)); } } private void Enqueue(BarkRequest barkRequest) { var member = barkRequest.member; if (member.evaluateIdEveryBark) member.UpdateMembership(); var groupId = member.currentIdValue; if (!queues.ContainsKey(groupId)) queues.Add(groupId, new Queue()); var queue = queues[groupId]; if (queueLimitMode != BarkGroupQueueLimitMode.NoLimit && queue.Count > queueLimit) { switch (queueLimitMode) { case BarkGroupQueueLimitMode.StopAtLimit: return; case BarkGroupQueueLimitMode.DropOldestAtLimit: queue.Dequeue(); break; } } queue.Enqueue(barkRequest); if (queue.Count == 1) barkRequest.delayTime = 0; // Play immediately. } private void Update() { // Check each group's queue: var enumerator = queues.GetEnumerator(); // Enumerates manually to avoid garbage. while (enumerator.MoveNext()) { var queue = enumerator.Current.Value; if (queue.Count == 0) continue; var barkRequest = queue.Peek(); if (!barkRequest.isPlaying) { // If request at front of queue isn't playing yet, update time left: barkRequest.delayTime -= Time.deltaTime; if (barkRequest.delayTime <= 0) { // If it's now time, play the bark: if (barkRequest.member == null || barkRequest.barkUI == null || (string.IsNullOrEmpty(barkRequest.conversation) && string.IsNullOrEmpty(barkRequest.barkText))) { queue.Dequeue(); } else if (!string.IsNullOrEmpty(barkRequest.conversation)) { if (barkRequest.entryID == -1) { DialogueManager.Bark(barkRequest.conversation, barkRequest.member.transform, barkRequest.listener, barkRequest.barkHistory); } else { DialogueManager.Bark(barkRequest.conversation, barkRequest.member.transform, barkRequest.listener, barkRequest.entryID); } } else { DialogueManager.BarkString(barkRequest.barkText, barkRequest.member.transform, barkRequest.listener, barkRequest.sequence); } barkRequest.isPlaying = true; barkRequest.delayTime = 0.5f; // Wait a little for bark to set up before checking if it's done playing. } } else { barkRequest.delayTime -= Time.deltaTime; if (barkRequest.delayTime <= 0 && !barkRequest.barkUI.isPlaying) { // If request at front of queue is playing but bark UI is done playing, dequeue: queue.Dequeue(); } } } } } }