From af9deb873b24dadd0d509ce199fc6cac2b3efbc9 Mon Sep 17 00:00:00 2001 From: Charles Date: Wed, 21 Apr 2010 03:03:20 +0000 Subject: [PATCH] SaberMonster loads from map file and walks around using the path finder, and a lot of refactoring. git-svn-id: https://bd85.net/svn/cs3505_group@122 92bb83a3-7c8f-8a45-bc97-515c4e399668 --- CarFire/CarFire/CarFire/CarFire.csproj | 2 + .../CarFire/Content/Maps/sandbox.cfmap | 14 +- .../CarFire/CarFire/Content/Maps/stable.cfmap | 7 +- CarFire/CarFire/CarFire/Display.cs | 34 +- CarFire/CarFire/CarFire/Game.cs | 386 +++++++++++------- CarFire/CarFire/CarFire/Human.cs | 26 +- CarFire/CarFire/CarFire/IEntity.cs | 45 ++ CarFire/CarFire/CarFire/IPlayer.cs | 8 +- CarFire/CarFire/CarFire/Map.cs | 39 +- CarFire/CarFire/CarFire/MovementManager.cs | 46 +++ CarFire/CarFire/CarFire/PathFinder.cs | 16 +- CarFire/CarFire/CarFire/SaberMonster.cs | 171 ++++++++ 12 files changed, 572 insertions(+), 222 deletions(-) create mode 100644 CarFire/CarFire/CarFire/IEntity.cs create mode 100644 CarFire/CarFire/CarFire/SaberMonster.cs diff --git a/CarFire/CarFire/CarFire/CarFire.csproj b/CarFire/CarFire/CarFire/CarFire.csproj index 6548035..0b85b76 100644 --- a/CarFire/CarFire/CarFire/CarFire.csproj +++ b/CarFire/CarFire/CarFire/CarFire.csproj @@ -84,6 +84,8 @@ + + diff --git a/CarFire/CarFire/CarFire/Content/Maps/sandbox.cfmap b/CarFire/CarFire/CarFire/Content/Maps/sandbox.cfmap index e663d50..28050cf 100644 --- a/CarFire/CarFire/CarFire/Content/Maps/sandbox.cfmap +++ b/CarFire/CarFire/CarFire/Content/Maps/sandbox.cfmap @@ -13,12 +13,12 @@ [A] type = SaberMonster + speed = 10 path = [1,1] [5,6] wait(5) [45,2] - loop = true create = [5,7] [2,3] -[B] - condition = has(key) - event = wait(2) remove([2,6]) remove([2,7]) +;[B] +; condition = has(key) +; event = wait(2) remove([2,6]) remove([2,7]) ; Function ideas: ; has(entity) Player has given entity in his inventory. @@ -32,13 +32,13 @@ +------------------------------------------------------------------------------+ | | | 1 | -| B | +| | | 2 +---- | | | | -| 3 | A | +| 3 | | | | | | 4 | | -| | | +| A | | +-------------+----+ | | | | | | | diff --git a/CarFire/CarFire/CarFire/Content/Maps/stable.cfmap b/CarFire/CarFire/CarFire/Content/Maps/stable.cfmap index fe5a7e2..385626c 100644 --- a/CarFire/CarFire/CarFire/Content/Maps/stable.cfmap +++ b/CarFire/CarFire/CarFire/Content/Maps/stable.cfmap @@ -8,6 +8,11 @@ tileset = FuturisticBuilding numplayers = <1,4> +[M] + type = SaberMonster + speed = 10 + path = [16,8] [20,6] wait(5) [56,9] [71,17] [75,2] [11,13] + [maptable] +------------------------------------------------------------------------------+ | | @@ -21,7 +26,7 @@ | | | | +-------------+----+ | \ / | | | | \ / | -| | | \ / | +| M | | \ / | | | | \ / | | | \ @ # $ % ^ & * ( ) | | | | +-------+ | / \ | diff --git a/CarFire/CarFire/CarFire/Display.cs b/CarFire/CarFire/CarFire/Display.cs index 9fecb5b..aceadfe 100644 --- a/CarFire/CarFire/CarFire/Display.cs +++ b/CarFire/CarFire/CarFire/Display.cs @@ -26,14 +26,13 @@ namespace CarFire IPlayer[] mCharacters = new IPlayer[4]; Texture2D everything; Texture2D projectile1; - Map mMap; - int currentCenterX = 5; - int currentCenterY = 5; + Game mGame; #if SINGLE_TEST List mLastPressedKeys = new List(); #endif - public Display() + public Display(Game game) { + mGame = game; /* mMap = aMap; mCharacters = characters; @@ -48,9 +47,6 @@ namespace CarFire { everything = contentManager.Load("cs"); projectile1 = contentManager.Load("projectile"); - mMap = contentManager.Load("Maps/stable"); - Map.DefaultTile = contentManager.Load("default"); - mMap.CenterCell = new Vector2(currentCenterX,currentCenterY); } /// @@ -99,7 +95,7 @@ namespace CarFire for (int i = 0; i < mProjectiles.Count; i++ ) { bool removed = false; - if (!mMap.IsCellOpen(new Point(mProjectiles[i].Coordinates.X, mProjectiles[i].Coordinates.Y))) + if (!mGame.State.Map.IsCellOpen(new Point(mProjectiles[i].Coordinates.X, mProjectiles[i].Coordinates.Y))) { mProjectiles.RemoveAt(i); @@ -127,32 +123,32 @@ namespace CarFire } } //Update input for each player - for (int i = 0; i < 4; i++) + for (int i = 0; i < mGame.State.NumberOfPlayers; i++) { //If player has not selected a player yet let them select one. if (mCharacters[i] == null) { - if (state.keysDown[i].Contains(Keys.Enter)) + if (mGame.State.GetKeysDown(i).Contains(Keys.Enter)) { - mCharacters[i] = new Human(mMap, "", everything, projectile1, this, mMap.GetStartingPositionForPlayer(i+1)); + mCharacters[i] = new Human(mGame, "", everything, projectile1, this, mGame.State.Map.GetStartingPositionForPlayer(i + 1)); } } //Regular player input updates else { - mCharacters[i].MovePlayer(timespan, state.keysDown[i]); + mCharacters[i].MovePlayer(timespan, mGame.State.GetKeysDown(i)); } } if (mCharacters[0] != null) { - mMap.CenterCell = mCharacters[0].Position; + mGame.State.Map.CenterCell = mCharacters[0].Position; } //Handle wall collisions of projectiles again... for (int i = 0; i < mProjectiles.Count; i++) { - if (!mMap.IsCellOpen(new Point(mProjectiles[i].Coordinates.X, mProjectiles[i].Coordinates.Y))) + if (!mGame.State.Map.IsCellOpen(new Point(mProjectiles[i].Coordinates.X, mProjectiles[i].Coordinates.Y))) { mProjectiles.RemoveAt(i); i--; @@ -161,8 +157,8 @@ namespace CarFire } #if INGAME_ZOOM - if (Keyboard.GetState().IsKeyDown(Keys.PageUp)) mMap.Zoom = mMap.Zoom + 0.5f; - if (Keyboard.GetState().IsKeyDown(Keys.PageDown)) mMap.Zoom = mMap.Zoom - 0.5f; + if (Keyboard.GetState().IsKeyDown(Keys.PageUp)) mGame.State.Map.Zoom = mGame.State.Map.Zoom + 0.5f; + if (Keyboard.GetState().IsKeyDown(Keys.PageDown)) mGame.State.Map.Zoom = mGame.State.Map.Zoom - 0.5f; #endif } @@ -172,12 +168,14 @@ namespace CarFire /// Used to draw with public void Draw(SpriteBatch spriteBatch) { - mMap.Draw(spriteBatch); + mGame.State.Map.Draw(spriteBatch); + mGame.State.Entities.ForEach(delegate(IEntity e) { e.Draw(spriteBatch); }); + foreach(Projectile projectile in mProjectiles) { projectile.Draw(spriteBatch); } - for(int i = 0; i < 4; i++)//IPlayer character in mCharacters) + for(int i = 0; i < mGame.State.NumberOfPlayers; i++)//IPlayer character in mCharacters) { if (mCharacters[i] != null) diff --git a/CarFire/CarFire/CarFire/Game.cs b/CarFire/CarFire/CarFire/Game.cs index b1b7238..fa3fbd1 100644 --- a/CarFire/CarFire/CarFire/Game.cs +++ b/CarFire/CarFire/CarFire/Game.cs @@ -9,203 +9,258 @@ using Microsoft.Xna.Framework.Input; namespace CarFire { - // Everything in objects built from this class represent the critical game state + /// + /// Container class for the whole state of the game. + /// public class GameState { - public long frameNumber; + #region Public Properties - private long checksum; - public long Checksum { get { return checksum; } } + public long FrameNumber { get { return mFrameNumber; } } - public bool[] isGameOver; - public bool[] isTerminated; + public long Checksum { get { return mChecksum; } } - // Since this is not a game, I'll just keep track of the user inputs as the game state. + public int NumberOfPlayers { get { return mNumberOfPlayers; } } - public int[] mouseLocationX; - public int[] mouseLocationY; - public bool[] mouseButton; - public List[] keysDown; - public int[] keypressCount; + public Map Map; + public List Entities = new List(); - public long elapsedTime; + #endregion - /* Constructor */ - public GameState() - { - frameNumber = 0; - checksum = 0; - isGameOver = new bool[4]; - isTerminated = new bool[4]; + #region Public Methods - mouseLocationX = new int[4]; - mouseLocationY = new int[4]; - mouseButton = new bool[4]; - keysDown = new List[4]; - for (int i = 0; i < 4; i++) - keysDown[i] = new List(); - keypressCount = new int[4]; + /// + /// Construct a game state container with the number of players. + /// + /// Number of players. + public GameState(int numPlayers) + { + mNumberOfPlayers = numPlayers; + mFrameNumber = 0; - elapsedTime = 0; + mIsGameOver = new bool[numPlayers]; + mIsTerminated = new bool[numPlayers]; - checksum = 0; + mMouseLocation = new Point[numPlayers]; + mMouseButton = new bool[numPlayers]; + mKeysDown = new List[numPlayers]; + for (int i = 0; i < numPlayers; i++) mKeysDown[i] = new List(); + + mKeypressCount = new int[numPlayers]; + mElapsedTime = 0; + mChecksum = 0; } - /* The game engine! */ - public void advanceFrame(NextInputs inputs, long milliseconds) + + /// + /// Should be called by the Game class to advance the state + /// to the next frame. + /// + /// The inputs that occurred to be + /// applied this coming frame. + /// Milliseconds; used for the checksum. + public void AdvanceFrame(NextInputs inputs, long milliseconds) { - // Advance frame number - frameNumber++; - - // Advance game - for the test harness, just record statistics and input states. + mFrameNumber++; + mElapsedTime += milliseconds; - elapsedTime += milliseconds; - - for (int player = 0; player < 4; player++) + for (int player = 0; player < NumberOfPlayers; player++) { + if (inputs.IsMousePressedChanged[player]) + { + mMouseButton[player] = inputs.MousePressed[player]; + } - //if (isGameOver[player]) - // continue; - - if (inputs.mousePressedChanged[player]) - mouseButton[player] = inputs.mousePressed[player]; - - if (inputs.mouseLocationChanged[player]) + if (inputs.IsMouseLocationChanged[player]) { - mouseLocationX[player] = inputs.mouseLocationX[player]; - mouseLocationY[player] = inputs.mouseLocationY[player]; + mMouseLocation[player] = inputs.MouseLocation[player]; } - foreach (Keys k in inputs.keysPressed[player]) - if (!keysDown[player].Contains(k)) + foreach (Keys k in inputs.KeysPressed[player]) + { + if (!mKeysDown[player].Contains(k)) { - keysDown[player].Add(k); - keypressCount[player]++; + mKeysDown[player].Add(k); + mKeypressCount[player]++; } + } - foreach (Keys k in inputs.keysReleased[player]) - keysDown[player].Remove(k); - - // If the mouse was pressed for a certain player, activate game over or terminated states as appropriate + foreach (Keys k in inputs.KeysReleased[player]) mKeysDown[player].Remove(k); + } - if (inputs.mousePressed[player]) - for (int p = 0; p < 4; p++) - { - int x = 200 * p + 10; - int y = 220; - - if (mouseLocationX[player] >= x && mouseLocationY[player] >= y && - mouseLocationX[player] < x + 25 && mouseLocationY[player] < y + 25) - { - isGameOver[p] = true; - } - y += 25; - if (mouseLocationX[player] >= x && mouseLocationY[player] >= y && - mouseLocationX[player] < x + 25 && mouseLocationY[player] < y + 25) - { - isGameOver[p] = true; - isTerminated[p] = true; - } - } - + ComputeChecksum(); + } - } - // Advance the checksum. + /// + /// Get the mouse location for a player. + /// + /// Player Number. + /// Mouse location. + public Point GetMouseLocation(int playerNum) + { + return mMouseLocation[playerNum]; + } - computeChecksum(); + /// + /// Get the mouse button state for a player. + /// + /// Player number. + /// Mouse button state.. + public bool GetMouseButton(int playerNum) + { + return mMouseButton[playerNum]; + } + /// + /// Get the keyboard state for a player. + /// + /// Player number. + /// Keyboard state. + public List GetKeysDown(int playerNum) + { + return mKeysDown[playerNum]; } - /* Just hash the values */ - private long computeChecksum() + #endregion + + + #region Private Methods + + // Calculates a checksum for debugging network synchronization issues. + long ComputeChecksum() { - checksum += frameNumber; - for (int i = 0; i < 4; i++) + mChecksum += FrameNumber; + for (int i = 0; i < NumberOfPlayers; i++) { - checksum = checksum + keypressCount[i]; - checksum = checksum * 3 + (isGameOver[i] ? 1 : 2); - checksum = checksum * 3 + (isTerminated[i] ? 1 : 2); - foreach (Keys k in keysDown[i]) - checksum = checksum * 257 + (int)k; - checksum = checksum * 25789 + mouseLocationX[i] * 259 + mouseLocationY[i] + 375; - checksum = checksum * 3 + (mouseButton[i] ? 1 : 2); + mChecksum = mChecksum + mKeypressCount[i]; + mChecksum = mChecksum * 3 + (mIsGameOver[i] ? 1 : 2); + mChecksum = mChecksum * 3 + (mIsTerminated[i] ? 1 : 2); + foreach (Keys k in mKeysDown[i]) + mChecksum = mChecksum * 257 + (int)k; + mChecksum = mChecksum * 25789 + mMouseLocation[i].X * 259 + mMouseLocation[i].Y + 375; + mChecksum = mChecksum * 3 + (mMouseButton[i] ? 1 : 2); } - checksum += elapsedTime; + mChecksum += mElapsedTime; - return checksum; + return mChecksum; } + + #endregion + + + #region Private Variables + + int mNumberOfPlayers; + public Point[] mMouseLocation; + public bool[] mMouseButton; + public List[] mKeysDown; + + long mFrameNumber; + + bool[] mIsGameOver; + bool[] mIsTerminated; + + int[] mKeypressCount; + long mElapsedTime; + long mChecksum; + + #endregion } - //code from Prof Jensen's TestHarness - // This class encapsulates inputs from the players. + + /// + /// Container class for all the inputs for a single frame. + /// public class NextInputs { - public List[] keysPressed; - public List[] keysReleased; - public int[] mouseLocationX; - public int[] mouseLocationY; - public bool[] mouseLocationChanged; - public bool[] mousePressed; - public bool[] mousePressedChanged; - - public NextInputs() + public List[] KeysPressed; + public List[] KeysReleased; + public Point[] MouseLocation; + public bool[] IsMouseLocationChanged; + public bool[] MousePressed; + public bool[] IsMousePressedChanged; + + public NextInputs(int numPlayers) { - keysPressed = new List[4]; - keysReleased = new List[4]; - mouseLocationX = new int[4]; - mouseLocationY = new int[4]; - mouseLocationChanged = new bool[4]; - mousePressed = new bool[4]; - mousePressedChanged = new bool[4]; - for (int i = 0; i < 4; i++) - keysPressed[i] = new List(); - for (int i = 0; i < 4; i++) - keysReleased[i] = new List(); + KeysPressed = new List[numPlayers]; + KeysReleased = new List[numPlayers]; + IsMouseLocationChanged = new bool[numPlayers]; + MousePressed = new bool[numPlayers]; + IsMousePressedChanged = new bool[numPlayers]; + for (int i = 0; i < numPlayers; i++) KeysPressed[i] = new List(); + for (int i = 0; i < numPlayers; i++) KeysReleased[i] = new List(); } - } + + /// + /// The big kahuna. + /// public class Game : IDeterministicGame { - #region IDeterministicGame Members - List mPlayers; - NextInputs inputs; - Object[] playerIdentifiers; - Display mDisplay; - Map mMap; + #region Public Properties + + /// + /// Get the content manager associated with this game. + /// + public ContentManager ContentManager { get { return mContentManager; } } + + /// + /// Get the state. + /// + public GameState State; + + public bool[,] Grid + { + get + { + bool[,] grid = State.Map.Grid; + foreach (IEntity entity in State.Entities) + { + Point coordinates = entity.Coordinates; + grid[coordinates.X, coordinates.Y] = true; + } + return grid; + } + } + + #endregion + - GameState state; - int thisPlayerID; + #region Public Methods + + public bool IsCellOpen(Point point) + { + if (!State.Map.IsCellOpen(point)) return false; + foreach (IEntity entity in State.Entities) + { + if (entity.Coordinates == point) return false; + } + return true; + } public Game() { - mDisplay = new Display(); - mPlayers = new List(); - playerIdentifiers = new Object[4]; + mDisplay = new Display(this); } public void LoadContent(ContentManager contentManager) { - //Texture2D everything = contentManager.Load("default"); + mContentManager = contentManager; mDisplay.LoadContent(contentManager); - int currentCenterX = 5; //Creates a map like the one in Display - int currentCenterY = 5; - mMap = contentManager.Load("Maps/stable"); - Map.DefaultTile = contentManager.Load("default"); - mMap.CenterCell = new Vector2(currentCenterX, currentCenterY); - } public void UnloadContent() { } - private int idPlayer(Object playerIdentifier) + private int GetPlayerNumber(Object playerIdentifier) { - for (int i = 0; i < playerIdentifiers.Length; i++) - if (playerIdentifiers[i] == playerIdentifier) - return i; + for (int i = 0; i < mPlayerIdentifiers.Length; i++) + { + if (mPlayerIdentifiers[i] == playerIdentifier) return i; + } throw new Exception("Illegal player identifier" + playerIdentifier); } @@ -224,21 +279,23 @@ namespace CarFire get { return 4; } } - public void ResetGame(object[] PlayerIdentifiers, object thisPlayer) + public void ResetGame(object[] playerIdentifiers, object thisPlayer) { - // Now the test harness will at least run with less than 4 players... - int numPlayers = PlayerIdentifiers.Count(); - for (int i = 0; i < numPlayers; i++) - this.playerIdentifiers[i] = PlayerIdentifiers[i]; + int numPlayers = playerIdentifiers.Count(); - // Create new game state and inputs objects. + mPlayerIdentifiers = new object[numPlayers]; + for (int i = 0; i < numPlayers; i++) mPlayerIdentifiers[i] = playerIdentifiers[i]; - state = new GameState(); - inputs = new NextInputs(); + mThisPlayerID = GetPlayerNumber(thisPlayer); - // Record 'this' player. + State = new GameState(numPlayers); + mInputs = new NextInputs(numPlayers); + + State.Map = mContentManager.Load("Maps/stable"); + State.Map.Game = this; + State.Entities = State.Map.GetAllEntities(); + Map.DefaultTile = mContentManager.Load("default"); - this.thisPlayerID = idPlayer(thisPlayer); /* mPlayers.Clear(); for (int i = 0; i < PlayerIdentifiers.Length; i++) @@ -255,13 +312,12 @@ namespace CarFire Point starting = mMap.GetStartingPositionForPlayer(i + 1); mPlayers[i].Spawn(new Vector2(starting.X, starting.Y)); } - */ - + */ } public long CurrentFrameNumber { - get { return state.frameNumber; } + get { return State.FrameNumber; } } public long CurrentChecksum @@ -272,13 +328,13 @@ namespace CarFire public void ApplyKeyInput(object playerIdentifier, Keys key, bool isKeyPressed) { //code from Prof Jensen's TestHarness - int player = idPlayer(playerIdentifier); + int player = GetPlayerNumber(playerIdentifier); - if (isKeyPressed && !inputs.keysPressed[player].Contains(key)) - inputs.keysPressed[player].Add(key); + if (isKeyPressed && !mInputs.KeysPressed[player].Contains(key)) + mInputs.KeysPressed[player].Add(key); - if (!isKeyPressed && !inputs.keysReleased[player].Contains(key)) - inputs.keysReleased[player].Add(key); + if (!isKeyPressed && !mInputs.KeysReleased[player].Contains(key)) + mInputs.KeysReleased[player].Add(key); } @@ -304,11 +360,12 @@ namespace CarFire public long Update(TimeSpan elapsedTime) { - state.advanceFrame(inputs, elapsedTime.Milliseconds); // Apply the inputs, advance game state. - mDisplay.Update(elapsedTime, state); - inputs = new NextInputs(); // Start with inputs cleared on the next frame. + State.AdvanceFrame(mInputs, elapsedTime.Milliseconds); // Apply the inputs, advance game state. + mDisplay.Update(elapsedTime, State); + State.Entities.ForEach(delegate(IEntity e) { e.Update(elapsedTime); }); + mInputs = new NextInputs(State.NumberOfPlayers); // Start with inputs cleared on the next frame. //mDisplay.Update(elapsedTime); - return state.frameNumber; + return State.FrameNumber; } @@ -319,5 +376,18 @@ namespace CarFire } #endregion + + + #region Private Variables + + Display mDisplay; + + ContentManager mContentManager; + NextInputs mInputs; + + Object[] mPlayerIdentifiers; + int mThisPlayerID; + + #endregion } } diff --git a/CarFire/CarFire/CarFire/Human.cs b/CarFire/CarFire/CarFire/Human.cs index 519d9d0..08d962a 100644 --- a/CarFire/CarFire/CarFire/Human.cs +++ b/CarFire/CarFire/CarFire/Human.cs @@ -22,7 +22,7 @@ namespace CarFire const int shootCoolDown = 10; State state; String CharName; - Map theMap; + Game game; Texture2D charModel; Texture2D projectileModel; int health; @@ -40,9 +40,9 @@ namespace CarFire int projectileCoolDown; - public Human(Map _theMap, String Name, Texture2D model, Texture2D projectile, Display mDisplay, Point position) + public Human(Game theGame, String Name, Texture2D model, Texture2D projectile, Display mDisplay, Point position) { - theMap = _theMap; + game = theGame; CharName = Name; theDisplay = mDisplay; health = 100; @@ -55,7 +55,7 @@ namespace CarFire projectileSpeed = 30; // Speed is the number of grid cells you can move through per second. - mMotion = new MovementManager(position, 5.0f); + mMotion = new MovementManager(position, 8.0f); } public void LoadContent(ContentManager contentManager) @@ -65,26 +65,18 @@ namespace CarFire } - public void UnloadContent() - { - - } - public long Update(GameTime gameTime, NetworkManager networkGame) + public void Update(TimeSpan timeSpan) { - return 0; - } /// /// This method will draw a character to the screen. /// /// - /// - public long Draw(SpriteBatch spriteBatch) + public void Draw(SpriteBatch spriteBatch) { - Rectangle position = theMap.GetRectangleFromCoordinates(mMotion.Position); + Rectangle position = game.State.Map.GetRectangleFromCoordinates(mMotion.Position); spriteBatch.Draw(charModel, position, Color.White); - return 0; } public int Health { get { return health; } } @@ -124,7 +116,7 @@ namespace CarFire Point destination = MovementManager.GetNeighborCell(mMotion.Coordinates, moveLeft, moveRight, moveUp, moveDown); if (!keysPressed.Contains(Keys.LeftControl)) { - if (theMap.IsCellOpen(destination)) + if (game.IsCellOpen(destination)) { mMotion.Update(timeSpan, moveLeft, moveRight, moveUp, moveDown); } @@ -173,7 +165,7 @@ namespace CarFire toShoot.Normalize(); toShoot *= projectileSpeed; projectileCoolDown = shootCoolDown; - theDisplay.AddProjectiles(new Projectile(theMap, projectileModel, + theDisplay.AddProjectiles(new Projectile(game.State.Map, projectileModel, toShoot, new Point(startX, startY))); /* diff --git a/CarFire/CarFire/CarFire/IEntity.cs b/CarFire/CarFire/CarFire/IEntity.cs new file mode 100644 index 0000000..cfa72c2 --- /dev/null +++ b/CarFire/CarFire/CarFire/IEntity.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Content; +using Microsoft.Xna.Framework.Graphics; + +namespace CarFire +{ + /// + /// A class to represent any object that can exist as an + /// independent piece of the game. + /// + public interface IEntity + { + /// + /// Load the resources the entity needs. + /// + /// The foobar. + void LoadContent(ContentManager contentManager); + + /// + /// Update the entity's state. + /// + /// The timeslice. + void Update(TimeSpan timeSpan); + + /// + /// Render the entity on the screen. + /// + /// The widget. + void Draw(SpriteBatch spriteBatch); + + /// + /// Get the actual position. + /// + Vector2 Position { get; } + + /// + /// Get the coordinates on the grid. + /// + Point Coordinates { get; } + } +} diff --git a/CarFire/CarFire/CarFire/IPlayer.cs b/CarFire/CarFire/CarFire/IPlayer.cs index a65ced5..f90fc15 100644 --- a/CarFire/CarFire/CarFire/IPlayer.cs +++ b/CarFire/CarFire/CarFire/IPlayer.cs @@ -9,16 +9,10 @@ using Microsoft.Xna.Framework.Input; namespace CarFire { - public interface ICharacter + public interface ICharacter : IEntity { - void LoadContent(ContentManager contentManager); - void UnloadContent(); - long Update(GameTime gameTime, NetworkManager networkGame); - long Draw(SpriteBatch spriteBatch); int Health { get; } void causeDamageTo(int amount); - Vector2 Position { get; } - Point Coordinates { get; } } public interface IPlayer : ICharacter diff --git a/CarFire/CarFire/CarFire/Map.cs b/CarFire/CarFire/CarFire/Map.cs index 93dc5ef..8c744ba 100644 --- a/CarFire/CarFire/CarFire/Map.cs +++ b/CarFire/CarFire/CarFire/Map.cs @@ -166,6 +166,16 @@ namespace CarFire set { mView.Zoom = value; } } + + /// + /// Get and set the associated game object. + /// + public Game Game + { + get { return mData.Game; } + set { mData.Game = value; } + } + #endregion @@ -278,7 +288,7 @@ namespace CarFire /// thrown if there are entities without associated classes. /// /// List of entity objects loaded. - public List GetAllEntities() + public List GetAllEntities() { return mData.GetAllEntities(); } @@ -358,6 +368,8 @@ namespace CarFire public Point[] PlayerPositions { get { return mPlayerPositions; } } public bool[,] Grid { get { return mBooleanGrid; } } + public Game Game; + public Model(Metadata metadata, char[,] grid, char defaultTile, List entities, Point[] playerPositions) @@ -426,9 +438,9 @@ namespace CarFire } - public List GetAllEntities() + public List GetAllEntities() { - List list = new List(); + List list = new List(); foreach (RawEntity raw in mEntities) { @@ -436,16 +448,20 @@ namespace CarFire { string typename = raw.Attributes["type"]; - object[] args = new object[3]; + object[] args = new object[4]; args[0] = raw.Id; args[1] = raw.Position; args[2] = raw.Attributes; + args[3] = Game; try { - - object entity = Activator.CreateInstance(System.Type.GetType("CarFire." + typename), args); - if (entity != null) list.Add(entity); + IEntity entity = (IEntity)Activator.CreateInstance(System.Type.GetType("CarFire." + typename), args); + if (entity != null) + { + entity.LoadContent(Game.ContentManager); + list.Add(entity); + } else throw new RuntimeException(); } #pragma warning disable 0168 @@ -474,13 +490,18 @@ namespace CarFire { if (raw.Attributes.ContainsKey("type") && typename == raw.Attributes["type"]) { - object[] args = new object[3]; + object[] args = new object[4]; args[0] = raw.Id; args[1] = raw.Position; args[2] = raw.Attributes; + args[3] = Game; T entity = (T)Activator.CreateInstance(type, args); - if (entity != null) list.Add(entity); + if (entity != null) + { + ((IEntity)entity).LoadContent(Game.ContentManager); + list.Add(entity); + } else throw new RuntimeException("Entity of type " + typename + " not loaded because an entity class can't be found."); } } diff --git a/CarFire/CarFire/CarFire/MovementManager.cs b/CarFire/CarFire/CarFire/MovementManager.cs index e700203..3511e42 100644 --- a/CarFire/CarFire/CarFire/MovementManager.cs +++ b/CarFire/CarFire/CarFire/MovementManager.cs @@ -103,6 +103,24 @@ namespace CarFire Update(timeSpan, false, false, false, false); } + /// + /// Update the movement manager with the timeslice and a direction. + /// + /// The timeslice. + /// Direction you want to move. + public void Update(TimeSpan timeSpan, Direction direction) + { + if (direction == Direction.Left) Update(timeSpan, true, false, false, false); + else if (direction == Direction.UpperLeft) Update(timeSpan, true, false, true, false); + else if (direction == Direction.Up) Update(timeSpan, false, false, true, false); + else if (direction == Direction.UpperRight) Update(timeSpan, false, true, true, false); + else if (direction == Direction.Right) Update(timeSpan, false, true, false, false); + else if (direction == Direction.LowerRight) Update(timeSpan, false, true, false, true); + else if (direction == Direction.Down) Update(timeSpan, false, false, false, true); + else if (direction == Direction.LowerLeft) Update(timeSpan, true, false, false, true); + else Update(timeSpan); + } + /// /// Update the movement manager with the timeslice and the directions /// the object is supposed to go. The directions will be ignored if the @@ -221,6 +239,34 @@ namespace CarFire else return Direction.None; } + /// + /// Helper method to get the general Direction type if you want to move + /// from one cell to another. + /// Starting point. + /// Destination point. + /// The direction toward the cell. + public static Direction GetDirection(Point a, Point b) + { + int dx = b.X - a.X; + int dy = b.Y - a.Y; + + if (dx < 0) + { + if (dy < 0) return Direction.UpperLeft; + else if (dy > 0) return Direction.LowerLeft; + else return Direction.Left; + } + else if (dx > 0) + { + if (dy < 0) return Direction.UpperRight; + else if (dy > 0) return Direction.LowerRight; + else return Direction.Right; + } + else if (dy < 0) return Direction.Up; + else if (dy > 0) return Direction.Down; + else return Direction.None; + } + #endregion diff --git a/CarFire/CarFire/CarFire/PathFinder.cs b/CarFire/CarFire/CarFire/PathFinder.cs index 9172438..53e148d 100644 --- a/CarFire/CarFire/CarFire/PathFinder.cs +++ b/CarFire/CarFire/CarFire/PathFinder.cs @@ -1,4 +1,8 @@ -using System; + +// Uncomment this to disable diagonal movemet. +//#define ALLOW_DIAGONAL_MOVEMENT + +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -136,14 +140,16 @@ namespace CarFire } List neighbors = new List(8); + neighbors.Add(new Point(cell.Point.X, cell.Point.Y - 1)); + neighbors.Add(new Point(cell.Point.X - 1, cell.Point.Y)); + neighbors.Add(new Point(cell.Point.X + 1, cell.Point.Y)); + neighbors.Add(new Point(cell.Point.X, cell.Point.Y + 1)); +#if ALLOW_DIAGONAL_MOVEMENT neighbors.Add(new Point(cell.Point.X - 1, cell.Point.Y - 1)); - neighbors.Add(new Point(cell.Point.X + 0, cell.Point.Y - 1)); neighbors.Add(new Point(cell.Point.X + 1, cell.Point.Y - 1)); - neighbors.Add(new Point(cell.Point.X - 1, cell.Point.Y + 0)); - neighbors.Add(new Point(cell.Point.X + 1, cell.Point.Y + 0)); neighbors.Add(new Point(cell.Point.X - 1, cell.Point.Y + 1)); - neighbors.Add(new Point(cell.Point.X + 0, cell.Point.Y + 1)); neighbors.Add(new Point(cell.Point.X + 1, cell.Point.Y + 1)); +#endif foreach (Point point in neighbors) { Cell inQueue = mCells[point.X, point.Y]; diff --git a/CarFire/CarFire/CarFire/SaberMonster.cs b/CarFire/CarFire/CarFire/SaberMonster.cs new file mode 100644 index 0000000..06fd127 --- /dev/null +++ b/CarFire/CarFire/CarFire/SaberMonster.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Content; +using Microsoft.Xna.Framework.Graphics; + +namespace CarFire +{ + public enum AiState + { + Standing, + Pacing, + Chasing, + Dazed, + Fighting, + Retreating + } + + + public class SaberMonster : IMonster + { + public SaberMonster(char identifier, Point position, Dictionary info, Game game) + { + mId = identifier; + mMotion = new MovementManager(position); + mGame = game; + + string speedString; + if (info.TryGetValue("speed", out speedString)) + { + int? speed = Parse.Integer(speedString); + if (speed != null) mMotion.Speed = speed.Value; + } + + // Get the "idle path" coordinates loaded from the map. + string idlePath; + if (info.TryGetValue("path", out idlePath)) + { + string[] idlePathPoints = Parse.List(idlePath); + foreach (string pathPoint in idlePathPoints) + { + Point? point = Parse.Coordinates(pathPoint); + if (point != null) mIdlePath.Add(point.Value); + } + } + + StartPacing(); + } + + + public void StartPacing() + { + mState = AiState.Pacing; + + if (mIdlePath.Count == 0) return; + + // Determine the best (closest) waypoint to start at. + mIdlePathIndex = 0; + int closest = int.MaxValue; + for (int i = 0; i < mIdlePath.Count; i++) + { + int distance = PathFinder.GetManhattanDistance(Coordinates, mIdlePath[i]); + if (distance < closest) + { + mIdlePathIndex = 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; + } + + 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; + } + + if (mPath[mPathIndex % mPath.Count] == mMotion.Coordinates) + { + mPathIndex++; + mPathDirection = MovementManager.GetDirection(mMotion.Coordinates, mPath[mPathIndex % mPath.Count]); + } + + return mPathDirection; + } + + + #region IMonster Members + + public bool visible + { + get { throw new NotImplementedException(); } + } + + #endregion + + + #region ICharacter Members + + public void LoadContent(ContentManager contentManager) + { + mTexture = contentManager.Load("menuItem"); + } + + public void Update(TimeSpan timeSpan) + { + if (mState == AiState.Pacing) + { + mMotion.Update(timeSpan, GetDirectionToNextCell()); + } + } + + public void Draw(SpriteBatch spriteBatch) + { + Rectangle position = mGame.State.Map.GetRectangleFromCoordinates(mMotion.Position); + spriteBatch.Draw(mTexture, position, Color.White); + } + + public int Health + { + get { throw new NotImplementedException(); } + } + + public void causeDamageTo(int amount) + { + throw new NotImplementedException(); + } + + public Vector2 Position { get { return mMotion.Position; } } + + public Point Coordinates { get { return mMotion.Coordinates; } } + + #endregion + + + #region Private Variables + + Game mGame; + + char mId; + MovementManager mMotion; + + List mIdlePath = new List(); + int mIdlePathIndex; + + List mPath; + int mPathIndex; + Direction mPathDirection; + + AiState mState; + + Texture2D mTexture; + + #endregion + } +} -- 2.43.0