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 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]; } } } } } }
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 PlaceShape(Dungeon room_to_be_placed,bool force_connection,bool force_cardinal_direction_connection,bool allow_room_placement_under_features,params CellType[] allowed_to_overwrite) { int first_row = H+1; int first_col = W+1; //todo: this doesn't respect the map boundaries yet - it can overwrite the edge walls with the room. int last_row = -1; int last_col = -1; for(int i=0;i<room_to_be_placed.H;++i){ for(int j=0;j<room_to_be_placed.W;++j){ if(!room_to_be_placed[i,j].IsWall()){ if(i < first_row){ first_row = i; } if(i > last_row){ last_row = i; } if(j < first_col){ first_col = j; } if(j > last_col){ last_col = j; } } } } Dungeon room = new Dungeon((last_row - first_row) + 1,(last_col - first_col) + 1); int rh = room.H; int rw = room.W; //'force connection' means that the added room must connect somehow to the existing passable tiles of the map. for(int i=0;i<rh;++i){ for(int j=0;j<rw;++j){ room[i,j] = room_to_be_placed[i+first_row,j+first_col]; } } List<pos> positions = map.AllPositions().Where(x=>x.row < H-rh && x.col < W-rw).Randomize(); //'allow room placement under features' means, if the room has a floor and the map has a feature in the same location, that won't disqualify placement - instead, if the room passes, the feature will appear instead of the floor. Dungeon connected = new Dungeon(rh,rw); for(int num=0;num<positions.Count;++num){ pos p = positions[num]; bool bad_connection = force_connection; for(int i=0;i<rh;++i){ for(int j=0;j<rw;++j){ if(room[i,j].IsPassable()){ foreach(pos neighbor in new pos(i,j).PositionsWithinDistance(1)){ pos neighbor_offset = new pos(p.row + neighbor.row,p.col + neighbor.col); if(!force_cardinal_direction_connection){ //if this bool is set, check later. if(map.BoundsCheck(neighbor_offset) && map[neighbor_offset].IsPassable()){ bad_connection = false; } } if(connected.BoundsCheck(neighbor)){ connected[neighbor] = map[neighbor_offset]; } } if(force_cardinal_direction_connection){ foreach(pos neighbor in new pos(i,j).CardinalAdjacentPositions()){ if(connected.BoundsCheck(neighbor) && map[p.row+neighbor.row,p.col+neighbor.col].IsPassable()){ bad_connection = false; } } } } } } if(!bad_connection && connected.IsFullyConnected()){ bool valid = true; for(int i=0;i<rh && valid;++i){ for(int j=0;j<rw && valid;++j){ if(room[i,j] != CellType.Wall){ bool this_position_valid = false; if(map[p.row+i,p.col+j].IsWall()){ this_position_valid = true; } if(map[p.row+i,p.col+j].Is(allowed_to_overwrite)){ this_position_valid = true; } if(map[p.row+i,p.col+j] == room[i,j]){ this_position_valid = true; } if(map[p.row+i,p.col+j].IsFloor() && room[i,j].IsFloor()){ this_position_valid = true; } if(room[i,j].IsFloor() && allow_room_placement_under_features){ this_position_valid = true; } if(!this_position_valid){ valid = false; } } } } if(valid){ for(int i=0;i<rh;++i){ for(int j=0;j<rw;++j){ if(room[i,j] != CellType.Wall){ if(map[p.row+i,p.col+j].IsWall() || map[p.row+i,p.col+j].Is(allowed_to_overwrite)){ map[p.row+i,p.col+j] = room[i,j]; } } } } return true; } } } return false; }
public bool PlaceShape(Dungeon room_to_be_placed) { return PlaceShape(room_to_be_placed,true,true,false); }
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 GenerateFinalLevel() { final_level_cultist_count = new int[5]; final_level_demon_count = 0; final_level_clock = 0; current_level = 21; InitializeNewLevel(); string[] final_map = FinalLevelLayout(); PosArray<CellType> map = new PosArray<CellType>(ROWS,COLS); PosArray<bool> doors = new PosArray<bool>(ROWS,COLS); List<List<pos>> door_sets = new List<List<pos>>(); for(int i=0;i<ROWS;++i){ string s = final_map[i]; for(int j=0;j<COLS;++j){ switch(s[j]){ case '#': map[i,j] = CellType.Wall; break; case '.': map[i,j] = CellType.RoomInterior; break; case '2': map[i,j] = CellType.RoomFeature1; break; case '&': map[i,j] = CellType.RoomFeature2; break; case '*': map[i,j] = CellType.RoomFeature3; break; case 'X': map[i,j] = CellType.RoomFeature4; break; case '+': map[i,j] = CellType.Wall; if(!doors[i,j]){ doors[i,j] = true; pos p = new pos(i,j); List<pos> door_set = new List<pos>{p}; foreach(int dir in new int[]{2,6}){ p = new pos(i,j); while(true){ p = p.PosInDir(dir); if(p.BoundsCheck(tile) && final_map[p.row][p.col] == '+'){ doors[p] = true; door_set.Add(p); } else{ break; } } } door_sets.Add(door_set); } break; } } } Dungeon d = new Dungeon(ROWS,COLS); d.map = map; while(!d.IsFullyConnected() && door_sets.Count > 0){ List<pos> door_set = door_sets.RemoveRandom(); d.map[door_set.Random()] = CellType.RoomInterior; } List<Tile> flames = new List<Tile>(); for(int i=0;i<ROWS;++i){ for(int j=0;j<COLS;++j){ switch(map[i,j]){ case CellType.Wall: Tile.Create(TileType.WALL,i,j); break; case CellType.RoomFeature1: Tile.Create(TileType.DEMONIC_IDOL,i,j); break; case CellType.RoomFeature2: Tile.Create(TileType.FLOOR,i,j); flames.Add(tile[i,j]); break; case CellType.RoomFeature3: Tile.Create(TileType.FLOOR,i,j); tile[i,j].color = Color.RandomDoom; break; case CellType.RoomFeature4: Tile.Create(TileType.FIRE_RIFT,i,j); break; default: Tile.Create(TileType.FLOOR,i,j); break; } tile[i,j].solid_rock = true; } } //todo! add dungeon descriptions for final level. //todo: ^^^ or else it crashes ^^^ player.ResetForNewLevel(); foreach(Tile t in AllTiles()){ if(t.light_radius > 0){ t.UpdateRadius(0,t.light_radius); } } foreach(Tile t in flames){ t.AddFeature(FeatureType.FIRE); } int light = player.light_radius; int fire = player.attrs[AttrType.BURNING]; player.light_radius = 0; player.attrs[AttrType.BURNING] = 0; player.Move(6,7); player.UpdateRadius(0,Math.Max(light,fire),true); player.light_radius = light; player.attrs[AttrType.BURNING] = fire; foreach(Tile t in AllTiles()){ if(t.type != TileType.WALL){ foreach(Tile neighbor in t.TilesAtDistance(1)){ neighbor.solid_rock = false; } } } for(int i=0;i<3;++i){ Actor a = SpawnMob(ActorType.CULTIST); List<Actor> group = new List<Actor>(a.group); a.group.Clear(); if(a != null && group != null){ int ii = 0; foreach(Actor a2 in group){ ++ii; pos circle = FinalLevelSummoningCircle(ii); a2.FindPath(circle.row,circle.col); a2.attrs[AttrType.COOLDOWN_2] = ii; a2.type = ActorType.FINAL_LEVEL_CULTIST; a2.group = null; if(!R.OneIn(20)){ a2.attrs[AttrType.NO_ITEM] = 1; } } } } Q.Add(new Event(500,EventType.FINAL_LEVEL_SPAWN_CULTISTS)); }
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; }