/// <summary>
        /// Handles all firing events including target aquisition and firing.
        /// Events are:
        ///     OnStart() :
        ///         Runs once when the firing system first becomes active
        ///     OnUpdate() :
        ///         Runs each frame while the firing system is active
        ///     OnTargetUpdate() :
        ///         Runs each frame while tracking a target (there is at least one target.)
        ///     OnIdleUpdate() :
        ///         Runs each frame while the firing system is idle (no targets)
        ///     OnFire() :
        ///         Runs when it is time to fire.
        ///
        /// Counter Behavior Notes:
        ///   * If there are no targets. the counter will keep running up.
        ///     This means the next target to enter will be fired upon
        ///     immediatly.
        ///
        ///   * The counter is always active so if the last target exits, then a
        ///     new target enters right after that, there may still be a wait.
        /// </summary>
        private IEnumerator FiringSystem()
        {
            // While (true) because of the timer, we want this to run all the time, not
            //   start and stop based on targets in range
            if (this.initIntervalCountdownAtZero)
            {
                this.fireIntervalCounter = 0;
            }
            else
            {
                this.fireIntervalCounter = this.interval;
            }

            this.targets.Clear();
            this.OnStart();   // EVENT TRIGGER

            while (true)
            {
                // if there is no target, counter++, handle idle behavior, and
                //   try next frame.
                // Will init this.targets for child classes as well.
                this.targets = this.targetTracker.targets;

                if (this.targets.Count != 0)
                {
                    // Let the delegate filter a copy of the list just for the OnFire
                    //   Test. We still want this.targets to remain as is.
                    //   Do this in here to still trigger OnTargetUpdate
                    var targetsCopy = new TargetList();
                    targetsCopy.AddRange(this.targets);

                    if (this.onPreFireDelegates != null)
                    {
                        this.onPreFireDelegates(targetsCopy);
                    }

                    // if all is right, fire
                    if (targetsCopy.Count != 0 && // Incase of pre-fire delegate changes
                        this.fireIntervalCounter <= 0 &&
                        this.isLockedOnTarget)    // Always true if WaitForAlignment is OFF
                    {
                        this.OnFire();
                        this.fireIntervalCounter = this.interval;  // Reset
                    }
                    else if (this.debugLevel > DEBUG_LEVELS.Off)
                    {
                        // Just for debug. Show a gizmo line to each target being tracked
                        //   OnFire() has another color, so keep this here where this
                        //   won't overlay the OnFired line.
                        foreach (Target target in targets)
                        {
                            Debug.DrawLine
                            (
                                this.emitter.position,
                                target.transform.position,
                                Color.gray
                            );
                        }
                    }

                    // Update event while tracking a target
                    this.OnTargetUpdate(targets);   // EVENT TRIGGER
                }
                else
                {
                    // Update event while NOT tracking a target
                    this.OnIdleUpdate();   // EVENT TRIGGER
                }

                this.fireIntervalCounter -= Time.deltaTime;

                // Update event no matter what
                this.OnUpdate();   // EVENT TRIGGER

                // Stager calls to get Target (the whole system actually)
                yield return(null);
            }
        }
        /// <summary>
        /// Destroys the projectile on impact and finds objects in range to
        ///	affect if they share the same tag as target.
        /// </summary>
        public void DetonateProjectile()
        {
            // Prevent being run more than once in a frame where it has already
            //   been destroyed.
            if (!this.gameObject.activeInHierarchy)
            {
                return;
            }

            // Build a new list of targets depending on the options used
            var targetList = new TargetList();

            if (this.areaHit)
            {
                // This is turned back off OnDisable() (base class)
                this.perimeter.enabled = true;

                targetList.AddRange(this.targets); // Add all targets in range
            }
            else
            {
                if (this.target != Target.Null)
                {
                    targetList.Add(this.target); // Add projectile target
                }
            }

            if (this.debugLevel > DEBUG_LEVELS.Off)
            {
                string msg = string.Format("Detonating with targets: {0}", targetList);
                Debug.Log(string.Format("Projectile ({0}): {1}", this.name, msg));
            }

            // Create a new list of targets which have this target tracker reference.
            //   This is for output so targets which are handled at all by this Projectile
            //   are stamped with a reference.
            var    targetCopies = new TargetList();
            Target target;

            foreach (Target inTarget in targetList)
            {
                if (inTarget == Target.Null)
                {
                    continue;
                }

                // Can't edit a struct in a foreach loop, so need to copy and store
                target            = new Target(inTarget);
                target.projectile = this;  // Add reference. null before t
                targetCopies.Add(target);

                switch (this.notifyTargets)
                {
                case NOTIFY_TARGET_OPTIONS.Direct:
                    target.targetable.OnHit(this.effectsOnTarget, target, this.collider);
                    break;
                }

                // Just for debug. Show a gizmo line when firing
                if (this.debugLevel > DEBUG_LEVELS.Off)
                {
                    Debug.DrawLine
                    (
                        this.xform.position,
                        target.transform.position,
                        Color.red
                    );
                }
            }

            switch (this.notifyTargets)
            {
            case NOTIFY_TARGET_OPTIONS.Direct:
                this.SpawnDetonatorPrefab(false);
                break;

            case NOTIFY_TARGET_OPTIONS.PassToDetonator:
                this.SpawnDetonatorPrefab(true);
                break;
            }


            // Trigger delegates
            if (this.OnDetonationDelegates != null)
            {
                this.OnDetonationDelegates(targetCopies);
            }

            // Clean-up in case this instance is used in a pooling system like PoolManager
            this.target = Target.Null;

            InstanceManager.Despawn(this.transform);
        }