Beispiel #1
0
void CreateDistance::DetermineChain()
{
    //If we're not in a state to interupt, just continue with what we've got going
    if((m_chain != NULL) && (!m_chain->IsInterruptible()))
    {
        m_chain->PressButtons();
        return;
    }
    else
    {
        //If we get here, make a whole new chain, don't continue an old one
        delete m_chain;
        m_chain = NULL;
    }

    bool isOnRight = m_state->m_memory->player_one_x < m_state->m_memory->player_two_x;
    if(m_state->m_memory->player_two_action == SHIELD_RELEASE)
    {
        CreateChain2(Wavedash, isOnRight);
        m_chain->PressButtons();
        return;
    }

    double distance = std::abs(m_state->m_memory->player_one_x - m_state->m_memory->player_two_x);

    //If we're right on the edge of range, and are walkign inwards, just wavedash back
    if(distance < 45 &&
        (m_state->m_memory->player_two_action == WALK_SLOW ||
        m_state->m_memory->player_two_action == WALK_MIDDLE ||
        m_state->m_memory->player_two_action == WALK_FAST))
    {
        CreateChain2(Wavedash, isOnRight);
        m_chain->PressButtons();
        return;
    }

    if(m_state->getStageEdgeGroundPosition() - std::abs(m_state->m_memory->player_two_x) < 40)
    {
        CreateChain(Nothing);
        m_chain->PressButtons();
    }
    else
    {
        CreateChain2(ShortHopLaser, RETREATING);
        m_chain->PressButtons();
    }

}
Beispiel #2
0
void DI::DetermineChain()
{   
    //Start SDI Section
    if(m_hitlagFramesLeftP2 > 1)
    {
        if(m_isFacingRightP1 == true)
        {
            CreateChain2(SmashDI, m_isFacingRightP1);
            m_chain->PressButtons();
        }
        if(m_isFacingRightP1 == false)
        {
            CreateChain2(SmashDI, m_isFacingRightP1);
            m_chain->PressButtons();
        }
    }
    //End SDI, Begin optimal DI section
    if(m_hitlagFramesLeftP2 == 1)
    {
        //TODO: implement Trajectory DI
    }
}
Beispiel #3
0
void Parry::DetermineChain()
{
    if(m_state->m_memory->player_two_action == EDGE_HANGING)
    {
        CreateChain2(EdgeAction, Controller::BUTTON_MAIN);
        m_chain->PressButtons();
        return;
    }

    //TODO: SWORD_DANCE_4_LOW is a multi-hit attack. Let's handle that differently. Maybe just light shield
    if(m_state->m_memory->player_one_action == ACTION::GRAB ||
        m_state->m_memory->player_one_action == ACTION::GRAB_RUNNING)
    {
        CreateChain(SpotDodge);
        m_chain->PressButtons();
    }
    //We're assuming there's no other grab-type attacks. (Yoshi's neutral-b for instance)
    else
    {
        CreateChain(Powershield);
        m_chain->PressButtons();
    }
}
Beispiel #4
0
void TechChase::DetermineChain()
{
    //If we're not in a state to interupt, just continue with what we've got going
    if((m_chain != NULL) && (!m_chain->IsInterruptible()))
    {
        m_chain->PressButtons();
        return;
    }

    bool player_two_is_to_the_left = (m_state->m_memory->player_one_x > m_state->m_memory->player_two_x);

    //Dash back, since we're about to start running
    //UNLESS we're trying to tech chase a tech roll. Then we don't have enough time to dash back
    if(m_state->m_memory->player_two_action == DASHING &&
        m_state->m_memory->player_two_action_frame >= FOX_DASH_FRAMES-1 &&
        m_state->m_memory->player_one_action != FORWARD_TECH &&
        m_state->m_memory->player_one_action != BACKWARD_TECH)
    {
        //Make a new Run chain, since it's always interruptible
        delete m_chain;
        m_chain = NULL;
        CreateChain2(Run, !m_state->m_memory->player_two_facing);
        m_chain->PressButtons();
        return;
    }

    //If our opponent is just lying there, go dash around the pivot point and wait
    if(m_state->m_memory->player_one_action == LYING_GROUND_UP ||
      m_state->m_memory->player_one_action == TECH_MISS_UP ||
      m_state->m_memory->player_one_action == TECH_MISS_DOWN)
    {
        bool isLeft = m_state->m_memory->player_one_x < 0;
        int pivot_offset = isLeft ? 20 : -20;
        m_pivotPosition = m_state->m_memory->player_one_x + pivot_offset;

        //Make a new Run chain, since it's always interruptible
        delete m_chain;
        m_chain = NULL;
        bool left_of_pivot_position = m_state->m_memory->player_two_x < m_pivotPosition;
        CreateChain2(Run, left_of_pivot_position);
        m_chain->PressButtons();
        return;
    }

    uint lastHitboxFrame = m_state->lastHitboxFrame((CHARACTER)m_state->m_memory->player_one_character,
        (ACTION)m_state->m_memory->player_one_action);

    //If they're vulnerable, go punish it
    if(m_state->isRollingState((ACTION)m_state->m_memory->player_one_action) ||
        (m_state->isAttacking((ACTION)m_state->m_memory->player_one_action) &&
            m_state->m_memory->player_one_action_frame > lastHitboxFrame))
    {
        //Figure out where they will stop rolling, only on the first frame
        if(m_roll_position == 0)
        {
            switch(m_state->m_memory->player_one_action)
            {
                case ROLL_FORWARD:
                {
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_rollStartPosition + MARTH_ROLL_DISTANCE;
                    }
                    else
                    {
                        m_roll_position = m_state->m_rollStartPosition - MARTH_ROLL_DISTANCE;
                    }
                    break;
                }
                case ROLL_BACKWARD:
                {
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_rollStartPosition - MARTH_ROLL_DISTANCE;
                    }
                    else
                    {
                        m_roll_position = m_state->m_rollStartPosition + MARTH_ROLL_DISTANCE;
                    }
                    break;
                }
                case EDGE_ROLL_SLOW:
                {
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_rollStartPosition + MARTH_EDGE_ROLL_DISTANCE ;
                    }
                    else
                    {
                        m_roll_position = m_state->m_rollStartPosition - MARTH_EDGE_ROLL_DISTANCE;
                    }
                    break;
                }
                case EDGE_ROLL_QUICK:
                {
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_rollStartPosition + MARTH_EDGE_ROLL_DISTANCE;
                    }
                    else
                    {
                        m_roll_position = m_state->m_rollStartPosition - MARTH_EDGE_ROLL_DISTANCE;
                    }
                    break;
                }
                case EDGE_GETUP_QUICK:
                {
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_rollStartPosition + MARTH_GETUP_DISTANCE;
                    }
                    else
                    {
                        m_roll_position = m_state->m_rollStartPosition - MARTH_GETUP_DISTANCE;
                    }
                    break;
                }
                case EDGE_GETUP_SLOW:
                {
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_rollStartPosition + MARTH_GETUP_DISTANCE;
                    }
                    else
                    {
                        m_roll_position = m_state->m_rollStartPosition - MARTH_GETUP_DISTANCE;
                    }
                    break;
                }
                case FORWARD_TECH:
                {
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_rollStartPosition + MARTH_TECHROLL_DISTANCE;
                    }
                    else
                    {
                        m_roll_position = m_state->m_rollStartPosition - MARTH_TECHROLL_DISTANCE;
                    }
                    break;
                }
                case BACKWARD_TECH:
                {
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_rollStartPosition - MARTH_TECHROLL_DISTANCE;
                    }
                    else
                    {
                        m_roll_position = m_state->m_rollStartPosition + MARTH_TECHROLL_DISTANCE;
                    }
                    break;
                }
                case GROUND_ROLL_FORWARD:
                case GROUND_ROLL_FORWARD_OTHER:
                {
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_rollStartPosition + MARTH_GROUND_FORWARD_ROLL_DISTANCE;
                    }
                    else
                    {
                        m_roll_position = m_state->m_rollStartPosition - MARTH_GROUND_FORWARD_ROLL_DISTANCE;
                    }
                    break;
                }
                case GROUND_ROLL_BACKWARD:
                case GROUND_ROLL_BACKWARD_OTHER:
                {
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_rollStartPosition - MARTH_GROUND_BACK_ROLL_DISTANCE;
                    }
                    else
                    {
                        m_roll_position = m_state->m_rollStartPosition + MARTH_GROUND_BACK_ROLL_DISTANCE;
                    }
                    break;
                }
                default:
                {
                    m_roll_position = m_state->m_rollStartPosition;
                    break;
                }
            }

            //Roll position can't be off the stage
            if(m_roll_position > m_state->getStageEdgeGroundPosition())
            {
                m_roll_position = m_state->getStageEdgeGroundPosition();
            }
            else if (m_roll_position < (-1) * m_state->getStageEdgeGroundPosition())
            {
                m_roll_position = (-1) * m_state->getStageEdgeGroundPosition();
            }

            //If the opponent is attacking, set their roll position to be where they're at now (not the last roll)
            if(m_state->isAttacking((ACTION)m_state->m_memory->player_one_action))
            {
                m_roll_position = m_state->m_memory->player_one_x;
            }

            if(player_two_is_to_the_left)
            {
                m_pivotPosition = m_roll_position - FOX_GRAB_RANGE;
            }
            else
            {
                m_pivotPosition = m_roll_position + FOX_GRAB_RANGE;
            }
        }

        bool to_the_left = m_roll_position > m_state->m_memory->player_two_x;

        int frames_left = m_state->totalActionFrames((CHARACTER)m_state->m_memory->player_one_character,
            (ACTION)m_state->m_memory->player_one_action) - m_state->m_memory->player_one_action_frame;

        int frameDelay = 7;
        double distance;
        if(m_state->m_memory->player_two_action == DASHING ||
            m_state->m_memory->player_two_action == RUNNING)
        {
            double slidingAdjustment = frameDelay * (std::abs(m_state->m_memory->player_two_speed_ground_x_self));
            distance = std::abs(std::abs(m_roll_position - m_state->m_memory->player_two_x) - slidingAdjustment);
        }
        else
        {
            distance = std::abs(m_roll_position - m_state->m_memory->player_two_x);
        }

        //If we're too late, at least get close
        if(frames_left < frameDelay)
        {
            Logger::Instance()->Log(INFO, "Trying to tech chase but can't make it in time...");
            CreateChain2(Run, to_the_left);
            m_chain->PressButtons();
            return;
        }

        Logger::Instance()->Log(INFO, "Trying to tech chase a roll at position: " + std::to_string(m_roll_position) +
            " with: " + std::to_string(frames_left) + " frames left");

        //How many frames of vulnerability are there at the tail end of the animation?
        int vulnerable_frames = 7;
        if(m_state->m_memory->player_one_action == MARTH_COUNTER)
        {
            vulnerable_frames = 59;
        }

        bool facingRight = m_state->m_memory->player_two_facing;
        if(m_state->m_memory->player_two_action == TURNING)
        {
            facingRight = !facingRight;
        }

        //Can we grab the opponent right now?
        if(frames_left - frameDelay >= 0 &&
            frames_left - frameDelay <= vulnerable_frames &&
            distance < FOX_GRAB_RANGE &&
            to_the_left == facingRight &&
            m_state->m_memory->player_one_action != TECH_MISS_UP && //Don't try to grab when they miss a tech, it doesn't work
            m_state->m_memory->player_one_action != TECH_MISS_DOWN)
        {
            CreateChain2(GrabAndThrow, DOWN_THROW);
            m_chain->PressButtons();
            return;
        }
        else
        {
            //If they're right in front of us and we're not already running, then just hang out and wait
            if(m_state->m_memory->player_two_action != DASHING &&
                m_state->m_memory->player_two_action != RUNNING &&
                m_state->m_memory->player_two_action != TURNING &&
                distance < FOX_GRAB_RANGE &&
                to_the_left == m_state->m_memory->player_two_facing)
            {
                CreateChain(Nothing);
                m_chain->PressButtons();
                return;
            }

            //Make a new Run chain, since it's always interruptible
            delete m_chain;
            m_chain = NULL;
            bool left_of_pivot_position = m_state->m_memory->player_two_x < m_pivotPosition;
            CreateChain2(Run, left_of_pivot_position);
            m_chain->PressButtons();
            return;
        }
    }

    //Default to walking in towards the player
    //Make a new Run chain, since it's always interruptible
    delete m_chain;
    m_chain = NULL;
    CreateChain2(Run, player_two_is_to_the_left);
    m_chain->PressButtons();
    return;
}
Beispiel #5
0
void Punish::DetermineChain()
{
    //If we're not in a state to interupt, just continue with what we've got going
    if((m_chain != NULL) && (!m_chain->IsInterruptible()))
    {
        m_chain->PressButtons();
        return;
    }

    //If we're hanging on the egde, and they are falling above the stage, stand up
    if(m_state->m_memory->player_one_action == DEAD_FALL &&
        m_state->m_memory->player_two_action == EDGE_HANGING &&
        std::abs(m_state->m_memory->player_one_x) < m_state->getStageEdgeGroundPosition() + .001)
    {
        CreateChain2(EdgeAction, STAND_UP);
        m_chain->PressButtons();
        return;
    }

    //If they're rolling, go punish it where they will stop
    if(m_state->m_memory->player_one_action == ROLL_FORWARD ||
        m_state->m_memory->player_one_action == ROLL_BACKWARD ||
        m_state->m_memory->player_one_action == EDGE_ROLL_SLOW ||
        m_state->m_memory->player_one_action == EDGE_ROLL_QUICK ||
        m_state->m_memory->player_one_action == EDGE_GETUP_QUICK ||
        m_state->m_memory->player_one_action == EDGE_GETUP_SLOW)
    {
        //Figure out where they will stop rolling, only on the first frame
        if(m_roll_position == 0)
        {
            //Scale the offset depending on how far into the roll they are
            //TODO: This is not strictly linear. But let's assume it is and maybe it will work well enough
            double scale = 1;
            switch(m_state->m_memory->player_one_action)
            {
                case ROLL_FORWARD:
                {
                    scale = 1 - ((double)(m_state->m_memory->player_one_action_frame - 1) / (double)MARTH_ROLL_FRAMES);
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_memory->player_one_x + MARTH_ROLL_DISTANCE * scale;
                    }
                    else
                    {
                        m_roll_position = m_state->m_memory->player_one_x - MARTH_ROLL_DISTANCE * scale;
                    }
                    break;
                }
                case ROLL_BACKWARD:
                {
                    scale = 1 - ((double)(m_state->m_memory->player_one_action_frame - 1) / (double)MARTH_ROLL_FRAMES);
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_memory->player_one_x - MARTH_ROLL_DISTANCE * scale;
                    }
                    else
                    {
                        m_roll_position = m_state->m_memory->player_one_x + MARTH_ROLL_DISTANCE * scale;
                    }
                    break;
                }
                case EDGE_ROLL_SLOW:
                {
                    scale = 1 - ((double)(m_state->m_memory->player_one_action_frame - 1) / (double)MARTH_EDGE_ROLL_SLOW_FRAMES);
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_memory->player_one_x + MARTH_EDGE_ROLL_DISTANCE * scale;
                    }
                    else
                    {
                        m_roll_position = m_state->m_memory->player_one_x - MARTH_EDGE_ROLL_DISTANCE * scale;
                    }
                    break;
                }
                case EDGE_ROLL_QUICK:
                {
                    scale = 1 - ((double)(m_state->m_memory->player_one_action_frame - 1) / (double)MARTH_EDGE_ROLL_FRAMES);
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_memory->player_one_x + MARTH_EDGE_ROLL_DISTANCE * scale;
                    }
                    else
                    {
                        m_roll_position = m_state->m_memory->player_one_x - MARTH_EDGE_ROLL_DISTANCE * scale;
                    }
                    break;
                }
                case EDGE_GETUP_QUICK:
                {
                    scale = 1 - ((double)(m_state->m_memory->player_one_action_frame - 1) / (double)MARTH_EDGE_GETUP_QUICK_FRAMES);
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_memory->player_one_x + MARTH_GETUP_DISTANCE * scale;
                    }
                    else
                    {
                        m_roll_position = m_state->m_memory->player_one_x - MARTH_GETUP_DISTANCE * scale;
                    }
                    break;
                }
                case EDGE_GETUP_SLOW:
                {
                    scale = 1 - ((double)(m_state->m_memory->player_one_action_frame - 1) / (double)MARTH_EDGE_GETUP_SLOW_FRAMES);
                    if(m_state->m_memory->player_one_facing)
                    {
                        m_roll_position = m_state->m_memory->player_one_x + MARTH_GETUP_DISTANCE * scale;
                    }
                    else
                    {
                        m_roll_position = m_state->m_memory->player_one_x - MARTH_GETUP_DISTANCE * scale;
                    }
                    break;
                }
            }

            if(m_roll_position > m_state->getStageEdgeGroundPosition())
            {
                m_roll_position = m_state->getStageEdgeGroundPosition();
            }
            else if (m_roll_position < (-1) * m_state->getStageEdgeGroundPosition())
            {
                m_roll_position = (-1) * m_state->getStageEdgeGroundPosition();
            }
        }

        int frames_left;
        if(m_state->m_memory->player_one_action == ROLL_FORWARD ||
            m_state->m_memory->player_one_action == ROLL_BACKWARD)
        {
            frames_left = MARTH_ROLL_FRAMES - m_state->m_memory->player_one_action_frame;
        }
        else if(m_state->m_memory->player_one_action == EDGE_ROLL_SLOW)
        {
            frames_left = MARTH_EDGE_ROLL_SLOW_FRAMES - m_state->m_memory->player_one_action_frame;
        }
        else if(m_state->m_memory->player_one_action == EDGE_ROLL_QUICK)
        {
            frames_left = MARTH_EDGE_ROLL_FRAMES - m_state->m_memory->player_one_action_frame;
        }
        else if(m_state->m_memory->player_one_action == EDGE_GETUP_QUICK)
        {
            frames_left = MARTH_EDGE_GETUP_QUICK_FRAMES - m_state->m_memory->player_one_action_frame;
        }
        else if(m_state->m_memory->player_one_action == EDGE_GETUP_SLOW)
        {
            frames_left = MARTH_EDGE_GETUP_SLOW_FRAMES - m_state->m_memory->player_one_action_frame;
        }

        if(frames_left <= 7)
        {
            CreateChain(Nothing);
            m_chain->PressButtons();
            return;
        }

        //Upsmash if we're in range and facing the right way
        //  Factor in sliding during the smash animation
        double distance;
        int frameDelay = 8; //Frames until the first smash hitbox, plus one for leeway
        if(m_state->m_memory->player_two_action == DASHING ||
            m_state->m_memory->player_two_action == RUNNING)
        {
            double slidingAdjustment = 12.25 * (std::abs(m_state->m_memory->player_two_speed_ground_x_self));
            distance = std::abs(std::abs(m_roll_position - m_state->m_memory->player_two_x) - slidingAdjustment);
            frameDelay += 4;
        }
        else
        {
            distance = std::abs(m_roll_position - m_state->m_memory->player_two_x);
        }

        Logger::Instance()->Log(INFO, "Trying to punish a roll at position: " + std::to_string(m_roll_position) +
            " with: " + std::to_string(frames_left) + " frames left");

        bool to_the_left = m_roll_position > m_state->m_memory->player_two_x;
        if(frames_left >= frames_left &&
            distance < FOX_UPSMASH_RANGE_NEAR &&
            to_the_left == m_state->m_memory->player_two_facing)
        {
            CreateChain3(SmashAttack, SmashAttack::UP, std::max(0, frames_left - frameDelay));
            m_chain->PressButtons();
            return;
        }
        else
        {
            //If the target location is right behind us, just turn around, don't run
            if(distance < FOX_UPSMASH_RANGE_NEAR &&
                to_the_left != m_state->m_memory->player_two_facing)
            {
                CreateChain2(Walk, to_the_left);
                m_chain->PressButtons();
                return;
            }
            else
            {
                CreateChain2(Run, to_the_left);
                m_chain->PressButtons();
                return;
            }
        }
    }

    //Calculate distance between players
    double distance = pow(m_state->m_memory->player_one_x - m_state->m_memory->player_two_x, 2);
    distance += pow(m_state->m_memory->player_one_y - m_state->m_memory->player_two_y, 2);
    distance = sqrt(distance);

    //How many frames do we have until we need to do something?
    int frames_left;
    //Are we before the attack or after?
    if(m_state->m_memory->player_one_action_frame < m_state->lastHitboxFrame((CHARACTER)m_state->m_memory->player_one_character,
        (ACTION)m_state->m_memory->player_one_action))
    {
        //Before
        frames_left = m_state->firstHitboxFrame((CHARACTER)m_state->m_memory->player_one_character,
            (ACTION)m_state->m_memory->player_one_action) - m_state->m_memory->player_one_action_frame - 1;
        Logger::Instance()->Log(INFO, "Frames until first hitbox of the attack: " + std::to_string(frames_left));
    }
    else
    {
        //After
        frames_left = m_state->totalActionFrames((CHARACTER)m_state->m_memory->player_one_character,
           (ACTION)m_state->m_memory->player_one_action) - m_state->m_memory->player_one_action_frame - 1;
       Logger::Instance()->Log(INFO, "Frames left until end of the attack: " + std::to_string(frames_left));
    }

    bool player_two_is_to_the_left = (m_state->m_memory->player_one_x > m_state->m_memory->player_two_x);
    //If we're in upsmash/jab range, then prepare for attack
    if(m_state->m_memory->player_two_facing == player_two_is_to_the_left && //Facing the right way?
        (distance < FOX_UPSMASH_RANGE ||
        (distance < FOX_UPSMASH_RANGE - 25.5 && (m_state->m_memory->player_two_action == DASHING ||
            m_state->m_memory->player_two_action == RUNNING))))
    {

        int frameDelay = 9; //Frames until the first smash hitbox, plus one for strage startup latency and another for charge lag
        if(m_state->m_memory->player_two_action == DASHING ||
            m_state->m_memory->player_two_action == RUNNING)
        {
            frameDelay += 4;
        }

        //Do we have time to upsmash? Do that.
        if(frames_left > frameDelay)
        {
            //Do two less frames of charging than we could, just to be safe
            CreateChain3(SmashAttack, SmashAttack::UP, std::max(0, frames_left - frameDelay));
            m_chain->PressButtons();
            return;
        }

        //Do we have time to jab? Do that.
        if(frames_left > 3 &&
            m_state->m_memory->player_two_action != DASHING &&
            m_state->m_memory->player_two_action != RUNNING)
        {
            CreateChain(Jab);
            m_chain->PressButtons();
            return;
        }
    }

    //Is it safe to wavedash in after shielding the attack?
    //  Don't wavedash off the edge of the stage
    if(frames_left > 15 &&
        m_state->m_memory->player_two_action == SHIELD_RELEASE &&
        (m_state->getStageEdgeGroundPosition() > std::abs(m_state->m_memory->player_two_x) + 10))
    {
        CreateChain2(Wavedash, player_two_is_to_the_left);
        m_chain->PressButtons();
        return;
    }

    //Default to walking in towards the player
    CreateChain2(Walk, player_two_is_to_the_left);
    m_chain->PressButtons();
    return;
}
Beispiel #6
0
void Techroll::DetermineChain()
{
    CreateChain2(Tech, IN_PLACE);
    m_chain->PressButtons();
    return;
}
Beispiel #7
0
void TechChase::DetermineChain()
{
    //Step 0: If we're not interuptible, then just keep doing what we are already doing
    if((m_chain != NULL) && (!m_chain->IsInterruptible()))
    {
        m_chain->PressButtons();
        return;
    }

    //**********************************************************************************
    // Step 1: Set up global variables
    //      There's a lot of calculations that happen below, so first let's set up some
    //      helper variables that are reused frequently
    //**********************************************************************************

    bool player_two_is_to_the_left = (m_state->m_memory->player_one_x > m_state->m_memory->player_two_x);

    uint lastHitboxFrame = m_state->lastHitboxFrame((CHARACTER)m_state->m_memory->player_one_character,
        (ACTION)m_state->m_memory->player_one_action);

    int frames_left = m_state->totalActionFrames((CHARACTER)m_state->m_memory->player_one_character,
        (ACTION)m_state->m_memory->player_one_action) - m_state->m_memory->player_one_action_frame;

    //If enemy is in hit stun
    bool isDamage = m_state->isDamageState((ACTION)m_state->m_memory->player_one_action);
    if(isDamage)
    {
        frames_left = m_state->m_memory->player_one_hitstun_frames_left;
    }

    int totalFrames = m_state->totalActionFrames((CHARACTER)m_state->m_memory->player_one_character,
        (ACTION)m_state->m_memory->player_one_action);

    double distance = std::abs(m_state->m_memory->player_one_x - m_state->m_memory->player_two_x);

    double enemyHitSlide = 0;
    double enemySelfSlide = 0;

    //Figure out where they will stop, only on the first frame
    if(m_endPosition == 0)
    {
        //We're assuming that the opponent is vulnerable for some duration of time
        //Where will the opponent be at the end of their vulnerable state?
        if(m_state->isRollingState((ACTION)m_state->m_memory->player_one_action))
        {
            double rollDistance = m_state->getRollDistance((CHARACTER)m_state->m_memory->player_one_character,
                (ACTION)m_state->m_memory->player_one_action);

            bool directon = m_state->getRollDirection((ACTION)m_state->m_memory->player_one_action);
            if(m_state->m_memory->player_one_facing == directon)
            {
                m_endPosition = m_state->m_rollStartPosition + rollDistance;
            }
            else
            {
                m_endPosition = m_state->m_rollStartPosition - rollDistance;
            }

            //Calculate hit sliding from the START of the roll.
            enemyHitSlide = m_state->calculateSlideDistance((CHARACTER)m_state->m_memory->player_one_character,
                m_state->m_rollStartSpeed, totalFrames);
            m_endPosition += enemyHitSlide;

        }
        else
        {
            m_endPosition = m_state->m_memory->player_one_x;
            enemySelfSlide = m_state->calculateSlideDistance((CHARACTER)m_state->m_memory->player_one_character,
                m_state->m_memory->player_one_speed_ground_x_self, frames_left);
            m_endPosition += enemySelfSlide;

            enemyHitSlide = m_state->calculateSlideDistance((CHARACTER)m_state->m_memory->player_one_character,
                m_state->m_memory->player_one_speed_x_attack, frames_left);
            m_endPosition += enemyHitSlide;
        }

        //You can't roll off the stage
        if(m_state->isRollingState((ACTION)m_state->m_memory->player_one_action))
        {
            if(m_endPosition > m_state->getStageEdgeGroundPosition())
            {
                m_endPosition = m_state->getStageEdgeGroundPosition();
            }
            else if (m_endPosition < (-1) * m_state->getStageEdgeGroundPosition())
            {
                m_endPosition = (-1) * m_state->getStageEdgeGroundPosition();
            }
        }
    }

    //**********************************************************************************
    //  Step 2: Do we need to wavedash in order to get closer?
    //      When we're in shield, we can't dash. Fastest option is to wavedash. But we'll
    //      be in stun for a while when sliding, so make sure that we have enough time
    //      to complete the wavedash safely.
    //**********************************************************************************

    //Is it safe to wavedash in after shielding the attack?
    //  Don't wavedash off the edge of the stage
    //  And don't bother if we're already in grab range. (Just grab them)
    if(frames_left > 15 &&
        distance > FOX_GRAB_RANGE &&
        m_state->m_memory->player_two_action == SHIELD_RELEASE &&
        (m_state->getStageEdgeGroundPosition() > std::abs(m_state->m_memory->player_two_x) + 10))
    {
        CreateChain2(Wavedash, player_two_is_to_the_left);
        m_chain->PressButtons();
        return;
    }

    //**********************************************************************************
    //  Step 3: If opponent is lying on the ground, wait nearby for them to get up
    //**********************************************************************************

    if(m_state->m_memory->player_one_action == LYING_GROUND_UP ||
        m_state->m_memory->player_one_action == LYING_GROUND_DOWN ||
        m_state->m_memory->player_one_action == TECH_MISS_UP ||
        m_state->m_memory->player_one_action == TECH_MISS_DOWN)
    {
        bool isRight = m_state->m_memory->player_one_x < m_state->m_memory->player_two_x;

        //If opponent missed a tech right nearby us, just walk in close.
        if(distance < 25 &&
            m_state->m_memory->player_two_action != DASHING &&
            m_state->m_memory->player_two_action != RUNNING)
        {
            bool onInnerStage = std::abs(m_state->m_memory->player_two_x) < m_state->getStageEdgeGroundPosition() - FOX_GRAB_RANGE;
            bool nearRightEdge = m_state->m_memory->player_two_x > 0;
            bool facingEdge = nearRightEdge == m_state->m_memory->player_two_facing;

            //Walk in if they're getting too far away, or if they're behind us
            //But if we've already backed them into a corner, don't go further
            if((distance > 7 &&
                (onInnerStage || !facingEdge)) ||
                isRight == m_state->m_memory->player_two_facing)
            {
                CreateChain2(Walk, !isRight);
                m_chain->PressButtons();
                return;
            }
            else
            {
                CreateChain(Nothing);
                m_chain->PressButtons();
                return;
            }
        }

        //try to pivot away from the enemy, unless that would put us off the stage
        int pivot_offset = isRight ? 20 : -20;
        if(std::abs(m_state->m_memory->player_two_x + pivot_offset) > m_state->getStageEdgeGroundPosition())
        {
            pivot_offset = isRight ? -20 : 20;
        }
        m_pivotPosition = m_state->m_memory->player_one_x + pivot_offset;

        CreateChain3(DashDance, m_pivotPosition, 0);
        m_chain->PressButtons();
        return;
    }

    //**********************************************************************************
    // Step 4: Opponent is in stun. Go grab them.
    //      Four main ways this can happen:
    //          a - Pre-attack windup
    //          b - Post-attack cooldown
    //          c - Stunned state
    //          d - Roll state
    //**********************************************************************************

    if(isDamage ||
        m_state->m_memory->player_one_action == WAVEDASH_SLIDE ||
        m_state->m_memory->player_one_action == LANDING_SPECIAL ||
        m_state->isRollingState((ACTION)m_state->m_memory->player_one_action) ||
        (m_state->isAttacking((ACTION)m_state->m_memory->player_one_action) &&
        m_state->m_memory->player_one_action_frame > lastHitboxFrame))
    {
        int frameDelay = 7;
        double distanceFromRoll;
        if(m_state->m_memory->player_two_action == DASHING ||
            m_state->m_memory->player_two_action == RUNNING ||
            m_state->m_memory->player_two_action == SHIELD_RELEASE)
        {
            //We have to jump cancel the grab. So that takes an extra frame
            frameDelay++;
        }

        double slidingAdjustment = m_state->calculateSlideDistance((CHARACTER)m_state->m_memory->player_two_character,
            m_state->m_memory->player_two_speed_ground_x_self, frameDelay);

        distanceFromRoll = std::abs(m_endPosition - (m_state->m_memory->player_two_x + slidingAdjustment));

        Logger::Instance()->Log(INFO, "Trying to tech chase a roll at position: " + std::to_string(m_endPosition) +
            " with: " + std::to_string(frames_left) + " frames left");

        //How many frames of vulnerability are there at the tail end of the animation?
        int vulnerable_frames = m_state->trailingVulnerableFrames((CHARACTER)m_state->m_memory->player_one_character,
            (ACTION)m_state->m_memory->player_one_action);

        //Except for marth counter. You can always grab that
        if(m_state->m_memory->player_one_action == MARTH_COUNTER)
        {
            vulnerable_frames = 59;
        }
        //If the opponent is attacking, they don't have invulnerability like rolls do
        //Except getup attack, which is both a roll and an attack
        if(m_state->isAttacking((ACTION)m_state->m_memory->player_one_action) &&
            m_state->m_memory->player_one_action != GETUP_ATTACK &&
            m_state->m_memory->player_one_action != GROUND_ATTACK_UP)
        {
            vulnerable_frames = totalFrames;
        }

        bool facingRight = m_state->m_memory->player_two_facing;
        if(m_state->m_memory->player_two_action == TURNING)
        {
            facingRight = !facingRight;
        }

        bool to_the_left = m_endPosition > m_state->m_memory->player_two_x;
        //Given sliding, are we going to be behind the enemy?
        bool behindEnemy = (m_endPosition < (m_state->m_memory->player_two_x + slidingAdjustment)) == m_state->m_memory->player_two_facing;
        double verticalDistance = std::abs(m_state->m_memory->player_two_y - m_state->m_memory->player_one_y);

        //TODO: Needs updating for platform edges
        double distanceFromEdge = m_state->getStageEdgeGroundPosition() - std::abs(m_state->m_memory->player_two_x);

        //Can we grab the opponent right now?
        if(frames_left - frameDelay >= 0 &&
            frames_left - frameDelay < vulnerable_frames &&
            distanceFromRoll < FOX_GRAB_RANGE &&
            verticalDistance < FOX_GRAB_RANGE_Y &&
            to_the_left == facingRight &&
            !behindEnemy &&
            m_state->m_memory->player_one_action != TECH_MISS_UP && //Don't try to grab when they miss a tech, it doesn't work
            m_state->m_memory->player_one_action != TECH_MISS_DOWN)
        {
            if(distanceFromEdge < 20)
            {
                CreateChain2(GrabAndThrow, FORWARD_THROW);
                m_chain->PressButtons();
                return;
            }
            else
            {
                CreateChain2(GrabAndThrow, DOWN_THROW);
                m_chain->PressButtons();
                return;
            }
        }
        else
        {
            double currentDistance = std::abs(m_endPosition - m_state->m_memory->player_two_x);

            //If they're right in front of us and we're not already running, then just hang out and wait
            if(m_state->m_memory->player_two_action != DASHING &&
                m_state->m_memory->player_two_action != RUNNING &&
                m_state->m_memory->player_two_action != TURNING &&
                distanceFromRoll < FOX_GRAB_RANGE)
            {
                //If the target location is right behind us, just turn around, don't run
                // Also, if it's all the same, just get a little closer than we probably need.
                // Sometimes grab is shorter range than we'd like
                if(to_the_left != m_state->m_memory->player_two_facing ||
                    distanceFromRoll > FOX_GRAB_RANGE/2)
                {
                    CreateChain2(Walk, to_the_left);
                    m_chain->PressButtons();
                    return;
                }
                else
                {
                    CreateChain(Nothing);
                    m_chain->PressButtons();
                    return;
                }
            }

            bool needsWavedash = m_state->m_memory->player_two_action == SHIELD_RELEASE ||
                m_state->m_memory->player_two_action == SHIELD||
                m_state->m_memory->player_two_action == DOWN_B_GROUND;

            //Do we need to wavedash?
            if(frames_left >= WAVEDASH_FRAMES &&
                 needsWavedash)
            {
                //Is it against an aerial? If so, make sure it's a rising attack so opponent can't cancel it
                if(m_state->m_memory->player_one_on_ground ||
                    m_state->m_memory->player_one_speed_y_self > 0)
                {
                    CreateChain2(Wavedash, player_two_is_to_the_left);
                    m_chain->PressButtons();
                    return;
                }
            }

            //If we're sure we'll have time (Like when we have lots of vulnerable frames) dash dance right at the edge of range
            //Default to dashing at the opponent
            if(vulnerable_frames >= 7 &&
                m_state->m_memory->player_one_action != FORWARD_TECH &&
                m_state->m_memory->player_one_action != BACKWARD_TECH)
            {
                bool isRight = m_endPosition < m_state->m_memory->player_two_x;
                double pivot_offset = isRight ? FOX_JC_GRAB_MAX_SLIDE : -FOX_JC_GRAB_MAX_SLIDE;
                //Don't anchor our dash dance off the stage
                if(std::abs(m_endPosition + pivot_offset) > m_state->getStageEdgeGroundPosition())
                {
                    pivot_offset = isRight ? -FOX_JC_GRAB_MAX_SLIDE : FOX_JC_GRAB_MAX_SLIDE;
                }
                m_pivotPosition = m_endPosition + pivot_offset;
                CreateChain3(DashDance, m_pivotPosition, 0);
                m_chain->PressButtons();
                return;
            }

            //How many frames do we have to wait until we can just dash in there and grab in one go.
            //Tech roll is the longest roll and we can just barely make that with 0 frames of waiting. So
            //this should always be possible

            //This is the number of frames we're going to need to run the distance until we're in range
            int travelFramesMin = (currentDistance - FOX_GRAB_RANGE - FOX_JC_GRAB_MAX_SLIDE) / FOX_DASH_SPEED;
            int travelFramesMax = (currentDistance - FOX_JC_GRAB_MAX_SLIDE) / FOX_DASH_SPEED;

            //Account for the startup frames, too
            if(m_state->m_memory->player_two_action != DASHING &&
                m_state->m_memory->player_two_action != RUNNING)
            {
                travelFramesMin++;
                travelFramesMax++;
            }
            if(to_the_left != facingRight)
            {
                travelFramesMin++;
                travelFramesMax++;
            }

            //This is the most frames we can wait before we NEED to leave.
            int maxWaitFrames = frames_left - travelFramesMax - 8;
            //This is the smallest number of frames we HAVE to wait or else we'll get there too early.
            int minWaitFrames = (frames_left - vulnerable_frames) - travelFramesMin - 8;
            int midWaitFrames = (maxWaitFrames + minWaitFrames) / 2;

            bool canWalkState =  m_state->m_memory->player_two_action == STANDING ||
                m_state->m_memory->player_two_action == WALK_SLOW ||
                m_state->m_memory->player_two_action == WALK_MIDDLE;

            //If the enemy is juuuuuust beyond our reach, then we should just walk forward a little. Don't dash or we'll slide past it
            // But only do this if we're facing the right way. We don't want to turn around using this
            if(canWalkState &&
                currentDistance < FOX_JC_GRAB_MAX_SLIDE &&
                to_the_left == facingRight)
            {
                CreateChain2(Walk, to_the_left);
                m_chain->PressButtons();
                return;
            }

            if(midWaitFrames > 0)
            {
                //If we're dashing, we will need to do a turn around
                if(m_state->m_memory->player_two_action == DASHING)
                {
                    delete m_chain;
                    m_chain = NULL;
                    CreateChain2(Run, !m_state->m_memory->player_two_facing);
                    m_chain->PressButtons();
                    return;
                }
                CreateChain(Nothing);
                m_chain->PressButtons();
                return;
            }

            //Make a new Run chain, since it's always interruptible
            delete m_chain;
            m_chain = NULL;
            bool left_of_end_position = m_state->m_memory->player_two_x < m_endPosition;
            CreateChain2(Run, left_of_end_position);
            m_chain->PressButtons();
            return;
        }
    }

    //Default to dashing at the opponent
    CreateChain3(DashDance, m_state->m_memory->player_one_x, 0);
    m_chain->PressButtons();
    return;
}
Beispiel #8
0
void Edgeguard::DetermineChain()
{
    //If we're not in a state to interupt, just continue with what we've got going
    if((m_chain != NULL) && (!m_chain->IsInterruptible()))
    {
        m_chain->PressButtons();
        return;
    }

    double lowerEventHorizon = MARTH_LOWER_EVENT_HORIZON;
    if(m_state->m_memory->player_one_jumps_left == 0)
    {
        lowerEventHorizon += MARTH_DOUBLE_JUMP_HEIGHT;
    }

    //Marth is dead if he's at this point
    if(m_state->m_memory->player_one_y < lowerEventHorizon)
    {
        if(m_state->m_memory->player_two_action == EDGE_HANGING)
        {
            CreateChain2(EdgeAction, WAVEDASH_UP);
            m_chain->PressButtons();
            return;
        }
        if(m_state->m_memory->player_two_on_ground)
        {
            CreateChain(Nothing);
            m_chain->PressButtons();
            return;
        }
    }

    //Edgehog our opponent if they're UP-B'ing sweetspotted.
    //Grab the edge if we're still on the stage
    if(m_state->m_memory->player_one_action == UP_B &&
        m_state->m_memory->player_two_action == EDGE_HANGING)
    {
        //Is marth so low that he must grab the edge? If so, just roll up.
        if(m_state->m_memory->player_one_y < MARTH_RECOVER_HIGH_EVENT_HORIZON + MARTH_DOUBLE_JUMP_HEIGHT)
        {
            CreateChain3(EdgeAction, ROLL_UP, 2);
            m_chain->PressButtons();
            return;
        }
        //If not, he might land on the stage. So, just stand up and attack on the other end
        else
        {
            CreateChain2(EdgeAction, STAND_UP);
            m_chain->PressButtons();
            return;
        }
    }

    //distance formula
    double distance = pow(std::abs(m_state->m_memory->player_one_x - m_state->m_memory->player_two_x), 2);
    distance += pow(std::abs(m_state->m_memory->player_one_y - m_state->m_memory->player_two_y), 2);
    distance = sqrt(distance);

    double edge_distance_x = std::abs(std::abs(m_state->m_memory->player_one_x) - m_state->getStageEdgePosition());
    double edge_distance_y = std::abs(m_state->m_memory->player_one_y - EDGE_HANGING_Y_POSITION);
    double edge_distance = sqrt(pow(edge_distance_x, 2) + pow(edge_distance_y, 2));

    //If we're able to shine p1 right now, let's do that
    if(std::abs(distance) < FOX_SHINE_RADIUS &&
        !m_state->m_memory->player_one_invulnerable &&
        m_state->m_memory->player_one_action != AIRDODGE &&
        m_state->m_memory->player_one_action != MARTH_COUNTER &&
        m_state->m_memory->player_one_action != MARTH_COUNTER_FALLING)
    {
        //Are we in a state where we can shine?
        if(m_state->m_memory->player_two_action == FALLING ||
            m_state->m_memory->player_two_action == EDGE_HANGING)
        {
            CreateChain(JumpCanceledShine);
            m_chain->PressButtons();
            return;
        }
    }

    //Refresh invincibility if the enemy is getting close
    if(m_state->m_memory->player_two_action == EDGE_HANGING &&
        distance < (2 * MARTH_FSMASH_RANGE) &&
        edge_distance > 25)
    {
        //Don't edge stall if opponent can up-b to the edge quickly
        if(edge_distance_y > MARTH_UP_B_HEIGHT + 10 ||
            ((edge_distance_x > MARTH_UP_B_X_DISTANCE) && (std::abs(m_state->m_memory->player_one_x) > m_state->getStageEdgePosition())))
        {
            CreateChain(EdgeStall);
            m_chain->PressButtons();
            return;
        }
    }

    //Drop down and shine the enemy if they are below us and we have enough invincibility
    int invincibilityFramesLeft = 29 - (m_state->m_memory->frame - m_state->m_edgeInvincibilityStart);
    //TODO Test out this 8 unit lateral leeway
    if(std::abs(m_state->m_memory->player_two_x - m_state->m_memory->player_one_x) < 8 &&
        (invincibilityFramesLeft * FOX_FASTFALL_SPEED) < distance &&
        m_state->m_memory->player_two_y > m_state->m_memory->player_one_y &&
        m_state->m_memory->player_one_y > (-1)*(FOX_DOUBLE_JUMP_HEIGHT) &&
        m_state->m_memory->player_one_on_ground &&
        m_state->m_memory->player_two_on_ground)
    {
        CreateChain2(EdgeAction, FASTFALL);
        m_chain->PressButtons();
        return;
    }

    //Alternatively, we can shine when they are hanging on the edge
    if(std::abs(m_state->getStageEdgeGroundPosition() - std::abs(m_state->m_memory->player_two_x)) < 2 &&
        m_state->m_memory->player_one_action == EDGE_HANGING &&
        !m_state->m_memory->player_one_invulnerable)
    {
        CreateChain(Waveshine);
        m_chain->PressButtons();
        return;
    }

    //Dash dance around the edge
    if((m_state->m_memory->player_one_action == SLIDING_OFF_EDGE ||
        m_state->m_memory->player_one_action == EDGE_CATCHING ||
        m_state->m_memory->player_one_action == EDGE_HANGING) &&
        m_state->m_memory->player_two_on_ground)
    {
        bool onLeft = m_state->m_memory->player_one_x < 0;
        double pivotPoint = onLeft ? (-1) * m_state->getStageEdgeGroundPosition() : m_state->getStageEdgeGroundPosition();
        CreateChain3(DashDance, pivotPoint, 0);
        m_chain->PressButtons();
        return;
    }

    //If we're still on the stage, see if it's safe to grab the edge
    if(m_state->m_memory->player_two_on_ground)
    {
        //If the enemy is in a stunned damage state, go ahead and try.
        if(m_state->isDamageState((ACTION)m_state->m_memory->player_one_action) &&
            m_state->m_memory->player_one_hitstun_frames_left > 15)
        {
            CreateChain(GrabEdge);
            m_chain->PressButtons();
            return;
        }

        //Calculate distance between players
        double distance = pow(std::abs(m_state->m_memory->player_one_x) - m_state->getStageEdgePosition(), 2);
        distance += pow(m_state->m_memory->player_one_y, 2);
        distance = sqrt(distance);

        //If marth is out of attack range and UP-B range, then go ahead and do it
        if(distance > MARTH_FSMASH_RANGE &&
            (std::abs(m_state->m_memory->player_one_x) - m_state->getStageEdgePosition() > MARTH_UP_B_X_DISTANCE + 5 ||
            edge_distance_y > MARTH_UP_B_HEIGHT + 15))
        {
            CreateChain(GrabEdge);
            m_chain->PressButtons();
            return;
        }

        //If marth is side-B'ing (out of attack range) then it's safe
        if(m_state->m_memory->player_one_action == SWORD_DANCE_1)
        {
            CreateChain(GrabEdge);
            m_chain->PressButtons();
            return;
        }
    }

    //Do the marth killer if we're on the stage and Marth is going to be stuck recovering with an up-B
    if(m_state->m_memory->player_two_on_ground &&
        m_state->m_memory->player_one_jumps_left == 0 &&
        edge_distance_x > 30)
    {
        CreateChain(MarthKiller);
        m_chain->PressButtons();
        return;
    }

    //If we're still on the stage, then dash dance around the edge
    if(m_state->m_memory->player_two_on_ground)
    {
        bool onLeft = m_state->m_memory->player_one_x < 0;
        double pivotPoint = onLeft ? (-1) * m_state->getStageEdgeGroundPosition() : m_state->getStageEdgeGroundPosition();
        CreateChain3(DashDance, pivotPoint, 0);
        m_chain->PressButtons();
        return;
    }

    //Aim at our opponent if we're falling at them
    if(m_state->m_memory->player_two_y > m_state->m_memory->player_one_y &&
        m_state->m_memory->player_two_action == FALLING)
    {
        CreateChain3(DI, m_state->m_memory->player_one_x > m_state->m_memory->player_two_x ? true : false, .5);
        m_chain->PressButtons();
        return;
    }

    //Just hang out and do nothing
    CreateChain(Nothing);
    m_chain->PressButtons();
    return;
}