feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune

ludic.base's Queue<T> carries a QueueTag (its name and pending count): queue_new(name), q_push(q, v),
q_drain(q), q_clear(q) take no BaseState, and core_undrained(tags) names the given queues still holding
facts. A reducer on a package's state can now call that package's verbs (wallet_earn(wallet_st, n)).

ludic migrate state --prune (ludicc --migrate-prune) takes out each state parameter a function no
longer uses, nor anything it calls, and the argument that fills it - including an argument for a
parameter the callee has dropped, which is taken out before the call is checked, so a generic's T is
told by the argument that says it. A reducer keeps its state. --dry-run now counts the edits it would
make. Every package was moved with it: 608 base_st parameters and their arguments, 1889 edits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-26 04:19:56 +03:00
parent 8cf4b3fed6
commit 71735b10a2
151 changed files with 58668 additions and 54719 deletions

View file

@ -9,17 +9,17 @@ const chr_pi: float = 3.14159265
export function char_facts(base_st: mut BaseState, character_st: mut CharacterState) -> Queue<CharacterFact> {
if character_st.chr_fact_q == null { character_st.chr_fact_q = queue_new(base_st, "character.facts") }
export function char_facts(character_st: mut CharacterState) -> Queue<CharacterFact> {
if character_st.chr_fact_q == null { character_st.chr_fact_q = queue_new("character.facts") }
return character_st.chr_fact_q
}
function chr_say(base_st: mut BaseState, character_st: mut CharacterState, what: int, v: float) -> void {
function chr_say(character_st: mut CharacterState, what: int, v: float) -> void {
let f = new CharacterFact
f.what = what
f.x = character_st.chr_x
f.y = character_st.chr_y
f.z = character_st.chr_z
f.v = v
q_push(base_st, char_facts(base_st, character_st), f)
q_push(char_facts(character_st), f)
}

View file

@ -34,7 +34,7 @@ function chr_steer(character_st: mut CharacterState, dx: float, dz: float, mag:
# The gait's phase is distance over the stride, so the feet never slide; a boot comes down as the
# phase crosses 0 and again as it crosses pi, and that is when a footfall is said.
function chr_gait(base_st: mut BaseState, character_st: mut CharacterState, dist: float, dyaw: float, dpitch: float, dt: float) -> void {
function chr_gait(character_st: mut CharacterState, dist: float, dyaw: float, dpitch: float, dt: float) -> void {
if character_st.chr_land > 0.0 { character_st.chr_land = character_st.chr_land - dt }
if character_st.chr_steep > 0.0 { character_st.chr_steep = character_st.chr_steep - dt }
if character_st.chr_swim { character_st.chr_swim_phase = (character_st.chr_swim_phase + dt * (1.6 + character_st.chr_speed * 0.9)) % (2.0 * chr_pi) }
@ -42,7 +42,7 @@ function chr_gait(base_st: mut BaseState, character_st: mut CharacterState, dist
let phase0 = character_st.chr_phase
character_st.chr_phase = (character_st.chr_phase + dist / stride * (2.0 * chr_pi)) % (2.0 * chr_pi)
if not character_st.chr_air and not character_st.chr_swim and not character_st.chr_sit and character_st.chr_speed > 0.4 {
if chr_cross(phase0, character_st.chr_phase, 0.0) or chr_cross(phase0, character_st.chr_phase, chr_pi) { chr_say(base_st, character_st, CHAR_FOOTFALL, character_st.chr_speed) }
if chr_cross(phase0, character_st.chr_phase, 0.0) or chr_cross(phase0, character_st.chr_phase, chr_pi) { chr_say(character_st, CHAR_FOOTFALL, character_st.chr_speed) }
}
var mv = 0.0
if character_st.chr_speed > 0.12 { mv = 1.0 }

View file

@ -1,10 +1,10 @@
# swim.ludic - afloat: buoyancy, the descent, the breath, and the shore stopping the body
function chr_swim_tick(base_st: mut BaseState, character_st: mut CharacterState, dx: float, dz: float, jump: bool, dt: float) -> void {
function chr_swim_tick(character_st: mut CharacterState, dx: float, dz: float, jump: bool, dt: float) -> void {
# standing still out here is floating, not swimming
if Math.abs(dx) + Math.abs(dz) > 0.05 { character_st.chr_float = 0.0 } else { character_st.chr_float = Math.min(1.0, character_st.chr_float + dt / 1.5) }
chr_dive_tick(character_st, jump, dt)
chr_breath_tick(base_st, character_st, dt)
chr_current_tick(base_st, character_st, dt)
chr_breath_tick(character_st, dt)
chr_current_tick(character_st, dt)
let step = character_st.chr_speed * dt
if step > 0.0 {
chr_push(character_st, character_st.chr_x - Math.sin(character_st.chr_yaw) * step, character_st.chr_z - Math.cos(character_st.chr_yaw) * step, 0.35, character_st.chr_y, character_st.chr_y + CHAR_BODY)
@ -18,5 +18,5 @@ function chr_swim_tick(base_st: mut BaseState, character_st: mut CharacterState,
let bob = 0.04 * Math.sin(character_st.chr_time * 3.8)
character_st.chr_y = character_st.chr_water - 0.30 + bob - character_st.chr_depth
# shallow enough to stand up in, and at the top rather than under it
if chr_bed_depth(character_st.chr_x, character_st.chr_z) < CHAR_STAND_UP and character_st.chr_depth < 0.2 { chr_leave_water(base_st, character_st) }
if chr_bed_depth(character_st.chr_x, character_st.chr_z) < CHAR_STAND_UP and character_st.chr_depth < 0.2 { chr_leave_water(character_st) }
}

View file

@ -1,6 +1,6 @@
# system.ludic - the body as a system: reset, a tick from the input port, and nothing saved (where a
# body stands is the game's to write, with the rest of the player)
export function char_reset(base_st: mut BaseState, character_st: mut CharacterState) -> void {
export function char_reset(character_st: mut CharacterState) -> void {
char_teleport(character_st, 0.0, 0.0, 0.0)
character_st.chr_time = 0.0
character_st.chr_phase = 0.0
@ -11,10 +11,10 @@ export function char_reset(base_st: mut BaseState, character_st: mut CharacterSt
character_st.chr_land = 0.0
character_st.chr_edge_said = 0.0
character_st.chr_fps = false
q_clear(base_st, char_facts(base_st, character_st))
q_clear(char_facts(character_st))
}
function chr_sys_tick(base_st: mut BaseState, character_st: mut CharacterState, t: Tick) -> void { char_tick(base_st, character_st, t.dt) }
function chr_sys_tick(character_st: mut CharacterState, t: Tick) -> void { char_tick(character_st, t.dt) }
export function char_system() -> System {
let s = system_new("character", PH_INPUT)

View file

@ -65,85 +65,85 @@ program CharacterTest {
bind CharacterBody { riding: fn fk_riding, ride: fn fk_ride, sneaking: fn fk_sneak, pace: fn fk_pace, eyes_ok: fn fk_eyes }
bind CharacterInput { move_z: fn fk_move_z }
function fresh(base_st: mut BaseState, character_st: mut CharacterState, x: float, z: float, yaw: float) -> void {
char_reset(base_st, character_st)
function fresh(character_st: mut CharacterState, x: float, z: float, yaw: float) -> void {
char_reset(character_st)
char_release(character_st, CHAR_HOLD_SCREEN)
char_teleport(character_st, x, z, yaw)
}
function facts(base_st: mut BaseState, character_st: mut CharacterState) -> []CharacterFact { return q_drain(base_st, char_facts(base_st, character_st)) }
function facts(character_st: mut CharacterState) -> []CharacterFact { return q_drain(char_facts(character_st)) }
function count(fs: []CharacterFact, what: int) -> int {
var n = 0
for i in 0 .. len(fs) { if fs[i].what == what { n += 1 } }
return n
}
# walk the stick straight ahead (the camera looks where the body faces) for `secs`
function walk(base_st: mut BaseState, character_st: mut CharacterState, secs: float, hz: float, run: bool) -> void {
function walk(character_st: mut CharacterState, secs: float, hz: float, run: bool) -> void {
let n = int(secs * hz)
for i in 0 .. n { char_update(base_st, character_st, 0.0, 1.0, run, false, 0.0, 0.0, 1.0 / hz) }
for i in 0 .. n { char_update(character_st, 0.0, 1.0, run, false, 0.0, 0.0, 1.0 / hz) }
}
# yaw 0 faces -z; the heading of a walk toward +x is -pi/2
const EAST: float = -1.5707963
const SOUTH: float = 3.14159265
test "a 45 cm deck is a step at 60 Hz and at 240" (base_st: mut BaseState, character_st: mut CharacterState) {
fresh(base_st, character_st, 1.0, 0.0, EAST)
walk(base_st, character_st, 2.0, 60.0, false)
test "a 45 cm deck is a step at 60 Hz and at 240" (character_st: mut CharacterState) {
fresh(character_st, 1.0, 0.0, EAST)
walk(character_st, 2.0, 60.0, false)
expect(body_x(character_st) > 5.0)
expect_near(body_y(character_st), 0.45, 0.01)
fresh(base_st, character_st, 1.0, 0.0, EAST)
walk(base_st, character_st, 2.0, 240.0, false)
fresh(character_st, 1.0, 0.0, EAST)
walk(character_st, 2.0, 240.0, false)
expect(body_x(character_st) > 5.0)
expect_near(body_y(character_st), 0.45, 0.01)
expect_eq(count(facts(base_st, character_st), CHAR_TOO_STEEP), 0)
expect_eq(count(facts(character_st), CHAR_TOO_STEEP), 0)
}
test "a cliff is refused at the same place whatever the frame rate, and said once" (base_st: mut BaseState, character_st: mut CharacterState) {
fresh(base_st, character_st, 0.0, -8.0, 0.0)
walk(base_st, character_st, 3.0, 30.0, false)
test "a cliff is refused at the same place whatever the frame rate, and said once" (character_st: mut CharacterState) {
fresh(character_st, 0.0, -8.0, 0.0)
walk(character_st, 3.0, 30.0, false)
let at30 = body_z(character_st)
expect(body_steep(character_st) > 0.0)
expect_eq(count(facts(base_st, character_st), CHAR_TOO_STEEP), 1)
fresh(base_st, character_st, 0.0, -8.0, 0.0)
walk(base_st, character_st, 3.0, 240.0, false)
expect_eq(count(facts(character_st), CHAR_TOO_STEEP), 1)
fresh(character_st, 0.0, -8.0, 0.0)
walk(character_st, 3.0, 240.0, false)
expect_near(body_z(character_st), at30, 0.25)
expect(body_z(character_st) > -10.0)
expect(body_y(character_st) < 0.3)
}
test "a gentle rise is walked up" (base_st: mut BaseState, character_st: mut CharacterState) {
fresh(base_st, character_st, 0.0, 8.0, SOUTH)
walk(base_st, character_st, 3.0, 60.0, false)
test "a gentle rise is walked up" (character_st: mut CharacterState) {
fresh(character_st, 0.0, 8.0, SOUTH)
walk(character_st, 3.0, 60.0, false)
expect(body_z(character_st) > 14.0)
expect(body_y(character_st) > 1.0)
}
test "the boots set the limit: a rise they cannot take is refused" (base_st: mut BaseState, character_st: mut CharacterState) {
fresh(base_st, character_st, 0.0, 8.0, SOUTH)
walk(base_st, character_st, 0.2, 60.0, false)
test "the boots set the limit: a rise they cannot take is refused" (character_st: mut CharacterState) {
fresh(character_st, 0.0, 8.0, SOUTH)
walk(character_st, 0.2, 60.0, false)
expect(not char_slope_blocks(character_st, body_y(character_st) + 0.1, 0.0, 1.0))
}
test "the walk reaches the walk speed, the run the run speed, a sneak its own" (base_st: mut BaseState, character_st: mut CharacterState, character_test_st: mut CharacterTestState) {
fresh(base_st, character_st, 0.0, 0.0, SOUTH)
walk(base_st, character_st, 2.0, 60.0, false)
test "the walk reaches the walk speed, the run the run speed, a sneak its own" (character_st: mut CharacterState, character_test_st: mut CharacterTestState) {
fresh(character_st, 0.0, 0.0, SOUTH)
walk(character_st, 2.0, 60.0, false)
expect_near(body_speed(character_st), 3.2, 0.05)
fresh(base_st, character_st, 0.0, 0.0, SOUTH)
walk(base_st, character_st, 2.0, 60.0, true)
fresh(character_st, 0.0, 0.0, SOUTH)
walk(character_st, 2.0, 60.0, true)
expect_near(body_speed(character_st), 6.8, 0.05)
character_test_st.pace = 0.5
fresh(base_st, character_st, 0.0, 0.0, SOUTH)
walk(base_st, character_st, 2.0, 60.0, false)
fresh(character_st, 0.0, 0.0, SOUTH)
walk(character_st, 2.0, 60.0, false)
expect_near(body_speed(character_st), 1.6, 0.05)
character_test_st.pace = 1.0
character_test_st.sneaking = true
fresh(base_st, character_st, 0.0, 0.0, SOUTH)
walk(base_st, character_st, 2.0, 60.0, true)
fresh(character_st, 0.0, 0.0, SOUTH)
walk(character_st, 2.0, 60.0, true)
expect_near(body_speed(character_st), 1.3, 0.05)
}
test "a boulder is a wall to a body below its step and a step onto its top" (base_st: mut BaseState, character_st: mut CharacterState) {
fresh(base_st, character_st, 0.0, 3.0, SOUTH)
walk(base_st, character_st, 2.0, 60.0, false)
test "a boulder is a wall to a body below its step and a step onto its top" (character_st: mut CharacterState) {
fresh(character_st, 0.0, 3.0, SOUTH)
walk(character_st, 2.0, 60.0, false)
expect(body_z(character_st) < 5.1)
expect_near(char_stand_at(0.0, 6.0, 0.0), 0.4, 0.001)
expect_near(char_stand_at(3.0, 6.0, 0.0), 0.0, 0.001)
@ -151,48 +151,48 @@ program CharacterTest {
expect_near(fk_height(0.0, 6.0), 0.0, 0.001)
}
test "wading shelves into a swim, and the shallows give the ground back" (base_st: mut BaseState, character_st: mut CharacterState) {
fresh(base_st, character_st, 18.0, 0.0, EAST)
walk(base_st, character_st, 3.0, 60.0, false)
let fs = facts(base_st, character_st)
test "wading shelves into a swim, and the shallows give the ground back" (character_st: mut CharacterState) {
fresh(character_st, 18.0, 0.0, EAST)
walk(character_st, 3.0, 60.0, false)
let fs = facts(character_st)
expect(body_swimming(character_st))
expect_eq(count(fs, CHAR_STARTED_SWIMMING), 1)
expect_near(body_y(character_st), -0.30, 0.06)
expect(body_breath(character_st) > 40.0)
char_face(character_st, -EAST)
char_look(character_st, -EAST, 0.0)
walk(base_st, character_st, 8.0, 60.0, false)
walk(character_st, 8.0, 60.0, false)
expect(not body_swimming(character_st))
expect_eq(count(facts(base_st, character_st), CHAR_LEFT_WATER), 1)
expect_eq(count(facts(character_st), CHAR_LEFT_WATER), 1)
}
test "the jump held dives, the air runs out, and the body surfaces" (base_st: mut BaseState, character_st: mut CharacterState) {
fresh(base_st, character_st, 30.0, 0.0, 0.0)
test "the jump held dives, the air runs out, and the body surfaces" (character_st: mut CharacterState) {
fresh(character_st, 30.0, 0.0, 0.0)
char_resume_swim(character_st, false, 0.0, 2.0)
for i in 0 .. 600 { char_update(base_st, character_st, 0.0, 0.0, false, true, 0.0, 0.0, 1.0 / 60.0) }
let fs = facts(base_st, character_st)
for i in 0 .. 600 { char_update(character_st, 0.0, 0.0, false, true, 0.0, 0.0, 1.0 / 60.0) }
let fs = facts(character_st)
expect(count(fs, CHAR_OUT_OF_AIR) > 0)
for i in 0 .. 300 { char_update(base_st, character_st, 0.0, 0.0, false, false, 0.0, 0.0, 1.0 / 60.0) }
for i in 0 .. 300 { char_update(character_st, 0.0, 0.0, false, false, 0.0, 0.0, 1.0 / 60.0) }
expect(body_depth(character_st) < 0.05)
expect(body_breath(character_st) > 10.0)
}
test "a jump leaves the ground, falls, lands, and says both" (base_st: mut BaseState, character_st: mut CharacterState) {
fresh(base_st, character_st, 0.0, 0.0, 0.0)
char_update(base_st, character_st, 0.0, 0.0, false, true, 0.0, 0.0, 1.0 / 60.0)
test "a jump leaves the ground, falls, lands, and says both" (character_st: mut CharacterState) {
fresh(character_st, 0.0, 0.0, 0.0)
char_update(character_st, 0.0, 0.0, false, true, 0.0, 0.0, 1.0 / 60.0)
expect(body_airborne(character_st))
var guard = 0
while body_airborne(character_st) and guard < 200 {
char_update(base_st, character_st, 0.0, 0.0, false, false, 0.0, 0.0, 1.0 / 60.0)
char_update(character_st, 0.0, 0.0, false, false, 0.0, 0.0, 1.0 / 60.0)
guard += 1
}
let fs = facts(base_st, character_st)
let fs = facts(character_st)
expect(not body_airborne(character_st))
expect_eq(count(fs, CHAR_JUMPED), 1)
expect_eq(count(fs, CHAR_LANDED), 1)
expect(guard > 30)
char_allow_run(character_st, false)
char_update(base_st, character_st, 0.0, 0.0, false, true, 0.0, 0.0, 1.0 / 60.0)
char_update(character_st, 0.0, 0.0, false, true, 0.0, 0.0, 1.0 / 60.0)
expect(not body_airborne(character_st))
}
@ -207,47 +207,47 @@ program CharacterTest {
expect_near(body_safe_x(character_st), 15.0, 0.001)
}
test "sitting where you stand, and the refusals" (base_st: mut BaseState, character_st: mut CharacterState, character_test_st: mut CharacterTestState) {
fresh(base_st, character_st, 0.0, 0.0, 0.0)
test "sitting where you stand, and the refusals" (character_st: mut CharacterState, character_test_st: mut CharacterTestState) {
fresh(character_st, 0.0, 0.0, 0.0)
expect_eq(char_sit_here(character_st), CHAR_SAT)
for i in 0 .. 30 { char_update(base_st, character_st, 0.0, 0.0, false, false, 0.0, 0.0, 1.0 / 60.0) }
for i in 0 .. 30 { char_update(character_st, 0.0, 0.0, false, false, 0.0, 0.0, 1.0 / 60.0) }
expect(body_sitting(character_st))
expect_near(body_sit_t(character_st), 1.0, 0.01)
expect_eq(char_sit_here(character_st), CHAR_STOOD)
expect(not body_sitting(character_st))
fresh(base_st, character_st, 0.0, -14.0, 0.0)
fresh(character_st, 0.0, -14.0, 0.0)
expect_eq(char_sit_here(character_st), CHAR_NOT_STEEP)
fresh(base_st, character_st, 24.0, 0.0, 0.0)
fresh(character_st, 24.0, 0.0, 0.0)
char_resume_swim(character_st, false, 0.0, 10.0)
expect_eq(char_sit_here(character_st), CHAR_NOT_WET)
character_test_st.riding = true
fresh(base_st, character_st, 0.0, 0.0, 0.0)
fresh(character_st, 0.0, 0.0, 0.0)
expect_eq(char_sit_here(character_st), CHAR_NOT_RIDING)
character_test_st.riding = false
}
test "a hold stops the stick, and only its own reason lets it go" (base_st: mut BaseState, character_st: mut CharacterState, character_test_st: mut CharacterTestState) {
fresh(base_st, character_st, 0.0, 0.0, 0.0)
test "a hold stops the stick, and only its own reason lets it go" (character_st: mut CharacterState, character_test_st: mut CharacterTestState) {
fresh(character_st, 0.0, 0.0, 0.0)
character_test_st.mz = 1.0
char_hold(character_st, CHAR_HOLD_STAGE)
char_hold(character_st, CHAR_HOLD_SCREEN)
for i in 0 .. 60 { char_tick(base_st, character_st, 1.0 / 60.0) }
for i in 0 .. 60 { char_tick(character_st, 1.0 / 60.0) }
expect_near(body_z(character_st), 0.0, 0.001)
char_release(character_st, CHAR_HOLD_SCREEN)
char_release(character_st, CHAR_HOLD_SCREEN)
expect(body_held(character_st))
char_release(character_st, CHAR_HOLD_STAGE)
for i in 0 .. 60 { char_tick(base_st, character_st, 1.0 / 60.0) }
for i in 0 .. 60 { char_tick(character_st, 1.0 / 60.0) }
expect(body_z(character_st) < -1.0)
character_test_st.mz = 0.0
}
test "riding hands the stick over and leaves the body where the ride puts it" (base_st: mut BaseState, character_st: mut CharacterState, character_test_st: mut CharacterTestState) {
fresh(base_st, character_st, 0.0, 0.0, 0.0)
test "riding hands the stick over and leaves the body where the ride puts it" (character_st: mut CharacterState, character_test_st: mut CharacterTestState) {
fresh(character_st, 0.0, 0.0, 0.0)
character_test_st.riding = true
character_test_st.rides = 0
char_board(character_st, 3.0, 3.0, 0.0)
for i in 0 .. 10 { char_update(base_st, character_st, 0.0, 1.0, false, false, 0.0, 0.0, 1.0 / 60.0) }
for i in 0 .. 10 { char_update(character_st, 0.0, 1.0, false, false, 0.0, 0.0, 1.0 / 60.0) }
expect_eq(character_test_st.rides, 10)
char_ride_at(character_st, 4.0, 1.2, 3.0, 0.0, 2.0)
expect_near(body_x(character_st), 4.0, 0.001)
@ -257,8 +257,8 @@ program CharacterTest {
expect_near(body_y(character_st), 0.45, 0.001)
}
test "the orbit sits behind and above the ground; the eyes at head height, refused when sat" (base_st: mut BaseState, character_st: mut CharacterState) {
fresh(base_st, character_st, 0.0, 0.0, 0.0)
test "the orbit sits behind and above the ground; the eyes at head height, refused when sat" (character_st: mut CharacterState) {
fresh(character_st, 0.0, 0.0, 0.0)
char_camera(character_st, 1.0)
expect(body_eye_z(character_st) > 1.0)
expect(body_eye_y(character_st) > 0.55)

View file

@ -1,5 +1,5 @@
# update.ludic - a frame of the body: the stick in the camera's frame, the step, the gait, the camera
export function char_update(base_st: mut BaseState, character_st: mut CharacterState, ix0: float, iz0: float, run0: bool, jump0: bool, dyaw: float, dpitch: float, dt: float) -> void {
export function char_update(character_st: mut CharacterState, ix0: float, iz0: float, run0: bool, jump0: bool, dyaw: float, dpitch: float, dt: float) -> void {
var run = run0 and character_st.chr_can_run
var ix = ix0
var iz = iz0
@ -32,8 +32,8 @@ export function char_update(base_st: mut BaseState, character_st: mut CharacterS
mag = 1.0
}
chr_steer(character_st, dx, dz, mag, run, dt)
let dist = chr_travel(base_st, character_st, dx, dz, -Math.sin(character_st.chr_yaw), -Math.cos(character_st.chr_yaw), jump, dt)
chr_gait(base_st, character_st, dist, dyaw, dpitch, dt)
let dist = chr_travel(character_st, dx, dz, -Math.sin(character_st.chr_yaw), -Math.cos(character_st.chr_yaw), jump, dt)
chr_gait(character_st, dist, dyaw, dpitch, dt)
char_camera(character_st, dt)
}
@ -47,7 +47,7 @@ function chr_ride_update(character_st: mut CharacterState, ix: float, iz: float,
}
# a frame from the input port: the game's devices, read once, stepping the body
export function char_tick(base_st: mut BaseState, character_st: mut CharacterState, dt: float) -> void {
export function char_tick(character_st: mut CharacterState, dt: float) -> void {
var ix = 0.0
var iz = 0.0
var run = false
@ -64,5 +64,5 @@ export function char_tick(base_st: mut BaseState, character_st: mut CharacterSta
let wheel = CharacterInput.zoom()
if wheel != 0.0 { character_st.chr_cam_dist = Math.clamp(character_st.chr_cam_dist - wheel * 0.35, 1.8, 9.0) }
}
char_update(base_st, character_st, ix, iz, run, jump, dyaw, dpitch, dt)
char_update(character_st, ix, iz, run, jump, dyaw, dpitch, dt)
}

View file

@ -1,15 +1,15 @@
# walk.ludic - the step itself: swimming, in the air, or on the ground; the distance, for the gait
function chr_travel(base_st: mut BaseState, character_st: mut CharacterState, dx: float, dz: float, fx: float, fz: float, jump: bool, dt: float) -> float {
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(base_st, character_st, dx, dz, jump, dt)
chr_swim_tick(character_st, dx, dz, jump, dt)
return character_st.chr_speed * dt
}
if character_st.chr_air { return chr_fly(base_st, character_st, dx, dz, dt) }
return chr_walk(base_st, character_st, fx, fz, jump, dt)
if character_st.chr_air { return chr_fly(character_st, dx, dz, dt) }
return chr_walk(character_st, fx, fz, jump, dt)
}
# ballistic: gravity, the takeoff's velocity carried, a little steering
function chr_fly(base_st: mut BaseState, character_st: mut CharacterState, dx: float, dz: float, dt: float) -> float {
function chr_fly(character_st: mut CharacterState, dx: float, dz: float, dt: float) -> float {
character_st.chr_vy = character_st.chr_vy - 14.0 * dt
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)
@ -23,32 +23,32 @@ function chr_fly(base_st: mut BaseState, character_st: mut CharacterState, dx: f
character_st.chr_air = false
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(base_st, character_st, CHAR_LANDED, character_st.chr_speed)
chr_say(character_st, CHAR_LANDED, character_st.chr_speed)
}
return character_st.chr_speed * (dt * 0.4)
}
# on the ground: a step, unless a slope refuses it or the water takes the body; then a jump
function chr_walk(base_st: mut BaseState, character_st: mut CharacterState, fx: float, fz: float, jump: bool, dt: float) -> float {
function chr_walk(character_st: mut CharacterState, fx: float, fz: float, jump: bool, dt: float) -> float {
var dist = character_st.chr_speed * dt
if dist > 0.0 { dist = chr_walk_step(base_st, character_st, fx, fz, dist) } else { character_st.chr_y = CharacterGround.height(character_st.chr_x, character_st.chr_z) }
if dist > 0.0 { dist = chr_walk_step(character_st, fx, fz, dist) } else { character_st.chr_y = CharacterGround.height(character_st.chr_x, character_st.chr_z) }
if jump and character_st.chr_can_run and character_st.chr_land < 0.05 {
character_st.chr_air = true
character_st.chr_vy = 5.0
character_st.chr_air_vx = fx * character_st.chr_speed
character_st.chr_air_vz = fz * character_st.chr_speed
chr_say(base_st, character_st, CHAR_JUMPED, 0.0)
chr_say(character_st, CHAR_JUMPED, 0.0)
}
return dist
}
function chr_walk_step(base_st: mut BaseState, character_st: mut CharacterState, fx: float, fz: float, dist: float) -> float {
function chr_walk_step(character_st: mut CharacterState, fx: float, fz: float, dist: float) -> float {
chr_push(character_st, character_st.chr_x + fx * dist, character_st.chr_z + fz * dist, 0.35, character_st.chr_y, character_st.chr_y + CHAR_BODY)
let nx = character_st.chr_out_x
let nz = character_st.chr_out_z
let nh = char_stand_at(nx, nz, character_st.chr_y)
if char_slope_blocks(character_st, nh, fx, fz) {
if not (character_st.chr_steep > 0.0) { chr_say(base_st, character_st, CHAR_TOO_STEEP, nh - character_st.chr_y) }
if not (character_st.chr_steep > 0.0) { chr_say(character_st, CHAR_TOO_STEEP, nh - character_st.chr_y) }
character_st.chr_steep = 1.2
character_st.chr_speed = 0.0
return 0.0
@ -56,7 +56,7 @@ function chr_walk_step(base_st: mut BaseState, character_st: mut CharacterState,
# the lake shelves: shin-deep is a wade, and past CHAR_WADE_MAX it is a swim. Entering owns
# the position and the height this frame, or the body sits on the bed for one frame.
if nh < CharacterGround.water(nx, nz) - CHAR_WADE_MAX {
chr_enter_water(base_st, character_st, nx, nz)
chr_enter_water(character_st, nx, nz)
return dist
}
let mx = nx - character_st.chr_x

View file

@ -1,7 +1,7 @@
# water.ludic - one axis decides it: how far the bed is below the surface where the body stands.
# Under CHAR_WADE_MAX the walk wades; past it the feet leave the bottom. Coming out wants a
# shallower bed (CHAR_STAND_UP) than going in did, so a shelving shore does not flicker.
function chr_enter_water(base_st: mut BaseState, character_st: mut CharacterState, x: float, z: float) -> void {
function chr_enter_water(character_st: mut CharacterState, x: float, z: float) -> void {
if character_st.chr_swim { return }
character_st.chr_swim = true
character_st.chr_dive = false
@ -15,17 +15,17 @@ function chr_enter_water(base_st: mut BaseState, character_st: mut CharacterStat
character_st.chr_z = z
character_st.chr_water = CharacterGround.water(x, z)
character_st.chr_y = character_st.chr_water - 0.30
chr_say(base_st, character_st, CHAR_STARTED_SWIMMING, 0.0)
chr_say(character_st, CHAR_STARTED_SWIMMING, 0.0)
}
function chr_leave_water(base_st: mut BaseState, character_st: mut CharacterState) -> void {
function chr_leave_water(character_st: mut CharacterState) -> void {
if not character_st.chr_swim { return }
character_st.chr_swim = false
character_st.chr_dive = false
character_st.chr_depth = 0.0
character_st.chr_y = CharacterGround.height(character_st.chr_x, character_st.chr_z)
character_st.chr_speed = Math.min(character_st.chr_speed, 1.0)
chr_say(base_st, character_st, CHAR_LEFT_WATER, 0.0)
chr_say(character_st, CHAR_LEFT_WATER, 0.0)
}
# the descent: the jump held pushes the body under, letting go floats it back up
@ -42,7 +42,7 @@ function chr_dive_tick(character_st: mut CharacterState, jump: bool, dt: float)
}
# air is spent only under the surface, and comes back fast at the top
function chr_breath_tick(base_st: mut BaseState, character_st: mut CharacterState, dt: float) -> void {
function chr_breath_tick(character_st: mut CharacterState, dt: float) -> void {
if not (character_st.chr_depth > 0.55) {
character_st.chr_breath = Math.min(character_st.chr_breath + dt * 12.0, body_breath_max())
return
@ -52,13 +52,13 @@ function chr_breath_tick(base_st: mut BaseState, character_st: mut CharacterStat
character_st.chr_breath = 0.0
character_st.chr_dive = false
character_st.chr_depth = Math.max(character_st.chr_depth - 3.0 * dt, 0.0)
chr_say(base_st, character_st, CHAR_OUT_OF_AIR, dt)
chr_say(character_st, CHAR_OUT_OF_AIR, dt)
}
}
# The edge of the world is a current, not a wall: it takes the outward part of a stroke, so
# turning round always works. Said once every twelve seconds while it is strong.
function chr_current_tick(base_st: mut BaseState, character_st: mut CharacterState, dt: float) -> void {
function chr_current_tick(character_st: mut CharacterState, dt: float) -> void {
let fx = -Math.sin(character_st.chr_yaw)
let fz = -Math.cos(character_st.chr_yaw)
let take = Math.clamp(CharacterGround.current(character_st.chr_x, character_st.chr_z, fx, fz), 0.0, 1.0)
@ -66,7 +66,7 @@ function chr_current_tick(base_st: mut BaseState, character_st: mut CharacterSta
character_st.chr_speed = character_st.chr_speed * (1.0 - take)
if take > 0.55 and character_st.chr_edge_said < 0.0 {
character_st.chr_edge_said = 12.0
chr_say(base_st, character_st, CHAR_CURRENT, take)
chr_say(character_st, CHAR_CURRENT, take)
}
}
character_st.chr_edge_said = character_st.chr_edge_said - dt