ludic/packages/ludic.vehicles/horse.ludic
Orkuncakilkaya 668a9ecf69 vehicles: a kind's numbers are a VehicleSpec the game hands over (vehicles_spec), not constants
The seat height, the horse's walk, gallop, acceleration and turn (VE_HORSE_*), its stamina (a gallop
over 6 m/s spends 6 a second, a walk gives back 3, 5 needed to gallop) and hay (0.02 a metre,
VEHICLE_HAY_MIN), the boat's stroke, turn and wind (VE_ROW, VE_ROW_TURN, VE_WIND) and where a rider
gets off (the horse's 1.2 m flank, the boat's 1.25 m wade) move into VehicleSpec (spec.ludic), held
per kind in VehiclesState.ve_specs and kept across a reset. vehicles_spec(kind, spec) sets one,
vehicle_spec(kind) asks it; a kind with no spec cannot be called. VEHICLE_HORSE and VEHICLE_BOAT stay
the kinds. The tests hand Maroon Lake's numbers in, and a new one holds a spec's walk and seat_h.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:22:02 +03:00

47 lines
2.3 KiB
Text

# horse.ludic - a horse under the stick: a walk, a canter on stamina, hay for every metre; it will
# not step into water, and its legs (VehicleLegs) meet the ground and whatever stands in its way
# (its numbers are its kind's VehicleSpec: walk, run, acc, turn, the stamina and the hay)
function ve_ride_horse(vehicles_st: mut VehiclesState, v: Vehicle, ix: float, iz: float, run: bool, dt: float) -> void {
let sp = vehicle_spec(vehicles_st, v.kind)
var top = sp.walk
if run and v.fuel > sp.run_min { top = sp.run }
v.speed = v.speed + Math.clamp(iz * top - v.speed, -sp.acc * dt, sp.acc * dt)
# Steering follows SIGNED speed: backing up with the stick left swings the nose right
if Math.abs(v.speed) > 0.3 {
var rate = Math.clamp(Math.abs(v.speed) / 3.0, 0.4, 1.0)
if v.speed < 0.0 { rate = -rate }
v.yaw = v.yaw - ix * sp.turn * (dt * rate)
}
let fx = -Math.sin(v.yaw)
let fz = -Math.cos(v.yaw)
let d = v.speed * dt
let nh = VehicleWorld.ground(v.x + fx * d, v.z + fz * d)
if nh < VehicleWorld.water(v.x + fx * d, v.z + fz * d) + 0.4 {
v.speed = 0.0
return
}
ve_legs_walk(vehicles_st, v, fx * v.speed, fz * v.speed, dt)
ve_stamina(sp, v, d, dt)
}
# the legs carry it from where it is; what they covered along its heading is its speed now, never
# more than was asked, so a trunk or a rise it cannot take brings it to a stand
function ve_legs_walk(vehicles_st: mut VehiclesState, v: Vehicle, vx: float, vz: float, dt: float) -> void {
if not VehicleLegs.walk(v.nid, v.x, v.y, v.z, vx, vz, dt) { return }
let dx = VehicleLegs.x() - v.x
let dz = VehicleLegs.z() - v.z
v.x = VehicleLegs.x()
v.y = VehicleLegs.y()
v.z = VehicleLegs.z()
let got = (dx * -Math.sin(v.yaw) + dz * -Math.cos(v.yaw)) / dt
if v.speed > 0.0 { v.speed = Math.clamp(got, 0.0, v.speed) } else { v.speed = Math.clamp(got, v.speed, 0.0) }
let moved = Math.sqrt(dx * dx + dz * dz)
if moved > 0.0 { ve_moved_add(vehicles_st, v, moved) }
}
# a gallop spends stamina and a walk gives it back; every metre costs hay
function ve_stamina(sp: VehicleSpec, v: Vehicle, d: float, dt: float) -> void {
if Math.abs(v.speed) > sp.tire_at { v.fuel = Math.max(v.fuel - sp.tire * dt, 0.0) } else { v.fuel = Math.min(v.fuel + sp.rest * dt, 100.0) }
v.food = Math.max(v.food - Math.abs(d) * sp.hay_per_m, 0.0)
}