public override void Apply(T map) { Grid.LocTest checkGround = (Loc testLoc) => { if (!Collision.InBounds(map.Width, map.Height, testLoc)) { return(false); } return(map.Tiles[testLoc.X][testLoc.Y].TileEquivalent(map.RoomTerrain) && !map.HasTileEffect(testLoc)); }; Grid.LocTest checkBlock = (Loc testLoc) => { if (!Collision.InBounds(map.Width, map.Height, testLoc)) { return(false); } return(map.Tiles[testLoc.X][testLoc.Y].TileEquivalent(map.WallTerrain)); }; List <LocRay4> rays = Detection.DetectWalls(((IViewPlaceableGenContext <MapGenEntrance>)map).GetLoc(0), new Rect(0, 0, map.Width, map.Height), checkBlock, checkGround); EffectTile effect = new EffectTile(LockedTile, true); TileListState state = new TileListState(); effect.TileStates.Set(state); List <Loc> freeTiles = new List <Loc>(); LocRay4? ray = PlaceRoom(map, rays, effect, freeTiles); if (ray != null) { PlaceEntities(map, freeTiles); } }
/// <summary> /// Returns a list of wall edges with definite 4-directional normals /// </summary> /// <param name="rect"></param> /// <param name="checkBlock">Determines if this is ground that can be burrowed into.</param> /// <param name="checkGround">Determines if this is ground that can reach a wall.</param> /// <returns></returns> public static List <LocRay4> DetectWalls(Rect rect, Grid.LocTest checkBlock, Grid.LocTest checkGround) { if (checkBlock == null) { throw new ArgumentNullException(nameof(checkBlock)); } List <LocRay4> walls = new List <LocRay4>(); for (int xx = rect.X; xx < rect.Width; xx++) { for (int yy = rect.Y; yy < rect.Height; yy++) { Loc testLoc = new Loc(xx, yy); if (checkBlock(testLoc)) { LocRay4 ray = GetWallDir(testLoc, checkBlock, checkGround); if (ray.Dir != Dir4.None) { walls.Add(ray); } } } } return(walls); }
public static bool IsCardinalPathBlocked(Loc start, Loc diff, Grid.LocTest checkBlock) { int sgn_x = Math.Sign(diff.X); int sgn_y = Math.Sign(diff.Y); int dx = Math.Abs(diff.X); int dy = Math.Abs(diff.Y); if (dx > 0) { for (int x = 1; x < dx; x++) { if (checkBlock(start + new Loc(x * sgn_x, 0))) { return(true); } } } else { for (int y = 1; y < dy; y++) { if (checkBlock(start + new Loc(0, y * sgn_y))) { return(true); } } } return(false); }
List <Loc> IGroupPlaceableGenContext <Team> .GetFreeTiles(Rect rect) { Grid.LocTest checkOp = (Loc testLoc) => { return(canPlaceItemTile(testLoc)); }; return(Grid.FindTilesInBox(rect.Start, rect.Size, checkOp)); }
protected List <Loc> getOpenItemTiles(Rect rect) { Grid.LocTest checkOp = (Loc testLoc) => { return(canPlaceItemTile(testLoc)); }; return(Grid.FindTilesInBox(rect.Start, rect.Size, checkOp)); }
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)); }
/// <summary> /// Returns a list of wall edges with definite 4-directional normals, connected to a start position /// </summary> /// <param name="start"></param> /// <param name="rect"></param> /// <param name="checkBlock">Determines if this is ground that can be burrowed into.</param> /// <param name="checkGround">Determines if this is ground that can reach a wall.</param> /// <returns></returns> public static List <LocRay4> DetectWalls(Loc start, Rect rect, Grid.LocTest checkBlock, Grid.LocTest checkGround) { if (checkBlock == null) { throw new ArgumentNullException(nameof(checkBlock)); } if (checkGround == null) { throw new ArgumentNullException(nameof(checkGround)); } bool[][] checkGrid = new bool[rect.Width][]; bool[][] fillGrid = new bool[rect.Width][]; for (int xx = 0; xx < rect.Width; xx++) { checkGrid[xx] = new bool[rect.Height]; fillGrid[xx] = new bool[rect.Height]; } List <LocRay4> walls = new List <LocRay4>(); // scan and find solely-facing walls // cache already-checked walls since flood fill occasionally checks twice Grid.FloodFill( rect, (Loc testLoc) => { if (fillGrid[testLoc.X][testLoc.Y]) { return(true); } if (!checkGrid[testLoc.X][testLoc.Y] && checkBlock(testLoc)) { LocRay4 ray = GetWallDir(testLoc, checkBlock, checkGround); if (ray.Dir != Dir4.None) { walls.Add(ray); } } checkGrid[testLoc.X][testLoc.Y] = true; return(!checkGround(testLoc)); }, (Loc testLoc) => true, (Loc fillLoc) => fillGrid[fillLoc.X][fillLoc.Y] = true, start); return(walls); }
private List <Loc> getOpenEntranceExitTiles(Rect rect) { List <Loc> tiles = getOpenItemTiles(rect); if (tiles.Count > 0) { return(tiles); } Grid.LocTest checkLenient = (Loc testLoc) => { return(Tiles[testLoc.X][testLoc.Y].TileEquivalent(RoomTerrain)); }; tiles = Grid.FindTilesInBox(rect.Start, rect.Size, checkLenient); return(tiles); }
private bool CanPlaceRect(T map, Rect rect, Grid.LocTest checkBlock) { for (int ii = rect.Left; ii < rect.Right; ii++) { for (int jj = rect.Top; jj < rect.Bottom; jj++) { Loc testLoc = new Loc(ii, jj); if (!Collision.InBounds(map.Width, map.Height, testLoc)) { return(false); } if (!checkBlock(testLoc)) { return(false); } } } return(true); }
public static BlobMap DetectBlobs(Rect rect, Grid.LocTest isValid) { if (isValid == null) { throw new ArgumentNullException(nameof(isValid)); } var blobMap = new BlobMap(rect.Width, rect.Height); for (int xx = rect.X; xx < rect.End.X; xx++) { for (int yy = rect.Y; yy < rect.End.Y; yy++) { if (isValid(new Loc(xx, yy)) && blobMap.Map[xx][yy] == -1) { var blob = new BlobMap.Blob(new Rect(xx, yy, 1, 1), 0); // fill the area, keeping track of the total area and blob bounds Grid.FloodFill( rect, (Loc testLoc) => (!isValid(testLoc) || blobMap.Map[testLoc.X][testLoc.Y] != -1), (Loc testLoc) => true, (Loc fillLoc) => { blobMap.Map[fillLoc.X][fillLoc.Y] = blobMap.Blobs.Count; blob.Bounds = Rect.IncludeLoc(blob.Bounds, fillLoc); blob.Area += 1; }, new Loc(xx, yy)); blobMap.Blobs.Add(blob); } } } return(blobMap); }
public static bool IsInFOV(Loc start, Loc end, Grid.LocTest checkBlock) { Loc diff = end - start; int dx = Math.Abs(diff.X); int dy = Math.Abs(diff.Y); if (dx <= 1 && dy <= 1) { return(true); } if (start.Y == end.Y || start.X == end.X) { return(!IsCardinalPathBlocked(start, diff, checkBlock)); } else { //signs int sgn_x = Math.Sign(diff.X); int sgn_y = Math.Sign(diff.Y); //xy swiz if (dy > dx) { return(IsPathClear(start, 1, dy, dx, 0, sgn_x, sgn_y, 0, (dx * SLOPE_GRANULARITY + SLOPE_GRANULARITY / 2) * 2 / (dy * 2 - 1), (dx * SLOPE_GRANULARITY - SLOPE_GRANULARITY / 2) * 2 / (dy * 2 + 1), checkBlock)); } else { return(IsPathClear(start, 1, dx, dy, sgn_x, 0, 0, sgn_y, (dy * SLOPE_GRANULARITY + SLOPE_GRANULARITY / 2) * 2 / (dx * 2 - 1), (dy * SLOPE_GRANULARITY - SLOPE_GRANULARITY / 2) * 2 / (dx * 2 + 1), checkBlock)); } } }
public override void Apply(T map) { if (Spawns.Count > 0) { List <Loc> freeTiles = new List <Loc>(); //get all places that traps are eligible for (int ii = 0; ii < map.RoomPlan.RoomCount; ii++) { IRoomGen room = map.RoomPlan.GetRoom(ii); List <Loc> tiles = ((IPlaceableGenContext <EffectTile>)map).GetFreeTiles(room.Draw); freeTiles.AddRange(tiles); } // add tile if (freeTiles.Count > 0) { int randIndex = ((IGenContext)map).Rand.Next(freeTiles.Count); Loc loc = freeTiles[randIndex]; EffectTile spawnedTrap = Spawns.Pick(((IGenContext)map).Rand); map.PlaceItem(loc, spawnedTrap); freeTiles.RemoveAt(randIndex); if (GuardSpawns.Count > 0) { for (int ii = 0; ii < 10; ii++) { Team newTeam = GuardSpawns.Pick(((IGenContext)map).Rand).Spawn(map); if (newTeam == null) { continue; } //spawn guards Grid.LocTest checkSpawnOpen = (Loc testLoc) => { return(((IGroupPlaceableGenContext <Team>)map).CanPlaceItem(testLoc)); }; Grid.LocTest checkSpawnBlock = (Loc testLoc) => { return(map.TileBlocked(testLoc)); }; Grid.LocTest checkDiagSpawnBlock = (Loc testLoc) => { return(map.TileBlocked(testLoc, true)); }; List <Loc> resultLocs = new List <Loc>(); foreach (Loc resultLoc in Grid.FindClosestConnectedTiles(new Loc(), new Loc(map.Width, map.Height), checkSpawnOpen, checkSpawnBlock, checkDiagSpawnBlock, loc, newTeam.MemberGuestCount)) { resultLocs.Add(resultLoc); } if (resultLocs.Count >= newTeam.MemberGuestCount) { Loc[] locs = new Loc[newTeam.MemberGuestCount]; for (int jj = 0; jj < locs.Length; jj++) { locs[jj] = resultLocs[jj]; } map.PlaceItems(newTeam, locs); break; } } } } } }
public override void Apply(T map) { Grid.LocTest checkBlock = (Loc testLoc) => { return(!map.Tiles[testLoc.X][testLoc.Y].TileEquivalent(map.RoomTerrain) || map.HasTileEffect(testLoc)); }; //choose a room to put the chest in //do not choose a room that would cause disconnection of the floor List <int> possibleRooms = new List <int>(); for (int ii = 0; ii < map.RoomPlan.RoomCount; ii++) { FloorRoomPlan testPlan = map.RoomPlan.GetRoomPlan(ii); //if (map.RoomPlan.IsChokePoint(new RoomHallIndex(ii, false))) // continue; if (!BaseRoomFilter.PassesAllFilters(testPlan, this.Filters)) { continue; } //also do not choose a room that contains the end stairs IViewPlaceableGenContext <MapGenExit> exitMap = (IViewPlaceableGenContext <MapGenExit>)map; if (Collision.InBounds(testPlan.RoomGen.Draw, exitMap.GetLoc(0))) { continue; } possibleRooms.Add(ii); } if (possibleRooms.Count == 0) { return; } List <Loc> freeTiles = new List <Loc>(); IRoomGen room = null; while (possibleRooms.Count > 0) { int chosenRoom = map.Rand.Next(possibleRooms.Count); room = map.RoomPlan.GetRoom(possibleRooms[chosenRoom]); //get all places that the chest is eligible freeTiles = Grid.FindTilesInBox(room.Draw.Start, room.Draw.Size, (Loc testLoc) => { if (map.Tiles[testLoc.X][testLoc.Y].TileEquivalent(map.RoomTerrain) && !map.HasTileEffect(testLoc) && map.Tiles[testLoc.X][testLoc.Y + 1].TileEquivalent(map.RoomTerrain) && !map.HasTileEffect(new Loc(testLoc.X, testLoc.Y + 1)) && !map.PostProcGrid[testLoc.X][testLoc.Y].Status[(int)PostProcType.Panel] && !map.PostProcGrid[testLoc.X][testLoc.Y].Status[(int)PostProcType.Item]) { if (Grid.GetForkDirs(testLoc, checkBlock, checkBlock).Count < 2) { foreach (MapItem item in map.Items) { if (item.TileLoc == testLoc) { return(false); } } foreach (Team team in map.AllyTeams) { foreach (Character testChar in team.EnumerateChars()) { if (testChar.CharLoc == testLoc) { return(false); } } } foreach (Team team in map.MapTeams) { foreach (Character testChar in team.EnumerateChars()) { if (testChar.CharLoc == testLoc) { return(false); } } } return(true); } } return(false); }); if (freeTiles.Count > 0) { break; } possibleRooms.RemoveAt(chosenRoom); } //can't find any free tile in any room, return if (freeTiles.Count == 0) { return; } if (!ItemThemes.CanPick) { return; } //choose which item theme to work with ItemTheme chosenItemTheme = ItemThemes.Pick(map.Rand); //the item spawn list in this class dictates the items available for spawning //it will be queried for items that match the theme selected List <MapItem> chosenItems = chosenItemTheme.GenerateItems(map, Items); if (chosenItems.Count == 0) { return; } int randIndex = map.Rand.Next(freeTiles.Count); Loc loc = freeTiles[randIndex]; EffectTile spawnedChest = new EffectTile(37, true); if (Ambush && MobThemes.CanPick) { spawnedChest.Danger = true; //the mob theme will be selected randomly MobTheme chosenMobTheme = MobThemes.Pick(map.Rand); //the mobs in this class are the ones that would be available when the game wants to spawn things outside of the floor's spawn list //it will be queried for monsters that match the theme provided List <MobSpawn> chosenMobs = chosenMobTheme.GenerateMobs(map, Mobs); MobSpawnState mobSpawn = new MobSpawnState(); foreach (MobSpawn mob in chosenMobs) { MobSpawn copyMob = mob.Copy(); if (map.Rand.Next(ALT_COLOR_ODDS) == 0) { copyMob.BaseForm.Skin = 1; } mobSpawn.Spawns.Add(copyMob); } spawnedChest.TileStates.Set(mobSpawn); } ItemSpawnState itemSpawn = new ItemSpawnState(); itemSpawn.Spawns = chosenItems; spawnedChest.TileStates.Set(itemSpawn); Rect wallBounds = new Rect(room.Draw.X - 1, room.Draw.Y - 1, room.Draw.Size.X + 2, room.Draw.Size.Y + 2); spawnedChest.TileStates.Set(new BoundsState(wallBounds)); ((IPlaceableGenContext <EffectTile>)map).PlaceItem(loc, spawnedChest); map.PostProcGrid[loc.X][loc.Y].Status[(int)PostProcType.Panel] = true; map.PostProcGrid[loc.X][loc.Y].Status[(int)PostProcType.Item] = true; GenContextDebug.DebugProgress("Placed Chest"); }
public static bool IsPathClear(Loc start, int startCol, int endCol, int endRow, int xx, int xy, int yx, int yy, int startSlope, int endSlope, Grid.LocTest checkBlock) { bool prevBlocked = false; int savedRightSlope = -SLOPE_GRANULARITY; for (int currentCol = startCol; currentCol <= endCol; currentCol++) { int xc = currentCol; for (int yc = currentCol; yc >= 0; yc--) { int gridX = start.X + xc * xx + yc * xy; int gridY = start.Y + xc * yx + yc * yy; int leftBlockSlope = (yc * SLOPE_GRANULARITY + SLOPE_GRANULARITY / 2) * 2 / (xc * 2 - 1); int rightBlockSlope = (yc * SLOPE_GRANULARITY - SLOPE_GRANULARITY / 2) * 2 / (xc * 2 + 1); if (rightBlockSlope > startSlope) // Block is above the left edge of our view area; skip. { continue; } else if (leftBlockSlope < endSlope) // Block is below the right edge of our view area; we're done. { break; } if (currentCol == endCol && yc == endRow) { return(true); } bool curBlocked = checkBlock(new Loc(gridX, gridY)); if (prevBlocked) { if (curBlocked) { savedRightSlope = rightBlockSlope; } else { prevBlocked = false; startSlope = savedRightSlope; } } else { if (curBlocked) { if (leftBlockSlope <= startSlope) { if (IsPathClear(start, currentCol + 1, endCol, endRow, xx, xy, yx, yy, startSlope, leftBlockSlope, checkBlock)) { return(true); } } prevBlocked = true; savedRightSlope = rightBlockSlope; } } } if (prevBlocked) { break; } } return(false); }
/// <summary> /// Detects if an added blob disconnects the map's existing connectivity. /// </summary> /// <param name="mapBounds"></param> /// <param name="isMapValid">Checks for a valid path tile.</param> /// <param name="blobDest">Position to draw the blob at.</param> /// <param name="blobSize"></param> /// <param name="isBlobValid">Checks for a valid blob tile. Loc is with respect to the top right of the blob rect.</param> /// <param name="countErasures">Whether a completely erased graph counts as disconnected or not.</param> /// <returns></returns> public static bool DetectDisconnect(Rect mapBounds, Grid.LocTest isMapValid, Loc blobDest, Loc blobSize, Grid.LocTest isBlobValid, bool countErasures) { if (isMapValid == null) { throw new ArgumentNullException(nameof(isMapValid)); } if (isBlobValid == null) { throw new ArgumentNullException(nameof(isBlobValid)); } List <int> mapBlobCounts = new List <int>(); int[][] fullGrid = new int[mapBounds.Width][]; int[][] splitGrid = new int[mapBounds.Width][]; for (int xx = 0; xx < mapBounds.Width; xx++) { fullGrid[xx] = new int[mapBounds.Height]; splitGrid[xx] = new int[mapBounds.Height]; for (int yy = 0; yy < mapBounds.Height; yy++) { fullGrid[xx][yy] = -1; splitGrid[xx][yy] = -1; } } // iterate the map and flood fill when finding a walkable. // Count the number of times a flood fill is required. This is the blob count. for (int xx = 0; xx < mapBounds.Width; xx++) { for (int yy = 0; yy < mapBounds.Height; yy++) { if (isMapValid(new Loc(xx, yy)) && fullGrid[xx][yy] == -1) { int totalFill = 0; Grid.FloodFill( new Rect(0, 0, mapBounds.Width, mapBounds.Height), (Loc testLoc) => (fullGrid[testLoc.X][testLoc.Y] == mapBlobCounts.Count) || !isMapValid(testLoc), (Loc testLoc) => true, (Loc fillLoc) => { fullGrid[fillLoc.X][fillLoc.Y] = mapBlobCounts.Count; totalFill++; }, new Loc(xx, yy)); mapBlobCounts.Add(totalFill); } } } // we've passed in a boolean grid containing a blob, with an offset of where to render it to for (int xx = Math.Max(0, blobDest.X); xx < Math.Min(mapBounds.Width, blobDest.X + blobSize.X); xx++) { for (int yy = Math.Max(0, blobDest.Y); yy < Math.Min(mapBounds.Height, blobDest.Y + blobSize.Y); yy++) { int blobIndex = fullGrid[xx][yy]; if (blobIndex > -1 && isBlobValid(new Loc(xx, yy) - blobDest)) { mapBlobCounts[blobIndex] = mapBlobCounts[blobIndex] - 1; fullGrid[xx][yy] = -1; } } } // remove the blobs that have been entirely erased; return false if entirely erased. for (int ii = mapBlobCounts.Count - 1; ii >= 0; ii--) { if (mapBlobCounts[ii] == 0) { if (countErasures) { return(true); } mapBlobCounts.RemoveAt(ii); } } // iterate the map and flood fill when finding a walkable (needs a new bool grid), this time discounting tiles involved in the blob. count times needed for this int blobsFound = 0; for (int xx = 0; xx < mapBounds.Width; xx++) { for (int yy = 0; yy < mapBounds.Height; yy++) { if (fullGrid[xx][yy] > -1 && splitGrid[xx][yy] == -1) { Grid.FloodFill( new Rect(0, 0, mapBounds.Width, mapBounds.Height), (Loc testLoc) => (splitGrid[testLoc.X][testLoc.Y] == blobsFound) || (fullGrid[testLoc.X][testLoc.Y] == -1), (Loc testLoc) => true, (Loc fillLoc) => splitGrid[fillLoc.X][fillLoc.Y] = blobsFound, new Loc(xx, yy)); blobsFound++; } } } // more times = more blobs = failure return(blobsFound != mapBlobCounts.Count); }
public override void Apply(T map) { //first get all free tiles suitable for the switch List <Loc> freeSwitchTiles = ((IPlaceableGenContext <EffectTile>)map).GetAllFreeTiles(); if (freeSwitchTiles.Count == 0) { return; } Grid.LocTest checkGround = (Loc testLoc) => { if (!Collision.InBounds(map.Width, map.Height, testLoc)) { return(false); } return(map.Tiles[testLoc.X][testLoc.Y].TileEquivalent(map.RoomTerrain) && !map.HasTileEffect(testLoc)); }; Grid.LocTest checkBlock = (Loc testLoc) => { if (!Collision.InBounds(map.Width, map.Height, testLoc)) { return(false); } return(map.Tiles[testLoc.X][testLoc.Y].TileEquivalent(map.WallTerrain)); }; List <LocRay4> rays = Detection.DetectWalls(((IViewPlaceableGenContext <MapGenEntrance>)map).GetLoc(0), new Rect(0, 0, map.Width, map.Height), checkBlock, checkGround); EffectTile effect = new EffectTile(SealedTile, true); List <Loc> freeTiles = new List <Loc>(); List <LocRay4> createdEntrances = new List <LocRay4>(); int amount = EntranceCount.Pick(map.Rand); for (int ii = 0; ii < amount; ii++) { LocRay4?ray = PlaceRoom(map, rays, effect, freeTiles); if (ray != null) { createdEntrances.Add(ray.Value); } } if (createdEntrances.Count > 0) { PlaceEntities(map, freeTiles); EffectTile switchTile = new EffectTile(SwitchTile, true); if (TimeLimit) { switchTile.Danger = true; } TileListState state = new TileListState(); for (int mm = 0; mm < createdEntrances.Count; mm++) { state.Tiles.Add(new Loc(createdEntrances[mm].Loc)); } switchTile.TileStates.Set(state); int randIndex = map.Rand.Next(freeSwitchTiles.Count); ((IPlaceableGenContext <EffectTile>)map).PlaceItem(freeSwitchTiles[randIndex], switchTile); } }
protected LocRay4?PlaceRoom(T map, List <LocRay4> rays, EffectTile sealingTile, List <Loc> freeTiles) { Grid.LocTest checkBlockForPlace = (Loc testLoc) => { if (!Collision.InBounds(map.Width, map.Height, testLoc)) { return(false); } return(!map.Tiles[testLoc.X][testLoc.Y].TileEquivalent(map.RoomTerrain) && !map.Tiles[testLoc.X][testLoc.Y].TileEquivalent(map.UnbreakableTerrain)); }; //try X times to dig a passage for (int ii = 0; ii < 500 && rays.Count > 0; ii++) { int rayIndex = map.Rand.Next(rays.Count); LocRay4 ray = rays[rayIndex]; rays.RemoveAt(rayIndex); Loc rayDirLoc = ray.Dir.GetLoc(); Axis4 axis = ray.Dir.ToAxis(); Axis4 orth = axis == Axis4.Horiz ? Axis4.Vert : Axis4.Horiz; int minLength = Math.Max(1, HallLength.Min); Rect hallBound = new Rect(ray.Loc + DirExt.AddAngles(ray.Dir, Dir4.Left).GetLoc(), new Loc(1)); hallBound = Rect.IncludeLoc(hallBound, ray.Loc + rayDirLoc * (minLength - 1) + DirExt.AddAngles(ray.Dir, Dir4.Right).GetLoc()); //make sure the MIN hall can tunnel unimpeded if (!CanPlaceRect(map, hallBound, checkBlockForPlace)) { continue; } for (int jj = 0; jj < 100; jj++) { //plan the room RoomGen <T> plan = GenericRooms.Pick(map.Rand).Copy(); Loc size = plan.ProposeSize(map.Rand); plan.PrepareSize(map.Rand, size); //attempt to place the bounds somewhere, anywhere, within the limitations that the room itself provides List <int> candidateOpenings = new List <int>(); int planLength = plan.GetBorderLength(ray.Dir.Reverse()); for (int kk = 0; kk < planLength; kk++) { if (plan.GetFulfillableBorder(ray.Dir.Reverse(), kk)) { candidateOpenings.Add(kk); } } //as well as continue extending the hall until we hit a walkable. int tunnelLen = Math.Max(1, HallLength.Pick(map.Rand)); Loc roomLoc = ray.Loc + rayDirLoc * tunnelLen; int perpOffset = candidateOpenings[map.Rand.Next(candidateOpenings.Count)]; roomLoc += orth.CreateLoc(-perpOffset, 0); if (rayDirLoc.GetScalar(axis) < 0)//move back the top-left of the entrance { roomLoc += rayDirLoc * (size.GetScalar(axis) - 1); } Rect roomTestBound = new Rect(roomLoc, size); roomTestBound.Inflate(1, 1); //make a rect for the rest of the hall Rect hallExtBound = new Rect(ray.Loc + rayDirLoc * minLength + DirExt.AddAngles(ray.Dir, Dir4.Left).GetLoc(), new Loc(1)); hallExtBound = Rect.IncludeLoc(hallBound, ray.Loc + rayDirLoc * (tunnelLen - 1) + DirExt.AddAngles(ray.Dir, Dir4.Right).GetLoc()); //now that we've chosen our position, let's test it if (!CanPlaceRect(map, roomTestBound, checkBlockForPlace) || !CanPlaceRect(map, hallExtBound, checkBlockForPlace)) // also test that the CHOSEN hallway can be properly sealed { continue; //invalid location, try another place } else { plan.SetLoc(roomLoc); plan.ReceiveBorderRange(new IntRange(perpOffset, perpOffset + 1) + roomLoc.GetScalar(orth), ray.Dir.Reverse()); //draw the room plan.DrawOnMap(map); //surround the room with bounds for (int xx = roomTestBound.X; xx < roomTestBound.Right; xx++) { map.Tiles[xx][roomTestBound.Y] = (Tile)map.UnbreakableTerrain.Copy(); map.Tiles[xx][roomTestBound.End.Y - 1] = (Tile)map.UnbreakableTerrain.Copy(); } for (int yy = roomTestBound.Y + 1; yy < roomTestBound.Bottom - 1; yy++) { map.Tiles[roomTestBound.X][yy] = (Tile)map.UnbreakableTerrain.Copy(); map.Tiles[roomTestBound.End.X - 1][yy] = (Tile)map.UnbreakableTerrain.Copy(); } //spawn tiles, items, foes List <Loc> addedTiles = ((IPlaceableGenContext <MapItem>)map).GetFreeTiles(plan.Draw); freeTiles.AddRange(addedTiles); //tunnel to the room Loc loc = ray.Loc; for (int tt = 0; tt < tunnelLen; tt++) { //make walkable map.Tiles[loc.X][loc.Y] = (Tile)map.RoomTerrain.Copy(); //make left side unbreakable Loc lLoc = loc + DirExt.AddAngles(ray.Dir, Dir4.Left).GetLoc(); map.Tiles[lLoc.X][lLoc.Y] = (Tile)map.UnbreakableTerrain.Copy(); //make right side unbreakable Loc rLoc = loc + DirExt.AddAngles(ray.Dir, Dir4.Right).GetLoc(); map.Tiles[rLoc.X][rLoc.Y] = (Tile)map.UnbreakableTerrain.Copy(); loc += rayDirLoc; } //finally, seal with a locked door map.Tiles[ray.Loc.X][ray.Loc.Y] = (Tile)map.UnbreakableTerrain.Copy(); EffectTile newEffect = new EffectTile(sealingTile, ray.Loc); ((IPlaceableGenContext <EffectTile>)map).PlaceItem(ray.Loc, newEffect); return(ray); } } } //DiagManager.Instance.LogInfo("Couldn't place sealed detour!"); return(null); }
public static void CalculateAnalogFOV(Loc rectStart, Loc rectSize, Loc start, Grid.LocTest checkBlock, LightOperation lightOp) { // Viewer's cell is always visible. if (Collision.InBounds(rectStart, rectSize, start)) { lightOp(start.X, start.Y, 1f); } for (int dir = 0; dir < DirExt.DIR8_COUNT; dir++) { CastPartialLight(rectStart, rectSize, start, checkBlock, lightOp, 1, 3 * SLOPE_GRANULARITY, -SLOPE_GRANULARITY, (Dir8)dir); } }
private static void CastPartialLight(Loc rectStart, Loc rectSize, Loc start, Grid.LocTest checkBlock, LightOperation lightOp, int startColumn, int startSlope, int endSlope, Dir8 dir) { // Set true if the previous cell we encountered was blocked. bool prevBlocked = false; int savedRightSlope = -SLOPE_GRANULARITY; int colVal = OctantTransform[(int)dir, 0] != 0 ? OctantTransform[(int)dir, 0] : OctantTransform[(int)dir, 2]; Loc colDiff = new Loc(); if (colVal > 0) { colDiff = rectStart + rectSize - start - new Loc(1); } else { colDiff = start - rectStart; } int maxCol = OctantTransform[(int)dir, 0] != 0 ? colDiff.X : colDiff.Y; int rowVal = OctantTransform[(int)dir, 3] != 0 ? OctantTransform[(int)dir, 3] : OctantTransform[(int)dir, 1]; Loc rowDiff = new Loc(); if (rowVal > 0) { rowDiff = rectStart + rectSize - start - new Loc(1); } else { rowDiff = start - rectStart; } int maxRow = OctantTransform[(int)dir, 3] != 0 ? rowDiff.Y : rowDiff.X; for (int currentCol = startColumn; currentCol <= maxCol; currentCol++) { int xc = currentCol; for (int yc = Math.Min(maxRow, currentCol); yc >= 0; yc--) { int gridX = start.X + xc * OctantTransform[(int)dir, 0] + yc * OctantTransform[(int)dir, 1]; int gridY = start.Y + xc * OctantTransform[(int)dir, 2] + yc * OctantTransform[(int)dir, 3]; //due to safeguards, this block should never be hit if (!Collision.InBounds(rectStart, rectSize, new Loc(gridX, gridY))) { continue; } int leftBlockSlope = (yc * SLOPE_GRANULARITY + SLOPE_GRANULARITY / 2) * 2 / (xc * 2 - 1); int rightBlockSlope = (yc * SLOPE_GRANULARITY - SLOPE_GRANULARITY / 2) * 2 / (xc * 2 + 1); if (rightBlockSlope > startSlope) // Block is above the left edge of our view area; skip. { continue; } else if (leftBlockSlope < endSlope) // Block is below the right edge of our view area; we're done. { break; } float lighting = 1f; if (leftBlockSlope > startSlope) // Block is above the left edge of our view area; skip. { lighting = 0.5f; } else if (rightBlockSlope < endSlope) // Block is below the right edge of our view area; we're done. { lighting = 0.5f; } if (((int)dir % 2 == 0 || yc != 0) && ((int)dir % 2 == 1 || yc != currentCol)) { lightOp(gridX, gridY, lighting); } bool curBlocked = checkBlock(new Loc(gridX, gridY)); if (prevBlocked) { if (curBlocked) { savedRightSlope = rightBlockSlope; } else { prevBlocked = false; startSlope = savedRightSlope; } } else { if (curBlocked) { if (leftBlockSlope <= startSlope) { CastPartialLight(rectStart, rectSize, start, checkBlock, lightOp, currentCol + 1, startSlope, leftBlockSlope, dir); } prevBlocked = true; savedRightSlope = rightBlockSlope; } } } if (prevBlocked) { break; } } }
public List <Character> RespawnMob() { List <Character> respawns = new List <Character>(); if (TeamSpawns.Count > 0) { bool[][] traversedGrid = new bool[Width][]; for (int xx = 0; xx < Width; xx++) { traversedGrid[xx] = new bool[Height]; } List <Loc> freeTiles = new List <Loc>(); Grid.FloodFill(new Rect(new Loc(), new Loc(Width, Height)), (Loc testLoc) => { if (traversedGrid[testLoc.X][testLoc.Y]) { return(true); } return(TileBlocked(testLoc)); }, (Loc testLoc) => { if (traversedGrid[testLoc.X][testLoc.Y]) { return(true); } return(TileBlocked(testLoc, true)); }, (Loc testLoc) => { traversedGrid[testLoc.X][testLoc.Y] = true; //must be walkable, not have a nonwalkable on at least 3 cardinal directions, not be within eyesight of any of the player characters foreach (Character character in ActiveTeam.Players) { if (character.IsInSightBounds(testLoc)) { return; } } foreach (Team team in MapTeams) { foreach (Character character in team.Players) { if (!character.Dead && character.CharLoc == testLoc) { return; } } } freeTiles.Add(testLoc); }, EntryPoints[0].Loc); if (freeTiles.Count > 0) { for (int ii = 0; ii < 10; ii++) { Team newTeam = TeamSpawns.Pick(Rand).Spawn(this); if (newTeam == null) { continue; } Loc trialLoc = freeTiles[Rand.Next(freeTiles.Count)]; //find a way to place all members- needs to fit all of them in, or else fail the spawn Grid.LocTest checkOpen = (Loc testLoc) => { if (TileBlocked(testLoc)) { return(false); } Character locChar = GetCharAtLoc(testLoc); if (locChar != null) { return(false); } return(true); }; Grid.LocTest checkBlock = (Loc testLoc) => { return(TileBlocked(testLoc, true)); }; Grid.LocTest checkDiagBlock = (Loc testLoc) => { return(TileBlocked(testLoc, true, true)); }; List <Loc> resultLocs = new List <Loc>(); foreach (Loc loc in Grid.FindClosestConnectedTiles(new Loc(), new Loc(Width, Height), checkOpen, checkBlock, checkDiagBlock, trialLoc, newTeam.Players.Count)) { resultLocs.Add(loc); } if (resultLocs.Count >= newTeam.Players.Count) { for (int jj = 0; jj < newTeam.Players.Count; jj++) { newTeam.Players[jj].CharLoc = resultLocs[jj]; } MapTeams.Add(newTeam); foreach (Character member in newTeam.Players) { member.RefreshTraits(); respawns.Add(member); } break; } } } } return(respawns); }
private GameAction DumbAvoid(Character controlledChar, bool preThink, List <Character> seenCharacters, CharIndex ownIndex, IRandom rand) { StablePriorityQueue <double, Dir8> candidateDirs = new StablePriorityQueue <double, Dir8>(); //choose the single direction that avoids other characters the most bool respectPeers = !preThink; for (int ii = -1; ii < DirExt.DIR8_COUNT; ii++) { Loc checkLoc = controlledChar.CharLoc + ((Dir8)ii).GetLoc(); double dirDistance = 0; //iterated in increasing character indices foreach (Character seenChar in seenCharacters) { if (RunFromAllies && !RunFromFoes) { //only avoid if their character index is lower than this one, aka higher ranking member CharIndex seenIndex = ZoneManager.Instance.CurrentMap.GetCharIndex(seenChar); if (seenIndex.Team > ownIndex.Team) { break; } else if (seenIndex.Team == ownIndex.Team) { if (seenIndex.Char > ownIndex.Char && seenChar.MemberTeam.LeaderIndex != seenIndex.Char) { continue; } } } dirDistance += Math.Sqrt((checkLoc - seenChar.CharLoc).DistSquared()); } candidateDirs.Enqueue(-dirDistance, (Dir8)ii); } Grid.LocTest checkDiagBlock = (Loc testLoc) => { return(ZoneManager.Instance.CurrentMap.TileBlocked(testLoc, controlledChar.Mobility, true)); //enemy/ally blockings don't matter for diagonals }; Grid.LocTest checkBlock = (Loc testLoc) => { if (ZoneManager.Instance.CurrentMap.TileBlocked(testLoc, controlledChar.Mobility)) { return(true); } if ((IQ & AIFlags.TrapAvoider) != AIFlags.None) { Tile tile = ZoneManager.Instance.CurrentMap.Tiles[testLoc.X][testLoc.Y]; if (tile.Effect.ID > -1) { TileData entry = DataManager.Instance.GetTile(tile.Effect.ID); if (entry.StepType == TileData.TriggerType.Trap || entry.StepType == TileData.TriggerType.Site || entry.StepType == TileData.TriggerType.Switch) { return(true); } } } if (respectPeers && BlockedByChar(testLoc, Alignment.Self | Alignment.Foe)) { return(true); } return(false); }; //try each direction from most appealing to least appealing, stopping if we get to "none" while (candidateDirs.Count > 0) { Dir8 highestDir = candidateDirs.Dequeue(); if (highestDir == Dir8.None) { if (AbortIfCornered)//this plan will be aborted, try the next plan in the list { return(null); } else//cry in a corner { return(new GameAction(GameAction.ActionType.Wait, Dir8.None)); } } else { //check to see if we can walk this way if (!Grid.IsDirBlocked(controlledChar.CharLoc, highestDir, checkBlock, checkDiagBlock)) { return(TrySelectWalk(controlledChar, highestDir)); } } } if (AbortIfCornered)//this plan will be aborted, try the next plan in the list { return(null); } else//cry in a corner { return(new GameAction(GameAction.ActionType.Wait, Dir8.None)); } }