Final touchups to the dialogue component

This commit is contained in:
2025-09-26 16:16:33 +02:00
committed by Michal Pikulski
parent b07eea6aae
commit 4c1be82cf2

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Core; using Core;
using Interactions; using Interactions;
using UnityEngine; using UnityEngine;
@@ -16,35 +15,48 @@ namespace Dialogue
private RuntimeDialogueNode currentNode; private RuntimeDialogueNode currentNode;
private int currentLineIndex; private int currentLineIndex;
private bool isWaitingForCondition; private bool initialized = false;
// Events
public event Action<string, string> OnDialogueLineChanged; // speaker name, dialogue text
public event Action OnDialogueCompleted;
public event Action<bool> OnConditionStatusChanged; // Whether the condition is now met
// References to managers
private ItemManager itemManager;
private PuzzleManager puzzleManager;
// Properties // Properties
public bool IsActive { get; private set; } public bool IsActive { get; private set; }
public bool IsCompleted { get; private set; } public bool IsCompleted { get; private set; }
public string CurrentSpeakerName => dialogueGraph?.speakerName; public string CurrentSpeakerName => dialogueGraph?.speakerName;
private void Awake() // Event for UI updates if needed
public event Action<string> OnDialogueChanged;
private void Start()
{ {
// Auto-injection of managers // Register for global events
itemManager = FindFirstObjectByType<ItemManager>(); if (PuzzleManager.Instance != null)
puzzleManager = FindFirstObjectByType<PuzzleManager>(); PuzzleManager.Instance.OnStepCompleted += OnAnyPuzzleStepCompleted;
if (ItemManager.Instance != null)
{
ItemManager.Instance.OnItemPickedUp += OnAnyItemPickedUp;
ItemManager.Instance.OnCorrectItemSlotted += OnAnyItemSlotted;
}
if (itemManager == null) // Auto-start the dialogue
Debug.LogWarning("DialogueComponent: ItemManager not found in scene!"); StartDialogue();
if (puzzleManager == null)
Debug.LogWarning("DialogueComponent: PuzzleManager not found in scene!");
} }
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;
}
}
/// <summary>
/// Start the dialogue from the beginning
/// </summary>
public void StartDialogue() public void StartDialogue()
{ {
if (dialogueGraph == null) if (dialogueGraph == null)
@@ -56,45 +68,61 @@ namespace Dialogue
// Reset state // Reset state
IsActive = true; IsActive = true;
IsCompleted = false; IsCompleted = false;
isWaitingForCondition = false;
currentLineIndex = 0; currentLineIndex = 0;
initialized = true;
// Set to entry node // Set to entry node
currentNode = dialogueGraph.GetNodeByID(dialogueGraph.entryNodeID); currentNode = dialogueGraph.GetNodeByID(dialogueGraph.entryNodeID);
// Register for events based on current node // Process the node
RegisterForEvents();
// Try to process the current node
ProcessCurrentNode(); ProcessCurrentNode();
} }
public bool CanAdvance() /// <summary>
/// Get the current dialogue line and advance to the next line or node if appropriate
/// Each call represents one interaction with the NPC
/// </summary>
public string GetCurrentDialogueLine()
{ {
// Can't advance if dialogue is not active or is completed // Initialize if needed
if (!IsActive || IsCompleted) return false; if (!initialized)
{
StartDialogue();
}
// Check if we're waiting for a condition if (!IsActive || IsCompleted || currentNode == null || currentNode.dialogueLines.Count == 0)
if (isWaitingForCondition) return false; return string.Empty;
// Check if we have more lines in the current node // Get current line
if (currentLineIndex < currentNode.dialogueLines.Count - 1 || string currentLine = string.Empty;
(currentNode.loopThroughLines && currentNode.dialogueLines.Count > 0)) if (currentLineIndex >= 0 && currentLineIndex < currentNode.dialogueLines.Count)
return true; {
currentLine = currentNode.dialogueLines[currentLineIndex];
}
// Check if we have a next node // Advance dialogue state for next interaction
return !string.IsNullOrEmpty(currentNode.nextNodeID); AdvanceDialogueState();
// Return the current line
return currentLine;
} }
public void Advance() /// <summary>
/// Advance dialogue state for the next interaction
/// </summary>
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 we have more lines in the current node, advance to the next line
if (currentLineIndex < currentNode.dialogueLines.Count - 1) if (currentLineIndex < currentNode.dialogueLines.Count - 1)
{ {
currentLineIndex++; currentLineIndex++;
OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine());
return; return;
} }
@@ -102,7 +130,6 @@ namespace Dialogue
if (currentNode.loopThroughLines && currentNode.dialogueLines.Count > 0) if (currentNode.loopThroughLines && currentNode.dialogueLines.Count > 0)
{ {
currentLineIndex = 0; currentLineIndex = 0;
OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine());
return; return;
} }
@@ -110,102 +137,13 @@ namespace Dialogue
MoveToNextNode(); 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<string> lines, bool loopThrough)
{
StartCoroutine(ShowResponseRoutine(lines, loopThrough));
}
private IEnumerator ShowResponseRoutine(List<string> 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() private void MoveToNextNode()
{ {
// Unregister from events based on current node
UnregisterFromEvents();
// If there's no next node, complete the dialogue // If there's no next node, complete the dialogue
if (string.IsNullOrEmpty(currentNode.nextNodeID)) if (string.IsNullOrEmpty(currentNode.nextNodeID))
{ {
IsActive = false; IsActive = false;
IsCompleted = true; IsCompleted = true;
OnDialogueCompleted?.Invoke();
return; return;
} }
@@ -213,9 +151,6 @@ namespace Dialogue
currentNode = dialogueGraph.GetNodeByID(currentNode.nextNodeID); currentNode = dialogueGraph.GetNodeByID(currentNode.nextNodeID);
currentLineIndex = 0; currentLineIndex = 0;
// Register for events based on new node
RegisterForEvents();
// Process the new node // Process the new node
ProcessCurrentNode(); ProcessCurrentNode();
} }
@@ -232,26 +167,40 @@ namespace Dialogue
switch (currentNode.nodeType) switch (currentNode.nodeType)
{ {
case RuntimeDialogueNodeType.Dialogue: case RuntimeDialogueNodeType.Dialogue:
isWaitingForCondition = false; // Regular dialogue node, nothing special to do
OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine());
break; break;
case RuntimeDialogueNodeType.WaitOnPuzzleStep: 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; break;
case RuntimeDialogueNodeType.WaitOnPickup: 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; break;
case RuntimeDialogueNodeType.WaitOnSlot: 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; break;
case RuntimeDialogueNodeType.End: case RuntimeDialogueNodeType.End:
// End node, complete the dialogue
IsActive = false; IsActive = false;
IsCompleted = true; IsCompleted = true;
OnDialogueCompleted?.Invoke();
break; break;
default: default:
@@ -260,180 +209,88 @@ namespace Dialogue
} }
} }
private void HandlePuzzleStepNode() // Global event handlers
private void OnAnyPuzzleStepCompleted(PuzzleStepSO step)
{ {
if (puzzleManager == null) // Only react if we're active and waiting on a puzzle step
{ if (!IsActive || IsCompleted || currentNode == null ||
Debug.LogError("DialogueComponent: PuzzleManager is required for WaitOnPuzzleStep nodes!"); currentNode.nodeType != RuntimeDialogueNodeType.WaitOnPuzzleStep)
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)
return; return;
// Check if this is the step we're waiting for
if (step.stepId == currentNode.puzzleStepID) if (step.stepId == currentNode.puzzleStepID)
{ {
isWaitingForCondition = false; // Move to next node automatically when condition is met
OnConditionStatusChanged?.Invoke(true);
MoveToNextNode(); MoveToNextNode();
OnDialogueChanged?.Invoke(GetCurrentDialogueLine());
} }
} }
// Event handlers for ItemManager private void OnAnyItemPickedUp(PickupItemData item)
private void OnItemPickedUp(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; return;
// Check if this is the item we're waiting for
if (item.itemId == currentNode.pickupItemID) if (item.itemId == currentNode.pickupItemID)
{ {
isWaitingForCondition = false; // Move to next node automatically when condition is met
OnConditionStatusChanged?.Invoke(true);
MoveToNextNode(); 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; return;
// Check if this is the slot we're waiting for
if (slotDefinition.itemId == currentNode.slotItemID) if (slotDefinition.itemId == currentNode.slotItemID)
{ {
isWaitingForCondition = false; // Move to next node automatically when condition is met
OnConditionStatusChanged?.Invoke(true);
MoveToNextNode(); MoveToNextNode();
OnDialogueChanged?.Invoke(GetCurrentDialogueLine());
} }
} }
// Helper methods // 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) private bool IsPuzzleStepComplete(string stepID)
{ {
if (puzzleManager == null) return false; return PuzzleManager.Instance != null &&
PuzzleManager.Instance.IsPuzzleStepCompleted(stepID);
// Use the public method instead of accessing the private field
return puzzleManager.IsPuzzleStepCompleted(stepID);
} }
private bool IsItemPickedUp(string itemID) 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 // Check all pickups for the given ID
foreach (var pickup in itemManager.Pickups) 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; return true;
} }
@@ -443,13 +300,13 @@ namespace Dialogue
private bool IsItemSlotted(string slotID) 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 // Check if any slot with this ID has the correct item
foreach (var slot in itemManager.ItemSlots) foreach (var slot in ItemManager.Instance.ItemSlots)
{ {
if (slot.CurrentSlottedState == ItemSlotState.Correct && if (slot.itemData != null && slot.itemData.itemId == slotID &&
slot.itemData != null && slot.itemData.itemId == slotID) slot.CurrentSlottedState == ItemSlotState.Correct)
{ {
return true; return true;
} }