public bool Damage(SCAttack attack, IntegerVector origin, IntegerVector hitPoint)
    {
        if (this.Invincible)
            return false;

        //TODO - Normalize knockback effect to 16 directions
        Vector2 knockbackDirection = ((Vector2)(hitPoint - origin)).normalized;
        this.Actor.Velocity = knockbackDirection * attack.KnockbackPower;
        this.Invincible = true;
        _invincibilityTimer.reset(attack.HitInvincibilityDuration + FREEZE_FRAMES);
        _invincibilityTimer.start();
        this.localNotifier.SendEvent(_freezeFrameEvent);
        _hitStunEvent.NumFrames = attack.HitStunDuration;
        this.localNotifier.SendEvent(_hitStunEvent);
        return true;
    }
 private SCAttack.HitboxKeyframe? getKeyframeForUpdateFrame(SCAttack attack, int updateFrame)
 {
     SCAttack.HitboxKeyframe? keyframe = null;
     for (int i = 0; i < attack.HitboxKeyframes.Length; ++i)
     {
         if (attack.HitboxKeyframes[i].Frame <= updateFrame)
             keyframe = attack.HitboxKeyframes[i];
         else
             break;
     }
     return keyframe;
 }
 public SCAttack.VelocityBoost? GetCurrentVelocityBoost(SCAttack currentAttack)
 {
     return currentAttack.GetVelocityBoostForFrame(this.Animator.Elapsed);
 }
    public void UpdateHitBoxes(SCAttack currentAttack, SCAttack.HurtboxState hurtboxState)
    {
        if (currentAttack == null)
        {
            for (int i = 0; i < this.DamageBoxes.Length; ++i)
            {
                this.DamageBoxes[i].transform.localPosition = Vector2.zero;
                this.DamageBoxes[i].enabled = false;
            }
        }
        else
        {
            SCAttack.HitboxKeyframe? keyframe = getKeyframeForUpdateFrame(currentAttack, this.Animator.Elapsed);
            if (keyframe.HasValue)
            {
                if (keyframe.Value.HurtboxState != hurtboxState)
                {
                    bool success = this.HurtboxChangeCallback(keyframe.Value.HurtboxState);
                    if (!success)
                        return;
                }

                GameObject collided = null;
                IntegerRectCollider collider = null;
                for (int i = 0; i < this.DamageBoxes.Length; ++i)
                {
                    if (i < keyframe.Value.HitboxCount)
                    {
                        this.DamageBoxes[i].enabled = true;
                        this.DamageBoxes[i].transform.localPosition = (Vector2)keyframe.Value.HitboxPositions[i];
                        this.DamageBoxes[i].Size = keyframe.Value.HitboxSizes[i];

                        if (collided == null)
                        {
                            collided = this.DamageBoxes[i].CollideFirst(0, 0, this.DamagableLayers);
                            if (collided != null)
                                collider = this.DamageBoxes[i];
                        }
                    }
                    else
                    {
                        this.DamageBoxes[i].enabled = false;
                    }
                }

                // Apply damage if we hit
                if (collided != null)
                {
                    Damagable otherDamagable = collided.GetComponent<Damagable>();
                    if (otherDamagable != null)
                    {
                        IntegerVector hitPoint = collided.GetComponent<IntegerCollider>().ClosestContainedPoint((Vector2)collider.transform.position);
                        bool landedHit = otherDamagable.Damage(currentAttack, (Vector2)this.Actor.transform.position, hitPoint);

                        if (landedHit)
                        {
                            if (_freezeFrameEvent == null)
                                _freezeFrameEvent = new FreezeFrameEvent(Damagable.FREEZE_FRAMES);
                            this.localNotifier.SendEvent(_freezeFrameEvent);
                            if (this.Damagable != null)
                                this.Damagable.SetInvincible(Damagable.FREEZE_FRAMES);
                            PooledObject hitEffect = this.HitEffect.Retain();
                            hitEffect.transform.position = (Vector2)hitPoint;
                            hitEffect.GetComponent<HitEffectHandler>().InitializeWithFreezeFrames(Damagable.FREEZE_FRAMES);
                        }
                    }
                }
            }
        }
    }
 private void updateHurtboxForState(SCAttack.HurtboxState state)
 {
     switch (state)
     {
         default:
         case SCAttack.HurtboxState.Normal:
             this.Hurtbox.Offset = this.MoveSet.NormalHitboxSpecs.Center;
             this.Hurtbox.Size = this.MoveSet.NormalHitboxSpecs.Size;
             break;
         case SCAttack.HurtboxState.Ducking:
             this.Hurtbox.Offset = this.MoveSet.DuckHitboxSpecs.Center;
             this.Hurtbox.Size = this.MoveSet.DuckHitboxSpecs.Size;
             break;
     }
 }
    private bool attemptHurtboxStateChange(SCAttack.HurtboxState newState)
    {
        if (newState == this.HurtboxState)
            return true;
        updateHurtboxForState(newState);
        if (this.Hurtbox.CollideFirst(0, 0, this.HaltMovementMask) != null)
        {
            // We collided when trying to stand up, so boot out of attack into ducking position
            _currentAttack = null;
            updateHurtboxForState(this.HurtboxState);
            return false;
        }

        this.HurtboxState = newState;
        return true;
    }
 public CharacterUpdateFinishedEvent(SCAttack currentAttack = null)
 {
     this.Name = NAME;
     this.CurrentAttack = currentAttack;
 }