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;
     });
 }
 public bool AddRockFormations(int percent_chance_per_room,int minimum_distance_from_wall)
 {
     return ForEachRoom(list => {
         if(PercentChance(percent_chance_per_room)){
             int start_r = list.WhereLeast(x=>x.row)[0].row;
             int end_r = list.WhereGreatest(x=>x.row)[0].row;
             int start_c = list.WhereLeast(x=>x.col)[0].col;
             int end_c = list.WhereGreatest(x=>x.col)[0].col;
             Dungeon room = new Dungeon((end_r - start_r) + 3,(end_c - start_c) + 3); //includes borders
             while(true){
                 room.FillWithRandomWalls(25);
                 room.ApplyCellularAutomataXYRule(3);
                 for(int i=start_r;i<=end_r;++i){
                     for(int j=start_c;j<=end_c;++j){
                         pos p = new pos(i,j);
                         if(!p.PositionsWithinDistance(minimum_distance_from_wall-1,room.map).All(x=>list.Contains(x))){ //todo: I probably broke this.
                             room[i-start_r+1,j-start_c+1] = CellType.RoomInterior;
                         }
                     }
                 }
                 room.ConnectDiagonals();
                 room.RemoveDeadEndCorridors();
                 room.RemoveUnconnectedAreas();
                 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;
             }
         }
         return true;
     });
 }
 public bool MakeRoomsCavelike(int percent_chance_per_room,bool ignore_rooms_with_single_exit)
 {
     //this isn't guaranteed to succeed, so you might need to check the return value
     return ForEachRoom(list => {
         if(PercentChance(percent_chance_per_room)){
             int start_r = list.WhereLeast(x=>x.row)[0].row;
             int end_r = list.WhereGreatest(x=>x.row)[0].row;
             int start_c = list.WhereLeast(x=>x.col)[0].col;
             int end_c = list.WhereGreatest(x=>x.col)[0].col;
             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 && ignore_rooms_with_single_exit){
                 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;
     });
 }
 public void AddLake(bool allow_disconnected_lakes)
 {
     bool trees = CoinFlip();
     while(true){
         Dungeon shape = new Dungeon(21,21);
         shape[10,10] = CellType.DeepWater;
         for(bool done=false;!done;){
             pos p = new pos(Roll(19),Roll(19));
             if(shape[p].Is(CellType.Wall)){
                 bool good = false;
                 foreach(int dir in FourDirections){
                     if(shape[p.PosInDir(dir)].Is(CellType.DeepWater)){
                         good = true;
                         break;
                     }
                 }
                 if(good){
                     if(p.ApproximateEuclideanDistanceFromX10(10,10) < 100){
                         if(PercentChance(100 - p.ApproximateEuclideanDistanceFromX10(10,10))){
                             shape[p] = CellType.DeepWater;
                             if(p.row == 1 || p.col == 1 || p.row == 19 || p.col == 19){
                                 done = true;
                             }
                         }
                     }
                 }
             }
         }
         shape.RemoveDeadEndCorridors();
         shape.ApplyCellularAutomataXYRule(5);
         for(int i=0;i<21;++i){
             for(int j=0;j<21;++j){
                 if(!shape[i,j].IsWall()){
                     bool done = false;
                     for(int s=0;s<21 && !done;++s){
                         for(int t=0;t<21 && !done;++t){
                             if(shape[s,t].IsWall() && new pos(i,j).ApproximateEuclideanDistanceFromX10(s,t) < 20){
                                 shape[i,j] = CellType.ShallowWater;
                                 done = true;
                             }
                             else{
                                 if(shape[s,t].IsWall() && new pos(i,j).ApproximateEuclideanDistanceFromX10(s,t) == 20){
                                     if(CoinFlip()){
                                         shape[i,j] = CellType.ShallowWater;
                                         done = true;
                                     }
                                 }
                             }
                         }
                     }
                     if(!done){
                         shape[i,j] = CellType.DeepWater;
                     }
                 }
             }
         }
         int start_r = 999; //these are for the position of the lake within the "shape" variable
         int start_c = 999;
         int end_r = -1;
         int end_c = -1;
         for(int i=0;i<21;++i){
             for(int j=0;j<21;++j){
                 if(!shape[i,j].IsWall()){
                     if(i < start_r){
                         start_r = i;
                     }
                     if(i > end_r){
                         end_r = i;
                     }
                     if(j < start_c){
                         start_c = j;
                     }
                     if(j > end_c){
                         end_c = j;
                     }
                 }
             }
         }
         int lake_h = (end_r - start_r) + 1;
         int lake_w = (end_c - start_c) + 1;
         CellType[,] old_cells = new CellType[lake_h,lake_w];
         for(int tries=0;tries<200;++tries){
             int rr = Roll(H - lake_h - 1);
             int rc = Roll(W - lake_w - 1);
             for(int i=0;i<lake_h;++i){
                 for(int j=0;j<lake_w;++j){
                     old_cells[i,j] = map[i+rr,j+rc];
                     if(shape[i+start_r,j+start_c].Is(CellType.ShallowWater,CellType.DeepWater) && !map[i+rr,j+rc].Is(CellType.DeepWater)){
                         map[i+rr,j+rc] = shape[i+start_r,j+start_c];
                     }
                 }
             }
             if(allow_disconnected_lakes || this.IsFullyConnected()){
                 for(int i=0;i<lake_h;++i){
                     for(int j=0;j<lake_w;++j){
                         if(map[i+rr,j+rc] == CellType.ShallowWater){
                             if(trees){
                                 map[i+rr,j+rc] = CellType.Tree;
                             }
                         }
                         if(map[i+rr,j+rc] == CellType.DeepWater){
                             if(trees){
                                 map[i+rr,j+rc] = CellType.RoomInterior;
                             }
                         }
                     }
                 }
                 return;
             }
             else{
                 for(int i=0;i<lake_h;++i){ //undo what we just did and try again
                     for(int j=0;j<lake_w;++j){
                         map[i+rr,j+rc] = old_cells[i,j];
                     }
                 }
             }
         }
     }
 }
Beispiel #5
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;
 }