From 08f41ef45f3c41ca6302150bc6d5270c8e7143db Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 26 Apr 2010 22:07:09 +0000 Subject: [PATCH] New IEntity properties: Coordinates (set), Identifier. Loadable entities should now keep the identifier character to implement the Identifier property. Documented Game methods and added new ones: AdvanceLevel, Reset, RemoveEntity. New Map property: Map.Next gets the name of the next map to load. Parse.Constant change: return type is now nullable and will return null if parsing fails. Refined triggers/scripts: added trigger options, more consistent behavior, many more functions. Added key/lock design concept to level1, using Player.Inventory. git-svn-id: https://bd85.net/svn/cs3505_group@154 92bb83a3-7c8f-8a45-bc97-515c4e399668 --- .../CarFire/CarFire/Content/Maps/level1.cfmap | 19 +- .../CarFire/Content/Maps/sandbox.cfmap | 3 - CarFire/CarFire/CarFire/Game.cs | 47 ++++ CarFire/CarFire/CarFire/IEntity.cs | 7 +- CarFire/CarFire/CarFire/Key.cs | 12 +- CarFire/CarFire/CarFire/Map.cs | 6 + CarFire/CarFire/CarFire/MapReader.cs | 11 +- CarFire/CarFire/CarFire/Parse.cs | 6 +- CarFire/CarFire/CarFire/Player.cs | 3 +- CarFire/CarFire/CarFire/SaberMonster.cs | 10 +- CarFire/CarFire/CarFire/Script.cs | 50 +--- CarFire/CarFire/CarFire/Trigger.cs | 266 ++++++++++++++++-- 12 files changed, 346 insertions(+), 94 deletions(-) diff --git a/CarFire/CarFire/CarFire/Content/Maps/level1.cfmap b/CarFire/CarFire/CarFire/Content/Maps/level1.cfmap index 7431e08..20e1da9 100644 --- a/CarFire/CarFire/CarFire/Content/Maps/level1.cfmap +++ b/CarFire/CarFire/CarFire/Content/Maps/level1.cfmap @@ -5,6 +5,7 @@ dimensions = [80,21] tileset = Warehouse numplayers = <1,4> + next = maze [M] type = SaberMonster @@ -13,22 +14,22 @@ [k] type = Key - condition = True() - event = PickUp() + script = PickUp() + options = Once -[T] +[L] type = Trigger - condition = Print("Trigger tested.") False() - event = Print("Trigger fired!") + script = Print("Checking for key...") Has(k) Drop(k) Print("KEY DROPPED") Wait(15) Set([9,3] %) Set([10,3] %) Set([11,3] %) Set([9,4] %) Set([11,4] %) Set([9,5] %) Set([10,5] %) Set([11,5] %) Print("Haha you're trapped.") Wait(200) Set([9,3] >) Set([10,3] >) Set([11,3] >) Set([9,4] >) Set([11,4] >) Set([9,5] >) Set([10,5] >) Set([11,5] >) Print("Just kidding.") Wait(500) Next() + options = Once [maptable] +------------------------------------------------------------------------------+ | | | 1 2 | -| T | -| 3 4 +---- | -| | M | -| k | | +| ... | +| 3 4 .L. +---- | +| ... | M | +| k | | | |------------------------------------------ | | | | | | | | | diff --git a/CarFire/CarFire/CarFire/Content/Maps/sandbox.cfmap b/CarFire/CarFire/CarFire/Content/Maps/sandbox.cfmap index 28050cf..0481034 100644 --- a/CarFire/CarFire/CarFire/Content/Maps/sandbox.cfmap +++ b/CarFire/CarFire/CarFire/Content/Maps/sandbox.cfmap @@ -21,9 +21,6 @@ ; event = wait(2) remove([2,6]) remove([2,7]) ; Function ideas: -; has(entity) Player has given entity in his inventory. -; wait(seconds) Pause for some number of seconds. -; remove(entity) Remove entities of a certain type from the game. ; remove(coord) Remove whatever is at the given coordinates. ; create(entity, coord) Create an entity at some location. ; play(soundname) Play a sound. diff --git a/CarFire/CarFire/CarFire/Game.cs b/CarFire/CarFire/CarFire/Game.cs index 1486e65..f78945b 100644 --- a/CarFire/CarFire/CarFire/Game.cs +++ b/CarFire/CarFire/CarFire/Game.cs @@ -240,6 +240,11 @@ namespace CarFire #region Public Methods + /// + /// Get an entity at a certain place on the map. + /// + /// The coordinates. + /// The entity, or null if none is at that location. public IEntity GetEntityAtCoordinates(Point point) { foreach (IEntity entity in State.Entities) @@ -249,6 +254,11 @@ namespace CarFire return null; } + /// + /// Get a player at a certain place on the map. + /// + /// The coordinates. + /// The player, or null if none is at that location. public Player GetPlayerAtCoordinates(Point point) { foreach (Player player in State.mCharacters) @@ -258,6 +268,12 @@ namespace CarFire return null; } + /// + /// Determine if a cell is open, depending on the static scenery + /// of the map and if there are any collidable entities. + /// + /// The coordinates. + /// True if the cell is open; false otherwise. public bool IsCellOpen(Point point) { if (!State.Map.IsCellOpen(point)) return false; @@ -266,6 +282,37 @@ namespace CarFire return true; } + /// + /// Remove a specific entity from the game. The entity can still + /// be tracked some other way, but it won't included when the game is + /// updating and drawing stuff. + /// + /// The entity. + /// The entity that was removed, or null if no entity was removed. + public IEntity RemoveEntity(IEntity entity) + { + if (State.Entities.Remove(entity)) return entity; + return null; + } + + /// + /// Move on to the next map, and advance the level. + /// + public void AdvanceLevel() + { + // TODO: Load the next map, etc... + } + + /// + /// Restart the current level. + /// + public void Reset() + { + State.Map.Reset(); + // TODO: Do other stuff to reset everything. + } + + public Game() { diff --git a/CarFire/CarFire/CarFire/IEntity.cs b/CarFire/CarFire/CarFire/IEntity.cs index e916f2a..76dcc19 100644 --- a/CarFire/CarFire/CarFire/IEntity.cs +++ b/CarFire/CarFire/CarFire/IEntity.cs @@ -45,6 +45,11 @@ namespace CarFire /// /// Get the coordinates on the grid. /// - Point Coordinates { get; } + Point Coordinates { get; set; } + + /// + /// Get the entity's identifier. + /// + char Identifier { get; } } } diff --git a/CarFire/CarFire/CarFire/Key.cs b/CarFire/CarFire/CarFire/Key.cs index 0fab663..cebad87 100644 --- a/CarFire/CarFire/CarFire/Key.cs +++ b/CarFire/CarFire/CarFire/Key.cs @@ -10,11 +10,12 @@ namespace CarFire { /// /// A key entity. Keys can be used to unlock doors... what a surprise. + /// All that stuff is handled by the trigger, though. /// public class Key : Trigger { #region Public Methods - + /// /// Construct a key entity. /// @@ -25,10 +26,9 @@ namespace CarFire public Key(char identifier, Point position, Dictionary info, Game game) : base(identifier, position, info, game) { - mPosition = new Vector2(position.X, position.Y); - mGame = game; + // No implementation needed. } - + public override void LoadContent(ContentManager contentManager) { mTexture = contentManager.Load("default"); @@ -36,7 +36,7 @@ namespace CarFire public override void Draw(SpriteBatch spriteBatch) { - Rectangle position = mGame.State.Map.GetRectangleFromCoordinates(mPosition); + Rectangle position = Game.State.Map.GetRectangleFromCoordinates(Position); spriteBatch.Draw(mTexture, position, Color.White); } @@ -46,8 +46,6 @@ namespace CarFire #region Private Variables Texture2D mTexture; - Game mGame; - Vector2 mPosition; #endregion } diff --git a/CarFire/CarFire/CarFire/Map.cs b/CarFire/CarFire/CarFire/Map.cs index 0178e2b..504a68c 100644 --- a/CarFire/CarFire/CarFire/Map.cs +++ b/CarFire/CarFire/CarFire/Map.cs @@ -46,6 +46,7 @@ namespace CarFire public string Name; public Mode Type; public string Author; + public string Next; public HashSet NumPlayers = new HashSet(); public string Tileset; public int GridWidth; @@ -82,6 +83,11 @@ namespace CarFire /// public string Author { get { return mData.Metadata.Author; } } + /// + /// Get the name of the next map to load after this one. + /// + public string Next { get { return mData.Metadata.Next; } } + /// /// Get a set of integers containing each allowable number of players. /// diff --git a/CarFire/CarFire/CarFire/MapReader.cs b/CarFire/CarFire/CarFire/MapReader.cs index 9c58cb3..347f6bd 100644 --- a/CarFire/CarFire/CarFire/MapReader.cs +++ b/CarFire/CarFire/CarFire/MapReader.cs @@ -321,10 +321,17 @@ namespace CarFire else throw new Exception("Unexpected value on line " + mInput.LineNumber + ": " + atom); } + public void set_next(string atom) + { + string value = Parse.String(atom); + if (value != null) mMetadata.Next = value; + else throw new Exception("Unexpected value on line " + mInput.LineNumber + ": " + atom); + } + public void set_type(string atom) { - Map.Mode value = Parse.Constant(atom); - if (value != default(Map.Mode)) mMetadata.Type = value; + Map.Mode? value = Parse.Constant(atom); + if (value != null) mMetadata.Type = value.Value; else throw new Exception("Unexpected type on line " + mInput.LineNumber + ": " + atom); } diff --git a/CarFire/CarFire/CarFire/Parse.cs b/CarFire/CarFire/CarFire/Parse.cs index 4c2abcc..ca98353 100644 --- a/CarFire/CarFire/CarFire/Parse.cs +++ b/CarFire/CarFire/CarFire/Parse.cs @@ -126,8 +126,8 @@ namespace CarFire /// /// An enumeration. /// Text. - /// The constant, or default(T) if parsing failed. - public static T Constant(string atom) + /// The constant, or null if parsing failed. + public static T? Constant(string atom) where T : struct { try { @@ -137,7 +137,7 @@ namespace CarFire catch (System.Exception ex) #pragma warning restore 0168 { - return default(T); + return null; } } diff --git a/CarFire/CarFire/CarFire/Player.cs b/CarFire/CarFire/CarFire/Player.cs index 9d6c6b4..b84b048 100644 --- a/CarFire/CarFire/CarFire/Player.cs +++ b/CarFire/CarFire/CarFire/Player.cs @@ -35,12 +35,13 @@ namespace CarFire public int PlayerIndex { get { return mPlayerIndex; } } public bool IsCollidable { get { return true; } } public Vector2 Position { get { return mMotion.Position; } } - public Point Coordinates { get { return mMotion.Coordinates; } + public Point Coordinates { get { return mMotion.Coordinates; } set { Coordinates = value; mMotion = new MovementManager(value, basePlayerSpeed); } } + public char Identifier { get { return mPlayerIndex.ToString()[0]; } } public int Damage { get { return playerDamage; } set { playerDamage = value; } } public List Inventory { get { return mInventory; } } #endregion diff --git a/CarFire/CarFire/CarFire/SaberMonster.cs b/CarFire/CarFire/CarFire/SaberMonster.cs index a3507c8..f417d52 100644 --- a/CarFire/CarFire/CarFire/SaberMonster.cs +++ b/CarFire/CarFire/CarFire/SaberMonster.cs @@ -221,7 +221,15 @@ namespace CarFire /// /// Get the grid coordinates. /// - public Point Coordinates { get { return mMotion.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 diff --git a/CarFire/CarFire/CarFire/Script.cs b/CarFire/CarFire/CarFire/Script.cs index 9676bbb..0cf9dbd 100644 --- a/CarFire/CarFire/CarFire/Script.cs +++ b/CarFire/CarFire/CarFire/Script.cs @@ -30,9 +30,9 @@ namespace CarFire /// /// The script code. /// A game reference. - public Script(string code, Game game) + public Script(string code, object bindings) { - mImpl = new Impl(game); + mBindings = bindings; string[] functions = Parse.List(code); if (functions != null) @@ -87,6 +87,12 @@ namespace CarFire return result; } + public void Reset() + { + mIsRunning = false; + + } + #endregion @@ -106,7 +112,7 @@ namespace CarFire object[] args = new object[2]; args[0] = player; args[1] = mFunctions[index].Arguments; - return (bool)typeof(Impl).InvokeMember(mFunctions[index].Name, BindingFlags.InvokeMethod, null, null, args); + return (bool)mBindings.GetType().InvokeMember(mFunctions[index].Name, BindingFlags.InvokeMethod, null, mBindings, args); } #pragma warning disable 0168 catch (System.MissingMethodException ex) @@ -121,42 +127,6 @@ namespace CarFire #region Private Types - class Impl - { - public static bool True(Player player, string[] args) - { - return true; - } - - public static bool False(Player player, string[] args) - { - return false; - } - - public static bool Has(Player player, string[] args) - { - return false; - } - - public static bool Print(Player player, string[] args) - { - foreach (string arg in args) - { - string line = Parse.String(arg); - if (line != null) Console.WriteLine(line); - } - return true; - } - - - public Impl(Game game) - { - mGame = game; - } - - Game mGame; - } - class Function { public string Name { get { return mName; } } @@ -177,7 +147,7 @@ namespace CarFire #region Private Variables - Impl mImpl; + object mBindings; List mFunctions = new List(); bool mIsRunning; int mRunningIndex; diff --git a/CarFire/CarFire/CarFire/Trigger.cs b/CarFire/CarFire/CarFire/Trigger.cs index e405aa9..6c92b03 100644 --- a/CarFire/CarFire/CarFire/Trigger.cs +++ b/CarFire/CarFire/CarFire/Trigger.cs @@ -8,10 +8,22 @@ using Microsoft.Xna.Framework.Content; namespace CarFire { + /// + /// Trigger options modify trigger behavior. + /// + [Flags] + public enum TriggerOptions + { + Default = 0x00, + Reset = 0x01, // The script will be reset each time. + Once = 0x02, // Trigger can only be fired once. + Continuous = 0x04, // Trigger is always checked each update. + } + + /// /// A trigger is an invisible entity whose only purpose is - /// to check a condition and run a script when the condition - /// is right. + /// to check for a player to step on it and then run a script. /// public class Trigger : IEntity { @@ -26,44 +38,53 @@ namespace CarFire /// The game reference. public Trigger(char identifier, Point position, Dictionary info, Game game) { + mId = identifier; mGame = game; - mPrevCondition = false; mCoordinates = position; - string condition; - if (info.TryGetValue("condition", out condition)) + Functions functions = new Functions(this, game); + + string script; + if (info.TryGetValue("script", out script)) { - mCondition = new Script(condition, game); + mScript = new Script(script, functions); } - else throw new Exception("Triggers must define a condition script."); + else throw new Exception("Triggers must define a script."); - string eventt; - if (info.TryGetValue("event", out eventt)) + string options; + if (info.TryGetValue("options", out options)) { - mEvent = new Script(eventt, game); + string[] list = Parse.List(options); + if (list != null) + { + foreach (string option in list) + { + TriggerOptions? flag = Parse.Constant(option); + if (flag != null) mFlags |= flag.Value; + } + } } - else throw new Exception("Triggers must define an event script."); } /// - /// Check the trigger condition and execute the event if the - /// condition evaluates to true. + /// Check for a player and execute the trigger script if + /// there is a player on this cell. /// public void Call() { - Player player = mGame.GetPlayerAtCoordinates(mCoordinates); - if (player != null) + if (!mIsExpired) { - bool condition = mCondition.Run(player); - if (condition && condition != mPrevCondition) + Player player = mGame.GetPlayerAtCoordinates(mCoordinates); + if (player != null) { - mEvent.Run(player); + if (!mIsFinished || (mFlags & TriggerOptions.Continuous) == TriggerOptions.Continuous) + { + mIsFinished = mScript.Run(player); + if (mIsFinished && (mFlags & TriggerOptions.Once) == TriggerOptions.Once) mIsExpired = true; + else if ((mFlags & TriggerOptions.Reset) == TriggerOptions.Reset) mScript.Reset(); + } } - mPrevCondition = condition; - } - else - { - mPrevCondition = false; + else mIsFinished = false; } } @@ -94,12 +115,199 @@ namespace CarFire public Vector2 Position { - get { return Vector2.Zero; } + get { return new Vector2(mCoordinates.X, mCoordinates.Y); } } public Point Coordinates { get { return mCoordinates; } + set { mCoordinates = value; } + } + + public char Identifier + { + get { return mId; } + } + + public Game Game + { + get { return mGame; } + } + + #endregion + + + #region Private Types + + /// + /// The script bindings. + /// + class Functions + { + // Always evaluates to true. + public bool True(Player player, string[] args) + { + return true; + } + + // Always evaluates to false. + public bool False(Player player, string[] args) + { + return false; + } + + // Check the player's inventory for an entity. + // Arg1: Entity identifier. + public bool Has(Player player, string[] args) + { + if (args.Length == 0) return false; + + char? entity = Parse.Char(args[0]); + if (entity != null) + { + foreach (IEntity item in player.Inventory) + { + if (entity == item.Identifier) return true; + } + } + return false; + } + + // Put the trigger in the player's inventory. + public bool PickUp(Player player, string[] args) + { + IEntity entity = mGame.RemoveEntity(mTrigger); + if (entity != null) + { + player.Inventory.Add(entity); + return true; + } + return false; + } + + // Drop an entity from the player's inventory here. + // Arg1: Entity identifier. + public bool Drop(Player player, string[] args) + { + if (args.Length == 0) return false; + + char? entity = Parse.Char(args[0]); + if (entity != null) + { + foreach (IEntity item in player.Inventory) + { + if (entity == item.Identifier) + { + player.Inventory.Remove(item); + mGame.State.Entities.Add(item); + item.Coordinates = mTrigger.Coordinates; + return true; + } + } + } + return false; + } + + // Use an entity in the player's inventory, disposing it. + // Arg1: Entity identifier. + public bool Use(Player player, string[] args) + { + if (args.Length == 0) return false; + + char? entity = Parse.Char(args[0]); + if (entity != null) + { + foreach (IEntity item in player.Inventory) + { + if (entity == item.Identifier) + { + player.Inventory.Remove(item); + return true; + } + } + } + return false; + } + + // Wait for a ceretain number of ticks. + // Arg1: Number of ticks. + public bool Wait(Player player, string[] args) + { + if (args.Length == 0) return false; + + if (mTicks == 0) + { + int? timer = Parse.Integer(args[0]); + if (timer != null) + { + mTicks = 1; + mTimerTicks = timer.Value; + } + } + else if (++mTicks >= mTimerTicks) + { + mTicks = 0; + return true; + } + return false; + } + + // Print each argument to the console as a string. + public bool Print(Player player, string[] args) + { + foreach (string arg in args) + { + string line = Parse.String(arg); + if (line != null) Console.WriteLine(line); + } + return true; + } + + // Change a cell's tile on the map. + // Arg1: The coordinates. + // Arg2: The character representing the tile. + public bool Set(Player player, string[] args) + { + if (args.Length < 2) return false; + + Point? coord = Parse.Coordinates(args[0]); + if (coord != null) + { + char? tile = Parse.Char(args[1]); + if (tile != null) + { + mGame.State.Map.SetCell(coord.Value, tile.Value); + return true; + } + } + return false; + } + + // Move on to the next level. + public bool Next(Player player, string[] args) + { + mGame.AdvanceLevel(); + return true; + } + + // Restart the current level. + public bool Reset(Player player, string[] args) + { + mGame.Reset(); + return true; + } + + + public Functions(IEntity trigger, Game game) + { + mTrigger = trigger; + mGame = game; + } + + IEntity mTrigger; + Game mGame; + int mTicks; + int mTimerTicks; } #endregion @@ -107,10 +315,14 @@ namespace CarFire #region Private Variables - Script mCondition; - Script mEvent; + Script mScript; + + TriggerOptions mFlags; + bool mIsFinished; + bool mIsExpired; + + char mId; Game mGame; - bool mPrevCondition; Point mCoordinates; #endregion -- 2.43.0