using FNAF_Server.Map; using GlobalClassLib; namespace FNAF_Server.Enemies; public class RoamingPathfinder : Pathfinder { public Predicate AdditionalTileFilter{ get; set; } = _ => true; public Predicate AdditionalConnectorFilter{ get; set; } = _ => true; protected int tolerance; public List TakenPath { get; } = new(); private Random random = new(); // private Func defaultConnectorFilter; public RoamingPathfinder(Enemy enemy, int tolerance) : base(enemy) { this.tolerance = tolerance; } protected Dictionary Crawl(MapTile tile) { Dictionary distances = new(){ [tile] = 0 }; CrawlStep(tile, distances); return distances; } private void CrawlStep(MapTile tile, Dictionary distances) { List neighbours = GetNeighbours(tile, c => AdditionalConnectorFilter(c) && (!c.Blocked || c.Type == ConnectorType.DOOR_OFFICE) && tile != Enemy.Location && (!distances.ContainsKey(c.OtherTile(tile)) || distances[c.OtherTile(tile)] > distances[tile] + 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[tile] + tile.GetConnector(t)!.Value); if (neighbours.Count != 0){ neighbours.ForEach(t => CrawlStep(t, distances)); } } public override Decision DecideNext(MapTile goal) { Dictionary distances = Crawl(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(); } }