// Copyright (c) Pixel Crushers. All rights reserved. using UnityEngine; using System.Collections; namespace PixelCrushers.DialogueSystem { /// /// Adds and removes extra dialogue databases. /// [AddComponentMenu("")] // Use wrapper. public class ExtraDatabases : MonoBehaviour { /// /// Add the databases when this trigger occurs. /// public DialogueTriggerEvent addTrigger = DialogueTriggerEvent.OnStart; /// /// Remove the databases when this trigger occurs. /// public DialogueTriggerEvent removeTrigger = DialogueTriggerEvent.None; /// /// The databases to add/remove. /// public DialogueDatabase[] databases = new DialogueDatabase[0]; /// /// The condition that must be true for the trigger to fire. /// public Condition condition = new Condition(); /// /// As soon as one event (add or remove) has occurred, destroy this component. /// [Tooltip("As soon as one event (add or remove) has occurred, destroy this component.")] public bool once = false; /// /// Add/remove one database per frame instead of adding them all at the same time. /// Useful to avoid stutter when adding several databases. /// [Tooltip("Add/remove one database per frame instead of adding them all at the same time. Useful to avoid stutter when adding several databases.")] public bool onePerFrame = false; /// /// This event is called after ExtraDatabases has finished adding its list of databases /// to the DialogueManager's MasterDatabase. /// public static event System.Action addedDatabases = delegate { }; /// /// This event is called after ExtraDatabases has finished removing its list of databases /// from the DialogueManager's MasterDatabase. /// public static event System.Action removedDatabases = delegate { }; #if UNITY_2019_3_OR_NEWER && UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] static void InitStaticVariables() { addedDatabases = delegate { }; removedDatabases = delegate { }; } #endif protected bool m_trying = false; protected Coroutine m_destroyCoroutine = null; protected int m_numActiveCoroutines = 0; protected virtual void TryAddDatabases(Transform interactor, bool onePerFrame) { if (!m_trying) { m_trying = true; try { if ((condition == null) || condition.IsTrue(interactor)) { AddDatabases(onePerFrame); } } finally { m_trying = false; } } } public virtual void AddDatabases(bool onePerFrame) { if (onePerFrame) { StartCoroutine(AddDatabasesCoroutine()); } else if (gameObject.activeInHierarchy && enabled) { AddDatabasesImmediate(); } } protected virtual void AddDatabasesImmediate() { foreach (var database in databases) { AddDatabase(database); } addedDatabases(); if (once) Destroy(this); } protected virtual IEnumerator AddDatabasesCoroutine() { m_numActiveCoroutines++; if (once && m_destroyCoroutine == null) m_destroyCoroutine = StartCoroutine(DestroyCoroutine()); foreach (var database in databases) { AddDatabase(database); yield return null; } addedDatabases(); m_numActiveCoroutines--; } protected virtual void AddDatabase(DialogueDatabase database) { if (database != null) { if (DialogueDebug.logInfo) Debug.Log("Dialogue System: Adding database " + database.name, this); DialogueManager.AddDatabase(database); } } protected virtual void TryRemoveDatabases(Transform interactor, bool onePerFrame) { if (!m_trying) { m_trying = true; try { if ((condition == null) || condition.IsTrue(interactor)) { RemoveDatabases(onePerFrame); } } finally { m_trying = false; } } } public virtual void RemoveDatabases(bool onePerFrame) { if (onePerFrame) { StartCoroutine(RemoveDatabasesCoroutine()); } else if (gameObject.activeInHierarchy && enabled) { RemoveDatabasesImmediate(); } } protected virtual void RemoveDatabasesImmediate() { foreach (var database in databases) { RemoveDatabase(database); } removedDatabases(); if (once) Destroy(this); } protected virtual IEnumerator RemoveDatabasesCoroutine() { m_numActiveCoroutines++; if (once && m_destroyCoroutine == null) m_destroyCoroutine = StartCoroutine(DestroyCoroutine()); foreach (var database in databases) { RemoveDatabase(database); yield return null; } removedDatabases(); m_numActiveCoroutines--; } protected virtual void RemoveDatabase(DialogueDatabase database) { if (database != null) { if (DialogueDebug.logInfo) Debug.Log("Dialogue System: Removing database " + database.name, this); DialogueManager.RemoveDatabase(database); } } protected virtual IEnumerator DestroyCoroutine() { // If Once is ticked and component both adds & removes databases, we need to wait for both to finish: while (m_numActiveCoroutines > 0) { yield return null; } m_destroyCoroutine = null; Destroy(this); } public virtual void Start() { if (addTrigger == DialogueTriggerEvent.OnStart || removeTrigger == DialogueTriggerEvent.OnStart) { StartCoroutine(StartEndOfFrame()); } } protected virtual IEnumerator StartEndOfFrame() { yield return CoroutineUtility.endOfFrame; if (addTrigger == DialogueTriggerEvent.OnStart) TryAddDatabases(null, onePerFrame); if (removeTrigger == DialogueTriggerEvent.OnStart) TryRemoveDatabases(null, onePerFrame); } public virtual void OnEnable() { if (addTrigger == DialogueTriggerEvent.OnEnable) TryAddDatabases(null, onePerFrame); if (removeTrigger == DialogueTriggerEvent.OnEnable) TryRemoveDatabases(null, onePerFrame); } public virtual void OnDisable() { if (addTrigger == DialogueTriggerEvent.OnDisable) TryAddDatabases(null, false); // Can't run coroutine when disabled. if (removeTrigger == DialogueTriggerEvent.OnDisable) TryRemoveDatabases(null, false); } public virtual void OnDestroy() { if (addTrigger == DialogueTriggerEvent.OnDestroy) TryAddDatabases(null, false); // Can't run coroutine when destroyed. if (removeTrigger == DialogueTriggerEvent.OnDestroy) TryRemoveDatabases(null, false); } public virtual void OnUse(Transform actor) { if (!enabled) return; if (addTrigger == DialogueTriggerEvent.OnUse) TryAddDatabases(actor, onePerFrame); if (removeTrigger == DialogueTriggerEvent.OnUse) TryRemoveDatabases(actor, onePerFrame); } public virtual void OnUse(string message) { if (!enabled) return; if (addTrigger == DialogueTriggerEvent.OnUse) TryAddDatabases(null, onePerFrame); if (removeTrigger == DialogueTriggerEvent.OnUse) TryRemoveDatabases(null, onePerFrame); } public virtual void OnUse() { if (!enabled) return; if (addTrigger == DialogueTriggerEvent.OnUse) TryAddDatabases(null, onePerFrame); if (removeTrigger == DialogueTriggerEvent.OnUse) TryRemoveDatabases(null, onePerFrame); } public virtual void OnTriggerEnter(Collider other) { if (!enabled) return; if (addTrigger == DialogueTriggerEvent.OnTriggerEnter) TryAddDatabases(other.transform, onePerFrame); if (removeTrigger == DialogueTriggerEvent.OnTriggerEnter) TryRemoveDatabases(other.transform, onePerFrame); } public virtual void OnTriggerExit(Collider other) { if (!enabled) return; if (addTrigger == DialogueTriggerEvent.OnTriggerExit) TryAddDatabases(other.transform, onePerFrame); if (removeTrigger == DialogueTriggerEvent.OnTriggerExit) TryRemoveDatabases(other.transform, onePerFrame); } #if USE_PHYSICS2D || !UNITY_2018_1_OR_NEWER public virtual void OnTriggerEnter2D(Collider2D other) { if (!enabled) return; if (addTrigger == DialogueTriggerEvent.OnTriggerEnter) TryAddDatabases(other.transform, onePerFrame); if (removeTrigger == DialogueTriggerEvent.OnTriggerEnter) TryRemoveDatabases(other.transform, onePerFrame); } public virtual void OnTriggerExit2D(Collider2D other) { if (!enabled) return; if (addTrigger == DialogueTriggerEvent.OnTriggerExit) TryAddDatabases(other.transform, onePerFrame); if (removeTrigger == DialogueTriggerEvent.OnTriggerExit) TryRemoveDatabases(other.transform, onePerFrame); } #endif } }