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; } } }
protected override bool WillUseMove(Monster monster, Entity target, BreedMoveInfo info) { // the odds of breeding decays exponentially int chance = info.Generation * info.Generation; // random chance to breed return(Rng.OneIn(chance)); }
/// <summary> /// Tries to find a tile reachable from the starting tile that has as few items as possible. /// </summary> /// <param name="startPos"></param> /// <returns></returns> public Vec GetOpenItemPosition(Vec startPos) { Vec currentPos = startPos; bool changed = true; while (changed) { int currentCount = Items.CountAt(currentPos); changed = false; // short-circuit if we hit an empty tile if (currentCount == 0) { break; } int found = 0; Vec fromPos = currentPos; foreach (Direction dir in Direction.Clockwise) { Vec pos = fromPos + dir; // make sure it's a valid tile if (Bounds.Contains(pos) && Tiles[pos].IsPassable) { int count = Items.CountAt(pos); // if we found a square just as good, randomly choose between // it and all of the previously found ones in this loop if ((count == currentCount) && (found > 0)) { if (Rng.OneIn(found + 1)) { // pick this new one currentCount++; } else { // still consider it found even if it was skipped found++; } } if (count < currentCount) { found++; currentPos = pos; currentCount = count; changed = true; } } } } return(currentPos); }
public void Init(Content content) { // add the buildings List <Rect> maxSizes = new List <Rect>(); // see if it's a horizontal or vertical layout if (Rng.OneIn(2)) { // horizontal Vec maxSize = new Vec(Bounds.Width / 3, Bounds.Height / 2); maxSizes.Add(new Rect(Bounds.Left, Bounds.Top, maxSize).Inflate(-2)); maxSizes.Add(new Rect(Bounds.Left + maxSize.X, Bounds.Top, maxSize).Inflate(-2)); maxSizes.Add(new Rect(Bounds.Left + maxSize.X + maxSize.X, Bounds.Top, maxSize).Inflate(-2)); maxSizes.Add(new Rect(Bounds.Left, Bounds.Top + maxSize.Y, maxSize).Inflate(-2)); maxSizes.Add(new Rect(Bounds.Left + maxSize.X, Bounds.Top + maxSize.Y, maxSize).Inflate(-2)); maxSizes.Add(new Rect(Bounds.Left + maxSize.X + maxSize.X, Bounds.Top + maxSize.Y, maxSize).Inflate(-2)); } else { // vertical Vec maxSize = new Vec(Bounds.Width / 2, Bounds.Height / 3); maxSizes.Add(new Rect(Bounds.Left, Bounds.Top, maxSize).Inflate(-2)); maxSizes.Add(new Rect(Bounds.Left + maxSize.X, Bounds.Top, maxSize).Inflate(-2)); maxSizes.Add(new Rect(Bounds.Left, Bounds.Top + maxSize.Y, maxSize).Inflate(-2)); maxSizes.Add(new Rect(Bounds.Left + maxSize.X, Bounds.Top + maxSize.Y, maxSize).Inflate(-2)); maxSizes.Add(new Rect(Bounds.Left, Bounds.Top + maxSize.Y + maxSize.Y, maxSize).Inflate(-2)); maxSizes.Add(new Rect(Bounds.Left + maxSize.X, Bounds.Top + maxSize.Y + maxSize.Y, maxSize).Inflate(-2)); } InitBuildings(content, maxSizes); // add the down stairs bool foundOpen = false; while (!foundOpen) { mStairsPos = Rng.Vec(Bounds.Inflate(-1)); foundOpen = true; foreach (Building building in mBuildings) { // see if the stairs are overlapping a building if (building.Bounds.Contains(mStairsPos)) { foundOpen = false; break; } } } }
public override Action NextAction() { Entity target = Monster.Dungeon.Game.Hero; Vec distance = target.Position - Monster.Position; // wake up or fall asleep if (!mIsAwake) { // if close enough to the hero, attempt to wake up if ((distance.KingLength < WakeUpDistance) && Rng.OneIn(WakeUpChance)) { WakeUp(); } } else { // if far enough, try to fall back asleep if ((distance.KingLength > FallAsleepDistance) && Rng.OneIn(FallAsleepChance)) { FallAsleep(); } } if (mIsAwake) { // consider performing a move foreach (Move move in Monster.Race.Moves) { // see if it's possible and the odds match if (move.WillUseMove(Monster, target) && move.ShouldAttempt()) { // use this move return(move.GetAction(Monster, target)); } } // walk Direction direction = mPathfinder.GetDirection(Monster, target); return(new WalkAction(Monster, direction)); } // otherwise, just stand still return(new WalkAction(Monster, Direction.None)); }
/// <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> /// Chooses a random item of the given level from the set of items that /// match the given predicate. /// </summary> protected T Random(int level, Func <T, bool> predicate) { // exponential chance of reducing the chosen level while ((level > 1) && Rng.OneIn(3)) { level--; } // chance of increasing the chosen level while ((level <= Game.MaxDepth) && Rng.OneIn(5)) { level++; } // choose a matching item from the level return(Random((rareObj) => (rareObj.MinLevel <= level) && (rareObj.MaxLevel >= level) && predicate(rareObj.Object))); }
/// <summary> /// Randomly walks the given starting value repeatedly up and/or down /// with the given probabilities. Will only walk in one direction. /// </summary> /// <param name="start">Value to start at.</param> public static int Walk(int start, int chanceOfDec, int chanceOfInc) { // make sure we won't get stuck in an infinite loop if (chanceOfDec == 1) { throw new ArgumentOutOfRangeException("chanceOfDec must be zero or greater than one."); } if (chanceOfInc == 1) { throw new ArgumentOutOfRangeException("chanceOfInc must be zero greater than one."); } // decide if walking up or down int direction = Int(chanceOfDec + chanceOfInc); if (direction < chanceOfDec) { // exponential chance of decrementing int sanity = 10000; while (Rng.OneIn(chanceOfDec) && (sanity-- > 0)) { start--; } } else if (direction < chanceOfDec + chanceOfInc) { // exponential chance of incrementing int sanity = 10000; while (Rng.OneIn(chanceOfInc) && (sanity-- > 0)) { start++; } } return(start); }
public void AddLoops(int chance) { if (chance > 0) { foreach (Vec cell in new Rect(0, 0, Bounds.Width - 1, Bounds.Height - 1)) { if (Rng.OneIn(chance)) { if (IsOpen(cell) && IsOpen(cell + Direction.E)) { Carve(cell, Direction.E); } } if (Rng.OneIn(chance)) { if (IsOpen(cell) && IsOpen(cell + Direction.S)) { Carve(cell, Direction.S); } } } } }
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); }
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); }
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); }