using GlobalClassLib; using ONDServer.Map; using ONDServer.Net; namespace ONDServer.Enemies; public class RoamingPathfinder(Enemy enemy, int tolerance) : Pathfinder(enemy) { public Predicate AdditionalTileFilter{ get; set; } = _ => true; public Predicate AdditionalConnectorFilter{ get; set; } = _ => true; protected int Tolerance = tolerance; public List TakenPath { get; } = new(); private Random random = new(); protected Dictionary Search(MapTile startingTile) { Dictionary distances = new(){ [startingTile] = 0 }; Queue tilesToSearch = new(); tilesToSearch.Enqueue(startingTile); while (tilesToSearch.Count > 0){ MapTile currentTile = tilesToSearch.Dequeue(); List neighbours = GetNeighbours(currentTile, c => AdditionalConnectorFilter(c) && (!c.Blocked || c.Type == ConnectorType.DOOR_OFFICE) && (!distances.ContainsKey(c.OtherTile(currentTile)) || distances[c.OtherTile(currentTile)] > distances[currentTile] + c.Value), t => AdditionalTileFilter(t) && (!Enemy.BlocksTile || EnemyManager.GetByLocation(t).All(e => !e.BlocksTile || e == Enemy)) && Server.Players.All(p => p.Value.State.OfficeTileId != t.Id)); neighbours.ForEach(t => { distances[t] = distances[currentTile] + currentTile.GetConnector(t)!.Value; tilesToSearch.Enqueue(t); }); } return distances; } public override Decision DecideNext(MapTile goal) { if (Enemy.Location == null) return Decision.Wait(); Dictionary distances = Search(goal); List closerTiles = GetNeighbours(Enemy.Location, c => AdditionalConnectorFilter(c) && !c.Blocked || c.Type == ConnectorType.DOOR_OFFICE, t => AdditionalTileFilter(t) && distances.ContainsKey(t) && distances[t] + t.GetConnector(Enemy.Location)!.Value <= distances[Enemy.Location] + Tolerance); if (closerTiles.Contains(goal)){ if (Enemy.Location.GetConnector(goal)!.Blocked){ return Decision.Reset(); } IEnumerable players = Server.Players.Values.Where(p => p.State.OfficeTileId == goal.Id); if (players.Count() == 1){ return Decision.Attack(players.First()); } return Decision.Move(goal); } if (closerTiles.Count != 0 && closerTiles.All(t => TakenPath.Contains(t))){ TakenPath.Clear(); return DecideNext(goal); } closerTiles.RemoveAll(t => TakenPath.Contains(t)); closerTiles.ForEach(t => distances[t] += Enemy.Location.GetConnector(t)!.Value); double roll = random.NextDouble() * closerTiles.Sum(t => 1.0 / distances[t]); foreach (var tile in closerTiles){ double value = 1.0 / distances[tile]; if (roll <= value){ TakenPath.Add(tile); return Decision.Move(tile); } roll -= value; } return Decision.Wait(); } }