Files
AppleHillsProduction/Assets/Scripts/Dialogue/DialogueComponent.cs

324 lines
11 KiB
C#
Raw Normal View History

using System;
2025-09-26 15:47:26 +02:00
using System.Collections;
using System.Collections.Generic;
2025-09-26 15:47:26 +02:00
using Core;
using Interactions;
using UnityEngine;
2025-09-26 15:47:26 +02:00
using PuzzleS;
namespace Dialogue
{
2025-09-26 15:47:26 +02:00
[AddComponentMenu("Apple Hills/Dialogue/Dialogue Component")]
public class DialogueComponent : MonoBehaviour
{
2025-09-26 15:47:26 +02:00
[SerializeField] private RuntimeDialogueGraph dialogueGraph;
private RuntimeDialogueNode currentNode;
private int currentLineIndex;
private bool initialized = false;
2025-09-26 15:47:26 +02:00
// Properties
public bool IsActive { get; private set; }
public bool IsCompleted { get; private set; }
public string CurrentSpeakerName => dialogueGraph?.speakerName;
// Event for UI updates if needed
public event Action<string> OnDialogueChanged;
private void Start()
2025-09-26 15:47:26 +02:00
{
// Register for global events
if (PuzzleManager.Instance != null)
PuzzleManager.Instance.OnStepCompleted += OnAnyPuzzleStepCompleted;
if (ItemManager.Instance != null)
{
ItemManager.Instance.OnItemPickedUp += OnAnyItemPickedUp;
ItemManager.Instance.OnCorrectItemSlotted += OnAnyItemSlotted;
}
2025-09-26 15:47:26 +02:00
// Auto-start the dialogue
StartDialogue();
2025-09-26 15:47:26 +02:00
}
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>
2025-09-26 15:47:26 +02:00
public void StartDialogue()
{
if (dialogueGraph == null)
{
Debug.LogError("DialogueComponent: No dialogue graph assigned!");
return;
}
// Reset state
IsActive = true;
IsCompleted = false;
currentLineIndex = 0;
initialized = true;
2025-09-26 15:47:26 +02:00
// Set to entry node
currentNode = dialogueGraph.GetNodeByID(dialogueGraph.entryNodeID);
// Process the node
2025-09-26 15:47:26 +02:00
ProcessCurrentNode();
}
/// <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()
2025-09-26 15:47:26 +02:00
{
// Initialize if needed
if (!initialized)
{
StartDialogue();
}
if (!IsActive || IsCompleted || currentNode == null || currentNode.dialogueLines.Count == 0)
return string.Empty;
2025-09-26 15:47:26 +02:00
// Get current line
string currentLine = string.Empty;
if (currentLineIndex >= 0 && currentLineIndex < currentNode.dialogueLines.Count)
{
currentLine = currentNode.dialogueLines[currentLineIndex];
}
2025-09-26 15:47:26 +02:00
// Advance dialogue state for next interaction
AdvanceDialogueState();
2025-09-26 15:47:26 +02:00
// Return the current line
return currentLine;
2025-09-26 15:47:26 +02:00
}
/// <summary>
/// Advance dialogue state for the next interaction
/// </summary>
private void AdvanceDialogueState()
{
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;
2025-09-26 15:47:26 +02:00
// If we have more lines in the current node, advance to the next line
if (currentLineIndex < currentNode.dialogueLines.Count - 1)
{
2025-09-26 15:47:26 +02:00
currentLineIndex++;
return;
}
2025-09-26 15:47:26 +02:00
// If we should loop through lines, reset the index
if (currentNode.loopThroughLines && currentNode.dialogueLines.Count > 0)
{
2025-09-26 15:47:26 +02:00
currentLineIndex = 0;
return;
}
2025-09-26 15:47:26 +02:00
// Otherwise, move to the next node
MoveToNextNode();
}
2025-09-26 15:47:26 +02:00
private void MoveToNextNode()
{
2025-09-26 15:47:26 +02:00
// If there's no next node, complete the dialogue
if (string.IsNullOrEmpty(currentNode.nextNodeID))
{
2025-09-26 15:47:26 +02:00
IsActive = false;
IsCompleted = true;
return;
}
2025-09-26 15:47:26 +02:00
// Move to the next node
currentNode = dialogueGraph.GetNodeByID(currentNode.nextNodeID);
currentLineIndex = 0;
// Process the new node
ProcessCurrentNode();
}
2025-09-26 15:47:26 +02:00
private void ProcessCurrentNode()
{
if (currentNode == null)
{
Debug.LogError("DialogueComponent: Current node is null!");
return;
}
// Handle different node types
switch (currentNode.nodeType)
{
case RuntimeDialogueNodeType.Dialogue:
// Regular dialogue node, nothing special to do
2025-09-26 15:47:26 +02:00
break;
case RuntimeDialogueNodeType.WaitOnPuzzleStep:
// Check if the puzzle step is already completed
if (IsPuzzleStepComplete(currentNode.puzzleStepID))
{
// If it's already complete, move past this node automatically
MoveToNextNode();
}
2025-09-26 15:47:26 +02:00
break;
case RuntimeDialogueNodeType.WaitOnPickup:
// Check if the item is already picked up
if (IsItemPickedUp(currentNode.pickupItemID))
{
// If it's already picked up, move past this node automatically
MoveToNextNode();
}
2025-09-26 15:47:26 +02:00
break;
case RuntimeDialogueNodeType.WaitOnSlot:
// Check if the item is already slotted
if (IsItemSlotted(currentNode.slotItemID))
{
// If it's already slotted, move past this node automatically
MoveToNextNode();
}
2025-09-26 15:47:26 +02:00
break;
case RuntimeDialogueNodeType.End:
// End node, complete the dialogue
2025-09-26 15:47:26 +02:00
IsActive = false;
IsCompleted = true;
break;
default:
Debug.LogError($"DialogueComponent: Unknown node type {currentNode.nodeType}");
break;
}
}
// Global event handlers
private void OnAnyPuzzleStepCompleted(PuzzleStepSO step)
2025-09-26 15:47:26 +02:00
{
// Only react if we're active and waiting on a puzzle step
if (!IsActive || IsCompleted || currentNode == null ||
currentNode.nodeType != RuntimeDialogueNodeType.WaitOnPuzzleStep)
2025-09-26 15:47:26 +02:00
return;
// Check if this is the step we're waiting for
if (step.stepId == currentNode.puzzleStepID)
2025-09-26 15:47:26 +02:00
{
// Move to next node automatically when condition is met
2025-09-26 15:47:26 +02:00
MoveToNextNode();
OnDialogueChanged?.Invoke(GetCurrentDialogueLine());
2025-09-26 15:47:26 +02:00
}
}
private void OnAnyItemPickedUp(PickupItemData item)
2025-09-26 15:47:26 +02:00
{
// Only react if we're active and waiting on an item pickup
if (!IsActive || IsCompleted || currentNode == null ||
currentNode.nodeType != RuntimeDialogueNodeType.WaitOnPickup)
2025-09-26 15:47:26 +02:00
return;
// Check if this is the item we're waiting for
if (item.itemId == currentNode.pickupItemID)
2025-09-26 15:47:26 +02:00
{
// Move to next node automatically when condition is met
2025-09-26 15:47:26 +02:00
MoveToNextNode();
OnDialogueChanged?.Invoke(GetCurrentDialogueLine());
2025-09-26 15:47:26 +02:00
}
}
private void OnAnyItemSlotted(PickupItemData slotDefinition, PickupItemData slottedItem)
2025-09-26 15:47:26 +02:00
{
// 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)
2025-09-26 15:47:26 +02:00
{
// Move to next node automatically when condition is met
MoveToNextNode();
OnDialogueChanged?.Invoke(GetCurrentDialogueLine());
2025-09-26 15:47:26 +02:00
}
}
// Helper methods
private bool IsWaitingForCondition()
2025-09-26 15:47:26 +02:00
{
if (currentNode == null) return false;
2025-09-26 15:47:26 +02:00
switch (currentNode.nodeType)
{
case RuntimeDialogueNodeType.WaitOnPuzzleStep:
return !IsPuzzleStepComplete(currentNode.puzzleStepID);
2025-09-26 15:47:26 +02:00
case RuntimeDialogueNodeType.WaitOnPickup:
return !IsItemPickedUp(currentNode.pickupItemID);
2025-09-26 15:47:26 +02:00
case RuntimeDialogueNodeType.WaitOnSlot:
return !IsItemSlotted(currentNode.slotItemID);
default:
return false;
2025-09-26 15:47:26 +02:00
}
}
private bool IsPuzzleStepComplete(string stepID)
{
return PuzzleManager.Instance != null &&
PuzzleManager.Instance.IsPuzzleStepCompleted(stepID);
2025-09-26 15:47:26 +02:00
}
private bool IsItemPickedUp(string itemID)
{
if (ItemManager.Instance == null) return false;
2025-09-26 15:47:26 +02:00
// Check all pickups for the given ID
foreach (var pickup in ItemManager.Instance.Pickups)
2025-09-26 15:47:26 +02:00
{
if (pickup.isPickedUp && pickup.itemData != null &&
pickup.itemData.itemId == itemID)
2025-09-26 15:47:26 +02:00
{
return true;
}
}
return false;
}
private bool IsItemSlotted(string slotID)
{
if (ItemManager.Instance == null) return false;
2025-09-26 15:47:26 +02:00
// Check if any slot with this ID has the correct item
foreach (var slot in ItemManager.Instance.ItemSlots)
2025-09-26 15:47:26 +02:00
{
if (slot.itemData != null && slot.itemData.itemId == slotID &&
slot.CurrentSlottedState == ItemSlotState.Correct)
2025-09-26 15:47:26 +02:00
{
return true;
}
}
return false;
}
// Editor functionality
public void SetDialogueGraph(RuntimeDialogueGraph graph)
{
2025-09-26 15:47:26 +02:00
dialogueGraph = graph;
}
}
2025-09-26 15:47:26 +02:00
}