public PassiveTreeViewModel(JsonPassiveTree?jsonPassiveTree, JsonPassiveTreeOptions?options = null)
        {
            JsonPassiveTree = jsonPassiveTree ?? throw new ArgumentNullException(nameof(JsonPassiveTree));
            Root            = new PassiveNodeViewModel(JsonPassiveTree.Root);

            InitializeJsonPassiveTreeOptions(options);
            InitializePassiveNodeGroups();
            InitializePassiveNodes();
            InitializePassiveNodeNeighbors();
            FixAscendancyPassiveNodeGroups();
        }
        public override object ReadJson(JsonReader reader, Type objectType, object?existingValue, JsonSerializer serializer)
        {
            var jObject     = JObject.Load(reader);
            var passiveTree = new JsonPassiveTree();

            if (jObject.GetValue("nodes") is JToken nodesToken)
            {
                if (nodesToken.Type == JTokenType.Array)
                {
                    foreach (var item in nodesToken.ToObject <List <JsonPassiveNode> >() ?? new List <JsonPassiveNode>())
                    {
                        item.Skill = item.Id;
                        passiveTree.PassiveNodes.Add(item.Id, item);
                    }
                }
                else
                {
                    var nodes     = nodesToken.ToObject <Dictionary <string, JsonPassiveNode> >() ?? new Dictionary <string, JsonPassiveNode>();
                    var maxLength = nodes.Max(x => x.Key.Length);
                    foreach (var node in nodes.OrderBy(x => x.Key.PadLeft(maxLength, '0')))
                    {
                        if (ushort.TryParse(node.Key, out var id))
                        {
                            if (node.Value.Skill == 0)
                            {
                                node.Value.Skill = id;
                            }
                            node.Value.Id = id;
                            passiveTree.PassiveNodes[id] = node.Value;
                        }
                        else if (node.Key == "root")
                        {
                            passiveTree.Root = node.Value;
                        }
                    }
                }

                jObject.Remove("nodes");
            }

            if (jObject.GetValue("skillSprites") is JToken spritesToken)
            {
                // This piece should only run when the older style of skill sprites are used (only active and inactive in the dictionary).
                if (spritesToken.ToObject <Dictionary <string, List <JsonPassiveTreeOldSkillSprite> > >() is var sprites && sprites is { Count : 2 })
 public static PassiveTreeDefinition Convert(JsonPassiveTree passiveTree)
 => new PassiveTreeDefinition(passiveTree);
 private PassiveTreeDefinition(JsonPassiveTree passiveTree)
     : this(passiveTree.PassiveNodes.Select(x => PassiveNodeDefinition.Convert(x.Value)).ToArray())
 {
 }