using System; using Chernobyl.Collections.Generic.Event; using Chernobyl.Dependency; using Chernobyl.Measures.Time; using Chernobyl.Update; using Chernobyl.Utility; namespace Chernobyl.StateMachine { /// /// An that performs the following: /// /// /// When the /// is entered (i.e. is raised) the /// instance will start a countdown (the /// count of which is specified by the user). /// /// /// When the countdown reaches 0, the /// instance will set the instance onto the /// of its . /// This will, of course, cause the to become /// an inactive (i.e. /// will be raised). /// /// /// If the instance becomes /// an inactive before the countdown has /// reached 0, the countdown will be paused and /// will not become the active state. The countdown can be reactivated /// and returned to its previous count by activating the /// again. /// /// /// public class Countdown : UpdateableState { /// /// Initializes a new instance of the class. /// /// The instance that takes services and gives /// them out. /// The instance that is to become the active state /// (i.e. set on the property) of this /// ) after the countdown timer has reached 0 or /// null if null is to be set on the /// property. /// The amount of time, in milliseconds, between this /// becoming active and the /// becoming active. /// Thrown if /// is null. /// Thrown if /// is less than or equal to zero. public Countdown(IEventCollection services, IState next, Milliseconds count) { services.ThrowIfNull("services"); Next = next; CurrentCount = Count = count; Entered += OnEntered; Left += OnLeft; services.Inject(this); } /// /// The instance that is to become the active state (i.e. set on the /// property of this ) /// after the countdown timer has reached 0 or null if null is to be set /// on the property. /// public IState Next { get; set; } /// /// The amount of time, in milliseconds, between this /// becoming active and the /// becoming active. /// = /// Thrown if the value set /// on this property is less than or equal to zero. public Milliseconds Count { get { return _count; } set { if(value <= 0) throw new ArgumentOutOfRangeException("value", value, "The countdown amount cannot be less than or equal to " + "zero. Please provide a value greater than zero."); _count = value; } } /// /// The current amount of time, in milliseconds, before this instance /// becomes inactive and makes the active state. /// public Milliseconds CurrentCount { get; private set; } /// /// Checks the current countdown and updates this instance's children /// instances. /// /// The amount of time that has /// passed since the last call to this update. public override void Update(TimeSpan deltaTime) { CurrentCount -= (Milliseconds)deltaTime.Milliseconds; if(CurrentCount <= 0) { // Don't let the CurrentCount go below 0 (because it wouldn't // make much sense) and make the next state active (deactivating // this state in the process). CurrentCount = (Milliseconds)0; ParentState.ChildState = Next; } base.Update(deltaTime); } /// /// The instance that is to be used to time this /// instance. /// [Inject(Type = typeof(IRootUpdateable))] public IUpdateable Updateable { get { return _updateable; } set { value.ThrowIfNull("value"); IUpdateable previousValue = _updateable; _updateable = value; if (previousValue != null) Chernobyl.Update.Updateable.SeparateParentChild(previousValue, this); // Only request updates if this IState is active. if (this.IsActive()) Chernobyl.Update.Updateable.MakeParentChild(Updateable, this); } } /// /// An event handler that is invoked when this instance becomes an active /// state. This method ensures the is, if /// below or equal to zero, set to and makes sure /// this instance is added to (if available) so /// that it can be updated. /// /// The sender of the event. /// The instance containing the /// event data. void OnEntered(object sender, EventArgs e) { if (CurrentCount <= 0) CurrentCount = Count; // Updates are now needed so that the countdown can proceed. if(Updateable != null) Chernobyl.Update.Updateable.MakeParentChild(Updateable, this); } /// /// An event handler that is invoked when this instance becomes an /// inactive state. This method ensures this instance is removed from /// (if available) so that it will not be /// updated. /// /// The sender of the event. /// The instance containing the /// event data. void OnLeft(object sender, EventArgs e) { // Updates are no longer needed. We'll wait for this state to // become active again before re-enabling updates. if (Updateable != null) Chernobyl.Update.Updateable.SeparateParentChild(Updateable, this); } /// /// The backing field to . /// Milliseconds _count; /// /// The backing field to . /// IUpdateable _updateable; } }