Flying

From Ukikipedia
Revision as of 20:51, 3 August 2023 by Icecream17 (talk | contribs) (this code is so indirect)
Jump to navigation Jump to search
Flying
Properties
Hex 0x10880899
Action Flags air, diving, attacking, swimming/flying
Action Group Airborne
ID 0x099
Transitions
Into Airborner cancels: Water Plunge, Squished, Vertical Wind (theoretically), (Technically, because of the flying triple jump code): Double Jump Land, Lava Boost (theoretically), non cancel: Ground Pound, Freefall, Dive Slide, Backwards Air Kb, Lava Boost (again) (theoretically)
Out of Shot From Cannon, Flying Triple Jump, when spawning in some levels like Tower of the Wing Cap
Other
Animation 0x5B fly from cannon, 0xCF forwards spinning flip, 0x29 wing cap fly

Flying is an action that can occurs when Mario triple jumps or does a cannon shot while wearing the wing cap.

Entering flying

  • Cannon shot: when y vel < 0 (and Mario does not cancel) (Action argument: 0)
  • Flying Triple Jump: when y vel < 4 (and Mario does not cancel, dive, or ground pound) (Action argument: 1)

Behavior

As with all airborne actions, a variety of "cancels" are checked prior to actually performing any airborne action. See Jump#Airborne cancels.

Then:

  1. If Z is pressed
    1. If Mario's camera mode is CAMERA_MODE_BEHIND_MARIO, set_camera_mode to m->area->camera->defMode
    2. Ground Pound
  2. If Mario is not wearing the wing cap
    1. If Mario's camera mode is CAMERA_MODE_BEHIND_MARIO, set_camera_mode to m->area->camera->defMode
    2. Freefall
  3. If Mario's camera mode is not CAMERA_MODE_BEHIND_MARIO, set_camera_mode to CAMERA_MODE_BEHIND_MARIO
  4. If the action state is 0
    1. If the action argument is 0 (was shot from cannon), set Mario's animation to MARIO_ANIM_FLY_FROM_CANNON, else set Mario's animation to MARIO_ANIM_FORWARD_SPINNING_FLIP
    2. If the animation finished,
      1. If the action argument is 2 (spawned in), load_level_init_text and set action argument to 1
      2. Set Mario's animation to MARIO_ANIM_WING_CAP_FLY
      3. Set action state to 1
  5. Call update_flying
  6. switch Movement_steps#Perform_Air_Step:
    • air step none:
      1. Update graphics (camera) angle to be behind Mario
      2. Set action timer to 0
    • air step land:
      1. Set action to Dive Slide
      2. Set animation to MARIO_ANIM_DIVE
      3. Set animation frame to 7
      4. Set facing angle (x) to 0
      5. Set camera mode to m->area->camera->defMode
    • air step hit wall:
      1. todo
      2. if wall is not null, stuff, Backwards Air Kb
      3. else, stuff (but no knockback, this is probably out of bounds)
    • air step hit lava wall:
      1. stop holding, stop riding, Lava Boost
  7. finally, play the flying sound (adjust sound for speed)

Notice that there is no code handling air steps for ledge grabbing or hanging on a ceiling, so such transitions are impossible.

update_flying

I am too lazy to describe the exact details of update_flying, so here is the code

void update_flying(struct MarioState *m) {
    UNUSED u8 filler[4];

    update_flying_pitch(m);
    update_flying_yaw(m);

    m->forwardVel -= 2.0f * ((f32) m->faceAngle[0] / 0x4000) + 0.1f;
    m->forwardVel -= 0.5f * (1.0f - coss(m->angleVel[1]));

    if (m->forwardVel < 0.0f) {
        m->forwardVel = 0.0f;
    }

    if (m->forwardVel > 16.0f) {
        m->faceAngle[0] += (m->forwardVel - 32.0f) * 6.0f;
    } else if (m->forwardVel > 4.0f) {
        m->faceAngle[0] += (m->forwardVel - 32.0f) * 10.0f;
    } else {
        m->faceAngle[0] -= 0x400;
    }

    m->faceAngle[0] += m->angleVel[0];

    if (m->faceAngle[0] > 0x2AAA) {
        m->faceAngle[0] = 0x2AAA;
    }
    if (m->faceAngle[0] < -0x2AAA) {
        m->faceAngle[0] = -0x2AAA;
    }

    m->vel[0] = m->forwardVel * coss(m->faceAngle[0]) * sins(m->faceAngle[1]);
    m->vel[1] = m->forwardVel * sins(m->faceAngle[0]);
    m->vel[2] = m->forwardVel * coss(m->faceAngle[0]) * coss(m->faceAngle[1]);

    m->slideVelX = m->vel[0];
    m->slideVelZ = m->vel[2];
}

