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(); } }
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 } }
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(); } }
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; }
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; }
void Techroll::DetermineChain() { CreateChain2(Tech, IN_PLACE); m_chain->PressButtons(); return; }
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; }
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; }