new PathFinder.GetPathAsync to push path finding onto the thread pool; new PathFinder...
[chaz/carfire] / CarFire / CarFire / CarFire / SaberMonster.cs
1 ´╗┐using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using Microsoft.Xna.Framework;
6 using Microsoft.Xna.Framework.Content;
7 using Microsoft.Xna.Framework.Graphics;
8
9 namespace CarFire
10 {
11 /*
12 /// <summary>
13 /// A type for the states of an artificually intelligent entity.
14 /// </summary>
15 public enum AiState
16 {
17 Standing,
18 Pacing,
19 Chasing,
20 Dazed,
21 Fighting,
22 Retreating
23 }
24 */
25
26 /// <summary>
27 /// An example monster. This can serve as a starting place for
28 /// creating other monsters. This one just follows a path.
29 /// </summary>
30 public class SaberMonster : IMonster
31 {
32 //starting health
33 int health = 100;
34 /// <summary>
35 /// Construct this type of monster. This constructor is called
36 /// by the map when the game requests entities.
37 /// </summary>
38 /// <param name="identifier">The single character ID.</param>
39 /// <param name="position">The initial position on the map.</param>
40 /// <param name="info">More parameters.</param>
41 /// <param name="game">The game object reference.</param>
42 public SaberMonster(char identifier, Point position, Dictionary<string, string> info, Game game)
43 {
44 mId = identifier;
45 mMotion = new MovementManager(position);
46 mRetryInterval = 2 + (position.X * 25789 + position.Y * 259) % 30;
47
48 // We need to keep the game reference in order to get the grid when we
49 // need to find paths.
50 mGame = game;
51
52 // Get the speed of the monster. If not set in the map, it defaults to
53 // whatever the default of MovementManager is... 1 I think.
54 string speedString;
55 if (info.TryGetValue("speed", out speedString))
56 {
57 int? speed = Parse.Integer(speedString);
58 if (speed != null) mMotion.Speed = speed.Value;
59 }
60
61 // Get the "idle path" coordinates loaded from the map.
62 string idlePath;
63 if (info.TryGetValue("path", out idlePath))
64 {
65 string[] idlePathPoints = Parse.List(idlePath);
66 foreach (string pathPoint in idlePathPoints)
67 {
68 Point? point = Parse.Coordinates(pathPoint);
69 if (point != null) mWaypoints.Add(point.Value);
70 }
71 }
72 mPath = new List<Point>();
73
74 // Start doing something...
75 StartPacing();
76 }
77 public void DefaultAction()
78 {
79
80 }
81 public void Chasing(Point Chase)
82 {
83
84 }
85
86 /// <summary>
87 /// Call this to switch the monster AI state to pacing and set up
88 /// the initial paths. The monster will start following the path it
89 /// was defined with in the map file.
90 /// </summary>
91 public void StartPacing()
92 {
93 if (mWaypoints.Count == 0) return;
94
95 mState = AiState.Pacing;
96
97 // Determine the best (closest) waypoint to start at.
98 // We may not be on the path, so we have to walk to get on it.
99 mWaypointIndex = 0;
100 int closest = int.MaxValue;
101 for (int i = 0; i < mWaypoints.Count; i++)
102 {
103 int distance = PathFinder.GetManhattanDistance(Coordinates, mWaypoints[i]);
104 if (distance < closest)
105 {
106 mWaypointIndex = i;
107 closest = distance;
108 }
109 }
110
111 // Find the path to get to the closest waypoint.
112 ChartPath();
113 }
114
115 Direction GetDirectionToNextCell()
116 {
117 if (mPath != null)
118 {
119 if (mPathIndex >= mPath.Count)
120 {
121 // We're done with the current path, so find the path to
122 // the next waypoint... forever.
123 mWaypointIndex++;
124 ChartPath();
125 }
126
127 // We need to make sure our direction is set to the next cell
128 // we want to be. If our current coordinates match that, we need
129 // to change our direction to get to the next cell.
130 else if (mPath[mPathIndex] == mMotion.Coordinates)
131 {
132 mPathIndex++;
133 mPathDirection = MovementManager.GetDirection(mMotion.Coordinates, mPath[mPathIndex % mPath.Count]);
134 }
135
136 return mPathDirection;
137 }
138 else return Direction.None;
139 }
140
141 void ChartPath()
142 {
143 if (mPathSearch == null)
144 {
145 Point waypoint = mWaypoints[mWaypointIndex % mWaypoints.Count];
146 PathFinder pathFinder = new PathFinder(mGame.Grid);
147 Point? nearby = pathFinder.GetNearbyOpenCell(waypoint);
148 if (nearby != null) mPathSearch = pathFinder.GetPathAsync(mMotion.Coordinates, nearby.Value);
149
150 mPathIndex = 0;
151 mPath = null;
152 }
153 else if (mPathSearch.IsCompleted)
154 {
155 mPath = (List<Point>)mPathSearch.Path;
156 mPathSearch = null;
157
158 if (mPath != null) mPathDirection = MovementManager.GetDirection(mMotion.Coordinates, mPath[0]);
159 }
160 }
161
162
163 #region IMonster Members
164
165 /// <summary>
166 /// I don't know what this is for.
167 /// </summary>
168 public bool visible
169 {
170 get { throw new NotImplementedException(); }
171 }
172
173 #endregion
174
175
176 #region ICharacter Members
177
178 /// <summary>
179 /// Load the monster's content. This is called by the map when
180 /// the game requests the entities.
181 /// </summary>
182 /// <param name="contentManager">The zaphnod.</param>
183 public void LoadContent(ContentManager contentManager)
184 {
185 mTexture = contentManager.Load<Texture2D>("default");
186 }
187
188 /// <summary>
189 /// Update the monster's state. This should be called by the game
190 /// every "frame" (whenever the game is updating its state). In this
191 /// simple monster, all we need to do is update the motion manager.
192 /// </summary>
193 /// <param name="timeSpan"></param>
194 public void Update(TimeSpan timeSpan)
195 {
196 switch (mState)
197 {
198 case AiState.Pacing:
199
200 Direction direction = GetDirectionToNextCell();
201 Point destination = MovementManager.GetNeighbor(Coordinates, direction);
202
203 if (mGame.IsCellOpen(destination))
204 {
205 mMotion.Update(timeSpan, direction);
206 }
207 else
208 {
209 if (mGame.CurrentFrameNumber % mRetryInterval == 0) ChartPath();
210 mMotion.Update(timeSpan);
211 }
212
213 break;
214
215 case AiState.Standing:
216
217 mMotion.Update(timeSpan);
218 break;
219 }
220 }
221
222 /// <summary>
223 /// Draw the monster. We just ask the map for our screen position,
224 /// passing it the position which the motion manager keeps track of.
225 /// </summary>
226 /// <param name="spriteBatch">The </param>
227 public void Draw(SpriteBatch spriteBatch)
228 {
229 Rectangle position = mGame.State.Map.GetRectangleFromCoordinates(mMotion.Position);
230 spriteBatch.Draw(mTexture, position, Color.White);
231 }
232
233 /// <summary>
234 /// A monster should keep track of its health. This one doesn't.
235 /// </summary>
236 public int Health
237 {
238 get { return this.health; }
239
240 }
241
242 /// <summary>
243 /// This monster is invincible.
244 /// </summary>
245 /// <param name="amount"></param>
246 public void causeDamageTo(int amount)
247 {
248 this.health -= amount;
249 }
250
251 public bool IsCollidable { get { return true; } }
252
253 /// <summary>
254 /// Get the smoothed position.
255 /// </summary>
256 public Vector2 Position { get { return mMotion.Position; } }
257
258 /// <summary>
259 /// Get the grid coordinates.
260 /// </summary>
261 public Point Coordinates {
262 get { return mMotion.Coordinates; }
263 set { mMotion = new MovementManager(value, mMotion.Speed); }
264 }
265
266 /// <summary>
267 /// Get the entity identifier.
268 /// </summary>
269 public char Identifier { get { return mId; } }
270
271 #endregion
272
273
274 #region Private Variables
275
276 Game mGame;
277
278 char mId;
279 MovementManager mMotion;
280
281 List<Point> mWaypoints = new List<Point>(); // List of waypoints that we got from the map.
282 int mWaypointIndex; // Index to the waypoint we're heading for.
283
284 List<Point> mPath; // List of cells in the path between the position between where
285 // we started and the waypoint we're heading for.
286 int mPathIndex; // Index to the cell we're heading for.
287 Direction mPathDirection; // The direction between our current position and the place we're going.
288 int mRetryInterval;
289 PathFinder.AsyncTask mPathSearch; // If a path search is in progress, this is the task object.
290
291 AiState mState; // What is the monster doing?
292
293 Texture2D mTexture; // Obvious.
294
295 #endregion
296 }
297 }
This page took 0.057598 seconds and 4 git commands to generate.