protected void Attack(BaseEntity target, bool advanceTime = true)
    {
        if (advanceTime)
        {
            atkTimer.AdvanceTime();
        }

        // See if the move has ended
        if (atkTimer.CurFrame() >= curAttack.GetTotalFrames())
        {
            EndAttack();
            return;
        }

        // Heal the user on the startup frame
        if (curAttack.isHeal)
        {
            if (!hasHitTarget && curAttack.IsActive(atkTimer.CurFrame()))
            {
                hasHitTarget = true;
                curHP       += curAttack.power;
            }
        }

        // Check if the move connected during its active frames
        else if (curAttack.IsActive(atkTimer.CurFrame()) && !hitList.Contains(target))
        {
            // Setup knockback and pushback data
            Vector2 userDir = new Vector2(Mathf.Sign(transform.localScale.x), 1);

            // Check if attack connected with any target if it has a hitbox
            var tarHurt = target.GetHurtBox();

            var wasTipper = false;
            var power     = curAttack.power;
            var isHit     = IsHitTarget(curAttack.basespots, gameObject, tarHurt, target.gameObject);
            if (!isHit)
            {
                isHit = IsHitTarget(curAttack.sweetspots, gameObject, tarHurt, target.gameObject);
                if (isHit)
                {
                    wasTipper = true;
                    power     = (int)(power * constants.critMultiplier);
                }
            }
            else if (!hasHitTarget && curAttack.pushback != Vector2.zero)
            {
                Vector2 push = curAttack.pushback * userDir;
                // if (Math.Abs(push.y) < Mathf.Epsilon && velocity.y <= 0) velocity = velocity + push;
                // else velocity = curAttack.pushback * playerDir;
                if (push.y > 0 && velocity.y < push.y)
                {
                    velocity = curAttack.pushback * userDir;
                }
                else
                {
                    velocity = velocity + push / 2;
                }
            }

            // Tell the target that it has been attacked
            if (isHit)
            {
                hitList.Add(target);
                hasHitTarget = true;
                target.Attacked(power, curAttack.knockback * userDir, wasTipper);
            }
        }

        if (curAttack.projectile)
        {
            HandleProjectile(false);
        }
    }