private IEnumerator DoSwing(SwingEvent swing)
    {
        gameObject.SendMessage("OnSwingBegan");

        float warmupTime = swing.rate / 3.0f;
        float recoveryTime = warmupTime * 2.0f; // FIXME: this shouldn't be hardcoded! makes combat boring!

        yield return new WaitForSeconds(warmupTime);

        var bulletPos = hotspot.position;

        if(swing.reach != 0.0f)
            bulletPos += owner.transform.forward * swing.reach;

        var clone = Instantiate(swing.prefab, bulletPos, hotspot.rotation) as GameObject;
        var b = clone.GetComponent(typeof(Bullet)) as Bullet;
        if(b != null)
        {
            swing.damage.team = damageable.team;
            swing.damage.owner = owner.gameObject;
            b.data = swing.damage;
            Physics.IgnoreCollision(clone.collider, owner.collider);
        }

        // turn on debug to see swing colliders
        if(debug)
        {
            clone.renderer.enabled = true;
        }

        yield return new WaitForSeconds(recoveryTime);

        gameObject.SendMessage("OnSwingEnded");
    }
    public bool OnFire(SwingEvent swingEvent)
    {
        if(cooldown > 0.0f) return false;

        cooldown = swingEvent.rate;

        StartCoroutine(DoSwing(swingEvent));

        return true;
    }
    public bool OnFire(SwingEvent swingEvent)
    {
        if (cooldown > 0.0f)
        {
            return(false);
        }

        cooldown = swingEvent.rate;

        StartCoroutine(DoSwing(swingEvent));

        return(true);
    }
    private IEnumerator DoSwing(SwingEvent swing)
    {
        gameObject.SendMessage("OnSwingBegan");

        float warmupTime   = swing.rate / 3.0f;
        float recoveryTime = warmupTime * 2.0f;         // FIXME: this shouldn't be hardcoded! makes combat boring!

        yield return(new WaitForSeconds(warmupTime));

        var bulletPos = hotspot.position;

        if (swing.reach != 0.0f)
        {
            bulletPos += owner.transform.forward * swing.reach;
        }

        var clone = Instantiate(swing.prefab, bulletPos, hotspot.rotation) as GameObject;
        var b     = clone.GetComponent(typeof(Bullet)) as Bullet;

        if (b != null)
        {
            swing.damage.team  = damageable.team;
            swing.damage.owner = owner.gameObject;
            b.data             = swing.damage;
            Physics.IgnoreCollision(clone.collider, owner.collider);
        }

        // turn on debug to see swing colliders
        if (debug)
        {
            clone.renderer.enabled = true;
        }

        yield return(new WaitForSeconds(recoveryTime));

        gameObject.SendMessage("OnSwingEnded");
    }
    IEnumerator doSwing(SwingEvent swingEvent)
    {
        if (status.dodging)
        {
            //animated.animation.Stop();// Stop("bjorn_dodge");
            //animated.animation.Play("bjorn_idle");
            status.dodging = false;
            // dumb hack because Unity animation system is dumb dumb dumb
            //yield return new WaitForSeconds(0.02f);
        }

        /*
         * bool stunAttack = (swingEvent.damage.effects.ContainsKey("OnStun"));
         * if(stunAttack)
         * {
         *      var fx = Resources.Load("Effects/effect_fire_sword") as GameObject;
         *
         *      var fireEffect = Instantiate(fx, mainWeapon.transform.position, mainWeapon.transform.rotation) as GameObject;
         *      fireEffect.transform.parent = mainWeapon.transform;
         *      fireEffect.name = "stunEffect";
         * }
         */

        status.attacking = true;
        gameObject.BroadcastMessage("OnDisable");

        if (telegraphAttacks && swingNumber <= 1)
        {
            animated.animation.Play(swingEvent.animation.name + "_telegraph");

            activeTelegraph.gameObject.SetActive(true);
            //activeTelegraphCircle.gameObject.SetActive(true);
            //activeTelegraphCircle.localScale = Vector3.one;
            //activeTelegraphCircle.SendMessage("OnTelegraph", telegraphRate * 2.0f);
            yield return(new WaitForSeconds(telegraphRate - 0.2f));

            // this is a bit heavy and shouldn't be living here
            foreach (Transform decalChild in activeTelegraph)
            {
                decalChild.renderer.material.SetColor("_TintColor", Color.white);
            }
            status.unbalanced = true;
            yield return(new WaitForSeconds(0.2f));
        }

        // if we don't telegraph, set unbalanced for the duration of the attack
        status.unbalanced = true;

        // don't remove telegraph until the attack chain is over
        int maxSwings = 1;        //mainWeapon.swingPrefabs.Length;

        if (activeTelegraph != null && (swingNumber == maxSwings || maxSwings <= 1))
        {
            activeTelegraph.gameObject.SetActive(false);
            //activeTelegraphCircle.gameObject.SetActive(false);
            foreach (Transform decalChild in activeTelegraph)
            {
                decalChild.renderer.material.SetColor("_TintColor", originalTelegraphColor);
            }
        }

        // step into the attack
        //iTween.MoveTo(gameObject, transform.position + (transform.forward * 10.0f), 0.2f);
        dude.AddForce(transform.forward * swingEvent.step * Time.fixedDeltaTime);

        // fire actual weapon

        gameObject.SendMessage("OnFire");

        /*
         * if(mainWeapon.swingSound != null)
         * {
         *      float originalPitch = audio.pitch;
         *      audio.pitch = (1.0f - swingEvent.rate);// + 0.5f;
         *      //audio.PlayOneShot(mainWeapon.swingSound);
         *      audio.pitch = originalPitch;
         * }
         */
        // dumb hack because Unity animation system is dumb dumb dumb
        //animated.animation.Stop();
        //animated.animation.Play("bjorn_idle");
        //yield return new WaitForSeconds(0.02f);
        yield return(new WaitForEndOfFrame());

        //string animName = mainWeapon.swingAnimations[swingNumber - 1].name;
        if (swingEvent.animation != null)
        {
            string animName = swingEvent.animation.name;
            animated.animation[animName].speed = attackAnimationRatio / swingEvent.rate;
            animated.animation.Play(animName, PlayMode.StopAll);            //CrossFade(animName);
        }

        yield return(new WaitForSeconds(swingEvent.rate * (1.0f - attackCancelWindow)));

        if (swingNumber < maxSwings)
        {
            status.attacking         = false;
            status.unbalanced        = false;
            status.attackingRecovery = true;
        }

        yield return(new WaitForSeconds(swingEvent.rate * attackCancelWindow));

        gameObject.BroadcastMessage("OnEnable");

        status.attacking         = false;
        status.unbalanced        = false;
        status.attackingRecovery = false;

        /*
         * if(stunAttack)
         * {
         *      Transform fx = mainWeapon.transform.FindChild("stunEffect");
         *      fx.particleEmitter.emit = false;
         *      fx.parent = null;
         * }
         */

        // reset swing animation entirely
        ResetAnimation();

        gameObject.SendMessage("OnFollowUp");
    }
    public bool OnAttack()
    {
        // actually just see if I'm disabled or not
        if (dude.blocking || status.stunned || attackCooldown > 0.0f)
        {
            return(false);
        }

        if (status.dodging)
        {
            dude.velocity = Vector3.zero;
        }

        int maxSwings = 3;

        // in certain states, if attack is pressed,
        // queue up another attack
        if (disabled && !status.stunned && (status.attacking || status.attackingRecovery || status.dodging))       // && followUpTimer <= followUpWindow/2)
        {
            if (swingNumber + 1 < maxSwings)
            {
                followUpQueued = true;
            }
            else
            {
                followUpQueued = false;
            }
            //dude.Look();
            return(false);
        }

        if (swingNumber <= maxSwings && alwaysFollowUp)
        {
            followUpQueued = true;
        }

        //gameObject.BroadcastMessage("OnFire");
        if (true)          //mainWeapon.CanFire() )
        {
            // ran out of time for follow-up swings, so act like this is the first
            if (followUpTimer <= 0.0f)
            {
                swingNumber   = 0;
                followUpTimer = followUpWindow + 0.001f;
            }

            // 1st swing
            if (followUpTimer > 0.0f && swingNumber < maxSwings)
            {
                swingNumber += 1;
                SwingEvent swing = attackChain.comboChain[0];
                //SwingEvent swing = new SwingEvent();

                // it would be better to move this logic to the weapon

                /*
                 * if(mainWeapon.lastSwingStuns && swingNumber == maxSwings)
                 * {
                 *      swing.damage.effects["OnStun"] = mainWeapon.stunPower;
                 * }
                 */
                StartCoroutine("doSwing", swing);

                if (swingNumber < maxSwings)
                {
                    followUpTimer = swing.rate + followUpWindow;
                }
                else
                {
                    // final swing -- reset swinging
                    swingNumber    = 0;
                    followUpTimer  = 0.0f;
                    followUpQueued = false;
                    attackCooldown = attackDelay;
                }
                return(true);
            }
        }

        return(false);
    }
    IEnumerator doSwing(SwingEvent swingEvent)
    {
        if(status.dodging)
        {
            //animated.animation.Stop();// Stop("bjorn_dodge");
            //animated.animation.Play("bjorn_idle");
            status.dodging = false;
            // dumb hack because Unity animation system is dumb dumb dumb
            //yield return new WaitForSeconds(0.02f);
        }

        /*
        bool stunAttack = (swingEvent.damage.effects.ContainsKey("OnStun"));
        if(stunAttack)
        {
            var fx = Resources.Load("Effects/effect_fire_sword") as GameObject;

            var fireEffect = Instantiate(fx, mainWeapon.transform.position, mainWeapon.transform.rotation) as GameObject;
            fireEffect.transform.parent = mainWeapon.transform;
            fireEffect.name = "stunEffect";
        }
        */

        status.attacking = true;
        gameObject.BroadcastMessage("OnDisable");

        if(telegraphAttacks && swingNumber <= 1)
        {
            animated.animation.Play(swingEvent.animation.name + "_telegraph");

            activeTelegraph.gameObject.SetActive(true);
            //activeTelegraphCircle.gameObject.SetActive(true);
            //activeTelegraphCircle.localScale = Vector3.one;
            //activeTelegraphCircle.SendMessage("OnTelegraph", telegraphRate * 2.0f);
            yield return new WaitForSeconds( telegraphRate - 0.2f );
            // this is a bit heavy and shouldn't be living here
            foreach(Transform decalChild in activeTelegraph)
            {
                decalChild.renderer.material.SetColor("_TintColor", Color.white);
            }
            status.unbalanced = true;
            yield return new WaitForSeconds( 0.2f );
        }

        // if we don't telegraph, set unbalanced for the duration of the attack
        status.unbalanced = true;

        // don't remove telegraph until the attack chain is over
        int maxSwings = 1;//mainWeapon.swingPrefabs.Length;
        if(activeTelegraph != null && (swingNumber == maxSwings || maxSwings <= 1))
        {
            activeTelegraph.gameObject.SetActive(false);
            //activeTelegraphCircle.gameObject.SetActive(false);
            foreach(Transform decalChild in activeTelegraph)
            {
                decalChild.renderer.material.SetColor("_TintColor", originalTelegraphColor);
            }
        }

        // step into the attack
        //iTween.MoveTo(gameObject, transform.position + (transform.forward * 10.0f), 0.2f);
        dude.AddForce(transform.forward * swingEvent.step * Time.fixedDeltaTime);

        // fire actual weapon

        gameObject.SendMessage("OnFire");
        /*
        if(mainWeapon.swingSound != null)
        {
            float originalPitch = audio.pitch;
            audio.pitch = (1.0f - swingEvent.rate);// + 0.5f;
            //audio.PlayOneShot(mainWeapon.swingSound);
            audio.pitch = originalPitch;
        }
        */
        // dumb hack because Unity animation system is dumb dumb dumb
        //animated.animation.Stop();
        //animated.animation.Play("bjorn_idle");
        //yield return new WaitForSeconds(0.02f);
        yield return new WaitForEndOfFrame();

        //string animName = mainWeapon.swingAnimations[swingNumber - 1].name;
        if(swingEvent.animation != null)
        {
            string animName = swingEvent.animation.name;
            animated.animation[animName].speed = attackAnimationRatio / swingEvent.rate;
            animated.animation.Play(animName, PlayMode.StopAll);//CrossFade(animName);
        }

        yield return new WaitForSeconds( swingEvent.rate * (1.0f - attackCancelWindow) );

        if(swingNumber < maxSwings)
        {
            status.attacking = false;
            status.unbalanced = false;
            status.attackingRecovery = true;
        }

        yield return new WaitForSeconds( swingEvent.rate * attackCancelWindow );
        gameObject.BroadcastMessage("OnEnable");

        status.attacking = false;
        status.unbalanced = false;
        status.attackingRecovery = false;
        /*
        if(stunAttack)
        {
            Transform fx = mainWeapon.transform.FindChild("stunEffect");
            fx.particleEmitter.emit = false;
            fx.parent = null;
        }
        */

        // reset swing animation entirely
        ResetAnimation();

        gameObject.SendMessage("OnFollowUp");
    }