diff --git a/Assets/Scripts/Dialogue/DialogueComponent.cs b/Assets/Scripts/Dialogue/DialogueComponent.cs index 44aba83e..42898fc9 100644 --- a/Assets/Scripts/Dialogue/DialogueComponent.cs +++ b/Assets/Scripts/Dialogue/DialogueComponent.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using Core; using Interactions; using UnityEngine; @@ -16,35 +15,48 @@ namespace Dialogue private RuntimeDialogueNode currentNode; private int currentLineIndex; - private bool isWaitingForCondition; - - // Events - public event Action OnDialogueLineChanged; // speaker name, dialogue text - public event Action OnDialogueCompleted; - public event Action OnConditionStatusChanged; // Whether the condition is now met - - // References to managers - private ItemManager itemManager; - private PuzzleManager puzzleManager; + private bool initialized = false; // Properties public bool IsActive { get; private set; } public bool IsCompleted { get; private set; } public string CurrentSpeakerName => dialogueGraph?.speakerName; - private void Awake() + // Event for UI updates if needed + public event Action OnDialogueChanged; + + private void Start() { - // Auto-injection of managers - itemManager = FindFirstObjectByType(); - puzzleManager = FindFirstObjectByType(); + // Register for global events + if (PuzzleManager.Instance != null) + PuzzleManager.Instance.OnStepCompleted += OnAnyPuzzleStepCompleted; + + if (ItemManager.Instance != null) + { + ItemManager.Instance.OnItemPickedUp += OnAnyItemPickedUp; + ItemManager.Instance.OnCorrectItemSlotted += OnAnyItemSlotted; + } - if (itemManager == null) - Debug.LogWarning("DialogueComponent: ItemManager not found in scene!"); - - if (puzzleManager == null) - Debug.LogWarning("DialogueComponent: PuzzleManager not found in scene!"); + // Auto-start the dialogue + StartDialogue(); } + private void OnDestroy() + { + // Unregister from events + if (PuzzleManager.Instance != null) + PuzzleManager.Instance.OnStepCompleted -= OnAnyPuzzleStepCompleted; + + if (ItemManager.Instance != null) + { + ItemManager.Instance.OnItemPickedUp -= OnAnyItemPickedUp; + ItemManager.Instance.OnCorrectItemSlotted -= OnAnyItemSlotted; + } + } + + /// + /// Start the dialogue from the beginning + /// public void StartDialogue() { if (dialogueGraph == null) @@ -56,45 +68,61 @@ namespace Dialogue // Reset state IsActive = true; IsCompleted = false; - isWaitingForCondition = false; currentLineIndex = 0; + initialized = true; // Set to entry node currentNode = dialogueGraph.GetNodeByID(dialogueGraph.entryNodeID); - // Register for events based on current node - RegisterForEvents(); - - // Try to process the current node + // Process the node ProcessCurrentNode(); } - public bool CanAdvance() + /// + /// Get the current dialogue line and advance to the next line or node if appropriate + /// Each call represents one interaction with the NPC + /// + public string GetCurrentDialogueLine() { - // Can't advance if dialogue is not active or is completed - if (!IsActive || IsCompleted) return false; + // Initialize if needed + if (!initialized) + { + StartDialogue(); + } - // Check if we're waiting for a condition - if (isWaitingForCondition) return false; + if (!IsActive || IsCompleted || currentNode == null || currentNode.dialogueLines.Count == 0) + return string.Empty; - // Check if we have more lines in the current node - if (currentLineIndex < currentNode.dialogueLines.Count - 1 || - (currentNode.loopThroughLines && currentNode.dialogueLines.Count > 0)) - return true; + // Get current line + string currentLine = string.Empty; + if (currentLineIndex >= 0 && currentLineIndex < currentNode.dialogueLines.Count) + { + currentLine = currentNode.dialogueLines[currentLineIndex]; + } - // Check if we have a next node - return !string.IsNullOrEmpty(currentNode.nextNodeID); + // Advance dialogue state for next interaction + AdvanceDialogueState(); + + // Return the current line + return currentLine; } - public void Advance() + /// + /// Advance dialogue state for the next interaction + /// + private void AdvanceDialogueState() { - if (!CanAdvance()) return; - + if (!IsActive || IsCompleted || currentNode == null) + return; + + // If we're on a conditional node, we can't advance past it until condition is met + if (IsWaitingForCondition()) + return; + // If we have more lines in the current node, advance to the next line if (currentLineIndex < currentNode.dialogueLines.Count - 1) { currentLineIndex++; - OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); return; } @@ -102,7 +130,6 @@ namespace Dialogue if (currentNode.loopThroughLines && currentNode.dialogueLines.Count > 0) { currentLineIndex = 0; - OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); return; } @@ -110,102 +137,13 @@ namespace Dialogue MoveToNextNode(); } - public void AdvanceToNextNode() - { - if (!IsActive || IsCompleted) return; - - // Force move to the next node, regardless of current line index - MoveToNextNode(); - } - - public string GetCurrentDialogueLine() - { - if (currentNode == null || currentNode.dialogueLines.Count == 0) return string.Empty; - - if (currentLineIndex < 0 || currentLineIndex >= currentNode.dialogueLines.Count) - return string.Empty; - - return currentNode.dialogueLines[currentLineIndex]; - } - - // Methods to handle dialogue responses for item slots - public void HandleItemSlotInteraction(string slotID, string itemID, bool isForbiddenItem) - { - if (!IsActive || IsCompleted || currentNode == null || - currentNode.nodeType != RuntimeDialogueNodeType.WaitOnSlot) - return; - - // If this is the slot we're waiting for - if (currentNode.slotItemID == slotID) - { - // If correct item is slotted, move to next node - if (itemID == slotID) - { - MoveToNextNode(); - } - // If it's a forbidden item, show the forbidden dialogue - else if (isForbiddenItem && currentNode.forbiddenItemLines.Count > 0) - { - ShowResponseLines(currentNode.forbiddenItemLines, currentNode.loopThroughForbiddenLines); - } - // Otherwise show incorrect item dialogue - else if (currentNode.incorrectItemLines.Count > 0) - { - ShowResponseLines(currentNode.incorrectItemLines, currentNode.loopThroughIncorrectLines); - } - } - } - - private void ShowResponseLines(List lines, bool loopThrough) - { - StartCoroutine(ShowResponseRoutine(lines, loopThrough)); - } - - private IEnumerator ShowResponseRoutine(List lines, bool loopThrough) - { - // Store original node and line index - var originalNode = currentNode; - var originalLineIndex = currentLineIndex; - - // Show each response line - for (int i = 0; i < lines.Count; i++) - { - // Break if dialogue state has changed - if (currentNode != originalNode || !IsActive || IsCompleted) - break; - - OnDialogueLineChanged?.Invoke(CurrentSpeakerName, lines[i]); - - // Wait for input to continue - yield return new WaitForSeconds(2f); // Wait time between lines, can be adjusted - - // If we should loop and we're at the end, start over - if (loopThrough && i == lines.Count - 1) - i = -1; - - // Break after first iteration if not looping - if (!loopThrough && i == 0) - break; - } - - // Restore original dialogue line - if (currentNode == originalNode && IsActive && !IsCompleted) - { - OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); - } - } - private void MoveToNextNode() { - // Unregister from events based on current node - UnregisterFromEvents(); - // If there's no next node, complete the dialogue if (string.IsNullOrEmpty(currentNode.nextNodeID)) { IsActive = false; IsCompleted = true; - OnDialogueCompleted?.Invoke(); return; } @@ -213,9 +151,6 @@ namespace Dialogue currentNode = dialogueGraph.GetNodeByID(currentNode.nextNodeID); currentLineIndex = 0; - // Register for events based on new node - RegisterForEvents(); - // Process the new node ProcessCurrentNode(); } @@ -232,26 +167,40 @@ namespace Dialogue switch (currentNode.nodeType) { case RuntimeDialogueNodeType.Dialogue: - isWaitingForCondition = false; - OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); + // Regular dialogue node, nothing special to do break; case RuntimeDialogueNodeType.WaitOnPuzzleStep: - HandlePuzzleStepNode(); + // Check if the puzzle step is already completed + if (IsPuzzleStepComplete(currentNode.puzzleStepID)) + { + // If it's already complete, move past this node automatically + MoveToNextNode(); + } break; case RuntimeDialogueNodeType.WaitOnPickup: - HandlePickupNode(); + // Check if the item is already picked up + if (IsItemPickedUp(currentNode.pickupItemID)) + { + // If it's already picked up, move past this node automatically + MoveToNextNode(); + } break; case RuntimeDialogueNodeType.WaitOnSlot: - HandleSlotNode(); + // Check if the item is already slotted + if (IsItemSlotted(currentNode.slotItemID)) + { + // If it's already slotted, move past this node automatically + MoveToNextNode(); + } break; case RuntimeDialogueNodeType.End: + // End node, complete the dialogue IsActive = false; IsCompleted = true; - OnDialogueCompleted?.Invoke(); break; default: @@ -260,180 +209,88 @@ namespace Dialogue } } - private void HandlePuzzleStepNode() + // Global event handlers + private void OnAnyPuzzleStepCompleted(PuzzleStepSO step) { - if (puzzleManager == null) - { - Debug.LogError("DialogueComponent: PuzzleManager is required for WaitOnPuzzleStep nodes!"); - MoveToNextNode(); - return; - } - - // Check if the puzzle step is already completed - if (IsPuzzleStepComplete(currentNode.puzzleStepID)) - { - isWaitingForCondition = false; - OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); - return; - } - - // Otherwise, wait for the puzzle step - isWaitingForCondition = true; - OnConditionStatusChanged?.Invoke(false); - OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); - } - - private void HandlePickupNode() - { - if (itemManager == null) - { - Debug.LogError("DialogueComponent: ItemManager is required for WaitOnPickup nodes!"); - MoveToNextNode(); - return; - } - - // Check if the item is already picked up - if (IsItemPickedUp(currentNode.pickupItemID)) - { - isWaitingForCondition = false; - OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); - return; - } - - // Otherwise, wait for the item pickup - isWaitingForCondition = true; - OnConditionStatusChanged?.Invoke(false); - OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); - } - - private void HandleSlotNode() - { - if (itemManager == null) - { - Debug.LogError("DialogueComponent: ItemManager is required for WaitOnSlot nodes!"); - MoveToNextNode(); - return; - } - - // Check if the slot already has the correct item - if (IsItemSlotted(currentNode.slotItemID)) - { - isWaitingForCondition = false; - OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); - MoveToNextNode(); - return; - } - - // Otherwise, wait for the correct item to be slotted - isWaitingForCondition = true; - OnConditionStatusChanged?.Invoke(false); - OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); - } - - private void RegisterForEvents() - { - if (currentNode == null) return; - - switch (currentNode.nodeType) - { - case RuntimeDialogueNodeType.WaitOnPuzzleStep: - if (puzzleManager != null) - puzzleManager.OnStepCompleted += OnStepCompleted; - break; - - case RuntimeDialogueNodeType.WaitOnPickup: - if (itemManager != null) - itemManager.OnItemPickedUp += OnItemPickedUp; - break; - - case RuntimeDialogueNodeType.WaitOnSlot: - if (itemManager != null) - itemManager.OnCorrectItemSlotted += OnCorrectItemSlotted; - break; - } - } - - private void UnregisterFromEvents() - { - if (currentNode == null) return; - - switch (currentNode.nodeType) - { - case RuntimeDialogueNodeType.WaitOnPuzzleStep: - if (puzzleManager != null) - puzzleManager.OnStepCompleted -= OnStepCompleted; - break; - - case RuntimeDialogueNodeType.WaitOnPickup: - if (itemManager != null) - itemManager.OnItemPickedUp -= OnItemPickedUp; - break; - - case RuntimeDialogueNodeType.WaitOnSlot: - if (itemManager != null) - itemManager.OnCorrectItemSlotted -= OnCorrectItemSlotted; - break; - } - } - - // Event handlers for PuzzleManager - private void OnStepCompleted(PuzzleStepSO step) - { - if (!IsActive || !isWaitingForCondition || currentNode.nodeType != RuntimeDialogueNodeType.WaitOnPuzzleStep) + // Only react if we're active and waiting on a puzzle step + if (!IsActive || IsCompleted || currentNode == null || + currentNode.nodeType != RuntimeDialogueNodeType.WaitOnPuzzleStep) return; + // Check if this is the step we're waiting for if (step.stepId == currentNode.puzzleStepID) { - isWaitingForCondition = false; - OnConditionStatusChanged?.Invoke(true); + // Move to next node automatically when condition is met MoveToNextNode(); + OnDialogueChanged?.Invoke(GetCurrentDialogueLine()); } } - // Event handlers for ItemManager - private void OnItemPickedUp(PickupItemData item) + private void OnAnyItemPickedUp(PickupItemData item) { - if (!IsActive || !isWaitingForCondition || currentNode.nodeType != RuntimeDialogueNodeType.WaitOnPickup) + // Only react if we're active and waiting on an item pickup + if (!IsActive || IsCompleted || currentNode == null || + currentNode.nodeType != RuntimeDialogueNodeType.WaitOnPickup) return; + // Check if this is the item we're waiting for if (item.itemId == currentNode.pickupItemID) { - isWaitingForCondition = false; - OnConditionStatusChanged?.Invoke(true); + // Move to next node automatically when condition is met MoveToNextNode(); + OnDialogueChanged?.Invoke(GetCurrentDialogueLine()); } } - private void OnCorrectItemSlotted(PickupItemData slotDefinition, PickupItemData slottedItem) + private void OnAnyItemSlotted(PickupItemData slotDefinition, PickupItemData slottedItem) { - if (!IsActive || !isWaitingForCondition || currentNode.nodeType != RuntimeDialogueNodeType.WaitOnSlot) + // Only react if we're active and waiting on a slot + if (!IsActive || IsCompleted || currentNode == null || + currentNode.nodeType != RuntimeDialogueNodeType.WaitOnSlot) return; + // Check if this is the slot we're waiting for if (slotDefinition.itemId == currentNode.slotItemID) { - isWaitingForCondition = false; - OnConditionStatusChanged?.Invoke(true); + // Move to next node automatically when condition is met MoveToNextNode(); + OnDialogueChanged?.Invoke(GetCurrentDialogueLine()); } } // Helper methods + private bool IsWaitingForCondition() + { + if (currentNode == null) return false; + + switch (currentNode.nodeType) + { + case RuntimeDialogueNodeType.WaitOnPuzzleStep: + return !IsPuzzleStepComplete(currentNode.puzzleStepID); + case RuntimeDialogueNodeType.WaitOnPickup: + return !IsItemPickedUp(currentNode.pickupItemID); + case RuntimeDialogueNodeType.WaitOnSlot: + return !IsItemSlotted(currentNode.slotItemID); + default: + return false; + } + } + private bool IsPuzzleStepComplete(string stepID) { - if (puzzleManager == null) return false; - - // Use the public method instead of accessing the private field - return puzzleManager.IsPuzzleStepCompleted(stepID); + return PuzzleManager.Instance != null && + PuzzleManager.Instance.IsPuzzleStepCompleted(stepID); } private bool IsItemPickedUp(string itemID) { - if (itemManager == null) return false; + if (ItemManager.Instance == null) return false; - // Check if any picked up item has this ID - foreach (var pickup in itemManager.Pickups) + // Check all pickups for the given ID + foreach (var pickup in ItemManager.Instance.Pickups) { - if (pickup.isPickedUp && pickup.itemData != null && pickup.itemData.itemId == itemID) + if (pickup.isPickedUp && pickup.itemData != null && + pickup.itemData.itemId == itemID) { return true; } @@ -443,13 +300,13 @@ namespace Dialogue private bool IsItemSlotted(string slotID) { - if (itemManager == null) return false; + if (ItemManager.Instance == null) return false; - // Check if any slot has the correct item with this ID - foreach (var slot in itemManager.ItemSlots) + // Check if any slot with this ID has the correct item + foreach (var slot in ItemManager.Instance.ItemSlots) { - if (slot.CurrentSlottedState == ItemSlotState.Correct && - slot.itemData != null && slot.itemData.itemId == slotID) + if (slot.itemData != null && slot.itemData.itemId == slotID && + slot.CurrentSlottedState == ItemSlotState.Correct) { return true; }