/// <summary> /// Method that switfly finds the best path from start to end. Doesn't reverse outcome /// </summary> /// <returns>The end breadcrump where each next is a step back)</returns> private static BreadCrumb FindPathReversed(Level world, BotMap level, Point3D start, Point3D end) { MinHeap <BreadCrumb> openList = new MinHeap <BreadCrumb>(256); BreadCrumb[, ,] brWorld = new BreadCrumb[world.Size.x, world.Size.y, world.Size.z]; BreadCrumb node; Point3D tmp; int cost; int diff; BreadCrumb current = new BreadCrumb(start); current.cost = 0; BreadCrumb finish = new BreadCrumb(end); try { brWorld[current.position.X, current.position.Y, current.position.Z] = current; } catch { return(current); } openList.Add(current); while (openList.Count > 0) { //Find best item and switch it to the 'closedList' current = openList.ExtractFirst(); current.onClosedList = true; //Find neighbours for (int i = 0; i < surrounding.Length; i++) { tmp = current.position + surrounding[i]; if ((tmp.X <= -1 || tmp.Y <= -1 || tmp.Z <= -1) || (tmp.X >= level.Size.x || tmp.Y >= level.Size.y || tmp.Z >= level.Size.z)) { break; } TriBool block = false; try { block = level.AirMap[tmp.X, tmp.Z, tmp.Y]; //Check if block is air } catch { } if (block != TriBool.Unknown) { //Check if we've already examined a neighbour, if not create a new node for it. if (brWorld[tmp.X, tmp.Y, tmp.Z] == null) { node = new BreadCrumb(tmp); brWorld[tmp.X, tmp.Y, tmp.Z] = node; } else { node = brWorld[tmp.X, tmp.Y, tmp.Z]; } //If the node is not on the 'closedList' check it's new score, keep the best if (!node.onClosedList) { diff = 0; if (current.position.X != node.position.X) { diff += 1; } if (current.position.Y != node.position.Y) { diff += 1; } if (current.position.Z != node.position.Z) { diff += 1; } if (block == false) //Solid but breakable, allows bot to go through solid areas { diff += 50; } cost = current.cost + diff + node.position.GetDistanceSquared(end); if (cost < node.cost) { node.cost = cost; node.next = current; } //If the node wasn't on the openList yet, add it if (!node.onOpenList) { //Check to see if we're done if (node.Equals(finish)) { node.next = current; return(node); } node.onOpenList = true; openList.Add(node); } } } } } return(null); //no path found }
/// <summary> /// Handles bot AI /// </summary> public static void HandleBots() { foreach (Bot Bot in Server.Bots.ToArray()) { Random Random = new Random(); if (Bot.Movement) { Vector3S TemporaryLocation = new Vector3S(Bot.Player.Pos.x, Bot.Player.Pos.z, Bot.Player.Pos.y); string PlayerName = ""; if (Bot.FollowPlayers) { #region Find Closest Player bool HitAPlayer = false; Vector3S ClosestLocation = Bot.Player.Level.Size * 32; foreach (Player p in Server.Players) { if (p.Level == Bot.Player.Level) { TargetPlayerArgs eargs = new TargetPlayerArgs(p); bool cancel = OnBotTargetPlayer.Call(Bot, eargs).Canceled; if (!cancel) { if (Bot.BlackListPlayers.ContainsKey(p.Username)) { if (Math.Abs(Bot.BlackListPlayers[p.Username] - Bot.shouldCheckAgainLoopInt) > 100) { Bot.BlackListPlayers.Remove(p.Username); } } if (p.Pos - Bot.Player.Pos < ClosestLocation - Bot.Player.Pos && !Bot.BlackListPlayers.ContainsKey(p.Username)) { HitAPlayer = true; ClosestLocation = new Vector3S(p.Pos); PlayerName = p.Username; if (Math.Abs((ClosestLocation.x / 32) - (Bot.LastPos.x / 32)) >= 12 || Math.Abs((ClosestLocation.z / 32) - (Bot.LastPos.z / 32)) >= 12) { Bot.shouldCheckAgainLoopInt = 1000; } } } } } #endregion if (HitAPlayer) { Vector3S TempLocation = new Vector3S(Bot.Player.Pos); TemporaryLocation = new Vector3S(Bot.Player.Pos); Vector3S Pathfound = new Vector3S(Bot.Player.Pos); #region AStar if (Bot.shouldCheckAgain || Bot.Waypoint == null) { Bot.Waypoint = Pathfind(Bot, ClosestLocation); Bot.LastPos = ClosestLocation; } try { Pathfound.x = (short)(Bot.Waypoint.position.X * 32); Pathfound.z = (short)(Bot.Waypoint.position.Z * 32); Pathfound.y = (short)(Bot.Waypoint.position.Y * 32); } catch { Bot.shouldCheckAgainLoopInt = 1000; try { Bot.BlackListPlayers.Add(PlayerName, Bot.shouldCheckAgainLoopInt); } catch { } break; } if (Bot.intLoop >= 2) //Slows down the bots so they arent insta-propogate, it slows them a bit too much though, need to fix { //Also makes them a bit less accurate than instant, but much more accurate than Vector2D.Move() Bot.intLoop = 0; Bot.Waypoint = Bot.Waypoint.next; } else { Bot.intLoop += 1; } TemporaryLocation.x += (short)((Pathfound.x - TemporaryLocation.x) / 2); TemporaryLocation.z += (short)((Pathfound.z - TemporaryLocation.z) / 2); //TemporaryLocation.y += (short)((Pathfound.y - TemporaryLocation.y) / 2); #endregion Block Block1 = Bot.Player.Level.GetBlock(TemporaryLocation / 32); Block Block2 = Bot.Player.Level.GetBlock((TemporaryLocation.x / 32), (TemporaryLocation.z / 32), (TemporaryLocation.y / 32) - 1); Block BlockUnderneath = Bot.Player.Level.GetBlock((TemporaryLocation.x / 32), (TemporaryLocation.z / 32), (TemporaryLocation.y / 32) - 2); Block BlockAbove = Bot.Player.Level.GetBlock((TemporaryLocation.x / 32), (TemporaryLocation.z / 32), (TemporaryLocation.y / 32) + 1); Vector3S delta = new Vector3S((short)Math.Abs(ClosestLocation.x - TemporaryLocation.x), (short)Math.Abs(ClosestLocation.z - TemporaryLocation.z), (short)Math.Abs(ClosestLocation.y - TemporaryLocation.y)); if (Block.CanWalkThrough(BlockUnderneath) && Block.CanWalkThrough(Block2) && !Block.CanEscalate(Block1) && !Block.CanEscalate(Block2)) { TemporaryLocation.y -= 21; } if (Block.CanWalkThrough(Block1) && !Block.CanWalkThrough(Block2) && !Block.CanWalkThrough(BlockUnderneath)) { TemporaryLocation.y += 21; } else if (Block.CanEscalate(Block1) && Block.CanEscalate(Block2) && Pathfound.y > TemporaryLocation.y) { TemporaryLocation.y += 21; } else if (Block.CanWalkThrough(BlockAbove) && !Block.CanWalkThrough(BlockUnderneath) && Pathfound.y > TemporaryLocation.y && !Block.IsOPBlock(BlockUnderneath)) { TemporaryLocation.y += 21; Bot.Player.Level.BlockChange((ushort)(TemporaryLocation.x / 32), (ushort)(TemporaryLocation.z / 32), (ushort)((TemporaryLocation.y / 32) - 2), 1); } else if (!Block.CanWalkThrough(BlockAbove) && !Block.CanWalkThrough(BlockUnderneath) && !Block.IsOPBlock(BlockAbove)) { Bot.Player.Level.BlockChange((ushort)(TemporaryLocation.x / 32), (ushort)(TemporaryLocation.z / 32), (ushort)((TemporaryLocation.y / 32) + 1), 0); } if (Block.CanWalkThrough(BlockUnderneath) && !Block.CanWalkThrough(Bot.Player.Level.GetBlock((Bot.Player.oldPos.x / 32), (Bot.Player.oldPos.z / 32), (Bot.Player.oldPos.y / 32) - 2)) && !Block.IsOPBlock(BlockUnderneath) && Pathfound.y > TemporaryLocation.y) { Bot.Player.Level.BlockChange((ushort)(TemporaryLocation.x / 32), (ushort)(TemporaryLocation.z / 32), (ushort)((TemporaryLocation.y / 32) - 2), 1); } if ((!Block.IsOPBlock(Block1) && !Block.IsOPBlock(Block2)) && (!Block.CanWalkThrough(Block1) && !Block.CanWalkThrough(Block2)) && (Block1 != Block.BlockList.UNKNOWN && Block2 != Block.BlockList.UNKNOWN)) { Bot.Player.Level.BlockChange(TemporaryLocation / 32, 0); Bot.Player.Level.BlockChange((ushort)(TemporaryLocation.x / 32), (ushort)(TemporaryLocation.z / 32), (ushort)((TemporaryLocation.y / 32) - 1), 0); } if (!Block.CanWalkThrough(BlockUnderneath) && (Pathfound.y / 32) < (TemporaryLocation.y / 32) && !Block.IsOPBlock(BlockUnderneath)) { Bot.Player.Level.BlockChange((ushort)(TemporaryLocation.x / 32), (ushort)(TemporaryLocation.z / 32), (ushort)((TemporaryLocation.y / 32) - 2), 0); } MoveEventArgs eargs = new MoveEventArgs(TemporaryLocation, Bot.Player.Pos); bool cancel = OnBotMove.Call(Bot, eargs).Canceled; if (cancel) { TemporaryLocation = TempLocation; } } } Bot.Player.Pos = TemporaryLocation; Bot.Player.UpdatePosition(true); //Pls leave this true, bots dont appear properly otherwise } } }