public static LocRay4 GetWallDir(Loc loc, Grid.LocTest checkBlock, Grid.LocTest checkGround) { if (checkBlock == null) { throw new ArgumentNullException(nameof(checkBlock)); } if (checkGround == null) { throw new ArgumentNullException(nameof(checkGround)); } // check the four directions Dir4 chosenDir = Dir4.None; // ensure that there is only one direction where it is unblocked foreach (Dir4 dir in DirExt.VALID_DIR4) { Loc newLoc = loc + dir.GetLoc(); if (checkGround(newLoc)) { if (chosenDir != Dir4.None) { return(new LocRay4(loc)); } else { chosenDir = dir.Reverse(); } } else if (!checkBlock(newLoc)) { // all four directions must be valid ground, or valid block return(new LocRay4(loc)); } } if (chosenDir == Dir4.None) { return(new LocRay4(loc)); } // then check to make sure that the left and right diagonal of this direction are also valid blocked Loc lLoc = loc + DirExt.AddAngles(chosenDir.ToDir8(), Dir8.DownLeft).GetLoc(); if (!checkBlock(lLoc)) { return(new LocRay4(loc)); } Loc rLoc = loc + DirExt.AddAngles(chosenDir.ToDir8(), Dir8.DownRight).GetLoc(); if (!checkBlock(rLoc)) { return(new LocRay4(loc)); } return(new LocRay4(loc, chosenDir)); }
private static void ScanFill( Rect rect, LocTest checkBlock, LocTest checkDiagBlock, LocAction fillOp, int min, int max, int range_min, int range_max, int y, bool isNext, DirV dir, Stack <ScanLineTile> stack) { if (checkBlock == null) { throw new ArgumentNullException(nameof(checkBlock)); } if (fillOp == null) { throw new ArgumentNullException(nameof(fillOp)); } // move y down or up int new_y = y + dir.GetLoc().Y; // for diagonal checking: check slightly further int sub = (range_min > rect.Start.X) ? 1 : 0; int add = (range_max < rect.Start.X + rect.Size.X - 1) ? 1 : 0; int line_start = -1; int x = range_min - sub; for (; x <= range_max + add; x++) { bool unblocked = !checkBlock(new Loc(x, new_y)); // check diagonal if applicable if (x < range_min) { unblocked &= !IsDirBlocked(new Loc(range_min, y), DirExt.Combine(DirH.Left, dir), checkBlock, checkDiagBlock); } else if (x > range_max) { unblocked &= !IsDirBlocked(new Loc(range_max, y), DirExt.Combine(DirH.Right, dir), checkBlock, checkDiagBlock); } // skip testing, if testing previous line within previous range bool empty = (isNext || (x <min || x> max)) && unblocked; if (line_start == -1 && empty) { line_start = x; } else if (line_start > -1 && !empty) { stack.Push(new ScanLineTile(new IntRange(line_start, x - 1), new_y, dir, line_start <= range_min, x > range_max)); line_start = -1; } if (line_start > -1) { fillOp(new Loc(x, new_y)); } if (!isNext && x == min) { x = max; } } if (line_start > -1) { stack.Push(new ScanLineTile(new IntRange(line_start, x - 1), new_y, dir, line_start <= range_min, true)); } }
protected static ListPathTraversalNode?GetRoomToConnect(FloorPlan floorPlan, RoomHallIndex chosenFrom, Dir4 dir) { // extend a rectangle to the border of the floor in the chosen direction bool vertical = dir.ToAxis() == Axis4.Vert; int dirSign = dir.GetLoc().GetScalar(dir.ToAxis()); IRoomGen genFrom = floorPlan.GetRoomHall(chosenFrom).RoomGen; Rect sampleRect = genFrom.Draw; // expand from the start of that border direction to the borders of the floor sampleRect.Start += dir.GetLoc() * sampleRect.Size.GetScalar(dir.ToAxis()); // it doesn't have to be exactly the borders so just add the total size to be sure sampleRect.Expand(dir, vertical ? floorPlan.Size.Y : floorPlan.Size.X); // find the closest room. var chosenTo = new RoomHallIndex(-1, false); foreach (RoomHallIndex collision in floorPlan.CheckCollision(sampleRect)) { Rect collidedRect = floorPlan.GetRoomHall(collision).RoomGen.Draw; // limit the expansion by direction int sampleScalar = sampleRect.GetScalar(dir); int collidedScalar = collidedRect.GetScalar(dir.Reverse()); bool limit = dirSign == Math.Sign(sampleScalar - collidedScalar); if (limit) { // update the boundaries sampleRect.SetScalar(dir, collidedScalar); chosenTo = collision; } } // if it didn't collide with ANYTHING, then quit if (chosenTo.Index == -1) { return(null); } IRoomGen genTo = floorPlan.GetRoomHall(chosenTo).RoomGen; // narrow the rectangle if touching something on the side // widen the rectangle by width Rect widthRect = sampleRect; widthRect.Inflate(vertical ? 1 : 0, vertical ? 0 : 1); bool retractLeft = false; bool retractRight = false; Dir4 leftDir = DirExt.AddAngles(dir, Dir4.Left); Dir4 rightDir = DirExt.AddAngles(dir, Dir4.Right); foreach (RoomHallIndex collision in floorPlan.CheckCollision(widthRect)) { Rect collidedRect = floorPlan.GetRoomHall(collision).RoomGen.Draw; if (!retractLeft) { if (collidedRect.GetScalar(leftDir.Reverse()) == sampleRect.GetScalar(leftDir)) { retractLeft = true; } } if (!retractRight) { if (collidedRect.GetScalar(rightDir.Reverse()) == sampleRect.GetScalar(rightDir)) { retractRight = true; } } } // retract the rectangle if (retractLeft) { sampleRect.Expand(leftDir, -1); } if (retractRight) { sampleRect.Expand(rightDir, -1); } // if the rectangle has been retracted too much, we can't go on if (sampleRect.Area <= 0) { return(null); } // check for border availability between start and end bool foundFrom = HasBorderOpening(genFrom, sampleRect, dir); bool foundTo = HasBorderOpening(genTo, sampleRect, dir.Reverse()); // return the expansion if one is found if (foundFrom && foundTo) { return(new ListPathTraversalNode(chosenFrom, chosenTo, sampleRect)); } else { return(null); } }