/// <summary> /// Repeatedly adds an increment to a given starting value as long as random /// numbers continue to be chosen from within a given range. Yields numbers /// whose probability gradually tapers off from the starting value. /// </summary> /// <param name="start">Starting value.</param> /// <param name="increment">Amount to modify starting value every successful /// iteration.</param> /// <param name="chance">The odds of an iteration being successful.</param> /// <param name="outOf">The range to choose from to see if the iteration /// is successful.</param> /// <returns>The resulting value.</returns> public static int Taper(int start, int increment, int chance, int outOf) { if (increment == 0) { throw new ArgumentOutOfRangeException("increment", "The increment cannot be zero."); } if (chance <= 0) { throw new ArgumentOutOfRangeException("chance", "The chance must be greater than zero."); } if (chance >= outOf) { throw new ArgumentOutOfRangeException("chance", "The chance must be less than the range."); } if (outOf <= 0) { throw new ArgumentOutOfRangeException("outOf", "The range must be positive."); } int value = start; while (Rng.Int(outOf) < chance) { value += increment; } return(value); }
private Rect CreateRoom(Connector connector) { int width = Rng.Int(6, 13); int height = Rng.Int(6, 13); Rect bounds = CreateRectRoom(connector, width, height); // bail if we failed if (bounds == Rect.Empty) { return(bounds); } // place the room foreach (Vec pos in bounds) { mWriter.SetTile(pos, TileType.Floor); } TileType decoration = ChooseInnerWall(); RoomDecoration.Decorate(bounds, new FeatureFactory.RoomDecorator(this, pos => mWriter.Populate(pos, 60, 200, mDepth + Rng.Int(mDepth / 10)))); mWriter.LightRect(bounds, mDepth); // place the connectors AddRoomConnectors(connector, bounds); Populate(bounds, 20, 20, mDepth); return(bounds); }
private void AddRoomEdgeConnectors(Connector connector, Rect edge, Direction dir) { bool skip = Rng.OneIn(2); foreach (Vec pos in edge) { // don't place connectors close to the incoming connector if (connector != null) { if (Vec.IsDistanceWithin(connector.Position, pos, 1)) { continue; } } if (!skip && (Rng.Int(100) < mWriter.Options.ChanceOfRoomConnector)) { mWriter.AddRoomConnector(pos, dir); skip = true; } else { skip = false; } } }
/// <summary> /// Creates an inner room with a single entrace inside the room. /// </summary> /// <example> /// ########## /// # # /// # ****** # /// # * * # /// # * * # /// # * # /// # * * # /// # ****** # /// # # /// ########## /// </example> public static void DecorateInnerRoom(Rect room, IRoomDecorator decorator) { if ((room.Width > 4) && (room.Height > 4)) { foreach (Vec pos in room.Inflate(-1).Trace()) { decorator.AddDecoration(pos); } // populate the inside foreach (Vec pos in room.Inflate(-2)) { decorator.AddInsideRoom(pos); } // add an opening Vec opening = Vec.Zero; switch (Rng.Int(4)) { case 0: opening = new Vec(Rng.Int(room.Left + 2, room.Right - 2), room.Y + 1); break; case 1: opening = new Vec(room.X + 1, Rng.Int(room.Top + 2, room.Bottom - 2)); break; case 2: opening = new Vec(Rng.Int(room.Left + 2, room.Right - 2), room.Bottom - 2); break; case 3: opening = new Vec(room.Right - 2, Rng.Int(room.Top + 2, room.Bottom - 2)); break; } decorator.AddDoor(opening); } }
public static void DecorateVerticalWall(Rect room, IRoomDecorator decorator) { int x = Rng.Int(room.Left + 1, room.Right - 1); foreach (Vec pos in Rect.Column(x, room.Top + 1, room.Height - 2)) { decorator.AddDecoration(pos); } }
public static void DecorateHorizontalWall(Rect room, IRoomDecorator decorator) { int y = Rng.Int(room.Top + 1, room.Bottom - 1); foreach (Vec pos in Rect.Row(room.Left + 1, y, room.Width - 2)) { decorator.AddDecoration(pos); } }
void IFeatureWriter.LightRect(Rect bounds, int depth) { // light the room if ((depth <= Rng.Int(1, 80))) { foreach (Vec pos in bounds.Inflate(1)) { mDungeon.SetTilePermanentLit(pos, true); } } }
protected override bool OnStandsFirm(Hit hit) { // can either dodge it or just tough it out int power = Stats.Strength + Stats.Agility; if (Rng.Int(Stat.BaseMax * 2) < power) { // resisted return(true); } return(false); }
/// <summary> /// Randomly chooses and applies a room decoration. /// </summary> /// <param name="room">Bounds of the room to decorate.</param> /// <param name="addDecoration">Callback to add a decoration at the given location.</param> public static void Decorate(Rect room, IRoomDecorator decorator) { // decorate it switch (Rng.Int(7)) { case 0: DecorateVerticalWall(room, decorator); break; case 1: DecorateHorizontalWall(room, decorator); break; case 2: DecorateInnerPillars(room, decorator); break; case 3: DecorateOuterPillars(room, decorator); break; case 4: DecorateInnerRoom(room, decorator); break; } }
private bool MakeStair(Connector connector) { // check to see if we can place it Rect rect = new Rect(connector.Position.Offset(-1, -1), 3, 3); if (!mWriter.IsOpen(rect, connector.Position + connector.Direction.Rotate180)) { return(false); } TileType type = (Rng.Int(10) < 6) ? TileType.StairsDown : TileType.StairsUp; mWriter.SetTile(connector.Position, type); return(true); }
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); }
private void PlaceDoor(Vec pos) { int choice = Rng.Int(100); if (choice < mWriter.Options.ChanceOfOpenDoor) { mWriter.SetTile(pos, TileType.DoorOpen); } else if (choice - mWriter.Options.ChanceOfOpenDoor < mWriter.Options.ChanceOfClosedDoor) { mWriter.SetTile(pos, TileType.DoorClosed); } else { mWriter.SetTile(pos, TileType.Floor); } //### bob: add locked and secret doors }
/// <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); }
/// <summary> /// Gets whether or not the Entity resists an attempt to physically move it against its will. /// </summary> /// <param name="damage">The hit that's moving the Entity.</param> /// <returns><c>true</c> if the Entity resists and should not be moved.</returns> public bool StandsFirm(Hit hit) { // if the element of the attack is resisted, always stand firm if (OnGetResistance(hit.Attack.Element) < 1.0f) { return(true); } // give the derived one a chance to resist too if (OnStandsFirm(hit)) { return(true); } // by default, the odds of resisting are half the fraction of the max health that // the damage is doing. so, if the damage is taking half the entity's health, // the odds of resisting are 1 in 4. return(Rng.Int(Health.Max) < (hit.Damage / 2)); }
void IFeatureWriter.Populate(Vec pos, int monsterDensity, int itemDensity, int depth) { // test every open tile if (mDungeon.Tiles[pos].IsPassable) { // place a monster if ((mDungeon.Entities.GetAt(pos) == null) && (Rng.Int(1000) < monsterDensity + (depth / 4))) { Monster.AddRandom(mDungeon, depth, pos); } // place an item if (Rng.Int(1000) < itemDensity + (depth / 4)) { Race race = Race.Random(mDungeon, depth, false); race.PlaceDrop(mDungeon, pos); } } }
public static Item Random(Vec pos, ItemType type, int level) { Item item = new Item(pos, type); // let the level wander level = Rng.WalkLevel(level); // randomly give it a power if (Rng.Int(150) <= level) { PowerType powerType = type.Content.Powers.Random(level, item.Type.Supercategory, item.Type.Subcategory); if (powerType != null) { item.mPower = new Power(powerType); } } return(item); }
/// <summary> /// Churns the store's inventory a bit. Should be called more frequently the longer it's been /// since the Hero last visited. /// </summary> public void UpdateInventory() { for (int i = 0; i < Rng.Int(10); i++) { // drop some new items foreach (Item item in mType.Value.Drop.Create(mType.Value.Depth)) { // as the store gets more full, remove items if (mInventory.Count > Rng.Int(mInventory.Max)) { mInventory.RemoveAt(Rng.Int(mInventory.Count)); } mInventory.Stack(item); if (item.Quantity > 0) { mInventory.Add(item); } } } }
private void InitBuildings(Content content, List <Rect> maxSizes) { if (maxSizes.Count != 6) { throw new ArgumentException("Should have the max sizes for 6 buildings."); } List <TileType> buildings = new List <TileType>() { TileType.DoorStore1, TileType.DoorStore2, TileType.DoorStore3, TileType.DoorStore4, TileType.DoorStore5, TileType.DoorStore6 }; // make each building foreach (Rect maxSize in maxSizes) { // pick an actual rect within it Vec size = new Vec(Rng.Int(6, maxSize.Width), Rng.Int(4, maxSize.Height)); Vec pos = maxSize.Position + Rng.Vec(maxSize.Size - size); Rect bounds = new Rect(pos, size); // pick the type int index = Rng.Int(buildings.Count); while (buildings[index] == TileType.Floor) { index = (index + 1) % 6; } TileType type = buildings[index]; // don't use this store again buildings[index] = TileType.Floor; Store store = new Store(content.Stores[index]); Vec doorPos = new Vec(Rng.IntInclusive(bounds.Left + 1, bounds.Right - 1), bounds.Bottom - 1); mBuildings.Add(new Building(store, type, bounds, doorPos)); } }
private bool MakePit(Connector connector) { // pits use room size right now int width = Rng.Int(mWriter.Options.RoomSizeMin, mWriter.Options.RoomSizeMax); int height = Rng.Int(mWriter.Options.RoomSizeMin, mWriter.Options.RoomSizeMax); Rect bounds = CreateRectRoom(connector, width, height); // bail if we failed if (bounds == Rect.Empty) { return(false); } // light it mWriter.LightRect(bounds, mDepth); // choose a group IList <Race> races = mWriter.Content.Races.AllInGroup(Rng.Item(mWriter.Content.Races.Groups)); // make sure we've got some races that aren't too out of depth races = new List <Race>(races.Where(race => race.Depth <= mDepth + 10)); if (races.Count == 0) { return(false); } // place the room foreach (Vec pos in bounds) { mWriter.SetTile(pos, TileType.Floor); } RoomDecoration.DecorateInnerRoom(bounds, new RoomDecorator(this, pos => mWriter.AddEntity(new Monster(pos, Rng.Item(races))))); return(true); }
public bool ResistDrain() { //### bob enemy will should affect return(Rng.Int(100) < Current); }
private bool MakeJunction(Connector connector) { // create a random junction Vec center = connector.Position + connector.Direction; bool left = false; bool right = false; bool straight = false; int choice = Rng.Int(100); if (choice < mWriter.Options.ChanceOfTurn) { if (Rng.OneIn(2)) { left = true; } else { right = true; } } else if (choice - mWriter.Options.ChanceOfTurn < mWriter.Options.ChanceOfFork) { if (Rng.OneIn(2)) { left = true; } else { right = true; } straight = true; } else if (choice - mWriter.Options.ChanceOfTurn - mWriter.Options.ChanceOfFork < mWriter.Options.ChanceOfTee) { left = true; right = true; } else if (choice - mWriter.Options.ChanceOfTurn - mWriter.Options.ChanceOfFork - mWriter.Options.ChanceOfTee < mWriter.Options.ChanceOfFourWay) { left = true; right = true; straight = true; } else { straight = true; } // check to see if we can place it Rect rect = new Rect(center.Offset(-1, -1), 3, 3); if (!mWriter.IsOpen(rect, center + connector.Direction.Rotate180)) { return(false); } // place the junction mWriter.SetTile(center, TileType.Floor); // add the connectors if (left) { mWriter.AddRoomConnector(center + connector.Direction.RotateLeft90, connector.Direction.RotateLeft90); } if (right) { mWriter.AddRoomConnector(center + connector.Direction.RotateRight90, connector.Direction.RotateRight90); } if (straight) { mWriter.AddRoomConnector(center + connector.Direction, connector.Direction); } return(true); }
private bool MakeMaze(Connector connector) { // in maze units (i.e. thin walls), not tiles int width = Rng.Int(mWriter.Options.MazeSizeMin, mWriter.Options.MazeSizeMax); int height = Rng.Int(mWriter.Options.MazeSizeMin, mWriter.Options.MazeSizeMax); int tileWidth = width * 2 + 3; int tileHeight = height * 2 + 3; Rect bounds = CreateRectRoom(connector, tileWidth, tileHeight); // bail if we failed if (bounds == Rect.Empty) { return(false); } // the hallway around the maze foreach (Vec pos in bounds.Trace()) { mWriter.SetTile(pos, TileType.Floor); } // sometimes make the walls low if (Rng.OneIn(2)) { foreach (Vec pos in bounds.Inflate(-1)) { mWriter.SetTile(pos, TileType.LowWall); } } // add an opening in one corner Vec doorway; switch (Rng.Int(8)) { case 0: doorway = bounds.TopLeft.Offset(2, 1); break; case 1: doorway = bounds.TopLeft.Offset(1, 2); break; case 2: doorway = bounds.TopRight.Offset(-3, 1); break; case 3: doorway = bounds.TopRight.Offset(-2, 2); break; case 4: doorway = bounds.BottomRight.Offset(-3, -2); break; case 5: doorway = bounds.BottomRight.Offset(-2, -3); break; case 6: doorway = bounds.BottomLeft.Offset(2, -2); break; case 7: doorway = bounds.BottomLeft.Offset(1, -3); break; default: throw new Exception(); } PlaceDoor(doorway); // carve the maze Maze maze = new Maze(width, height); maze.GrowTree(); Vec offset = bounds.Position.Offset(1, 1); maze.Draw(pos => mWriter.SetTile(pos + offset, TileType.Floor)); mWriter.LightRect(bounds, mDepth); // populate it int boostedDepth = mDepth + Rng.Int(mDepth / 5) + 2; Populate(bounds.Inflate(-2), 200, 300, boostedDepth); // place the connectors AddRoomConnectors(connector, bounds); return(true); }
public void Randomize() { mEnergy = Rng.Int(ActionCost); }
public bool MakeHall(Connector connector) { // create a random hall int length = Rng.Int(mWriter.Options.HallLengthMin, mWriter.Options.HallLengthMax); // check to see if we can place it Rect bounds = Rect.Empty; if (connector.Direction == Direction.N) { bounds = new Rect(connector.Position.X - 1, connector.Position.Y - length, 3, length + 1); } if (connector.Direction == Direction.S) { bounds = new Rect(connector.Position.X - 1, connector.Position.Y, 3, length + 1); } if (connector.Direction == Direction.E) { bounds = new Rect(connector.Position.X, connector.Position.Y - 1, length + 1, 3); } if (connector.Direction == Direction.W) { bounds = new Rect(connector.Position.X - length, connector.Position.Y - 1, length + 1, 3); } if (!mWriter.IsOpen(bounds, null)) { return(false); } // make sure the end corners aren't open unless the position in front of the end is too // prevents cases like: Vec pos = connector.Position + (connector.Direction.Offset * (length + 1)); if (!mWriter.Bounds.Contains(pos)) { return(false); } if (!mWriter.Bounds.Contains(pos + connector.Direction.RotateLeft90)) { return(false); } if (!mWriter.Bounds.Contains(pos + connector.Direction.RotateRight90)) { return(false); } // ####.. // ####.. // ....## <- new hall ends at corner of room // ###### if ((mWriter.GetTile(pos + connector.Direction.RotateLeft90) != TileType.Wall) && (mWriter.GetTile(pos) == TileType.Wall)) { return(false); } if ((mWriter.GetTile(pos + connector.Direction.RotateRight90) != TileType.Wall) && (mWriter.GetTile(pos) == TileType.Wall)) { return(false); } // place the hall pos = connector.Position; for (int i = 0; i <= length; i++) { mWriter.SetTile(pos, TileType.Floor); pos += connector.Direction; } PlaceDoor(connector.Position); PlaceDoor(connector.Position + (connector.Direction.Offset * length)); // add the connectors mWriter.AddHallConnector(connector.Position + (connector.Direction.Offset * length), connector.Direction); Populate(bounds, 10, 10, mDepth); return(true); }
protected override ActionResult OnProcess() { Hero hero = (Hero)Entity; // restore to max mStat.Restore(); if (mStat.Base < Stat.BaseMax) { // pick a stat to drain int i = Rng.Int(hero.Stats.Count - 1); if (hero.Stats[i] == mStat) { // picked the stat being raised, so skip it i++; } // drain one hero.Stats[i].Base--; // to raise another mStat.Base++; if (mStat == hero.Stats.Strength) { Log(LogType.PermanentGood, "{subject} feel[s] filled with brute strength!"); } else if (mStat == hero.Stats.Agility) { Log(LogType.PermanentGood, "{subject} feel[s] thin and nimble!"); } else if (mStat == hero.Stats.Stamina) { Log(LogType.PermanentGood, "{subject} feel[s] solid as a rock!"); } else if (mStat == hero.Stats.Will) { Log(LogType.PermanentGood, "{subject} feel[s] filled with blind courage!"); } else if (mStat == hero.Stats.Intellect) { Log(LogType.PermanentGood, "{subject} feel[s] clever!"); } else if (mStat == hero.Stats.Charisma) { Log(LogType.PermanentGood, "{subject} feel[s] pretty!"); } else { throw new Exception("Unknown stat \"" + mStat.Name + "\"."); } } else { if (mStat == hero.Stats.Strength) { Log(LogType.DidNotWork, "{subject} do[es]n't feel any stronger."); } else if (mStat == hero.Stats.Agility) { Log(LogType.DidNotWork, "{subject} do[es]n't feel any more graceful."); } else if (mStat == hero.Stats.Stamina) { Log(LogType.DidNotWork, "{subject} do[es]n't feel any tougher."); } else if (mStat == hero.Stats.Will) { Log(LogType.DidNotWork, "{subject} do[es]n't feel any more courageous."); } else if (mStat == hero.Stats.Intellect) { Log(LogType.DidNotWork, "{subject} do[es]n't feel any smarter."); } else if (mStat == hero.Stats.Charisma) { Log(LogType.DidNotWork, "{subject} do[es]n't feel any prettier."); } else { throw new Exception("Unknown stat \"" + mStat.Name + "\"."); } } return(ActionResult.Done); }
public bool ShouldAttempt() { return(Rng.Int(Info.Range) < Info.Chance); }
public Direction GetDirection(Monster monster, Entity target) { Vec relative = target.Position - monster.Position; // walk towards player Direction direction = Direction.Towards(relative); // move erratically switch (mPursue) { case Pursue.Closely: // do nothing break; case Pursue.SlightlyErratically: while (Rng.OneIn(3)) { if (Rng.OneIn(2)) { direction = direction.Next; } else { direction = direction.Previous; } } break; case Pursue.Erratically: while (Rng.OneIn(2)) { if (Rng.OneIn(2)) { direction = direction.Next; } else { direction = direction.Previous; } } break; case Pursue.VeryErratically: int turns = Rng.Int(3); for (int i = 0; i < turns; i++) { if (Rng.OneIn(2)) { direction = direction.Next; } else { direction = direction.Previous; } } break; } // don't walk through walls if (!monster.CanMove(direction) || monster.IsOccupiedByOtherMonster(direction.Offset, target)) { // try to go around obstacle Direction firstTry = direction.Previous; Direction secondTry = direction.Next; // don't always try the same order if (Rng.OneIn(2)) { Obj.Swap(ref firstTry, ref secondTry); } if (monster.CanMove(firstTry) && !monster.IsOccupiedByOtherMonster(firstTry.Offset, target)) { direction = firstTry; } else if (monster.CanMove(secondTry) && !monster.IsOccupiedByOtherMonster(secondTry.Offset, target)) { direction = secondTry; } else { // give up direction = Direction.None; } } return(direction); }
public IEnumerable <T> Create(int level) { // find the drops before and after the level int before = -1; int after = -1; for (int i = 0; i < Choices.Count; i++) { if (Choices[i].Odds <= level) { before = i; } else if (Choices[i].Odds >= level) { after = i; break; } } if (before == -1) { before = after; } if (after == -1) { after = before; } // choose between the two drops, weighted by the level's distance to each one int index = before; if (before != after) { if (Rng.IntInclusive((int)Choices[before].Odds, (int)Choices[after].Odds) <= level) { index = after; } else { index = before; } } const int ChanceOfDecrement = 40; // out of 100 const int ChanceOfIncrement = 10; // randomly walk through the drops if (Rng.Int(100) < ChanceOfDecrement) { // walk down while (index > 0) { index--; if (Rng.Int(100) >= ChanceOfDecrement) { break; } } } else if (Rng.Int(100) < ChanceOfIncrement) { // walk up while (index < Choices.Count - 1) { index++; if (Rng.Int(100) >= ChanceOfIncrement) { break; } } } // drop it foreach (var item in Choices[index].Drop.Create(level)) { yield return(item); } }
void IFeatureWriter.AddHallConnector(Vec pos, Direction dir) { mUnusedConnectors.Insert(Rng.Int(mUnusedConnectors.Count), new Connector(ConnectFrom.Hall, dir, pos)); }