/// <summary> /// Constructor. Takes an existing grid view to create a view from, and getter/setter /// functions taking a map value and its corresponding position. /// </summary> /// <param name="baseGrid">Your underlying grid data.</param> /// <param name="getter">The TranslateGet implementation.</param> /// <param name="setter">The TranslateSet implementation.</param> public LambdaSettableTranslationGridView(ISettableGridView <T1> baseGrid, Func <Point, T1, T2> getter, Func <Point, T2, T1> setter) : base(baseGrid) { _getter = getter ?? throw new ArgumentNullException(nameof(getter)); _setter = setter ?? throw new ArgumentNullException(nameof(setter)); }
/// <inheritdoc /> public Area CreateTunnel(ISettableGridView <bool> map, Point start, Point end) { var lineAlgorithm = _adjacencyRule == AdjacencyRule.Cardinals ? Lines.Algorithm.Orthogonal : Lines.Algorithm.Bresenham; var area = new Area(); var previous = Point.None; foreach (var pos in Lines.Get(start, end, lineAlgorithm)) { map[pos] = true; area.Add(pos); // Previous cell, and we're going vertical, go 2 wide so it looks nicer Make sure not // to break rectangles (less than last index)! if (_doubleWideVertical && previous != Point.None && pos.Y != previous.Y && pos.X + 1 < map.Width - 1) { var wideningPos = pos + (1, 0); map[wideningPos] = true; area.Add(wideningPos); } previous = pos; } return(area); }
public void IndexerAccessGridViewTest() { var grid = new ArrayView <bool>(_width, _height); ISettableGridView <bool> setGridView = grid; IGridView <bool> gridView = grid; bool[] array = grid; // Set last entry via indexer syntax (via the ArrayView, to prove implicit implementations // work at all levels) grid[^ 1] = true;
/// <summary> /// Constructor. Takes an existing grid view to create a view from, and getter/setter /// functions taking only a value from the underlying representation. /// </summary> /// <remarks> /// If a position is also needed to perform the translation, an overload is provided taking /// corresponding functions. /// </remarks> /// <param name="baseGrid">Your underlying grid data.</param> /// <param name="getter">The TranslateGet implementation.</param> /// <param name="setter">The TranslateSet implementation.</param> public LambdaSettableTranslationGridView(ISettableGridView <T1> baseGrid, Func <T1, T2> getter, Func <T2, T1> setter) : base(baseGrid) { if (getter == null) { throw new ArgumentNullException(nameof(getter)); } if (setter == null) { throw new ArgumentNullException(nameof(setter)); } _getter = (pos, t1) => getter(t1); _setter = (pos, t2) => setter(t2); }
/// <summary> /// Sets all the values of the current grid view to be equal to the corresponding values from /// the grid view you pass in. /// </summary> /// <typeparam name="T" /> /// <param name="self" /> /// <param name="overlay"> /// The data apply to the view. Must have identical dimensions to the current view. /// </param> public static void ApplyOverlay <T>(this ISettableGridView <T> self, IGridView <T> overlay) { if (self.Height != overlay.Height || self.Width != overlay.Width) { throw new ArgumentException("Overlay size must match current grid view size."); } for (var y = 0; y < self.Height; ++y) { for (var x = 0; x < self.Width; ++x) { self[x, y] = overlay[x, y]; } } }
private static void CellAutoSmoothingAlgo(ISettableGridView <bool> map, ArrayView <bool> oldMap, bool bigAreaFill) { // Record current state of the map so we can compare to it to determine nearest walls oldMap.ApplyOverlay(map); // Iterate over inner square only to avoid messing with outer walls foreach (var pos in map.Bounds().Expand(-1, -1).Positions()) { if (CountWallsNear(oldMap, pos, 1) >= 5 || bigAreaFill && CountWallsNear(oldMap, pos, 2) <= 2) { map[pos] = false; } else { map[pos] = true; } } }
/// <summary> /// Constructor. Takes an existing grid view to create a view from and applies view data to it. /// </summary> /// <param name="baseGrid">Your underlying grid data.</param> /// <param name="overlay"> /// The view data to apply to the underlying representation. Must have identical dimensions to /// <paramref name="baseGrid" />. /// </param> /// <param name="getter">The TranslateGet implementation.</param> /// <param name="setter">The TranslateSet implementation.</param> public LambdaSettableTranslationGridView(ISettableGridView <T1> baseGrid, ISettableGridView <T2> overlay,
/// <inheritdoc /> protected override void SetInitialMapValues(ISettableGridView <MapGenTileState> map) => map.Fill(MapGenTileState.Wall);
/// <summary> /// Constructor. Takes an existing grid view to create a view from. /// </summary> /// <param name="baseGrid">A grid view exposing your underlying map data.</param> protected SettableTranslationGridView(ISettableGridView <T1> baseGrid) => BaseGrid = baseGrid;
/// <summary> /// Constructor. Takes the map view to represent. The viewport will represent the entire given map view. /// </summary> /// <param name="gridView">The map view to represent.</param> public SettableViewport(ISettableGridView <T> gridView) : base(gridView) { }
/// <summary> /// Constructor. /// </summary> /// <param name="transparencyView"> /// The values used to calculate field of view. Values of true are considered /// non-blocking (transparent) to line of sight, while false values are considered /// to be blocking. /// </param> /// <param name="resultView">The view in which FOV calculations are stored.</param> protected FOVBase(IGridView <bool> transparencyView, ISettableGridView <double> resultView) { ResultView = resultView; TransparencyView = transparencyView; BooleanResultView = new LambdaTranslationGridView <double, bool>(ResultView, val => val > 0.0); }
/// <inheritdoc /> public Area CreateTunnel(ISettableGridView <bool> map, int startX, int startY, int endX, int endY) => CreateTunnel(map, new Point(startX, startY), new Point(endX, endY));
/// <summary> /// Sets the values for each location of the current grid view to be equal to the value returned from the given /// function when given that position. /// </summary> /// <typeparam name="T" /> /// <param name="self" /> /// <param name="valueFunc"> /// Function returning data for each location in the grid view. /// </param> public static void ApplyOverlay <T>(this ISettableGridView <T> self, Func <Point, T> valueFunc) => self.ApplyOverlay(new LambdaGridView <T>(self.Width, self.Height, valueFunc));
/// <summary> /// Sets each location in the grid view to the value specified. /// </summary> /// <typeparam name="T" /> /// <param name="self" /> /// <param name="value">Value to fill the grid view with.</param> public static void Fill <T>(this ISettableGridView <T> self, T value) => self.ApplyOverlay(_ => value);
private static void ShadowCast(int row, double start, double end, int xx, int xy, int yx, int yy, double radius, int startX, int startY, double decay, ISettableGridView <double> lightMap, HashSet <Point> fovSet, IGridView <bool> map, Distance distanceStrategy) { double newStart = 0; if (start < end) { return; } var blocked = false; for (var distance = row; distance <= radius && distance < map.Width + map.Height && !blocked; distance++) { var deltaY = -distance; for (var deltaX = -distance; deltaX <= 0; deltaX++) { var currentX = startX + deltaX * xx + deltaY * xy; var currentY = startY + deltaX * yx + deltaY * yy; double leftSlope = (deltaX - 0.5f) / (deltaY + 0.5f); double rightSlope = (deltaX + 0.5f) / (deltaY - 0.5f); if (!(currentX >= 0 && currentY >= 0 && currentX < map.Width && currentY < map.Height) || start < rightSlope) { continue; } if (end > leftSlope) { break; } var deltaRadius = distanceStrategy.Calculate(deltaX, deltaY); // If within lightable area, light if needed if (deltaRadius <= radius) { var bright = 1 - decay * deltaRadius; if (bright > lightMap[currentX, currentY]) { lightMap[currentX, currentY] = bright; fovSet.Add(new Point(currentX, currentY)); } } if (blocked) // Previous cell was blocked { if (!map[currentX, currentY]) // Hit a wall... { newStart = rightSlope; } else { blocked = false; start = newStart; } } else { if (map[currentX, currentY] || !(distance < radius)) { continue; } blocked = true; ShadowCast(distance + 1, start, leftSlope, xx, xy, yx, yy, radius, startX, startY, decay, lightMap, fovSet, map, distanceStrategy); newStart = rightSlope; } } } }
private static void ShadowCastLimited(int row, double start, double end, int xx, int xy, int yx, int yy, double radius, int startX, int startY, double decay, ISettableGridView <double> lightMap, HashSet <Point> fovSet, IGridView <bool> map, Distance distanceStrategy, double angle, double span) { double newStart = 0; if (start < end) { return; } var blocked = false; for (var distance = row; distance <= radius && distance < map.Width + map.Height && !blocked; distance++) { var deltaY = -distance; for (var deltaX = -distance; deltaX <= 0; deltaX++) { var currentX = startX + deltaX * xx + deltaY * xy; var currentY = startY + deltaX * yx + deltaY * yy; double leftSlope = (deltaX - 0.5f) / (deltaY + 0.5f); double rightSlope = (deltaX + 0.5f) / (deltaY - 0.5f); if (!(currentX >= 0 && currentY >= 0 && currentX < map.Width && currentY < map.Height) || start < rightSlope) { continue; } if (end > leftSlope) { break; } var deltaRadius = distanceStrategy.Calculate(deltaX, deltaY); var at2 = Math.Abs(angle - MathHelpers.ScaledAtan2Approx(currentY - startY, currentX - startX)); // Check if within lightable area, light if needed if (deltaRadius <= radius && (at2 <= span * 0.5 || at2 >= 1.0 - span * 0.5)) { var bright = 1 - decay * deltaRadius; if (bright > lightMap[currentX, currentY]) { lightMap[currentX, currentY] = bright; fovSet.Add(new Point(currentX, currentY)); } } if (blocked) // Previous cell was blocking { if (!map[currentX, currentY]) // We hit a wall... { newStart = rightSlope; } else { blocked = false; start = newStart; } } else if (!map[currentX, currentY] && distance < radius) // Wall within line of sight { blocked = true; ShadowCastLimited(distance + 1, start, leftSlope, xx, xy, yx, yy, radius, startX, startY, decay, lightMap, fovSet, map, distanceStrategy, angle, span); newStart = rightSlope; } } } }
/// <summary> /// Constructor. Takes the parent map view, and the initial subsection of that map view to represent. /// </summary> /// <param name="gridView">The map view being represented.</param> /// <param name="viewArea">The initial subsection of that map to represent.</param> public SettableViewport(ISettableGridView <T> gridView, Rectangle viewArea) : base(gridView, viewArea) { }