public void HighlightNode(SkillNode node, HighlightState newFlags) { if (newFlags == 0) return; if (nodeHighlights.ContainsKey(node)) nodeHighlights[node] |= newFlags; else nodeHighlights.Add(node, newFlags); }
/// <summary> /// Adds a skill node to the graph. New nodes are automatically /// connected to existing adjacent nodes. /// </summary> /// <param name="node">The skill node to be added.</param> /// <returns>The graph node that is added to the graph.</returns> public GraphNode AddNode(SkillNode node) { SingleNode graphNode = new SingleNode(node); NodeDict.Add(node, graphNode); CheckLinks(node); return graphNode; }
// Builds a graph from an adjacency matrix. // Only the lower left half is checked. SearchGraph SearchGraphFromData(bool[,] adjacencyMatrix) { int n = adjacencyMatrix.GetUpperBound(0) + 1; // Don't screw this up. Assert.IsTrue(n == adjacencyMatrix.GetUpperBound(1) + 1); List<SkillNode> nodes = new List<SkillNode>(); for (int i = 0; i < n; i++) { SkillNode node = new SkillNode(); node.Id = (ushort)i; nodes.Add(node); } for (int i = 0; i < n; i++) { nodes[i].Neighbor = new List<SkillNode>(); for (int j = 0; j < i; j++) { if (adjacencyMatrix[i, j]) { nodes[i].Neighbor.Add(nodes[j]); // No directed edges atm. nodes[j].Neighbor.Add(nodes[i]); } } } SearchGraph graph = new SearchGraph(); foreach (SkillNode node in nodes) { graph.AddNode(node); } return graph; }
/// <summary> /// Adds a skill node to the graph. New nodes are automatically /// connected to existing adjacent nodes. /// </summary> /// <param name="node">The skill node to be added.</param> /// <returns>The graph node that is added to the graph.</returns> public GraphNode AddNode(SkillNode node) { var graphNode = new GraphNode(node.Id); NodeDict.Add(node, graphNode); CheckLinks(node); return graphNode; }
public bool NodeHasHighlights(SkillNode node, HighlightState flags) { if (_nodeHighlights.ContainsKey(node)) { return _nodeHighlights[node].HasFlag(flags); } return false; }
public void HighlightNode(SkillNode node, HighlightState newFlags) { var flags = CleanFlags(node, newFlags); if (flags == 0) return; if (_nodeHighlights.ContainsKey(node)) _nodeHighlights[node] |= flags; else _nodeHighlights.Add(node, flags); }
public void UnhighlightNode(SkillNode node, HighlightState removeFlags) { if (_nodeHighlights.ContainsKey(node)) { // Each flag only remains set if it's not one of the flags to be removed. HighlightState newState = _nodeHighlights[node] & ~removeFlags; if (newState == 0) _nodeHighlights.Remove(node); else _nodeHighlights[node] = newState; } }
public void ToggleHighlightNode(SkillNode node, HighlightState toggleFlags) { if (toggleFlags == 0) return; if (nodeHighlights.ContainsKey(node)) { nodeHighlights[node] ^= toggleFlags; if (nodeHighlights[node] == 0) nodeHighlights.Remove(node); } else nodeHighlights.Add(node, toggleFlags); }
public void HighlightNode(SkillNode node, HighlightState newFlags) { if (newFlags == 0) { return; } if (nodeHighlights.ContainsKey(node)) { nodeHighlights[node] |= newFlags; } else { nodeHighlights.Add(node, newFlags); } }
private void DrawLinkBackgroundLayer(List <ushort[]> links) { picLinks = new DrawingVisual(); var pen2 = new Pen(Brushes.DarkSlateGray, 20f); using (DrawingContext dc = picLinks.RenderOpen()) { foreach (var nid in links) { SkillNode n1 = Skillnodes[nid[0]]; SkillNode n2 = Skillnodes[nid[1]]; DrawConnection(dc, pen2, n1, n2); } } }
public void DrawJewelHighlight(SkillNode node) { const int thickness = 10; var radiusPen = new Pen(Brushes.Cyan, thickness); const int smallRadius = 800 - thickness / 2; const int mediumRadius = 1200 - thickness / 2; const int largeRadius = 1500 - thickness / 2; using (DrawingContext dc = picJewelHighlight.RenderOpen()) { dc.DrawEllipse(null, radiusPen, node.Position, smallRadius, smallRadius); dc.DrawEllipse(null, radiusPen, node.Position, mediumRadius, mediumRadius); dc.DrawEllipse(null, radiusPen, node.Position, largeRadius, largeRadius); } }
public void UnhighlightNode(SkillNode node, HighlightState removeFlags) { if (nodeHighlights.ContainsKey(node)) { // Each flag only remains set if it's not one of the flags to be removed. HighlightState newState = nodeHighlights[node] & ~removeFlags; if (newState == 0) { nodeHighlights.Remove(node); } else { nodeHighlights[node] = newState; } } }
public void HighlightNode(SkillNode node, HighlightState newFlags) { var flags = CleanFlags(node, newFlags); if (flags == 0) { return; } if (_nodeHighlights.ContainsKey(node)) { _nodeHighlights[node] |= flags; } else { _nodeHighlights.Add(node, flags); } }
private HashSet <ushort> GetAvailableNodes(HashSet <ushort> skilledNodes) { HashSet <ushort> availNodes = new HashSet <ushort>(); foreach (ushort inode in skilledNodes) { SkillNode node = Skillnodes[inode]; foreach (SkillNode skillNode in node.Neighbor) { if (!CharName.Contains(skillNode.Name) && !SkilledNodes.Contains(skillNode.Id)) { availNodes.Add(skillNode.Id); } } } return(availNodes); }
/// <summary> /// Changes the HighlightState of the node: /// ... <- None <- Checked <- Crossed <- None /// (preserves other HighlightStates than Checked and Crossed) /// </summary> /// <param name="node">Node to change the HighlightState for</param> public void CycleNodeTagBackward(SkillNode node) { if (_nodeHighlighter.NodeHasHighlights(node, HighlightState.Crossed)) { _nodeHighlighter.UnhighlightNode(node, HighlightState.Crossed); _nodeHighlighter.HighlightNode(node, HighlightState.Checked); } else if (_nodeHighlighter.NodeHasHighlights(node, HighlightState.Checked)) { _nodeHighlighter.UnhighlightNode(node, HighlightState.Checked); } else { _nodeHighlighter.HighlightNode(node, HighlightState.Crossed); } DrawHighlights(_nodeHighlighter); }
private void CheckLinks(SkillNode node) { if (!NodeDict.ContainsKey(node)) return; GraphNode currentNode = NodeDict[node]; foreach (SkillNode neighbor in node.Neighbor) { if (NodeDict.ContainsKey(neighbor)) { GraphNode adjacentNode = NodeDict[neighbor]; if (adjacentNode == currentNode) continue; adjacentNode.AddNeighbor(currentNode); currentNode.AddNeighbor(adjacentNode); } } }
public void ToggleHighlightNode(SkillNode node, HighlightState toggleFlags) { if (toggleFlags == 0) { return; } if (nodeHighlights.ContainsKey(node)) { nodeHighlights[node] ^= toggleFlags; if (nodeHighlights[node] == 0) { nodeHighlights.Remove(node); } } else { nodeHighlights.Add(node, toggleFlags); } }
public void DrawPath(List <ushort> path) { var pen2 = new Pen(Brushes.LawnGreen, 15f); pen2.DashStyle = new DashStyle(new DoubleCollection { 2 }, 2); using (DrawingContext dc = picPathOverlay.RenderOpen()) { for (int i = -1; i < path.Count - 1; i++) { SkillNode n1 = i == -1 ? Skillnodes[path[i + 1]].Neighbor.First(sn => SkilledNodes.Contains(sn.Id)) : Skillnodes[path[i]]; SkillNode n2 = Skillnodes[path[i + 1]]; DrawConnection(dc, pen2, n1, n2); } } }
public static void DecodeURL(string url, out HashSet <ushort> skillednodes, out int chartype) { skillednodes = new HashSet <ushort>(); url = Regex.Replace(url, @"\t| |\n|\r", ""); string s = url.Substring(TreeAddress.Length + (url.StartsWith("https") ? 1 : 0)) .Replace("-", "+") .Replace("_", "/"); byte[] decbuff = Convert.FromBase64String(s); int i = BitConverter.ToInt32(new[] { decbuff[3], decbuff[2], decbuff[1], decbuff[1] }, 0); byte b = decbuff[4]; long j = 0L; if (i > 0) { j = decbuff[5]; } var nodes = new List <UInt16>(); for (int k = 6; k < decbuff.Length; k += 2) { byte[] dbff = { decbuff[k + 1], decbuff[k + 0] }; if (Skillnodes.Keys.Contains(BitConverter.ToUInt16(dbff, 0))) { nodes.Add((BitConverter.ToUInt16(dbff, 0))); } } chartype = b; SkillNode startnode = Skillnodes.First(nd => nd.Value.Name.ToUpper() == CharName[b].ToUpper()).Value; skillednodes.Add(startnode.Id); foreach (ushort node in nodes) { skillednodes.Add(node); } }
private void DrawConnection(DrawingContext dc, Pen pen2, SkillNode n1, SkillNode n2) { if (n1.SkillNodeGroup == n2.SkillNodeGroup && n1.Orbit == n2.Orbit) { if (n1.Arc - n2.Arc > 0 && n1.Arc - n2.Arc <= Math.PI || n1.Arc - n2.Arc < -Math.PI) { dc.DrawArc(null, pen2, n1.Position, n2.Position, new Size(SkillNode.OrbitRadii[n1.Orbit], SkillNode.OrbitRadii[n1.Orbit])); } else { dc.DrawArc(null, pen2, n2.Position, n1.Position, new Size(SkillNode.OrbitRadii[n1.Orbit], SkillNode.OrbitRadii[n1.Orbit])); } } else { dc.DrawLine(pen2, n1.Position, n2.Position); } }
private void DrawConnection(DrawingContext dc, Pen pen2, SkillNode n1, SkillNode n2) { if (n1.VisibleNeighbors.Contains(n2) && n2.VisibleNeighbors.Contains(n1)) { if (n1.SkillNodeGroup == n2.SkillNodeGroup && n1.Orbit == n2.Orbit) { if (n1.Arc - n2.Arc > 0 && n1.Arc - n2.Arc <= Math.PI || n1.Arc - n2.Arc < -Math.PI) { dc.DrawArc(null, pen2, n1.Position, n2.Position, new Size(SkillNode.OrbitRadii[n1.Orbit], SkillNode.OrbitRadii[n1.Orbit])); } else { dc.DrawArc(null, pen2, n2.Position, n1.Position, new Size(SkillNode.OrbitRadii[n1.Orbit], SkillNode.OrbitRadii[n1.Orbit])); } } else { var draw = true; foreach (var attibute in n1.attributes) { if (AscendantClassStartRegex.IsMatch(attibute)) { draw = false; } } if (draw) { dc.DrawLine(pen2, n1.Position, n2.Position); } } } }
public void ToggleNodeHighlight(SkillNode node) { _nodeHighlighter.ToggleHighlightNode(node, HighlightState.FromNode); DrawHighlights(_nodeHighlighter); }
public static Dictionary <string, List <float> > GetAttributesWithoutImplicit(IEnumerable <ushort> skilledNodes, int chartype) { var temp = new Dictionary <string, List <float> >(); foreach (var attr in CharBaseAttributes[chartype]) { if (!temp.ContainsKey(attr.Key)) { temp[attr.Key] = new List <float>(); } if (temp.ContainsKey(attr.Key) && temp[attr.Key].Count > 0) { temp[attr.Key][0] += attr.Value; } else { temp[attr.Key].Add(attr.Value); } } foreach (var attr in BaseAttributes) { if (!temp.ContainsKey(attr.Key)) { temp[attr.Key] = new List <float>(); } if (temp.ContainsKey(attr.Key) && temp[attr.Key].Count > 0) { temp[attr.Key][0] += attr.Value; } else { temp[attr.Key].Add(attr.Value); } } foreach (ushort inode in skilledNodes) { SkillNode node = Skillnodes[inode]; foreach (var attr in ExpandHybridAttributes(node.Attributes)) { if (!temp.ContainsKey(attr.Key)) { temp[attr.Key] = new List <float>(); } for (int i = 0; i < attr.Value.Count; i++) { if (temp.ContainsKey(attr.Key) && temp[attr.Key].Count > i) { temp[attr.Key][i] += attr.Value[i]; } else { temp[attr.Key].Add(attr.Value[i]); } } } } return(temp); }
private static void DrawConnection(DrawingContext dc, Pen pen2, SkillNode n1, SkillNode n2) { if (!n1.VisibleNeighbors.Contains(n2) || !n2.VisibleNeighbors.Contains(n1)) return; if (n1.SkillNodeGroup == n2.SkillNodeGroup && n1.Orbit == n2.Orbit) { if (n1.Arc - n2.Arc > 0 && n1.Arc - n2.Arc <= Math.PI || n1.Arc - n2.Arc < -Math.PI) { dc.DrawArc(null, pen2, n1.Position, n2.Position, new Size(SkillNode.OrbitRadii[n1.Orbit], SkillNode.OrbitRadii[n1.Orbit])); } else { dc.DrawArc(null, pen2, n2.Position, n1.Position, new Size(SkillNode.OrbitRadii[n1.Orbit], SkillNode.OrbitRadii[n1.Orbit])); } } else { var draw = true; foreach (var attibute in n1.attributes) { if (AscendantClassStartRegex.IsMatch(attibute)) draw = false; } if (n1.Type == NodeType.Mastery || n2.Type == NodeType.Mastery) draw = false; if (draw) dc.DrawLine(pen2, n1.Position, n2.Position); } }
/// <summary> /// Loads from the unofficial online tool /// </summary> public static async Task LoadBuildFromPoezone(IDialogCoordinator dialogCoordinator, SkillTree tree, string buildUrl) { if (!buildUrl.Contains('#')) { throw new FormatException(); } const string dataUrl = "http://poezone.ru/skilltree/data.js"; const string buildPostUrl = "http://poezone.ru/skilltree/"; string build = buildUrl.Substring(buildUrl.LastIndexOf('#') + 1); string dataFile, buildFile; { var req = (HttpWebRequest)WebRequest.Create(dataUrl); req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.30 (KHTML, like Gecko) Iron/12.0.750.0 Chrome/12.0.750.0 Safari/534.30"; WebResponse resp = req.GetResponse(); dataFile = new StreamReader(resp.GetResponseStream()).ReadToEnd(); } { string postData = "build=" + build; byte[] postBytes = Encoding.ASCII.GetBytes(postData); var req = (HttpWebRequest)WebRequest.Create(buildPostUrl); req.Method = "POST"; req.ContentLength = postBytes.Length; req.ContentType = "application/x-www-form-urlencoded"; req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.30 (KHTML, like Gecko) Iron/12.0.750.0 Chrome/12.0.750.0 Safari/534.30"; req.Accept = "application/json, text/javascript, */*; q=0.01"; req.Host = "poezone.ru"; req.Referer = "http://poezone.ru/skilltree/"; req.AutomaticDecompression = DecompressionMethods.GZip; req.Headers.Add("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.3"); req.Headers.Add("Accept-Encoding", "gzip,deflate,sdch"); req.Headers.Add("Accept-Language", "en-US,en;q=0.8"); req.Headers.Add("Origin", "http://poezone.ru"); req.Headers.Add("X-Requested-With", "XMLHttpRequest"); req.Expect = ""; req.Credentials = CredentialCache.DefaultCredentials; Stream dataStream = req.GetRequestStream(); dataStream.Write(postBytes, 0, postBytes.Length); dataStream.Close(); WebResponse resp = req.GetResponse(); buildFile = new StreamReader(resp.GetResponseStream()).ReadToEnd(); } if (!buildFile.Contains("[")) { await dialogCoordinator.ShowErrorAsync(tree, string.Format( L10n.Message("An error occured while attempting to load Skill tree from {0} location."), "poezone.ru")); return; } // position decompose var positions = new List <Vector2D?>(); string[] lines = dataFile.Split('\n'); foreach (string line in lines) { if (line.StartsWith("skillpos=")) { string posString = line.Substring(line.IndexOf('[') + 1, line.LastIndexOf(']') - line.IndexOf('[') - 1); var sb = new StringBuilder(); bool inBracket = false; foreach (char c in posString) { if (!inBracket && c == ',') { positions.Add(sb.Length == 0 ? null : new Vector2D?(new Vector2D( int.Parse(sb.ToString().Split(',')[0]), int.Parse(sb.ToString().Split(',')[1]) ))); sb.Clear(); } else { if (c == '[') { inBracket = true; } else if (c == ']') { inBracket = false; } else { sb.Append(c); } } } positions.Add(sb.Length == 0 ? null : new Vector2D?(new Vector2D( int.Parse(sb.ToString().Split(',')[0]), int.Parse(sb.ToString().Split(',')[1]) ))); } } // min max double minx = float.MaxValue, miny = float.MaxValue, maxx = float.MinValue, maxy = float.MinValue; foreach (var posn in positions) { if (!posn.HasValue) { continue; } Vector2D pos = posn.Value; minx = Math.Min(pos.X, minx); miny = Math.Min(pos.Y, miny); maxx = Math.Max(pos.X, maxx); maxy = Math.Max(pos.Y, maxy); } double nminx = float.MaxValue, nminy = float.MaxValue, nmaxx = float.MinValue, nmaxy = float.MinValue; foreach (SkillNode node in SkillTree.Skillnodes.Values) { Vector2D pos = node.Position; nminx = Math.Min(pos.X, nminx); nminy = Math.Min(pos.Y, nminy); nmaxx = Math.Max(pos.X, nmaxx); nmaxy = Math.Max(pos.Y, nmaxy); } //respose string[] buildResp = buildFile.Replace("[", "").Replace("]", "").Split(','); int character = int.Parse(buildResp[0]); var skilled = new List <int>(); tree.Chartype = character; tree.SkilledNodes.Clear(); SkillNode startnode = SkillTree.Skillnodes.First(nd => nd.Value.Name == SkillTree.CharName[tree.Chartype].ToUpper()).Value; tree.AllocateSkillNode(startnode); for (int i = 1; i < buildResp.Length; ++i) { if (!positions[int.Parse(buildResp[i])].HasValue) { Debugger.Break(); } Vector2D poezonePos = (positions[int.Parse(buildResp[i])].Value - new Vector2D(minx, miny)) * new Vector2D(1 / (maxx - minx), 1 / (maxy - miny)); double minDis = 2; var minNode = new KeyValuePair <ushort, SkillNode>(); foreach (var node in SkillTree.Skillnodes) { Vector2D nodePos = (node.Value.Position - new Vector2D(nminx, nminy)) * new Vector2D(1 / (nmaxx - nminx), 1 / (nmaxy - nminy)); double dis = (nodePos - poezonePos).Length; if (dis < minDis) { minDis = dis; minNode = node; } } tree.AllocateSkillNode(minNode.Value); } }
/// <summary> /// Returns flags without HighlightState.Tags if the node is an ascendancy node. /// Returns flags unchanged if it is not. /// </summary> private static HighlightState CleanFlags(SkillNode node, HighlightState flags) { if (node.ascendancyName == null) return flags; return flags & ~HighlightState.Tags; }
public void ForceRefundNode(SkillNode node) { if (!SkilledNodes.Contains(node)) return; var charStartNode = GetCharNode(); var front = new HashSet<SkillNode>() { charStartNode }; foreach (var i in charStartNode.Neighbor) if (SkilledNodes.Contains(i) && i != node) front.Add(i); var reachable = new HashSet<SkillNode>(front); while (front.Any()) { var newFront = new HashSet<SkillNode>(); foreach (var i in front) { foreach (var j in i.Neighbor) { if (reachable.Contains(j) || !SkilledNodes.Contains(j) || j == node) continue; newFront.Add(j); reachable.Add(j); } } front = newFront; } var removable = SkilledNodes.Except(reachable).ToList(); SkilledNodes.ExceptWith(removable); }
public HashSet<SkillNode> ForceRefundNodePreview(SkillNode node) { if (!SkilledNodes.Contains(node)) return new HashSet<SkillNode>(); var charStartNode = GetCharNode(); var front = new HashSet<SkillNode>() { charStartNode }; foreach (var i in charStartNode.Neighbor) if (SkilledNodes.Contains(i) && i != node) front.Add(i); var reachable = new HashSet<SkillNode>(front); while (front.Any()) { var newFront = new HashSet<SkillNode>(); foreach (var i in front) { foreach (var j in i.Neighbor) { if (j == node || reachable.Contains(j) || !SkilledNodes.Contains(j)) continue; newFront.Add(j); reachable.Add(j); } } front = newFront; } var unreachable = new HashSet<SkillNode>(SkilledNodes); unreachable.ExceptWith(reachable); return unreachable; }
public List<SkillNode> GetShortestPathTo(SkillNode targetNode, IEnumerable<SkillNode> start) { var startNodes = start as IList<SkillNode> ?? start.ToList(); if (startNodes.Contains(targetNode)) return new List<SkillNode>(); var adjacent = GetAvailableNodes(startNodes); if (adjacent.Contains(targetNode)) return new List<SkillNode> { targetNode }; var visited = new HashSet<SkillNode>(startNodes); var distance = new Dictionary<SkillNode, int>(); var parent = new Dictionary<SkillNode, SkillNode>(); var newOnes = new Queue<SkillNode>(); var toOmit = new HashSet<SkillNode>( from entry in _nodeHighlighter.NodeHighlights where entry.Value.HasFlag(HighlightState.Crossed) select entry.Key); foreach (var node in adjacent) { if (toOmit.Contains(node)) { continue; } newOnes.Enqueue(node); distance.Add(node, 1); } while (newOnes.Count > 0) { var newNode = newOnes.Dequeue(); var dis = distance[newNode]; visited.Add(newNode); foreach (var connection in newNode.Neighbor) { if (toOmit.Contains(connection)) continue; if (visited.Contains(connection)) continue; if (distance.ContainsKey(connection)) continue; if (newNode.Spc.HasValue) continue; if (newNode.Type == NodeType.Mastery) continue; if (IsAscendantClassStartNode(newNode)) continue; distance.Add(connection, dis + 1); newOnes.Enqueue(connection); parent.Add(connection, newNode); if (connection == targetNode) { newOnes.Clear(); break; } } } if (!distance.ContainsKey(targetNode)) return new List<SkillNode>(); var curr = targetNode; var result = new List<SkillNode> { curr }; while (parent.ContainsKey(curr)) { result.Add(parent[curr]); curr = parent[curr]; } result.Reverse(); return result; }
/// <summary> /// Returns true iff node is a Ascendant "Path of the ..." node. /// </summary> public static bool IsAscendantClassStartNode(SkillNode node) { return node.attributes.Any(s => AscendantClassStartRegex.IsMatch(s)); }
private void DrawSurround(DrawingContext dc, SkillNode node, bool onlyAscendancy = false, bool isActive = false, bool isHighlight = false) { if (onlyAscendancy && node.ascendancyName == null) return; var surroundBrush = _nodeSurroundBrushes; var factor = 1f; var activeOffset = 0; if (isActive) activeOffset = 1; if (isHighlight) { surroundBrush = _nodeSurroundComparisonBrushes; factor = HighlightFactor; } var ascendancyClassName = AscClasses.GetClassName(_chartype, AscType); if (node.IsAscendancyStart) { if (!DrawAscendancy || isHighlight || isActive) return; const string ascStartName = "PassiveSkillScreenAscendancyMiddle"; var bitmap = Assets[ascStartName]; var brush = new ImageBrush(Assets[ascStartName]); if (_persistentData.Options.ShowAllAscendancyClasses || node.ascendancyName == ascendancyClassName) dc.DrawRectangle(brush, null, new Rect(node.Position - new Vector2D(bitmap.PixelWidth, bitmap.PixelHeight), new Size(bitmap.PixelWidth * 2, bitmap.PixelHeight * 2))); } else if (node.ascendancyName != null && node.Type == NodeType.Notable) { if (!DrawAscendancy) return; if (_persistentData.Options.ShowAllAscendancyClasses || node.ascendancyName == ascendancyClassName) dc.DrawRectangle(surroundBrush[10 + activeOffset].Value, null, new Rect((int)node.Position.X - surroundBrush[10 + activeOffset].Key.Width * .875 * factor, (int)node.Position.Y - surroundBrush[10 + activeOffset].Key.Height * .875 * factor, surroundBrush[10 + activeOffset].Key.Width * 1.75 * factor, surroundBrush[10 + activeOffset].Key.Height * 1.75 * factor)); } else if (node.ascendancyName != null && node.Type == NodeType.Normal) { if (!DrawAscendancy) return; if (_persistentData.Options.ShowAllAscendancyClasses || node.ascendancyName == ascendancyClassName) dc.DrawRectangle(surroundBrush[8 + activeOffset].Value, null, new Rect((int)node.Position.X - surroundBrush[8 + activeOffset].Key.Width * factor, (int)node.Position.Y - surroundBrush[8 + activeOffset].Key.Height * factor, surroundBrush[8 + activeOffset].Key.Width * 2 * factor, surroundBrush[8 + activeOffset].Key.Height * 2 * factor)); } else if (node.Type == NodeType.Notable) { dc.DrawRectangle(surroundBrush[2 + activeOffset].Value, null, new Rect((int)node.Position.X - surroundBrush[2 + activeOffset].Key.Width * factor, (int)node.Position.Y - surroundBrush[2 + activeOffset].Key.Height * factor, surroundBrush[2 + activeOffset].Key.Width * 2 * factor, surroundBrush[2 + activeOffset].Key.Height * 2 * factor)); } else if (node.Type == NodeType.Keystone) { dc.DrawRectangle(surroundBrush[4 + activeOffset].Value, null, new Rect((int)node.Position.X - surroundBrush[4 + activeOffset].Key.Width * factor, (int)node.Position.Y - surroundBrush[4 + activeOffset].Key.Height * factor, surroundBrush[4 + activeOffset].Key.Width * 2 * factor, surroundBrush[4 + activeOffset].Key.Height * 2 * factor)); } else if (node.Type == NodeType.Mastery) { //Needs to be here so that "Masteries" (Middle images of nodes) don't get anything drawn around them. } else if (node.Type == NodeType.JewelSocket) { dc.DrawRectangle(surroundBrush[6 + activeOffset].Value, null, new Rect((int)node.Position.X - surroundBrush[6 + activeOffset].Key.Width * factor, (int)node.Position.Y - surroundBrush[6 + activeOffset].Key.Height * factor, surroundBrush[6 + activeOffset].Key.Width * 2 * factor, surroundBrush[6 + activeOffset].Key.Height * 2 * factor)); } else { dc.DrawRectangle(surroundBrush[0 + activeOffset].Value, null, new Rect((int)node.Position.X - surroundBrush[0 + activeOffset].Key.Width * factor, (int)node.Position.Y - surroundBrush[0 + activeOffset].Key.Height * factor, surroundBrush[0 + activeOffset].Key.Width * 2 * factor, surroundBrush[0 + activeOffset].Key.Height * 2 * factor)); } }
public void AllocateSkillNode(SkillNode node, bool bulk = false) { if (node == null) return; if (node.IsAscendancyStart) { var remove = SkilledNodes.Where(x => x.ascendancyName != null && x.ascendancyName != node.ascendancyName).ToArray(); ChangeAscClass(AscClasses.GetClassNumber(node.ascendancyName)); SkilledNodes.ExceptWith(remove); } else if (node.IsMultipleChoiceOption) { var remove = SkilledNodes.Where(x => x.IsMultipleChoiceOption && AscClasses.GetStartingClass(node.Name) == AscClasses.GetStartingClass(x.Name)).ToArray(); SkilledNodes.ExceptWith(remove); } if (!bulk) SkilledNodes.Add(node); }
private void DrawSkillNodeIcon(DrawingContext dc, SkillNode skillNode, bool onlyAscendancy = false, bool isActive = false) { if (onlyAscendancy && skillNode.ascendancyName == null) return; Rect rect; BitmapImage bitmapImage; if (isActive) { rect = IconActiveSkills.SkillPositions[skillNode.IconKey]; bitmapImage = IconActiveSkills.GetSkillImage(skillNode.IconKey); } else { rect = IconInActiveSkills.SkillPositions[skillNode.IconKey]; bitmapImage = IconInActiveSkills.GetSkillImage(skillNode.IconKey); } var imageBrush = new ImageBrush() { Stretch = Stretch.Uniform, ImageSource = bitmapImage, ViewboxUnits = BrushMappingMode.RelativeToBoundingBox, Viewbox = new Rect(rect.X / bitmapImage.PixelWidth, rect.Y / bitmapImage.PixelHeight, rect.Width / bitmapImage.PixelWidth, rect.Height / bitmapImage.PixelHeight) }; dc.DrawEllipse(imageBrush, null, skillNode.Position, rect.Width, rect.Height); }
/// <summary> /// Changes the HighlightState of the node: /// None -> FromNode -> Crossed -> None -> ... /// (preserves other HighlightStates than FromNode and Crossed) /// </summary> /// <param name="node">Node to change the HighlightState for</param> public void CycleNodeHighlightForward(SkillNode node) { if (_nodeHighlighter.NodeHasHighlights(node, HighlightState.FromNode)) { _nodeHighlighter.UnhighlightNode(node, HighlightState.FromNode); _nodeHighlighter.HighlightNode(node, HighlightState.Crossed); } else if (_nodeHighlighter.NodeHasHighlights(node, HighlightState.Crossed)) { _nodeHighlighter.UnhighlightNode(node, HighlightState.Crossed); } else { _nodeHighlighter.HighlightNode(node, HighlightState.FromNode); } DrawHighlights(_nodeHighlighter); }
public SingleNode(SkillNode baseNode) : base(baseNode.Id) { }
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; }
/// <summary> /// Changes the HighlightState of the node: /// ... <- None <- Checked <- Crossed <- None /// (preserves other HighlightStates than Checked and Crossed) /// </summary> /// <param name="node">Node to change the HighlightState for</param> public void CycleNodeTagBackward(SkillNode node) { if (_nodeHighlighter.NodeHasHighlights(node, HighlightState.Crossed)) { _nodeHighlighter.UnhighlightNode(node, HighlightState.Crossed); _nodeHighlighter.HighlightNode(node, HighlightState.Checked); } else if (_nodeHighlighter.NodeHasHighlights(node, HighlightState.Checked)) { _nodeHighlighter.UnhighlightNode(node, HighlightState.Checked); } else { _nodeHighlighter.HighlightNode(node, HighlightState.Crossed); } DrawHighlights(); }
/// <summary> /// Changes the HighlightState of the node: /// ... <- None <- Checked <- Crossed <- None /// (preserves other HighlightStates than Checked and Crossed) /// </summary> /// <param name="node">Node to change the HighlightState for</param> public void CycleNodeTagBackward(SkillNode node) { var id = node.Id; var build = _persistentData.CurrentBuild; if (_nodeHighlighter.NodeHasHighlights(node, HighlightState.Crossed)) { _nodeHighlighter.UnhighlightNode(node, HighlightState.Crossed); _nodeHighlighter.HighlightNode(node, HighlightState.Checked); build.CrossedNodeIds.Remove(id); build.CheckedNodeIds.Add(id); } else if (_nodeHighlighter.NodeHasHighlights(node, HighlightState.Checked)) { _nodeHighlighter.UnhighlightNode(node, HighlightState.Checked); build.CheckedNodeIds.Remove(id); } else { _nodeHighlighter.HighlightNode(node, HighlightState.Crossed); build.CrossedNodeIds.Add(id); } DrawHighlights(); }