Walking

Revision as of 00:26, 21 July 2023 by Icecream17 (talk | contribs) (there's more)

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, todo, 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).

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)

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; (todo; see ref) [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