X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fcarfire;a=blobdiff_plain;f=CarFire%2FCarFire%2FCarFire%2FSaberMonster.cs;h=69b42ca1a44f11d578af78fb4a92fcb5b52b5a9d;hp=06fd1272adbbc58deeaf14908805374b43f98666;hb=122c062297acac44673e947b666c1d72cd23fb1b;hpb=af9deb873b24dadd0d509ce199fc6cac2b3efbc9 diff --git a/CarFire/CarFire/CarFire/SaberMonster.cs b/CarFire/CarFire/CarFire/SaberMonster.cs index 06fd127..69b42ca 100644 --- a/CarFire/CarFire/CarFire/SaberMonster.cs +++ b/CarFire/CarFire/CarFire/SaberMonster.cs @@ -8,6 +8,9 @@ using Microsoft.Xna.Framework.Graphics; namespace CarFire { + /// + /// A type for the states of an artificually intelligent entity. + /// public enum AiState { Standing, @@ -19,14 +22,34 @@ namespace CarFire } + /// + /// An example monster. This can serve as a starting place for + /// creating other monsters. This one just follows a path. + /// public class SaberMonster : IMonster { + //starting health + int health = 100; + /// + /// Construct this type of monster. This constructor is called + /// by the map when the game requests entities. + /// + /// The single character ID. + /// The initial position on the map. + /// More parameters. + /// The game object reference. public SaberMonster(char identifier, Point position, Dictionary info, Game game) { mId = identifier; mMotion = new MovementManager(position); + mRetryInterval = 20 + (position.X * 25789 + position.Y * 259) % 30; + + // We need to keep the game reference in order to get the grid when we + // need to find paths. mGame = game; + // Get the speed of the monster. If not set in the map, it defaults to + // whatever the default of MovementManager is... 1 I think. string speedString; if (info.TryGetValue("speed", out speedString)) { @@ -42,55 +65,58 @@ namespace CarFire foreach (string pathPoint in idlePathPoints) { Point? point = Parse.Coordinates(pathPoint); - if (point != null) mIdlePath.Add(point.Value); + if (point != null) mWaypoints.Add(point.Value); } } + // Start doing something... StartPacing(); } + /// + /// Call this to switch the monster AI state to pacing and set up + /// the initial paths. The monster will start following the path it + /// was defined with in the map file. + /// public void StartPacing() { - mState = AiState.Pacing; + if (mWaypoints.Count == 0) return; - if (mIdlePath.Count == 0) return; + mState = AiState.Pacing; // Determine the best (closest) waypoint to start at. - mIdlePathIndex = 0; + // We may not be on the path, so we have to walk to get on it. + mWaypointIndex = 0; int closest = int.MaxValue; - for (int i = 0; i < mIdlePath.Count; i++) + for (int i = 0; i < mWaypoints.Count; i++) { - int distance = PathFinder.GetManhattanDistance(Coordinates, mIdlePath[i]); + int distance = PathFinder.GetManhattanDistance(Coordinates, mWaypoints[i]); if (distance < closest) { - mIdlePathIndex = i; + mWaypointIndex = i; closest = distance; } } - PathFinder pathFinder = new PathFinder(mGame.Grid); - mPath = new List(32); - mPath.Add(Coordinates); - mPath.AddRange(pathFinder.GetPath(mMotion.Coordinates, mIdlePath[mIdlePathIndex])); - mPath.Add(mIdlePath[mIdlePathIndex]); - mPathIndex = 0; + // Find the path to get to the closest waypoint. + ChartPath(); } Direction GetDirectionToNextCell() { if (mPathIndex >= mPath.Count) { - mIdlePathIndex++; - PathFinder pathFinder = new PathFinder(mGame.Grid); - mPath = new List(32); - mPath.Add(Coordinates); - mPath.AddRange(pathFinder.GetPath(mMotion.Coordinates, mIdlePath[mIdlePathIndex % mIdlePath.Count])); - mPath.Add(mIdlePath[mIdlePathIndex % mIdlePath.Count]); - mPathIndex = 0; + // We're done with the current path, so find the path to + // the next waypoint... forever. + mWaypointIndex++; + ChartPath(); } - if (mPath[mPathIndex % mPath.Count] == mMotion.Coordinates) + // We need to make sure our direction is set to the next cell + // we want to be. If our current coordinates match that, we need + // to change our direction to get to the next cell. + if (mPathIndex < mPath.Count && mPath[mPathIndex] == mMotion.Coordinates) { mPathIndex++; mPathDirection = MovementManager.GetDirection(mMotion.Coordinates, mPath[mPathIndex % mPath.Count]); @@ -99,9 +125,26 @@ namespace CarFire return mPathDirection; } + void ChartPath() + { + mPath = new List(32); + + Point waypoint = mWaypoints[mWaypointIndex % mWaypoints.Count]; + PathFinder pathFinder = new PathFinder(mGame.Grid); + List path = pathFinder.GetPath(mMotion.Coordinates, waypoint); + if (path != null) mPath.AddRange(path); + + mPathIndex = 0; + if (mPathIndex < mPath.Count) mPathDirection = MovementManager.GetDirection(mMotion.Coordinates, mPath[mPathIndex]); + else mPathDirection = Direction.None; + } + #region IMonster Members + /// + /// I don't know what this is for. + /// public bool visible { get { throw new NotImplementedException(); } @@ -112,38 +155,112 @@ namespace CarFire #region ICharacter Members + /// + /// Load the monster's content. This is called by the map when + /// the game requests the entities. + /// + /// The zaphnod. public void LoadContent(ContentManager contentManager) { - mTexture = contentManager.Load("menuItem"); + mTexture = contentManager.Load("default"); } + /// + /// Update the monster's state. This should be called by the game + /// every "frame" (whenever the game is updating its state). In this + /// simple monster, all we need to do is update the motion manager. + /// + /// public void Update(TimeSpan timeSpan) { - if (mState == AiState.Pacing) + switch (mState) { - mMotion.Update(timeSpan, GetDirectionToNextCell()); + case AiState.Pacing: + + Direction direction = GetDirectionToNextCell(); + Point destination = MovementManager.GetNeighbor(Coordinates, direction); + + if (mGame.IsCellOpen(destination)) + { + mMotion.Update(timeSpan, direction); + } + else + { + if (mGame.CurrentFrameNumber % mRetryInterval == 0) + { + // Something is in our way, so let's chart a new course. + ChartPath(); + + direction = GetDirectionToNextCell(); + /*if (direction == Direction.None) + { + // If we still can't chart a course, just stand there + // and try to chart again later. + mState = AiState.Standing; + }*/ + + mMotion.Update(timeSpan, direction); + } + else mMotion.Update(timeSpan); + } + + break; + + case AiState.Standing: + + mMotion.Update(timeSpan); + break; } } + /// + /// Draw the monster. We just ask the map for our screen position, + /// passing it the position which the motion manager keeps track of. + /// + /// The public void Draw(SpriteBatch spriteBatch) { Rectangle position = mGame.State.Map.GetRectangleFromCoordinates(mMotion.Position); spriteBatch.Draw(mTexture, position, Color.White); } + /// + /// A monster should keep track of its health. This one doesn't. + /// public int Health { - get { throw new NotImplementedException(); } + get { return this.health; } + } + /// + /// This monster is invincible. + /// + /// public void causeDamageTo(int amount) { - throw new NotImplementedException(); + this.health -= amount; } + public bool IsCollidable { get { return true; } } + + /// + /// Get the smoothed position. + /// public Vector2 Position { get { return mMotion.Position; } } - public Point Coordinates { get { return mMotion.Coordinates; } } + /// + /// Get the grid coordinates. + /// + public Point Coordinates { + get { return mMotion.Coordinates; } + set { mMotion = new MovementManager(value, mMotion.Speed); } + } + + /// + /// Get the entity identifier. + /// + public char Identifier { get { return mId; } } #endregion @@ -155,16 +272,18 @@ namespace CarFire char mId; MovementManager mMotion; - List mIdlePath = new List(); - int mIdlePathIndex; + List mWaypoints = new List(); // List of waypoints that we got from the map. + int mWaypointIndex; // Index to the waypoint we're heading for. - List mPath; - int mPathIndex; - Direction mPathDirection; + List mPath; // List of cells in the path between the position between where + // we started and the waypoint we're heading for. + int mPathIndex; // Index to the cell we're heading for. + Direction mPathDirection; // The direction between our current position and the place we're going. + int mRetryInterval; - AiState mState; + AiState mState; // What is the monster doing? - Texture2D mTexture; + Texture2D mTexture; // Obvious. #endregion }