Here are some tables summarizing the approach_s32, update_flying_pitch, and update_flying_yaw functions.

update_flying_pitch (target vel = -(stickX * (forwardVel / 5)))
joystick is down (move up) joystick is up (move down) joystick is neutral
negative pitch velocity add 64 vel, cap at 32 approach (at most 40 up or 20 down) approach 0 (by at most 40)
positive pitch velocity approach (at most 20 down or 40 up) subtract 64 vel, cap at -32 approach 0 (by at most 40)
zero pitch velocity approach (at most 20 down or 40 up) approach (at most 40 up or 20 down) approach 0 (by at most 40)
update_flying_yaw (target vel = -(stickY * (forwardVel / 4)))
joystick is left joystick is right joystick is neutral
negative yaw velocity add 64 vel, cap at 16 approach (at most 20 up or 10 down) approach 0 (by at most 40)
positive yaw velocity approach (at most 10 up or 20 down) subtract 64 vel, cap at -16 approach 0 (by at most 40)
zero yaw velocity approach (at most 10 up or 20 down) approach (at most 20 up or 10 down) approach 0 (by at most 40)

After that, update_flying_yaw does:

  1. Add yaw velocity to yaw
  2. Set roll to yaw times negative twenty (-20)

Note that turning left = positive yaw (up = positive pitch).[1] As can be seen, many joystick positions will be equivalent, and the pitch velocity or yaw velocity change by at most 40.

/**
 * Return the value 'current' after it tries to approach target, going up at
 * most 'inc' and going down at most 'dec'.
 */
s32 approach_s32(s32 current, s32 target, s32 inc, s32 dec) {
    //! If target is close to the max or min s32, then it's possible to overflow
    // past it without stopping.

    if (current < target) {
        current += inc;
        if (current > target) {
            current = target;
        }
    } else {
        current -= dec;
        if (current < target) {
            current = target;
        }
    }
    return current;
}

void update_flying_yaw(struct MarioState *m) {
    s16 targetYawVel = -(s16)(m->controller->stickX * (m->forwardVel / 4.0f));

    if (targetYawVel > 0) {
        if (m->angleVel[1] < 0) {
            m->angleVel[1] += 0x40;
            if (m->angleVel[1] > 0x10) {
                m->angleVel[1] = 0x10;
            }
        } else {
            m->angleVel[1] = approach_s32(m->angleVel[1], targetYawVel, 0x10, 0x20);
        }
    } else if (targetYawVel < 0) {
        if (m->angleVel[1] > 0) {
            m->angleVel[1] -= 0x40;
            if (m->angleVel[1] < -0x10) {
                m->angleVel[1] = -0x10;
            }
        } else {
            m->angleVel[1] = approach_s32(m->angleVel[1], targetYawVel, 0x20, 0x10);
        }
    } else {
        m->angleVel[1] = approach_s32(m->angleVel[1], 0, 0x40, 0x40);
    }

    m->faceAngle[1] += m->angleVel[1];
    m->faceAngle[2] = 20 * -m->angleVel[1];
}

void update_flying_pitch(struct MarioState *m) {
    s16 targetPitchVel = -(s16)(m->controller->stickY * (m->forwardVel / 5.0f));

    if (targetPitchVel > 0) {
        if (m->angleVel[0] < 0) {
            m->angleVel[0] += 0x40;
            if (m->angleVel[0] > 0x20) {
                m->angleVel[0] = 0x20;
            }
        } else {
            m->angleVel[0] = approach_s32(m->angleVel[0], targetPitchVel, 0x20, 0x40);
        }
    } else if (targetPitchVel < 0) {
        if (m->angleVel[0] > 0) {
            m->angleVel[0] -= 0x40;
            if (m->angleVel[0] < -0x20) {
                m->angleVel[0] = -0x20;
            }
        } else {
            m->angleVel[0] = approach_s32(m->angleVel[0], targetPitchVel, 0x40, 0x20);
        }
    } else {
        m->angleVel[0] = approach_s32(m->angleVel[0], 0, 0x40, 0x40);
    }
}

References