The package keeps the rules and hands them over each step: speed and heading, whether a jump leaves, CHAR_STEP and the boots' slope limit. The walker (Jolt CharacterVirtual) does gravity, landing, steps, slopes and following the ground down. Too steep is ground too steep facing the way the body wanted to go; stepping off an edge is not. ludic.physics: phys_walker_move (follow the body if something else moved it, then step), the slope limit, the jump always honoured. The character's tests run on a real Jolt walker over the same fake world: 15 pass, a knee-high boulder now a step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
65 lines
3.1 KiB
Text
65 lines
3.1 KiB
Text
# walk.ludic - the step itself: swimming, in the air, or on the ground; the distance, for the gait
|
|
function chr_travel(character_st: mut CharacterState, dx: float, dz: float, fx: float, fz: float, jump: bool, dt: float) -> float {
|
|
if character_st.chr_swim {
|
|
chr_swim_tick(character_st, dx, dz, jump, dt)
|
|
return character_st.chr_speed * dt
|
|
}
|
|
return chr_walk(character_st, fx, fz, dx, dz, jump, dt)
|
|
}
|
|
|
|
# on land the game's walker moves the body (CharacterGround.walk): gravity, landing, a step up to
|
|
# CHAR_STEP, a slope up to what the boots take. The rules stay here: how fast, where, when a jump
|
|
# leaves, when the lake takes the body, and what is said about it.
|
|
function chr_walk(character_st: mut CharacterState, fx: float, fz: float, dx: float, dz: float, jump: bool, dt: float) -> float {
|
|
var vx = fx * character_st.chr_speed
|
|
var vz = fz * character_st.chr_speed
|
|
var up = 0.0
|
|
if character_st.chr_air {
|
|
character_st.chr_air_vx = character_st.chr_air_vx + dx * (4.0 * dt)
|
|
character_st.chr_air_vz = character_st.chr_air_vz + dz * (4.0 * dt)
|
|
vx = character_st.chr_air_vx
|
|
vz = character_st.chr_air_vz
|
|
} else if jump and character_st.chr_can_run and character_st.chr_land < 0.05 {
|
|
up = 5.0
|
|
character_st.chr_air_vx = vx
|
|
character_st.chr_air_vz = vz
|
|
chr_say(character_st, CHAR_JUMPED, 0.0)
|
|
}
|
|
let slope = Math.atan2(CharacterGround.slope_limit(), 1.0) * 57.29578
|
|
let g = CharacterGround.walk(character_st.chr_x, character_st.chr_y, character_st.chr_z, vx, vz, up, CHAR_STEP, slope, dt)
|
|
return chr_walked(character_st, g, up > 0.0, dt)
|
|
}
|
|
|
|
# where the walker put the body, and what that means: the lake, a landing, a slope too steep
|
|
function chr_walked(character_st: mut CharacterState, g: int, jumped: bool, dt: float) -> float {
|
|
let nx = CharacterGround.walked_x()
|
|
let ny = CharacterGround.walked_y()
|
|
let nz = CharacterGround.walked_z()
|
|
if ny < CharacterGround.water(nx, nz) - CHAR_WADE_MAX {
|
|
chr_enter_water(character_st, nx, nz)
|
|
return character_st.chr_speed * dt
|
|
}
|
|
let was_air = character_st.chr_air
|
|
character_st.chr_air = g == CHAR_WALK_AIR or jumped
|
|
if was_air and not character_st.chr_air { chr_landed(character_st) }
|
|
if g == CHAR_WALK_STEEP and not character_st.chr_air {
|
|
if not (character_st.chr_steep > 0.0) { chr_say(character_st, CHAR_TOO_STEEP, ny - character_st.chr_y) }
|
|
character_st.chr_steep = 1.2
|
|
character_st.chr_speed = 0.0
|
|
}
|
|
let mx = nx - character_st.chr_x
|
|
let mz = nz - character_st.chr_z
|
|
character_st.chr_step = Math.sqrt(mx * mx + mz * mz)
|
|
character_st.chr_vy = (ny - character_st.chr_y) / Math.max(dt, 0.0001)
|
|
character_st.chr_x = nx
|
|
character_st.chr_y = ny
|
|
character_st.chr_z = nz
|
|
if character_st.chr_air { return character_st.chr_speed * (dt * 0.4) }
|
|
return character_st.chr_speed * dt
|
|
}
|
|
|
|
function chr_landed(character_st: mut CharacterState) -> void {
|
|
character_st.chr_land = 0.22
|
|
character_st.chr_speed = Math.sqrt(character_st.chr_air_vx * character_st.chr_air_vx + character_st.chr_air_vz * character_st.chr_air_vz)
|
|
chr_say(character_st, CHAR_LANDED, character_st.chr_speed)
|
|
}
|