/// <summary> /// Generates usually one random Monster chosen appropriately for the given depth. May /// generate more than one if the Monster chosen has escorts or appears in groups. Adds /// them to the current dungeon. /// </summary> /// <param name="level">Dungeon level to generate at.</param> /// <returns>The new Monsters that were added to the Dungeon.</returns> public static IList <Monster> AddRandom(Dungeon dungeon, int level, Vec startPos) { Race race = Race.Random(dungeon, level, true); List <Monster> monsters = new List <Monster>(); // create the monster(s) Monster monster = new Monster(startPos, race); monsters.Add(monster); dungeon.Entities.Add(monster); // generate friends if (race.NumberInGroup > 1) { int numMonsters = Rng.TriangleInt(race.NumberInGroup, race.NumberInGroup / 3); int tries = 0; while ((monsters.Count < numMonsters) && (tries < 100)) { tries++; // pick a random spot next to one of the monsters in the group Vec pos; if (dungeon.TryFindOpenAdjacent(Rng.Item(monsters).Position, out pos)) { // found one, so put another there Monster buddy = new Monster(pos, race); monsters.Add(buddy); dungeon.Entities.Add(buddy); } } } return(monsters); }
/// <summary> /// Implementation of the "growing tree" algorithm from here: /// http://www.astrolog.org/labyrnth/algrithm.htm. /// </summary> /// <remarks> /// This is a general algorithm, capable of creating Mazes of different textures. It requires /// storage up to the size of the Maze. Each time you carve a cell, add that cell to a list. /// Proceed by picking a cell from the list, and carving into an unmade cell next to it. If /// there are no unmade cells next to the current cell, remove the current cell from the list. /// The Maze is done when the list becomes empty. The interesting part that allows many possible /// textures is how you pick a cell from the list. For example, if you always pick the most /// recent cell added to it, this algorithm turns into the recursive backtracker. If you always /// pick cells at random, this will behave similarly but not exactly to Prim's algorithm. If you /// always pick the oldest cells added to the list, this will create Mazes with about as low a /// "river" factor as possible, even lower than Prim's algorithm. If you usually pick the most /// recent cell, but occasionally pick a random cell, the Maze will have a high "river" factor /// but a short direct solution. If you randomly pick among the most recent cells, the Maze will /// have a low "river" factor but a long windy solution. /// </remarks> public void GrowTree() { List <Vec> cells = new List <Vec>(); // start with a random cell Vec pos = Rng.Vec(Bounds); Open(pos); cells.Add(pos); while (cells.Count > 0) { // weighting how the index is chosen here will affect the way the // maze looks. see the function description int index = Math.Abs(Rng.TriangleInt(0, cells.Count - 1)); Vec cell = cells[index]; // see which adjacent cells are open List <Direction> unmadeCells = new List <Direction>(); if (CanCarve(cell, Direction.N)) { unmadeCells.Add(Direction.N); } if (CanCarve(cell, Direction.S)) { unmadeCells.Add(Direction.S); } if (CanCarve(cell, Direction.E)) { unmadeCells.Add(Direction.E); } if (CanCarve(cell, Direction.W)) { unmadeCells.Add(Direction.W); } if (unmadeCells.Count > 0) { Direction direction = Rng.Item(unmadeCells); Carve(cell, direction); cells.Add(cell + direction); } else { // no adjacent uncarved cells cells.RemoveAt(index); } } }
private Rect CreateRectRoom(Connector connector, int width, int height) { int x = 0; int y = 0; // position the room if (connector == null) { // initial room, so start near center x = Rng.TriangleInt((mWriter.Bounds.Width - width) / 2, (mWriter.Bounds.Width - width) / 2 - 4); y = Rng.TriangleInt((mWriter.Bounds.Height - height) / 2, (mWriter.Bounds.Height - height) / 2 - 4); } else if (connector.Direction == Direction.N) { // above the connector x = Rng.Int(connector.Position.X - width + 1, connector.Position.X + 1); y = connector.Position.Y - height; } else if (connector.Direction == Direction.E) { // to the right of the connector x = connector.Position.X + 1; y = Rng.Int(connector.Position.Y - height + 1, connector.Position.Y + 1); } else if (connector.Direction == Direction.S) { // below the connector x = Rng.Int(connector.Position.X - width + 1, connector.Position.X + 1); y = connector.Position.Y + 1; } else if (connector.Direction == Direction.W) { // to the left of the connector x = connector.Position.X - width; y = Rng.Int(connector.Position.Y - height + 1, connector.Position.Y + 1); } Rect bounds = new Rect(x, y, width, height); // check to see if the room can be positioned if (!mWriter.IsOpen(bounds.Inflate(1), (connector != null) ? (Vec?)connector.Position : (Vec?)null)) { return(Rect.Empty); } return(bounds); }
/// <summary> /// Randomly walks the given level using a... unique distribution. The /// goal is to return a value that approximates a bell curve centered /// on the start level whose wideness increases as the level increases. /// Thus, starting at a low start level will only walk a short distance, /// while starting at a higher level can wander a lot farther. /// </summary> /// <returns></returns> public static int WalkLevel(int level) { int result = level; // stack a few triangles to approximate a bell for (int i = 0; i < Math.Min(5, level); i++) { // the width of the triangle is based on the level result += Rng.TriangleInt(0, 1 + (level / 20)); } // also have an exponentially descreasing change of going out of depth while (Rng.OneIn(10)) { result += 1 + Rng.Int(2 + (level / 5)); } return(result); }
public static Roller Triangle(int center, int range) { return(new Roller( () => Rng.TriangleInt(center, range), center, center.ToString() + "t" + range.ToString())); }