Walking: Difference between revisions
Jump to navigation
Jump to search
(added mario speed graph) |
Icecream17 (talk | contribs) (consistent title) |
||
(8 intermediate revisions by 2 users not shown) | |||
Line 7: | Line 7: | ||
|group= Moving | |group= Moving | ||
|id= todo | |id= todo | ||
|into= | |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]] | ||
|out of= | |out of= | ||
|animation= | |animation= | ||
|related= | |related= | ||
}} | }} | ||
[[File:Walking.svg|alt=A velocity/time graph of Mario's speed during the first 45 frames (1.5 seconds) of walking|thumb|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]]).]] | |||
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]]). | |||
== Transition in == | |||
Before even the common cancels or behavior, when transitioning into this action: | |||
# If the floor is not very slippery, and Mario's forward velocity is from [0 to min(intendedMag, 8)) | |||
## Set forward velocity to min(intendedMag, 8) | |||
# Set walking pitch to 0 | |||
== Common moving cancels == | |||
All actions in the "moving" group check the following cancels before running any behavior script: | |||
# If Mario's y position is lower than 100 units below the water level, do stuff, [[Water Plunge]] | |||
# If Mario's current action does not have the "invulnerable" flag, and input_stomped, dropheldobject stopridingobject [[Shockwave Bounce]] | |||
# If input_squished, dropheldobject stopridingobject [[Squished]] | |||
# If Mario's current action does not have the "invulnerable" flag, and health < 0x100, dropheldobject stopridingobject [[Standing Death]] | |||
# [[Idle#quicksand death|Potentially cancel into Quicksand Death]] (sinkingSpeed = 0.25) | |||
== Set jump from landing == | |||
# If quicksand depth > 11, if Mario is holding an object, [[Hold Quicksand Jump Land]] else [[Quicksand Jump Land]] | |||
# If on steep floor, update forwardVelocity and facing direction, [[Steep Jump]] | |||
# Else: | |||
## If double jump timer is 0, or squish timer is not 0, [[Jump]] | |||
## Else if the previous action is: | |||
### [[Jump Land]], [[Freefall Land]], or [[Side Flip Land Stop]]: [[Double Jump]] | |||
### [[Double Jump Land]]; then | |||
#### if has wing cap: [[Flying Triple Jump]] | |||
#### if forward velocity > 20: [[Triple Jump]] | |||
#### [[Jump]] | |||
### else: [[Jump]] | |||
# Set double jump timer to 0 | |||
== Behavior == | |||
When walking<ref>https://github.com/n64decomp/sm64/blob/master/src/game/mario_actions_moving.c#L777</ref>: | When walking<ref>https://github.com/n64decomp/sm64/blob/master/src/game/mario_actions_moving.c#L777</ref>: | ||
# | # mario_drop_held_object | ||
# if should_begin_sliding; [[Begin Sliding]] | #if should_begin_sliding; [[Begin Sliding]] | ||
# if INPUT_FIRST_PERSON: | #if INPUT_FIRST_PERSON: | ||
## mario_drop_held_object | ##mario_drop_held_object | ||
## if actionState == 1; [[Standing Against Wall]] | ## if actionState == 1; [[Standing Against Wall]] | ||
## if forwardVel >= 16 and m->floor->normal.y >= 0.17364818; [[Braking]] | ##if forwardVel >= 16 and m->floor->normal.y >= 0.17364818; [[Braking]] | ||
## else; [[Decelerating]] | ##else; [[Decelerating]] | ||
# if INPUT_A_PRESSED; | #if INPUT_A_PRESSED; [[#Set jump from landing]]<ref>https://github.com/n64decomp/sm64/blob/1372ae1bb7cbedc03df366393188f4f05dcfc422/src/game/mario.c#L1018 set_jump_from_landing</ref> | ||
# if INPUT_B_PRESSED; | #if INPUT_B_PRESSED; | ||
## if forwardVel >= 29 and stickMag > 48; yVel=20; [[Dive]] | ##if forwardVel >= 29 and stickMag > 48; yVel=20; [[Dive]] | ||
## else; [[Move Punching]] | ##else; [[Move Punching]] | ||
# if INPUT_UNKNOWN_5 (aka neutral joystick: https://ukikipedia.net/mediawiki/index.php?title=Turning_Around&oldid=17030); (same code as INPUT_FIRST_PERSON) | #if INPUT_UNKNOWN_5 (aka neutral joystick: https://ukikipedia.net/mediawiki/index.php?title=Turning_Around&oldid=17030); (same code as INPUT_FIRST_PERSON) | ||
# if analog_stick_held_back and forwardVel >= 16; [[Turning Around]] | #if analog_stick_held_back and forwardVel >= 16; [[Turning Around]] | ||
# if INPUT_Z_PRESSED; [[Crouch Slide]] | #if INPUT_Z_PRESSED; [[Crouch Slide]] | ||
# | #update_walking_speed | ||
# case GROUND_STEP_LEFT_GROUND; [[Freefall]] | #switch (perform_ground_step): | ||
# case GROUND_STEP_HIT_WALL; push_or_sidle_wall | ##case GROUND_STEP_LEFT_GROUND; [[Freefall]] | ||
##case GROUND_STEP_NONE: if intendedMag - forwardVel > 16, dust | |||
##case GROUND_STEP_HIT_WALL; push_or_sidle_wall | |||
#check_ledge_climb_down (can transition to [[Ledge Climb Down]]) | |||
#tilt_body_walking | |||
Here's what update_walking_speed is for convenience:<syntaxhighlight lang="c"> | |||
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; | |||
} | |||
</syntaxhighlight> | |||
==References== | ==References== | ||
<references /> | <references /> | ||
{{actions}} | {{actions}} |
Latest revision as of 20:51, 18 June 2024
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 |
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).
Transition in
Before even the common cancels or behavior, when transitioning into this action:
- If the floor is not very slippery, and Mario's forward velocity is from [0 to min(intendedMag, 8))
- Set forward velocity to min(intendedMag, 8)
- Set walking pitch to 0
Common moving cancels
All actions in the "moving" group check the following cancels before running any behavior script:
- If Mario's y position is lower than 100 units below the water level, do stuff, Water Plunge
- If Mario's current action does not have the "invulnerable" flag, and input_stomped, dropheldobject stopridingobject Shockwave Bounce
- If input_squished, dropheldobject stopridingobject Squished
- If Mario's current action does not have the "invulnerable" flag, and health < 0x100, dropheldobject stopridingobject Standing Death
- Potentially cancel into Quicksand Death (sinkingSpeed = 0.25)
Set jump from landing
- If quicksand depth > 11, if Mario is holding an object, Hold Quicksand Jump Land else Quicksand Jump Land
- If on steep floor, update forwardVelocity and facing direction, Steep Jump
- Else:
- If double jump timer is 0, or squish timer is not 0, Jump
- Else if the previous action is:
- Jump Land, Freefall Land, or Side Flip Land Stop: Double Jump
- Double Jump Land; then
- if has wing cap: Flying Triple Jump
- if forward velocity > 20: Triple Jump
- Jump
- else: Jump
- Set double jump timer to 0
Behavior
When walking[1]:
- mario_drop_held_object
- if should_begin_sliding; Begin Sliding
- if INPUT_FIRST_PERSON:
- mario_drop_held_object
- if actionState == 1; Standing Against Wall
- if forwardVel >= 16 and m->floor->normal.y >= 0.17364818; Braking
- else; Decelerating
- if INPUT_A_PRESSED; #Set jump from landing[2]
- if INPUT_B_PRESSED;
- if forwardVel >= 29 and stickMag > 48; yVel=20; Dive
- else; Move Punching
- if INPUT_UNKNOWN_5 (aka neutral joystick: https://ukikipedia.net/mediawiki/index.php?title=Turning_Around&oldid=17030); (same code as INPUT_FIRST_PERSON)
- if analog_stick_held_back and forwardVel >= 16; Turning Around
- if INPUT_Z_PRESSED; Crouch Slide
- update_walking_speed
- switch (perform_ground_step):
- case GROUND_STEP_LEFT_GROUND; Freefall
- case GROUND_STEP_NONE: if intendedMag - forwardVel > 16, dust
- case GROUND_STEP_HIT_WALL; push_or_sidle_wall
- check_ledge_climb_down (can transition to Ledge Climb Down)
- 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;
}