using System; using System.Collections.Generic; using System.Linq; using Lumina.Excel.GeneratedSheets; namespace QuestMap { internal class Node { internal uint Id { get; } internal List> Parents { get; set; } internal T Value { get; set; } internal List> Children { get; } = new(); internal Node(List> parents, uint id, T value) { this.Id = id; this.Parents = parents; this.Value = value; } private Node(uint id) { this.Id = id; this.Parents = new List>(); this.Value = default!; } internal Node? Find(uint id) { if (this.Id == id) { return this; } foreach (var child in this.Children) { var result = child.Find(id); if (result != null) { return result; } } return null; } internal IEnumerable> Ancestors() { var parents = new Stack>(); foreach (var parent in this.Parents) { parents.Push(parent); } while (parents.Any()) { var next = parents.Pop(); yield return next; foreach (var parent in next.Parents) { parents.Push(parent); } } } internal IEnumerable> Ancestors(Func consolidator) { var parents = new Stack>(); foreach (var parent in this.Parents) { var consolidated = consolidator(parent.Value); parents.Push(consolidated == null ? parent : new Node(new List>(), parent.Id, consolidated) { Children = { this }, }); } while (parents.Any()) { var next = parents.Pop(); yield return next; foreach (var parent in next.Parents) { var consolidated = consolidator(parent.Value); parents.Push(consolidated == null ? parent : new Node(new List>(), parent.Id, consolidated) { Children = { next }, }); } } } internal IEnumerable> Traverse() { var stack = new Stack>(); stack.Push(this); while (stack.Any()) { var next = stack.Pop(); yield return next; foreach (var child in next.Children) { stack.Push(child); } } } internal IEnumerable, uint>> TraverseWithDepth() { var stack = new Stack, uint>>(); stack.Push(Tuple.Create(this, (uint) 0)); while (stack.Any()) { var next = stack.Pop(); yield return next; foreach (var child in next.Item1.Children) { stack.Push(Tuple.Create(child, next.Item2 + 1)); } } } internal static (List>, Dictionary>) BuildTree(Dictionary layouts) { var lookup = new Dictionary>(); var rootNodes = new List>(); var allNodes = new Dictionary>(); foreach (var item in layouts) { if (lookup.TryGetValue(item.Key, out var ourNode)) { ourNode.Value = item.Value; } else { ourNode = new Node(new List>(), item.Key, item.Value); lookup[item.Key] = ourNode; allNodes[item.Key] = ourNode; } var previous = item.Value.PreviousQuests().ToList(); if (previous.Count == 0) { rootNodes.Add(ourNode); } else { foreach (var prev in previous) { if (!lookup.TryGetValue(prev.RowId, out var parentNode)) { // create preliminary parent parentNode = new Node(prev.RowId); lookup[prev.RowId] = parentNode; allNodes[prev.RowId] = parentNode; } parentNode.Children.Add(ourNode); ourNode.Parents.Add(parentNode); } } } return (rootNodes, allNodes); } } internal static class NodeExt { internal static Node? Find(this IEnumerable> nodes, uint id) { foreach (var node in nodes) { var found = node.Find(id); if (found != null) { return found; } } return null; } internal static IEnumerable PreviousQuests(this Quest quest) { foreach (var previous in quest.PreviousQuest) { if (previous != null && previous.Row != 0) { yield return previous.Value!; } } } } }