コード例 #1
0
        /// <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);
        }
コード例 #2
0
        /// <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);
                }
            }
        }
コード例 #3
0
ファイル: Stats.cs プロジェクト: stjordanis/amaranth
        public Stats()
        {
            // reset the stats and then
            foreach (Stat stat in this)
            {
                stat.Base = 5;
            }

            // randomly add points until a total of 15 points per stat have been distributed
            for (int i = 0; i < 10 * Count; i++)
            {
                Rng.Item(this).Base++;
            }
        }
コード例 #4
0
        public bool TryFindOpenTileWithin(Vec startPos, int minRadius, int maxRadius, out Vec pos)
        {
            // find all possible tiles
            List <Vec> positions = new List <Vec>();
            Rect       bounds    = new Rect(startPos - (Vec.One * maxRadius), Vec.One * (maxRadius + maxRadius + 1));

            foreach (Vec tryPos in bounds)
            {
                // skip if out of bounds
                if (!Bounds.Contains(tryPos))
                {
                    continue;
                }

                // skip if outside the valid radii
                int distanceSquared = (tryPos - startPos).LengthSquared;
                if ((distanceSquared < minRadius) || (distanceSquared > maxRadius))
                {
                    continue;
                }

                // skip if not open
                if (!Tiles[tryPos].IsPassable)
                {
                    continue;
                }

                // skip if already an entity there
                if (mEntities.GetAt(tryPos) != null)
                {
                    continue;
                }

                // if we got here, we found one
                positions.Add(tryPos);
            }

            // bail if there are none
            pos = startPos;
            if (positions.Count == 0)
            {
                return(false);
            }

            // choose one randomly
            pos = Rng.Item(positions);
            return(true);
        }
コード例 #5
0
        /*
         * /// Finds a nearby position for an <see cref="Item"/> starting at the given
         * /// position. Tries to spread items out (minimize floor stacking) but still
         * /// make sure that all spawn items are reachable from the starting location.
         * /// </summary>
         * /// <param name="startPos"></param>
         * public IList<Vec> FindItemPositions(Vec startPos, int numNeeded)
         * {
         *  // get the possible positions
         *  IDictionary<Vec, int> reachable = GetReachablePositions(startPos, numNeeded + 2);
         *
         *  // weight by the number of items and jitter a little
         *  IDictionary<Vec, int> weighted = new Dictionary<Vec, int>();
         *
         *  foreach (KeyValuePair<Vec, int> pair in reachable)
         *  {
         *      weighted[pair.Key] = ((pair.Value + Items.GetAllAt(pair.Key).Count) * 5) + Rng.Int(7);
         *  }
         *
         *  // sort by weight
         *  List<Vec> positions = new List<Vec>(weighted.Keys);
         *  positions.Sort((a, b) => weighted[a].CompareTo(weighted[b]));
         *
         *  // add duplicates if we don't have enough
         *  while (positions.Count < numNeeded)
         *  {
         *      positions.Add(Rng.Item(positions));
         *  }
         *
         *  return positions;
         * }
         *
         * private IDictionary<Vec, int> GetReachablePositions(Vec startPos, int maxSteps)
         * {
         *  // convert between flood coordinates and dungeon coordinates
         *  Vec toDungeon = startPos - new Vec(maxSteps, maxSteps);
         *  Vec toFlood = Vec.Zero - toDungeon;
         *
         *  Array2D<int> flood = new Array2D<int>(maxSteps * 2 + 1, maxSteps * 2 + 1);
         *
         *  // mark the invalid areas
         *  foreach (Vec pos in flood.Bounds)
         *  {
         *      Vec dungeon = pos + toDungeon;
         *
         *      if (!Bounds.Contains(dungeon) || !Tiles[dungeon].IsPassable)
         *      {
         *          flood[pos] = -1;
         *      }
         *  }
         *
         *  // start the flood
         *  flood[startPos + toFlood] = 1;
         *
         *  // flood
         *  bool changed = true;
         *
         *  while (changed)
         *  {
         *      changed = false;
         *
         *      foreach (Vec pos in flood.Bounds)
         *      {
         *          // if this tile has been reached
         *          if (flood[pos] > 0)
         *          {
         *              // reach its neighbors
         *              foreach (Direction direction in Direction.Clockwise)
         *              {
         *                  Vec neighbor = pos + direction;
         *
         *                  // flood it
         *                  if (flood.Bounds.Contains(neighbor) && (flood[neighbor] == 0))
         *                  {
         *                      flood[neighbor] = flood[pos] + 1;
         *                      changed = true;
         *                  }
         *              }
         *          }
         *      }
         *  }
         *
         *  // collect the reached tiles
         *  IDictionary<Vec, int> positions = new Dictionary<Vec, int>();
         *
         *  foreach (Vec pos in flood.Bounds)
         *  {
         *      if (flood[pos] > 0)
         *      {
         *          positions[pos + toDungeon] = flood[pos];
         *      }
         *  }
         *
         *  return positions;
         * }
         */

        /// <summary>
        /// Attempts to find a tile adjacent to the given starting position
        /// that does not contain any Monsters. If there are multiple available
        /// adjacent tiles, one will be chosen randomly.
        /// </summary>
        /// <param name="startPos">The position to search around.</param>
        /// <param name="pos">The chosen open tile.</param>
        /// <returns><c>true</c> if one was found.</returns>
        public bool TryFindOpenAdjacent(Vec startPos, out Vec pos)
        {
            // find all possible ones
            List <Vec> positions = new List <Vec>();

            foreach (Direction direction in Direction.Clockwise)
            {
                Vec tryPos = startPos + direction;

                // skip if out of bounds
                if (!Bounds.Contains(tryPos))
                {
                    continue;
                }

                // skip if not open
                if (!Tiles[tryPos].IsPassable)
                {
                    continue;
                }

                // skip if already an entity there
                if (mEntities.GetAt(tryPos) != null)
                {
                    continue;
                }

                // found a possible one
                positions.Add(tryPos);
            }

            // bail if there aren't any
            pos = startPos;
            if (positions.Count == 0)
            {
                return(false);
            }

            // choose one randomly
            pos = Rng.Item(positions);
            return(true);
        }
