Walking

Revision as of 20:50, 18 June 2024 by Icecream17 (talk | contribs) (set_mario_action)

The walking action occurs when Mario is moving only by using the analog stick. It can also occur when exiting water (except if exiting water through a water jump).

Walking
Properties
Hex todo
Action Flags todo
Action Group Moving
ID todo
Transitions
Into common moving cancels: Water Plunge, Shockwave Bounce, Squished, Standing Death, Quicksand Death, non-cancel: Begin Sliding, Standing against Wall, Breaking, Decelerating, (jumps: Hold Quicksand Jump Land, Quicksand Jump Land, Steep Jump, Jump, Doube Jump, Flying Triple Jump, Triple Jump), Dive, Move Punching, Turning Around, Crouch Slide, Freefall, Ledge Climb Down
A velocity/time graph of Mario's speed during the first 45 frames (1.5 seconds) of walking
Mario's speed during the first 45 frames (1.5 seconds) of walking. Note that this depends on the floor's slope (see De facto speed).

set_mario_action

Before even the common cancels or behavior, when transitioning into this action:

  1. If the floor is not very slippery, and Mario's forward velocity is from [0 to min(intendedMag, 8))
    1. Set forward velocity to min(intendedMag, 8)
  2. Set walking pitch to 0

Common moving cancels

All actions in the "moving" group check the following cancels before running any behavior script:

  1. If Mario's y position is lower than 100 units below the water level, do stuff, Water Plunge
  2. If Mario's current action does not have the "invulnerable" flag, and input_stomped, dropheldobject stopridingobject Shockwave Bounce
  3. If input_squished, dropheldobject stopridingobject Squished
  4. If Mario's current action does not have the "invulnerable" flag, and health < 0x100, dropheldobject stopridingobject Standing Death
  5. Potentially cancel into Quicksand Death (sinkingSpeed = 0.25)

Set jump from landing

  1. If quicksand depth > 11, if Mario is holding an object, Hold Quicksand Jump Land else Quicksand Jump Land
  2. If on steep floor, update forwardVelocity and facing direction, Steep Jump
  3. Else:
    1. If double jump timer is 0, or squish timer is not 0, Jump
    2. Else if the previous action is:
      1. Jump Land, Freefall Land, or Side Flip Land Stop: Double Jump
      2. Double Jump Land; then
        1. if has wing cap: Flying Triple Jump
        2. if forward velocity > 20: Triple Jump
        3. Jump
      3. else: Jump
  4. Set double jump timer to 0

Behavior

