示例#1
0
 public void RemoveUnconnectedAreas()
 {
     //leaving only the largest
     int[,] num = new int[H,W];
     for(int i=0;i<H;++i){
         for(int j=0;j<W;++j){
             if(map[i,j].IsPassable()){
                 num[i,j] = 0;
             }
             else{
                 num[i,j] = -1;
             }
         }
     }
     int count = 0;
     for(int i=0;i<H;++i){
         for(int j=0;j<W;++j){
             if(num[i,j] == 0){
                 count++;
                 num[i,j] = count;
                 bool changed = true;
                 while(changed){
                     changed = false;
                     for(int s=0;s<H;++s){
                         for(int t=0;t<W;++t){
                             if(num[s,t] == count){
                                 for(int ds=-1;ds<=1;++ds){
                                     for(int dt=-1;dt<=1;++dt){
                                         if(BoundsCheck(s+ds,t+dt,true) && num[s+ds,t+dt] == 0){
                                             num[s+ds,t+dt] = count;
                                             changed = true;
                                         }
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
     int biggest_area = -1;
     int size_of_biggest_area = 0;
     for(int k=1;k<=count;++k){
         int size = 0;
         for(int i=0;i<H;++i){
             for(int j=0;j<W;++j){
                 if(num[i,j] == k){
                     size++;
                 }
             }
         }
         if(size > size_of_biggest_area){
             size_of_biggest_area = size;
             biggest_area = k;
         }
     }
     for(int i=0;i<H;++i){
         for(int j=0;j<W;++j){
             if(num[i,j] != biggest_area){
                 if(!map[i,j].IsPassable()){
                     pos p = new pos(i,j);
                     bool make_wall = true;
                     foreach(pos neighbor in p.PositionsAtDistance(1,map)){
                         if(num[neighbor.row,neighbor.col] == biggest_area){
                             make_wall = false;
                             break;
                         }
                     }
                     if(make_wall){
                         map[i,j] = CellType.Wall;
                     }
                 }
                 else{
                     map[i,j] = CellType.Wall;
                 }
             }
         }
     }
 }
示例#2
0
 public void AlterRooms(int no_change_freq,int add_pillars_freq,int cross_room_freq,int cave_widen_freq,int cave_fill_freq)
 {
     List<int> modification = new List<int>();
     for(int i=0;i<no_change_freq;++i){
         modification.Add(0);
     }
     for(int i=0;i<add_pillars_freq;++i){
         modification.Add(1);
     }
     for(int i=0;i<cross_room_freq;++i){
         modification.Add(2);
     }
     for(int i=0;i<cave_widen_freq;++i){
         modification.Add(3);
     }
     for(int i=0;i<cave_fill_freq;++i){
         modification.Add(4);
     }
     if(modification.Count == 0){
         return;
     }
     ForEachRectangularRoom((start_r,start_c,end_r,end_c) => {
         int mod = modification.Random();
         switch(mod){
         case 0:
             return true;
         case 1:
         {
             int height = end_r - start_r + 1;
             int width = end_c - start_c + 1;
             if(height > 3 || width > 3){
                 List<PillarArrangement> layouts = new List<PillarArrangement>();
                 if(height % 2 == 1 && width % 2 == 1){
                     layouts.Add(PillarArrangement.Single);
                 }
                 if((height % 2 == 1 || width % 2 == 1) && height != 4 && width != 4){
                     layouts.Add(PillarArrangement.Row);
                 }
                 if(height >= 5 && width >= 5){
                     layouts.Add(PillarArrangement.Corners);
                 }
                 if(height > 2 && width > 2 && height != 4 && width != 4){
                     layouts.Add(PillarArrangement.Full);
                 }
                 if((width % 2 == 1 && width >= 5) || (height % 2 == 1 && height >= 5)){
                     layouts.Add(PillarArrangement.StatueEdges);
                 }
                 if(layouts.Count == 0 || CoinFlip()){ //otherwise they're too common
                     layouts.Add(PillarArrangement.StatueCorners);
                 }
                 if(layouts.Count > 0){
                     CellType pillar = CellType.Pillar;
                     switch(layouts.Random()){
                     case PillarArrangement.Single:
                         map[(start_r + end_r)/2,(start_c + end_c)/2] = pillar;
                         break;
                     case PillarArrangement.Row:
                     {
                         bool vertical;
                         if(width % 2 == 1 && height % 2 == 0){
                             vertical = true;
                         }
                         else{
                             if(height % 2 == 1 && width % 2 == 0){
                                 vertical = false;
                             }
                             else{
                                 vertical = CoinFlip();
                             }
                         }
                         if(vertical){
                             if(height % 2 == 1){
                                 for(int i=start_r+1;i<=end_r-1;i+=2){
                                     map[i,(start_c + end_c)/2] = pillar;
                                 }
                             }
                             else{
                                 int offset = 0;
                                 if(height % 4 == 0){
                                     offset = Roll(2) - 1;
                                 }
                                 for(int i=start_r+1+offset;i<(start_r + end_r)/2;i+=2){
                                     map[i,(start_c + end_c)/2] = pillar;
                                 }
                                 for(int i=end_r-1-offset;i>(start_r + end_r)/2+1;i-=2){
                                     map[i,(start_c + end_c)/2] = pillar;
                                 }
                             }
                         }
                         else{
                             if(width % 2 == 1){
                                 for(int i=start_c+1;i<=end_c-1;i+=2){
                                     map[(start_r + end_r)/2,i] = pillar;
                                 }
                             }
                             else{
                                 int offset = 0;
                                 if(width % 4 == 0){
                                     offset = Roll(2) - 1;
                                 }
                                 for(int i=start_c+1+offset;i<(start_c + end_c)/2;i+=2){
                                     map[(start_r + end_r)/2,i] = pillar;
                                 }
                                 for(int i=end_c-1-offset;i>(start_c + end_c)/2+1;i-=2){
                                     map[(start_r + end_r)/2,i] = pillar;
                                 }
                             }
                         }
                         break;
                     }
                     case PillarArrangement.Corners:
                     {
                         int v_offset = 0;
                         int h_offset = 0;
                         if(height % 4 == 0){
                             v_offset = Roll(2) - 1;
                         }
                         if(width % 4 == 0){
                             h_offset = Roll(2) - 1;
                         }
                         map[start_r + 1 + v_offset,start_c + 1 + h_offset] = pillar;
                         map[start_r + 1 + v_offset,end_c - 1 - h_offset] = pillar;
                         map[end_r - 1 - v_offset,start_c + 1 + h_offset] = pillar;
                         map[end_r - 1 - v_offset,end_c - 1 - h_offset] = pillar;
                         break;
                     }
                     case PillarArrangement.Full:
                     {
                         int v_offset = 0;
                         int h_offset = 0;
                         if(height % 4 == 0){
                             v_offset = Roll(2) - 1;
                         }
                         if(width % 4 == 0){
                             h_offset = Roll(2) - 1;
                         }
                         int half_r = (start_r + end_r)/2;
                         int half_c = (start_c + end_c)/2;
                         int half_r_offset = (start_r + end_r + 1)/2;
                         int half_c_offset = (start_c + end_c + 1)/2;
                         for(int i=start_r+1+v_offset;i<half_r;i+=2){
                             for(int j=start_c+1+h_offset;j<half_c;j+=2){
                                 map[i,j] = pillar;
                             }
                         }
                         for(int i=start_r+1+v_offset;i<half_r;i+=2){
                             for(int j=end_c-1-h_offset;j>half_c_offset;j-=2){
                                 map[i,j] = pillar;
                             }
                         }
                         for(int i=end_r-1-v_offset;i>half_r_offset;i-=2){
                             for(int j=start_c+1+h_offset;j<half_c;j+=2){
                                 map[i,j] = pillar;
                             }
                         }
                         for(int i=end_r-1-v_offset;i>half_r_offset;i-=2){
                             for(int j=end_c-1-h_offset;j>half_c_offset;j-=2){
                                 map[i,j] = pillar;
                             }
                         }
                         if((width+1) % 4 == 0){
                             if(height % 2 == 1){
                                 for(int i=start_r+1;i<=end_r-1;i+=2){
                                     map[i,half_c] = pillar;
                                 }
                             }
                             else{
                                 int offset = 0;
                                 if(height % 4 == 0){
                                     offset = Roll(2) - 1;
                                 }
                                 for(int i=start_r+1+offset;i<half_r;i+=2){
                                     map[i,half_c] = pillar;
                                 }
                                 for(int i=end_r-1-offset;i>half_r_offset;i-=2){
                                     map[i,half_c] = pillar;
                                 }
                             }
                         }
                         if((height+1) % 4 == 0){
                             if(width % 2 == 1){
                                 for(int i=start_c+1;i<=end_c-1;i+=2){
                                     map[half_r,i] = pillar;
                                 }
                             }
                             else{
                                 int offset = 0;
                                 if(width % 4 == 0){
                                     offset = Roll(2) - 1;
                                 }
                                 for(int i=start_c+1+offset;i<half_c;i+=2){
                                     map[half_r,i] = pillar;
                                 }
                                 for(int i=end_c-1-offset;i>half_c_offset;i-=2){
                                     map[half_r,i] = pillar;
                                 }
                             }
                         }
                         break;
                     }
                     case PillarArrangement.StatueCorners:
                         map[start_r,start_c] = CellType.Statue;
                         map[start_r,end_c] = CellType.Statue;
                         map[end_r,start_c] = CellType.Statue;
                         map[end_r,end_c] = CellType.Statue;
                         break;
                     case PillarArrangement.StatueEdges:
                     {
                         map[start_r,start_c] = CellType.Statue;
                         map[start_r,end_c] = CellType.Statue;
                         map[end_r,start_c] = CellType.Statue;
                         map[end_r,end_c] = CellType.Statue;
                         if(width % 2 == 1 && width > 3){
                             int half_c = (start_c + end_c)/2;
                             int corridors = new pos(start_r,half_c).CardinalAdjacentPositions().Where(x => BoundsCheck(x) && map[x].IsCorridorType()).Count;
                             if(corridors == 0){
                                 map[start_r,half_c] = CellType.Statue;
                             }
                             corridors = new pos(end_r,half_c).CardinalAdjacentPositions().Where(x => BoundsCheck(x) && map[x].IsCorridorType()).Count;
                             if(corridors == 0){
                                 map[end_r,half_c] = CellType.Statue;
                             }
                         }
                         if(height % 2 == 1 && height > 3){
                             int half_r = (start_r + end_r)/2;
                             int corridors = new pos(half_r,start_c).CardinalAdjacentPositions().Where(x => BoundsCheck(x) && map[x].IsCorridorType()).Count;
                             if(corridors == 0){
                                 map[half_r,start_c] = CellType.Statue;
                             }
                             corridors = new pos(half_r,end_c).CardinalAdjacentPositions().Where(x => BoundsCheck(x) && map[x].IsCorridorType()).Count;
                             if(corridors == 0){
                                 map[half_r,end_c] = CellType.Statue;
                             }
                         }
                         break;
                     }
                     default:
                         break;
                     }
                 }
             }
             return true;
         }
         case 2:
         {
             int height = end_r - start_r + 1;
             int width = end_c - start_c + 1;
             if(height < 4 || width < 4){ //nothing happens until we get above 4x4
                 return true;
             }
             int rows_to_convert = Roll((height/2)-1);
             int cols_to_convert = Roll((width/2)-1);
             if(rows_to_convert == 1 && cols_to_convert == 1){
                 return true;
             }
             List<pos> blocked = new List<pos>();
             for(int i=start_r;i<=end_r;++i){
                 for(int j=start_c;j<=end_c;++j){
                     if((i < start_r + rows_to_convert || i > end_r - rows_to_convert) && (j < start_c + cols_to_convert || j > end_c - cols_to_convert)){
                         pos p = new pos(i,j);
                         foreach(pos neighbor in p.CardinalAdjacentPositions()){
                             if(map[neighbor].IsCorridorType()){
                                 blocked.Add(p);
                             }
                         }
                         map[i,j] = CellType.Wall;
                     }
                 }
             }
             blocked.Randomize();
             foreach(pos p in blocked){
                 bool done = false;
                 foreach(pos neighbor in p.CardinalAdjacentPositions()){
                     if(map[neighbor].IsRoomType()){
                         map[p] = CellType.RoomInterior;
                         done = true;
                         break;
                     }
                 }
                 if(!done){
                     List<int> valid_dirs = new List<int>();
                     foreach(int dir in U.FourDirections){
                         pos next = p.PosInDir(dir);
                         while(next.row >= start_r && next.row <= end_r && next.col >= start_c && next.col <= end_c){
                             if(next.CardinalAdjacentPositions().Any(x=>map[x].IsRoomType())){
                                 valid_dirs.Add(dir);
                                 break;
                             }
                             next = next.PosInDir(dir);
                         }
                     }
                     int valid_dir = valid_dirs.RandomOrDefault();
                     pos next2 = p.PosInDir(valid_dir);
                     List<pos> new_corridor = new List<pos>{p};
                     while(true){
                         new_corridor.Add(next2);
                         if(next2.CardinalAdjacentPositions().Any(x=>map[x].IsRoomType())){
                             break;
                         }
                         next2 = next2.PosInDir(valid_dir);
                     }
                     foreach(pos p2 in new_corridor){
                         map[p2] = CellType.RoomInterior;
                     }
                 }
             }
             return true;
         }
         case 3:
         {
             List<pos> list = map.PositionsWhere(x=>x.row >= start_r && x.row <= end_r && x.col >= start_c && x.col <= end_c);
             PosArray<CellType> old_map = new PosArray<CellType>(H,W);
             foreach(pos p in list){
                 old_map[p] = map[p];
                 map[p] = CellType.Wall;
             }
             PosArray<bool> rock = new PosArray<bool>(H,W);
             for(int i=0;i<H;++i){
                 for(int j=0;j<W;++j){
                     pos p = new pos(i,j);
                     rock[p] = true;
                     if(BoundsCheck(i,j,false)){
                         foreach(pos neighbor in p.AdjacentPositionsClockwise()){
                             if(map[neighbor] != CellType.Wall){
                                 rock[p] = false;
                                 break;
                             }
                         }
                     }
                 }
             }
             foreach(pos p in list){
                 map[p] = CellType.RoomInterior; //todo: might this step be extraneous?
             }
             List<pos> frontier = new List<pos>();
             {
                 PosArray<bool> in_list = new PosArray<bool>(H,W);
                 foreach(pos p in list){
                     in_list[p] = true;
                 }
                 for(int i=0;i<H;++i){
                     for(int j=0;j<W;++j){
                         pos p = new pos(i,j);
                         if(in_list[p]){
                             foreach(pos neighbor in p.PositionsAtDistance(1,in_list)){
                                 if(!in_list[neighbor]){
                                     frontier.Add(p);
                                     break;
                                 }
                             }
                         }
                     }
                 }
             }
             int fail_counter = 0;
             int num_added = 0;
             bool finished = false;
             while(!finished){
                 if(frontier.Count == 0 || num_added >= 30){ //todo check this value
                     finished = true;
                     break;
                 }
                 pos f = frontier.RemoveRandom();
                 foreach(pos neighbor in f.CardinalAdjacentPositions()){
                     if(!BoundsCheck(neighbor,false) || !rock[neighbor.row,neighbor.col]){
                         ++fail_counter; //this might now be unreachable
                         if(!BoundsCheck(neighbor,false)){
                             fail_counter += 25; //fail quicker when against the edge of the map to prevent ugliness
                         } //however, this doesn't actually fail as quickly as it should - i've overlooked something.
                         if(fail_counter >= 50){
                             finished = true;
                             break;
                         }
                     }
                     else{
                         if(map[neighbor] != CellType.RoomInterior){
                             map[neighbor] = CellType.RoomInterior;
                             ++num_added;
                             bool add_neighbor = true;
                             foreach(pos n2 in neighbor.CardinalAdjacentPositions()){
                                 if(!BoundsCheck(n2,false) || !rock[n2.row,n2.col]){
                                     add_neighbor = false;
                                     ++fail_counter; //this might now be unreachable
                                     if(!BoundsCheck(neighbor,false)){
                                         fail_counter += 25; //fail quicker when against the edge of the map to prevent ugliness
                                     } //however, this doesn't actually fail as quickly as it should - i've overlooked something.
                                     if(fail_counter >= 50){
                                         finished = true;
                                     }
                                     break;
                                 }
                             }
                             if(finished){
                                 break;
                             }
                             if(add_neighbor){
                                 frontier.Add(neighbor);
                             }
                         }
                     }
                 }
             }
             foreach(pos p in list){
                 map[p] = old_map[p];
             }
             return true;
         }
         case 4:
         {
             List<pos> list = map.PositionsWhere(x=>x.row >= start_r && x.row <= end_r && x.col >= start_c && x.col <= end_c);
             Dungeon room = new Dungeon((end_r - start_r) + 3,(end_c - start_c) + 3); //includes borders
             List<pos> map_exits = list.Where(x=>x.CardinalAdjacentPositions().Any(y=>map[y].IsCorridorType())); //grab the positions from list that have any adjacent corridor-type cells
             if(map_exits.Count < 2){
                 return true;
             }
             List<pos> room_exits = new List<pos>();
             foreach(pos exit in map_exits){
                 room_exits.Add(new pos(exit.row-start_r+1,exit.col-start_c+1));
             }
             int tries = 0;
             while(true){
                 room.FillWithRandomWalls(25);
                 room.ApplyCellularAutomataXYRule(3);
                 room.ConnectDiagonals();
                 room.RemoveDeadEndCorridors();
                 room.RemoveUnconnectedAreas();
                 bool exits_open = true;
                 foreach(pos p in room_exits){
                     if(!room[p].IsPassable()){
                         exits_open = false;
                     }
                 }
                 if(exits_open){
                     for(int i=start_r;i<=end_r;++i){
                         for(int j=start_c;j<=end_c;++j){
                             if(list.Contains(new pos(i,j))){
                                 map[i,j] = room[(i-start_r)+1,(j-start_c)+1];
                             }
                         }
                     }
                     break;
                 }
                 ++tries;
                 if(tries > 50){
                     return false;
                 }
             }
             return true;
         }
         default:
             break;
         }
         return true;
     });
 }
示例#3
0
 public pos MoveRoom(List<pos> room,int direction)
 {
     List<CellType> prev_types = new List<CellType>();
     foreach(pos p in room){
         prev_types.Add(map[p]);
         map[p] = CellType.Wall;
     }
     List<pos> edge_positions = room.Where(x=>!room.Contains(x.PosInDir(direction)));
     pos offset = new pos(0,0);
     while(true){
         pos new_offset = offset.PosInDir(direction);
         bool good = true;
         foreach(pos p in edge_positions){
             pos n = new pos(p.row + new_offset.row,p.col + new_offset.col);
             foreach(pos neighbor in n.PositionsAtDistance(1)){
                 if(!neighbor.BoundsCheck(map,true) || !map[neighbor].IsWall()){
                     good = false;
                     break;
                 }
             }
             if(!good){
                 break;
             }
         }
         if(good){
             offset = new_offset;
         }
         else{
             break;
         }
     }
     int idx = 0;
     foreach(pos p in room){
         map[p.row + offset.row,p.col + offset.col] = prev_types[idx++];
     }
     return offset;
 }
示例#4
0
 public void CaveWidenRooms(int percent_chance_per_room,int number_of_tiles_to_add)
 {
     List<List<pos>> roomlist = new List<List<pos>>();
     ForEachRoom(list=>{
         if(PercentChance(percent_chance_per_room)){
             roomlist.Add(list);
         }
         return true;
     });
     while(roomlist.Count > 0){
         List<pos> list = roomlist.RemoveRandom();
         PosArray<CellType> old_map = new PosArray<CellType>(H,W);
         foreach(pos p in list){
             old_map[p] = map[p];
             map[p] = CellType.Wall;
         }
         PosArray<bool> rock = new PosArray<bool>(H,W);
         for(int i=0;i<H;++i){
             for(int j=0;j<W;++j){
                 pos p = new pos(i,j);
                 rock[p] = true;
                 if(BoundsCheck(i,j,false)){
                     foreach(pos neighbor in p.AdjacentPositionsClockwise()){
                         if(map[neighbor] != CellType.Wall){
                             rock[p] = false;
                             break;
                         }
                     }
                 }
             }
         }
         foreach(pos p in list){
             map[p] = CellType.RoomInterior; //todo: might this step be extraneous?
         }
         List<pos> frontier = new List<pos>();
         {
             PosArray<bool> in_list = new PosArray<bool>(H,W);
             foreach(pos p in list){
                 in_list[p] = true;
             }
             for(int i=0;i<H;++i){
                 for(int j=0;j<W;++j){
                     pos p = new pos(i,j);
                     if(in_list[p]){
                         foreach(pos neighbor in p.PositionsAtDistance(1,in_list)){
                             if(!in_list[neighbor]){
                                 frontier.Add(p);
                                 break;
                             }
                         }
                     }
                 }
             }
         }
         int fail_counter = 0;
         int num_added = 0;
         bool finished = false;
         while(!finished){
             if(frontier.Count == 0 || num_added >= number_of_tiles_to_add){
                 finished = true;
                 break;
             }
             pos f = frontier.RemoveRandom();
             foreach(pos neighbor in f.CardinalAdjacentPositions()){
                 if(!BoundsCheck(neighbor,false) || !rock[neighbor.row,neighbor.col]){
                     ++fail_counter; //this might now be unreachable
                     if(!BoundsCheck(neighbor,false)){
                         fail_counter += 25; //fail quicker when against the edge of the map to prevent ugliness
                     } //however, this doesn't actually fail as quickly as it should - i've overlooked something.
                     if(fail_counter >= 50){
                         finished = true;
                         break;
                     }
                 }
                 else{
                     if(map[neighbor] != CellType.RoomInterior){
                         map[neighbor] = CellType.RoomInterior;
                         ++num_added;
                         bool add_neighbor = true;
                         foreach(pos n2 in neighbor.CardinalAdjacentPositions()){
                             if(!BoundsCheck(n2,false) || !rock[n2.row,n2.col]){
                                 add_neighbor = false;
                                 ++fail_counter; //this might now be unreachable
                                 if(!BoundsCheck(neighbor,false)){
                                     fail_counter += 25; //fail quicker when against the edge of the map to prevent ugliness
                                 } //however, this doesn't actually fail as quickly as it should - i've overlooked something.
                                 if(fail_counter >= 50){
                                     finished = true;
                                 }
                                 break;
                             }
                         }
                         if(finished){
                             break;
                         }
                         if(add_neighbor){
                             frontier.Add(neighbor);
                         }
                     }
                 }
             }
         }
         foreach(pos p in list){
             map[p] = old_map[p];
         }
     }
 }
示例#5
0
 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;
         }
     }
 }
示例#6
0
 public PosArray<CellType> GenerateMap(LevelType type)
 {
     PosArray<CellType> result = new PosArray<CellType>(ROWS,COLS);
     Dungeon d = new Dungeon(ROWS,COLS);
     switch(type){
     case LevelType.Standard:
         while(true){
             d.CreateBasicMap();
             d.ConnectDiagonals();
             d.RemoveUnconnectedAreas();
             d.RemoveDeadEndCorridors();
             d.AddDoors(25);
             d.AlterRooms(5,2,2,1,0);
             d.MarkInterestingLocations();
             d.RemoveUnconnectedAreas();
             if(d.NumberOfFloors() < 320 || d.HasLargeUnusedSpaces(300)){
                 d.Clear();
             }
             else{
                 for(int i=0;i<ROWS;++i){
                     for(int j=0;j<COLS;++j){
                         result[i,j] = d.map[i,j];
                     }
                 }
                 return result;
             }
         }
     case LevelType.Cave:
     {
         int roll = R.Roll(2);
         if(R.OneIn(20)){
             roll = 3;
         }
         switch(roll){ //three different algorithms
         case 1:
         {
             while(true){
                 d.FillWithRandomWalls(25);
                 d.ApplyCellularAutomataXYRule(3);
                 d.ConnectDiagonals();
                 d.ImproveMapEdges(5);
                 d.RemoveDeadEndCorridors();
                 d.RemoveUnconnectedAreas();
                 d.MarkInterestingLocationsNonRectangular();
                 if(d.NumberOfFloors() < 320 || d.HasLargeUnusedSpaces(300)){
                     d.Clear();
                 }
                 else{
                     for(int i=0;i<ROWS;++i){
                         for(int j=0;j<COLS;++j){
                             result[i,j] = d.map[i,j];
                         }
                     }
                     return result;
                 }
             }
         }
         case 2:
         {
             while(true){
                 d.CreateTwistyCave(true,40);
                 U.DefaultMetric = DistanceMetric.Manhattan;
                 var dijk = d.map.GetDijkstraMap(x=>!d.map[x].IsWall(),x=>false);
                 for(int i=1;i<ROWS-1;++i){
                     for(int j=1;j<COLS-1;++j){
                         U.DefaultMetric = DistanceMetric.Manhattan;
                         if(dijk[i,j] == 1){
                             pos p = new pos(i,j);
                             List<pos> floors = null;
                             foreach(int dir in U.FourDirections){
                                 pos n = p.PosInDir(dir);
                                 if(dijk[n] == 1){
                                     if(floors == null){
                                         floors = p.PositionsAtDistance(1,dijk).Where(x=>dijk[x] == 0);
                                     }
                                     List<pos> floors2 = new List<pos>();
                                     foreach(pos n2 in n.PositionsAtDistance(1,dijk)){
                                         if(dijk[n2] == 0 && !floors.Contains(n2)){
                                             floors2.Add(n2);
                                         }
                                     }
                                     if(floors2.Count > 0 && R.OneIn(5)){ //IIRC this checks each pair twice, so that affects the chance here
                                         pos f1 = floors.Random();
                                         pos f2 = floors2.Random();
                                         U.DefaultMetric = DistanceMetric.Chebyshev;
                                         int dist = d.map.PathingDistanceFrom(f1,f2,x=>!d.map[x].IsPassable() && d.map[x] != CellType.Door);
                                         if(dist > 22 || (dist > 8 && R.OneIn(4))){
                                             CellType rubble = R.OneIn(8)? CellType.Rubble : CellType.CorridorIntersection;
                                             d[p] = R.OneIn(3)? rubble : CellType.CorridorIntersection;
                                             d[n] = R.OneIn(3)? rubble : CellType.CorridorIntersection;
                                             List<pos> neighbors = new List<pos>();
                                             foreach(pos nearby in p.PositionsAtDistance(1)){
                                                 if(nearby.BoundsCheck(d.map,false) && nearby.DistanceFrom(n) == 1){
                                                     neighbors.Add(nearby);
                                                 }
                                             }
                                             while(neighbors.Count > 0){
                                                 pos neighbor = neighbors.RemoveRandom();
                                                 if(R.OneIn(neighbors.Count + 3) && !d.SeparatesMultipleAreas(neighbor)){
                                                     d[neighbor] = R.OneIn(2)? CellType.Rubble : CellType.CorridorIntersection;
                                                 }
                                             }
                                         }
                                     }
                                     break;
                                 }
                             }
                         }
                     }
                 }
                 /*List<pos> thin_walls = d.map.AllPositions().Where(x=>d.map[x].IsWall() && x.HasOppositePairWhere(true,y=>y.BoundsCheck() && d.map[y].IsFloor()));
                 while(thin_walls.Count > 0){
                     pos p = thin_walls.Random();
                     foreach(int dir in new int[]{8,4}){
                         if(d.map[p.PosInDir(dir)] != CellType.Wall && d.map[p.PosInDir(dir.RotateDir(true,4))] != CellType.Wall){
                             var dijkstra = d.map.GetDijkstraMap(x=>d[x] == CellType.Wall,new List<pos>{p.PosInDir(dir)}); //todo: this would be better as "get distance"
                             if(Math.Abs(dijkstra[p.PosInDir(dir)] - dijkstra[p.PosInDir(dir.RotateDir(true,4))]) > 30){
                                 d.map[p] = CellType.CorridorIntersection;
                                 break;
                             }
                         }
                     }
                     thin_walls.Remove(p); //todo: move thin-wall-removal to schism
                 }*/
                 d.ConnectDiagonals();
                 d.RemoveUnconnectedAreas();
                 d.ImproveMapEdges(5);
                 d.SmoothCorners(60);
                 d.RemoveDeadEndCorridors();
                 d.MarkInterestingLocationsNonRectangular();
                 if(d.NumberOfFloors() < 320 || d.HasLargeUnusedSpaces(300)){
                     d.Clear();
                 }
                 else{
                     for(int i=0;i<ROWS;++i){
                         for(int j=0;j<COLS;++j){
                             result[i,j] = d.map[i,j];
                         }
                     }
                     U.DefaultMetric = DistanceMetric.Chebyshev;
                     return result;
                 }
             }
         }
         case 3:
         {
             d.RoomHeightMax = 3;
             d.RoomWidthMax = 3;
             while(true){
                 int successes = 0;
                 int consecutive_failures = 0;
                 while(successes < 13){
                     if(d.CreateRoom()){
                         ++successes;
                         consecutive_failures = 0;
                     }
                     else{
                         if(consecutive_failures++ >= 50){
                             d.Clear();
                             successes = 0;
                             consecutive_failures = 0;
                         }
                     }
                 }
                 d.CaveWidenRooms(100,50);
                 d.AddRockFormations(40,2);
                 List<pos> thin_walls = d.map.AllPositions().Where(x=>d.map[x].IsWall() && x.HasOppositePairWhere(true,y=>y.BoundsCheck(tile) && d.map[y].IsFloor()));
                 while(!d.IsFullyConnected() && thin_walls.Count > 0){
                     pos p = thin_walls.Random();
                     d.map[p] = CellType.CorridorIntersection;
                     foreach(pos neighbor in p.PositionsWithinDistance(1,d.map)){
                         thin_walls.Remove(neighbor);
                     }
                 }
                 d.ConnectDiagonals();
                 d.RemoveDeadEndCorridors();
                 d.RemoveUnconnectedAreas();
                 d.MarkInterestingLocationsNonRectangular();
                 if(d.NumberOfFloors() < 320 || d.HasLargeUnusedSpaces(300)){ //todo: add 'proper coverage' check here - make sure it stretches across enough of the map.
                     d.Clear();
                 }
                 else{
                     for(int i=0;i<ROWS;++i){
                         for(int j=0;j<COLS;++j){
                             result[i,j] = d.map[i,j];
                         }
                     }
                     return result;
                 }
             }
         }
         }
         break;
     }
     case LevelType.Hive:
     {
         d.RoomHeightMax = 3;
         d.RoomWidthMax = 3;
         while(true){
             int successes = 0;
             int consecutive_failures = 0;
             while(successes < 35){
                 if(d.CreateRoom()){
                     ++successes;
                     consecutive_failures = 0;
                 }
                 else{
                     if(consecutive_failures++ >= 40){
                         d.Clear();
                         successes = 0;
                         consecutive_failures = 0;
                     }
                 }
             }
             d.CaveWidenRooms(100,10);
             d.CaveWidenRooms(3,20);
             List<pos> thin_walls = d.map.AllPositions().Where(x=>d.map[x].IsWall() && x.HasOppositePairWhere(true,y=>y.BoundsCheck(tile) && d.map[y].IsFloor()));
             while(!d.IsFullyConnected() && thin_walls.Count > 0){
                 pos p = thin_walls.Random();
                 d.map[p] = CellType.CorridorIntersection;
                 foreach(pos neighbor in p.PositionsWithinDistance(2,d.map)){
                     thin_walls.Remove(neighbor);
                 }
             }
             d.ConnectDiagonals();
             d.RemoveDeadEndCorridors();
             d.RemoveUnconnectedAreas();
             d.MarkInterestingLocations();
             //to find rooms big enough for stuff in the center:
         //var dijkstra = d.map.GetDijkstraMap(x=>d.map[x].IsWall(),d.map.AllPositions().Where(x=>d.map[x].IsWall() && x.HasAdjacentWhere(y=>d.map.BoundsCheck(y) && !d.map[y].IsWall())));
             if(d.NumberOfFloors() < 340 || d.HasLargeUnusedSpaces(300)){ //todo: add 'proper coverage' check here - make sure it stretches across enough of the map.
                 d.Clear();
             }
             else{
                 for(int i=0;i<ROWS;++i){
                     for(int j=0;j<COLS;++j){
                         result[i,j] = d.map[i,j];
                     }
                 }
                 return result;
             }
         }
     }
     case LevelType.Mine:
     {
         d.CorridorExtraLengthChance = 0;
         d.CorridorChainSizeMax = 10;
         while(true){
             d.RoomHeightMin = 8;
             d.RoomWidthMin = 8;
             d.RoomHeightMax = 8;
             d.RoomWidthMax = 10;
             d.MinimumSpaceBetweenCorridors = 3;
             d.CorridorLengthMin = 3;
             d.CorridorLengthMax = 5;
             while(!d.CreateRoom()){}
             d.RoomHeightMin = 5;
             d.RoomWidthMin = 5;
             d.RoomHeightMax = 5;
             d.RoomWidthMax = 5;
             while(!d.CreateRoom()){}
             while(!d.CreateRoom()){}
             /*for(int i=0;i<10;++i){
                 d.CreateRoom();
             }
             d.AddRockFormations(100,2);*/
             d.MinimumSpaceBetweenCorridors = 5;
             d.CorridorLengthMin = 4;
             d.CorridorLengthMax = 12;
             for(int i=0;i<70;++i){
                 d.CreateCorridor();
             }
             d.CorridorLengthMin = 3;
             d.CorridorLengthMax = 5;
             d.MinimumSpaceBetweenCorridors = 3;
             for(int i=0;i<350;++i){
                 d.CreateCorridor();
             }
             d.RemoveUnconnectedAreas();
             d.ConnectDiagonals(true);
             d.RemoveUnconnectedAreas();
             d.MarkInterestingLocations();
             if(d.NumberOfFloors() < 250 || d.HasLargeUnusedSpaces(300)){
                 d.Clear();
             }
             else{
                 for(int i=0;i<ROWS;++i){
                     for(int j=0;j<COLS;++j){
                         result[i,j] = d.map[i,j];
                     }
                 }
                 return result;
             }
         }
     }
     case LevelType.Fortress:
         while(true){
             int H = ROWS;
             int W = COLS;
             for(int i=H/2-1;i<H/2+1;++i){
                 for(int j=1;j<W-1;++j){
                     if(j==1 || j==W-2){
                         d.map[i,j] = CellType.RoomCorner;
                     }
                     else{
                         d.map[i,j] = CellType.RoomEdge;
                     }
                 }
             }
             for(int i=0;i<700;++i){
                 if(R.OneIn(5)){
                     d.CreateCorridor();
                 }
                 else{
                     d.CreateRoom();
                 }
             }
             bool reflect_features = R.PercentChance(80);
             if(reflect_features){
                 d.AddDoors(25);
                 d.AddPillars(30);
             }
             d.Reflect(true,false);
             d.ConnectDiagonals();
             d.RemoveDeadEndCorridors();
             d.RemoveUnconnectedAreas();
             if(!reflect_features){
                 d.AddDoors(25);
                 d.AddPillars(30);
             }
             bool door_right = false;
             bool door_left = false;
             int rightmost_door = 0;
             int leftmost_door = 999;
             for(int j=0;j<22;++j){
                 if(d[H/2-2,j].IsCorridorType()){
                     door_left = true;
                     if(leftmost_door == 999){
                         leftmost_door = j;
                     }
                 }
                 if(d[H/2-2,W-1-j].IsCorridorType()){
                     door_right = true;
                     if(rightmost_door == 0){
                         rightmost_door = W-1-j;
                     }
                 }
             }
             if(!door_left || !door_right){
                 d.Clear();
                 continue;
             }
             for(int j=1;j<leftmost_door-6;++j){
                 d[H/2-1,j] = CellType.Wall;
                 d[H/2,j] = CellType.Wall;
             }
             for(int j=W-2;j>rightmost_door+6;--j){
                 d[H/2-1,j] = CellType.Wall;
                 d[H/2,j] = CellType.Wall;
             }
             for(int j=1;j<W-1;++j){
                 if(d[H/2-1,j].IsFloor()){
                     d[H/2-1,j] = CellType.Statue;
                     d[H/2,j] = CellType.Statue;
                     break;
                 }
                 else{
                     if(d[H/2-1,j] == CellType.Statue){
                         break;
                     }
                 }
             }
             for(int j=W-2;j>0;--j){
                 if(d[H/2-1,j].IsFloor()){
                     d[H/2-1,j] = CellType.Statue;
                     d[H/2,j] = CellType.Statue;
                     break;
                 }
                 else{
                     if(d[H/2-1,j] == CellType.Statue){
                         break;
                     }
                 }
             }
             for(int i=H/2-1;i<H/2+1;++i){
                 for(int j=1;j<W-1;++j){
                     if(d[i,j] == CellType.RoomCorner || d[i,j] == CellType.RoomEdge){
                         d[i,j] = CellType.CorridorIntersection;
                     }
                 }
             }
             d.MarkInterestingLocations();
             if(d.NumberOfFloors() < 420 || d.HasLargeUnusedSpaces(300)){
                 d.Clear();
             }
             else{
                 for(int i=0;i<ROWS;++i){
                     for(int j=0;j<COLS;++j){
                         result[i,j] = d.map[i,j];
                     }
                 }
                 return result;
             }
         }
     case LevelType.Slime:
         while(true){
             /*for(int i=1;i<ROWS-1;++i){
                 for(int j=1;j<COLS-1;++j){
                     if(d[i,j].IsWall()){
                         if(!d[i+1,j+1].IsWall()){
                             d[i,j] = d[i+1,j+1];
                         }
                         else{
                             if(!d[i+1,j].IsWall()){
                                 d[i,j] = d[i+1,j];
                             }
                             else{
                                 if(!d[i,j+1].IsWall()){
                                     d[i,j] = d[i,j+1];
                                 }
                             }
                         }
                     }
                 }
             }*/
             d.CreateBasicMap();
             d.ConnectDiagonals();
             d.RemoveUnconnectedAreas();
             d.AddDoors(25);
             d.CaveWidenRooms(30,30);
             d.RemoveDeadEndCorridors();
             d.AddPillars(30);
             d.MarkInterestingLocations();
             if(d.NumberOfFloors() < 120 || d.HasLargeUnusedSpaces(300)){
                 d.Clear();
             }
             else{
                 for(int i=0;i<ROWS;++i){
                     for(int j=0;j<COLS;++j){
                         result[i,j] = d.map[i,j];
                     }
                 }
                 return result;
             }
         }
     case LevelType.Garden:
     {
         d.RoomHeightMin = 4;
         d.RoomHeightMax = 10;
         d.RoomWidthMin = 4;
         d.RoomWidthMax = 10;
         while(true){
             d.CreateBasicMap();
             d.ConnectDiagonals();
             d.RemoveUnconnectedAreas();
             d.RemoveDeadEndCorridors();
             var dijkstra = d.map.GetDijkstraMap(x=>d[x].IsPassable(),x=>false);
             List<pos> possible_room_centers = d.map.PositionsWhere(x=>dijkstra[x] == 3 && x.row > 1 && x.row < ROWS-2 && x.col > 1 && x.col < COLS-2);
             int rooms = 0;
             while(rooms < 6 && possible_room_centers.Count > 0){
                 pos p = possible_room_centers.RemoveRandom();
                 List<int> valid_dirs = new List<int>();
                 foreach(int dir in U.FourDirections){
                     pos p2 = p.PosInDir(dir).PosInDir(dir).PosInDir(dir);
                     if(p2.BoundsCheck(d.map) && d[p2].IsPassable() && d[p2] != CellType.RoomCorner){
                         valid_dirs.Add(dir);
                     }
                 }
                 if(valid_dirs.Count > 0){
                     foreach(pos neighbor in p.PositionsWithinDistance(1,d.map)){
                         d[neighbor] = CellType.RoomInterior;
                     }
                     possible_room_centers.RemoveWhere(x=>p.DistanceFrom(x) <= 3);
                     foreach(int dir in valid_dirs){
                         d[p.PosInDir(dir).PosInDir(dir)] = CellType.CorridorIntersection;
                     }
                     ++rooms;
                 }
             }
             CellType water_type = CellType.ShallowWater;
             if(R.OneIn(8)){
                 water_type = CellType.Ice;
             }
             d.ForEachRectangularRoom((start_r,start_c,end_r,end_c)=>{
                 int room_height = (end_r - start_r) + 1;
                 int room_width = (end_c - start_c) + 1;
                 if(room_height <= 4 && room_width <= 4){
                     if(room_height == 3 && room_width == 3){
                         return true;
                     }
                     List<pos> water = new List<pos>();
                     if(!new pos(start_r+1,start_c).PositionsAtDistance(1,d.map).Any(x=>d[x].IsCorridorType())){
                         water.Add(new pos(start_r+1,start_c));
                         water.Add(new pos(start_r+2,start_c));
                     }
                     if(!new pos(start_r,start_c+1).PositionsAtDistance(1,d.map).Any(x=>d[x].IsCorridorType())){
                         water.Add(new pos(start_r,start_c+1));
                         water.Add(new pos(start_r,start_c+2));
                     }
                     if(!new pos(end_r-1,end_c).PositionsAtDistance(1,d.map).Any(x=>d[x].IsCorridorType())){
                         water.Add(new pos(end_r-1,end_c));
                         water.Add(new pos(end_r-2,end_c));
                     }
                     if(!new pos(end_r,end_c-1).PositionsAtDistance(1,d.map).Any(x=>d[x].IsCorridorType())){
                         water.Add(new pos(end_r,end_c-1));
                         water.Add(new pos(end_r,end_c-2));
                     }
                     foreach(pos p in water){
                         d[p] = water_type;
                     }
                     d[start_r,start_c] = CellType.Statue;
                     d[start_r,end_c] = CellType.Statue;
                     d[end_r,start_c] = CellType.Statue;
                     d[end_r,end_c] = CellType.Statue;
                 }
                 else{
                     CellType center_type = CellType.RoomFeature1;
                     switch(R.Roll(3)){
                     case 1:
                         center_type = water_type;
                         break;
                     case 2:
                         center_type = CellType.Poppies;
                         break;
                     case 3:
                         center_type = CellType.Brush;
                         break;
                     }
                     bool statues = R.CoinFlip();
                     CellType statue_type = CellType.Statue;
                     if(room_height <= 8 && room_width <= 8 && R.OneIn(8)){
                         statue_type = CellType.Torch;
                     }
                     CellType edge_type = CellType.ShallowWater;
                     if(center_type != water_type && !R.OneIn(4)){
                         edge_type = CellType.ShallowWater;
                     }
                     else{
                         int vine_chance = 50;
                         if(!statues){
                             vine_chance = 80;
                         }
                         if(R.PercentChance(vine_chance)){
                             edge_type = CellType.Vine;
                         }
                         else{
                             edge_type = CellType.Gravel;
                         }
                         if(R.OneIn(32)){
                             if(R.CoinFlip()){
                                 edge_type = CellType.Statue;
                             }
                             else{
                                 edge_type = CellType.GlowingFungus;
                             }
                         }
                     }
                     bool gravel = R.OneIn(16);
                     bool edges = R.CoinFlip();
                     if(room_height < 6 || room_width < 6){
                         edges = false;
                     }
                     if(room_height >= 8 && room_width >= 8){
                         edges = !R.OneIn(4);
                     }
                     if(edges){
                         for(int i=start_r;i<=end_r;++i){
                             for(int j=start_c;j<=end_c;++j){
                                 if(i == start_r || i == end_r || j == start_c || j == end_c){ //edges
                                     if(statues && (i == start_r || i == end_r) && (j == start_c || j == end_c)){ //corners
                                         d[i,j] = statue_type;
                                     }
                                     else{
                                         pos p = new pos(i,j);
                                         if(!p.CardinalAdjacentPositions().Any(x=>d[x].IsCorridorType())){
                                             d[i,j] = edge_type;
                                         }
                                     }
                                 }
                                 else{
                                     if(i == start_r+1 || i == end_r-1 || j == start_c+1 || j == end_c-1){ //the path
                                         if(gravel){
                                             d[i,j] = CellType.Gravel;
                                         }
                                     }
                                     else{
                                         d[i,j] = center_type;
                                     }
                                 }
                             }
                         }
                     }
                     else{
                         for(int i=start_r;i<=end_r;++i){
                             for(int j=start_c;j<=end_c;++j){
                                 if(i == start_r || i == end_r || j == start_c || j == end_c){
                                     if(gravel){
                                         d[i,j] = CellType.Gravel;
                                     }
                                 }
                                 else{
                                     d[i,j] = center_type;
                                 }
                             }
                         }
                     }
                     if(center_type == water_type && room_height % 2 == 1 && room_width % 2 == 1){
                         statue_type = CellType.Statue;
                         if(room_height <= 7 && room_width <= 7 && R.OneIn(12)){
                             statue_type = CellType.Torch;
                         }
                         d[(start_r+end_r)/2,(start_c+end_c)/2] = statue_type;
                     }
                 }
                 return true;
             });
             d.ConnectDiagonals();
             d.RemoveUnconnectedAreas();
             d.AddDoors(10);
             d.RemoveDeadEndCorridors();
             d.MarkInterestingLocations();
             if(d.NumberOfFloors() < 320 || d.HasLargeUnusedSpaces(300)){
                 d.Clear();
             }
             else{
                 for(int i=0;i<ROWS;++i){
                     for(int j=0;j<COLS;++j){
                         result[i,j] = d.map[i,j];
                     }
                 }
                 return result;
             }
         }
     }
     case LevelType.Crypt:
     {
         while(true){
             pos room1origin = new pos(ROWS/2,R.Roll(COLS/8 - 1) + COLS/8 - 1);
             pos room2origin = new pos(ROWS/2,R.Roll(COLS/8 - 1) + (COLS*6 / 8) - 1);
             while(!d.CreateRoom(room1origin.row,room1origin.col)){} //left half
             while(!d.CreateRoom(room2origin.row,room2origin.col)){} //right half
             d.CaveWidenRooms(100,150);
             d.MoveRoom(room1origin,4);
             d.MoveRoom(room2origin,6);
             var dijkstra = d.map.GetDijkstraMap(x=>d.map[x] == CellType.Wall,x=>false); //todo: among these Map dijkstra maps I have, like, 3 different ways of testing for walls. are these all correct?
             int distance_from_walls = 3;
             List<pos> central_room = d.map.PositionsWhere(x=>dijkstra[x] > distance_from_walls);
             int required_consecutive = 3;
             for(int i=0;i<ROWS;++i){ //first, check each row...
                 for(int j=0;j<COLS;++j){
                     List<pos> this_row = new List<pos>();
                     while(j < COLS && dijkstra[i,j] > distance_from_walls){
                         this_row.Add(new pos(i,j));
                         ++j;
                     }
                     if(this_row.Count < required_consecutive){
                         foreach(pos p in this_row){
                             central_room.Remove(p);
                         }
                     }
                 }
             }
             for(int j=0;j<COLS;++j){ //...then each column
                 for(int i=0;i<ROWS;++i){
                     List<pos> this_col = new List<pos>();
                     while(i < ROWS && dijkstra[i,j] > distance_from_walls){
                         this_col.Add(new pos(i,j));
                         ++i;
                     }
                     if(this_col.Count < required_consecutive){
                         foreach(pos p in this_col){
                             central_room.Remove(p);
                         }
                     }
                 }
             }
             central_room = d.map.GetFloodFillPositions(central_room.Where(x=>x.PositionsWithinDistance(1).All(y=>central_room.Contains(y))),false,x=>central_room.Contains(x));
             List<pos> walls = new List<pos>();
             foreach(pos p in central_room){
                 d.map[p] = CellType.InterestingLocation;
                 foreach(pos neighbor in p.PositionsAtDistance(1,d.map)){
                     if(!central_room.Contains(neighbor)){
                         d.map[neighbor] = CellType.Wall;
                         walls.Add(neighbor);
                     }
                 }
             }
             while(true){
                 List<pos> potential_doors = new List<pos>();
                 foreach(pos p in walls){
                     foreach(int dir in U.FourDirections){
                         if(d.map[p.PosInDir(dir)] == CellType.InterestingLocation && d.map[p.PosInDir(dir.RotateDir(true,4))].IsRoomType() && d.map[p.PosInDir(dir.RotateDir(true,4))] != CellType.InterestingLocation){
                             potential_doors.Add(p);
                             break;
                         }
                     }
                 }
                 if(potential_doors.Count > 0){
                     pos p = potential_doors.Random();
                     d.map[p] = CellType.Door;
                     List<pos> room = d.map.GetFloodFillPositions(p,true,x=>d.map[x] == CellType.InterestingLocation);
                     foreach(pos p2 in room){
                         d.map[p2] = CellType.RoomInterior;
                     }
                 }
                 else{
                     break;
                 }
             }
             dijkstra = d.map.GetDijkstraMap(x=>d.map[x] == CellType.Wall,x=>false);
             int num_chests = 0;
             d.ForEachRoom(list=>{
                 if(central_room.Contains(list[0])){
                     if(num_chests++ < 2){
                         d[list.Random()] = CellType.Chest;
                     }
                     return true;
                 }
                 List<pos> room = list.Where(x=>dijkstra[x] > 1);
                 int start_r = room.WhereLeast(x=>x.row)[0].row;
                 int end_r = room.WhereGreatest(x=>x.row)[0].row;
                 int start_c = room.WhereLeast(x=>x.col)[0].col;
                 int end_c = room.WhereGreatest(x=>x.col)[0].col;
                 List<List<pos>> offsets = new List<List<pos>>();
                 for(int i=0;i<4;++i){
                     offsets.Add(new List<pos>());
                 }
                 for(int i=start_r;i<=end_r;i+=2){
                     for(int j=start_c;j<=end_c;j+=2){
                         if(room.Contains(new pos(i,j))){
                             offsets[0].Add(new pos(i,j));
                         }
                         if(i+1 <= end_r && room.Contains(new pos(i+1,j))){
                             offsets[1].Add(new pos(i+1,j));
                         }
                         if(j+1 <= end_c && room.Contains(new pos(i,j+1))){
                             offsets[2].Add(new pos(i,j+1));
                         }
                         if(i+1 <= end_r && j+1 <= end_c && room.Contains(new pos(i+1,j+1))){
                             offsets[3].Add(new pos(i+1,j+1));
                         }
                     }
                 }
                 List<pos> tombstones = offsets.WhereGreatest(x=>x.Count).RandomOrDefault();
                 if(tombstones != null){
                     foreach(pos p in tombstones){
                         d.map[p] = CellType.Tombstone;
                     }
                 }
                 return true;
             });
             for(int i=0;i<ROWS;++i){
                 for(int j=0;j<COLS;++j){
                     if(d[i,j] == CellType.Door){
                         pos p = new pos(i,j);
                         List<pos> potential_statues = p.PositionsAtDistance(1,d.map).Where(x=>!d[x].IsWall() && !central_room.Contains(x) && p.DirectionOf(x) % 2 != 0 && !x.PositionsAtDistance(1,d.map).Any(y=>d[y].Is(CellType.Tombstone)));
                         if(potential_statues.Count == 2){
                             d[potential_statues[0]] = CellType.Statue;
                             d[potential_statues[1]] = CellType.Statue;
                         }
                     }
                 }
             }
             List<pos> room_one = null;
             List<pos> room_two = null;
             for(int j=0;j<COLS && room_one == null;++j){
                 for(int i=0;i<ROWS;++i){
                     if(d[i,j] != CellType.Wall){
                         room_one = d.map.GetFloodFillPositions(new pos(i,j),false,x=>!d[x].IsWall());
                         break;
                     }
                 }
             }
             for(int j=COLS-1;j>=0 && room_two == null;--j){
                 for(int i=0;i<ROWS;++i){
                     if(d[i,j] != CellType.Wall){
                         room_two = d.map.GetFloodFillPositions(new pos(i,j),false,x=>!d[x].IsWall());
                         break;
                     }
                 }
             }
             if(room_one.WhereGreatest(x=>x.col).Random().DistanceFrom(room_two.WhereLeast(x=>x.col).Random()) < 12){
                 d.Clear();
                 continue;
             }
             Dungeon d2 = new Dungeon(ROWS,COLS);
             int tries = 0;
             while(tries < 10){
                 d2.CreateBasicMap();
                 d2.ConnectDiagonals();
                 for(int i=0;i<ROWS;++i){
                     for(int j=0;j<COLS;++j){
                         if(d[i,j] != CellType.Wall){
                             pos p = new pos(i,j);
                             foreach(pos neighbor in p.PositionsAtDistance(1,d2.map)){
                                 d2[neighbor] = CellType.Wall;
                             }
                         }
                     }
                 }
                 d2.RemoveUnconnectedAreas();
                 List<pos> room_one_walls = new List<pos>();
                 List<pos> room_two_walls = new List<pos>();
                 for(int i=0;i<ROWS;++i){
                     for(int j=COLS-1;j>=0;--j){
                         pos p = new pos(i,j);
                         if(room_one.Contains(p)){
                             room_one_walls.Add(p);
                             break;
                         }
                     }
                     for(int j=0;j<COLS;++j){
                         pos p = new pos(i,j);
                         if(room_two.Contains(p)){
                             room_two_walls.Add(p);
                             break;
                         }
                     }
                 }
                 List<pos> room_one_valid_connections = new List<pos>();
                 List<pos> room_two_valid_connections = new List<pos>();
                 foreach(pos p in room_one_walls){
                     pos next = p.PosInDir(6);
                     while(BoundsCheck(next) && p.DistanceFrom(next) < 7){
                         if(d2[next] != CellType.Wall){
                             room_one_valid_connections.Add(p.PosInDir(6));
                             break;
                         }
                         next = next.PosInDir(6);
                     }
                 }
                 foreach(pos p in room_two_walls){
                     pos next = p.PosInDir(4);
                     while(BoundsCheck(next) && p.DistanceFrom(next) < 7){
                         if(d2[next] != CellType.Wall){
                             room_two_valid_connections.Add(p.PosInDir(4));
                             break;
                         }
                         next = next.PosInDir(4);
                     }
                 }
                 if(room_one_valid_connections.Count > 0 && room_two_valid_connections.Count > 0){
                     pos one = room_one_valid_connections.Random();
                     while(true){
                         if(d2[one] == CellType.Wall){
                             d2[one] = CellType.CorridorHorizontal;
                         }
                         else{
                             break;
                         }
                         one = one.PosInDir(6);
                     }
                     pos two = room_two_valid_connections.Random();
                     while(true){
                         if(d2[two] == CellType.Wall){
                             d2[two] = CellType.CorridorHorizontal;
                         }
                         else{
                             break;
                         }
                         two = two.PosInDir(4);
                     }
                     break;
                 }
                 else{
                     d2.Clear();
                 }
                 ++tries;
             }
             if(tries == 10){
                 d.Clear();
                 continue;
             }
             for(int i=0;i<ROWS;++i){
                 for(int j=0;j<COLS;++j){
                     if(d2[i,j] != CellType.Wall){
                         d[i,j] = d2[i,j];
                     }
                 }
             }
             //d.CaveWidenRooms(100,20);
             //d.MakeCavesMoreRectangular(4);
             //d.RemoveDeadEndCorridors();
             //d.MakeCavesMoreRectangular(1 + num++ / 10);
             //d.Clear();
             //continue;
             d.ConnectDiagonals();
             d.RemoveUnconnectedAreas();
             d.RemoveDeadEndCorridors();
             d.MarkInterestingLocations();
             d.RemoveUnconnectedAreas();
             if(d.NumberOfFloors() < 340 || d.HasLargeUnusedSpaces(350)){
                 d.Clear();
             }
             else{
                 for(int i=0;i<ROWS;++i){
                     for(int j=0;j<COLS;++j){
                         result[i,j] = d.map[i,j];
                     }
                 }
                 return result;
             }
         }
         }
     }
     return null;
 }
示例#7
0
 public void GenerateLevel()
 {
     if(current_level < 20){
         ++current_level;
     }
     InitializeNewLevel();
     PosArray<CellType> map = GenerateMap(level_types[current_level-1]);
     List<pos> interesting_tiles = new List<pos>();
     for(int i=0;i<ROWS;++i){
         for(int j=0;j<COLS;++j){
             if(map[i,j] == CellType.InterestingLocation){
                 interesting_tiles.Add(new pos(i,j));
             }
         }
     }
     int attempts = 0;
     List<CellType> shrines = null;
     if(current_level % 2 == 1){
         shrines = new List<CellType>{CellType.SpecialFeature1,CellType.SpecialFeature2,CellType.SpecialFeature3,CellType.SpecialFeature4,CellType.SpecialFeature5};
         shrines.Randomize(); // after this initialization step, shrines are only removed from the front of the list, to enable prediction of pairs.
         nextLevelShrines = new List<CellType>();
         int n = R.Between(0,5);
         for(int i=0;i<n;++i){
             nextLevelShrines.Add(shrines.RemoveLast());
         }
     }
     else{
         shrines = nextLevelShrines;
         nextLevelShrines = null;
     }
     while(shrines.Count > 0){
         attempts = 0;
         for(bool done=false;!done;++attempts){
             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;
             }
             pos temp = new pos(rr,rc);
             if(shrines.Count > 1){
                 if(map[rr,rc].IsFloor()){
                     if(attempts > 1000){
                         bool good = true;
                         foreach(pos p in temp.PositionsWithinDistance(4,map)){
                             CellType ch = map[p];
                             if(ch.Is(CellType.SpecialFeature1,CellType.SpecialFeature2,CellType.SpecialFeature3,CellType.SpecialFeature4,CellType.SpecialFeature5)){
                                 good = false;
                             }
                         }
                         if(good){
                             List<pos> dist2 = new List<pos>();
                             foreach(pos p2 in temp.PositionsAtDistance(2,map)){
                                 if(map[p2].IsFloor()){
                                     dist2.Add(p2);
                                 }
                             }
                             if(dist2.Count > 0){
                                 map[rr,rc] = shrines.RemoveFirst();
                                 pos p2 = dist2.Random();
                                 map[p2.row,p2.col] = shrines.RemoveFirst();
                                 done = true;
                                 break;
                             }
                         }
                         else{
                             interesting_tiles.Remove(temp);
                         }
                     }
                     bool floors = true;
                     foreach(pos p in temp.PositionsAtDistance(1,map)){
                         if(!map[p.row,p.col].IsFloor()){
                             floors = false;
                         }
                     }
                     foreach(pos p in temp.PositionsWithinDistance(3,map)){
                         CellType ch = map[p];
                         if(ch.Is(CellType.SpecialFeature1,CellType.SpecialFeature2,CellType.SpecialFeature3,CellType.SpecialFeature4,CellType.SpecialFeature5)){
                             floors = false;
                         }
                     }
                     if(floors){
                         if(R.CoinFlip()){
                             map[rr-1,rc] = shrines.RemoveFirst();
                             map[rr+1,rc] = shrines.RemoveFirst();
                         }
                         else{
                             map[rr,rc-1] = shrines.RemoveFirst();
                             map[rr,rc+1] = shrines.RemoveFirst();
                         }
                         CellType center = CellType.Wall;
                         while(center == CellType.Wall){
                             switch(R.Roll(5)){
                             case 1:
                                 if(level_types[current_level-1] != LevelType.Hive && level_types[current_level-1] != LevelType.Garden){
                                     center = CellType.Pillar;
                                 }
                                 break;
                             case 2:
                                 center = CellType.Statue;
                                 break;
                             case 3:
                                 if(level_types[current_level-1] != LevelType.Garden){
                                     center = CellType.FirePit;
                                 }
                                 break;
                             case 4:
                                 center = CellType.ShallowWater;
                                 break;
                             case 5:
                                 if(level_types[current_level-1] != LevelType.Hive){
                                     center = CellType.Torch;
                                 }
                                 break;
                             }
                         }
                         map[rr,rc] = center;
                         interesting_tiles.Remove(temp);
                         done = true;
                         break;
                     }
                 }
             }
             else{
                 if(map[rr,rc].IsFloor()){
                     bool good = true;
                     foreach(pos p in temp.PositionsWithinDistance(2,map)){
                         CellType ch = map[p];
                         if(ch.Is(CellType.SpecialFeature1,CellType.SpecialFeature2,CellType.SpecialFeature3,CellType.SpecialFeature4,CellType.SpecialFeature5)){
                             good = false;
                         }
                     }
                     if(good){
                         if(attempts > 1000){
                             map[rr,rc] = shrines.RemoveFirst();
                             interesting_tiles.Remove(temp);
                             done = true;
                             break;
                         }
                         else{
                             bool floors = true;
                             foreach(pos p in temp.PositionsAtDistance(1,map)){
                                 if(!map[p.row,p.col].IsFloor()){
                                     floors = false;
                                 }
                             }
                             if(floors){
                                 map[rr,rc] = shrines.RemoveFirst();
                                 interesting_tiles.Remove(temp);
                                 done = true;
                                 break;
                             }
                         }
                     }
                 }
                 if(map[rr,rc].IsWall()){
                     if(!map[rr+1,rc].IsFloor() && !map[rr-1,rc].IsFloor() && !map[rr,rc-1].IsFloor() && !map[rr,rc+1].IsFloor()){
                         continue; //no floors? retry.
                     }
                     bool no_good = false;
                     foreach(pos p in temp.PositionsWithinDistance(2,map)){
                         CellType ch = map[p];
                         if(ch.Is(CellType.SpecialFeature1,CellType.SpecialFeature2,CellType.SpecialFeature3,CellType.SpecialFeature4,CellType.SpecialFeature5)){
                             no_good = true;
                         }
                     }
                     if(no_good){
                         continue;
                     }
                     int walls = 0;
                     foreach(pos p in temp.PositionsAtDistance(1,map)){
                         if(map[p].IsWall()){
                             ++walls;
                         }
                     }
                     if(walls >= 5){
                         int successive_walls = 0;
                         CellType[] rotated = new CellType[8];
                         for(int i=0;i<8;++i){
                             pos temp2;
                             temp2 = temp.PosInDir(8.RotateDir(true,i));
                             rotated[i] = map[temp2.row,temp2.col];
                         }
                         for(int i=0;i<15;++i){
                             if(rotated[i%8].IsWall()){
                                 ++successive_walls;
                             }
                             else{
                                 successive_walls = 0;
                             }
                             if(successive_walls == 5){
                                 done = true;
                                 map[rr,rc] = shrines.RemoveFirst();
                                 interesting_tiles.Remove(temp);
                                 break;
                             }
                         }
                     }
                 }
             }
         }
     }
     int num_chests = R.Between(0,1);
     if(level_types[current_level-1] == LevelType.Crypt){
         num_chests -= map.PositionsWhere(x=>map[x] == CellType.Chest).Count;
     }
     for(int i=0;i<num_chests;++i){
         int tries = 0;
         for(bool done=false;!done;++tries){
             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[rr,rc] = CellType.RoomInterior;
             }
             if(map[rr,rc].IsFloor()){
                 bool floors = true;
                 pos temp = new pos(rr,rc);
                 foreach(pos p in temp.PositionsAtDistance(1,map)){
                     if(!map[p.row,p.col].IsFloor()){
                         floors = false;
                     }
                 }
                 if(floors || tries > 1000){ //after 1000 tries, place it anywhere
                     map[rr,rc] = CellType.Chest;
                     done = true;
                 }
             }
         }
     }
     attempts = 0;
     for(bool done=false;!done;++attempts){
         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;
             pos temp = new pos(rr,rc);
             foreach(pos p in temp.PositionsAtDistance(1,map)){
                 if(!map[p.row,p.col].IsFloor()){
                     floors = false;
                 }
             }
             if(floors || attempts > 1000){
                 map[rr,rc] = CellType.Stairs;
                 done = true;
             }
         }
     }
     if(level_types[current_level-1] != LevelType.Garden){
         GenerateFloorTypes(map);
         GenerateFeatures(map,interesting_tiles);
     }
     int num_traps = R.Roll(2,3);
     for(int i=0;i<num_traps;++i){
         int tries = 0;
         for(bool done=false;!done && tries < 100;++tries){
             int rr = R.Roll(ROWS-2);
             int rc = R.Roll(COLS-2);
             if(map[rr,rc].IsFloor() && map[rr,rc] != CellType.ShallowWater){
                 map[rr,rc] = CellType.Trap;
                 done = true;
             }
         }
     }
     List<Tile> hidden = new List<Tile>();
     Event grave_dirt_event = null;
     Event poppy_event = null;
     Tile stairs = null;
     for(int i=0;i<ROWS;++i){
         for(int j=0;j<COLS;++j){
             //Screen.WriteMapChar(i,j,map[i,j]);
             switch(map[i,j]){
             case CellType.Wall:
             case CellType.Pillar:
                 Tile.Create(TileType.WALL,i,j);
                 break;
             case CellType.Door:
                 if(R.OneIn(120)){
                     if(R.CoinFlip()){
                         Tile.Create(TileType.STONE_SLAB,i,j);
                         Q.Add(new Event(tile[i,j],new List<Tile>{tile[i,j]},100,EventType.STONE_SLAB));
                     }
                     else{
                         Tile.Create(TileType.HIDDEN_DOOR,i,j);
                         hidden.Add(tile[i,j]);
                     }
                 }
                 else{
                     Tile.Create(TileType.DOOR_C,i,j);
                 }
                 break;
             case CellType.Stairs:
                 if(current_level < 20){
                     Tile.Create(TileType.STAIRS,i,j);
                     stairs = tile[i,j];
                 }
                 else{
                     if(current_level == 20){
                         Tile.Create(TileType.STAIRS,i,j);
                         tile[i,j].color = Color.Red;
                         tile[i,j].SetName("scorched stairway");
                         stairs = tile[i,j];
                     }
                     else{
                         Tile.Create(TileType.FLOOR,i,j);
                     }
                 }
                 break;
             case CellType.Statue:
                 Tile.Create(TileType.STATUE,i,j);
                 break;
             case CellType.Rubble:
                 Tile.Create(TileType.RUBBLE,i,j);
                 break;
             case CellType.FirePit:
                 Tile.Create(TileType.FIREPIT,i,j);
                 break;
             case CellType.Pool:
                 Tile.Create(TileType.POOL_OF_RESTORATION,i,j);
                 break;
             case CellType.BlastFungus:
                 Tile.Create(TileType.BLAST_FUNGUS,i,j);
                 break;
             case CellType.CrackedWall:
                 Tile.Create(TileType.CRACKED_WALL,i,j);
                 break;
             case CellType.Chest:
                 Tile.Create(TileType.CHEST,i,j);
                 break;
             case CellType.Trap:
             {
                 TileType type = Tile.RandomTrap();
                 Tile.Create(type,i,j);
                 if(tile[i,j].IsTrap()){
                     tile[i,j].name = "floor";
                     tile[i,j].the_name = "the floor";
                     tile[i,j].a_name = "a floor";
                     tile[i,j].symbol = '.';
                     tile[i,j].color = Color.White;
                     hidden.Add(tile[i,j]);
                 }
                 break;
             }
             case CellType.Geyser:
                 Tile.Create(TileType.FIRE_GEYSER,i,j);
                 int frequency = R.Roll(31) + 8; //9-39
                 int variance = R.Roll(10) - 1; //0-9
                 int variance_amount = (frequency * variance) / 10;
                 int number_of_values = variance_amount*2 + 1;
                 int minimum_value = frequency - variance_amount;
                 if(minimum_value < 5){
                     int diff = 5 - minimum_value;
                     number_of_values -= diff;
                     minimum_value = 5;
                 }
                 int delay = ((minimum_value - 1) + R.Roll(number_of_values)) * 100;
                 Q.Add(new Event(tile[i,j],delay + 200,EventType.FIRE_GEYSER,(frequency*10)+variance)); //notice the hacky way the value is stored
                 Q.Add(new Event(tile[i,j],delay,EventType.FIRE_GEYSER_ERUPTION,2));
                 break;
             case CellType.FogVent:
                 Tile.Create(TileType.FOG_VENT,i,j);
                 Q.Add(new Event(tile[i,j],100,EventType.FOG_VENT));
                 break;
             case CellType.PoisonVent:
                 Tile.Create(TileType.POISON_GAS_VENT,i,j);
                 Q.Add(new Event(tile[i,j],100,EventType.POISON_GAS_VENT));
                 break;
             case CellType.Tombstone:
             {
                 Tile t = Tile.Create(TileType.TOMBSTONE,i,j);
                 if(R.OneIn(8)){
                     Q.Add(new Event(null,new List<Tile>{t},100,EventType.TOMBSTONE_GHOST));
                 }
                 break;
             }
             case CellType.HiddenDoor:
                 Tile.Create(TileType.HIDDEN_DOOR,i,j);
                 hidden.Add(tile[i,j]);
                 break;
             case CellType.SpecialFeature1:
                 Tile.Create(TileType.COMBAT_SHRINE,i,j);
                 break;
             case CellType.SpecialFeature2:
                 Tile.Create(TileType.DEFENSE_SHRINE,i,j);
                 break;
             case CellType.SpecialFeature3:
                 Tile.Create(TileType.MAGIC_SHRINE,i,j);
                 break;
             case CellType.SpecialFeature4:
                 Tile.Create(TileType.SPIRIT_SHRINE,i,j);
                 break;
             case CellType.SpecialFeature5:
                 Tile.Create(TileType.STEALTH_SHRINE,i,j);
                 break;
             case CellType.DeepWater:
             case CellType.ShallowWater:
                 Tile.Create(TileType.WATER,i,j);
                 break;
             case CellType.Barrel:
                 Tile.Create(TileType.BARREL,i,j);
                 break;
             case CellType.Brush:
                 Tile.Create(TileType.BRUSH,i,j);
                 break;
             case CellType.GlowingFungus:
                 Tile.Create(TileType.GLOWING_FUNGUS,i,j);
                 break;
             case CellType.GraveDirt:
                 Tile.Create(TileType.GRAVE_DIRT,i,j);
                 if(grave_dirt_event == null){
                     grave_dirt_event = new Event(new List<Tile>{tile[i,j]},100,EventType.GRAVE_DIRT);
                     Q.Add(grave_dirt_event);
                 }
                 else{
                     grave_dirt_event.area.Add(tile[i,j]);
                 }
                 break;
             case CellType.Gravel:
                 Tile.Create(TileType.GRAVEL,i,j);
                 break;
             case CellType.Ice:
                 Tile.Create(TileType.ICE,i,j);
                 break;
             case CellType.Oil:
                 Tile.Create(TileType.FLOOR,i,j);
                 tile[i,j].AddFeature(FeatureType.OIL);
                 break;
             case CellType.PoisonBulb:
                 Tile.Create(TileType.POISON_BULB,i,j);
                 break;
             case CellType.Poppies:
                 Tile.Create(TileType.POPPY_FIELD,i,j);
                 if(poppy_event == null){
                     poppy_event = new Event(new List<Tile>{tile[i,j]},100,EventType.POPPIES);
                     Q.Add(poppy_event);
                 }
                 else{
                     poppy_event.area.Add(tile[i,j]);
                 }
                 break;
             case CellType.Slime:
                 Tile.Create(TileType.FLOOR,i,j);
                 tile[i,j].AddFeature(FeatureType.SLIME);
                 break;
             case CellType.Torch:
                 Tile.Create(TileType.STANDING_TORCH,i,j);
                 break;
             case CellType.Vine:
                 Tile.Create(TileType.VINE,i,j);
                 break;
             case CellType.Webs:
                 Tile.Create(TileType.FLOOR,i,j);
                 tile[i,j].AddFeature(FeatureType.WEB);
                 break;
             default:
                 Tile.Create(TileType.FLOOR,i,j);
                 break;
             }
             //alltiles.Add(tile[i,j]);
             tile[i,j].solid_rock = true;
         }
     }
     //Input.ReadKey();
     player.ResetForNewLevel();
     foreach(Tile t in AllTiles()){
         if(t.light_radius > 0){
             t.UpdateRadius(0,t.light_radius);
         }
     }
     int num_items = R.Between(0,2);
     for(int i=num_items;i>0;--i){
         SpawnItem();
     }
     bool poltergeist_spawned = false; //i'm not sure this is the right call, but for now
     bool mimic_spawned = false; // i'm limiting these guys, to avoid "empty" levels
     bool marble_horror_spawned = false;
     int num_monsters = R.Roll(2,2) + 4;
     if(num_monsters == 6){
         num_monsters += R.Roll(3); //this works out to 7/12 seven, 4/12 eight, and 1/12 nine.
     }
     for(int i=num_monsters;i>0;--i){
         ActorType type = MobType();
         if(type == ActorType.POLTERGEIST){
             if(!poltergeist_spawned){
                 SpawnMob(type);
                 poltergeist_spawned = true;
             }
             else{
                 ++i; //try again..
             }
         }
         else{
             if(type == ActorType.MIMIC){
                 if(!mimic_spawned){
                     SpawnMob(type);
                     mimic_spawned = true;
                 }
                 else{
                     ++i;
                 }
             }
             else{
                 if(type == ActorType.MARBLE_HORROR){
                     Tile statue = AllTiles().Where(t=>t.type == TileType.STATUE).RandomOrDefault();
                     if(!marble_horror_spawned && statue != null){
                         SpawnMob(type);
                         marble_horror_spawned = true;
                     }
                     else{
                         ++i;
                     }
                 }
                 else{
                     if(type == ActorType.ENTRANCER){
                         if(i >= 2){ //need 2 slots here
                             Actor entrancer = SpawnMob(type);
                             entrancer.attrs[AttrType.WANDERING]++;
                             List<Tile> tiles = new List<Tile>();
                             int dist = 1;
                             while(tiles.Count == 0 && dist < 100){
                                 foreach(Tile t in entrancer.TilesAtDistance(dist)){
                                     if(t.passable && !t.IsTrap() && t.actor() == null){
                                         tiles.Add(t);
                                     }
                                 }
                                 ++dist;
                             }
                             if(tiles.Count > 0){
                                 ActorType thralltype = ActorType.SPECIAL;
                                 bool done = false;
                                 while(!done){
                                     thralltype = MobType();
                                     switch(thralltype){ //not on the list: group/stealth/ranged/rare/immobile/hazardous monsters, plus carrion crawler, mechanical knight, mud elemental, and luminous avenger
                                     case ActorType.ROBED_ZEALOT:
                                     case ActorType.WILD_BOAR:
                                     case ActorType.TROLL:
                                     case ActorType.DERANGED_ASCETIC:
                                     case ActorType.ALASI_BATTLEMAGE:
                                     case ActorType.ALASI_SOLDIER:
                                     case ActorType.SKITTERMOSS:
                                     case ActorType.STONE_GOLEM:
                                     case ActorType.OGRE_BARBARIAN:
                                     case ActorType.SNEAK_THIEF:
                                     case ActorType.CRUSADING_KNIGHT:
                                     case ActorType.CORROSIVE_OOZE:
                                     case ActorType.ALASI_SENTINEL:
                                     case ActorType.CYCLOPEAN_TITAN:
                                         done = true;
                                         break;
                                     }
                                 }
                                 Tile t = tiles.Random();
                                 Actor thrall = Actor.Create(thralltype,t.row,t.col,TiebreakerAssignment.InsertAfterCurrent);
                                 if(entrancer.group == null){
                                     entrancer.group = new List<Actor>{entrancer};
                                 }
                                 entrancer.group.Add(thrall);
                                 thrall.group = entrancer.group;
                                 --i;
                             }
                         }
                         else{
                             ++i;
                         }
                     }
                     else{
                         Actor a = SpawnMob(type);
                         if(type == ActorType.WARG){
                             if(a.group != null){
                                 foreach(Actor a2 in a.group){
                                     a2.attrs[AttrType.WANDERING]++;
                                 }
                             }
                         }
                         else{
                             if(a.AlwaysWanders() || (R.PercentChance(40) && a.CanWanderAtLevelGen())){
                                 a.attrs[AttrType.WANDERING]++;
                             }
                         }
                     }
                 }
             }
         }
     }
     for(int i=(current_level-3)/4;i>0;--i){ //yes, this is all copied and pasted for a one-line change. i'll try to fix it later.
         if(R.CoinFlip()){ //generate some shallow monsters
             ActorType type = ShallowMobType();
             if(type == ActorType.POLTERGEIST){
                 if(!poltergeist_spawned){
                     SpawnMob(type);
                     poltergeist_spawned = true;
                 }
                 else{
                     ++i; //try again..
                 }
             }
             else{
                 if(type == ActorType.MIMIC){
                     if(!mimic_spawned){
                         SpawnMob(type);
                         mimic_spawned = true;
                     }
                     else{
                         ++i;
                     }
                 }
                 else{
                     if(type == ActorType.MARBLE_HORROR){
                         Tile statue = AllTiles().Where(t=>t.type == TileType.STATUE).RandomOrDefault();
                         if(!marble_horror_spawned && statue != null){
                             SpawnMob(type);
                             marble_horror_spawned = true;
                         }
                         else{
                             ++i;
                         }
                     }
                     else{
                         if(type == ActorType.ENTRANCER){
                             if(i >= 2){ //need 2 slots here
                                 Actor entrancer = SpawnMob(type);
                                 entrancer.attrs[AttrType.WANDERING]++;
                                 List<Tile> tiles = new List<Tile>();
                                 int dist = 1;
                                 while(tiles.Count == 0 && dist < 100){
                                     foreach(Tile t in entrancer.TilesAtDistance(dist)){
                                         if(t.passable && !t.IsTrap() && t.actor() == null){
                                             tiles.Add(t);
                                         }
                                     }
                                     ++dist;
                                 }
                                 if(tiles.Count > 0){
                                     ActorType thralltype = ActorType.SPECIAL;
                                     bool done = false;
                                     while(!done){
                                         thralltype = MobType();
                                         switch(thralltype){
                                         case ActorType.ROBED_ZEALOT:
                                         case ActorType.DERANGED_ASCETIC:
                                         case ActorType.BERSERKER:
                                         case ActorType.TROLL:
                                         case ActorType.CRUSADING_KNIGHT:
                                         case ActorType.SKITTERMOSS:
                                         case ActorType.OGRE_BARBARIAN:
                                         case ActorType.SNEAK_THIEF:
                                         case ActorType.STONE_GOLEM:
                                         case ActorType.LUMINOUS_AVENGER:
                                         case ActorType.WILD_BOAR:
                                         case ActorType.ALASI_SOLDIER:
                                         case ActorType.CORROSIVE_OOZE:
                                         case ActorType.ALASI_SENTINEL:
                                             done = true;
                                             break;
                                         }
                                     }
                                     Tile t = tiles.Random();
                                     Actor thrall = Actor.Create(thralltype,t.row,t.col,TiebreakerAssignment.InsertAfterCurrent);
                                     if(entrancer.group == null){
                                         entrancer.group = new List<Actor>{entrancer};
                                     }
                                     entrancer.group.Add(thrall);
                                     thrall.group = entrancer.group;
                                     --i;
                                 }
                             }
                             else{
                                 ++i;
                             }
                         }
                         else{
                             Actor a = SpawnMob(type);
                             if(type == ActorType.WARG){
                                 if(a.group != null){
                                     foreach(Actor a2 in a.group){
                                         a2.attrs[AttrType.WANDERING]++;
                                     }
                                 }
                             }
                             else{
                                 if(a.AlwaysWanders() || (R.PercentChance(40) && a.CanWanderAtLevelGen())){
                                     a.attrs[AttrType.WANDERING]++;
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
     int minimum_distance_from_stairs = 0;
     PosArray<int> distance_from_stairs = null;
     if(stairs != null){
         distance_from_stairs = tile.GetDijkstraMap(new List<pos>{stairs.p},x=>tile[x].BlocksConnectivityOfMap());
         minimum_distance_from_stairs = distance_from_stairs[distance_from_stairs.PositionsWhere(x=>distance_from_stairs[x].IsValidDijkstraValue()).WhereGreatest(x=>distance_from_stairs[x]).Random()] / 2;
     }
     bool[,] good_location = new bool[ROWS,COLS];
     for(int i=0;i<ROWS;++i){
         for(int j=0;j<COLS;++j){
             if(tile[i,j].Is(TileType.FLOOR) && !tile[i,j].Is(FeatureType.WEB) && (stairs == null || distance_from_stairs[i,j] >= minimum_distance_from_stairs)){
                 good_location[i,j] = true;
             }
             else{
                 good_location[i,j] = false;
             }
         }
     }
     foreach(Actor a in AllActors()){
         if(a != player){
             good_location[a.row,a.col] = false;
             for(int i=0;i<ROWS;++i){
                 for(int j=0;j<COLS;++j){
                     if(good_location[i,j] && a.HasLOS(i,j)){
                         good_location[i,j] = false;
                     }
                 }
             }
         }
     }
     bool at_least_one_good = false;
     for(int i=0;i<ROWS && !at_least_one_good;++i){
         for(int j=0;j<COLS && !at_least_one_good;++j){
             if(good_location[i,j]){
                 at_least_one_good = true;
             }
         }
     }
     if(!at_least_one_good){
         foreach(Actor a in AllActors()){
             if(a != player){
                 good_location[a.row,a.col] = false;
                 for(int i=0;i<ROWS;++i){
                     for(int j=0;j<COLS;++j){
                         if(good_location[i,j] && a.CanSee(i,j)){ //checking CanSee this time
                             good_location[i,j] = false;
                         }
                     }
                 }
             }
         }
     }
     List<Tile> goodtiles = new List<Tile>();
     for(int i=0;i<ROWS;++i){
         for(int j=0;j<COLS;++j){
             if(good_location[i,j]){
                 goodtiles.Add(tile[i,j]);
             }
         }
     }
     if(goodtiles.Count > 0){
         Tile t = goodtiles.Random();
         int light = player.light_radius;
         int burning = player.attrs[AttrType.BURNING];
         int shining = player.attrs[AttrType.SHINING];
         player.light_radius = 0;
         player.attrs[AttrType.BURNING] = 0;
         player.attrs[AttrType.SHINING] = 0;
         player.Move(t.row,t.col);
         player.light_radius = light;
         player.attrs[AttrType.BURNING] = burning;
         player.attrs[AttrType.SHINING] = shining;
         player.UpdateRadius(0,player.LightRadius());
     }
     else{
         for(bool done=false;!done;){
             int rr = R.Roll(ROWS-2);
             int rc = R.Roll(COLS-2);
             bool good = true;
             foreach(Tile t in tile[rr,rc].TilesWithinDistance(1)){
                 if(t.IsTrap()){
                     good = false;
                 }
             }
             if(good && tile[rr,rc].passable && actor[rr,rc] == null){
                 int light = player.light_radius;
                 int burning = player.attrs[AttrType.BURNING];
                 int shining = player.attrs[AttrType.SHINING];
                 player.light_radius = 0;
                 player.attrs[AttrType.BURNING] = 0;
                 player.attrs[AttrType.SHINING] = 0;
                 player.Move(rr,rc);
                 player.light_radius = light;
                 player.attrs[AttrType.BURNING] = burning;
                 player.attrs[AttrType.SHINING] = shining;
                 player.UpdateRadius(0,player.LightRadius());
                 done = true;
             }
         }
     }
     actor[player.row,player.col] = player; //this line fixes a bug that occurs when the player ends up in the same position on a new level
     Screen.screen_center_col = player.col;
     if(R.CoinFlip()){ //todo: copied and pasted below
         bool done = false;
         for(int tries=0;!done && tries<500;++tries){
             int rr = R.Roll(ROWS-4) + 1;
             int rc = R.Roll(COLS-4) + 1;
             bool good = true;
             foreach(Tile t in tile[rr,rc].TilesWithinDistance(2)){
                 if(t.type != TileType.WALL){
                     good = false;
                     break;
                 }
             }
             if(good){
                 List<int> dirs = new List<int>();
                 bool long_corridor = false;
                 int connections = 0;
                 for(int i=2;i<=8;i+=2){
                     Tile t = tile[rr,rc].TileInDirection(i).TileInDirection(i);
                     bool good_dir = true;
                     int distance = -1;
                     while(good_dir && t != null && t.type == TileType.WALL){
                         if(t.TileInDirection(i.RotateDir(false,2)).type != TileType.WALL){
                             good_dir = false;
                         }
                         if(t.TileInDirection(i.RotateDir(true,2)).type != TileType.WALL){
                             good_dir = false;
                         }
                         t = t.TileInDirection(i);
                         if(t != null && t.type == TileType.STATUE){
                             good_dir = false;
                         }
                         ++distance;
                     }
                     if(good_dir && t != null){
                         dirs.Add(i);
                         ++connections;
                         if(distance >= 4){
                             long_corridor = true;
                         }
                     }
                 }
                 if(dirs.Count > 0){
                     List<TileType> all_possible_traps = new List<TileType>{TileType.GRENADE_TRAP,TileType.POISON_GAS_TRAP,TileType.PHANTOM_TRAP,TileType.FIRE_TRAP,TileType.SHOCK_TRAP,TileType.SCALDING_OIL_TRAP,TileType.FLING_TRAP,TileType.STONE_RAIN_TRAP};
                     List<TileType> possible_traps = new List<TileType>();
                     //int trap_roll = R.Roll(7);
                     int num_types = R.Between(2,3);
                     /*if(trap_roll == 1){
                         num_types = 1;
                     }
                     else{
                         if(trap_roll <= 4){
                             num_types = 2;
                         }
                         else{
                             num_types = 3;
                         }
                     }*/
                     for(int i=0;i<num_types;++i){
                         TileType tt = all_possible_traps.Random();
                         if(possible_traps.Contains(tt)){
                             --i;
                         }
                         else{
                             possible_traps.Add(tt);
                         }
                     }
                     bool stone_slabs = false; //(instead of hidden doors)
                     if(R.OneIn(4)){
                         stone_slabs = true;
                     }
                     foreach(int i in dirs){
                         Tile t = tile[rr,rc].TileInDirection(i);
                         int distance = -2; //distance of the corridor between traps and secret door
                         while(t.type == TileType.WALL){
                             ++distance;
                             t = t.TileInDirection(i);
                         }
                         if(long_corridor && distance < 4){
                             continue;
                         }
                         t = tile[rr,rc].TileInDirection(i);
                         while(t.type == TileType.WALL){
                             if(distance >= 4){
                                 TileType tt = TileType.FLOOR;
                                 if(R.Roll(3) >= 2){
                                     tt = possible_traps.Random();
                                     hidden.Add(t);
                                 }
                                 t.TransformTo(tt);
                                 t.name = "floor";
                                 t.the_name = "the floor";
                                 t.a_name = "a floor";
                                 t.symbol = '.';
                                 t.color = Color.White;
                                 if(t.DistanceFrom(tile[rr,rc]) < distance+2){
                                     Tile neighbor = t.TileInDirection(i.RotateDir(false,2));
                                     if(neighbor.TileInDirection(i.RotateDir(false,1)).type == TileType.WALL
                                        && neighbor.TileInDirection(i.RotateDir(false,2)).type == TileType.WALL
                                        && neighbor.TileInDirection(i.RotateDir(false,3)).type == TileType.WALL){
                                         tt = TileType.FLOOR;
                                         if(R.Roll(3) >= 2){
                                             tt = possible_traps.Random();
                                         }
                                         neighbor.TransformTo(tt);
                                         if(possible_traps.Contains(tt)){
                                             neighbor.name = "floor";
                                             neighbor.the_name = "the floor";
                                             neighbor.a_name = "a floor";
                                             neighbor.symbol = '.';
                                             neighbor.color = Color.White;
                                             hidden.Add(neighbor);
                                         }
                                     }
                                     neighbor = t.TileInDirection(i.RotateDir(true,2));
                                     if(neighbor.TileInDirection(i.RotateDir(true,1)).type == TileType.WALL
                                        && neighbor.TileInDirection(i.RotateDir(true,2)).type == TileType.WALL
                                        && neighbor.TileInDirection(i.RotateDir(true,3)).type == TileType.WALL){
                                         tt = TileType.FLOOR;
                                         if(R.Roll(3) >= 2){
                                             tt = possible_traps.Random();
                                         }
                                         neighbor.TransformTo(tt);
                                         if(possible_traps.Contains(tt)){
                                             neighbor.name = "floor";
                                             neighbor.the_name = "the floor";
                                             neighbor.a_name = "a floor";
                                             neighbor.symbol = '.';
                                             neighbor.color = Color.White;
                                             hidden.Add(neighbor);
                                         }
                                     }
                                 }
                             }
                             else{
                                 TileType tt = TileType.FLOOR;
                                 if(R.CoinFlip()){
                                     tt = Tile.RandomTrap();
                                     hidden.Add(t);
                                 }
                                 t.TransformTo(tt);
                                 if(tt != TileType.FLOOR){
                                     t.name = "floor";
                                     t.the_name = "the floor";
                                     t.a_name = "a floor";
                                     t.symbol = '.';
                                     t.color = Color.White;
                                 }
                             }
                             t = t.TileInDirection(i);
                         }
                         t = t.TileInDirection(i.RotateDir(true,4));
                         if(stone_slabs){
                             t.TransformTo(TileType.STONE_SLAB);
                             Q.Add(new Event(t,new List<Tile>{t.TileInDirection(i.RotateDir(true,4))},100,EventType.STONE_SLAB));
                         }
                         else{
                             t.TransformTo(TileType.HIDDEN_DOOR);
                             hidden.AddUnique(t);
                         }
                         t = t.TileInDirection(i.RotateDir(true,4));
                         if(R.CoinFlip()){
                             if(t.IsTrap()){
                                 t.type = TileType.ALARM_TRAP;
                             }
                             else{
                                 t.TransformTo(TileType.ALARM_TRAP);
                                 t.name = "floor";
                                 t.the_name = "the floor";
                                 t.a_name = "a floor";
                                 t.symbol = '.';
                                 t.color = Color.White;
                                 hidden.AddUnique(t);
                             }
                         }
                     }
                     if(long_corridor && connections == 1){
                         foreach(Tile t in tile[rr,rc].TilesWithinDistance(1)){
                             t.TransformTo(possible_traps.Random());
                             t.name = "floor";
                             t.the_name = "the floor";
                             t.a_name = "a floor";
                             t.symbol = '.';
                             t.color = Color.White;
                             hidden.Add(t);
                         }
                         tile[rr,rc].TileInDirection(dirs[0].RotateDir(true,4)).TransformTo(TileType.CHEST);
                         tile[rr,rc].TileInDirection(dirs[0].RotateDir(true,4)).color = Color.Yellow;
                     }
                     else{
                         foreach(Tile t in tile[rr,rc].TilesAtDistance(1)){
                             t.TransformTo(Tile.RandomTrap());
                             t.name = "floor";
                             t.the_name = "the floor";
                             t.a_name = "a floor";
                             t.symbol = '.';
                             t.color = Color.White;
                             hidden.Add(t);
                         }
                         tile[rr,rc].TransformTo(TileType.CHEST);
                         tile[rr,rc].color = Color.Yellow;
                     }
                     done = true;
                 }
             }
         }
     }
     /*if(R.CoinFlip()){
         bool done = false;
         for(int tries=0;!done && tries<500;++tries){
             int rr = R.Roll(ROWS-4) + 1;
             int rc = R.Roll(COLS-4) + 1;
             bool good = true;
             foreach(Tile t in tile[rr,rc].TilesWithinDistance(2)){
                 if(t.type != TileType.WALL){
                     good = false;
                     break;
                 }
             }
             if(good){
                 List<int> dirs = new List<int>();
                 bool long_corridor = false;
                 int connections = 0;
                 for(int i=2;i<=8;i+=2){
                     Tile t = tile[rr,rc].TileInDirection(i).TileInDirection(i);
                     bool good_dir = true;
                     int distance = -1;
                     while(good_dir && t != null && t.type == TileType.WALL){
                         if(t.TileInDirection(i.RotateDir(false,2)).type != TileType.WALL){
                             good_dir = false;
                         }
                         if(t.TileInDirection(i.RotateDir(true,2)).type != TileType.WALL){
                             good_dir = false;
                         }
                         t = t.TileInDirection(i);
                         if(t != null && t.type == TileType.STATUE){
                             good_dir = false;
                         }
                         ++distance;
                     }
                     if(good_dir && t != null){
                         dirs.Add(i);
                         ++connections;
                         if(distance >= 4){
                             long_corridor = true;
                         }
                     }
                 }
                 if(dirs.Count > 0){
                     List<TileType> possible_traps = new List<TileType>();
                     int trap_roll = R.Roll(7);
                     if(trap_roll == 1 || trap_roll == 4 || trap_roll == 5 || trap_roll == 7){
                         possible_traps.Add(TileType.GRENADE_TRAP);
                     }
                     if(trap_roll == 2 || trap_roll == 4 || trap_roll == 6 || trap_roll == 7){
                         possible_traps.Add(TileType.POISON_GAS_TRAP);
                     }
                     if(trap_roll == 3 || trap_roll == 5 || trap_roll == 6 || trap_roll == 7){
                         possible_traps.Add(TileType.PHANTOM_TRAP);
                     }
                     bool stone_slabs = false; //(instead of hidden doors)
                     if(R.OneIn(4)){
                         stone_slabs = true;
                     }
                     foreach(int i in dirs){
                         Tile t = tile[rr,rc].TileInDirection(i);
                         int distance = -2; //distance of the corridor between traps and secret door
                         while(t.type == TileType.WALL){
                             ++distance;
                             t = t.TileInDirection(i);
                         }
                         if(long_corridor && distance < 4){
                             continue;
                         }
                         t = tile[rr,rc].TileInDirection(i);
                         while(t.type == TileType.WALL){
                             if(distance >= 4){
                                 TileType tt = TileType.FLOOR;
                                 if(R.Roll(3) >= 2){
                                     tt = possible_traps.Random();
                                     hidden.Add(t);
                                 }
                                 t.TransformTo(tt);
                                 t.name = "floor";
                                 t.the_name = "the floor";
                                 t.a_name = "a floor";
                                 t.symbol = '.';
                                 t.color = Color.White;
                                 if(t.DistanceFrom(tile[rr,rc]) < distance+2){
                                     Tile neighbor = t.TileInDirection(i.RotateDir(false,2));
                                     if(neighbor.TileInDirection(i.RotateDir(false,1)).type == TileType.WALL
                                        && neighbor.TileInDirection(i.RotateDir(false,2)).type == TileType.WALL
                                        && neighbor.TileInDirection(i.RotateDir(false,3)).type == TileType.WALL){
                                         tt = TileType.FLOOR;
                                         if(R.Roll(3) >= 2){
                                             tt = possible_traps.Random();
                                         }
                                         neighbor.TransformTo(tt);
                                         if(possible_traps.Contains(tt)){
                                             neighbor.name = "floor";
                                             neighbor.the_name = "the floor";
                                             neighbor.a_name = "a floor";
                                             neighbor.symbol = '.';
                                             neighbor.color = Color.White;
                                             hidden.Add(neighbor);
                                         }
                                     }
                                     neighbor = t.TileInDirection(i.RotateDir(true,2));
                                     if(neighbor.TileInDirection(i.RotateDir(true,1)).type == TileType.WALL
                                        && neighbor.TileInDirection(i.RotateDir(true,2)).type == TileType.WALL
                                        && neighbor.TileInDirection(i.RotateDir(true,3)).type == TileType.WALL){
                                         tt = TileType.FLOOR;
                                         if(R.Roll(3) >= 2){
                                             tt = possible_traps.Random();
                                         }
                                         neighbor.TransformTo(tt);
                                         if(possible_traps.Contains(tt)){
                                             neighbor.name = "floor";
                                             neighbor.the_name = "the floor";
                                             neighbor.a_name = "a floor";
                                             neighbor.symbol = '.';
                                             neighbor.color = Color.White;
                                             hidden.Add(neighbor);
                                         }
                                     }
                                 }
                             }
                             else{
                                 TileType tt = TileType.FLOOR;
                                 if(R.CoinFlip()){
                                     tt = Tile.RandomTrap();
                                     hidden.Add(t);
                                 }
                                 t.TransformTo(tt);
                                 if(tt != TileType.FLOOR){
                                     t.name = "floor";
                                     t.the_name = "the floor";
                                     t.a_name = "a floor";
                                     t.symbol = '.';
                                     t.color = Color.White;
                                 }
                             }
                             t = t.TileInDirection(i);
                         }
                         t = t.TileInDirection(i.RotateDir(true,4));
                         if(stone_slabs){
                             t.TransformTo(TileType.STONE_SLAB);
                             Q.Add(new Event(t,new List<Tile>{t.TileInDirection(i.RotateDir(true,4))},100,EventType.STONE_SLAB));
                         }
                         else{
                             t.TransformTo(TileType.HIDDEN_DOOR);
                             hidden.AddUnique(t);
                         }
                         t = t.TileInDirection(i.RotateDir(true,4));
                         if(R.CoinFlip()){
                             if(t.IsTrap()){
                                 t.type = TileType.ALARM_TRAP;
                             }
                             else{
                                 t.TransformTo(TileType.ALARM_TRAP);
                                 t.name = "floor";
                                 t.the_name = "the floor";
                                 t.a_name = "a floor";
                                 t.symbol = '.';
                                 t.color = Color.White;
                                 hidden.AddUnique(t);
                             }
                         }
                     }
                     if(long_corridor && connections == 1){
                         foreach(Tile t in tile[rr,rc].TilesWithinDistance(1)){
                             t.TransformTo(possible_traps.Random());
                             t.name = "floor";
                             t.the_name = "the floor";
                             t.a_name = "a floor";
                             t.symbol = '.';
                             t.color = Color.White;
                             hidden.Add(t);
                         }
                         tile[rr,rc].TileInDirection(dirs[0].RotateDir(true,4)).TransformTo(TileType.CHEST);
                         tile[rr,rc].TileInDirection(dirs[0].RotateDir(true,4)).color = Color.Yellow;
                     }
                     else{
                         foreach(Tile t in tile[rr,rc].TilesAtDistance(1)){
                             t.TransformTo(Tile.RandomTrap());
                             t.name = "floor";
                             t.the_name = "the floor";
                             t.a_name = "a floor";
                             t.symbol = '.';
                             t.color = Color.White;
                             hidden.Add(t);
                         }
                         tile[rr,rc].TransformTo(TileType.CHEST);
                         tile[rr,rc].color = Color.Yellow;
                     }
                     done = true;
                 }
             }
         }
     }*/
     foreach(Tile t in AllTiles()){
         if(t.type != TileType.WALL){
             foreach(Tile neighbor in t.TilesAtDistance(1)){
                 neighbor.solid_rock = false;
             }
         }
         if(t.type == TileType.GLOWING_FUNGUS){
             foreach(Tile neighbor in t.TilesAtDistance(1)){
                 if(neighbor.type == TileType.WALL){
                     neighbor.color = Color.RandomGlowingFungus;
                 }
             }
         }
     }
     if(level_types[current_level-1] == LevelType.Hive){
         var dijkstra = tile.GetDijkstraMap(x=>tile[x].type != TileType.WALL,x=>false);
         for(int i=0;i<ROWS;++i){
             for(int j=0;j<COLS;++j){
                 if((dijkstra[i,j] == 1 && !R.OneIn(20)) || (dijkstra[i,j] == 2 && R.CoinFlip())){
                     if(i == 0 || j == 0 || i == ROWS-1 || j == COLS-1){
                         tile[i,j].color = Color.DarkYellow;
                         tile[i,j].SetName("waxy wall"); //borders become waxy but don't burn
                     }
                     else{
                         tile[i,j].Toggle(null,TileType.WAX_WALL);
                     }
                 }
             }
         }
     }
     if(level_types[current_level-1] == LevelType.Fortress){
         foreach(pos p in tile.PositionsWhere(x=>tile[x].Is(TileType.WALL,TileType.HIDDEN_DOOR))){
             tile[p].color = Color.TerrainDarkGray;
         }
     }
     string desc = GetDungeonDescription();
     for(int i=0;i<ROWS;++i){
         for(int j=0;j<COLS;++j){ //todo fix this!
             if(!tile.BoundsCheck(i,j,false)) continue;
             pos p = new pos(i,j);
             if(SchismExtensionMethods.Extensions.ConsecutiveAdjacent(p,x=>!tile[x].BlocksConnectivityOfMap()) >= 3){ //todo, fix check!
                 dungeon_description[i,j] = desc;
             }
             else{
                 dungeon_description[i,j] = "You pass through a hallway of hewn stone."; //todo, fix message!
             }
         }
     }
     if(poppy_event != null){
         CalculatePoppyDistanceMap();
     }
     if(hidden.Count > 0){
         Event e = new Event(hidden,100,EventType.CHECK_FOR_HIDDEN);
         e.tiebreaker = 0;
         Q.Add(e);
     }
     {
         Event e = new Event(10000,EventType.RELATIVELY_SAFE);
         e.tiebreaker = 0;
         Q.Add(e);
     }
     {
         Event e = new Event(R.Between(400,450)*100,EventType.SPAWN_WANDERING_MONSTER);
         e.tiebreaker = 0;
         Q.Add(e);
     }
     if(current_level == 1){
         B.Add("In the mountain pass where travelers vanish, a stone staircase leads downward... Welcome, " + Actor.player_name + "! ");
     }
     else{
         B.Add(LevelMessage());
     }
 }