コード例 #6
0
        private Hero(string name, string race, Stats stats, bool cheatDeath)
            : base(Vec.Zero, Energy.NormalSpeed, 10)
        {
            mName = name;
            mRace = race;

            //### bob: temp. should be passed in
            mClass = new Warrior();

            mStats = stats;

            mCheatDeath = cheatDeath;

            mInventory = new Inventory();
            mEquipment = new Equipment();
            mEquipment.ItemEquipped.Add(Equipment_ItemEquipped);

            mLevel = 1;

            SetBehavior(new OneShotBehavior(null));

            mExperience.Changed += Experience_Changed;

            foreach (Stat stat in Stats)
            {
                stat.Changed += Stat_Changed;
            }

            // pick all of the random stat gains for levelling up. pick them now so that we
            // can unwind and rewind them as exp is drained and regained.
            mStatGains = new Stat[MaxLevel];
            for (int i = 0; i < mStatGains.Length; i++)
            {
                mStatGains[i] = Rng.Item(Stats);
            }

            RefreshMaxHealth();
            RefreshAutoHealTimer();
        }
コード例 #7
0
        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);
        }
コード例 #8
0
 public override Attack GetAttack(Entity defender)
 {
     // pick one randomly
     return(Rng.Item(Race.Attacks));
 }
コード例 #9
0
ファイル: PushBackAction.cs プロジェクト: stjordanis/amaranth
        protected override ActionResult OnProcess()
        {
            if (Entity.StandsFirm(mHit))
            {
                Log(LogType.Resist, "{subject} stand[s] firm against the waves.");

                return(ActionResult.Done);
            }

            // get a weighted list of the possible destinations
            List <Vec> positions = new List <Vec>();

            Vec straight = Entity.Position + mHit.Direction;
            Vec left45   = Entity.Position + mHit.Direction.Previous;
            Vec right45  = Entity.Position + mHit.Direction.Next;
            Vec left90   = Entity.Position + mHit.Direction.RotateLeft90;
            Vec right90  = Entity.Position + mHit.Direction.RotateRight90;

            // directly away
            if (Entity.CanOccupy(straight))
            {
                // more likely than other directions
                positions.Add(straight);
                positions.Add(straight);
                positions.Add(straight);
                positions.Add(straight);
            }

            // off to one side
            if (Entity.CanOccupy(left45))
            {
                positions.Add(left45);
                positions.Add(left45);
            }

            // off to the other side
            if (Entity.CanOccupy(right45))
            {
                positions.Add(right45);
                positions.Add(right45);
            }

            // off to one side
            if (Entity.CanOccupy(left90))
            {
                positions.Add(left90);
            }

            // off to the other side
            if (Entity.CanOccupy(right90))
            {
                positions.Add(right90);
            }

            // fail if nowhere to be pushed
            if (positions.Count == 0)
            {
                return(ActionResult.Fail);
            }

            // pick a random direction
            AddEffect(new Effect(Entity.Position, EffectType.Teleport, mHit.Attack.Element));

            Entity.Position = Rng.Item(positions);

            AddEffect(new Effect(Entity.Position, EffectType.Teleport, mHit.Attack.Element));

            Log(LogType.BadState, "{subject} [are|is] knocked back by the water!");

            return(ActionResult.Done);
        }
コード例 #10
0
 private TileType ChooseInnerWall()
 {
     return(Rng.Item(new TileType[] { TileType.Wall, TileType.LowWall }));
 }