ludic/packages/ludic.vehicles/ride.ludic
Orkuncakilkaya 231715bf98 fix(leaks): what play allocated every frame and never freed - a shadow uniform's name, the Tick, a ridden vehicle's moves
Found with malloc_history over 1200 frames of play without the interface (the first Ludic function
on each allocating stack, live bytes at two marks): sh_loc built `name + "[0]"` per program per pass
per frame - 24.6 MB of 33 in the window; its callers pass both names as literals now. tick_set fills
the frame loop's one Tick instead of tick_new making one a frame. A ridden boat or horse said
VEHICLE_MOVED as a new fact every frame; the metres are added up in VehiclesState and taken once
(vehicle_moved_take / _kind / _x / _z). Play's growth without the interface: 92 -> 14.7 MB a minute
(maroon-lake tests/leakcheck.sh); what is left is phys_push (Physics' fix on lang/nav) and the HUD's
strings. Packages 407, ludic-dev test 305 pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 23:51:37 +03:00

69 lines
2.8 KiB
Text

# ride.ludic - the rider's stick steers what they are on: a horse walks, canters on stamina and
# stops at water, a slope or a trunk; a boat is rowed - a stroke and a turn handed to its hull,
# which the water floats, the wind drifts and the lake bed stops
const VE_ROW: float = 630.0 # N: a full stroke forward, about 3 m/s on Maroon Lake's hull
const VE_ROW_TURN: float = 270.0 # N.m: the oars pulled against each other, about 1.1 rad/s
const VE_WIND: float = 70.0 # N on a boat in a full wind
export function vehicle_update(vehicles_st: mut VehiclesState, ix: float, iz: float, run: bool, dt: float) -> void {
let v = vehicles_st.ve_cur
if v == null { return }
if v.kind == VEHICLE_BOAT { ve_row(vehicles_st, v, ix, iz, dt) } else { ve_ride_horse(vehicles_st, v, ix, iz, run, dt) }
VehicleWorld.placed(v)
VehicleRider.ride_at(v.x, v.y + v.seat_h, v.z, v.yaw, Math.abs(v.speed))
}
# the stick is the oars: forward and back a stroke, left and right the two pulled against each
# other; the swell at the edge takes the outward share of a stroke
function ve_row(vehicles_st: mut VehiclesState, v: Vehicle, ix: float, iz: float, dt: float) -> void {
ve_hull_read(v)
let take = ve_swell(vehicles_st, v, iz, dt)
VehicleHull.row(v.nid, iz * VE_ROW * (1.0 - take), -ix * VE_ROW_TURN)
let d = Math.abs(v.speed) * dt
if d > 0.0 { ve_moved_add(vehicles_st, v, d) }
}
# where the hull is now, and how fast it goes along its bow
function ve_hull_read(v: Vehicle) -> void {
if not VehicleHull.read(v.nid) { return }
v.x = VehicleHull.x()
v.y = VehicleHull.y()
v.z = VehicleHull.z()
v.yaw = VehicleHull.yaw()
v.speed = VehicleHull.speed()
}
# a boat gets a longer leash than a swimmer and the same current at the end of it: the outward
# part of the stroke only, so a boat at the limit can always be put about. The share it takes.
function ve_swell(vehicles_st: mut VehiclesState, v: Vehicle, iz: float, dt: float) -> float {
var fx = -Math.sin(v.yaw)
var fz = -Math.cos(v.yaw)
if iz < 0.0 {
fx = -fx
fz = -fz
}
let take = Math.clamp(VehicleWorld.swell(v.x, v.z, fx, fz), 0.0, 1.0)
if take > 0.55 and vehicles_st.ve_swell_said < 0.0 {
vehicles_st.ve_swell_said = 14.0
ve_say(vehicles_st, VEHICLE_SWELL, v, take)
}
vehicles_st.ve_swell_said = vehicles_st.ve_swell_said - dt
return take
}
# every boat: its hull read back, and the wind pushing it across the lake, ridden or not
export function vehicles_tick(vehicles_st: VehiclesState, dt: float) -> void {
let l = ve_all(vehicles_st)
let w = VehicleWorld.wind() * VE_WIND
let wy = VehicleWorld.wind_yaw()
for i in 0 .. len(l) {
let v = l[i]
if v.kind == VEHICLE_BOAT {
if w > 0.0 { VehicleHull.drift(v.nid, -Math.sin(wy) * w, -Math.cos(wy) * w) }
if v.rider < 0 {
ve_hull_read(v)
VehicleWorld.placed(v)
}
}
}
}