using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Diagnostics; namespace CarFire { /// /// The Script class handles the parsing and execution of lists /// of functions. Scripts are closely related to triggers. /// public class Script { #region Public Properties /// /// Determine if the script is in the process of being run. /// public bool IsRunning { get { return mIsRunning; } } #endregion #region Public Methods /// /// Construct a script object with code and a game reference. /// /// The script code. /// A game reference. public Script(string code, object bindings) { mBindings = bindings; string[] functions = Parse.List(code); if (functions != null) { foreach (string function in functions) { string[] parts = Parse.Function(function); if (parts != null) { string[] args = Parse.List(parts[1]); if (args != null) { Function func = new Function(parts[0], args); mFunctions.Add(func); } else throw new Exception("Arguments could not be parsed: " + parts[1]); } else throw new Exception("Function could not be parsed: " + function); } } else throw new Exception("Script could not be parsed: " + code); } /// /// Start execution of the script. If there is no need to break /// execution before the script ends, it will finish before this method /// call ends. Otherwise, execution will be delayed and will finish sometime /// in the future. This will execute each function in sequence as long /// as each function evaluates to true. If a function does not evaluate to true, /// this method will return and execution will be delayed. In either case, /// the evaluation of the last function is returned by this method. /// /// The player associated with this script. /// Evaluation of the last function call. public bool Run(Player player) { bool result = false; if (!mIsRunning) { mIsRunning = true; mRunningIndex = 0; } for (; mRunningIndex < mFunctions.Count; mRunningIndex++) { result = Call(mRunningIndex, player); if (!result) break; } if (mRunningIndex >= mFunctions.Count - 1) mIsRunning = false; return result; } public void Reset() { mIsRunning = false; } #endregion #region Private Methods /// /// Call a function in the last at a certain index. /// /// The function index. /// The associated player object. /// The evaluation of the function. bool Call(int index, Player player) { Debug.Assert(0 <= index && index < mFunctions.Count); try { object[] args = new object[2]; args[0] = player; args[1] = mFunctions[index].Arguments; return (bool)mBindings.GetType().InvokeMember(mFunctions[index].Name, BindingFlags.InvokeMethod, null, mBindings, args); } #pragma warning disable 0168 catch (System.MissingMethodException ex) #pragma warning restore 0168 { throw new Exception("Function could not be found: " + mFunctions[index].Name); } } #endregion #region Private Types class Function { public string Name { get { return mName; } } public string[] Arguments { get { return mArgs; } } public Function(string name, string[] args) { mName = name; mArgs = args; } string mName; string[] mArgs; } #endregion #region Private Variables object mBindings; List mFunctions = new List(); bool mIsRunning; int mRunningIndex; #endregion } }