public void GenerateFeatures(PosArray<CellType> map,List<pos> interesting_tiles) { List<DungeonFeature> features = new List<DungeonFeature>(); foreach(DungeonFeature df in Enum.GetValues(typeof(DungeonFeature))){ features.Add(df); } int[] rarity = null; switch(level_types[current_level-1]){ case LevelType.Standard: rarity = new int[]{30,40,15,30, 25,6,8,15,15,3,3,4,4,4}; break; case LevelType.Cave: rarity = new int[]{30,15,10,15, 15,100,8,10,30,5,25,6,3,4}; break; case LevelType.Mine: rarity = new int[]{30,20,5,16, 18,6,7,15,10,3,5,30,1,1}; break; case LevelType.Hive: rarity = new int[]{30,15,100,50, 50,100,4,10,10,100,100,15,25,0}; break; case LevelType.Fortress: rarity = new int[]{30,100,100,100, 100,1,8,25,8,6,1,100,20,15}; break; case LevelType.Garden: rarity = new int[]{20,50,50,0, 20,30,20,20,20,20,5,5,8,15}; break; case LevelType.Crypt: rarity = new int[]{30,50,50,25, 25,30,8,10,15,20,30,5,8,8}; break; case LevelType.Slime: //todo default: rarity = new int[]{30,20,15,12, 10,4,8,10,7,3,3,3,4,10}; break; } /*int[] rarity = new int[]{30,20,15,12, 10,4,8,10,7,3,3,3,4}; int[] frequency = new int[]{1,1,2,2,3,3,3, 4,4,4,4,2,2,5,5,5,6,5,5,8};*/ int[] removal_chance = new int[]{95,20,10,60, 30,25,70,50,60,35,12,10,10,20}; /*List<DungeonFeature> feature_pool = new List<DungeonFeature>(); for(int i=0;i<20;++i){ for(int j=frequency[i];j>0;--j){ feature_pool.Add(features[i]); } }*/ List<DungeonFeature> feature_pool = new List<DungeonFeature>(); while(feature_pool.Count < 3){ feature_pool.Clear(); for(int i=0;i<14;++i){ if(rarity[i] > 0 && R.OneIn(rarity[i])){ feature_pool.Add(features[i]); } } } List<DungeonFeature> selected_features = new List<DungeonFeature>(); for(int i=0;i<5 && feature_pool.Count > 0;++i){ selected_features.Add(feature_pool.RemoveRandom()); } List<DungeonFeature> result = new List<DungeonFeature>(); for(int count=5;count>0 && selected_features.Count > 0;--count){ DungeonFeature df = selected_features.Random(); if(R.PercentChance(removal_chance[(int)df])){ selected_features.Remove(df); } result.Add(df); } List<pos> thin_walls = null; if(result.Contains(DungeonFeature.CRACKED_WALL)){ thin_walls = map.AllPositions().Where(x=>map[x].IsWall() && x.HasOppositePairWhere(true,y=>y.BoundsCheck(tile) && map[y].IsFloor())); } while(result.Count > 0){ DungeonFeature df = result.RemoveRandom(); switch(df){ case DungeonFeature.POOL_OF_RESTORATION: case DungeonFeature.FIRE_PIT: { for(int i=0;i<50;++i){ int rr = R.Roll(ROWS-4)+1; int rc = R.Roll(COLS-4)+1; if(interesting_tiles.Count > 0){ pos p = interesting_tiles.RemoveRandom(); rr = p.row; rc = p.col; map[p] = CellType.RoomInterior; } if(map[rr,rc].IsFloor()){ bool floors = true; foreach(pos p in new pos(rr,rc).PositionsAtDistance(1,map)){ if(!map[p].IsFloor()){ floors = false; break; } } if(floors){ if(df == DungeonFeature.POOL_OF_RESTORATION){ map[rr,rc] = CellType.Pool; } if(df == DungeonFeature.FIRE_PIT){ map[rr,rc] = CellType.FirePit; } break; } } } break; } case DungeonFeature.BARREL: case DungeonFeature.TORCH: for(int i=0;i<50;++i){ int rr = R.Roll(ROWS-2); int rc = R.Roll(COLS-2); if(map[rr,rc].IsRoomType() && map[rr,rc].IsFloor()){ if(df == DungeonFeature.BARREL){ map[rr,rc] = CellType.Barrel; } if(df == DungeonFeature.TORCH){ map[rr,rc] = CellType.Torch; } break; } } break; case DungeonFeature.WEBS: case DungeonFeature.RUBBLE: { for(int i=0;i<50;++i){ int rr = R.Roll(ROWS-2); int rc = R.Roll(COLS-2); if(map[rr,rc].IsRoomType()){ CellType cell = CellType.Webs; int max_radius = 2; switch(df){ case DungeonFeature.WEBS: cell = CellType.Webs; max_radius = 3; break; case DungeonFeature.RUBBLE: cell = CellType.Rubble; max_radius = 2; break; } map[rr,rc] = cell; for(int j=1;j<=max_radius;++j){ List<pos> added = new List<pos>(); foreach(pos p in new pos(rr,rc).PositionsWithinDistance(j,map)){ if(map[p] == cell){ foreach(pos neighbor in p.CardinalAdjacentPositions()){ if(map[neighbor].IsFloor() && R.CoinFlip()){ added.AddUnique(neighbor); } } } } foreach(pos p in added){ /*if(df == DungeonFeature.RUBBLE){ foreach(pos neighbor in p.CardinalAdjacentPositions()){ if(!added.Contains(neighbor) && map[neighbor].IsFloor() && R.OneIn(3)){ map[neighbor] = CellType.Gravel; } } }*/ map[p] = cell; } } break; } } break; } case DungeonFeature.SLIME: case DungeonFeature.OIL: { for(int i=0;i<50;++i){ int rr = R.Roll(ROWS-2); int rc = R.Roll(COLS-2); if(map[rr,rc].IsFloor()){ CellType cell = CellType.Wall; int max_radius = 2; switch(df){ case DungeonFeature.SLIME: cell = CellType.Slime; max_radius = 3; break; case DungeonFeature.OIL: cell = CellType.Oil; max_radius = 3; break; } map[rr,rc] = cell; for(int j=1;j<=max_radius;++j){ List<pos> added = new List<pos>(); foreach(pos p in new pos(rr,rc).PositionsWithinDistance(j,map)){ if(map[p] == cell){ foreach(pos neighbor in p.CardinalAdjacentPositions()){ if(map[neighbor].IsFloor() && R.CoinFlip()){ added.AddUnique(neighbor); } } } } foreach(pos p in added){ map[p] = cell; } } break; } } break; } case DungeonFeature.FIRE_GEYSER: { for(int i=0;i<50;++i){ int rr = R.Roll(ROWS-4)+1; int rc = R.Roll(COLS-4)+1; if(map[rr,rc].IsFloor()){ bool floors = true; foreach(pos p in new pos(rr,rc).PositionsAtDistance(1,map)){ if(!map[p].IsFloor()){ floors = false; break; } } if(floors){ map[rr,rc] = CellType.Geyser; break; } } } break; } case DungeonFeature.VINES: { for(int i=0;i<500;++i){ int rr = R.Roll(ROWS-2); int rc = R.Roll(COLS-2); pos p = new pos(rr,rc); if(map[p].IsRoomType() && p.HasAdjacentWhere(x=>map.BoundsCheck(x) && map[x].IsWall())){ PosArray<bool> vine = map.GetFloodFillArray(p,false,x=>map[x].IsRoomType() && x.HasAdjacentWhere(y=>map.BoundsCheck(y) && map[y].IsWall()) && !R.OneIn(3)); //changed from one in 6 so vines won't fill caves so often rr = R.Roll(ROWS-2); rc = R.Roll(COLS-2); pos p2 = new pos(rr,rc); PosArray<bool> new_vine = new PosArray<bool>(ROWS,COLS); int max = Math.Max(ROWS,COLS); for(int dist=0;dist<max;++dist){ bool found = false; foreach(pos possible_vine in p2.PositionsAtDistance(dist)){ if(possible_vine.BoundsCheck(new_vine,false)){ found = true; if(vine[possible_vine] && possible_vine.PositionsAtDistance(1,new_vine).Where(x=>new_vine[x] || map[x] == CellType.Vine).Count < 3){ new_vine[possible_vine] = true; } } } if(!found){ break; } } List<pos> added = new List<pos>(); for(int s=1;s<ROWS-1;++s){ for(int t=1;t<COLS-1;++t){ if(new_vine[s,t]){ pos neighbor = new pos(s,t); foreach(int dir in U.FourDirections){ if(R.OneIn(6) && map[neighbor.PosInDir(dir)].IsFloor()){ added.AddUnique(neighbor.PosInDir(dir)); } } } } } foreach(pos neighbor in added){ new_vine[neighbor] = true; } for(int s=1;s<ROWS-1;++s){ for(int t=1;t<COLS-1;++t){ if(new_vine[s,t]){ if(R.OneIn(35)){ map[s,t] = CellType.PoisonBulb; } else{ map[s,t] = CellType.Vine; } } } } break; } } break; } case DungeonFeature.BLAST_FUNGUS: case DungeonFeature.FOG_VENT: case DungeonFeature.POISON_VENT: for(int i=0;i<50;++i){ int rr = R.Roll(ROWS-2); int rc = R.Roll(COLS-2); if(map[rr,rc].IsFloor()){ if(df == DungeonFeature.BLAST_FUNGUS){ map[rr,rc] = CellType.BlastFungus; } if(df == DungeonFeature.FOG_VENT){ map[rr,rc] = CellType.FogVent; } if(df == DungeonFeature.POISON_VENT){ map[rr,rc] = CellType.PoisonVent; } break; } } break; case DungeonFeature.CRACKED_WALL: for(int i=R.Between(2,4);i>0;--i){ if(thin_walls.Count > 0){ map[thin_walls.RemoveRandom()] = CellType.CrackedWall; } } break; } } }