public static void AnimateStorm(pos origin,int radius,int num_frames,int num_per_frame,colorchar ch){ for(int i=0;i<num_frames;++i){ List<pos> cells = new List<pos>(); List<pos> nearby = origin.PositionsWithinDistance(radius); for(int j=0;j<num_per_frame;++j){ cells.Add(nearby.RemoveRandom()); } Screen.AnimateMapCells(cells,ch); } }
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 ForEachRoom(RoomDelegate action) { List<pos> completed = new List<pos>(); for(int i=1;i<H-1;++i){ for(int j=1;j<W-1;++j){ pos p = new pos(i,j); bool good = true; foreach(pos neighbor in p.PositionsWithinDistance(1,map)){ //assumes at least 3x3 rooms. this might need to change in the future. if(!map[neighbor].IsRoomType()){ good = false; break; } } if(good && !completed.Contains(p)){ List<pos> room = map.GetFloodFillPositions(p,false,x=>map[x].IsRoomType()); if(!action(room)){ //return false; } foreach(pos p2 in room){ completed.Add(p2); } } } } return true; }
public static void AnimateStorm(pos origin,int radius,int num_frames,int num_per_frame,colorchar ch) { for(int i=0;i<num_frames;++i){ List<pos> cells = new List<pos>(); List<pos> nearby = origin.PositionsWithinDistance(radius); for(int j=0;j<num_per_frame;++j){ pos p = nearby.RemoveRandom(); if(BoundsCheck(p.row,p.col)){ //the ones that are out of bounds still count toward the total, so they don't become more dense as you get near edges. cells.Add(p); } } Screen.AnimateMapCells(cells,ch); } }
private void UpdateDensity(pos position) { foreach(pos p in position.PositionsWithinDistance(8,monster_density)){ int dist = p.DistanceFrom(position); if(dist <= 1){ monster_density[p] += 3; } else{ if(dist <= 4){ monster_density[p] += 2; } else{ monster_density[p]++; } } } }
public void GenerateLevel() { if(current_level < 20){ ++current_level; } InitializeNewLevel(); PosArray<CellType> map = GenerateMap(level_types[current_level-1]); List<pos> interesting_tiles = new List<pos>(); for(int i=0;i<ROWS;++i){ for(int j=0;j<COLS;++j){ if(map[i,j] == CellType.InterestingLocation){ interesting_tiles.Add(new pos(i,j)); } } } int attempts = 0; List<CellType> shrines = null; if(current_level % 2 == 1){ shrines = new List<CellType>{CellType.SpecialFeature1,CellType.SpecialFeature2,CellType.SpecialFeature3,CellType.SpecialFeature4,CellType.SpecialFeature5}; shrines.Randomize(); // after this initialization step, shrines are only removed from the front of the list, to enable prediction of pairs. nextLevelShrines = new List<CellType>(); int n = R.Between(0,5); for(int i=0;i<n;++i){ nextLevelShrines.Add(shrines.RemoveLast()); } } else{ shrines = nextLevelShrines; nextLevelShrines = null; } while(shrines.Count > 0){ attempts = 0; for(bool done=false;!done;++attempts){ int rr = R.Roll(ROWS-4) + 1; int rc = R.Roll(COLS-4) + 1; if(interesting_tiles.Count > 0){ pos p = interesting_tiles.RemoveRandom(); rr = p.row; rc = p.col; map[p] = CellType.RoomInterior; } pos temp = new pos(rr,rc); if(shrines.Count > 1){ if(map[rr,rc].IsFloor()){ if(attempts > 1000){ bool good = true; foreach(pos p in temp.PositionsWithinDistance(4,map)){ CellType ch = map[p]; if(ch.Is(CellType.SpecialFeature1,CellType.SpecialFeature2,CellType.SpecialFeature3,CellType.SpecialFeature4,CellType.SpecialFeature5)){ good = false; } } if(good){ List<pos> dist2 = new List<pos>(); foreach(pos p2 in temp.PositionsAtDistance(2,map)){ if(map[p2].IsFloor()){ dist2.Add(p2); } } if(dist2.Count > 0){ map[rr,rc] = shrines.RemoveFirst(); pos p2 = dist2.Random(); map[p2.row,p2.col] = shrines.RemoveFirst(); done = true; break; } } else{ interesting_tiles.Remove(temp); } } bool floors = true; foreach(pos p in temp.PositionsAtDistance(1,map)){ if(!map[p.row,p.col].IsFloor()){ floors = false; } } foreach(pos p in temp.PositionsWithinDistance(3,map)){ CellType ch = map[p]; if(ch.Is(CellType.SpecialFeature1,CellType.SpecialFeature2,CellType.SpecialFeature3,CellType.SpecialFeature4,CellType.SpecialFeature5)){ floors = false; } } if(floors){ if(R.CoinFlip()){ map[rr-1,rc] = shrines.RemoveFirst(); map[rr+1,rc] = shrines.RemoveFirst(); } else{ map[rr,rc-1] = shrines.RemoveFirst(); map[rr,rc+1] = shrines.RemoveFirst(); } CellType center = CellType.Wall; while(center == CellType.Wall){ switch(R.Roll(5)){ case 1: if(level_types[current_level-1] != LevelType.Hive && level_types[current_level-1] != LevelType.Garden){ center = CellType.Pillar; } break; case 2: center = CellType.Statue; break; case 3: if(level_types[current_level-1] != LevelType.Garden){ center = CellType.FirePit; } break; case 4: center = CellType.ShallowWater; break; case 5: if(level_types[current_level-1] != LevelType.Hive){ center = CellType.Torch; } break; } } map[rr,rc] = center; interesting_tiles.Remove(temp); done = true; break; } } } else{ if(map[rr,rc].IsFloor()){ bool good = true; foreach(pos p in temp.PositionsWithinDistance(2,map)){ CellType ch = map[p]; if(ch.Is(CellType.SpecialFeature1,CellType.SpecialFeature2,CellType.SpecialFeature3,CellType.SpecialFeature4,CellType.SpecialFeature5)){ good = false; } } if(good){ if(attempts > 1000){ map[rr,rc] = shrines.RemoveFirst(); interesting_tiles.Remove(temp); done = true; break; } else{ bool floors = true; foreach(pos p in temp.PositionsAtDistance(1,map)){ if(!map[p.row,p.col].IsFloor()){ floors = false; } } if(floors){ map[rr,rc] = shrines.RemoveFirst(); interesting_tiles.Remove(temp); done = true; break; } } } } if(map[rr,rc].IsWall()){ if(!map[rr+1,rc].IsFloor() && !map[rr-1,rc].IsFloor() && !map[rr,rc-1].IsFloor() && !map[rr,rc+1].IsFloor()){ continue; //no floors? retry. } bool no_good = false; foreach(pos p in temp.PositionsWithinDistance(2,map)){ CellType ch = map[p]; if(ch.Is(CellType.SpecialFeature1,CellType.SpecialFeature2,CellType.SpecialFeature3,CellType.SpecialFeature4,CellType.SpecialFeature5)){ no_good = true; } } if(no_good){ continue; } int walls = 0; foreach(pos p in temp.PositionsAtDistance(1,map)){ if(map[p].IsWall()){ ++walls; } } if(walls >= 5){ int successive_walls = 0; CellType[] rotated = new CellType[8]; for(int i=0;i<8;++i){ pos temp2; temp2 = temp.PosInDir(8.RotateDir(true,i)); rotated[i] = map[temp2.row,temp2.col]; } for(int i=0;i<15;++i){ if(rotated[i%8].IsWall()){ ++successive_walls; } else{ successive_walls = 0; } if(successive_walls == 5){ done = true; map[rr,rc] = shrines.RemoveFirst(); interesting_tiles.Remove(temp); break; } } } } } } } int num_chests = R.Between(0,1); if(level_types[current_level-1] == LevelType.Crypt){ num_chests -= map.PositionsWhere(x=>map[x] == CellType.Chest).Count; } for(int i=0;i<num_chests;++i){ int tries = 0; for(bool done=false;!done;++tries){ int rr = R.Roll(ROWS-4) + 1; int rc = R.Roll(COLS-4) + 1; if(interesting_tiles.Count > 0){ pos p = interesting_tiles.RemoveRandom(); rr = p.row; rc = p.col; map[rr,rc] = CellType.RoomInterior; } if(map[rr,rc].IsFloor()){ bool floors = true; pos temp = new pos(rr,rc); foreach(pos p in temp.PositionsAtDistance(1,map)){ if(!map[p.row,p.col].IsFloor()){ floors = false; } } if(floors || tries > 1000){ //after 1000 tries, place it anywhere map[rr,rc] = CellType.Chest; done = true; } } } } attempts = 0; for(bool done=false;!done;++attempts){ int rr = R.Roll(ROWS-4) + 1; int rc = R.Roll(COLS-4) + 1; if(interesting_tiles.Count > 0){ pos p = interesting_tiles.RemoveRandom(); rr = p.row; rc = p.col; map[p] = CellType.RoomInterior; } if(map[rr,rc].IsFloor()){ bool floors = true; pos temp = new pos(rr,rc); foreach(pos p in temp.PositionsAtDistance(1,map)){ if(!map[p.row,p.col].IsFloor()){ floors = false; } } if(floors || attempts > 1000){ map[rr,rc] = CellType.Stairs; done = true; } } } if(level_types[current_level-1] != LevelType.Garden){ GenerateFloorTypes(map); GenerateFeatures(map,interesting_tiles); } int num_traps = R.Roll(2,3); for(int i=0;i<num_traps;++i){ int tries = 0; for(bool done=false;!done && tries < 100;++tries){ int rr = R.Roll(ROWS-2); int rc = R.Roll(COLS-2); if(map[rr,rc].IsFloor() && map[rr,rc] != CellType.ShallowWater){ map[rr,rc] = CellType.Trap; done = true; } } } List<Tile> hidden = new List<Tile>(); Event grave_dirt_event = null; Event poppy_event = null; Tile stairs = null; for(int i=0;i<ROWS;++i){ for(int j=0;j<COLS;++j){ //Screen.WriteMapChar(i,j,map[i,j]); switch(map[i,j]){ case CellType.Wall: case CellType.Pillar: Tile.Create(TileType.WALL,i,j); break; case CellType.Door: if(R.OneIn(120)){ if(R.CoinFlip()){ Tile.Create(TileType.STONE_SLAB,i,j); Q.Add(new Event(tile[i,j],new List<Tile>{tile[i,j]},100,EventType.STONE_SLAB)); } else{ Tile.Create(TileType.HIDDEN_DOOR,i,j); hidden.Add(tile[i,j]); } } else{ Tile.Create(TileType.DOOR_C,i,j); } break; case CellType.Stairs: if(current_level < 20){ Tile.Create(TileType.STAIRS,i,j); stairs = tile[i,j]; } else{ if(current_level == 20){ Tile.Create(TileType.STAIRS,i,j); tile[i,j].color = Color.Red; tile[i,j].SetName("scorched stairway"); stairs = tile[i,j]; } else{ Tile.Create(TileType.FLOOR,i,j); } } break; case CellType.Statue: Tile.Create(TileType.STATUE,i,j); break; case CellType.Rubble: Tile.Create(TileType.RUBBLE,i,j); break; case CellType.FirePit: Tile.Create(TileType.FIREPIT,i,j); break; case CellType.Pool: Tile.Create(TileType.POOL_OF_RESTORATION,i,j); break; case CellType.BlastFungus: Tile.Create(TileType.BLAST_FUNGUS,i,j); break; case CellType.CrackedWall: Tile.Create(TileType.CRACKED_WALL,i,j); break; case CellType.Chest: Tile.Create(TileType.CHEST,i,j); break; case CellType.Trap: { TileType type = Tile.RandomTrap(); Tile.Create(type,i,j); if(tile[i,j].IsTrap()){ tile[i,j].name = "floor"; tile[i,j].the_name = "the floor"; tile[i,j].a_name = "a floor"; tile[i,j].symbol = '.'; tile[i,j].color = Color.White; hidden.Add(tile[i,j]); } break; } case CellType.Geyser: Tile.Create(TileType.FIRE_GEYSER,i,j); int frequency = R.Roll(31) + 8; //9-39 int variance = R.Roll(10) - 1; //0-9 int variance_amount = (frequency * variance) / 10; int number_of_values = variance_amount*2 + 1; int minimum_value = frequency - variance_amount; if(minimum_value < 5){ int diff = 5 - minimum_value; number_of_values -= diff; minimum_value = 5; } int delay = ((minimum_value - 1) + R.Roll(number_of_values)) * 100; Q.Add(new Event(tile[i,j],delay + 200,EventType.FIRE_GEYSER,(frequency*10)+variance)); //notice the hacky way the value is stored Q.Add(new Event(tile[i,j],delay,EventType.FIRE_GEYSER_ERUPTION,2)); break; case CellType.FogVent: Tile.Create(TileType.FOG_VENT,i,j); Q.Add(new Event(tile[i,j],100,EventType.FOG_VENT)); break; case CellType.PoisonVent: Tile.Create(TileType.POISON_GAS_VENT,i,j); Q.Add(new Event(tile[i,j],100,EventType.POISON_GAS_VENT)); break; case CellType.Tombstone: { Tile t = Tile.Create(TileType.TOMBSTONE,i,j); if(R.OneIn(8)){ Q.Add(new Event(null,new List<Tile>{t},100,EventType.TOMBSTONE_GHOST)); } break; } case CellType.HiddenDoor: Tile.Create(TileType.HIDDEN_DOOR,i,j); hidden.Add(tile[i,j]); break; case CellType.SpecialFeature1: Tile.Create(TileType.COMBAT_SHRINE,i,j); break; case CellType.SpecialFeature2: Tile.Create(TileType.DEFENSE_SHRINE,i,j); break; case CellType.SpecialFeature3: Tile.Create(TileType.MAGIC_SHRINE,i,j); break; case CellType.SpecialFeature4: Tile.Create(TileType.SPIRIT_SHRINE,i,j); break; case CellType.SpecialFeature5: Tile.Create(TileType.STEALTH_SHRINE,i,j); break; case CellType.DeepWater: case CellType.ShallowWater: Tile.Create(TileType.WATER,i,j); break; case CellType.Barrel: Tile.Create(TileType.BARREL,i,j); break; case CellType.Brush: Tile.Create(TileType.BRUSH,i,j); break; case CellType.GlowingFungus: Tile.Create(TileType.GLOWING_FUNGUS,i,j); break; case CellType.GraveDirt: Tile.Create(TileType.GRAVE_DIRT,i,j); if(grave_dirt_event == null){ grave_dirt_event = new Event(new List<Tile>{tile[i,j]},100,EventType.GRAVE_DIRT); Q.Add(grave_dirt_event); } else{ grave_dirt_event.area.Add(tile[i,j]); } break; case CellType.Gravel: Tile.Create(TileType.GRAVEL,i,j); break; case CellType.Ice: Tile.Create(TileType.ICE,i,j); break; case CellType.Oil: Tile.Create(TileType.FLOOR,i,j); tile[i,j].AddFeature(FeatureType.OIL); break; case CellType.PoisonBulb: Tile.Create(TileType.POISON_BULB,i,j); break; case CellType.Poppies: Tile.Create(TileType.POPPY_FIELD,i,j); if(poppy_event == null){ poppy_event = new Event(new List<Tile>{tile[i,j]},100,EventType.POPPIES); Q.Add(poppy_event); } else{ poppy_event.area.Add(tile[i,j]); } break; case CellType.Slime: Tile.Create(TileType.FLOOR,i,j); tile[i,j].AddFeature(FeatureType.SLIME); break; case CellType.Torch: Tile.Create(TileType.STANDING_TORCH,i,j); break; case CellType.Vine: Tile.Create(TileType.VINE,i,j); break; case CellType.Webs: Tile.Create(TileType.FLOOR,i,j); tile[i,j].AddFeature(FeatureType.WEB); break; default: Tile.Create(TileType.FLOOR,i,j); break; } //alltiles.Add(tile[i,j]); tile[i,j].solid_rock = true; } } //Input.ReadKey(); player.ResetForNewLevel(); foreach(Tile t in AllTiles()){ if(t.light_radius > 0){ t.UpdateRadius(0,t.light_radius); } } int num_items = R.Between(0,2); for(int i=num_items;i>0;--i){ SpawnItem(); } bool poltergeist_spawned = false; //i'm not sure this is the right call, but for now bool mimic_spawned = false; // i'm limiting these guys, to avoid "empty" levels bool marble_horror_spawned = false; int num_monsters = R.Roll(2,2) + 4; if(num_monsters == 6){ num_monsters += R.Roll(3); //this works out to 7/12 seven, 4/12 eight, and 1/12 nine. } for(int i=num_monsters;i>0;--i){ ActorType type = MobType(); if(type == ActorType.POLTERGEIST){ if(!poltergeist_spawned){ SpawnMob(type); poltergeist_spawned = true; } else{ ++i; //try again.. } } else{ if(type == ActorType.MIMIC){ if(!mimic_spawned){ SpawnMob(type); mimic_spawned = true; } else{ ++i; } } else{ if(type == ActorType.MARBLE_HORROR){ Tile statue = AllTiles().Where(t=>t.type == TileType.STATUE).RandomOrDefault(); if(!marble_horror_spawned && statue != null){ SpawnMob(type); marble_horror_spawned = true; } else{ ++i; } } else{ if(type == ActorType.ENTRANCER){ if(i >= 2){ //need 2 slots here Actor entrancer = SpawnMob(type); entrancer.attrs[AttrType.WANDERING]++; List<Tile> tiles = new List<Tile>(); int dist = 1; while(tiles.Count == 0 && dist < 100){ foreach(Tile t in entrancer.TilesAtDistance(dist)){ if(t.passable && !t.IsTrap() && t.actor() == null){ tiles.Add(t); } } ++dist; } if(tiles.Count > 0){ ActorType thralltype = ActorType.SPECIAL; bool done = false; while(!done){ thralltype = MobType(); switch(thralltype){ //not on the list: group/stealth/ranged/rare/immobile/hazardous monsters, plus carrion crawler, mechanical knight, mud elemental, and luminous avenger case ActorType.ROBED_ZEALOT: case ActorType.WILD_BOAR: case ActorType.TROLL: case ActorType.DERANGED_ASCETIC: case ActorType.ALASI_BATTLEMAGE: case ActorType.ALASI_SOLDIER: case ActorType.SKITTERMOSS: case ActorType.STONE_GOLEM: case ActorType.OGRE_BARBARIAN: case ActorType.SNEAK_THIEF: case ActorType.CRUSADING_KNIGHT: case ActorType.CORROSIVE_OOZE: case ActorType.ALASI_SENTINEL: case ActorType.CYCLOPEAN_TITAN: done = true; break; } } Tile t = tiles.Random(); Actor thrall = Actor.Create(thralltype,t.row,t.col,TiebreakerAssignment.InsertAfterCurrent); if(entrancer.group == null){ entrancer.group = new List<Actor>{entrancer}; } entrancer.group.Add(thrall); thrall.group = entrancer.group; --i; } } else{ ++i; } } else{ Actor a = SpawnMob(type); if(type == ActorType.WARG){ if(a.group != null){ foreach(Actor a2 in a.group){ a2.attrs[AttrType.WANDERING]++; } } } else{ if(a.AlwaysWanders() || (R.PercentChance(40) && a.CanWanderAtLevelGen())){ a.attrs[AttrType.WANDERING]++; } } } } } } } for(int i=(current_level-3)/4;i>0;--i){ //yes, this is all copied and pasted for a one-line change. i'll try to fix it later. if(R.CoinFlip()){ //generate some shallow monsters ActorType type = ShallowMobType(); if(type == ActorType.POLTERGEIST){ if(!poltergeist_spawned){ SpawnMob(type); poltergeist_spawned = true; } else{ ++i; //try again.. } } else{ if(type == ActorType.MIMIC){ if(!mimic_spawned){ SpawnMob(type); mimic_spawned = true; } else{ ++i; } } else{ if(type == ActorType.MARBLE_HORROR){ Tile statue = AllTiles().Where(t=>t.type == TileType.STATUE).RandomOrDefault(); if(!marble_horror_spawned && statue != null){ SpawnMob(type); marble_horror_spawned = true; } else{ ++i; } } else{ if(type == ActorType.ENTRANCER){ if(i >= 2){ //need 2 slots here Actor entrancer = SpawnMob(type); entrancer.attrs[AttrType.WANDERING]++; List<Tile> tiles = new List<Tile>(); int dist = 1; while(tiles.Count == 0 && dist < 100){ foreach(Tile t in entrancer.TilesAtDistance(dist)){ if(t.passable && !t.IsTrap() && t.actor() == null){ tiles.Add(t); } } ++dist; } if(tiles.Count > 0){ ActorType thralltype = ActorType.SPECIAL; bool done = false; while(!done){ thralltype = MobType(); switch(thralltype){ case ActorType.ROBED_ZEALOT: case ActorType.DERANGED_ASCETIC: case ActorType.BERSERKER: case ActorType.TROLL: case ActorType.CRUSADING_KNIGHT: case ActorType.SKITTERMOSS: case ActorType.OGRE_BARBARIAN: case ActorType.SNEAK_THIEF: case ActorType.STONE_GOLEM: case ActorType.LUMINOUS_AVENGER: case ActorType.WILD_BOAR: case ActorType.ALASI_SOLDIER: case ActorType.CORROSIVE_OOZE: case ActorType.ALASI_SENTINEL: done = true; break; } } Tile t = tiles.Random(); Actor thrall = Actor.Create(thralltype,t.row,t.col,TiebreakerAssignment.InsertAfterCurrent); if(entrancer.group == null){ entrancer.group = new List<Actor>{entrancer}; } entrancer.group.Add(thrall); thrall.group = entrancer.group; --i; } } else{ ++i; } } else{ Actor a = SpawnMob(type); if(type == ActorType.WARG){ if(a.group != null){ foreach(Actor a2 in a.group){ a2.attrs[AttrType.WANDERING]++; } } } else{ if(a.AlwaysWanders() || (R.PercentChance(40) && a.CanWanderAtLevelGen())){ a.attrs[AttrType.WANDERING]++; } } } } } } } } int minimum_distance_from_stairs = 0; PosArray<int> distance_from_stairs = null; if(stairs != null){ distance_from_stairs = tile.GetDijkstraMap(new List<pos>{stairs.p},x=>tile[x].BlocksConnectivityOfMap()); minimum_distance_from_stairs = distance_from_stairs[distance_from_stairs.PositionsWhere(x=>distance_from_stairs[x].IsValidDijkstraValue()).WhereGreatest(x=>distance_from_stairs[x]).Random()] / 2; } bool[,] good_location = new bool[ROWS,COLS]; for(int i=0;i<ROWS;++i){ for(int j=0;j<COLS;++j){ if(tile[i,j].Is(TileType.FLOOR) && !tile[i,j].Is(FeatureType.WEB) && (stairs == null || distance_from_stairs[i,j] >= minimum_distance_from_stairs)){ good_location[i,j] = true; } else{ good_location[i,j] = false; } } } foreach(Actor a in AllActors()){ if(a != player){ good_location[a.row,a.col] = false; for(int i=0;i<ROWS;++i){ for(int j=0;j<COLS;++j){ if(good_location[i,j] && a.HasLOS(i,j)){ good_location[i,j] = false; } } } } } bool at_least_one_good = false; for(int i=0;i<ROWS && !at_least_one_good;++i){ for(int j=0;j<COLS && !at_least_one_good;++j){ if(good_location[i,j]){ at_least_one_good = true; } } } if(!at_least_one_good){ foreach(Actor a in AllActors()){ if(a != player){ good_location[a.row,a.col] = false; for(int i=0;i<ROWS;++i){ for(int j=0;j<COLS;++j){ if(good_location[i,j] && a.CanSee(i,j)){ //checking CanSee this time good_location[i,j] = false; } } } } } } List<Tile> goodtiles = new List<Tile>(); for(int i=0;i<ROWS;++i){ for(int j=0;j<COLS;++j){ if(good_location[i,j]){ goodtiles.Add(tile[i,j]); } } } if(goodtiles.Count > 0){ Tile t = goodtiles.Random(); int light = player.light_radius; int burning = player.attrs[AttrType.BURNING]; int shining = player.attrs[AttrType.SHINING]; player.light_radius = 0; player.attrs[AttrType.BURNING] = 0; player.attrs[AttrType.SHINING] = 0; player.Move(t.row,t.col); player.light_radius = light; player.attrs[AttrType.BURNING] = burning; player.attrs[AttrType.SHINING] = shining; player.UpdateRadius(0,player.LightRadius()); } else{ for(bool done=false;!done;){ int rr = R.Roll(ROWS-2); int rc = R.Roll(COLS-2); bool good = true; foreach(Tile t in tile[rr,rc].TilesWithinDistance(1)){ if(t.IsTrap()){ good = false; } } if(good && tile[rr,rc].passable && actor[rr,rc] == null){ int light = player.light_radius; int burning = player.attrs[AttrType.BURNING]; int shining = player.attrs[AttrType.SHINING]; player.light_radius = 0; player.attrs[AttrType.BURNING] = 0; player.attrs[AttrType.SHINING] = 0; player.Move(rr,rc); player.light_radius = light; player.attrs[AttrType.BURNING] = burning; player.attrs[AttrType.SHINING] = shining; player.UpdateRadius(0,player.LightRadius()); done = true; } } } actor[player.row,player.col] = player; //this line fixes a bug that occurs when the player ends up in the same position on a new level Screen.screen_center_col = player.col; if(R.CoinFlip()){ //todo: copied and pasted below bool done = false; for(int tries=0;!done && tries<500;++tries){ int rr = R.Roll(ROWS-4) + 1; int rc = R.Roll(COLS-4) + 1; bool good = true; foreach(Tile t in tile[rr,rc].TilesWithinDistance(2)){ if(t.type != TileType.WALL){ good = false; break; } } if(good){ List<int> dirs = new List<int>(); bool long_corridor = false; int connections = 0; for(int i=2;i<=8;i+=2){ Tile t = tile[rr,rc].TileInDirection(i).TileInDirection(i); bool good_dir = true; int distance = -1; while(good_dir && t != null && t.type == TileType.WALL){ if(t.TileInDirection(i.RotateDir(false,2)).type != TileType.WALL){ good_dir = false; } if(t.TileInDirection(i.RotateDir(true,2)).type != TileType.WALL){ good_dir = false; } t = t.TileInDirection(i); if(t != null && t.type == TileType.STATUE){ good_dir = false; } ++distance; } if(good_dir && t != null){ dirs.Add(i); ++connections; if(distance >= 4){ long_corridor = true; } } } if(dirs.Count > 0){ List<TileType> all_possible_traps = new List<TileType>{TileType.GRENADE_TRAP,TileType.POISON_GAS_TRAP,TileType.PHANTOM_TRAP,TileType.FIRE_TRAP,TileType.SHOCK_TRAP,TileType.SCALDING_OIL_TRAP,TileType.FLING_TRAP,TileType.STONE_RAIN_TRAP}; List<TileType> possible_traps = new List<TileType>(); //int trap_roll = R.Roll(7); int num_types = R.Between(2,3); /*if(trap_roll == 1){ num_types = 1; } else{ if(trap_roll <= 4){ num_types = 2; } else{ num_types = 3; } }*/ for(int i=0;i<num_types;++i){ TileType tt = all_possible_traps.Random(); if(possible_traps.Contains(tt)){ --i; } else{ possible_traps.Add(tt); } } bool stone_slabs = false; //(instead of hidden doors) if(R.OneIn(4)){ stone_slabs = true; } foreach(int i in dirs){ Tile t = tile[rr,rc].TileInDirection(i); int distance = -2; //distance of the corridor between traps and secret door while(t.type == TileType.WALL){ ++distance; t = t.TileInDirection(i); } if(long_corridor && distance < 4){ continue; } t = tile[rr,rc].TileInDirection(i); while(t.type == TileType.WALL){ if(distance >= 4){ TileType tt = TileType.FLOOR; if(R.Roll(3) >= 2){ tt = possible_traps.Random(); hidden.Add(t); } t.TransformTo(tt); t.name = "floor"; t.the_name = "the floor"; t.a_name = "a floor"; t.symbol = '.'; t.color = Color.White; if(t.DistanceFrom(tile[rr,rc]) < distance+2){ Tile neighbor = t.TileInDirection(i.RotateDir(false,2)); if(neighbor.TileInDirection(i.RotateDir(false,1)).type == TileType.WALL && neighbor.TileInDirection(i.RotateDir(false,2)).type == TileType.WALL && neighbor.TileInDirection(i.RotateDir(false,3)).type == TileType.WALL){ tt = TileType.FLOOR; if(R.Roll(3) >= 2){ tt = possible_traps.Random(); } neighbor.TransformTo(tt); if(possible_traps.Contains(tt)){ neighbor.name = "floor"; neighbor.the_name = "the floor"; neighbor.a_name = "a floor"; neighbor.symbol = '.'; neighbor.color = Color.White; hidden.Add(neighbor); } } neighbor = t.TileInDirection(i.RotateDir(true,2)); if(neighbor.TileInDirection(i.RotateDir(true,1)).type == TileType.WALL && neighbor.TileInDirection(i.RotateDir(true,2)).type == TileType.WALL && neighbor.TileInDirection(i.RotateDir(true,3)).type == TileType.WALL){ tt = TileType.FLOOR; if(R.Roll(3) >= 2){ tt = possible_traps.Random(); } neighbor.TransformTo(tt); if(possible_traps.Contains(tt)){ neighbor.name = "floor"; neighbor.the_name = "the floor"; neighbor.a_name = "a floor"; neighbor.symbol = '.'; neighbor.color = Color.White; hidden.Add(neighbor); } } } } else{ TileType tt = TileType.FLOOR; if(R.CoinFlip()){ tt = Tile.RandomTrap(); hidden.Add(t); } t.TransformTo(tt); if(tt != TileType.FLOOR){ t.name = "floor"; t.the_name = "the floor"; t.a_name = "a floor"; t.symbol = '.'; t.color = Color.White; } } t = t.TileInDirection(i); } t = t.TileInDirection(i.RotateDir(true,4)); if(stone_slabs){ t.TransformTo(TileType.STONE_SLAB); Q.Add(new Event(t,new List<Tile>{t.TileInDirection(i.RotateDir(true,4))},100,EventType.STONE_SLAB)); } else{ t.TransformTo(TileType.HIDDEN_DOOR); hidden.AddUnique(t); } t = t.TileInDirection(i.RotateDir(true,4)); if(R.CoinFlip()){ if(t.IsTrap()){ t.type = TileType.ALARM_TRAP; } else{ t.TransformTo(TileType.ALARM_TRAP); t.name = "floor"; t.the_name = "the floor"; t.a_name = "a floor"; t.symbol = '.'; t.color = Color.White; hidden.AddUnique(t); } } } if(long_corridor && connections == 1){ foreach(Tile t in tile[rr,rc].TilesWithinDistance(1)){ t.TransformTo(possible_traps.Random()); t.name = "floor"; t.the_name = "the floor"; t.a_name = "a floor"; t.symbol = '.'; t.color = Color.White; hidden.Add(t); } tile[rr,rc].TileInDirection(dirs[0].RotateDir(true,4)).TransformTo(TileType.CHEST); tile[rr,rc].TileInDirection(dirs[0].RotateDir(true,4)).color = Color.Yellow; } else{ foreach(Tile t in tile[rr,rc].TilesAtDistance(1)){ t.TransformTo(Tile.RandomTrap()); t.name = "floor"; t.the_name = "the floor"; t.a_name = "a floor"; t.symbol = '.'; t.color = Color.White; hidden.Add(t); } tile[rr,rc].TransformTo(TileType.CHEST); tile[rr,rc].color = Color.Yellow; } done = true; } } } } /*if(R.CoinFlip()){ bool done = false; for(int tries=0;!done && tries<500;++tries){ int rr = R.Roll(ROWS-4) + 1; int rc = R.Roll(COLS-4) + 1; bool good = true; foreach(Tile t in tile[rr,rc].TilesWithinDistance(2)){ if(t.type != TileType.WALL){ good = false; break; } } if(good){ List<int> dirs = new List<int>(); bool long_corridor = false; int connections = 0; for(int i=2;i<=8;i+=2){ Tile t = tile[rr,rc].TileInDirection(i).TileInDirection(i); bool good_dir = true; int distance = -1; while(good_dir && t != null && t.type == TileType.WALL){ if(t.TileInDirection(i.RotateDir(false,2)).type != TileType.WALL){ good_dir = false; } if(t.TileInDirection(i.RotateDir(true,2)).type != TileType.WALL){ good_dir = false; } t = t.TileInDirection(i); if(t != null && t.type == TileType.STATUE){ good_dir = false; } ++distance; } if(good_dir && t != null){ dirs.Add(i); ++connections; if(distance >= 4){ long_corridor = true; } } } if(dirs.Count > 0){ List<TileType> possible_traps = new List<TileType>(); int trap_roll = R.Roll(7); if(trap_roll == 1 || trap_roll == 4 || trap_roll == 5 || trap_roll == 7){ possible_traps.Add(TileType.GRENADE_TRAP); } if(trap_roll == 2 || trap_roll == 4 || trap_roll == 6 || trap_roll == 7){ possible_traps.Add(TileType.POISON_GAS_TRAP); } if(trap_roll == 3 || trap_roll == 5 || trap_roll == 6 || trap_roll == 7){ possible_traps.Add(TileType.PHANTOM_TRAP); } bool stone_slabs = false; //(instead of hidden doors) if(R.OneIn(4)){ stone_slabs = true; } foreach(int i in dirs){ Tile t = tile[rr,rc].TileInDirection(i); int distance = -2; //distance of the corridor between traps and secret door while(t.type == TileType.WALL){ ++distance; t = t.TileInDirection(i); } if(long_corridor && distance < 4){ continue; } t = tile[rr,rc].TileInDirection(i); while(t.type == TileType.WALL){ if(distance >= 4){ TileType tt = TileType.FLOOR; if(R.Roll(3) >= 2){ tt = possible_traps.Random(); hidden.Add(t); } t.TransformTo(tt); t.name = "floor"; t.the_name = "the floor"; t.a_name = "a floor"; t.symbol = '.'; t.color = Color.White; if(t.DistanceFrom(tile[rr,rc]) < distance+2){ Tile neighbor = t.TileInDirection(i.RotateDir(false,2)); if(neighbor.TileInDirection(i.RotateDir(false,1)).type == TileType.WALL && neighbor.TileInDirection(i.RotateDir(false,2)).type == TileType.WALL && neighbor.TileInDirection(i.RotateDir(false,3)).type == TileType.WALL){ tt = TileType.FLOOR; if(R.Roll(3) >= 2){ tt = possible_traps.Random(); } neighbor.TransformTo(tt); if(possible_traps.Contains(tt)){ neighbor.name = "floor"; neighbor.the_name = "the floor"; neighbor.a_name = "a floor"; neighbor.symbol = '.'; neighbor.color = Color.White; hidden.Add(neighbor); } } neighbor = t.TileInDirection(i.RotateDir(true,2)); if(neighbor.TileInDirection(i.RotateDir(true,1)).type == TileType.WALL && neighbor.TileInDirection(i.RotateDir(true,2)).type == TileType.WALL && neighbor.TileInDirection(i.RotateDir(true,3)).type == TileType.WALL){ tt = TileType.FLOOR; if(R.Roll(3) >= 2){ tt = possible_traps.Random(); } neighbor.TransformTo(tt); if(possible_traps.Contains(tt)){ neighbor.name = "floor"; neighbor.the_name = "the floor"; neighbor.a_name = "a floor"; neighbor.symbol = '.'; neighbor.color = Color.White; hidden.Add(neighbor); } } } } else{ TileType tt = TileType.FLOOR; if(R.CoinFlip()){ tt = Tile.RandomTrap(); hidden.Add(t); } t.TransformTo(tt); if(tt != TileType.FLOOR){ t.name = "floor"; t.the_name = "the floor"; t.a_name = "a floor"; t.symbol = '.'; t.color = Color.White; } } t = t.TileInDirection(i); } t = t.TileInDirection(i.RotateDir(true,4)); if(stone_slabs){ t.TransformTo(TileType.STONE_SLAB); Q.Add(new Event(t,new List<Tile>{t.TileInDirection(i.RotateDir(true,4))},100,EventType.STONE_SLAB)); } else{ t.TransformTo(TileType.HIDDEN_DOOR); hidden.AddUnique(t); } t = t.TileInDirection(i.RotateDir(true,4)); if(R.CoinFlip()){ if(t.IsTrap()){ t.type = TileType.ALARM_TRAP; } else{ t.TransformTo(TileType.ALARM_TRAP); t.name = "floor"; t.the_name = "the floor"; t.a_name = "a floor"; t.symbol = '.'; t.color = Color.White; hidden.AddUnique(t); } } } if(long_corridor && connections == 1){ foreach(Tile t in tile[rr,rc].TilesWithinDistance(1)){ t.TransformTo(possible_traps.Random()); t.name = "floor"; t.the_name = "the floor"; t.a_name = "a floor"; t.symbol = '.'; t.color = Color.White; hidden.Add(t); } tile[rr,rc].TileInDirection(dirs[0].RotateDir(true,4)).TransformTo(TileType.CHEST); tile[rr,rc].TileInDirection(dirs[0].RotateDir(true,4)).color = Color.Yellow; } else{ foreach(Tile t in tile[rr,rc].TilesAtDistance(1)){ t.TransformTo(Tile.RandomTrap()); t.name = "floor"; t.the_name = "the floor"; t.a_name = "a floor"; t.symbol = '.'; t.color = Color.White; hidden.Add(t); } tile[rr,rc].TransformTo(TileType.CHEST); tile[rr,rc].color = Color.Yellow; } done = true; } } } }*/ foreach(Tile t in AllTiles()){ if(t.type != TileType.WALL){ foreach(Tile neighbor in t.TilesAtDistance(1)){ neighbor.solid_rock = false; } } if(t.type == TileType.GLOWING_FUNGUS){ foreach(Tile neighbor in t.TilesAtDistance(1)){ if(neighbor.type == TileType.WALL){ neighbor.color = Color.RandomGlowingFungus; } } } } if(level_types[current_level-1] == LevelType.Hive){ var dijkstra = tile.GetDijkstraMap(x=>tile[x].type != TileType.WALL,x=>false); for(int i=0;i<ROWS;++i){ for(int j=0;j<COLS;++j){ if((dijkstra[i,j] == 1 && !R.OneIn(20)) || (dijkstra[i,j] == 2 && R.CoinFlip())){ if(i == 0 || j == 0 || i == ROWS-1 || j == COLS-1){ tile[i,j].color = Color.DarkYellow; tile[i,j].SetName("waxy wall"); //borders become waxy but don't burn } else{ tile[i,j].Toggle(null,TileType.WAX_WALL); } } } } } if(level_types[current_level-1] == LevelType.Fortress){ foreach(pos p in tile.PositionsWhere(x=>tile[x].Is(TileType.WALL,TileType.HIDDEN_DOOR))){ tile[p].color = Color.TerrainDarkGray; } } string desc = GetDungeonDescription(); for(int i=0;i<ROWS;++i){ for(int j=0;j<COLS;++j){ //todo fix this! if(!tile.BoundsCheck(i,j,false)) continue; pos p = new pos(i,j); if(SchismExtensionMethods.Extensions.ConsecutiveAdjacent(p,x=>!tile[x].BlocksConnectivityOfMap()) >= 3){ //todo, fix check! dungeon_description[i,j] = desc; } else{ dungeon_description[i,j] = "You pass through a hallway of hewn stone."; //todo, fix message! } } } if(poppy_event != null){ CalculatePoppyDistanceMap(); } if(hidden.Count > 0){ Event e = new Event(hidden,100,EventType.CHECK_FOR_HIDDEN); e.tiebreaker = 0; Q.Add(e); } { Event e = new Event(10000,EventType.RELATIVELY_SAFE); e.tiebreaker = 0; Q.Add(e); } { Event e = new Event(R.Between(400,450)*100,EventType.SPAWN_WANDERING_MONSTER); e.tiebreaker = 0; Q.Add(e); } if(current_level == 1){ B.Add("In the mountain pass where travelers vanish, a stone staircase leads downward... Welcome, " + Actor.player_name + "! "); } else{ B.Add(LevelMessage()); } }