From 19531f04bc30668c7941b4c74aede2c223a20e09 Mon Sep 17 00:00:00 2001 From: marcin Date: Fri, 5 Dec 2025 17:45:13 +0100 Subject: [PATCH] fixed barks/laments pauses while in conversation --- .../LevelsData/LevelLoaderData.asset | 2 +- Assets/Scripts/Utils/BarkPlayer.cs | 286 ++++++++---------- 2 files changed, 135 insertions(+), 153 deletions(-) diff --git a/Assets/ScriptableObjects/LevelsData/LevelLoaderData.asset b/Assets/ScriptableObjects/LevelsData/LevelLoaderData.asset index 52c2e4dda..e8e4e7547 100644 --- a/Assets/ScriptableObjects/LevelsData/LevelLoaderData.asset +++ b/Assets/ScriptableObjects/LevelsData/LevelLoaderData.asset @@ -21,7 +21,7 @@ MonoBehaviour: LevelNameToDisplay: Test Hub DialogueVariableName: LoadSceneMode: 0 - - LevelName: Wasteland + - LevelName: Wasteland_Arenas LevelNameToDisplay: Wasteland DialogueVariableName: LoadSceneMode: 0 diff --git a/Assets/Scripts/Utils/BarkPlayer.cs b/Assets/Scripts/Utils/BarkPlayer.cs index 6a29a97e1..326196aa3 100644 --- a/Assets/Scripts/Utils/BarkPlayer.cs +++ b/Assets/Scripts/Utils/BarkPlayer.cs @@ -2,11 +2,12 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using PixelCrushers; // For Saver -using Sirenix.OdinInspector; // For ReadOnly/Button attributes (Optional: remove if not using Odin) +using PixelCrushers.DialogueSystem; // REQUIRED: Added for DialogueManager events +using Sirenix.OdinInspector; using UnityEngine; using Random = UnityEngine.Random; -namespace Beyond // Ensure this namespace matches your project +namespace Beyond { [RequireComponent(typeof(Collider))] public class BarkPlayer : Saver @@ -28,6 +29,10 @@ namespace Beyond // Ensure this namespace matches your project [Tooltip("Play barks within each entry in a random order (shuffled when the entry starts).")] public bool shuffleOrder = false; + [Header("Dialogue Integration")] // --- NEW SECTION --- + [Tooltip("How long to wait after a dialogue ends before resuming barks.")] + public float resumeDelayAfterDialogue = 2.0f; + [Header("Trigger Settings")] [Tooltip("Layers that can activate this bark trigger.")] public LayerMask triggeringLayers; @@ -36,30 +41,30 @@ namespace Beyond // Ensure this namespace matches your project [Tooltip("If true, the BarkPlayer will be active automatically when the game starts or when loaded without existing save data for it.")] public bool startAutomaticallyOnLoad = false; - // Runtime state fields remain private but visible for debugging if needed [Header("Runtime State (Read Only)")] - [SerializeField, ReadOnly] // Use Sirenix ReadOnly or remove if not using Odin - private bool _runtime_IsStarted = false; // Tracks the current operational state + [SerializeField, ReadOnly] + private bool _runtime_IsStarted = false; [SerializeField, ReadOnly] - private int currentEntryArrayIndex = 0; // Index for barkManagerEntryIndices array + private int currentEntryArrayIndex = 0; [SerializeField, ReadOnly] - private int nextBarkSequenceIndex = 0; // Index for playbackOrder (within current entry) + private int nextBarkSequenceIndex = 0; [SerializeField, ReadOnly] - private bool isTriggeringObjectInside = false; // Is a valid object currently inside? + private bool isTriggeringObjectInside = false; #endregion #region Internal Variables - private List playbackOrder; // Stores the bark order for the CURRENTLY active entry + private List playbackOrder; private bool isInitialized = false; private Collider triggerCollider; - private BarkManager barkManager; // Cached reference + private BarkManager barkManager; private Coroutine currentPlaybackCoroutine = null; - private Transform currentTriggererTransform = null; // Transform of the object inside + private Coroutine resumeCoroutine = null; // To track the resume timer + private Transform currentTriggererTransform = null; #endregion @@ -68,10 +73,10 @@ namespace Beyond // Ensure this namespace matches your project [System.Serializable] public class BarkPlayerData { - public bool savedIsStarted; // Tracks the saved state from _runtime_IsStarted + public bool savedIsStarted; public int savedCurrentEntryArrayIndex; public int savedNextBarkSequenceIndex; - public bool wasShuffled; // Tracks shuffle setting at time of save + public bool wasShuffled; } private BarkPlayerData m_saveData = new BarkPlayerData(); @@ -87,22 +92,35 @@ namespace Beyond // Ensure this namespace matches your project triggerCollider = GetComponent(); if (!triggerCollider.isTrigger) { - Debug.LogWarning($"BarkPlayer on {gameObject.name}: Collider was not set to 'Is Trigger'. Forcing it.", this); triggerCollider.isTrigger = true; } if (triggeringLayers.value == 0) { - int playerLayer = LayerMask.NameToLayer("Player"); - if (playerLayer != -1) - { - triggeringLayers = LayerMask.GetMask("Player"); - Debug.LogWarning($"BarkPlayer on {gameObject.name}: Triggering Layers not set in inspector, defaulting to 'Player' layer.", this); - } - else - { - Debug.LogError($"BarkPlayer ({gameObject.name}): Triggering Layers is not set in the inspector, and the default 'Player' layer was not found. This trigger will likely not activate.", this); - } + triggeringLayers = LayerMask.GetMask("Player"); + } + } + + public override void OnEnable() + { + base.OnEnable(); + // Subscribe to global Dialogue System events + // We use the C# event delegates instead of the component message system for reliability + if (DialogueManager.instance != null) + { + DialogueManager.instance.conversationStarted += OnDialogueStarted; + DialogueManager.instance.conversationEnded += OnDialogueEnded; + } + } + + public override void OnDisable() + { + base.OnDisable(); + // Unsubscribe to prevent memory leaks + if (DialogueManager.instance != null) + { + DialogueManager.instance.conversationStarted -= OnDialogueStarted; + DialogueManager.instance.conversationEnded -= OnDialogueEnded; } } @@ -111,7 +129,6 @@ namespace Beyond // Ensure this namespace matches your project barkManager = BarkManager.Instance; if (barkManager == null) { - Debug.LogError($"BarkPlayer ({gameObject.name}): Could not find BarkManager instance! Disabling component.", this); enabled = false; return; } @@ -120,22 +137,12 @@ namespace Beyond // Ensure this namespace matches your project if (barkManagerEntryIndices == null || barkManagerEntryIndices.Length == 0) { - Debug.LogWarning($"BarkPlayer ({gameObject.name}): No Bark Manager Entry Indices provided. Disabling.", this); enabled = false; return; } - for (int i = 0; i < barkManagerEntryIndices.Length; i++) - { - if (barkManagerEntryIndices[i] < 0 || barkManagerEntryIndices[i] >= barkManager.m_barks.Length) - { - Debug.LogError($"BarkPlayer ({gameObject.name}): Invalid Bark Manager Entry Index {barkManagerEntryIndices[i]} at array position {i}. Max index is {barkManager.m_barks.Length - 1}. Disabling.", this); - enabled = false; - return; - } - } isInitialized = true; - CheckForAutoStartIfInside(); // Check immediately if conditions are met + CheckForAutoStartIfInside(); } private void OnDrawGizmos() @@ -151,73 +158,91 @@ namespace Beyond // Ensure this namespace matches your project #endregion + #region Dialogue System Event Handlers (New Logic) + + // Called automatically by Dialogue System whenever ANY conversation starts + private void OnDialogueStarted(Transform actor) + { + if (resumeCoroutine != null) StopCoroutine(resumeCoroutine); + + // Halt the bark loop immediately + if (currentPlaybackCoroutine != null) + { + Debug.Log($"BarkPlayer ({gameObject.name}): Dialogue started. Halting barks.", this); + StopCoroutine(currentPlaybackCoroutine); + currentPlaybackCoroutine = null; + } + } + + // Called automatically by Dialogue System whenever ANY conversation ends + private void OnDialogueEnded(Transform actor) + { + // Don't resume if we aren't supposed to be running or nobody is inside + if (_runtime_IsStarted && isTriggeringObjectInside && enabled) + { + Debug.Log($"BarkPlayer ({gameObject.name}): Dialogue ended. Resuming in {resumeDelayAfterDialogue}s.", this); + resumeCoroutine = StartCoroutine(ResumeAfterDialogueDelay()); + } + } + + private IEnumerator ResumeAfterDialogueDelay() + { + yield return new WaitForSeconds(resumeDelayAfterDialogue); + + // Check conditions again in case player left during the wait + CheckForAutoStartIfInside(); + resumeCoroutine = null; + } + + #endregion + #region Trigger Handling private void OnTriggerEnter(Collider other) { - // Only ignore if not initialized or component is disabled if (!isInitialized || !enabled) return; - // --- Layer Check --- int otherLayer = other.gameObject.layer; - if ((triggeringLayers.value & (1 << otherLayer)) == 0) - { - return; // Layer not in mask, ignore this trigger event - } + if ((triggeringLayers.value & (1 << otherLayer)) == 0) return; - // --- Check if already processing an object --- - // This prevents multiple triggering objects from interfering. Only the first one in matters. - if (isTriggeringObjectInside && other.transform != currentTriggererTransform) + if (isTriggeringObjectInside && other.transform != currentTriggererTransform) return; + if (isTriggeringObjectInside) return; + + isTriggeringObjectInside = true; + currentTriggererTransform = other.transform; + + // Added check: Don't start barking if a conversation is ALREADY active + if (DialogueManager.isConversationActive) { - // If another object enters while one is already tracked, ignore the new one. - // If it's the *same* object re-entering (somehow), this check might be too strict, - // but standard OnTriggerEnter/Exit should handle one object at a time. + Debug.Log($"BarkPlayer ({gameObject.name}): Trigger entered, but dialogue is active. Waiting for dialogue end.", this); return; } - if (isTriggeringObjectInside) return; // Simplified: if already tracking one, ignore others. - // --- Valid Trigger - Mark presence --- - // We set these REGARDLESS of _runtime_IsStarted so state is correct when StartPlayer() is called - Debug.Log($"BarkPlayer ({gameObject.name}): Triggering object '{other.name}' entered (tracking presence).", this); - isTriggeringObjectInside = true; - currentTriggererTransform = other.transform; // Store the transform for barking - - // --- Start Playback Coroutine if player is started and not already running --- - // This handles the case where the player enters *after* StartPlayer() has been called if (_runtime_IsStarted && currentPlaybackCoroutine == null) { - Debug.Log($"BarkPlayer ({gameObject.name}): Player is active. Starting playback loop due to new entry.", this); - // Apply potentially loaded/saved array index before starting loop currentEntryArrayIndex = Mathf.Clamp(m_saveData.savedCurrentEntryArrayIndex, 0, Mathf.Max(0, barkManagerEntryIndices.Length - 1)); - // nextBarkSequenceIndex will be set by PrepareEntrySequence inside the coroutine currentPlaybackCoroutine = StartCoroutine(ContinuousPlaybackLoop()); } - // If !_runtime_IsStarted, isTriggeringObjectInside is now true, - // and CheckForAutoStartIfInside() (called by StartPlayer()) will pick it up. } - private void OnTriggerExit(Collider other) { if (!isInitialized || !enabled) return; int otherLayer = other.gameObject.layer; - if ((triggeringLayers.value & (1 << otherLayer)) == 0) - { - return; - } + if ((triggeringLayers.value & (1 << otherLayer)) == 0) return; if (other.transform == currentTriggererTransform) { - Debug.Log($"BarkPlayer ({gameObject.name}): Tracked triggering object '{other.name}' exited.", this); isTriggeringObjectInside = false; currentTriggererTransform = null; + if (resumeCoroutine != null) StopCoroutine(resumeCoroutine); + if (currentPlaybackCoroutine != null) { StopCoroutine(currentPlaybackCoroutine); currentPlaybackCoroutine = null; - Debug.Log($"BarkPlayer ({gameObject.name}): Stopping playback loop due to exit.", this); } } } @@ -233,7 +258,6 @@ namespace Beyond // Ensure this namespace matches your project int barkCount = barkManager.GetBarkCountInEntry(entryIndexToPrepare); if (barkCount <= 0) { - Debug.LogWarning($"BarkPlayer ({gameObject.name}): BarkEntry {entryIndexToPrepare} (from array index {currentEntryArrayIndex}) has no barks. Skipping entry.", this); playbackOrder = new List(); nextBarkSequenceIndex = 0; return false; @@ -249,9 +273,8 @@ namespace Beyond // Ensure this namespace matches your project } } - // Apply saved index ONLY if the loaded entry array index matches the current one - // AND if the shuffle setting at time of save matches current. - // If shuffle setting changed, it's safer to restart the entry. + // If we are resuming mid-sequence (because coroutine was stopped/restarted), + // we try to respect the saved index, otherwise reset to 0. if (currentEntryArrayIndex == m_saveData.savedCurrentEntryArrayIndex && shuffleOrder == m_saveData.wasShuffled) { nextBarkSequenceIndex = Mathf.Clamp(m_saveData.savedNextBarkSequenceIndex, 0, playbackOrder.Count); @@ -260,36 +283,26 @@ namespace Beyond // Ensure this namespace matches your project { nextBarkSequenceIndex = 0; } - // Clear the saved next bark index after applying it or deciding not to, - // so it's not incorrectly reused if we loop back to this entry without saving/loading. - // m_saveData.savedNextBarkSequenceIndex = 0; // Reconsider this - only clear on actual load/new entry start. return true; } private IEnumerator ContinuousPlaybackLoop() { - Debug.Log($"BarkPlayer ({gameObject.name}): Starting playback loop.", this); - if (barkManagerEntryIndices == null || barkManagerEntryIndices.Length == 0) { - Debug.LogError($"BarkPlayer ({gameObject.name}): Cannot start loop, entry indices array is empty or null.", this); currentPlaybackCoroutine = null; yield break; } - // Outer loop: Continues as long as a valid triggering object is inside AND player is started - while (isTriggeringObjectInside && currentTriggererTransform != null && _runtime_IsStarted) // Added _runtime_IsStarted check here too + while (isTriggeringObjectInside && currentTriggererTransform != null && _runtime_IsStarted) { + // Safety check: if dialogue started while we were in a yield, break immediately + if (DialogueManager.isConversationActive) break; + if (currentEntryArrayIndex >= barkManagerEntryIndices.Length) { currentEntryArrayIndex = 0; - if (barkManagerEntryIndices.Length == 0) - { - Debug.LogError($"BarkPlayer ({gameObject.name}): Entry indices array became empty during loop?", this); - currentPlaybackCoroutine = null; // Ensure it's cleared - yield break; - } } int currentManagerEntryIndex = barkManagerEntryIndices[currentEntryArrayIndex]; @@ -298,25 +311,30 @@ namespace Beyond // Ensure this namespace matches your project if (!canPlayEntry) { currentEntryArrayIndex++; - m_saveData.savedNextBarkSequenceIndex = 0; // Ensure next time this entry is loaded (if looping), it starts fresh + m_saveData.savedNextBarkSequenceIndex = 0; yield return null; continue; } while (nextBarkSequenceIndex < playbackOrder.Count && isTriggeringObjectInside && currentTriggererTransform != null && _runtime_IsStarted) { - while (barkManager.IsPlaying && isTriggeringObjectInside && _runtime_IsStarted) + // Wait for any other barks (or dialogue) to finish + while ((barkManager.IsPlaying || DialogueManager.isConversationActive) && isTriggeringObjectInside && _runtime_IsStarted) { + // If dialogue becomes active during this wait, the OnDialogueStarted event + // will kill this coroutine, so this check is just a backup. yield return null; } if (!isTriggeringObjectInside || currentTriggererTransform == null || !_runtime_IsStarted) break; int barkIndexToPlay = playbackOrder[nextBarkSequenceIndex]; - Debug.Log($"BarkPlayer ({gameObject.name}): Playing Bark {nextBarkSequenceIndex + 1}/{playbackOrder.Count} (Actual Index: {barkIndexToPlay}) from Entry {currentManagerEntryIndex}.", this); - + AudioClip playedClip = barkManager.PlayBark(currentManagerEntryIndex, currentTriggererTransform, barkIndexToPlay); nextBarkSequenceIndex++; + // Update Save Data immediately so if we stop mid-sequence we know where we were + m_saveData.savedNextBarkSequenceIndex = nextBarkSequenceIndex; + float waitTime = delayBetweenBarks; if (playedClip != null) { waitTime += Mathf.Max(0f, playedClip.length); } @@ -325,6 +343,7 @@ namespace Beyond // Ensure this namespace matches your project float timer = 0f; while (timer < waitTime && isTriggeringObjectInside && currentTriggererTransform != null && _runtime_IsStarted) { + if (DialogueManager.isConversationActive) break; // Break timer on dialogue timer += Time.deltaTime; yield return null; } @@ -334,39 +353,41 @@ namespace Beyond // Ensure this namespace matches your project yield return null; } + if (DialogueManager.isConversationActive) break; // Exit loop if dialogue started if (!isTriggeringObjectInside || currentTriggererTransform == null || !_runtime_IsStarted) break; } + // If we broke out due to dialogue, exit the outer loop properly + if (DialogueManager.isConversationActive) break; + if (isTriggeringObjectInside && currentTriggererTransform != null && _runtime_IsStarted) { - Debug.Log($"BarkPlayer ({gameObject.name}): Finished sequence for Entry {currentManagerEntryIndex}.", this); currentEntryArrayIndex++; - m_saveData.savedNextBarkSequenceIndex = 0; // Reset for next entry + m_saveData.savedNextBarkSequenceIndex = 0; + // Reset save data sequence index for next entry + m_saveData.savedCurrentEntryArrayIndex = currentEntryArrayIndex; if (currentEntryArrayIndex >= barkManagerEntryIndices.Length) { - Debug.Log($"BarkPlayer ({gameObject.name}): Finished all entries in the array. Looping back.", this); currentEntryArrayIndex = 0; } if (delayBetweenEntries > 0) { - Debug.Log($"BarkPlayer ({gameObject.name}): Waiting {delayBetweenEntries}s before next entry.", this); float timer = 0f; while (timer < delayBetweenEntries && isTriggeringObjectInside && currentTriggererTransform != null && _runtime_IsStarted) { + if (DialogueManager.isConversationActive) break; timer += Time.deltaTime; yield return null; } + if (DialogueManager.isConversationActive) break; if (!isTriggeringObjectInside || currentTriggererTransform == null || !_runtime_IsStarted) break; } } } - Debug.Log($"BarkPlayer ({gameObject.name}): Playback loop finished.", this); - // Only clear if this instance of the coroutine is the one stopping. - // If StopPlayer() was called, it would have already nulled currentPlaybackCoroutine. - if (currentPlaybackCoroutine == this.currentPlaybackCoroutine) // Check if this is still the active coroutine + if (currentPlaybackCoroutine == this.currentPlaybackCoroutine) { currentPlaybackCoroutine = null; } @@ -381,23 +402,12 @@ namespace Beyond // Ensure this namespace matches your project { if (!_runtime_IsStarted) { - Debug.Log($"BarkPlayer ({gameObject.name}): Player Runtime Explicitly Started.", this); _runtime_IsStarted = true; - // Clear any previously saved next bark index when explicitly starting, - // unless shuffle settings are the same and we are on the same entry. - // This is tricky. For now, CheckForAutoStartIfInside will rely on PrepareEntrySequence - // to correctly determine if it should resume. - // m_saveData.savedNextBarkSequenceIndex = 0; // Maybe too aggressive. - if (isInitialized) { CheckForAutoStartIfInside(); } } - else - { - Debug.Log($"BarkPlayer ({gameObject.name}): Player Runtime was already started.", this); - } } [Button] @@ -405,53 +415,38 @@ namespace Beyond // Ensure this namespace matches your project { if (_runtime_IsStarted) { - Debug.Log($"BarkPlayer ({gameObject.name}): Player Runtime Explicitly Stopped.", this); _runtime_IsStarted = false; - + if (resumeCoroutine != null) StopCoroutine(resumeCoroutine); if (currentPlaybackCoroutine != null) { StopCoroutine(currentPlaybackCoroutine); currentPlaybackCoroutine = null; - Debug.Log($"BarkPlayer ({gameObject.name}): Stopping active playback loop due to StopPlayer call.", this); } } } private void CheckForAutoStartIfInside() { - // This is called from Start() and StartPlayer() if (_runtime_IsStarted && isTriggeringObjectInside && currentTriggererTransform != null && currentPlaybackCoroutine == null && isInitialized) { - Debug.Log($"BarkPlayer ({gameObject.name}): Conditions met for auto-start. Object inside, player started, no active coroutine. Starting playback loop.", this); - // Ensure currentEntryArrayIndex is correctly set from save data before starting - // (it should already be from Start() or ApplyData(), but good to be sure) - currentEntryArrayIndex = Mathf.Clamp(m_saveData.savedCurrentEntryArrayIndex, 0, Mathf.Max(0, barkManagerEntryIndices.Length - 1)); - // nextBarkSequenceIndex will be determined by PrepareEntrySequence - currentPlaybackCoroutine = StartCoroutine(ContinuousPlaybackLoop()); + // Double check dialogue isn't running before starting + if (!DialogueManager.isConversationActive) + { + currentEntryArrayIndex = Mathf.Clamp(m_saveData.savedCurrentEntryArrayIndex, 0, Mathf.Max(0, barkManagerEntryIndices.Length - 1)); + currentPlaybackCoroutine = StartCoroutine(ContinuousPlaybackLoop()); + } } } #endregion - #region Save System Integration (Pixel Crushers Saver) + #region Save System Integration public override string RecordData() { m_saveData.savedIsStarted = this._runtime_IsStarted; m_saveData.savedCurrentEntryArrayIndex = this.currentEntryArrayIndex; - // Only save nextBarkSequenceIndex if a coroutine is running and we are "mid-sequence" - // Otherwise, it should be 0 for the next entry. - if (currentPlaybackCoroutine != null && nextBarkSequenceIndex > 0 && nextBarkSequenceIndex < (playbackOrder?.Count ?? 0)) - { - m_saveData.savedNextBarkSequenceIndex = this.nextBarkSequenceIndex; - } - else - { - // If not actively playing a sequence or at the start/end, save 0. - // This means when loading, it will either start the currentEntryArrayIndex from the beginning, - // or if currentEntryArrayIndex was incremented after an entry finished, it will start the *new* entry from beginning. - m_saveData.savedNextBarkSequenceIndex = 0; - } + m_saveData.savedNextBarkSequenceIndex = this.nextBarkSequenceIndex; m_saveData.wasShuffled = this.shuffleOrder; return SaveSystem.Serialize(m_saveData); @@ -462,12 +457,11 @@ namespace Beyond // Ensure this namespace matches your project if (string.IsNullOrEmpty(s)) { _runtime_IsStarted = startAutomaticallyOnLoad; - m_saveData = new BarkPlayerData(); // Ensure fresh save data container - m_saveData.savedIsStarted = _runtime_IsStarted; // Sync with runtime + m_saveData = new BarkPlayerData(); + m_saveData.savedIsStarted = _runtime_IsStarted; m_saveData.savedCurrentEntryArrayIndex = 0; m_saveData.savedNextBarkSequenceIndex = 0; - m_saveData.wasShuffled = shuffleOrder; // Use current inspector setting - Debug.Log($"BarkPlayer ({gameObject.name}): No save data, using startAutomaticallyOnLoad ({_runtime_IsStarted}).", this); + m_saveData.wasShuffled = shuffleOrder; return; } @@ -476,22 +470,10 @@ namespace Beyond // Ensure this namespace matches your project { m_saveData = loadedData; this._runtime_IsStarted = m_saveData.savedIsStarted; - // currentEntryArrayIndex and nextBarkSequenceIndex will be used by Start() and PrepareEntrySequence() - Debug.Log($"BarkPlayer ({gameObject.name}): Applied loaded data. Started={_runtime_IsStarted}, EntryIdx={m_saveData.savedCurrentEntryArrayIndex}, BarkIdx={m_saveData.savedNextBarkSequenceIndex}, WasShuffled={m_saveData.wasShuffled}.", this); - } - else - { - Debug.LogError($"BarkPlayer ({gameObject.name}): Failed to deserialize save data. Using defaults.", this); - _runtime_IsStarted = startAutomaticallyOnLoad; - m_saveData = new BarkPlayerData(); - m_saveData.savedIsStarted = _runtime_IsStarted; - m_saveData.savedCurrentEntryArrayIndex = 0; - m_saveData.savedNextBarkSequenceIndex = 0; - m_saveData.wasShuffled = shuffleOrder; + this.currentEntryArrayIndex = m_saveData.savedCurrentEntryArrayIndex; + this.nextBarkSequenceIndex = m_saveData.savedNextBarkSequenceIndex; } - // After applying data, if loaded state is 'stopped', ensure coroutine is also stopped. - // CheckForAutoStartIfInside in Start() will handle restarting if needed. if (!this._runtime_IsStarted && currentPlaybackCoroutine != null) { StopCoroutine(currentPlaybackCoroutine);