├── externals └── Equationator.dll ├── ExampleApplication ├── App.config ├── FakeEmitter.cs ├── FakeBulletManager.cs ├── FakeBullet.cs ├── example.xml ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── ExampleApplication.csproj ├── .gitmodules ├── GameManager.cs ├── PositionDelegate.cs ├── Tasks ├── SetDirectionTask.cs ├── SetSpeedTask.cs ├── VanishTask.cs ├── RepeatTask.cs ├── WaitTask.cs ├── ChangeSpeedTask.cs ├── ActionTask.cs ├── AccelTask.cs ├── ChangeDirectionTask.cs ├── BulletMLTask.cs └── FireTask.cs ├── Nodes ├── BulletNode.cs ├── DirectionNode.cs ├── ActionRefNode.cs ├── FireRefNode.cs ├── FireNode.cs ├── BulletRefNode.cs ├── NodeFactory.cs ├── ActionNode.cs └── BulletMLNode.cs ├── README.md ├── IBulletManager.cs ├── Properties └── AssemblyInfo.cs ├── LICENSE.txt ├── BulletMLEquation.cs ├── Enums.cs ├── BulletMLLib4Unity.sln ├── bulletml.dtd ├── .gitignore ├── Emitter.cs ├── BulletMLLib4Unity.csproj ├── BulletPattern.cs └── Bullet.cs /externals/Equationator.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eatfrog/bulletmllib4unity/HEAD/externals/Equationator.dll -------------------------------------------------------------------------------- /ExampleApplication/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "BulletMLSample/Content/Samples"] 2 | path = BulletMLSample/Content/Samples 3 | url = https://github.com/dmanning23/BulletMLExamples.git 4 | [submodule "externals/Equationator"] 5 | path = externals/Equationator 6 | url = https://github.com/dmanning23/Equationator.git 7 | -------------------------------------------------------------------------------- /GameManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace BulletMLLib 5 | { 6 | /// 7 | /// This thing manages a few gameplay variables that used by the bulletml lib 8 | /// 9 | public static class GameManager 10 | { 11 | /// 12 | /// callback method to get the game difficulty. 13 | /// You need to set this at the start of the game 14 | /// 15 | static public FloatDelegate GameDifficulty; 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /PositionDelegate.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | namespace BulletMLLib 3 | { 4 | /// 5 | /// This is a callback method for getting a position 6 | /// used to break out dependencies 7 | /// 8 | /// a method to get a position. 9 | public delegate Vector2 PositionDelegate(); 10 | 11 | /// 12 | /// a method to get a float from somewhere 13 | /// separate from delgates 14 | /// 15 | /// get a float from somewhere 16 | public delegate float FloatDelegate(); 17 | } 18 | -------------------------------------------------------------------------------- /Tasks/SetDirectionTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace BulletMLLib 5 | { 6 | /// 7 | /// This task sets the direction of a bullet 8 | /// 9 | public class SetDirectionTask : BulletMLTask 10 | { 11 | 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | /// Node. 16 | /// Owner. 17 | public SetDirectionTask(BulletMLNode node, BulletMLTask owner) : base(node, owner) 18 | { 19 | 20 | } 21 | 22 | } 23 | } -------------------------------------------------------------------------------- /Nodes/BulletNode.cs: -------------------------------------------------------------------------------- 1 | namespace BulletMLLib 2 | { 3 | public class BulletNode : BulletMLNode 4 | { 5 | /// 6 | /// Initializes a new instance of the class. 7 | /// 8 | public BulletNode() : this(NodeName.Bullet) 9 | { 10 | } 11 | 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// this is the constructor used by sub classes 15 | /// 16 | /// the node type. 17 | public BulletNode(NodeName nodeType) : base(nodeType) 18 | { 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tasks/SetSpeedTask.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace BulletMLLib 4 | { 5 | /// 6 | /// This action sets the velocity of a bullet 7 | /// 8 | public class SetSpeedTask : BulletMLTask 9 | { 10 | #region Methods 11 | 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | /// Node. 16 | /// Owner. 17 | public SetSpeedTask(BulletMLNode node, BulletMLTask owner) 18 | : base(node, owner) 19 | { 20 | 21 | } 22 | 23 | #endregion //Methods 24 | } 25 | } -------------------------------------------------------------------------------- /ExampleApplication/FakeEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using BulletMLLib; 7 | 8 | namespace ExampleApplication 9 | { 10 | public class FakeEmitter : Emitter 11 | { 12 | IBulletManager _bulletManager; 13 | Bullet rootBullet; 14 | public FakeEmitter(IBulletManager bulletManager, BulletPattern pattern, Bullet bl) : base(bulletManager, pattern, bl) 15 | { 16 | rootBullet = bl; 17 | _bulletManager = bulletManager; 18 | } 19 | 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## BulletMLLib4Unity 2 | 3 | A fork off of https://github.com/dmanning23/BulletMLLib/ with modifications for using it with Unity2d. 4 | This is a C# library used to read BulletML XML files, based on the implementation by Keiichi Kashihara of Bandle Games. 5 | 6 | https://sites.google.com/site/bandlegames/bulletml-c 7 | 8 | ## BulletML 9 | 10 | XML schema used to define bullets, patterns, and behaviors. 11 | 12 | Based on the spec at ABA Games: 13 | 14 | http://www.asahi-net.or.jp/~cs8k-cyu/bulletml/index_e.html 15 | 16 | 17 | ## Getting started 18 | 19 | A proper guide is coming soon. Meanwhile, check out the example application. -------------------------------------------------------------------------------- /ExampleApplication/FakeBulletManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using BulletMLLib; 6 | using UnityEngine; 7 | 8 | namespace ExampleApplication 9 | { 10 | public class FakeBulletManager : IBulletManager 11 | { 12 | public List Bullets = new List(); 13 | public Vector2 PlayerPosition(Bullet targettedBullet) 14 | { 15 | return new Vector2(0, 0); 16 | } 17 | 18 | public void RemoveBullet(Bullet deadBullet) 19 | { 20 | Console.WriteLine("Bullet removed"); 21 | var b = Bullets.Single(x => x == deadBullet); 22 | b.IsActive = false; 23 | } 24 | 25 | public Bullet CreateBullet(Emitter e) 26 | { 27 | Console.WriteLine("New bullet created"); 28 | var bullet = new FakeBullet(this, e); 29 | Bullets.Add(bullet); 30 | return bullet; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /IBulletManager.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace BulletMLLib 4 | { 5 | /// 6 | /// This is the interface that outisde assemblies will use to manage bullets... mostly for creating/destroying them 7 | /// 8 | public interface IBulletManager 9 | { 10 | #region Methods 11 | 12 | /// 13 | /// a mathod to get current position of the player 14 | /// This is used to target bullets at that position 15 | /// 16 | /// The position to aim the bullet at 17 | /// the bullet we are getting a target for 18 | Vector2 PlayerPosition(Bullet targettedBullet); 19 | 20 | /// 21 | /// A bullet is done being used, do something to get rid of it. 22 | /// 23 | /// the Dead bullet. 24 | void RemoveBullet(Bullet deadBullet); 25 | 26 | /// 27 | /// Create a new bullet. 28 | /// 29 | /// A shiny new bullet 30 | Bullet CreateBullet(Emitter emitter); 31 | 32 | #endregion //Methods 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ExampleApplication/FakeBullet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using BulletMLLib; 7 | 8 | namespace ExampleApplication 9 | { 10 | public class FakeBullet : Bullet 11 | { 12 | 13 | public FakeBullet(IBulletManager myBulletManager) 14 | : base(myBulletManager) 15 | { 16 | TimeSpeed = 1; 17 | Scale = 1; 18 | } 19 | 20 | 21 | public FakeBullet(IBulletManager myBulletManager, Emitter e) : base(myBulletManager) 22 | { 23 | Emitter = e; 24 | } 25 | 26 | public bool IsActive { get; set; } 27 | 28 | public override void BulletSpawned() 29 | { 30 | 31 | Console.WriteLine("New bullet spawned. PatterName: " + Node.GetPatternName()); 32 | 33 | // instantiate prefab or something.. 34 | } 35 | 36 | public override float X { get; set; } 37 | public override float Y { get; set; } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("BulletMLLib")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("BulletMLLib")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("1.0.0.0")] 28 | [assembly: AssemblyFileVersion("1.0.0.0")] 29 | [assembly: ComVisible(false)] -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Dan Manning 4 | Copyright (c) 2014 Henri Toivonen 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /BulletMLEquation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace BulletMLLib 5 | { 6 | /// 7 | /// This is an equation used in BulletML nodes. 8 | /// This is an eays way to set up the grammar for all our equations. 9 | /// 10 | public class BulletMLEquation : Equationator.Equation 11 | { 12 | /// 13 | /// A randomizer for getting random values 14 | /// 15 | static private readonly Random GRandom = new Random(DateTime.Now.Millisecond); 16 | 17 | public BulletMLEquation() 18 | { 19 | //add the specific functions we will use for bulletml grammar 20 | AddFunction("rand", RandomValue); 21 | AddFunction("rank", GameDifficulty); 22 | } 23 | 24 | /// 25 | /// used as a callback function in bulletml euqations 26 | /// 27 | /// The value. 28 | public float RandomValue() 29 | { 30 | //this value is "$rand", return a random number 31 | return (float)GRandom.NextDouble(); 32 | } 33 | 34 | public float GameDifficulty() 35 | { 36 | //This number is "$rank" which is the game difficulty. 37 | return GameManager.GameDifficulty(); 38 | } 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /Tasks/VanishTask.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace BulletMLLib 4 | { 5 | /// 6 | /// This task removes a bullet from the game. 7 | /// 8 | public class VanishTask : BulletMLTask 9 | { 10 | #region Methods 11 | 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | /// Node. 16 | /// Owner. 17 | public VanishTask(BulletMLNode node, BulletMLTask owner) : base(node, owner) 18 | { 19 | 20 | } 21 | 22 | /// 23 | /// Run this task and all subtasks against a bullet 24 | /// This is called once a frame during runtime. 25 | /// 26 | /// RunStatus: whether this task is done, paused, or still running 27 | /// The bullet to update this task against. 28 | public override RunStatus Run(Bullet bullet) 29 | { 30 | //remove the bullet via the bullet manager interface 31 | IBulletManager manager = bullet.MyBulletManager; 32 | 33 | manager.RemoveBullet(bullet); 34 | return RunStatus.End; 35 | } 36 | 37 | #endregion //Methods 38 | } 39 | } -------------------------------------------------------------------------------- /Tasks/RepeatTask.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System; 4 | 5 | namespace BulletMLLib 6 | { 7 | /// 8 | /// This is a task..each task is the action from a single xml node, for one bullet. 9 | /// basically each bullet makes a tree of these to match its pattern 10 | /// 11 | public class RepeatTask : BulletMLTask 12 | { 13 | 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// Node. 18 | /// Owner. 19 | public RepeatTask(BulletMLNode node, BulletMLTask owner) : base(node, owner) 20 | { 21 | 22 | } 23 | 24 | /// 25 | /// Init this task and all its sub tasks. 26 | /// This method should be called AFTER the nodes are parsed, but BEFORE run is called. 27 | /// 28 | /// the bullet this dude is controlling 29 | public override void InitTask(Bullet bullet) 30 | { 31 | //Init task is being called on a RepeatTask, which means all the sequence nodes underneath this one need to be reset 32 | 33 | //Call the HardReset method of the base class 34 | HardReset(bullet); 35 | } 36 | 37 | } 38 | } -------------------------------------------------------------------------------- /ExampleApplication/example.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 100 12 | 13 | 14 | 0 15 | $rank + 0.13 16 | 17 | 18 | 19 | 5 20 | $rank + 0.13 21 | 22 | 23 | 0.7 + $rank 24 | 25 | 26 | 17 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | $1 38 | 0.2 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Enums.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace BulletMLLib 7 | { 8 | /// 9 | /// Different types of bullet patterns 10 | /// 11 | public enum PatternType 12 | { 13 | Vertical, 14 | Horizontal, 15 | None 16 | } 17 | 18 | public enum NodeType 19 | { 20 | None, 21 | Aim, 22 | Absolute, 23 | Relative, 24 | Sequence 25 | }; 26 | 27 | public enum NodeName 28 | { 29 | Bullet, 30 | Action, 31 | Fire, 32 | ChangeDirection, 33 | ChangeSpeed, 34 | Accel, 35 | Wait, 36 | Repeat, 37 | BulletRef, 38 | ActionRef, 39 | FireRef, 40 | Vanish, 41 | Horizontal, 42 | Vertical, 43 | Term, 44 | Times, 45 | Direction, 46 | Speed, 47 | Param, 48 | Bulletml 49 | }; 50 | 51 | /// 52 | /// Theese are used for tasks during runtime... 53 | /// 54 | public enum RunStatus 55 | { 56 | Continue, //keep parsing this task 57 | End, //this task is finished parsing 58 | Stop //this task is paused 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Nodes/DirectionNode.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace BulletMLLib 3 | { 4 | public class DirectionNode : BulletMLNode 5 | { 6 | /// 7 | /// Initializes a new instance of the class. 8 | /// 9 | public DirectionNode() : base(NodeName.Direction) 10 | { 11 | //set the default type to "aim" 12 | NodeType = NodeType.Aim; 13 | } 14 | 15 | /// 16 | /// Gets or sets the type of the node. 17 | /// This is virtual so sub-classes can override it and validate their own shit. 18 | /// 19 | /// The type of the node. 20 | public override NodeType NodeType 21 | { 22 | get 23 | { 24 | return base.NodeType; 25 | } 26 | protected set 27 | { 28 | switch (value) 29 | { 30 | case NodeType.Absolute: 31 | { 32 | base.NodeType = value; 33 | } 34 | break; 35 | 36 | case NodeType.Relative: 37 | { 38 | base.NodeType = value; 39 | } 40 | break; 41 | 42 | case NodeType.Sequence: 43 | { 44 | base.NodeType = value; 45 | } 46 | break; 47 | 48 | default: 49 | { 50 | //All other node types default to aim, because otherwise they are wrong! 51 | base.NodeType = NodeType.Aim; 52 | } 53 | break; 54 | } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ExampleApplication/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using BulletMLLib; 8 | 9 | namespace ExampleApplication 10 | { 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | var bm = new FakeBulletManager(); 16 | 17 | GameManager.GameDifficulty = () => 1f; 18 | 19 | BulletPattern pattern = new BulletPattern("../../example.xml"); 20 | var fakeBullet = new FakeBullet(bm); 21 | var emitter = new FakeEmitter(bm, pattern, fakeBullet); 22 | 23 | for (int i = 0; i < 500; i++) 24 | { 25 | emitter.Update(0, 0); 26 | for (int ii = 0; ii < bm.Bullets.Count; ii++) 27 | { 28 | bm.Bullets[ii].Update(); 29 | } 30 | bm.Bullets.ForEach(bullet => Console.WriteLine("X: {0} Y: {1} Aim: {2} Direction: {3}", bullet.X, bullet.Y, bullet.GetAngleTowardsPlayer(), bullet.Direction)); 31 | ConsoleKeyInfo key = Console.ReadKey(); 32 | if (key.Key == ConsoleKey.Q) break; 33 | else Console.WriteLine("--- Press Q for break or any other key for next step ---"); 34 | } 35 | Console.WriteLine("End"); 36 | Console.ReadKey(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Nodes/ActionRefNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Xml; 3 | 4 | namespace BulletMLLib 5 | { 6 | /// 7 | /// Action reference node. 8 | /// This node type references another Action node. 9 | /// 10 | public class ActionRefNode : ActionNode 11 | { 12 | #region Members 13 | 14 | public ActionNode ReferencedActionNode { get; private set; } 15 | 16 | #endregion //Members 17 | 18 | #region Methods 19 | 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | public ActionRefNode() : base(NodeName.ActionRef) 24 | { 25 | } 26 | 27 | /// 28 | /// Validates the node. 29 | /// Overloaded in child classes to validate that each type of node follows the correct business logic. 30 | /// This checks stuff that isn't validated by the XML validation 31 | /// 32 | public override void ValidateNode() 33 | { 34 | //do any base class validation 35 | base.ValidateNode(); 36 | 37 | //Find the action node this dude references 38 | BulletMLNode refNode = GetRootNode().FindLabelNode(Label, NodeName.Action); 39 | 40 | //make sure we foud something 41 | if (null == refNode) 42 | { 43 | throw new NullReferenceException("Couldn't find the action node \"" + Label + "\""); 44 | } 45 | 46 | ReferencedActionNode = refNode as ActionNode; 47 | if (null == ReferencedActionNode) 48 | { 49 | throw new NullReferenceException("The BulletMLNode \"" + Label + "\" isn't an action node"); 50 | } 51 | } 52 | 53 | #endregion //Methods 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ExampleApplication/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ExampleApplication")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ExampleApplication")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("ea25f36e-e299-48bf-8320-113c4d05d19d")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Nodes/FireRefNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Xml; 3 | 4 | namespace BulletMLLib 5 | { 6 | public class FireRefNode : FireNode 7 | { 8 | #region Members 9 | 10 | /// 11 | /// Gets the referenced fire node. 12 | /// 13 | /// The referenced fire node. 14 | public FireNode ReferencedFireNode { get; private set; } 15 | 16 | #endregion //Members 17 | 18 | #region Methods 19 | 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | public FireRefNode() : base(NodeName.FireRef) 24 | { 25 | } 26 | 27 | /// 28 | /// Validates the node. 29 | /// Overloaded in child classes to validate that each type of node follows the correct business logic. 30 | /// This checks stuff that isn't validated by the XML validation 31 | /// 32 | public override void ValidateNode() 33 | { 34 | //Find the action node this dude 35 | 36 | BulletMLNode refNode = GetRootNode().FindLabelNode(Label, NodeName.Fire); 37 | 38 | //make sure we foud something 39 | if (null == refNode) 40 | { 41 | throw new NullReferenceException("Couldn't find the fire node \"" + Label + "\""); 42 | } 43 | 44 | ReferencedFireNode = refNode as FireNode; 45 | if (null == ReferencedFireNode) 46 | { 47 | throw new NullReferenceException("The BulletMLNode \"" + Label + "\" isn't a fire node"); 48 | } 49 | 50 | //Do not validate the base class of this dude... it will crap out trying to find the bullet node! 51 | } 52 | 53 | #endregion //Methods 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /BulletMLLib4Unity.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30110.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BulletMLLib4Unity", "BulletMLLib4Unity.csproj", "{09435B69-7AF7-452D-8B19-984FCFFED149}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExampleApplication", "ExampleApplication\ExampleApplication.csproj", "{D8719284-A3FC-461F-A39F-75177FEEE55D}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0AF76397-E91A-40BA-AD4A-C38CE7E339B3}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {09435B69-7AF7-452D-8B19-984FCFFED149}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {09435B69-7AF7-452D-8B19-984FCFFED149}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {09435B69-7AF7-452D-8B19-984FCFFED149}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {09435B69-7AF7-452D-8B19-984FCFFED149}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {D8719284-A3FC-461F-A39F-75177FEEE55D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {D8719284-A3FC-461F-A39F-75177FEEE55D}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {D8719284-A3FC-461F-A39F-75177FEEE55D}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {D8719284-A3FC-461F-A39F-75177FEEE55D}.Release|Any CPU.Build.0 = Release|Any CPU 26 | EndGlobalSection 27 | GlobalSection(SolutionProperties) = preSolution 28 | HideSolutionNode = FALSE 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /bulletml.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Nodes/FireNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | 4 | namespace BulletMLLib 5 | { 6 | public class FireNode : BulletMLNode 7 | { 8 | #region Members 9 | 10 | /// 11 | /// A bullet node this task will use to set any bullets shot from this task 12 | /// 13 | /// The bullet node. 14 | public BulletNode BulletDescriptionNode { get; set; } 15 | 16 | #endregion //Members 17 | 18 | #region Methods 19 | 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | public FireNode() : this(NodeName.Fire) 24 | { 25 | } 26 | 27 | /// 28 | /// Initializes a new instance of the class. 29 | /// this is the constructor used by sub classes 30 | /// 31 | /// the node type. 32 | public FireNode(NodeName nodeType) : base(nodeType) 33 | { 34 | } 35 | 36 | /// 37 | /// Validates the node. 38 | /// Overloaded in child classes to validate that each type of node follows the correct business logic. 39 | /// This checks stuff that isn't validated by the XML validation 40 | /// 41 | public override void ValidateNode() 42 | { 43 | base.ValidateNode(); 44 | 45 | //check for a bullet node 46 | BulletDescriptionNode = GetChild(NodeName.Bullet) as BulletNode; 47 | 48 | if (null != BulletDescriptionNode) return; 49 | 50 | //make sure that dude knows what he's doing 51 | BulletRefNode refNode = GetChild(NodeName.BulletRef) as BulletRefNode; 52 | if (refNode != null) 53 | { 54 | refNode.FindMyBulletNode(); 55 | BulletDescriptionNode = refNode.ReferencedBulletNode; 56 | } 57 | } 58 | 59 | #endregion Methods 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Tasks/WaitTask.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace BulletMLLib 4 | { 5 | /// 6 | /// This task pauses for a specified amount of time before resuming 7 | /// 8 | public class WaitTask : BulletMLTask 9 | { 10 | #region Members 11 | 12 | /// 13 | /// How long to run this task... measured in frames 14 | /// This task will pause until the durection runs out, then resume running tasks 15 | /// 16 | private float Duration { get; set; } 17 | 18 | #endregion //Members 19 | 20 | #region Methods 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// Node. 26 | /// Owner. 27 | public WaitTask(BulletMLNode node, BulletMLTask owner) : base(node, owner) 28 | { 29 | Debug.Assert(null != Node); 30 | Debug.Assert(null != Owner); 31 | } 32 | 33 | /// 34 | /// this sets up the task to be run. 35 | /// 36 | /// Bullet. 37 | protected override void SetupTask(Bullet bullet) 38 | { 39 | Duration = Node.GetValue(this); 40 | } 41 | 42 | /// 43 | /// Run this task and all subtasks against a bullet 44 | /// This is called once a frame during runtime. 45 | /// 46 | /// RunStatus: whether this task is done, paused, or still running 47 | /// The bullet to update this task against. 48 | public override RunStatus Run(Bullet bullet) 49 | { 50 | Duration -= 1.0f * bullet.TimeSpeed; 51 | if (Duration >= 0.0f) 52 | { 53 | return RunStatus.Stop; 54 | } 55 | 56 | TaskFinished = true; 57 | return RunStatus.End; 58 | } 59 | 60 | #endregion //Methods 61 | } 62 | } -------------------------------------------------------------------------------- /Nodes/BulletRefNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Xml; 3 | 4 | namespace BulletMLLib 5 | { 6 | public class BulletRefNode : BulletNode 7 | { 8 | #region Members 9 | 10 | /// 11 | /// Gets the referenced bullet node. 12 | /// 13 | /// The referenced bullet node. 14 | public BulletNode ReferencedBulletNode { get; private set; } 15 | 16 | #endregion //Members 17 | 18 | #region Methods 19 | 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | public BulletRefNode() : base(NodeName.BulletRef) 24 | { 25 | } 26 | 27 | /// 28 | /// Validates the node. 29 | /// Overloaded in child classes to validate that each type of node follows the correct business logic. 30 | /// This checks stuff that isn't validated by the XML validation 31 | /// 32 | public override void ValidateNode() 33 | { 34 | //do any base class validation 35 | base.ValidateNode(); 36 | 37 | //make sure this dude knows where his bullet node is 38 | FindMyBulletNode(); 39 | } 40 | 41 | /// 42 | /// Finds the referenced bullet node. 43 | /// 44 | public void FindMyBulletNode() 45 | { 46 | if (null == ReferencedBulletNode) 47 | { 48 | //Find the action node this dude references 49 | BulletMLNode refNode = GetRootNode().FindLabelNode(Label, NodeName.Bullet); 50 | 51 | //make sure we foud something 52 | if (null == refNode) 53 | { 54 | throw new NullReferenceException("Couldn't find the bullet node \"" + Label + "\""); 55 | } 56 | 57 | ReferencedBulletNode = refNode as BulletNode; 58 | if (null == ReferencedBulletNode) 59 | { 60 | throw new NullReferenceException("The BulletMLNode \"" + Label + "\" isn't a bullet node"); 61 | } 62 | } 63 | } 64 | 65 | #endregion //Methods 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Rr]elease/ 19 | test-results/ 20 | x64/ 21 | *_i.c 22 | *_p.c 23 | *.ilk 24 | *.meta 25 | *.obj 26 | *.pch 27 | *.pdb 28 | *.pgc 29 | *.pgd 30 | *.rsp 31 | *.sbr 32 | *.tlb 33 | *.tli 34 | *.tlh 35 | *.tmp 36 | *.log 37 | *.vspscc 38 | *.vssscc 39 | .builds 40 | *.cachefile 41 | *.userprefs 42 | 43 | # Visual C++ cache files 44 | ipch/ 45 | *.aps 46 | *.ncb 47 | *.opensdf 48 | *.sdf 49 | 50 | # Visual Studio profiler 51 | *.psess 52 | *.vsp 53 | *.vspx 54 | 55 | # Guidance Automation Toolkit 56 | *.gpState 57 | 58 | # ReSharper is a .NET coding add-in 59 | _ReSharper* 60 | 61 | # NCrunch 62 | *.ncrunch* 63 | .*crunch*.local.xml 64 | 65 | # Installshield output folder 66 | [Ee]xpress 67 | 68 | # DocProject is a documentation generator add-in 69 | DocProject/buildhelp/ 70 | DocProject/Help/*.HxT 71 | DocProject/Help/*.HxC 72 | DocProject/Help/*.hhc 73 | DocProject/Help/*.hhk 74 | DocProject/Help/*.hhp 75 | DocProject/Help/Html2 76 | DocProject/Help/html 77 | 78 | # Click-Once directory 79 | publish 80 | 81 | # Publish Web Output 82 | *.Publish.xml 83 | 84 | # NuGet Packages Directory 85 | packages 86 | 87 | # Windows Azure Build Output 88 | csx 89 | *.build.csdef 90 | 91 | # Windows Store app package directory 92 | AppPackages/ 93 | 94 | # Others 95 | [Bb]in 96 | [Oo]bj 97 | sql 98 | TestResults 99 | [Tt]est[Rr]esult* 100 | *.Cache 101 | ClientBin 102 | [Ss]tyle[Cc]op.* 103 | ~$* 104 | *.dbmdl 105 | Generated_Code #added for RIA/Silverlight projects 106 | 107 | # Backup & report files from converting an old project file to a newer 108 | # Visual Studio version. Backup files are not needed, because we have git ;-) 109 | _UpgradeReport_Files/ 110 | Backup*/ 111 | UpgradeLog*.XML 112 | -------------------------------------------------------------------------------- /Nodes/NodeFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BulletMLLib 4 | { 5 | /// 6 | /// This is a simple class used to create different types of nodes. 7 | /// 8 | public static class NodeFactory 9 | { 10 | /// 11 | /// Given a node type, create the correct node. 12 | /// 13 | /// An instance of the correct node type 14 | /// Node type that we want. 15 | public static BulletMLNode CreateNode(NodeName nodeType) 16 | { 17 | switch (nodeType) 18 | { 19 | case NodeName.Bullet: 20 | { 21 | return new BulletNode(); 22 | } 23 | case NodeName.Action: 24 | { 25 | return new ActionNode(); 26 | } 27 | case NodeName.Fire: 28 | { 29 | return new FireNode(); 30 | } 31 | case NodeName.ChangeDirection: 32 | { 33 | return new BulletNode(NodeName.ChangeDirection); 34 | } 35 | case NodeName.ChangeSpeed: 36 | { 37 | return new BulletNode(NodeName.ChangeSpeed); 38 | } 39 | case NodeName.Accel: 40 | { 41 | return new BulletNode(NodeName.Accel); 42 | } 43 | case NodeName.Wait: 44 | { 45 | return new BulletNode(NodeName.Wait); 46 | } 47 | case NodeName.Repeat: 48 | { 49 | return new BulletNode(NodeName.Repeat); 50 | } 51 | case NodeName.BulletRef: 52 | { 53 | return new BulletRefNode(); 54 | } 55 | case NodeName.ActionRef: 56 | { 57 | return new ActionRefNode(); 58 | } 59 | case NodeName.FireRef: 60 | { 61 | return new FireRefNode(); 62 | } 63 | case NodeName.Vanish: 64 | { 65 | return new BulletNode(NodeName.Vanish); 66 | } 67 | case NodeName.Horizontal: 68 | { 69 | return new BulletNode(NodeName.Horizontal); 70 | } 71 | case NodeName.Vertical: 72 | { 73 | return new BulletNode(NodeName.Vertical); 74 | } 75 | case NodeName.Term: 76 | { 77 | return new BulletNode(NodeName.Term); 78 | } 79 | case NodeName.Times: 80 | { 81 | return new BulletNode(NodeName.Times); 82 | } 83 | case NodeName.Direction: 84 | { 85 | return new DirectionNode(); 86 | } 87 | case NodeName.Speed: 88 | { 89 | return new BulletNode(NodeName.Speed); 90 | } 91 | case NodeName.Param: 92 | { 93 | return new BulletNode(NodeName.Param); 94 | } 95 | case NodeName.Bulletml: 96 | { 97 | return new BulletMLNode(NodeName.Bulletml); 98 | } 99 | default: 100 | { 101 | throw new Exception("Unhandled type of NodeName: \"" + nodeType.ToString() + "\""); 102 | } 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Nodes/ActionNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Xml; 3 | 4 | namespace BulletMLLib 5 | { 6 | /// 7 | /// Action node... also the base class for actionref nodes 8 | /// 9 | public class ActionNode : BulletMLNode 10 | { 11 | #region Members 12 | 13 | /// 14 | /// Gets or sets the parent repeat node. 15 | /// This is the node immediately above this one that says how many times to repeat this action. 16 | /// 17 | /// The parent repeat node. 18 | public BulletNode ParentRepeatNode { get; private set; } 19 | 20 | #endregion //Members 21 | 22 | #region Methods 23 | 24 | /// 25 | /// Initializes a new instance of the class. 26 | /// 27 | public ActionNode() : this(NodeName.Action) 28 | { 29 | } 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// this is the constructor used by sub classes 34 | /// 35 | /// the node type. 36 | public ActionNode(NodeName nodeType) : base(nodeType) 37 | { 38 | } 39 | 40 | /// 41 | /// Validates the node. 42 | /// Overloaded in child classes to validate that each type of node follows the correct business logic. 43 | /// This checks stuff that isn't validated by the XML validation 44 | /// 45 | public override void ValidateNode() 46 | { 47 | //Get our parent repeat node if we have one 48 | ParentRepeatNode = FindParentRepeatNode(); 49 | 50 | //do any base class validation 51 | base.ValidateNode(); 52 | } 53 | 54 | /// 55 | /// Finds the parent repeat node. 56 | /// This method is not recursive, since action and actionref nodes can be nested. 57 | /// 58 | /// The parent repeat node. 59 | private BulletNode FindParentRepeatNode() 60 | { 61 | //Parent node should never ever be empty on an action node 62 | if (Parent == null ) 63 | { 64 | throw new NullReferenceException("Parent node cannot be empty on an action or actionRef node"); 65 | } 66 | 67 | //If the parent is a repeat node, check how many times to repeat this action 68 | if (Parent.Name == NodeName.Repeat) 69 | { 70 | return Parent as BulletNode; 71 | } 72 | 73 | //This dude is not under a repeat node 74 | return null; 75 | } 76 | 77 | /// 78 | /// Get the number of times this action should be repeated. 79 | /// 80 | /// the task to get the number of repeat times for 81 | /// The number of times to repeat this node, as specified by a parent Repeat node. 82 | public int RepeatNum(ActionTask myTask) 83 | { 84 | if (null != ParentRepeatNode) 85 | { 86 | //Get the equation value of the repeat node 87 | return (int)ParentRepeatNode.GetChildValue(NodeName.Times, myTask); 88 | } 89 | else 90 | { 91 | //no repeat nodes, just repeat it once 92 | return 1; 93 | } 94 | } 95 | 96 | #endregion //Methods 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Tasks/ChangeSpeedTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace BulletMLLib 3 | { 4 | /// 5 | /// This task changes the speed a little bit every frame. 6 | /// 7 | public class ChangeSpeedTask : BulletMLTask 8 | { 9 | private float _nodeSpeed; 10 | 11 | /// 12 | /// the type of speed change, pulled out of the node 13 | /// 14 | private NodeType _changeType; 15 | 16 | /// 17 | /// How long to run this task... measured in frames 18 | /// 19 | private float Duration { get; set; } 20 | 21 | /// 22 | /// How many frames this dude has ran 23 | /// 24 | private float RunDelta { get; set; } 25 | 26 | #region Methods 27 | 28 | /// 29 | /// Initializes a new instance of the class. 30 | /// 31 | /// Node. 32 | /// Owner. 33 | public ChangeSpeedTask(BulletMLNode node, BulletMLTask owner) : base(node, owner) 34 | { 35 | 36 | } 37 | 38 | /// 39 | /// this sets up the task to be run. 40 | /// 41 | /// Bullet. 42 | protected override void SetupTask(Bullet bullet) 43 | { 44 | //set the length of time to run this dude 45 | Duration = Node.GetChildValue(NodeName.Term, this); 46 | 47 | //check for divide by 0 48 | if (Math.Abs(Duration) < 0.01) 49 | { 50 | Duration = 1.0f; 51 | } 52 | 53 | _nodeSpeed = Node.GetChildValue(NodeName.Speed, this); 54 | _changeType = Node.GetChild(NodeName.Speed).NodeType; 55 | } 56 | 57 | /// 58 | /// Run this task and all subtasks against a bullet 59 | /// This is called once a frame during runtime. 60 | /// 61 | /// RunStatus: whether this task is done, paused, or still running 62 | /// The bullet to update this task against. 63 | public override RunStatus Run(Bullet bullet) 64 | { 65 | bullet.Speed += GetSpeed(bullet); 66 | 67 | RunDelta += 1.0f * bullet.TimeSpeed; 68 | if (Duration <= RunDelta) 69 | { 70 | TaskFinished = true; 71 | return RunStatus.End; 72 | } 73 | 74 | //since this task isn't finished, run it again next time 75 | return RunStatus.Continue; 76 | 77 | } 78 | 79 | private float GetSpeed(Bullet bullet) 80 | { 81 | switch (_changeType) 82 | { 83 | case NodeType.Sequence: 84 | return _nodeSpeed; 85 | case NodeType.Relative: 86 | return _nodeSpeed / Duration; 87 | 88 | default: 89 | return ((_nodeSpeed - bullet.Speed) / (Duration - RunDelta)); 90 | } 91 | } 92 | 93 | #endregion //Methods 94 | } 95 | } -------------------------------------------------------------------------------- /ExampleApplication/ExampleApplication.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {D8719284-A3FC-461F-A39F-75177FEEE55D} 8 | Exe 9 | Properties 10 | ExampleApplication 11 | ExampleApplication 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | False 44 | C:\Program Files (x86)\Unity\Editor\Data\Managed\UnityEngine.dll 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | {09435b69-7af7-452d-8b19-984fcffed149} 60 | BulletMLLib4Unity 61 | 62 | 63 | 64 | 65 | 66 | 67 | 74 | -------------------------------------------------------------------------------- /Emitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace BulletMLLib 7 | { 8 | 9 | /// 10 | /// The enemy, or such, that is emitting the bullets 11 | /// 12 | public class Emitter 13 | { 14 | // This is not really a bullet, but its pattern holder. needed for x, y and such, for now at least. 15 | private readonly Bullet _rootBullet; 16 | public BulletPattern Pattern { get; private set; } 17 | 18 | private readonly IBulletManager _bulletManager; 19 | 20 | public Emitter(IBulletManager bulletManager, BulletPattern pattern, Bullet rootBullet) 21 | { 22 | _bulletManager = bulletManager; 23 | Pattern = pattern; 24 | _rootBullet = rootBullet; 25 | _rootBullet.Emitter = this; 26 | InitTopNode(pattern.RootNode); 27 | } 28 | 29 | public void ClearTasks() 30 | { 31 | _rootBullet.Tasks.Clear(); 32 | } 33 | public void Update(float x, float y) 34 | { 35 | _rootBullet.X = x; 36 | _rootBullet.Y = y; 37 | _rootBullet.Update(); 38 | } 39 | 40 | /// 41 | /// Initialize this bullet with a top level node 42 | /// 43 | /// This is a top level node... find the first "top" node and use it to define this bullet 44 | public void InitTopNode(BulletMLNode rootNode) 45 | { 46 | 47 | //okay find the item labelled 'top' 48 | bool bValidBullet = false; 49 | BulletMLNode topNode = rootNode.FindLabelNode("top", NodeName.Action); 50 | if (topNode != null) 51 | { 52 | //initialize with the top node we found! 53 | _rootBullet.InitNode(topNode); 54 | bValidBullet = true; 55 | _rootBullet.BulletSpawned(); 56 | } 57 | else 58 | { 59 | //ok there is no 'top' node, so that means we have a list of 'top#' nodes 60 | for (int i = 1; i < 10; i++) 61 | { 62 | topNode = rootNode.FindLabelNode("top" + i, NodeName.Action); 63 | if (topNode != null) 64 | { 65 | if (!bValidBullet) 66 | { 67 | //Use this bullet! 68 | _rootBullet.InitNode(topNode); 69 | bValidBullet = true; 70 | _rootBullet.BulletSpawned(); 71 | } 72 | else 73 | { 74 | //Create a new bullet 75 | Bullet b = _bulletManager.CreateBullet(this); 76 | 77 | //set the position to this dude's position 78 | b.X = _rootBullet.X; 79 | b.Y = _rootBullet.Y; 80 | 81 | //initialize with the node we found 82 | b.InitNode(topNode); 83 | b.BulletSpawned(); 84 | } 85 | } 86 | } 87 | } 88 | 89 | if (!bValidBullet) 90 | { 91 | //We didnt find a "top" node for this dude, remove him from the game. 92 | _bulletManager.RemoveBullet(_rootBullet); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Tasks/ActionTask.cs: -------------------------------------------------------------------------------- 1 | namespace BulletMLLib 2 | { 3 | /// 4 | /// An action task, this dude contains a list of tasks that are repeated 5 | /// 6 | public class ActionTask : BulletMLTask 7 | { 8 | #region Members 9 | 10 | /// 11 | /// The max number of times to repeat this action 12 | /// 13 | public int RepeatNumMax { get; private set; } 14 | 15 | /// 16 | /// The number of times this task has been run. 17 | /// This starts at 0 and the task will repeat until it hits the "max" 18 | /// 19 | public int RepeatNum { get; private set; } 20 | 21 | #endregion //Members 22 | 23 | #region Methods 24 | 25 | /// 26 | /// Initializes a new instance of the class. 27 | /// 28 | /// Node. 29 | /// Owner. 30 | public ActionTask(ActionNode node, BulletMLTask owner) : base(node, owner) 31 | { 32 | 33 | //set the number of times to repeat this action 34 | RepeatNumMax = node.RepeatNum(this); 35 | } 36 | 37 | /// 38 | /// Parse a specified node and bullet into this task 39 | /// 40 | /// the bullet this dude is controlling 41 | public override void ParseTasks(Bullet bullet) 42 | { 43 | //is this an actionref task? 44 | if (NodeName.ActionRef == Node.Name) 45 | { 46 | //add a sub task under this one for the referenced action 47 | ActionRefNode myActionRefNode = Node as ActionRefNode; 48 | 49 | //create the action task 50 | ActionTask actionTask = new ActionTask(myActionRefNode.ReferencedActionNode, this); 51 | 52 | //parse the children of the action node into the task 53 | actionTask.ParseTasks(bullet); 54 | 55 | //store the task 56 | ChildTasks.Add(actionTask); 57 | } 58 | 59 | //call the base class 60 | base.ParseTasks(bullet); 61 | } 62 | 63 | /// 64 | /// this sets up the task to be run. 65 | /// 66 | /// Bullet. 67 | protected override void SetupTask(Bullet bullet) 68 | { 69 | RepeatNum = 0; 70 | } 71 | 72 | /// 73 | /// Run this task and all subtasks against a bullet 74 | /// This is called once a frame during runtime. 75 | /// 76 | /// RunStatus: whether this task is done, paused, or still running 77 | /// The bullet to update this task against. 78 | public override RunStatus Run(Bullet bullet) 79 | { 80 | //run the action until we hit the limit 81 | while (RepeatNum < RepeatNumMax) 82 | { 83 | RunStatus runStatus = base.Run(bullet); 84 | 85 | //What was the return value from running all teh child actions? 86 | switch (runStatus) 87 | { 88 | case RunStatus.End: 89 | { 90 | //The actions completed successfully, initialize everything and run it again 91 | RepeatNum++; 92 | 93 | //reset all the child tasks 94 | foreach (BulletMLTask task in ChildTasks) 95 | { 96 | task.InitTask(bullet); 97 | } 98 | } 99 | break; 100 | 101 | case RunStatus.Stop: 102 | { 103 | //Something in the child tasks paused this action 104 | return runStatus; 105 | } 106 | 107 | default: 108 | { 109 | //One of the child tasks needs to keep running next frame 110 | return RunStatus.Continue; 111 | } 112 | } 113 | } 114 | 115 | //if it gets here, all the child tasks have been run the correct number of times 116 | TaskFinished = true; 117 | return RunStatus.End; 118 | } 119 | 120 | #endregion //Methods 121 | } 122 | } -------------------------------------------------------------------------------- /BulletMLLib4Unity.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {09435B69-7AF7-452D-8B19-984FCFFED149} 8 | Library 9 | Properties 10 | BulletMLLib4Unity 11 | BulletMLLib4Unity 12 | v3.5 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | externals\Equationator.dll 36 | 37 | 38 | 39 | 40 | 41 | C:\Program Files (x86)\Unity\Editor\Data\Managed\UnityEngine.dll 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 84 | -------------------------------------------------------------------------------- /Tasks/AccelTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace BulletMLLib 5 | { 6 | /// 7 | /// This task adds acceleration to a bullet. 8 | /// 9 | public class AccelTask : BulletMLTask 10 | { 11 | /// 12 | /// How long to run this task... measured in frames 13 | /// 14 | public float Duration { get; private set; } 15 | 16 | /// 17 | /// The direction to accelerate in 18 | /// 19 | private Vector2 _acceleration = Vector2.zero; 20 | 21 | /// 22 | /// Gets or sets the acceleration. 23 | /// 24 | /// The acceleration. 25 | public Vector2 Acceleration 26 | { 27 | get 28 | { 29 | return _acceleration; 30 | } 31 | } 32 | 33 | /// 34 | /// Initializes a new instance of the class. 35 | /// 36 | /// Node. 37 | /// Owner. 38 | public AccelTask(BulletMLNode node, BulletMLTask owner) : base(node, owner) 39 | { 40 | 41 | } 42 | 43 | /// 44 | /// this sets up the task to be run. 45 | /// 46 | /// Bullet. 47 | protected override void SetupTask(Bullet bullet) 48 | { 49 | //set the accelerataion we are gonna add to the bullet 50 | Duration = Node.GetChildValue(NodeName.Term, this); 51 | 52 | //check for divide by 0 53 | if (Math.Abs(Duration) < 0.0f) 54 | { 55 | Duration = 1.0f; 56 | } 57 | 58 | //Get the horizontal node 59 | var horiz = Node.GetChild(NodeName.Horizontal) as BulletNode; 60 | if (null != horiz) 61 | { 62 | //Set the x component of the acceleration 63 | switch (horiz.NodeType) 64 | { 65 | case NodeType.Sequence: 66 | { 67 | //Sequence in an acceleration node means "add this amount every frame" 68 | _acceleration.x = horiz.GetValue(this); 69 | } 70 | break; 71 | 72 | case NodeType.Relative: 73 | { 74 | //accelerate by a certain amount 75 | _acceleration.x = horiz.GetValue(this) / Duration; 76 | } 77 | break; 78 | 79 | default: 80 | { 81 | //accelerate to a specific value 82 | _acceleration.x = (horiz.GetValue(this) - bullet.Acceleration.x) / Duration; 83 | } 84 | break; 85 | } 86 | } 87 | 88 | //Get the vertical node 89 | BulletNode vert = Node.GetChild(NodeName.Vertical) as BulletNode; 90 | if (null == vert) return; 91 | 92 | //set teh y component of the acceleration 93 | switch (vert.NodeType) 94 | { 95 | case NodeType.Sequence: 96 | { 97 | //Sequence in an acceleration node means "add this amount every frame" 98 | _acceleration.y = vert.GetValue(this); 99 | } 100 | break; 101 | 102 | case NodeType.Relative: 103 | { 104 | //accelerate by a certain amount 105 | _acceleration.y = vert.GetValue(this) / Duration; 106 | } 107 | break; 108 | 109 | default: 110 | { 111 | //accelerate to a specific value 112 | _acceleration.y = (vert.GetValue(this) - bullet.Acceleration.y) / Duration; 113 | } 114 | break; 115 | } 116 | } 117 | 118 | /// 119 | /// Run this task and all subtasks against a bullet 120 | /// This is called once a frame during runtime. 121 | /// 122 | /// RunStatus: whether this task is done, paused, or still running 123 | /// The bullet to update this task against. 124 | public override RunStatus Run(Bullet bullet) 125 | { 126 | //Add the acceleration to the bullet 127 | bullet.Acceleration += Acceleration; 128 | 129 | //decrement the amount if time left to run and return End when this task is finished 130 | Duration -= 1.0f * bullet.TimeSpeed; 131 | if (Duration <= 0.0f) 132 | { 133 | TaskFinished = true; 134 | return RunStatus.End; 135 | } 136 | 137 | //since this task isn't finished, run it again next time 138 | return RunStatus.Continue; 139 | } 140 | } 141 | } -------------------------------------------------------------------------------- /BulletPattern.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Xml; 4 | using System.Xml.Schema; 5 | 6 | namespace BulletMLLib 7 | { 8 | /// 9 | /// This is a complete document that describes a bullet pattern. 10 | /// 11 | public class BulletPattern 12 | { 13 | public BulletPattern(string path) 14 | { 15 | var settings = new XmlReaderSettings { ProhibitDtd = false }; 16 | var reader = XmlReader.Create(path, settings); 17 | ParseXML(reader, null); 18 | } 19 | public BulletPattern(XmlReader reader) 20 | { 21 | ParseXML(reader, null); 22 | } 23 | /// 24 | /// The root node of a tree structure that describes the bullet pattern 25 | /// 26 | public BulletMLNode RootNode { get; private set; } 27 | 28 | public string Filename { get; set; } 29 | 30 | /// 31 | /// the orientation of this bullet pattern: horizontal or veritcal 32 | /// this is read in from the xml 33 | /// 34 | /// The orientation. 35 | public PatternType Orientation { get; private set; } 36 | 37 | 38 | /// 39 | /// Initializes a new instance of the class. 40 | /// 41 | public BulletPattern() 42 | { 43 | RootNode = null; 44 | } 45 | 46 | /// 47 | /// convert a string to a pattern type enum 48 | /// 49 | /// The type to name. 50 | /// String. 51 | private static PatternType StringToPatternType(string str) 52 | { 53 | return (PatternType)Enum.Parse(typeof(PatternType), str, true); 54 | } 55 | 56 | /// 57 | /// Parses a bullet pattern from a BulletML Xml file 58 | /// 59 | /// The XmlReader that contains the xmlfile. Note that if you open it as a local file the web player will not work. 60 | /// You can use WWW-requests instead. 61 | /// A callback function for when the fileparsing is completed 62 | public void ParseXML(XmlReader reader, Action callback) 63 | { 64 | 65 | try 66 | { 67 | //Open the file. 68 | XmlDocument xmlDoc = new XmlDocument(); 69 | xmlDoc.Load(reader); 70 | XmlNode rootXmlNode = xmlDoc.DocumentElement; 71 | 72 | //make sure it is actually an xml node 73 | if (rootXmlNode != null && rootXmlNode.NodeType == XmlNodeType.Element) 74 | { 75 | //eat up the name of that xml node 76 | string strElementName = rootXmlNode.Name; 77 | if ("bulletml" != strElementName) 78 | { 79 | //The first node HAS to be bulletml 80 | UnityEngine.Debug.Log("Error reading \"" + "\": XML root node needs to be \"bulletml\", found \"" + strElementName + "\" instead"); 81 | throw new Exception("Error reading \"" + "\": XML root node needs to be \"bulletml\", found \"" + strElementName + "\" instead"); 82 | } 83 | 84 | //Create the root node of the bulletml tree 85 | RootNode = new BulletMLNode(NodeName.Bulletml); 86 | 87 | //Read in the whole bulletml tree 88 | RootNode.Parse(rootXmlNode, null); 89 | 90 | //Find what kind of pattern this is: horizontal or vertical 91 | XmlNamedNodeMap mapAttributes = rootXmlNode.Attributes; 92 | for (int i = 0; i < mapAttributes.Count; i++) 93 | { 94 | //will only have the name attribute 95 | string strName = mapAttributes.Item(i).Name; 96 | string strValue = mapAttributes.Item(i).Value; 97 | if ("type" == strName) 98 | { 99 | //if this is a top level node, "type" will be veritcal or horizontal 100 | Orientation = StringToPatternType(strValue); 101 | } 102 | } 103 | } 104 | } 105 | catch (Exception ex) 106 | { 107 | //an error ocurred reading in the tree 108 | UnityEngine.Debug.Log("Error reading." + ex.Message); 109 | throw new Exception("Error reading \"" + "\"", ex); 110 | } 111 | 112 | reader.Close(); 113 | 114 | //validate that the bullet nodes are all valid 115 | try 116 | { 117 | RootNode.ValidateNode(); 118 | } 119 | catch (Exception ex) 120 | { 121 | //an error ocurred reading in the tree 122 | UnityEngine.Debug.Log("Error reading \"" + "\"" + ex.Message); 123 | throw new Exception("Error reading \"" + "\"", ex); 124 | } 125 | 126 | try 127 | { 128 | UnityEngine.Debug.Log("Loading done."); 129 | } 130 | catch (Exception) 131 | { 132 | // will throw if not running unity 133 | } 134 | 135 | 136 | if (callback != null) 137 | callback(); 138 | } 139 | 140 | /// 141 | /// delegate method that gets called when a validation error occurs 142 | /// 143 | /// Sender. 144 | /// Arguments. 145 | public static void MyValidationEventHandler(object sender, ValidationEventArgs args) 146 | { 147 | throw new XmlSchemaException("Error validating bulletml document: " + args.Message, 148 | args.Exception, 149 | args.Exception.LineNumber, 150 | args.Exception.LinePosition); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Tasks/ChangeDirectionTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BulletMLLib 4 | { 5 | /// 6 | /// This task changes the direction a little bit every frame 7 | /// 8 | public class ChangeDirectionTask : BulletMLTask 9 | { 10 | 11 | /// 12 | /// The amount pulled out of the node 13 | /// 14 | private float NodeDirection; 15 | 16 | /// 17 | /// the type of direction change, pulled out of the node 18 | /// 19 | private NodeType ChangeType; 20 | 21 | /// 22 | /// How long to run this task... measured in frames 23 | /// 24 | private float Duration { get; set; } 25 | 26 | /// 27 | /// How many frames this dude has ran 28 | /// 29 | private float RunDelta { get; set; } 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// 34 | /// Node. 35 | /// Owner. 36 | public ChangeDirectionTask(BulletMLNode node, BulletMLTask owner) : base(node, owner) 37 | { 38 | 39 | } 40 | 41 | /// 42 | /// this sets up the task to be run. 43 | /// 44 | /// Bullet. 45 | protected override void SetupTask(Bullet bullet) 46 | { 47 | RunDelta = 0; 48 | 49 | //set the time length to run this dude 50 | Duration = Node.GetChildValue(NodeName.Term, this); 51 | 52 | //check for divide by 0 53 | if (Math.Abs(Duration) < 0.01) 54 | { 55 | Duration = 1.0f; 56 | } 57 | 58 | //Get the amount to change direction from the nodes 59 | DirectionNode dirNode = Node.GetChild(NodeName.Direction) as DirectionNode; 60 | if (dirNode != null) 61 | { 62 | NodeDirection = dirNode.GetValue(this) * (float)Math.PI / 180.0f; //also make sure to convert to radians 63 | 64 | //How do we want to change direction? 65 | ChangeType = dirNode.NodeType; 66 | } 67 | } 68 | 69 | public override RunStatus Run(Bullet bullet) 70 | { 71 | //change the direction of the bullet by the correct amount 72 | bullet.Direction += GetDirection(bullet); 73 | 74 | //decrement the amount if time left to run and return End when this task is finished 75 | RunDelta += 1.0f * bullet.TimeSpeed; 76 | if (Duration <= RunDelta) 77 | { 78 | TaskFinished = true; 79 | return RunStatus.End; 80 | } 81 | 82 | //since this task isn't finished, run it again next time 83 | return RunStatus.Continue; 84 | 85 | } 86 | 87 | private float GetDirection(Bullet bullet) 88 | { 89 | //How do we want to change direction? 90 | float direction; 91 | switch (ChangeType) 92 | { 93 | case NodeType.Sequence: 94 | { 95 | //We are going to add this amount to the direction every frame 96 | direction = NodeDirection; 97 | } 98 | break; 99 | 100 | case NodeType.Absolute: 101 | { 102 | //We are going to go in the direction we are given, regardless of where we are pointing right now 103 | direction = NodeDirection - bullet.Direction; 104 | } 105 | break; 106 | 107 | case NodeType.Relative: 108 | { 109 | //The direction change will be relative to our current direction 110 | direction = NodeDirection; 111 | } 112 | break; 113 | 114 | default: 115 | { 116 | //the direction change is to aim at the enemy 117 | direction = ((NodeDirection + bullet.GetAngleTowardsPlayer()) - bullet.Direction); 118 | } 119 | break; 120 | } 121 | 122 | //keep the direction between -180 and 180 123 | direction = WrapAngle(direction); 124 | 125 | //The sequence type of change direction is unaffected by the duration 126 | if (ChangeType == NodeType.Absolute) 127 | { 128 | //divide by the amount fo time remaining 129 | direction /= Duration - RunDelta; 130 | } 131 | else if (ChangeType != NodeType.Sequence) 132 | { 133 | //Divide by the duration so we ease into the direction change 134 | direction /= Duration; 135 | } 136 | 137 | return direction; 138 | } 139 | 140 | private static float WrapAngle(float direction) 141 | { 142 | float ret = direction; 143 | 144 | //keep the direction between 0-360 145 | if (ret > 2 * Math.PI) 146 | { 147 | ret -= (float)(2 * Math.PI); 148 | } 149 | else if (ret < 0) 150 | { 151 | ret += (float)(2 * Math.PI); 152 | } 153 | return ret; 154 | } 155 | 156 | } 157 | } -------------------------------------------------------------------------------- /Bullet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace BulletMLLib 6 | { 7 | /// 8 | /// This is the bullet class that outside assemblies will interact with. 9 | /// Just inherit from this class and override the abstract functions! 10 | /// 11 | public abstract class Bullet 12 | { 13 | /// 14 | /// A bullet manager that manages this bullet. 15 | /// 16 | /// My bullet manager. 17 | private readonly IBulletManager _bulletManager; 18 | 19 | /// 20 | /// The tree node that describes this bullet. These are shared between multiple bullets 21 | /// 22 | public BulletMLNode Node { get; private set; } 23 | 24 | /// 25 | /// How fast time moves in this game. 26 | /// Can be used to do slowdown, speedup, etc. 27 | /// 28 | /// The time speed. 29 | public float TimeSpeed { get; set; } 30 | 31 | /// 32 | /// Change the size of this bulletml script 33 | /// If you want to reuse a script for a game but the size is wrong, this can be used to resize it 34 | /// 35 | /// The scale. 36 | public float Scale { get; set; } 37 | 38 | /// 39 | /// The acceleration of this bullet 40 | /// 41 | /// The accel, in pixels/frame^2 42 | public Vector2 Acceleration { get; set; } 43 | 44 | /// 45 | /// Gets or sets the speed 46 | /// 47 | /// The speed, in pixels/frame 48 | public virtual float Speed { get; set; } 49 | 50 | /// 51 | /// A list of tasks that will define this bullets behavior 52 | /// 53 | public List Tasks { get; private set; } 54 | 55 | // X/Y position 56 | public abstract float X { get; set; } 57 | public abstract float Y { get; set; } 58 | 59 | /// 60 | /// Gets my bullet manager. 61 | /// 62 | /// My bullet manager. 63 | public IBulletManager MyBulletManager 64 | { 65 | get 66 | { 67 | return _bulletManager; 68 | } 69 | } 70 | 71 | // who is firing this? Needs to be set by the implementation of CreateBullet 72 | public Emitter Emitter { get; set; } 73 | 74 | 75 | private float _direction; 76 | /// 77 | /// Gets or sets the direction. 78 | /// 79 | /// The direction in radians. 80 | public virtual float Direction 81 | { 82 | get 83 | { 84 | return _direction; 85 | } 86 | set 87 | { 88 | _direction = value; 89 | 90 | //keep the direction between 0-360 91 | if (_direction > 2 * Math.PI) 92 | { 93 | _direction -= (float)(2 * Math.PI); 94 | } 95 | else if (_direction < 0) 96 | { 97 | _direction += (float)(2 * Math.PI); 98 | } 99 | } 100 | } 101 | 102 | /// 103 | /// Convenience property to get the label of a bullet. 104 | /// 105 | /// The label. 106 | public string Label 107 | { 108 | get 109 | { 110 | return Node.Label; 111 | } 112 | } 113 | 114 | /// 115 | /// Initializes a new instance of the class. 116 | /// 117 | /// My bullet manager. 118 | protected Bullet(IBulletManager myBulletManager) 119 | { 120 | _bulletManager = myBulletManager; 121 | 122 | Acceleration = Vector2.zero; 123 | 124 | Tasks = new List(); 125 | 126 | //init these to the default 127 | TimeSpeed = 1.0f; 128 | Scale = 1.0f; 129 | } 130 | 131 | 132 | /// 133 | /// This bullet is fired from another bullet, initialize it from the node that fired it 134 | /// 135 | /// Sub node that defines this bullet 136 | public void InitNode(BulletMLNode subNode) 137 | { 138 | //clear everything out 139 | Tasks.Clear(); 140 | 141 | //Grab that top level node 142 | Node = subNode; 143 | 144 | //found a top num node, add a task for it 145 | BulletMLTask task = new BulletMLTask(subNode, null); 146 | 147 | //parse the nodes into the task list 148 | task.ParseTasks(this); 149 | 150 | //initialize all the tasks 151 | task.InitTask(this); 152 | 153 | Tasks.Add(task); 154 | } 155 | 156 | /// 157 | /// Update this bullet. 158 | /// If you set timespeed to consider Time.deltaTime you can update as much as you want 159 | /// 160 | public virtual void Update() 161 | { 162 | if (Emitter != null) // when emitter is gone, let the bullet fly away without further actions 163 | Tasks.ForEach(x => x.Run(this)); 164 | 165 | float speed = (Speed * TimeSpeed) * Scale; 166 | X += Acceleration.x + (float)(Math.Sin(Direction) * speed); 167 | Y += Acceleration.y + (float)(-Math.Cos(Direction) * speed); 168 | } 169 | 170 | /// 171 | /// Get player direction if we're aiming towards him 172 | /// 173 | /// angle to target the bullet 174 | public float GetAngleTowardsPlayer() 175 | { 176 | Vector2 shipPos = MyBulletManager.PlayerPosition(this); 177 | return (float)Math.Atan2((shipPos.x - X), -(shipPos.y - Y)); 178 | } 179 | 180 | /// 181 | /// Finds the task by label. 182 | /// This recurses into child tasks to find the taks with the correct label 183 | /// Used only for unit testing! 184 | /// 185 | /// The task by label. 186 | /// String label. 187 | public BulletMLTask FindTaskByLabel(string strLabel) 188 | { 189 | //check if any of the child tasks have a task with that label 190 | foreach (BulletMLTask childTask in Tasks) 191 | { 192 | BulletMLTask foundTask = childTask.FindTaskByLabel(strLabel); 193 | if (null != foundTask) 194 | { 195 | return foundTask; 196 | } 197 | } 198 | 199 | return null; 200 | } 201 | 202 | /// 203 | /// Given a label and name, find the task that matches 204 | /// 205 | /// The task by label and name. 206 | /// String label of the task 207 | /// The name of the node the task should be attached to 208 | public BulletMLTask FindTaskByLabelAndName(string strLabel, NodeName name) 209 | { 210 | //check if any of teh child tasks have a task with that label 211 | foreach (BulletMLTask childTask in Tasks) 212 | { 213 | BulletMLTask foundTask = childTask.FindTaskByLabelAndName(strLabel, name); 214 | if (null != foundTask) 215 | { 216 | return foundTask; 217 | } 218 | } 219 | 220 | return null; 221 | } 222 | 223 | 224 | // Needs to be run after node init 225 | public abstract void BulletSpawned(); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /Nodes/BulletMLNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Xml; 5 | 6 | 7 | namespace BulletMLLib 8 | { 9 | /// 10 | /// This is a single node from a BulletML document. 11 | /// Used as the base node for all the other node types. 12 | /// 13 | public class BulletMLNode 14 | { 15 | 16 | internal string PatternName { get; set; } 17 | public string GetPatternName() 18 | { 19 | return !String.IsNullOrEmpty(PatternName) ? PatternName : GetPatternName(this); 20 | } 21 | 22 | private static string GetPatternName(BulletMLNode n) 23 | { 24 | while (true) 25 | { 26 | if (String.IsNullOrEmpty(n.PatternName) && n.Parent != null) 27 | { 28 | n = n.Parent; 29 | continue; 30 | } 31 | 32 | return n.PatternName; 33 | break; 34 | } 35 | } 36 | 37 | /// 38 | /// The XML node name of this item 39 | /// 40 | public NodeName Name { get; private set; } 41 | 42 | /// 43 | /// The type modifier of this node... like is it a sequence, or whatever 44 | /// 45 | private NodeType _nodeType = NodeType.None; 46 | 47 | 48 | public virtual NodeType NodeType 49 | { 50 | get 51 | { 52 | return _nodeType; 53 | } 54 | protected set 55 | { 56 | _nodeType = value; 57 | } 58 | } 59 | 60 | /// 61 | /// The label of this node 62 | /// This can be used by other nodes to reference this node 63 | /// 64 | public string Label { get; protected set; } 65 | 66 | /// 67 | /// An equation used to get a value of this node. 68 | /// 69 | /// The node value. 70 | private readonly BulletMLEquation _nodeEquation = new BulletMLEquation(); 71 | 72 | /// 73 | /// A list of all the child nodes for this dude 74 | /// 75 | public List ChildNodes { get; private set; } 76 | 77 | /// 78 | /// pointer to the parent node of this dude 79 | /// 80 | public BulletMLNode Parent { get; set; } 81 | 82 | 83 | /// 84 | /// Initializes a new instance of the class. 85 | /// 86 | public BulletMLNode(NodeName nodeType) 87 | { 88 | ChildNodes = new List(); 89 | Name = nodeType; 90 | NodeType = NodeType.None; 91 | } 92 | 93 | /// 94 | /// Convert a string to it's NodeType enum equivalent 95 | /// 96 | /// NodeType: the nuem value of that string 97 | /// The string to convert to an enum 98 | public static NodeType StringToType(string str) 99 | { 100 | if (String.IsNullOrEmpty(str)) 101 | return NodeType.None; 102 | 103 | return (NodeType)Enum.Parse(typeof(NodeType), str, true); 104 | 105 | } 106 | 107 | /// 108 | /// Convert a string to it's NodeName enum equivalent 109 | /// 110 | /// NodeName: the nuem value of that string 111 | /// The string to convert to an enum 112 | public static NodeName StringToName(string str) 113 | { 114 | return (NodeName)Enum.Parse(typeof(NodeName), str, true); 115 | } 116 | 117 | /// 118 | /// Gets the root node. 119 | /// 120 | /// The root node. 121 | public BulletMLNode GetRootNode() 122 | { 123 | //recurse up until we get to the root node 124 | return Parent != null ? Parent.GetRootNode() : this; 125 | 126 | } 127 | 128 | /// 129 | /// Find a node of a specific type and label 130 | /// Recurse into the xml tree until we find it! 131 | /// 132 | /// The label node. 133 | /// Label of the node we are looking for 134 | /// name of the node we are looking for 135 | public BulletMLNode FindLabelNode(string strLabel, NodeName name) 136 | { 137 | //this uses breadth first search, since labelled nodes are usually top level 138 | 139 | //Check if any of our child nodes match the request 140 | for (int i = 0; i < ChildNodes.Count; i++) 141 | { 142 | if ((name == ChildNodes[i].Name) && (strLabel == ChildNodes[i].Label)) 143 | { 144 | return ChildNodes[i]; 145 | } 146 | } 147 | 148 | //recurse into the child nodes and see if we find any matches 149 | for (int i = 0; i < ChildNodes.Count; i++) 150 | { 151 | BulletMLNode foundNode = ChildNodes[i].FindLabelNode(strLabel, name); 152 | if (null != foundNode) 153 | { 154 | return foundNode; 155 | } 156 | } 157 | 158 | //didnt find a BulletMLNode with that name :( 159 | return null; 160 | } 161 | 162 | /// 163 | /// Find a parent node of the specified node type 164 | /// 165 | /// The first parent node of that type, null if none found 166 | /// Node type to find. 167 | public BulletMLNode FindParentNode(NodeName nodeType) 168 | { 169 | //first check if we have a parent node 170 | if (null == Parent) return null; 171 | return nodeType == Parent.Name ? Parent : Parent.FindParentNode(nodeType); 172 | } 173 | 174 | /// 175 | /// Gets the value of a specific type of child node for a task 176 | /// 177 | /// The child value. return 0.0 if no node found 178 | /// type of child node we want. 179 | /// Task to get a value for 180 | public float GetChildValue(NodeName name, BulletMLTask task) 181 | { 182 | return (from tree in ChildNodes where tree.Name == name select tree.GetValue(task)).FirstOrDefault(); 183 | } 184 | 185 | /// 186 | /// Get a direct child node of a specific type. Does not recurse! 187 | /// 188 | /// The child. 189 | /// type of node we want. null if not found 190 | public BulletMLNode GetChild(NodeName name) 191 | { 192 | return ChildNodes.FirstOrDefault(node => node.Name == name); 193 | } 194 | 195 | /// 196 | /// Gets the value of this node for a specific instance of a task. 197 | /// 198 | /// The value. 199 | /// Task. 200 | public float GetValue(BulletMLTask task) 201 | { 202 | //send to the equation for an answer 203 | return _nodeEquation.Solve(task.GetParamValue); 204 | } 205 | 206 | /// 207 | /// Parse the specified bulletNodeElement. 208 | /// Read all the data from the xml node into this dude. 209 | /// 210 | /// Bullet node element. 211 | /// 212 | public void Parse(XmlNode bulletNodeElement, BulletMLNode parentNode) 213 | { 214 | // Handle null argument. 215 | if (null == bulletNodeElement) 216 | { 217 | throw new ArgumentNullException("bulletNodeElement"); 218 | } 219 | 220 | //grab the parent node 221 | Parent = parentNode; 222 | 223 | //Parse all our attributes 224 | XmlNamedNodeMap mapAttributes = bulletNodeElement.Attributes; 225 | 226 | for (int i = 0; i < mapAttributes.Count; i++) 227 | { 228 | string name = mapAttributes.Item(i).Name; 229 | string value = mapAttributes.Item(i).Value; 230 | 231 | if (name == "type" && Name == NodeName.Bulletml) continue; 232 | 233 | switch (name) 234 | { 235 | case "type": 236 | NodeType = StringToType(value); 237 | break; 238 | case "label": 239 | Label = value; //label is just a text value 240 | break; 241 | case "name": 242 | PatternName = value; 243 | break; 244 | } 245 | 246 | } 247 | 248 | //parse all the child nodes 249 | if (bulletNodeElement.HasChildNodes) 250 | { 251 | for (XmlNode childNode = bulletNodeElement.FirstChild; 252 | null != childNode; 253 | childNode = childNode.NextSibling) 254 | { 255 | //if the child node is a text node, parse it into this dude 256 | if (XmlNodeType.Text == childNode.NodeType) 257 | { 258 | //Get the text of the child xml node, but store it in THIS bullet node 259 | _nodeEquation.Parse(childNode.Value); 260 | continue; 261 | } 262 | if (XmlNodeType.Comment == childNode.NodeType) 263 | { 264 | //skip any comments in the bulletml script 265 | continue; 266 | } 267 | 268 | //create a new node 269 | BulletMLNode childBulletNode = NodeFactory.CreateNode(StringToName(childNode.Name)); 270 | 271 | //read in the node and store it 272 | childBulletNode.Parse(childNode, this); 273 | ChildNodes.Add(childBulletNode); 274 | } 275 | } 276 | } 277 | 278 | /// 279 | /// Validates the node. 280 | /// Overloaded in child classes to validate that each type of node follows the correct business logic. 281 | /// This checks stuff that isn't validated by the XML validation 282 | /// 283 | public virtual void ValidateNode() 284 | { 285 | //validate all the childe nodes 286 | foreach (BulletMLNode childnode in ChildNodes) 287 | { 288 | childnode.ValidateNode(); 289 | } 290 | } 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /Tasks/BulletMLTask.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System; 4 | 5 | namespace BulletMLLib 6 | { 7 | /// 8 | /// This is a task..each task is the action from a single xml node, for one bullet. 9 | /// basically each bullet makes a tree of these to match its pattern 10 | /// 11 | public class BulletMLTask 12 | { 13 | 14 | /// 15 | /// A list of child tasks of this dude 16 | /// 17 | public List ChildTasks { get; private set; } 18 | 19 | /// 20 | /// The parameter list for this task 21 | /// 22 | public List ParamList { get; private set; } 23 | 24 | /// 25 | /// the parent task of this dude in the tree 26 | /// Used to fetch param values. 27 | /// 28 | public BulletMLTask Owner { get; set; } 29 | 30 | /// 31 | /// The bullet ml node that this dude represents 32 | /// 33 | public BulletMLNode Node { get; private set; } 34 | 35 | /// 36 | /// whether or not this task has finished running 37 | /// 38 | public bool TaskFinished { get; protected set; } 39 | 40 | #region Methods 41 | 42 | /// 43 | /// Initializes a new instance of the class. 44 | /// 45 | /// Node. 46 | /// Owner. 47 | public BulletMLTask(BulletMLNode node, BulletMLTask owner) 48 | { 49 | if (null == node) 50 | { 51 | throw new NullReferenceException("node argument cannot be null"); 52 | } 53 | 54 | ChildTasks = new List(); 55 | ParamList = new List(); 56 | TaskFinished = false; 57 | Owner = owner; 58 | Node = node; 59 | } 60 | 61 | /// 62 | /// Parse a specified node and bullet into this task 63 | /// 64 | /// the node for this dude 65 | /// the bullet this dude is controlling 66 | public virtual void ParseTasks(Bullet bullet) 67 | { 68 | if (null == bullet) 69 | { 70 | throw new NullReferenceException("bullet argument cannot be null"); 71 | } 72 | 73 | foreach (BulletMLNode childNode in Node.ChildNodes) 74 | { 75 | ParseChildNode(childNode, bullet); 76 | } 77 | } 78 | 79 | /// 80 | /// Parse a specified node and bullet into this task 81 | /// 82 | /// 83 | /// the bullet this dude is controlling 84 | public virtual void ParseChildNode(BulletMLNode childNode, Bullet bullet) 85 | { 86 | 87 | switch (childNode.Name) 88 | { 89 | case NodeName.Repeat: 90 | { 91 | 92 | //create a placeholder bulletmltask for the repeat node 93 | RepeatTask repeatTask = new RepeatTask(childNode, this); 94 | 95 | //parse the child nodes into the repeat task 96 | repeatTask.ParseTasks(bullet); 97 | 98 | //store the task 99 | ChildTasks.Add(repeatTask); 100 | } 101 | break; 102 | 103 | case NodeName.Action: 104 | { 105 | //convert the node to an ActionNode 106 | ActionNode myActionNode = childNode as ActionNode; 107 | 108 | //create the action task 109 | ActionTask actionTask = new ActionTask(myActionNode, this); 110 | 111 | //parse the children of the action node into the task 112 | actionTask.ParseTasks(bullet); 113 | 114 | //store the task 115 | ChildTasks.Add(actionTask); 116 | } 117 | break; 118 | 119 | case NodeName.ActionRef: 120 | { 121 | //convert the node to an ActionNode 122 | ActionRefNode myActionNode = childNode as ActionRefNode; 123 | 124 | //create the action task 125 | ActionTask actionTask = new ActionTask(myActionNode, this); 126 | 127 | //add the params to the action task 128 | foreach (BulletMLNode node in childNode.ChildNodes) 129 | { 130 | actionTask.ParamList.Add(node.GetValue(this)); 131 | } 132 | 133 | //parse the children of the action node into the task 134 | actionTask.ParseTasks(bullet); 135 | 136 | //store the task 137 | ChildTasks.Add(actionTask); 138 | } 139 | break; 140 | 141 | case NodeName.ChangeSpeed: 142 | { 143 | ChildTasks.Add(new ChangeSpeedTask(childNode as BulletNode, this)); 144 | } 145 | break; 146 | 147 | case NodeName.ChangeDirection: 148 | { 149 | ChildTasks.Add(new ChangeDirectionTask(childNode as BulletNode, this)); 150 | } 151 | break; 152 | 153 | case NodeName.Fire: 154 | { 155 | //convert the node to a fire node 156 | FireNode myFireNode = childNode as FireNode; 157 | 158 | //create the fire task 159 | FireTask fireTask = new FireTask(myFireNode, this); 160 | 161 | //parse the children of the fire node into the task 162 | fireTask.ParseTasks(bullet); 163 | 164 | //store the task 165 | ChildTasks.Add(fireTask); 166 | } 167 | break; 168 | 169 | case NodeName.FireRef: 170 | { 171 | //convert the node to a fireref node 172 | FireRefNode myFireNode = childNode as FireRefNode; 173 | 174 | //create the fire task 175 | FireTask fireTask = new FireTask(myFireNode.ReferencedFireNode, this); 176 | 177 | //add the params to the fire task 178 | for (int i = 0; i < childNode.ChildNodes.Count; i++) 179 | { 180 | fireTask.ParamList.Add(childNode.ChildNodes[i].GetValue(this)); 181 | } 182 | 183 | //parse the children of the action node into the task 184 | fireTask.ParseTasks(bullet); 185 | 186 | //store the task 187 | ChildTasks.Add(fireTask); 188 | } 189 | break; 190 | 191 | case NodeName.Wait: 192 | { 193 | ChildTasks.Add(new WaitTask(childNode as BulletNode, this)); 194 | } 195 | break; 196 | 197 | case NodeName.Vanish: 198 | { 199 | ChildTasks.Add(new VanishTask(childNode as BulletNode, this)); 200 | } 201 | break; 202 | 203 | case NodeName.Accel: 204 | { 205 | ChildTasks.Add(new AccelTask(childNode as BulletNode, this)); 206 | } 207 | break; 208 | } 209 | } 210 | 211 | /// 212 | /// This gets called when nested repeat nodes get initialized. 213 | /// 214 | /// Bullet. 215 | public virtual void HardReset(Bullet bullet) 216 | { 217 | TaskFinished = false; 218 | 219 | foreach (BulletMLTask task in ChildTasks) 220 | { 221 | task.HardReset(bullet); 222 | } 223 | 224 | SetupTask(bullet); 225 | } 226 | 227 | /// 228 | /// Init this task and all its sub tasks. 229 | /// This method should be called AFTER the nodes are parsed, but BEFORE run is called. 230 | /// 231 | /// the bullet this dude is controlling 232 | public virtual void InitTask(Bullet bullet) 233 | { 234 | TaskFinished = false; 235 | 236 | foreach (BulletMLTask task in ChildTasks) 237 | { 238 | task.InitTask(bullet); 239 | } 240 | 241 | SetupTask(bullet); 242 | } 243 | 244 | /// 245 | /// this sets up the task to be run. 246 | /// 247 | /// Bullet. 248 | protected virtual void SetupTask(Bullet bullet) 249 | { 250 | //overload in child classes 251 | } 252 | 253 | /// 254 | /// Run this task and all subtasks against a bullet 255 | /// This is called once a frame during runtime. 256 | /// 257 | /// RunStatus: whether this task is done, paused, or still running 258 | /// The bullet to update this task against. 259 | public virtual RunStatus Run(Bullet bullet) 260 | { 261 | //run all the child tasks 262 | TaskFinished = true; 263 | foreach (BulletMLTask t in ChildTasks) 264 | { 265 | //is the child task finished running? 266 | if (!t.TaskFinished) 267 | { 268 | //Run the child task... 269 | RunStatus childStatus = t.Run(bullet); 270 | if (childStatus == RunStatus.Stop) 271 | { 272 | //The child task is paused, so it is not finished 273 | TaskFinished = false; 274 | return childStatus; 275 | } 276 | 277 | if (childStatus == RunStatus.Continue) 278 | { 279 | //child task needs to do some more work 280 | TaskFinished = false; 281 | } 282 | } 283 | } 284 | 285 | return (TaskFinished ? RunStatus.End : RunStatus.Continue); 286 | } 287 | 288 | /// 289 | /// Get the value of a parameter of this task. 290 | /// 291 | /// The parameter value. 292 | /// the index of the parameter to get 293 | public float GetParamValue(int iParamNumber) 294 | { 295 | //if that task doesn't have any params, go up until we find one that does 296 | if (ParamList.Count < iParamNumber) 297 | { 298 | //the current task doens't have enough params to solve this value 299 | return null != Owner ? Owner.GetParamValue(iParamNumber) : 0.0f; 300 | } 301 | 302 | //the value of that param is the one we want 303 | return ParamList[iParamNumber - 1]; 304 | } 305 | 306 | /// 307 | /// Gets the node value. 308 | /// 309 | /// The node value. 310 | public float GetNodeValue() 311 | { 312 | return Node.GetValue(this); 313 | } 314 | 315 | /// 316 | /// Finds the task by label. 317 | /// This recurses into child tasks to find the taks with the correct label 318 | /// Used only for unit testing! 319 | /// 320 | /// The task by label. 321 | /// String label. 322 | public BulletMLTask FindTaskByLabel(string strLabel) 323 | { 324 | //check if this is the corretc task 325 | if (strLabel == Node.Label) 326 | { 327 | return this; 328 | } 329 | 330 | //check if any of teh child tasks have a task with that label 331 | foreach (BulletMLTask childTask in ChildTasks) 332 | { 333 | BulletMLTask foundTask = childTask.FindTaskByLabel(strLabel); 334 | if (null != foundTask) 335 | { 336 | return foundTask; 337 | } 338 | } 339 | 340 | return null; 341 | } 342 | 343 | /// 344 | /// given a label and name, find the task that matches 345 | /// 346 | /// The task by label and name. 347 | /// String label of the task 348 | /// the name of the node the task should be attached to 349 | public BulletMLTask FindTaskByLabelAndName(string strLabel, NodeName name) 350 | { 351 | //check if this is the corretc task 352 | if ((strLabel == Node.Label) && (name == Node.Name)) 353 | { 354 | return this; 355 | } 356 | 357 | //check if any of teh child tasks have a task with that label 358 | foreach (BulletMLTask childTask in ChildTasks) 359 | { 360 | BulletMLTask foundTask = childTask.FindTaskByLabelAndName(strLabel, name); 361 | if (null != foundTask) 362 | { 363 | return foundTask; 364 | } 365 | } 366 | 367 | return null; 368 | } 369 | 370 | #endregion //Methods 371 | } 372 | } -------------------------------------------------------------------------------- /Tasks/FireTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | 4 | namespace BulletMLLib 5 | { 6 | /// 7 | /// A task to shoot a bullet 8 | /// 9 | public class FireTask : BulletMLTask 10 | { 11 | #region Members 12 | 13 | /// 14 | /// The direction that this task will fire a bullet. 15 | /// 16 | /// The fire direction. 17 | public float FireDirection { get; private set; } 18 | 19 | /// 20 | /// The speed that this task will fire a bullet. 21 | /// 22 | /// The fire speed. 23 | public float FireSpeed { get; private set; } 24 | 25 | /// 26 | /// The number of times init has been called on this task 27 | /// 28 | /// The number times initialized. 29 | public int NumTimesInitialized { get; private set; } 30 | 31 | /// 32 | /// Flag used to tell if this is the first time this task has been run 33 | /// Used to determine if we should use the "initial" or "sequence" nodes to set bullets. 34 | /// 35 | /// true if initial run; otherwise, false. 36 | public bool InitialRun 37 | { 38 | get 39 | { 40 | return NumTimesInitialized <= 0; 41 | } 42 | } 43 | 44 | /// 45 | /// If this fire node shoots from a bullet ref node, this will be a task created for it. 46 | /// This is needed so the params of the bullet ref can be set correctly. 47 | /// 48 | /// The bullet reference task. 49 | public BulletMLTask BulletRefTask { get; private set; } 50 | 51 | /// 52 | /// The node we are going to use to set the direction of any bullets shot with this task 53 | /// 54 | /// The dir node. 55 | public SetDirectionTask InitialDirectionTask { get; private set; } 56 | 57 | /// 58 | /// The node we are going to use to set the speed of any bullets shot with this task 59 | /// 60 | /// The speed node. 61 | public SetSpeedTask InitialSpeedTask { get; private set; } 62 | 63 | /// 64 | /// If there is a sequence direction node used to increment the direction of each successive bullet that is fired 65 | /// 66 | /// The sequence direction node. 67 | public SetDirectionTask SequenceDirectionTask { get; private set; } 68 | 69 | /// 70 | /// If there is a sequence direction node used to increment the direction of each successive bullet that is fired 71 | /// 72 | /// The sequence direction node. 73 | public SetSpeedTask SequenceSpeedTask { get; private set; } 74 | 75 | #endregion //Members 76 | 77 | 78 | 79 | /// 80 | /// Initializes a new instance of the class. 81 | /// 82 | /// Node. 83 | /// Owner. 84 | public FireTask(BulletMLNode node, BulletMLTask owner) : base(node, owner) 85 | { 86 | NumTimesInitialized = 0; 87 | } 88 | 89 | /// 90 | /// Parse a specified node and bullet into this task 91 | /// 92 | /// the bullet this dude is controlling 93 | public override void ParseTasks(Bullet bullet) 94 | { 95 | if (bullet == null) 96 | { 97 | throw new NullReferenceException("bullet argument cannot be null"); 98 | } 99 | 100 | foreach (BulletMLNode childNode in Node.ChildNodes) 101 | { 102 | ParseChildNode(childNode, bullet); 103 | } 104 | 105 | //Setup all the direction nodes 106 | GetDirectionTasks(this); 107 | GetDirectionTasks(BulletRefTask); 108 | 109 | //setup all the speed nodes 110 | GetSpeedNodes(this); 111 | GetSpeedNodes(BulletRefTask); 112 | } 113 | 114 | /// 115 | /// Parse a specified node and bullet into this task 116 | /// 117 | /// 118 | /// the bullet this dude is controlling 119 | public override void ParseChildNode(BulletMLNode childNode, Bullet bullet) 120 | { 121 | 122 | switch (childNode.Name) 123 | { 124 | case NodeName.BulletRef: 125 | { 126 | //Create a task for the bullet ref 127 | BulletRefNode refNode = childNode as BulletRefNode; 128 | BulletRefTask = new BulletMLTask(refNode.ReferencedBulletNode, this); 129 | 130 | //populate the params of the bullet ref 131 | foreach (BulletMLNode node in childNode.ChildNodes) 132 | { 133 | BulletRefTask.ParamList.Add(node.GetValue(this)); 134 | } 135 | 136 | BulletRefTask.ParseTasks(bullet); 137 | ChildTasks.Add(BulletRefTask); 138 | } 139 | break; 140 | 141 | case NodeName.Bullet: 142 | { 143 | //Create a task for the bullet ref 144 | BulletRefTask = new BulletMLTask(childNode, this); 145 | BulletRefTask.ParseTasks(bullet); 146 | ChildTasks.Add(BulletRefTask); 147 | } 148 | break; 149 | 150 | default: 151 | { 152 | //run the node through the base class if we don't want it 153 | base.ParseChildNode(childNode, bullet); 154 | } 155 | break; 156 | } 157 | } 158 | 159 | /// 160 | /// This gets called when nested repeat nodes get initialized. 161 | /// 162 | /// Bullet. 163 | public override void HardReset(Bullet bullet) 164 | { 165 | //This is the whole point of the hard reset, so the sequence nodes get reset. 166 | NumTimesInitialized = 0; 167 | 168 | base.HardReset(bullet); 169 | } 170 | 171 | /// 172 | /// this sets up the task to be run. 173 | /// 174 | /// Bullet. 175 | protected override void SetupTask(Bullet bullet) 176 | { 177 | //get the direction to shoot the bullet 178 | 179 | //is this the first time it has ran? If there isn't a sequence node, we don't care! 180 | if (InitialRun || (SequenceDirectionTask == null)) 181 | { 182 | //do we have an initial direction node? 183 | if (InitialDirectionTask != null) 184 | { 185 | //Set the fire direction to the "initial" value 186 | float newBulletDirection = InitialDirectionTask.GetNodeValue() * (float) Math.PI / 180.0f; 187 | switch (InitialDirectionTask.Node.NodeType) 188 | { 189 | case NodeType.Absolute: 190 | { 191 | //the new bullet points right at a particular direction 192 | FireDirection = newBulletDirection; 193 | } 194 | break; 195 | 196 | case NodeType.Relative: 197 | { 198 | //the new bullet direction will be relative to the old bullet 199 | FireDirection = newBulletDirection + bullet.Direction; 200 | } 201 | break; 202 | 203 | default: 204 | { 205 | //aim the bullet at the player 206 | FireDirection = newBulletDirection + bullet.GetAngleTowardsPlayer(); 207 | } 208 | break; 209 | } 210 | } 211 | else 212 | { 213 | //There isn't an initial direction task, so just aim at the bad guy. 214 | //aim the bullet at the player 215 | FireDirection = bullet.GetAngleTowardsPlayer(); 216 | } 217 | } 218 | else if (null != SequenceDirectionTask) 219 | { 220 | //else if there is a sequence node, add the value to the "shoot direction" 221 | FireDirection += SequenceDirectionTask.GetNodeValue() * (float)Math.PI / 180.0f; 222 | } 223 | 224 | //Set the speed to shoot the bullet 225 | 226 | //is this the first time it has ran? If there isn't a sequence node, we don't care! 227 | if (InitialRun || (null == SequenceSpeedTask)) 228 | { 229 | //do we have an initial speed node? 230 | if (null != InitialSpeedTask) 231 | { 232 | //set the shoot speed to the "initial" value. 233 | float newBulletSpeed = InitialSpeedTask.GetNodeValue(); 234 | switch (InitialSpeedTask.Node.NodeType) 235 | { 236 | case NodeType.Relative: 237 | { 238 | //the new bullet speed will be relative to the old bullet 239 | FireSpeed = newBulletSpeed + bullet.Speed; 240 | } 241 | break; 242 | 243 | default: 244 | { 245 | //the new bullet shoots at a predeterminde speed 246 | FireSpeed = newBulletSpeed; 247 | } 248 | break; 249 | } 250 | } 251 | else 252 | { 253 | //there is no initial speed task, use the old dude's speed 254 | FireSpeed = bullet.Speed; 255 | } 256 | } 257 | else if (null != SequenceSpeedTask) 258 | { 259 | //else if there is a sequence node, add the value to the "shoot direction" 260 | FireSpeed += SequenceSpeedTask.GetNodeValue(); 261 | } 262 | 263 | //make sure the direction is between 0 and 359 264 | while (FireDirection > Math.PI) 265 | { 266 | FireDirection -= (2.0f * (float)Math.PI); 267 | } 268 | while (-Math.PI > FireDirection) 269 | { 270 | FireDirection += (2.0f * (float)Math.PI); 271 | } 272 | 273 | //make sure we don't overwrite the initial values if we aren't supposed to 274 | NumTimesInitialized++; 275 | } 276 | 277 | /// 278 | /// Run this task and all subtasks against a bullet 279 | /// This is called once a frame during runtime. 280 | /// 281 | /// RunStatus: whether this task is done, paused, or still running 282 | /// The bullet to update this task against. 283 | public override RunStatus Run(Bullet bullet) 284 | { 285 | //Create the new bullet 286 | Bullet newBullet = bullet.MyBulletManager.CreateBullet(bullet.Emitter); 287 | 288 | 289 | if (newBullet == null || newBullet.Emitter == null) 290 | { 291 | //wtf did you do??? 292 | TaskFinished = true; 293 | return RunStatus.End; 294 | } 295 | 296 | //set the location of the new bullet 297 | newBullet.X = bullet.X; 298 | newBullet.Y = bullet.Y; 299 | 300 | //set the direction of the new bullet 301 | newBullet.Direction = FireDirection; 302 | 303 | //set teh speed of the new bullet 304 | newBullet.Speed = FireSpeed; 305 | 306 | //initialize the bullet with the bullet node stored in the Fire node 307 | FireNode myFireNode = Node as FireNode; 308 | 309 | if (myFireNode != null) newBullet.InitNode(myFireNode.BulletDescriptionNode); 310 | newBullet.BulletSpawned(); 311 | //set the owner of all the top level tasks for the new bullet to this dude 312 | foreach (BulletMLTask task in newBullet.Tasks) 313 | { 314 | task.Owner = this; 315 | } 316 | 317 | TaskFinished = true; 318 | return RunStatus.End; 319 | } 320 | 321 | /// 322 | /// Given a node, pull the direction nodes out from underneath it and store them if necessary 323 | /// 324 | /// task to check if has a child direction node. 325 | private void GetDirectionTasks(BulletMLTask taskToCheck) 326 | { 327 | if (taskToCheck == null) 328 | return; 329 | 330 | //check if the dude has a direction node 331 | DirectionNode dirNode = taskToCheck.Node.GetChild(NodeName.Direction) as DirectionNode; 332 | if (null == dirNode) return; 333 | 334 | //check if it is a sequence type of node 335 | if (NodeType.Sequence == dirNode.NodeType) 336 | { 337 | //do we need a sequence node? 338 | if (null == SequenceDirectionTask) 339 | { 340 | //store it in the sequence direction node 341 | SequenceDirectionTask = new SetDirectionTask(dirNode, taskToCheck); 342 | } 343 | } 344 | else 345 | { 346 | //else do we need an initial node? 347 | if (null == InitialDirectionTask) 348 | { 349 | //store it in the initial direction node 350 | InitialDirectionTask = new SetDirectionTask(dirNode, taskToCheck); 351 | } 352 | } 353 | } 354 | 355 | /// 356 | /// Given a node, pull the speed nodes out from underneath it and store them if necessary 357 | /// 358 | /// Task to check. 359 | private void GetSpeedNodes(BulletMLTask taskToCheck) 360 | { 361 | if (null == taskToCheck) 362 | { 363 | return; 364 | } 365 | 366 | //check if the dude has a speed node 367 | BulletNode spdNode = taskToCheck.Node.GetChild(NodeName.Speed) as BulletNode; 368 | if (null != spdNode) 369 | { 370 | //check if it is a sequence type of node 371 | if (NodeType.Sequence == spdNode.NodeType) 372 | { 373 | //do we need a sequence node? 374 | if (SequenceSpeedTask == null) 375 | { 376 | //store it in the sequence speed node 377 | SequenceSpeedTask = new SetSpeedTask(spdNode, taskToCheck); 378 | } 379 | } 380 | else 381 | { 382 | //else do we need an initial node? 383 | if (InitialSpeedTask == null) 384 | { 385 | //store it in the initial speed node 386 | InitialSpeedTask = new SetSpeedTask(spdNode, taskToCheck); 387 | } 388 | } 389 | } 390 | } 391 | 392 | } 393 | } --------------------------------------------------------------------------------