public void ExploreDungeon(bool checkEnemies) { // expMap // -1 : impassable // 0 : uncharted (WHITE) // 1 : observed through FOV (GREY) // 2 : Visited or having no explorable tiles (BLACK) var expMap = Map.ExplorationMap; var moveRange = Speed / 5; var diagonalCount = 0; //bool foundLootable = false; //AdventureEntity currentLoot = null; while (moveRange > 0) { // check if we have an enemy in fov if (checkEnemies && CombatState == CombatState.None) { // check field of view positions for enemies foreach (var entity in Map.EntitiesOnCoords(FovCoords).Where(p => p.IsAlive)) { if (entity.Faction == Faction.None) { continue; } if (Faction != entity.Faction) { CombatState = CombatState.Initiation; entity.CombatState = CombatState.Initiation; } } // break if we have found an enemy if (CombatState == CombatState.Initiation) { break; } } // check if we have lootable entities in fov if (!_foundLootable && LootablesInSight(out var lootableEntities)) { // Searching for the nearest reachable loot. Coord[] nearestPath = null; foreach (var loot in lootableEntities) { //System.Console.WriteLine($"Is in reach {loot.Name}? {IsInReach(loot)}"); if (IsInReach(loot)) { //System.Console.WriteLine($"looting {loot.Name}"); Loot(loot); continue; } var path = Algorithms.AStar(Coordinate, loot.Coordinate, Map); if (nearestPath == null) { nearestPath = path; _currentLoot = loot; } else if (path != null && nearestPath.Length > path.Length) { nearestPath = path; _currentLoot = loot; } } if (nearestPath != null) { // Set a path for the objective. _foundLootable = true; _lastPath = nearestPath; _pathIndex = 2; if (!MoveAtomic(nearestPath[1], ref moveRange, ref diagonalCount)) { return; } continue; } } else if (_foundLootable && IsInReach(_currentLoot)) { Loot(_currentLoot); _foundLootable = false; _currentLoot = null; } // check if already have a path if (_pathIndex > 0) { if (_pathIndex != _lastPath.Length) { if (Coordinate == _lastPath[_pathIndex - 1]) { if (!MoveAtomic(_lastPath[_pathIndex++], ref moveRange, ref diagonalCount)) { return; } continue; } } else if (_foundLootable) { // This branch means this entity follows a proper path to the current loot, // and in terms of path there is only 1 step left to the loot // but the one step is diagonal so that it is not reachable from the current coordinate. // To handle this problem, set the next coordinate as one of the cardinally adjacent tiles // of the loot. if (Math.Abs(Coord.EuclideanDistanceMagnitude(_currentLoot.Coordinate - Coordinate) - 2) < float.Epsilon) { var loot = _currentLoot.Coordinate; var projected = loot.Translate(0, -(loot - Coordinate).Y); // Prefer X direction for now; can be randomized if (Map.WalkabilityMap[projected]) { MoveAtomic(projected, ref moveRange, ref diagonalCount); } else { projected = loot.Translate(-(loot - Coordinate).X, 0); if (Map.WalkabilityMap[projected]) { MoveAtomic(projected, ref moveRange, ref diagonalCount); } else { _foundLootable = false; _currentLoot = null; } } } else { _foundLootable = false; _currentLoot = null; } } } _pathIndex = -1; // Atomic movement; consider adjacent tiles first var adjs = Algorithms.GetReachableNeighbors(Map.WalkabilityMap, Coordinate); var max = 0; var maxIndex = -1; for (var i = 0; i < adjs.Length; i++) { // Not consider Black tiles if (expMap[adjs[i]] == 2) { continue; } var c = Map.ExpectedFovNum[adjs[i].X, adjs[i].Y]; // Calculate which adjacent tile has the greatest number of expected FOV tiles if (max < c) { max = c; maxIndex = i; } } Coord next; // If all adjacent tiles are Black, explorer have to find the nearest grey tile. if (maxIndex < 0) { Coord[] nearest; // Consider grey tiles in FOV first. If not any, check the whole map var inFov = FovCoords .Where(c => expMap[c] == 1) .ToArray(); if (inFov.Length > 0) { nearest = inFov .Select(c => Algorithms.AStar(Coordinate, c, Map)) .OrderBy(p => p.Length) .First(); } else { nearest = expMap.Positions() .Where(c => expMap[c] == 1) .OrderBy(p => Distance.EUCLIDEAN.Calculate(Coordinate, p)) .Take(5) .Where(p => p != Coord.NONE) .Select(p => Algorithms.AStar(Coordinate, p, Map)) .OrderBy(p => p.Length) .First(); } if (nearest?.Length < 2) { for (int i = 0; i < expMap.Width; i++) { for (int j = 0; j < expMap.Height; j++) { expMap[i, j] = 2; } } return; } next = nearest[1]; // Save the path _lastPath = nearest; _pathIndex = 2; } else { next = adjs[maxIndex]; } if (!MoveAtomic(next, ref moveRange, ref diagonalCount)) { return; } } }
public override bool CanSee(AdventureEntity entity) { return(entity != null && FovCoords.Any(p => entity.Coordinate == p)); }