When walking[1]:

  1. mario_drop_held_object
  2. if should_begin_sliding; Begin Sliding
  3. if INPUT_FIRST_PERSON:
    1. mario_drop_held_object
    2. if actionState == 1; Standing Against Wall
    3. if forwardVel >= 16 and m->floor->normal.y >= 0.17364818; Braking
    4. else; Decelerating
  4. if INPUT_A_PRESSED; #Set jump from landing[2]
  5. if INPUT_B_PRESSED;
    1. if forwardVel >= 29 and stickMag > 48; yVel=20; Dive
    2. else; Move Punching
  6. if INPUT_UNKNOWN_5 (aka neutral joystick: https://ukikipedia.net/mediawiki/index.php?title=Turning_Around&oldid=17030); (same code as INPUT_FIRST_PERSON)
  7. if analog_stick_held_back and forwardVel >= 16; Turning Around
  8. if INPUT_Z_PRESSED; Crouch Slide
  9. update_walking_speed
  10. switch (perform_ground_step):
    1. case GROUND_STEP_LEFT_GROUND; Freefall
    2. case GROUND_STEP_NONE: if intendedMag - forwardVel > 16, dust
    3. case GROUND_STEP_HIT_WALL; push_or_sidle_wall
  11. check_ledge_climb_down (can transition to Ledge Climb Down)
  12. tilt_body_walking

Here's what update_walking_speed is for convenience:

void update_walking_speed(struct MarioState *m) {
    f32 maxTargetSpeed;
    f32 targetSpeed;

    if (m->floor != NULL && m->floor->type == SURFACE_SLOW) {
        maxTargetSpeed = 24.0f;
    } else {
        maxTargetSpeed = 32.0f;
    }

    targetSpeed = m->intendedMag < maxTargetSpeed ? m->intendedMag : maxTargetSpeed;

    if (m->quicksandDepth > 10.0f) {
        targetSpeed *= 6.25 / m->quicksandDepth;
    }

    if (m->forwardVel <= 0.0f) {
        m->forwardVel += 1.1f;
    } else if (m->forwardVel <= targetSpeed) {
        m->forwardVel += 1.1f - m->forwardVel / 43.0f;
    } else if (m->floor->normal.y >= 0.95f) {
        m->forwardVel -= 1.0f;
    }

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

    m->faceAngle[1] =
        m->intendedYaw - approach_s32((s16)(m->intendedYaw - m->faceAngle[1]), 0, 0x800, 0x800);
    apply_slope_accel(m);
}

/**
 * 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 apply_slope_accel(struct MarioState *m) {
    f32 slopeAccel;

    struct Surface *floor = m->floor;
    f32 steepness = sqrtf(floor->normal.x * floor->normal.x + floor->normal.z * floor->normal.z);

    UNUSED f32 normalY = floor->normal.y;
    s16 floorDYaw = m->floorAngle - m->faceAngle[1];

    if (mario_floor_is_slope(m)) {
        s16 slopeClass = 0;

        if (m->action != ACT_SOFT_BACKWARD_GROUND_KB && m->action != ACT_SOFT_FORWARD_GROUND_KB) {
            slopeClass = mario_get_floor_class(m);
        }

        switch (slopeClass) {
            case SURFACE_CLASS_VERY_SLIPPERY:
                slopeAccel = 5.3f;
                break;
            case SURFACE_CLASS_SLIPPERY:
                slopeAccel = 2.7f;
                break;
            default:
                slopeAccel = 1.7f;
                break;
            case SURFACE_CLASS_NOT_SLIPPERY:
                slopeAccel = 0.0f;
                break;
        }

        if (floorDYaw > -0x4000 && floorDYaw < 0x4000) {
            m->forwardVel += slopeAccel * steepness;
        } else {
            m->forwardVel -= slopeAccel * steepness;
        }
    }

    m->slideYaw = m->faceAngle[1];

    m->slideVelX = m->forwardVel * sins(m->faceAngle[1]);
    m->slideVelZ = m->forwardVel * coss(m->faceAngle[1]);

    m->vel[0] = m->slideVelX;
    m->vel[1] = 0.0f;
    m->vel[2] = m->slideVelZ;

    mario_update_moving_sand(m);
    mario_update_windy_ground(m);
}

static s16 sMovingSandSpeeds[] = { 12, 8, 4, 0 };
u32 mario_update_moving_sand(struct MarioState *m) {
    struct Surface *floor = m->floor;
    s32 floorType = floor->type;

    if (floorType == SURFACE_DEEP_MOVING_QUICKSAND || floorType == SURFACE_SHALLOW_MOVING_QUICKSAND
        || floorType == SURFACE_MOVING_QUICKSAND || floorType == SURFACE_INSTANT_MOVING_QUICKSAND) {
        s16 pushAngle = floor->force << 8;
        f32 pushSpeed = sMovingSandSpeeds[floor->force >> 8];

        m->vel[0] += pushSpeed * sins(pushAngle);
        m->vel[2] += pushSpeed * coss(pushAngle);

        return TRUE;
    }

    return FALSE;
}

u32 mario_update_windy_ground(struct MarioState *m) {
    struct Surface *floor = m->floor;

    if (floor->type == SURFACE_HORIZONTAL_WIND) {
        f32 pushSpeed;
        s16 pushAngle = floor->force << 8;

        if (m->action & ACT_FLAG_MOVING) {
            s16 pushDYaw = m->faceAngle[1] - pushAngle;

            pushSpeed = m->forwardVel > 0.0f ? -m->forwardVel * 0.5f : -8.0f;

            if (pushDYaw > -0x4000 && pushDYaw < 0x4000) {
                pushSpeed *= -1.0f;
            }

            pushSpeed *= coss(pushDYaw);
        } else {
            pushSpeed = 3.2f + (gGlobalTimer % 4);
        }

        m->vel[0] += pushSpeed * sins(pushAngle);
        m->vel[2] += pushSpeed * coss(pushAngle);

#ifdef VERSION_JP
        play_sound(SOUND_ENV_WIND2, m->marioObj->header.gfx.cameraToObject);
#endif
        return TRUE;
    }

    return FALSE;
}

References