public SkillTree(String treestring, bool displayProgress, UpdateLoadingWindow update) { PoESkillTree inTree = null; if (!_Initialized) { var jss = new JsonSerializerSettings { Error = delegate(object sender, ErrorEventArgs args) { Debug.WriteLine(args.ErrorContext.Error.Message); args.ErrorContext.Handled = true; } }; inTree = JsonConvert.DeserializeObject<PoESkillTree>(treestring, jss); } int qindex = 0; if (!_Initialized) { SkillTree._IconInActiveSkills = new SkillIcons(); //TODO: (SpaceOgre) This is not used atm, so no need to run it. foreach (var obj in inTree.skillSprites) { if (obj.Key.Contains("inactive")) continue; _IconInActiveSkills.Images[obj.Value[3].filename] = null; foreach (var o in obj.Value[3].coords) { _IconInActiveSkills.SkillPositions[o.Key + "_" + o.Value.w] = new KeyValuePair<Rect, string>(new Rect(o.Value.x, o.Value.y, o.Value.w, o.Value.h), obj.Value[3].filename); } } } if (!_Initialized) { SkillTree._IconActiveSkills = new SkillIcons(); foreach (var obj in inTree.skillSprites) { if (obj.Key.Contains("active")) continue; _IconActiveSkills.Images[obj.Value[3].filename] = null; foreach (var o in obj.Value[3].coords) { _IconActiveSkills.SkillPositions[o.Key + "_" + o.Value.w] = new KeyValuePair<Rect, string>(new Rect(o.Value.x, o.Value.y, o.Value.w, o.Value.h), obj.Value[3].filename); } } } if (!_Initialized) { foreach (var ass in inTree.assets) { _assets[ass.Key] = new Asset(ass.Key, ass.Value.ContainsKey(0.3835f) ? ass.Value[0.3835f] : ass.Value.Values.First()); } } if (!_Initialized) { _rootNodeList = new List<int>(); if (inTree.root != null) { foreach (int i in inTree.root.ot) { _rootNodeList.Add(i); } } else if (inTree.main != null) { foreach (int i in inTree.main.ot) { _rootNodeList.Add(i); } } } if (displayProgress) update(50, 100); if (!_Initialized) _IconActiveSkills.OpenOrDownloadImages(update); if (displayProgress) update(75, 100); if (!_Initialized) _IconInActiveSkills.OpenOrDownloadImages(update); if (!_Initialized) { _CharBaseAttributes = new Dictionary<string, float>[7]; foreach (var c in inTree.characterData) { _CharBaseAttributes[c.Key] = new Dictionary<string, float> { {"+# to Strength", c.Value.base_str}, {"+# to Dexterity", c.Value.base_dex}, {"+# to Intelligence", c.Value.base_int} }; } } if (!_Initialized) { _Skillnodes = new Dictionary<ushort, SkillNode>(); _rootNodeClassDictionary = new Dictionary<string, int>(); _startNodeDictionary = new Dictionary<int, int>(); foreach (Node nd in inTree.nodes) { _Skillnodes.Add(nd.id, new SkillNode { Id = nd.id, Name = nd.dn, attributes = nd.dn.Contains("Jewel Socket") ? new string[1] { "+1 Jewel Socket" } : SplitMultilineAttributes(nd.sd), Orbit = nd.o, OrbitIndex = nd.oidx, Icon = nd.icon, LinkId = nd.ot, G = nd.g, Da = nd.da, Ia = nd.ia, IsKeyStone = nd.ks, IsNotable = nd.not, IsJewelSocket = nd.dn.Contains("Jewel Socket"), Sa = nd.sa, IsMastery = nd.m, Spc = nd.spc.Count() > 0 ? (int?)nd.spc[0] : null }); if (_rootNodeList.Contains(nd.id)) { if (!_rootNodeClassDictionary.ContainsKey(nd.dn.ToString().ToUpperInvariant())) { _rootNodeClassDictionary.Add(nd.dn.ToString().ToUpperInvariant(), nd.id); } foreach (int linkedNode in nd.ot) { if (!_startNodeDictionary.ContainsKey(nd.id)) { _startNodeDictionary.Add(linkedNode, nd.id); } } } foreach (int node in nd.ot) { if (!_startNodeDictionary.ContainsKey(nd.id) && _rootNodeList.Contains(node)) { _startNodeDictionary.Add(nd.id, node); } } } foreach (var skillNode in Skillnodes) { foreach (ushort i in skillNode.Value.LinkId) { if ( _links.Count(nd => (nd[0] == i && nd[1] == skillNode.Key) || nd[0] == skillNode.Key && nd[1] == i) == 1) { continue; } _links.Add(new[] { skillNode.Key, i }); } } foreach (var ints in _links) { if (!Skillnodes[ints[0]].Neighbor.Contains(Skillnodes[ints[1]])) Skillnodes[ints[0]].Neighbor.Add(Skillnodes[ints[1]]); if (!Skillnodes[ints[1]].Neighbor.Contains(Skillnodes[ints[0]])) Skillnodes[ints[1]].Neighbor.Add(Skillnodes[ints[0]]); } } if (!_Initialized) { _NodeGroups = new List<SkillNodeGroup>(); foreach (var gp in inTree.groups) { var ng = new SkillNodeGroup(); ng.OcpOrb = gp.Value.oo; ng.Position = new Vector2D(gp.Value.x, gp.Value.y); foreach (ushort node in gp.Value.n) { ng.Nodes.Add(Skillnodes[node]); } NodeGroups.Add(ng); } foreach (SkillNodeGroup group in NodeGroups) { foreach (SkillNode node in group.Nodes) { node.SkillNodeGroup = group; } } } if (!_Initialized) { const int padding = 500; //This is to account for jewel range circles. Might need to find a better way to do it. _TRect = new Rect2D(new Vector2D(inTree.min_x * 1.1 - padding, inTree.min_y * 1.1 - padding), new Vector2D(inTree.max_x * 1.1 + padding, inTree.max_y * 1.1 + padding)); } InitNodeSurround();// DrawNodeSurround(); DrawNodeBaseSurround(); InitSkillIconLayers(); DrawSkillIconLayer(); DrawBackgroundLayer(); InitFaceBrushesAndLayer(); DrawLinkBackgroundLayer(_links); InitOtherDynamicLayers(); CreateCombineVisual(); if (_links != null) { var regexAttrib = new Regex("[0-9]*\\.?[0-9]+"); foreach (var skillNode in Skillnodes) { skillNode.Value.Attributes = new Dictionary<string, List<float>>(); foreach (string s in skillNode.Value.attributes) { var values = new List<float>(); foreach (Match m in regexAttrib.Matches(s)) { if (!AttributeTypes.Contains(regexAttrib.Replace(s, "#"))) AttributeTypes.Add(regexAttrib.Replace(s, "#")); if (m.Value == "") values.Add(float.NaN); else values.Add(float.Parse(m.Value, CultureInfo.InvariantCulture)); } string cs = (regexAttrib.Replace(s, "#")); skillNode.Value.Attributes[cs] = values; } } } if (displayProgress) update(100, 100); _Initialized = true; }
public SkillTree(String treestring, bool displayProgress, UpdateLoadingWindow update) { PoESkillTree inTree = null; if (!_Initialized) { var jss = new JsonSerializerSettings { Error = delegate(object sender, ErrorEventArgs args) { Debug.WriteLine(args.ErrorContext.Error.Message); args.ErrorContext.Handled = true; } }; inTree = JsonConvert.DeserializeObject <PoESkillTree>(treestring.Replace("Additional ", ""), jss); } int qindex = 0; if (!_Initialized) { SkillTree._IconInActiveSkills = new SkillIcons(); //TODO: (SpaceOgre) This is not used atm, so no need to run it. foreach (var obj in inTree.skillSprites) { if (obj.Key.Contains("inactive")) { continue; } _IconInActiveSkills.Images[obj.Value[3].filename] = null; foreach (var o in obj.Value[3].coords) { _IconInActiveSkills.SkillPositions[o.Key + "_" + o.Value.w] = new KeyValuePair <Rect, string>(new Rect(o.Value.x, o.Value.y, o.Value.w, o.Value.h), obj.Value[3].filename); } } } if (!_Initialized) { SkillTree._IconActiveSkills = new SkillIcons(); foreach (var obj in inTree.skillSprites) { if (obj.Key.Contains("active")) { continue; } _IconActiveSkills.Images[obj.Value[3].filename] = null; foreach (var o in obj.Value[3].coords) { _IconActiveSkills.SkillPositions[o.Key + "_" + o.Value.w] = new KeyValuePair <Rect, string>(new Rect(o.Value.x, o.Value.y, o.Value.w, o.Value.h), obj.Value[3].filename); } } } if (!_Initialized) { foreach (var ass in inTree.assets) { _assets[ass.Key] = new Asset(ass.Key, ass.Value.ContainsKey(0.3835f) ? ass.Value[0.3835f] : ass.Value.Values.First()); } } if (!_Initialized) { _rootNodeList = new List <int>(); if (inTree.root != null) { foreach (int i in inTree.root.ot) { _rootNodeList.Add(i); } } else if (inTree.main != null) { foreach (int i in inTree.main.ot) { _rootNodeList.Add(i); } } } if (displayProgress) { update(50, 100); } if (!_Initialized) { _IconActiveSkills.OpenOrDownloadImages(update); } if (displayProgress) { update(75, 100); } if (!_Initialized) { _IconInActiveSkills.OpenOrDownloadImages(update); } if (!_Initialized) { _CharBaseAttributes = new Dictionary <string, float> [7]; foreach (var c in inTree.characterData) { _CharBaseAttributes[c.Key] = new Dictionary <string, float> { { "+# to Strength", c.Value.base_str }, { "+# to Dexterity", c.Value.base_dex }, { "+# to Intelligence", c.Value.base_int } }; } } if (!_Initialized) { _Skillnodes = new Dictionary <ushort, SkillNode>(); foreach (Node nd in inTree.nodes) { _Skillnodes.Add(nd.id, new SkillNode { Id = nd.id, Name = nd.dn, attributes = nd.sd, Orbit = nd.o, OrbitIndex = nd.oidx, Icon = nd.icon, LinkId = nd.ot, G = nd.g, Da = nd.da, Ia = nd.ia, IsKeyStone = nd.ks, IsNotable = nd.not, Sa = nd.sa, IsMastery = nd.m, Spc = nd.spc.Count() > 0 ? (int?)nd.spc[0] : null }); if (_rootNodeList.Contains(nd.id)) { _rootNodeClassDictionary.Add(nd.dn.ToString().ToUpper(), nd.id); foreach (int linkedNode in nd.ot) { _startNodeDictionary.Add(linkedNode, nd.id); } } foreach (int node in nd.ot) { if (!_startNodeDictionary.ContainsKey(nd.id) && _rootNodeList.Contains(node)) { _startNodeDictionary.Add(nd.id, node); } } } foreach (var skillNode in Skillnodes) { foreach (ushort i in skillNode.Value.LinkId) { if ( _links.Count(nd => (nd[0] == i && nd[1] == skillNode.Key) || nd[0] == skillNode.Key && nd[1] == i) == 1) { continue; } _links.Add(new[] { skillNode.Key, i }); } } foreach (var ints in _links) { if (!Skillnodes[ints[0]].Neighbor.Contains(Skillnodes[ints[1]])) { Skillnodes[ints[0]].Neighbor.Add(Skillnodes[ints[1]]); } if (!Skillnodes[ints[1]].Neighbor.Contains(Skillnodes[ints[0]])) { Skillnodes[ints[1]].Neighbor.Add(Skillnodes[ints[0]]); } } } if (!_Initialized) { _NodeGroups = new List <SkillNodeGroup>(); foreach (var gp in inTree.groups) { var ng = new SkillNodeGroup(); ng.OcpOrb = gp.Value.oo; ng.Position = new Vector2D(gp.Value.x, gp.Value.y); foreach (ushort node in gp.Value.n) { ng.Nodes.Add(Skillnodes[node]); } NodeGroups.Add(ng); } foreach (SkillNodeGroup group in NodeGroups) { foreach (SkillNode node in group.Nodes) { node.SkillNodeGroup = group; } } } if (!_Initialized) { _TRect = new Rect2D(new Vector2D(inTree.min_x * 1.1, inTree.min_y * 1.1), new Vector2D(inTree.max_x * 1.1, inTree.max_y * 1.1)); } InitNodeSurround();// DrawNodeSurround(); DrawNodeBaseSurround(); InitSkillIconLayers(); DrawSkillIconLayer(); DrawBackgroundLayer(); InitFaceBrushesAndLayer(); DrawLinkBackgroundLayer(_links); InitOtherDynamicLayers(); CreateCombineVisual(); if (_links != null) { var regexAttrib = new Regex("[0-9]*\\.?[0-9]+"); foreach (var skillNode in Skillnodes) { skillNode.Value.Attributes = new Dictionary <string, List <float> >(); foreach (string s in skillNode.Value.attributes) { var values = new List <float>(); foreach (Match m in regexAttrib.Matches(s)) { if (!AttributeTypes.Contains(regexAttrib.Replace(s, "#"))) { AttributeTypes.Add(regexAttrib.Replace(s, "#")); } if (m.Value == "") { values.Add(float.NaN); } else { values.Add(float.Parse(m.Value, CultureInfo.InvariantCulture)); } } string cs = (regexAttrib.Replace(s, "#")); skillNode.Value.Attributes[cs] = values; } } } if (displayProgress) { update(100, 100); } _Initialized = true; }
private async Task InitializeAsync(string treestring, string opsstring, [CanBeNull] ProgressDialogController controller, AssetLoader assetLoader) { if (!_initialized) { var jss = new JsonSerializerSettings { Error = (sender, args) => { // This one is known: "515":{"x":_,"y":_,"oo":[],"n":[]}} has an Array in "oo". if (args.ErrorContext.Path != "groups.515.oo") Log.Error("Exception while deserializing Json tree", args.ErrorContext.Error); args.ErrorContext.Handled = true; } }; var inTree = JsonConvert.DeserializeObject<PoESkillTree>(treestring, jss); var inOpts = JsonConvert.DeserializeObject<Opts>(opsstring, jss); controller?.SetProgress(0.25); await assetLoader.DownloadSkillNodeSpritesAsync(inTree, d => controller?.SetProgress(0.25 + d * 0.30)); IconInActiveSkills = new SkillIcons(); IconActiveSkills = new SkillIcons(); foreach (var obj in inTree.skillSprites) { SkillIcons icons; string prefix; if (obj.Key.EndsWith("Active")) { // Adds active nodes to IconActiveSkills icons = IconActiveSkills; prefix = obj.Key.Substring(0, obj.Key.Length - "Active".Length); } else if (obj.Key.EndsWith("Inactive")) { // Adds inactive nodes to IconInActiveSkills icons = IconInActiveSkills; prefix = obj.Key.Substring(0, obj.Key.Length - "Inactive".Length); } else { // Adds masteries to IconInActiveSkills icons = IconInActiveSkills; prefix = obj.Key; } var sprite = obj.Value[AssetZoomLevel]; var path = _assetsFolderPath + sprite.filename; icons.Images[sprite.filename] = ImageHelper.OnLoadBitmapImage(new Uri(path, UriKind.Absolute)); foreach (var o in sprite.coords) { var iconKey = prefix + "_" + o.Key; icons.SkillPositions[iconKey] = new Rect(o.Value.x, o.Value.y, o.Value.w, o.Value.h); icons.SkillImages[iconKey] = sprite.filename; } } controller?.SetProgress(0.55); // The last percent progress is reserved for rounding errors as progress must not get > 1. await assetLoader.DownloadAssetsAsync(inTree, d => controller?.SetProgress(0.55 + d * 0.44)); foreach (var ass in inTree.assets) { var path = _assetsFolderPath + ass.Key + ".png"; Assets[ass.Key] = ImageHelper.OnLoadBitmapImage(new Uri(path, UriKind.Absolute)); } RootNodeList = new List<int>(); if (inTree.root != null) { foreach (int i in inTree.root.ot) { RootNodeList.Add(i); } } else if (inTree.main != null) { foreach (int i in inTree.main.ot) { RootNodeList.Add(i); } } _ascClasses = new AscendancyClasses(); if (inOpts != null) { foreach (KeyValuePair<int, baseToAscClass> ascClass in inOpts.ascClasses) { var classes = new List<AscendancyClasses.Class>(); foreach (KeyValuePair<int, classes> asc in ascClass.Value.classes) { var newClass = new AscendancyClasses.Class { Order = asc.Key, DisplayName = asc.Value.displayName, Name = asc.Value.name, FlavourText = asc.Value.flavourText, FlavourTextColour = asc.Value.flavourTextColour.Split(',').Select(int.Parse).ToArray() }; int[] tempPointList = asc.Value.flavourTextRect.Split(',').Select(int.Parse).ToArray(); newClass.FlavourTextRect = new Vector2D(tempPointList[0], tempPointList[1]); classes.Add(newClass); } AscClasses.Classes.Add(ascClass.Value.name, classes); } } CharBaseAttributes = new Dictionary<string, float>[7]; foreach (var c in inTree.characterData) { CharBaseAttributes[c.Key] = new Dictionary<string, float> { {"+# to Strength", c.Value.base_str}, {"+# to Dexterity", c.Value.base_dex}, {"+# to Intelligence", c.Value.base_int} }; } Skillnodes = new Dictionary<ushort, SkillNode>(); RootNodeClassDictionary = new Dictionary<string, int>(); StartNodeDictionary = new Dictionary<int, int>(); AscRootNodeList = new HashSet<SkillNode>(); foreach (var nd in inTree.nodes) { var skillNode = new SkillNode { Id = nd.id, Name = nd.dn, //this value should not be split on '\n' as it causes the attribute list to seperate nodes attributes = nd.dn.Contains("Jewel Socket") ? new[] { "+1 Jewel Socket" } : nd.sd, Orbit = nd.o, OrbitIndex = nd.oidx, Icon = nd.icon, LinkId = nd.ot, G = nd.g, Da = nd.da, Ia = nd.ia, Sa = nd.sa, Spc = nd.spc.Length > 0 ? (int?)nd.spc[0] : null, IsMultipleChoice = nd.isMultipleChoice, IsMultipleChoiceOption = nd.isMultipleChoiceOption, passivePointsGranted = nd.passivePointsGranted, ascendancyName = nd.ascendancyName, IsAscendancyStart = nd.isAscendancyStart, reminderText = nd.reminderText }; if (nd.ks && !nd.not && !nd.isJewelSocket && !nd.m) { skillNode.Type = NodeType.Keystone; } else if (!nd.ks && nd.not && !nd.isJewelSocket && !nd.m) { skillNode.Type = NodeType.Notable; } else if (!nd.ks && !nd.not && nd.isJewelSocket && !nd.m) { skillNode.Type = NodeType.JewelSocket; } else if (!nd.ks && !nd.not && !nd.isJewelSocket && nd.m) { skillNode.Type = NodeType.Mastery; } else if (!nd.ks && !nd.not && !nd.isJewelSocket && !nd.m) { skillNode.Type = NodeType.Normal; } else { throw new InvalidOperationException($"Invalid node type for node {skillNode.Name}"); } Skillnodes.Add(nd.id, skillNode); if(skillNode.IsAscendancyStart) if(!AscRootNodeList.Contains(skillNode)) AscRootNodeList.Add(skillNode); if (RootNodeList.Contains(nd.id)) { if (!RootNodeClassDictionary.ContainsKey(nd.dn.ToUpperInvariant())) { RootNodeClassDictionary.Add(nd.dn.ToUpperInvariant(), nd.id); } foreach (var linkedNode in nd.ot) { if (!StartNodeDictionary.ContainsKey(nd.id) && !nd.isAscendancyStart) { StartNodeDictionary.Add(linkedNode, nd.id); } } } foreach (var node in nd.ot) { if (!StartNodeDictionary.ContainsKey(nd.id) && RootNodeList.Contains(node)) { StartNodeDictionary.Add(nd.id, node); } } } foreach (var skillNode in Skillnodes) { foreach (var i in skillNode.Value.LinkId) { if (Links.Count(nd => (nd[0] == i && nd[1] == skillNode.Key) || nd[0] == skillNode.Key && nd[1] == i) != 1) Links.Add(new[] { skillNode.Key, i }); } } foreach (var ints in Links) { Regex regexString = new Regex(@"Can Allocate Passives from the .* starting point"); bool isScionAscendancyNotable = false; foreach (var attibute in Skillnodes[ints[0]].attributes) { if (regexString.IsMatch(attibute)) isScionAscendancyNotable = true; } foreach (var attibute in Skillnodes[ints[1]].attributes) { if (regexString.IsMatch(attibute)) isScionAscendancyNotable = true; } if (isScionAscendancyNotable && StartNodeDictionary.Keys.Contains(ints[0])) { if (!Skillnodes[ints[1]].Neighbor.Contains(Skillnodes[ints[0]])) Skillnodes[ints[1]].Neighbor.Add(Skillnodes[ints[0]]); } else if (isScionAscendancyNotable && StartNodeDictionary.Keys.Contains(ints[1])) { if (!Skillnodes[ints[0]].Neighbor.Contains(Skillnodes[ints[1]])) Skillnodes[ints[0]].Neighbor.Add(Skillnodes[ints[1]]); } else { if (!Skillnodes[ints[0]].Neighbor.Contains(Skillnodes[ints[1]])) Skillnodes[ints[0]].Neighbor.Add(Skillnodes[ints[1]]); if (!Skillnodes[ints[1]].Neighbor.Contains(Skillnodes[ints[0]])) Skillnodes[ints[1]].Neighbor.Add(Skillnodes[ints[0]]); } } var regexAttrib = new Regex("[0-9]*\\.?[0-9]+"); foreach (var skillnode in Skillnodes) { //add each other as visible neighbors foreach (var snn in skillnode.Value.Neighbor) { if (snn.IsAscendancyStart && skillnode.Value.LinkId.Contains(snn.Id)) continue; skillnode.Value.VisibleNeighbors.Add(snn); } //populate the Attributes fields with parsed attributes skillnode.Value.Attributes = new Dictionary<string, List<float>>(); foreach (string s in skillnode.Value.attributes) { var values = new List<float>(); foreach (Match m in regexAttrib.Matches(s)) { if (!AttributeTypes.Contains(regexAttrib.Replace(s, "#"))) AttributeTypes.Add(regexAttrib.Replace(s, "#")); if (m.Value == "") values.Add(float.NaN); else values.Add(float.Parse(m.Value, CultureInfo.InvariantCulture)); } string cs = (regexAttrib.Replace(s, "#")); skillnode.Value.Attributes[cs] = values; } } NodeGroups = new List<SkillNodeGroup>(); foreach (var gp in inTree.groups) { var ng = new SkillNodeGroup(); ng.OcpOrb = gp.Value.oo; ng.Position = new Vector2D(gp.Value.x, gp.Value.y); foreach (var node in gp.Value.n) { ng.Nodes.Add(Skillnodes[node]); } NodeGroups.Add(ng); } foreach (SkillNodeGroup group in NodeGroups) { foreach (SkillNode node in group.Nodes) { node.SkillNodeGroup = group; } } const int padding = 500; //This is to account for jewel range circles. Might need to find a better way to do it. SkillTreeRect = new Rect2D(new Vector2D(inTree.min_x * 1.1 - padding, inTree.min_y * 1.1 - padding), new Vector2D(inTree.max_x * 1.1 + padding, inTree.max_y * 1.1 + padding)); } if (_persistentData.Options.ShowAllAscendancyClasses) DrawAscendancy = true; InitialSkillTreeDrawing(); controller?.SetProgress(1); _initialized = true; }