using UnityEngine; using UnityEditor.AssetImporters; using Unity.GraphToolkit.Editor; using System; using System.Collections.Generic; using System.Linq; using Dialogue; using PuzzleS; namespace Editor.Dialogue { [ScriptedImporter(1, DialogueGraph.AssetExtension)] public class DialogueGraphImporter : ScriptedImporter { public override void OnImportAsset(AssetImportContext ctx) { DialogueGraph editorGraph = GraphDatabase.LoadGraphForImporter(ctx.assetPath); RuntimeDialogueGraph runtimeGraph = ScriptableObject.CreateInstance(); var nodeIDMap = new Dictionary(); // Generate stable GUIDs for each node foreach (var node in editorGraph.GetNodes()) { nodeIDMap[node] = Guid.NewGuid().ToString(); } // Process start node to get entry point and speaker name var startNode = editorGraph.GetNodes().OfType().FirstOrDefault(); if (startNode != null) { var entryPoint = startNode.GetOutputPorts().FirstOrDefault()?.firstConnectedPort; if (entryPoint != null) { runtimeGraph.entryNodeID = nodeIDMap[entryPoint.GetNode()]; } runtimeGraph.speakerName = GetPortValue(startNode.GetInputPortByName("SpeakerName")); } // Process each node in the graph foreach (var iNode in editorGraph.GetNodes()) { if (iNode is StartNode || iNode is IVariableNode) continue; var runtimeNode = new RuntimeDialogueNode{ nodeID = nodeIDMap[iNode] }; // Process node based on its type if (iNode is DialogueNode dialogueNode) { // Process base dialogue node properties (for all node types) ProcessDialogueNodeBase(dialogueNode, runtimeNode); if (iNode is WaitOnPuzzleStep puzzleNode) { ProcessPuzzleNode(puzzleNode, runtimeNode); } else if (iNode is WaitOnPickup pickupNode) { ProcessPickupNode(pickupNode, runtimeNode); } else if (iNode is WaitOnSlot slotNode) { ProcessSlotNode(slotNode, runtimeNode); } else if (iNode is WaitOnCombination combinationNode) { ProcessCombinationNode(combinationNode, runtimeNode); } } else if (iNode is EndNode) { runtimeNode.nodeType = RuntimeDialogueNodeType.End; // End nodes don't have next nodes, so we don't need to look for "out" ports } // Get next node connection (skip for EndNodes as they don't have out ports) if (!(iNode is EndNode)) { // Check if the node has output ports before trying to get one by name var outputPorts = iNode.GetOutputPorts(); if (outputPorts != null && outputPorts.Any()) { var outPort = outputPorts.FirstOrDefault(p => p.name == "out"); if (outPort != null && outPort.firstConnectedPort != null) { runtimeNode.nextNodeID = nodeIDMap[outPort.firstConnectedPort.GetNode()]; } } } // Add node to runtime graph runtimeGraph.allNodes.Add(runtimeNode); } ctx.AddObjectToAsset("RuntimeData", runtimeGraph); ctx.SetMainObject(runtimeGraph); Debug.Log($"Imported DialogueGraph with {runtimeGraph.allNodes.Count} nodes."); } private void ProcessDialogueNodeBase(DialogueNode node, RuntimeDialogueNode runtimeNode) { // Set default node type runtimeNode.nodeType = RuntimeDialogueNodeType.Dialogue; // Get line type and count options var lineTypeOption = node.GetNodeOptionByName("DialogueLineType"); lineTypeOption.TryGetValue(out var lineType); var lineCountOption = node.GetNodeOptionByName("NoLines"); lineCountOption.TryGetValue(out var lineCount); // Process dialogue content if (lineType == DialogueType.SayMultipleLines) { for (var i = 0; i < lineCount; i++) { var contentPort = node.GetInputPortByName($"DefaultDialogueContent{i + 1}"); var contentValue = GetPortValue(contentPort); if (contentValue != null) { // Add to dialogueContent list runtimeNode.dialogueContent.Add(contentValue); // Also add to legacy dialogueLines list for backward compatibility if (contentValue.ContentType == DialogueContentType.Text && !string.IsNullOrEmpty(contentValue.Text)) { runtimeNode.dialogueLines.Add(contentValue.Text); } } } } else { var contentPort = node.GetInputPortByName("DefaultDialogueContent"); var contentValue = GetPortValue(contentPort); if (contentValue != null) { // Add to dialogueContent list runtimeNode.dialogueContent.Add(contentValue); // Also add to legacy dialogueLines list for backward compatibility if (contentValue.ContentType == DialogueContentType.Text && !string.IsNullOrEmpty(contentValue.Text)) { runtimeNode.dialogueLines.Add(contentValue.Text); } } } // Get loop through lines option var loopThroughLines = GetPortValue(node.GetInputPortByName("LoopThroughDefaultLines")); runtimeNode.loopThroughLines = loopThroughLines; } private void ProcessPuzzleNode(WaitOnPuzzleStep node, RuntimeDialogueNode runtimeNode) { runtimeNode.nodeType = RuntimeDialogueNodeType.WaitOnPuzzleStep; var puzzleStep = GetPortValue(node.GetInputPortByName("RequiredPuzzleStep")); if (puzzleStep != null) { runtimeNode.puzzleStepID = puzzleStep.stepId; } } private void ProcessPickupNode(WaitOnPickup node, RuntimeDialogueNode runtimeNode) { runtimeNode.nodeType = RuntimeDialogueNodeType.WaitOnPickup; var pickup = GetPortValue(node.GetInputPortByName("RequiredPickup")); if (pickup != null) { runtimeNode.pickupItemID = pickup.itemId; } } private void ProcessSlotNode(WaitOnSlot node, RuntimeDialogueNode runtimeNode) { runtimeNode.nodeType = RuntimeDialogueNodeType.WaitOnSlot; var slot = GetPortValue(node.GetInputPortByName("RequiredSlot")); if (slot != null) { runtimeNode.slotItemID = slot.itemId; } // Get line type and count options for incorrect items var incorrectItemLineTypeOption = node.GetNodeOptionByName("IncorrectItemDialogueLineType"); incorrectItemLineTypeOption.TryGetValue(out var incorrectItemLineType); var incorrectItemLineCountOption = node.GetNodeOptionByName("IncorrectItemNoLines"); incorrectItemLineCountOption.TryGetValue(out var incorrectItemLineCount); // Process incorrect item content if (incorrectItemLineType == DialogueType.SayMultipleLines) { for (var i = 0; i < incorrectItemLineCount; i++) { var contentPort = node.GetInputPortByName($"IncorrectItemDialogueContent{i + 1}"); var contentValue = GetPortValue(contentPort); if (contentValue != null) { // Add to incorrectItemContent list runtimeNode.incorrectItemContent.Add(contentValue); // Also add to legacy incorrectItemLines list for backward compatibility if (contentValue.ContentType == DialogueContentType.Text && !string.IsNullOrEmpty(contentValue.Text)) { runtimeNode.incorrectItemLines.Add(contentValue.Text); } } } } else { var contentPort = node.GetInputPortByName("IncorrectItemDialogueContent"); var contentValue = GetPortValue(contentPort); if (contentValue != null) { // Add to incorrectItemContent list runtimeNode.incorrectItemContent.Add(contentValue); // Also add to legacy incorrectItemLines list for backward compatibility if (contentValue.ContentType == DialogueContentType.Text && !string.IsNullOrEmpty(contentValue.Text)) { runtimeNode.incorrectItemLines.Add(contentValue.Text); } } } runtimeNode.loopThroughIncorrectLines = GetPortValue(node.GetInputPortByName("LoopThroughIncorrectItemLines")); // Get line type and count options for forbidden items var forbiddenItemLineTypeOption = node.GetNodeOptionByName("ForbiddenItemDialogueLineType"); forbiddenItemLineTypeOption.TryGetValue(out var forbiddenItemLineType); var forbiddenItemLineCountOption = node.GetNodeOptionByName("ForbiddenItemNoLines"); forbiddenItemLineCountOption.TryGetValue(out var forbiddenItemLineCount); // Process forbidden item content if (forbiddenItemLineType == DialogueType.SayMultipleLines) { for (var i = 0; i < forbiddenItemLineCount; i++) { var contentPort = node.GetInputPortByName($"ForbiddenItemDialogueContent{i + 1}"); var contentValue = GetPortValue(contentPort); if (contentValue != null) { // Add to forbiddenItemContent list runtimeNode.forbiddenItemContent.Add(contentValue); // Also add to legacy forbiddenItemLines list for backward compatibility if (contentValue.ContentType == DialogueContentType.Text && !string.IsNullOrEmpty(contentValue.Text)) { runtimeNode.forbiddenItemLines.Add(contentValue.Text); } } } } else { var contentPort = node.GetInputPortByName("ForbiddenItemDialogueContent"); var contentValue = GetPortValue(contentPort); if (contentValue != null) { // Add to forbiddenItemContent list runtimeNode.forbiddenItemContent.Add(contentValue); // Also add to legacy forbiddenItemLines list for backward compatibility if (contentValue.ContentType == DialogueContentType.Text && !string.IsNullOrEmpty(contentValue.Text)) { runtimeNode.forbiddenItemLines.Add(contentValue.Text); } } } runtimeNode.loopThroughForbiddenLines = GetPortValue(node.GetInputPortByName("LoopThroughForbiddenItemLines")); } // Add new method to process combination nodes private void ProcessCombinationNode(WaitOnCombination node, RuntimeDialogueNode runtimeNode) { runtimeNode.nodeType = RuntimeDialogueNodeType.WaitOnCombination; var resultItem = GetPortValue(node.GetInputPortByName("RequiredResultItem")); if (resultItem != null) { runtimeNode.combinationResultItemID = resultItem.itemId; } } private T GetPortValue(IPort port) { if (port == null) return default(T); if (port.isConnected) { if (port.firstConnectedPort.GetNode() is IVariableNode variableNode) { variableNode.variable.TryGetDefaultValue(out T value); return value; } } port.TryGetValue(out T fallbackValue); return fallbackValue; } } }