/// <summary>Runs the thread.</summary> private void run() { List<WorldEvent> evnts = new List<WorldEvent>(); while (true) { evnts.Clear(); Waiting = true; while (Waiting) { if (Stop) break; if (SyncFinished) { lock (Events) { evnts.AddRange(Events); Events.Clear(); } lock (waiting) { lock (syncFinished) { waiting = false; syncFinished = false; } } } } if (Stop) break; Grid.startNewUpdate(CurTime); #region Sync { List<Node> newNodes = new List<Node>(); List<Segment> newSegs = new List<Segment>(); List<Node> updateNodes = new List<Node>(); List<List<Segment>> updateNodeSegs = new List<List<Segment>>(); SegmentSkel tempSeg = null; NodeSkel tempNode = null; foreach (WorldEvent evnt in evnts) { switch (evnt.WEvent) { // add segment case WorldEvent.EventType.AddSeg: if ((bool)evnt.Arguments[0]) // if owned by this player { tempSeg = (Segment)evnt.Arguments[1]; Segments.Add((Segment)tempSeg); newSegs.Add((Segment)tempSeg); Path.intersect(tempSeg.EndLoc[0], tempSeg.EndLoc[1], 1); tempSeg.Visible = true; } else { tempSeg = new SegmentSkel(); tempSeg.ID = (string)evnt.Arguments[1]; tempSeg.EndLoc[0] = (VectorF)evnt.Arguments[2]; tempSeg.State[0] = (SegmentSkel.SegState)evnt.Arguments[3]; tempSeg.EndLoc[1] = (VectorF)evnt.Arguments[4]; tempSeg.State[1] = (SegmentSkel.SegState)evnt.Arguments[5]; lock (Owner.Fog) { tempSeg.Visible = Owner.Fog.isVisible(tempSeg); } } SegByID.Add(tempSeg.ID, tempSeg); Grid.Line(tempSeg.EndLoc[0], tempSeg.EndLoc[1], tempSeg, gridAddSegment); break; // remove segment case WorldEvent.EventType.RemSeg: tempSeg = SegByID[(string)evnt.Arguments[0]]; if (tempSeg.Owner == Owner) { Segments.Remove((Segment)tempSeg); Path.intersect(tempSeg.EndLoc[0], tempSeg.EndLoc[1], -1); } SegByID.Remove(tempSeg.ID); Grid.Line(tempSeg.EndLoc[0], tempSeg.EndLoc[1], tempSeg, gridRemoveSegment); break; // change segment state case WorldEvent.EventType.SegChangeState: tempSeg = SegByID[(string)evnt.Arguments[0]]; Grid.Line(tempSeg.EndLoc[0], tempSeg.EndLoc[1], tempSeg, gridRemoveSegment); if (tempSeg.Owner == Owner) { Segment s = (Segment)tempSeg; FInt l0 = (FInt)evnt.Arguments[1]; FInt l1 = (FInt)evnt.Arguments[3]; bool reAdd = false; if (s.EndLength[0] != l0 || s.EndLength[1] != l1) { Path.intersect(s.EndLoc[0], s.EndLoc[1], -1); reAdd = true; } s.EndLength[0] = l0; s.State[0] = (SegmentSkel.SegState)evnt.Arguments[2]; s.EndLength[1] = l1; s.State[1] = (SegmentSkel.SegState)evnt.Arguments[4]; s.refreshEndLocs(); if (reAdd) Path.intersect(s.EndLoc[0], s.EndLoc[1], 1); } else { tempSeg.EndLoc[0] = (VectorF)evnt.Arguments[1]; tempSeg.State[0] = (SegmentSkel.SegState)evnt.Arguments[2]; tempSeg.EndLoc[1] = (VectorF)evnt.Arguments[3]; tempSeg.State[1] = (SegmentSkel.SegState)evnt.Arguments[4]; lock (Owner.Fog) { tempSeg.Visible = Owner.Fog.isVisible(tempSeg); } } Grid.Line(tempSeg.EndLoc[0], tempSeg.EndLoc[1], tempSeg, gridAddSegment); break; // add node case WorldEvent.EventType.AddNode: if ((bool)evnt.Arguments[0]) // if owned by this player { tempNode = (Node)evnt.Arguments[1]; tempNode.Visible = true; Nodes.Add((Node)tempNode); newNodes.Add((Node)tempNode); } else { tempNode = new NodeSkel(); tempNode.ID = (string)evnt.Arguments[1]; tempNode.IsParent = (bool)evnt.Arguments[2]; tempNode.Pos = (VectorF)evnt.Arguments[3]; tempNode.Radius = (FInt)evnt.Arguments[4]; lock (Owner.Fog) { tempNode.Visible = Owner.Fog.isVisible(tempNode); } } NodeByID.Add(tempNode.ID, tempNode); Grid.Point(tempNode.Pos, tempNode, gridAddNode); //if ((bool)evnt.Arguments[0] || tempNode.Active) // Path.intersect(tempNode.Pos, tempNode.Radius, 1); if ((bool)evnt.Arguments[0] && tempNode.Active) // if owned by this player refreshVisibility(new VectorF(tempNode.X - tempNode.SightDistance, tempNode.Y - tempNode.SightDistance), new VectorF(tempNode.X + tempNode.SightDistance, tempNode.Y + tempNode.SightDistance)); break; // change node state case WorldEvent.EventType.NodeChangeState: tempNode = NodeByID[(string)evnt.Arguments[0]]; if (tempNode.Owner == Owner) // if owned by this player { Node realNode = (Node)tempNode; bool applyVisibility = !realNode.Active && (bool)evnt.Arguments[1]; realNode.Active = (bool)evnt.Arguments[1]; List<Segment> segs = (List<Segment>)evnt.Arguments[2]; updateNodes.Add(realNode); updateNodeSegs.Add(segs); if (applyVisibility) { lock (Owner.Fog) { Owner.Fog.applyVisibility(realNode); } refreshVisibility(new VectorF(tempNode.X - tempNode.SightDistance, tempNode.Y - tempNode.SightDistance), new VectorF(tempNode.X + tempNode.SightDistance, tempNode.Y + tempNode.SightDistance)); } } else { /*if ((bool)evnt.Arguments[1] && !tempNode.Active) Path.intersect(tempNode.Pos, tempNode.Radius, 1); else if (!(bool)evnt.Arguments[1] && tempNode.Active) Path.intersect(tempNode.Pos, tempNode.Radius, -1);*/ tempNode.Active = (bool)evnt.Arguments[1]; } break; // remove node case WorldEvent.EventType.RemNode: tempNode = NodeByID[(string)evnt.Arguments[0]]; // prepare to invalidate fog of war VectorF upperLeft = VectorF.Zero; VectorF lowerRight = VectorF.Zero; if (tempNode.Active && tempNode.Owner == Owner) { upperLeft = new VectorF(tempNode.X - tempNode.SightDistance, tempNode.Y - tempNode.SightDistance); lowerRight = new VectorF(tempNode.X + tempNode.SightDistance, tempNode.Y + tempNode.SightDistance); } if (tempNode.Owner == Owner) Nodes.Remove((Node)tempNode); NodeByID.Remove(tempNode.ID); Grid.Point(tempNode.Pos, tempNode, gridRemoveNode); //if (tempNode.Owner == Owner || tempNode.Active) // Path.intersect(tempNode.Pos, tempNode.Radius, -1); // invalidate fog of war if (upperLeft != lowerRight) { lock (Owner.Fog) { Owner.Fog.invalidate(upperLeft, lowerRight); } refreshVisibility(upperLeft, lowerRight); } break; default: throw new Exception("Unrecognized event: " + evnt.ToString()); } } evnts.Clear(); // link new nodes to their connected segments and parents foreach (Node newNode in newNodes) { // segments for (int i = 0; i < newNode.Segments.Length; i++) { if (newNode.Segments[i] != null) { SegmentSkel reassignSeg = null; if (SegByID.TryGetValue(newNode.Segments[i].ID, out reassignSeg)) newNode.Segments[i] = (Segment)reassignSeg; #if DEBUG else throw new Exception("Segment id " + newNode.Segments[i].ID + " not found."); #endif } } // parents for (int i = 0; i < newNode.Parents.Count; i++) { NodeSkel reassignNode = null; if (NodeByID.TryGetValue(newNode.Parents[i].ID, out reassignNode)) newNode.Parents[i] = (Node)reassignNode; } } // link new segments to their connected nodes foreach (Segment newSeg in newSegs) { for (int i = 0; i < 2; i++) { NodeSkel reassignNode = null; if (NodeByID.TryGetValue(newSeg.Nodes[i].ID, out reassignNode)) newSeg.Nodes[i] = (Node)reassignNode; else newSeg.Nodes[i] = new Node(newSeg.Nodes[i]); } } // link updated nodes to their connected segments for (int i = 0; i < updateNodes.Count; i++) { Node node = updateNodes[i]; List<Segment> segs = updateNodeSegs[i]; updateNodes[i].NumSegments = 0; for (int j = 0; j < segs.Count; j++) { if (segs[j] != null) updateNodes[i].NumSegments++; if (segs[j] == null && node.Segments[j] != null) // if removing a segment { node.Segments[j] = null; } else if (segs[j] != null && node.Segments[j] == null) // if adding a segment { node.Segments[j] = (Segment)SegByID[segs[j].ID]; } else if (segs[j] != null && node.Segments[j] != null && segs[j].ID != node.Segments[j].ID) // if switching out a segment with another { node.Segments[j] = (Segment)SegByID[segs[j].ID]; } } } } #endregion Sync #region Update Destination if (destUpdateDue) { // TODO: make this work destUpdateDue = false; } #endregion Update Destination #region Decision-Making /*{ // Pattern AI Node top = null; Node bottom = null; Node right = null; Node left = null; foreach (Node n in Nodes) { if (top == null || n.Pos.Y < top.Y) top = n; if (bottom == null || n.Pos.Y > bottom.Y) bottom = n; if (right == null || n.Pos.X > right.X) right = n; if (left == null || n.Pos.X < left.X) left = n; } foreach (Node fromNode in Nodes) { if (!fromNode.Active || fromNode.NumSegments > 1) continue; VectorF dest = VectorF.Zero; if (fromNode.IsParent) { FInt x = (fromNode.Pos.X + fromNode.Segments[0].getOppNode(fromNode).Pos.X) / FInt.F2; if (left == fromNode) dest = new VectorF(x, fromNode.Pos.Y + (FInt)200); else dest = new VectorF(x, fromNode.Pos.Y - (FInt)200); } else { if (bottom == fromNode) dest = new VectorF(right.Pos.X + (FInt)200, right.Pos.Y); else if (top == fromNode) dest = new VectorF(left.Pos.X - (FInt)200, left.Pos.Y); else if (right == fromNode) dest = new VectorF(top.Pos.X, top.Pos.Y - (FInt)200); else // left dest = new VectorF(bottom.Pos.X, bottom.Pos.Y + (FInt)200); } Actions.Add(new PlayerAction(Owner, PlayerAction.ActionType.BuildSeg, fromNode.ID, false, dest)); } if (Actions.Count == 0) for (int i = 0; i < 9999999; i++) { } // give the main loop a break }*/ #endregion Decision-Making #region Decision-Making /*{ // Real thinking AI Node bestNode = null; PathNode bestPathNode = null; FInt bestWorth = FInt.F0; Node worstNode = null; PathNode worstPathNode = null; FInt worstWorth = FInt.F0; bool stillBuilding = false; FInt maxDist = (FInt)400; foreach (Node fromNode in Nodes) { if (!fromNode.Active) stillBuilding = true; if (!fromNode.Active || fromNode.NumSegments == fromNode.Segments.Length) continue; VectorF fromPos = fromNode.Pos; VectorF nSpacing = (VectorF)Path.NodeSpacing; VectorF nde = new VectorF(fromPos.X - maxDist, fromPos.Y - maxDist) / nSpacing; nde.X = (FInt)Math.Round((double)nde.X); nde.Y = (FInt)Math.Round((double)nde.Y); int left = Math.Max(0, (int)nde.X); int top = Math.Max(0, (int)nde.Y); nde = new VectorF(fromPos.X + maxDist, fromPos.Y + maxDist) / nSpacing; nde.X = (FInt)Math.Round((double)nde.X); nde.Y = (FInt)Math.Round((double)nde.Y); int right = Math.Min(Path.NumCols - 1, (int)nde.X); int bottom = Math.Min(Path.NumRows - 1, (int)nde.Y); FInt[,] worth = new FInt[bottom - top + 1, right - left + 1]; nde = (Grid.Squares[0, 0].Nodes[0].Pos + Grid.Squares[0, 0].Nodes[1].Pos) / FInt.F2 / nSpacing; nde.X = (FInt)Math.Round((double)nde.X); nde.Y = (FInt)Math.Round((double)nde.Y); VectorF toPos = (VectorF)Path.Grid[(int)nde.Y, (int)nde.X].Position; FInt totDist = VectorF.Distance(fromPos, toPos); for (int row = top; row <= bottom; row++) { for (int col = left; col <= right; col++) { PathNode pn = Path.Grid[row, col]; VectorF pnPos = (VectorF)pn.Position; // distance from source node FInt srcDist = VectorF.Distance(fromPos, pnPos); FInt srcDistWorth = FInt.F0; if (srcDist > maxDist || srcDist < (FInt)100) srcDistWorth = FInt.F1 / FInt.F2; else srcDistWorth = FInt.F1; // distance from destination node FInt destDist = VectorF.Distance(toPos, pnPos); FInt destDistWorth = FInt.F1 - (destDist / (maxDist * FInt.F2)); // collisions List<NodeSkel> ignoreNode = new List<NodeSkel>(1) { fromNode }; List<SegmentSkel> ignoreSeg = new List<SegmentSkel>(fromNode.NumSegments); foreach (Segment seg in fromNode.Segments) { if (seg != null) ignoreSeg.Add(seg); } FInt collWorth = FInt.F0; List<SegmentSkel> collSeg; List<NodeSkel> collNode; List<GeoSkel> collGeo; if (Collision.segCollision(fromPos, pnPos, ignoreSeg, ignoreNode, out collSeg, out collNode, out collGeo, true, Owner)) { if (collGeo.Count > 0) { collWorth = FInt.F0; } else if (collNode.Count > 0) { collWorth = FInt.F0; } else { SegmentSkel closestSeg = null; VectorF closestInt = VectorF.Zero; FInt closestDist = FInt.F0; for (int i = 0; i < collSeg.Count; i++) { VectorF nextInt = Calc.LineIntersect(fromPos, pnPos, collSeg[i].EndLoc[0], collSeg[i].EndLoc[1]); if (collSeg[i].Owner == Owner) { VectorF end0 = (collSeg[i].State[1] == SegmentSkel.SegState.Retracting) ? collSeg[i].EndLoc[0] : ((Segment)collSeg[i]).Nodes[0].Pos; VectorF end1 = (collSeg[i].State[0] == SegmentSkel.SegState.Retracting) ? collSeg[i].EndLoc[1] : ((Segment)collSeg[i]).Nodes[1].Pos; nextInt = Calc.LineIntersect(fromPos, pnPos, end0, end1); } else { nextInt = Calc.LineIntersect(fromPos, pnPos, collSeg[i].EndLoc[0], collSeg[i].EndLoc[1]); } FInt nextDist = VectorF.Distance(fromPos, nextInt); if (closestSeg == null || nextDist < closestDist) { closestSeg = collSeg[i]; closestInt = nextInt; closestDist = nextDist; } } if (closestSeg.Owner == Owner) collWorth = FInt.F0; else collWorth = FInt.F1; } } else { NodeType nType = Owner.InWorld.getNodeType(srcDist); if (pnPos.X - nType.Radius < 0 || pnPos.Y - nType.Radius < 0 || pnPos.X + nType.Radius >= Owner.InWorld.Width || pnPos.Y + nType.Radius >= Owner.InWorld.Height || Collision.nodeCollision(pnPos, nType.Radius, new List<SegmentSkel>(0), new List<NodeSkel>(0), true, Owner) || Collision.nodeCollNodeSpacing(pnPos, nType.Radius, nType.Spacing, Owner, new List<NodeSkel>(0), true)) { collWorth = FInt.F0; } else collWorth = FInt.F1 / FInt.F2; } // calculate worth worth[row - top, col - left] = (collWorth * FInt.F5) + (srcDistWorth * FInt.F2) + destDistWorth; if (worth[row - top, col - left] > bestWorth || (worth[row - top, col - left] == bestWorth && new Random().Next(2) == 1)) { bestNode = fromNode; bestPathNode = pn; bestWorth = worth[row - top, col - left]; } else if (fromNode.NumSegments == 1 && worth[row - top, col - left] < worstWorth || (worth[row - top, col - left] == worstWorth && new Random().Next(2) == 1)) { worstNode = fromNode; worstPathNode = pn; worstWorth = worth[row - top, col - left]; } } } } PlayerAction action = null; if (bestPathNode != null && bestNode != null && (!stillBuilding || bestWorth > (FInt)3.5)) // if worth isn't high enough, don't do anything action = new PlayerAction(Owner, PlayerAction.ActionType.BuildSeg, bestNode.ID, false, (VectorF)bestPathNode.Position); else if (worstPathNode != null && worstNode != null) action = new PlayerAction(Owner, PlayerAction.ActionType.DestroyNode, worstNode.ID); if (action != null) { lock (Actions) Actions.Add(action); } }*/ #endregion Decision-Making #region Decision-Making if (false) { VectorF nSpacing = (VectorF)Path.NodeSpacing; VectorF dest = (Grid.Squares[0, 0].Nodes[0].Pos + Grid.Squares[0, 0].Nodes[1].Pos) / FInt.F2 / nSpacing; PathNode destNode = Path.Grid[(int)dest.Y, (int)dest.X]; dest = (VectorF)destNode.Position; Node uselessNode = null; List<NodeDistPath> openNodes = new List<NodeDistPath>(); // collect list of nodes with an open branch foreach (Node n in Nodes) { if (n.Active && n.NumSegments < n.Segments.Length) openNodes.Add(new NodeDistPath(n, VectorF.Distance(n.Pos, dest), null)); } // // FIND CLOSEST NODE TO DESTINATION // // sort nodes by distance from destination NodeDistPath swap; for (int i = 1, j; i < openNodes.Count; i++) { for (j = i; j > 0 && openNodes[j] < openNodes[j - 1]; j--) { swap = openNodes[j]; openNodes[j] = openNodes[j - 1]; openNodes[j - 1] = swap; } } // find the node with the shortest path to the destination while (openNodes.Count > 0 && openNodes[0].Path == null) { // get a starting path node PathNode srcNode = Path.Grid[(int)((double)openNodes[0].Node.Pos.Y / Path.NodeSpacing.Y), (int)((double)openNodes[0].Node.Pos.X / Path.NodeSpacing.X)]; while (srcNode.isOrphaned()) { srcNode = Path.Nodes[srcNode.Index - Path.NumCols]; } // get shortest path openNodes[0].Path = Path.gridToList(Path.search(srcNode, destNode), srcNode, destNode); // if path doesn't exist if (openNodes[0].Path == null) { openNodes.RemoveAt(0); continue; } double dist = 0d; foreach (PathEdge edge in openNodes[0].Path) { dist += edge.Distance; } openNodes[0].Dist = (FInt)dist; // update order in list for (int i = 0; i < openNodes.Count - 1 && openNodes[i] > openNodes[i + 1]; i++) { swap = openNodes[i]; openNodes[i] = openNodes[i + 1]; openNodes[i + 1] = swap; } } if (openNodes.Count > 0) { // find furthest reachable path node FInt maxDist = (FInt)400; // TODO: get rid of magic number (400) int magnetEdge = -1; for (int i = openNodes[0].Path.Count - 1; i >= 0; i--) { if (VectorF.Distance(openNodes[0].Node.Pos, (VectorF)Path.Nodes[openNodes[0].Path[i].NodeDest].Position) < maxDist) { magnetEdge = i; break; } } // find furthest node with no collisions List<NodeSkel> ignoreNode = new List<NodeSkel>(1) { openNodes[0].Node }; List<SegmentSkel> ignoreSeg = new List<SegmentSkel>(openNodes[0].Node.NumSegments); foreach (Segment seg in openNodes[0].Node.Segments) { if (seg != null) ignoreSeg.Add(seg); } List<SegmentSkel> collSeg; List<NodeSkel> collNode; List<GeoSkel> collGeo; VectorF magnetPoint = VectorF.Zero; for (int i = magnetEdge; i >= 0; i--) { bool collision = false; if (Collision.segCollision(openNodes[0].Node.Pos, (VectorF)Path.Nodes[openNodes[0].Path[i].NodeDest].Position, ignoreSeg, ignoreNode, out collSeg, out collNode, out collGeo, true, Owner)) { if (collNode.Count > 0 || collGeo.Count > 0) { collision = true; } else { foreach (SegmentSkel s in collSeg) { if (s.Owner == Owner) { collision = true; break; } } } } if (!collision) { magnetPoint = (VectorF)Path.Nodes[openNodes[0].Path[i].NodeDest].Position; break; } } // find best path node towards destination VectorF nde = new VectorF(openNodes[0].Node.X - maxDist, openNodes[0].Node.Y - maxDist) / nSpacing; nde.X = (FInt)Math.Round((double)nde.X); nde.Y = (FInt)Math.Round((double)nde.Y); int left = Math.Max(0, (int)nde.X); int top = Math.Max(0, (int)nde.Y); nde = new VectorF(openNodes[0].Node.X + maxDist, openNodes[0].Node.Y + maxDist) / nSpacing; nde.X = (FInt)Math.Round((double)nde.X); nde.Y = (FInt)Math.Round((double)nde.Y); int right = Math.Min(Path.NumCols - 1, (int)nde.X); int bottom = Math.Min(Path.NumRows - 1, (int)nde.Y); Node bestNode = null; PathNode bestPNode = null; FInt bestWorth = FInt.F0; for (int row = top; row <= bottom; row++) { for (int col = left; col <= right; col++) { PathNode pn = Path.Grid[row, col]; VectorF pnPos = (VectorF)pn.Position; FInt collWorth = FInt.F0; if (Collision.segCollision(openNodes[0].Node.Pos, pnPos, ignoreSeg, ignoreNode, out collSeg, out collNode, out collGeo, true, Owner)) { if (collGeo.Count > 0 || collNode.Count > 0) { collWorth = FInt.FN1; } else { SegmentSkel closestSeg = null; VectorF closestInt = VectorF.Zero; FInt closestDist = FInt.F0; for (int i = 0; i < collSeg.Count; i++) { VectorF nextInt = Calc.LineIntersect(openNodes[0].Node.Pos, pnPos, collSeg[i].EndLoc[0], collSeg[i].EndLoc[1]); if (collSeg[i].Owner == Owner) { VectorF end0 = (collSeg[i].State[1] == SegmentSkel.SegState.Retracting) ? collSeg[i].EndLoc[0] : ((Segment)collSeg[i]).Nodes[0].Pos; VectorF end1 = (collSeg[i].State[0] == SegmentSkel.SegState.Retracting) ? collSeg[i].EndLoc[1] : ((Segment)collSeg[i]).Nodes[1].Pos; nextInt = Calc.LineIntersect(openNodes[0].Node.Pos, pnPos, end0, end1); } else { nextInt = Calc.LineIntersect(openNodes[0].Node.Pos, pnPos, collSeg[i].EndLoc[0], collSeg[i].EndLoc[1]); } FInt nextDist = VectorF.Distance(openNodes[0].Node.Pos, nextInt); if (closestSeg == null || nextDist < closestDist) { closestSeg = collSeg[i]; closestInt = nextInt; closestDist = nextDist; } } if (closestSeg.Owner == Owner) collWorth = FInt.FN1; else collWorth = FInt.F1; } } else { NodeType nType = Owner.InWorld.getNodeType(VectorF.Distance(openNodes[0].Node.Pos, pnPos)); if (pnPos.X - nType.Radius < 0 || pnPos.Y - nType.Radius < 0 || pnPos.X + nType.Radius >= Owner.InWorld.Width || pnPos.Y + nType.Radius >= Owner.InWorld.Height || Collision.nodeCollision(pnPos, nType.Radius, new List<SegmentSkel>(0), new List<NodeSkel>(0), true, Owner) || Collision.nodeCollNodeSpacing(pnPos, nType.Radius, nType.Spacing, Owner, new List<NodeSkel>(0), true)) { collWorth = FInt.FN1; } } if (collWorth != FInt.FN1) { // distance from destination node FInt destDist = VectorF.Distance(magnetPoint, pnPos); FInt worth = maxDist - destDist + collWorth; if (bestPNode == null || worth > bestWorth || (worth == bestWorth && new Random().Next(2) == 1)) { bestPNode = pn; bestWorth = worth; } } } } if (bestPNode != null) bestNode = openNodes[0].Node; // // FIND MOST USELESS NODE // for (int i = openNodes.Count - 1; i >= 0; i--) { if (!openNodes[i].Node.IsParent && openNodes[i].Node.NumSegments == 1) { uselessNode = openNodes[i].Node; break; } } // // FIND INTERSECTIONS // foreach (NodeDistPath fromNode in openNodes) { VectorF fromPos = fromNode.Node.Pos; nde = new VectorF(fromPos.X - maxDist, fromPos.Y - maxDist) / nSpacing; nde.X = (FInt)Math.Round((double)nde.X); nde.Y = (FInt)Math.Round((double)nde.Y); left = Math.Max(0, (int)nde.X); top = Math.Max(0, (int)nde.Y); nde = new VectorF(fromPos.X + maxDist, fromPos.Y + maxDist) / nSpacing; nde.X = (FInt)Math.Round((double)nde.X); nde.Y = (FInt)Math.Round((double)nde.Y); right = Math.Min(Path.NumCols - 1, (int)nde.X); bottom = Math.Min(Path.NumRows - 1, (int)nde.Y); nde = (Grid.Squares[0, 0].Nodes[0].Pos + Grid.Squares[0, 0].Nodes[1].Pos) / FInt.F2 / nSpacing; nde.X = (FInt)Math.Round((double)nde.X); nde.Y = (FInt)Math.Round((double)nde.Y); for (int row = top; row <= bottom; row++) { for (int col = left; col <= right; col++) { PathNode pn = Path.Grid[row, col]; VectorF pnPos = (VectorF)pn.Position; // distance from source node FInt srcDist = VectorF.Distance(fromPos, pnPos); // collisions ignoreNode = new List<NodeSkel>(1) { fromNode.Node }; ignoreSeg = new List<SegmentSkel>(fromNode.Node.NumSegments); foreach (Segment seg in fromNode.Node.Segments) { if (seg != null) ignoreSeg.Add(seg); } FInt collWorth = FInt.F0; if (Collision.segCollision(fromPos, pnPos, ignoreSeg, ignoreNode, out collSeg, out collNode, out collGeo, true, Owner) && collGeo.Count == 0 && collNode.Count == 0) { SegmentSkel closestSeg = null; VectorF closestInt = VectorF.Zero; FInt closestDist = FInt.F0; for (int i = 0; i < collSeg.Count; i++) { VectorF nextInt = Calc.LineIntersect(fromPos, pnPos, collSeg[i].EndLoc[0], collSeg[i].EndLoc[1]); if (collSeg[i].Owner == Owner) { VectorF end0 = (collSeg[i].State[1] == SegmentSkel.SegState.Retracting) ? collSeg[i].EndLoc[0] : ((Segment)collSeg[i]).Nodes[0].Pos; VectorF end1 = (collSeg[i].State[0] == SegmentSkel.SegState.Retracting) ? collSeg[i].EndLoc[1] : ((Segment)collSeg[i]).Nodes[1].Pos; nextInt = Calc.LineIntersect(fromPos, pnPos, end0, end1); } else { nextInt = Calc.LineIntersect(fromPos, pnPos, collSeg[i].EndLoc[0], collSeg[i].EndLoc[1]); } FInt nextDist = VectorF.Distance(fromPos, nextInt); if (closestSeg == null || nextDist < closestDist) { closestSeg = collSeg[i]; closestInt = nextInt; closestDist = nextDist; } } if (closestSeg.Owner == Owner) collWorth = FInt.F0; else collWorth = FInt.F1; // calculate worth FInt worth = collWorth * (maxDist - closestDist) * 2; if (bestPNode == null || worth > bestWorth || (worth == bestWorth && new Random().Next(2) == 1)) { bestNode = fromNode.Node; bestPNode = pn; bestWorth = worth; } } } } } // // MAKE DECISION // PlayerAction action = null; if (bestPNode != null && bestNode != null && (bestWorth > (FInt)3.5)) // if worth isn't high enough, don't do anything action = new PlayerAction(Owner, PlayerAction.ActionType.BuildSeg, bestNode.ID, false, (VectorF)bestPNode.Position); else if (uselessNode != null) action = new PlayerAction(Owner, PlayerAction.ActionType.DestroyNode, uselessNode.ID); if (action != null) { lock (Actions) Actions.Add(action); } } } #endregion Decision-Making #region Decision-Making if (false) { // Real thinking AI Node bestNode = null; PathNode bestPathNode = null; FInt bestWorth = FInt.F0; Node worstNode = null; PathNode worstPathNode = null; FInt worstWorth = FInt.F0; bool stillBuilding = false; FInt maxDist = (FInt)400; foreach (Node fromNode in Nodes) { if (!fromNode.Active) stillBuilding = true; if (!fromNode.Active || fromNode.NumSegments == fromNode.Segments.Length) continue; VectorF fromPos = fromNode.Pos; VectorF nSpacing = (VectorF)Path.NodeSpacing; VectorF nde = new VectorF(fromPos.X - maxDist, fromPos.Y - maxDist) / nSpacing; nde.X = (FInt)Math.Round((double)nde.X); nde.Y = (FInt)Math.Round((double)nde.Y); int left = Math.Max(0, (int)nde.X); int top = Math.Max(0, (int)nde.Y); nde = new VectorF(fromPos.X + maxDist, fromPos.Y + maxDist) / nSpacing; nde.X = (FInt)Math.Round((double)nde.X); nde.Y = (FInt)Math.Round((double)nde.Y); int right = Math.Min(Path.NumCols - 1, (int)nde.X); int bottom = Math.Min(Path.NumRows - 1, (int)nde.Y); FInt[,] worth = new FInt[bottom - top + 1, right - left + 1]; nde = (Grid.Squares[0, 0].Nodes[0].Pos + Grid.Squares[0, 0].Nodes[1].Pos) / FInt.F2 / nSpacing; nde.X = (FInt)Math.Round((double)nde.X); nde.Y = (FInt)Math.Round((double)nde.Y); VectorF toPos = (VectorF)Path.Grid[(int)nde.Y, (int)nde.X].Position; FInt totDist = VectorF.Distance(fromPos, toPos); for (int row = top; row <= bottom; row++) { for (int col = left; col <= right; col++) { PathNode pn = Path.Grid[row, col]; VectorF pnPos = (VectorF)pn.Position; // distance from source node FInt srcDist = VectorF.Distance(fromPos, pnPos); FInt srcDistWorth = FInt.F0; if (srcDist > maxDist || srcDist < (FInt)100) srcDistWorth = FInt.F1 / FInt.F2; else srcDistWorth = FInt.F1; // distance from destination node FInt destDist = VectorF.Distance(toPos, pnPos); FInt destDistWorth = FInt.F1 - (destDist / (maxDist * FInt.F2)); // collisions List<NodeSkel> ignoreNode = new List<NodeSkel>(1) { fromNode }; List<SegmentSkel> ignoreSeg = new List<SegmentSkel>(fromNode.NumSegments); foreach (Segment seg in fromNode.Segments) { if (seg != null) ignoreSeg.Add(seg); } FInt collWorth = FInt.F0; List<SegmentSkel> collSeg; List<NodeSkel> collNode; List<GeoSkel> collGeo; if (Collision.segCollision(fromPos, pnPos, ignoreSeg, ignoreNode, out collSeg, out collNode, out collGeo, true, Owner)) { if (collGeo.Count > 0 || collNode.Count > 0) { collWorth = FInt.F0; } else { SegmentSkel closestSeg = null; VectorF closestInt = VectorF.Zero; FInt closestDist = FInt.F0; for (int i = 0; i < collSeg.Count; i++) { VectorF nextInt = Calc.LineIntersect(fromPos, pnPos, collSeg[i].EndLoc[0], collSeg[i].EndLoc[1]); if (collSeg[i].Owner == Owner) { VectorF end0 = (collSeg[i].State[1] == SegmentSkel.SegState.Retracting) ? collSeg[i].EndLoc[0] : ((Segment)collSeg[i]).Nodes[0].Pos; VectorF end1 = (collSeg[i].State[0] == SegmentSkel.SegState.Retracting) ? collSeg[i].EndLoc[1] : ((Segment)collSeg[i]).Nodes[1].Pos; nextInt = Calc.LineIntersect(fromPos, pnPos, end0, end1); } else { nextInt = Calc.LineIntersect(fromPos, pnPos, collSeg[i].EndLoc[0], collSeg[i].EndLoc[1]); } FInt nextDist = VectorF.Distance(fromPos, nextInt); if (closestSeg == null || nextDist < closestDist) { closestSeg = collSeg[i]; closestInt = nextInt; closestDist = nextDist; } } if (closestSeg.Owner == Owner) collWorth = FInt.F0; else collWorth = FInt.F1; } } else { NodeType nType = Owner.InWorld.getNodeType(srcDist); if (pnPos.X - nType.Radius < 0 || pnPos.Y - nType.Radius < 0 || pnPos.X + nType.Radius >= Owner.InWorld.Width || pnPos.Y + nType.Radius >= Owner.InWorld.Height || Collision.nodeCollision(pnPos, nType.Radius, new List<SegmentSkel>(0), new List<NodeSkel>(0), true, Owner) || Collision.nodeCollNodeSpacing(pnPos, nType.Radius, nType.Spacing, Owner, new List<NodeSkel>(0), true)) { collWorth = FInt.F0; } else collWorth = FInt.F1 / FInt.F2; } // calculate worth worth[row - top, col - left] = (collWorth * FInt.F5) + (srcDistWorth * FInt.F2) + destDistWorth; if (worth[row - top, col - left] > bestWorth || (worth[row - top, col - left] == bestWorth && new Random().Next(2) == 1)) { bestNode = fromNode; bestPathNode = pn; bestWorth = worth[row - top, col - left]; } else if (fromNode.NumSegments == 1 && worth[row - top, col - left] < worstWorth || (worth[row - top, col - left] == worstWorth && new Random().Next(2) == 1)) { worstNode = fromNode; worstPathNode = pn; worstWorth = worth[row - top, col - left]; } } } } PlayerAction action = null; if (bestPathNode != null && bestNode != null && (!stillBuilding || bestWorth > (FInt)3.5)) // if worth isn't high enough, don't do anything action = new PlayerAction(Owner, PlayerAction.ActionType.BuildSeg, bestNode.ID, false, (VectorF)bestPathNode.Position); else if (worstPathNode != null && worstNode != null) action = new PlayerAction(Owner, PlayerAction.ActionType.DestroyNode, worstNode.ID); if (action != null) { lock (Actions) Actions.Add(action); } } #endregion Decision-Making } Waiting = false; }
/// <summary>Builds the dictionaries of all nodes and segments.</summary> public void buildDictionaries(NodeSkel[] nodes, SegmentSkel[] segs) { foreach (NodeSkel node in nodes) { NodeByID.Add(node.ID, node); } foreach (SegmentSkel seg in segs) { SegByID.Add(seg.ID, seg); } }
/// <summary>Determines whether or not the provided node is visible.</summary> /// <param name="node">The node to test.</param> /// <returns>Whether or not the provided node is visible.</returns> public bool isVisible(NodeSkel node) { if (node.Owner == Owner) return true; int topRow = Math.Max(0, (int)((node.Y - node.Radius) / SqrHeight)); int leftCol = Math.Max(0, (int)((node.X - node.Radius) / SqrWidth)); int bottomRow = Math.Min(NumRows - 1, (int)Calc.RoundUp((node.Y + node.Radius) / SqrHeight)); int rightCol = Math.Min(NumCols - 1, (int)Calc.RoundUp((node.X + node.Radius) / SqrWidth)); for (int row = topRow; row <= bottomRow; row++) { for (int col = leftCol; col <= rightCol; col++) { if (Grid[row, col] != VisOption.Visible) continue; FInt left = (FInt)col * SqrWidth; FInt right = left + SqrWidth; FInt top = (FInt)row * SqrHeight; FInt bottom = top + SqrHeight; FInt closestY = (node.Y >= top && node.Y <= bottom) ? node.Y : (node.Y > bottom) ? bottom : top; FInt closestX = (node.X >= left && node.X <= right) ? node.X : (node.X > right) ? right : left; if (VectorF.Distance(node.Pos, new VectorF(closestX, closestY)) <= node.Radius) return true; } } return false; }
/// <summary>Gets a skeleton version of this grid manager.</summary> /// <param name="forPlayer">The player that will use it. His nodes and segments are full instead of skeletons.</param> /// <param name="playerNodes">A list of all of the player's nodes.</param> /// <param name="playerSegs">A list of all of the player's segments.</param> /// <param name="allSegs">A list of all segments.</param> /// <param name="allNodes">A list of all nodes.</param> /// <returns>A skeleton version of this grid manager.</returns> public GridManager getSkeleton(Player forPlayer, out Node[] playerNodes, out Segment[] playerSegs, out NodeSkel[] allNodes, out SegmentSkel[] allSegs) { playerNodes = new Node[forPlayer.Nodes.Count]; playerSegs = new Segment[forPlayer.Segments.Count]; allNodes = new NodeSkel[InWorld.Nodes.Count]; allSegs = new SegmentSkel[InWorld.Segments.Count]; int nextPlyrNode = 0; int nextPlyrSeg = 0; int nextAllNode = 0; int nextAllSeg = 0; GridManager skeleton = new GridManager(NumCols, NumRows, InWorld, false); Dictionary<string, NodeSkel> nodes = new Dictionary<string, NodeSkel>(InWorld.Nodes.Count); Dictionary<string, SegmentSkel> segments = new Dictionary<string, SegmentSkel>(InWorld.Segments.Count); Dictionary<string, GeoSkel> geos = new Dictionary<string, GeoSkel>(InWorld.Geos.Count); // add nodes, segments, and geos to dictionary foreach (Node node in InWorld.Nodes) { if (node.Owner == forPlayer) { Node clone = new Node(node); nodes.Add(clone.ID, clone); playerNodes[nextPlyrNode] = clone; nextPlyrNode++; allNodes[nextAllNode] = clone; } else if (!node.Destroyed) { NodeSkel skel = node.getSkeleton(); nodes.Add(node.ID, skel); allNodes[nextAllNode] = skel; } nextAllNode++; } foreach (Segment seg in InWorld.Segments) { if (seg.Owner == forPlayer) { Segment clone = new Segment(seg); segments.Add(clone.ID, clone); playerSegs[nextPlyrSeg] = clone; nextPlyrSeg++; allSegs[nextAllSeg] = clone; nextAllSeg++; } else if (!seg.Destroyed) { SegmentSkel skel = seg.getSkeleton(); segments.Add(seg.ID, skel); allSegs[nextAllSeg] = skel; nextAllSeg++; } } foreach (Geo geo in InWorld.Geos) { geos.Add(geo.ID, geo.getSkeleton()); } // connect cloned nodes and segments to each other foreach (Node node in playerNodes) { for (int i = 0; i < node.Segments.Length; i++) { Segment seg = node.Segments[i]; if (seg != null) node.Segments[i] = (Segment)segments[seg.ID]; } for (int i = 0; i < node.Parents.Count; i++) { Node n = node.Parents[i]; node.Parents[i] = (Node)nodes[n.ID]; } } foreach (Segment seg in playerSegs) { for (int i = 0; i < seg.Nodes.Length; i++) { Node node = seg.Nodes[i]; if (!node.Destroyed) seg.Nodes[i] = (Node)nodes[node.ID]; else seg.Nodes[i] = new Node(node); } } // build grid skeleton.SqrSize = this.SqrSize; for (int row = 0; row < NumRows; row++) { for (int col = 0; col < NumRows; col++) { GridSqr sqr = Squares[row, col]; skeleton.Squares[row, col] = new GridSqr(new List<NodeSkel>(sqr.Nodes.Count), new List<SegmentSkel>(sqr.Segments.Count), new List<GeoSkel>(sqr.Geos.Count), new List<Hotspot>(sqr.Hotspots.Count), false); foreach (NodeSkel node in sqr.Nodes) { if (((Node)node).Active && !((Node)node).Destroyed) skeleton.Squares[row, col].Nodes.Add(nodes[node.ID]); } foreach (SegmentSkel seg in sqr.Segments) { if (!((Segment)seg).Destroyed) skeleton.Squares[row, col].Segments.Add(segments[seg.ID]); } foreach (GeoSkel geo in sqr.Geos) { skeleton.Squares[row, col].Geos.Add(geos[geo.ID]); } } } return skeleton; }
/// <summary>Gets a skeleton version of this node.</summary> /// <returns>A skeleton version of this node.</returns> public NodeSkel getSkeleton() { NodeSkel skeleton = new NodeSkel(); skeleton.Owner = this.Owner; skeleton.ID = this.ID; skeleton.NType = this.NType; skeleton.pos = this.pos; skeleton.Active = this.Active; skeleton.Radius = this.Radius; skeleton.GenSpacing = this.GenSpacing; skeleton.IsParent = this.IsParent; skeleton.SightDistance = this.SightDistance; skeleton.Spacing = this.Spacing; return skeleton; }
/// <summary>Searches for a collision between the provided line and any node or segment. Also finds the exact intersection point if segment collision.</summary> /// <param name="point1">The first point in the line.</param> /// <param name="point2">The second point in the line.</param> /// <param name="ignoreSeg">The segments to ignore.</param> /// <param name="ignoreNode">The nodes to ignore.</param> /// <param name="nodeColl">The node that collided with the provided line.</param> /// <param name="segColl">The segment that collided with the provided line.</param> /// <param name="geoColl">The geometric object that collided with the provided line.</param> /// <param name="intersection">The intersection point if intersecting a segment.</param> /// <returns>Whether or not there was a collision between the line and any node or segment.</returns> public bool segCollisionIntPoint(VectorF point1, VectorF point2, List<SegmentSkel> ignoreSeg, List<NodeSkel> ignoreNode, out SegmentSkel segColl, out NodeSkel nodeColl, out GeoSkel geoColl, out VectorF? intersection) { intersection = null; segColl = null; nodeColl = null; geoColl = null; object[] array = new object[8]; array[0] = point1; array[1] = point2; array[5] = false; array[6] = null; // look for collisions with segments List<SegmentSkel> segColls = new List<SegmentSkel>(1); array[2] = ignoreSeg; array[3] = segColls; array[4] = CollSrchType.GetIntersection; Grid.Line(point1, point2, array, gridSegCollSeg); if (segColls.Count > 0) { segColl = segColls[0]; intersection = (VectorF)array[7]; #if DEBUG if (array[7] == null) throw new Exception(segColls.Count.ToString() + " segment(s) was returned, but no intersections."); #endif } else // look for collisions with nodes and geos { // look for nodes List<NodeSkel> nodeColls = new List<NodeSkel>(1); array[2] = ignoreNode; array[3] = nodeColls; array[4] = CollSrchType.DoesItCollide; Grid.LineExpand(point1, point2, array, gridSegCollNode); if (nodeColls.Count > 0) { nodeColl = nodeColls[0]; } else // look for geos { List<GeoSkel> geoColls = new List<GeoSkel>(1); array[2] = null; array[3] = geoColls; array[4] = CollSrchType.DoesItCollide; Grid.LineExpand(point1, point2, array, gridSegCollGeo); if (geoColls.Count > 0) geoColl = geoColls[0]; } } return nodeColl != null || segColl != null || geoColl != null; }