/// <summary> /// To be called with each new floor, identifies blocked tiles in the pathCounters array with /// a value of -1. When hero discovers hidden doors or bashes open locked doors, call this /// again to update the map so new routes can be plotted. /// </summary> internal static void InitializeDistanceMap() { pathCounters = new int[FloorGenerator.FloorWidth, FloorGenerator.FloorHeight]; // DEBUG Feature if (GameConstants.DEBUG_MODE_DRAW_STEPS_TO_HERO) { pathCounterText = new GameText[FloorGenerator.FloorWidth, FloorGenerator.FloorHeight]; } for (int x = 0; x < FloorGenerator.FloorWidth; x++) { for (int y = 0; y < FloorGenerator.FloorHeight; y++) { pathCounters[x, y] = unmappedValue; if (!FloorGenerator.TileIsPassible(FloorGenerator.GetTileAt(x, y))) { pathCounters[x, y] = -1; } // DEBUG Feature if (GameConstants.DEBUG_MODE_DRAW_STEPS_TO_HERO) { pathCounterText[x, y] = new GameText(graphicsDevice); } } } }
/// <summary> /// Reveals any non-blocking tile in a straight line but stops at doors because the light in /// a lit room doesn't light up a hallway. /// </summary> /// <param name="startingPoint"></param> /// <param name="direction"></param> private void LightTilesInRow(Point startingPoint, GameConstants.Direction8 direction) { bool blocked = false; Point currentPoint = startingPoint; FloorTile.Surfaces currentSurface; while (!blocked) { currentSurface = FloorGenerator.GetTileAt(currentPoint); FloorGenerator.FloorRevealed[currentPoint.X, currentPoint.Y] = true; if (!FloorGenerator.TileIsPassible(currentSurface)) { blocked = true; } if (currentPoint == startingPoint) { blocked = false; // Allows effect if standing in doorway to split both ways } if (blocked) { break; } // Move to next tile currentPoint = FloorGenerator.PointInDirection(currentPoint, direction); } }
static internal bool InLineOfSight(Point startPt, Point endPt, int visibleDistance) { // for each point along the path from ptA to ptB, check the floor tile Vector2 path = new Vector2(endPt.X - startPt.X, endPt.Y - startPt.Y); Point checkPoint = Point.Zero; double targetDistance = Math.Floor(Distance(startPt, endPt)); if (targetDistance > visibleDistance) { return(false); } int testDistance = MathHelper.Min((int)targetDistance, visibleDistance); var theta = Math.Atan2(path.Y, path.X); for (int d = 1; d <= testDistance; d++) { var x = d * Math.Cos(theta); var y = d * Math.Sin(theta); checkPoint.X = (int)(startPt.X + x); checkPoint.Y = (int)(startPt.Y + y); // If outside boarder, space is not LOS if (checkPoint.X < 0 || checkPoint.Y < 0 || checkPoint.X > FloorGenerator.FloorWidth - 1 || checkPoint.Y > FloorGenerator.FloorHeight - 1) { return(false); } // If it's right next to you, such as monster on a door or you are on a door if (targetDistance <= 1) { return(true); } // If it's a wall, void, or closed door, it's not LOS var tile = FloorGenerator.GetTileAt(checkPoint); if (!FloorGenerator.TileIsPassible(tile) || tile == FloorTile.Surfaces.OpenDoor) // because this is also a light barrier { return(false); } // Keep checking until end of path reached } // Remaining spaces do not block monsters from being seen return(true); }
internal void ChaseTarget() { // Find lowest path values around monster that is not occupied by another monster Point nextPosition = location; // To help randomize movements a little when several options exist with the same value List <Point> possibleNextSteps = new List <Point>(); int lowestValue = int.MaxValue; for (int x = location.X - 1; x < location.X + 2; x++) { for (int y = location.Y - 1; y < location.Y + 2; y++) { if (pathCounters[x, y] > 0 && pathCounters[x, y] <= lowestValue && pathCounters[x, y] < unmappedValue && FloorGenerator.TileIsPassible(FloorGenerator.GetTileAt(x, y)) && MonsterFoundAt(new Point(x, y)) == null) { if (pathCounters[x, y] == lowestValue) { possibleNextSteps.Add(new Point(x, y)); } else { lowestValue = pathCounters[x, y]; possibleNextSteps.Clear(); nextPosition.X = x; nextPosition.Y = y; possibleNextSteps.Add(nextPosition); } } } } // Randomly pick one of the possible steps having the same value if (possibleNextSteps.Count > 0) { location = possibleNextSteps[Utility.Rand.Next(possibleNextSteps.Count)]; } // If path mapping disabled due to DEBUG_MODE_WALK_THROUGH_WALLS // then location will not change as the mapping will halt before the monster. CheckVisibility(); }
} // Monsters won't gain XP in this game internal void Roam() { // TODO: Make another Move method with path finding to get a monster moving toward a given point // For when an effect triggers all monsters to detect the hero or behavior should be less eratic. // This method should actually be used in the HeroStrikable method to move towards the hero. bool monsterMoved = false; Point checkPoint; while (!monsterMoved) { GameConstants.Direction8 moveDirection = (GameConstants.Direction8)rand.Next(8); checkPoint = FloorGenerator.PointInDirection(location, moveDirection); if (FloorGenerator.TileIsPassible(FloorGenerator.GetTileAt(checkPoint))) { location = checkPoint; monsterMoved = true; } } CheckVisibility(); }
/* Mapping the best route across the floor to the hero will require: * 1. Initialize int array with same size as floor plan using CreateMapForMonsters() * a. First time, all values are 0 * b. If a tile is not walkable, mark the space as -1 * c. Each reset of the array will only reset values > 0 back to 0 using ResetPathFinder() * 2. Starting from hero (with value 1), cycle outward adding 1 to hop count in any space in the * array not containing a value > 0 (or already blocked by -1). This continues until each * frenzied monster has been identified with a value > 0 in the array (at which point there is no * need to continue any furthur). * a. To cycle outward, each point in the array should be ran through an outer loop, testing each space * around it for a value > 0. * b. When a value > 0 is detected, pick the lowest value found and add 1 for the current space. * 3. During a monster turn, each monster checks the points immediately around him to pick a * point with the smallest number to move to. If this point is the hero, then the attack commences. * 4. The pathCounters array must be reset and recalculated with each move of the hero if, and * only if any one monster is triggered to seek out the hero. * */ internal static void CalculateDistanceMap() { ResetPositiveValues(); // Ignores blocked tiles with value of -1 // Step 2, set space with hero to 1 if (GameConstants.DEBUG_MODE_WALK_THROUGH_WALLS && FloorGenerator.MovementBlockedByFloor(DungeonGameEngine.Hero.Location)) { // Setting hero's tile to 1 when he walks on a wall causes everything to freeze so we need to // abort drawing the distance map. Monsters will stop moving if chasing the hero is true. return; } else { pathCounters[DungeonGameEngine.Hero.Location.X, DungeonGameEngine.Hero.Location.Y] = 1; } int outerlayer = 1; // Actualy one higher than outer layer to avoid repeating math in the for loop or <= operator. int layer; do { outerlayer++; for (layer = 1; layer < outerlayer; layer++) { foreach (var checkPoint in PointsInLayer(layer, DungeonGameEngine.Hero.Location)) { Rectangle floorBounds = new Rectangle(0, 0, FloorGenerator.FloorWidth, FloorGenerator.FloorHeight); if (!floorBounds.Contains(checkPoint)) { continue; } // Skip space if it's already blocked if (pathCounters[checkPoint.X, checkPoint.Y] == -1) { if (GameConstants.DEBUG_MODE_DRAW_STEPS_TO_HERO) { pathCounterText[checkPoint.X, checkPoint.Y].Text = pathCounters[checkPoint.X, checkPoint.Y].ToString(); } continue; } // First, check to see if space is still unmapped if (pathCounters[checkPoint.X, checkPoint.Y] == unmappedValue) { // Check all spaces around current space to get smallest value > 0, then add one to it var lowestPosValue = FindLowestPositiveValue(checkPoint.X, checkPoint.Y); if (lowestPosValue > 0 && lowestPosValue < unmappedValue && lowestPosValue < pathCounters[checkPoint.X, checkPoint.Y] + 1) { pathCounters[checkPoint.X, checkPoint.Y] = lowestPosValue + 1; } // Added in case DEBUG_MODE_WALK_THROUGH_WALLS enabled to // close up the path left on the blocking tiles if (GameConstants.DEBUG_MODE_WALK_THROUGH_WALLS && !FloorGenerator.TileIsPassible(FloorGenerator.GetTileAt(checkPoint))) { pathCounters[checkPoint.X, checkPoint.Y] = -1; } } if (GameConstants.DEBUG_MODE_DRAW_STEPS_TO_HERO) { if (pathCounters[checkPoint.X, checkPoint.Y] > 99) { pathCounterText[checkPoint.X, checkPoint.Y].Text = "XX"; } else { pathCounterText[checkPoint.X, checkPoint.Y].Text = pathCounters[checkPoint.X, checkPoint.Y].ToString(); } } } } } while (!GridMapped(outerlayer)); }