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>
47 lines
2.3 KiB
Text
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)
|
|
}
|
|
|