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>
466 lines
21 KiB
Text
466 lines
21 KiB
Text
# vehicles_test.ludic - a fake shore: land at x < 10, a lake past it deepening a metre a metre, a
|
|
# pier head at the shore facing out and a trough on the meadow, a trunk in a horse's way. Calling to
|
|
# berths and bays, the owner and the rider, mounting a hungry horse, riding both, the swell, the wind,
|
|
# getting off near a shore and far from one, a player leaving, and the save. The boat's hull and the
|
|
# horse's legs are real ludic.physics bodies on the same ground, the trunk a post in it.
|
|
import "ludic.vehicles"
|
|
import "ludic.base"
|
|
import "ludic.physics"
|
|
program VehiclesTest {
|
|
numbers float
|
|
function pose(physics_st: mut PhysicsState, id: int) -> PhysPose {
|
|
let p = new PhysPose
|
|
if not phys_pose(physics_st, id, p) { return null }
|
|
return p
|
|
}
|
|
function wpose(physics_st: mut PhysicsState, id: int) -> PhysWalk {
|
|
let p = new PhysWalk
|
|
if not phys_walker_pose(physics_st, id, p) { return null }
|
|
return p
|
|
}
|
|
|
|
function fk_ground(x: float, z: float) -> float {
|
|
if x > 10.0 { return -(x - 10.0) }
|
|
return 0.0
|
|
}
|
|
function fk_water(x: float, z: float) -> float {
|
|
if x > 9.5 { return 0.0 }
|
|
return -10000.0
|
|
}
|
|
state VehiclesTestState {
|
|
swell: float = 0.0
|
|
wind: float = 0.0
|
|
made: int = 0
|
|
gone: int = 0
|
|
placed: int = 0
|
|
rx: float = 0.0
|
|
ry: float = 0.0
|
|
rz: float = 0.0
|
|
boarded: int = 0
|
|
sx: float = 0.0
|
|
sz: float = 0.0
|
|
hull_nid: int = -1
|
|
hull: int = -1
|
|
hx: float = 0.0
|
|
hy: float = 0.0
|
|
hz: float = 0.0
|
|
hyaw: float = 0.0
|
|
hspeed: float = 0.0
|
|
hulls: int = 0
|
|
legs: int = -1
|
|
lx: float = 0.0
|
|
ly: float = 0.0
|
|
lz: float = 0.0
|
|
stood: int = 0
|
|
}
|
|
function fk_swell(vehicles_test_st: VehiclesTestState, x: float, z: float, fx: float, fz: float) -> float {
|
|
if fx > 0.0 { return vehicles_test_st.swell }
|
|
return 0.0
|
|
}
|
|
function fk_wind(vehicles_test_st: VehiclesTestState) -> float { return vehicles_test_st.wind }
|
|
function fk_made(vehicles_test_st: mut VehiclesTestState, v: Vehicle) -> void { vehicles_test_st.made += 1 }
|
|
function fk_gone(vehicles_test_st: mut VehiclesTestState, v: Vehicle) -> void { vehicles_test_st.gone += 1 }
|
|
function fk_placed(vehicles_test_st: mut VehiclesTestState, v: Vehicle) -> void { vehicles_test_st.placed += 1 }
|
|
|
|
# the rider: where it was put, and a safe spot that walks back toward the shore
|
|
function fk_board(vehicles_test_st: mut VehiclesTestState, x: float, z: float, yaw: float) -> void { vehicles_test_st.boarded += 1 }
|
|
function fk_ride(vehicles_test_st: mut VehiclesTestState, x: float, y: float, z: float, yaw: float, speed: float) -> void {
|
|
vehicles_test_st.rx = x
|
|
vehicles_test_st.ry = y
|
|
vehicles_test_st.rz = z
|
|
}
|
|
function fk_off(vehicles_test_st: mut VehiclesTestState, x: float, z: float) -> void {
|
|
vehicles_test_st.rx = x
|
|
vehicles_test_st.rz = z
|
|
vehicles_test_st.ry = fk_ground(x, z)
|
|
}
|
|
function fk_safe(vehicles_test_st: mut VehiclesTestState, x: float, z: float, from_y: float, max_depth: float) -> bool {
|
|
for s in 0 .. 11 {
|
|
let px = x - float(s)
|
|
let depth = fk_water(px, z) - fk_ground(px, z)
|
|
if not (depth > max_depth) {
|
|
vehicles_test_st.sx = px
|
|
vehicles_test_st.sz = z
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
function fk_sx(vehicles_test_st: VehiclesTestState) -> float { return vehicles_test_st.sx }
|
|
function fk_sz(vehicles_test_st: VehiclesTestState) -> float { return vehicles_test_st.sz }
|
|
|
|
# the hull: one boat's body in a Jolt lake - the shore's bed as a heightfield, the water at 0
|
|
function fk_lake(physics_st: mut PhysicsState) -> void {
|
|
if phys_is_open(physics_st) { return }
|
|
phys_open(physics_st, 64, 1)
|
|
let n = 160
|
|
let h = floats(n * n)
|
|
for j in 0 .. n { for i in 0 .. n { h[j * n + i] = fk_ground(float(i) - 20.0, float(j) - 100.0) } }
|
|
phys_ground_add(physics_st, phys_heightfield(physics_st, h, n, -20.0, -100.0, 1.0))
|
|
phys_static_add(physics_st, phys_box(physics_st, 15.0, 5.0, 1.0), 0.0, 0.0, -60.0, 0.0)
|
|
}
|
|
|
|
# the legs: the horse's own walker on that ground, made on its first step
|
|
function fk_walk(physics_st: mut PhysicsState, vehicles_test_st: mut VehiclesTestState, nid: int, x: float, y: float, z: float, vx: float, vz: float, dt: float) -> bool {
|
|
fk_lake(physics_st)
|
|
if vehicles_test_st.legs < 0 { vehicles_test_st.legs = phys_walker_add(physics_st, 0.8, 1.7, x, y, z, 40.0, 450.0) }
|
|
phys_walker_move(physics_st, vehicles_test_st.legs, x, y, z, vx, vz, 0.0, 0.45, 40.0, dt)
|
|
let p = wpose(physics_st, vehicles_test_st.legs)
|
|
vehicles_test_st.lx = p.x
|
|
vehicles_test_st.ly = p.y
|
|
vehicles_test_st.lz = p.z
|
|
return true
|
|
}
|
|
function fk_lx(vehicles_test_st: VehiclesTestState) -> float { return vehicles_test_st.lx }
|
|
function fk_ly(vehicles_test_st: VehiclesTestState) -> float { return vehicles_test_st.ly }
|
|
function fk_lz(vehicles_test_st: VehiclesTestState) -> float { return vehicles_test_st.lz }
|
|
function fk_stand(vehicles_test_st: mut VehiclesTestState, nid: int, x: float, y: float, z: float) -> void { vehicles_test_st.stood += 1 }
|
|
function fk_put(physics_st: mut PhysicsState, vehicles_test_st: mut VehiclesTestState, nid: int, x: float, y: float, z: float, yaw: float) -> void {
|
|
fk_lake(physics_st)
|
|
if vehicles_test_st.hull >= 0 and vehicles_test_st.hull_nid == nid {
|
|
phys_place(physics_st, vehicles_test_st.hull, x, y + 0.12, z, yaw)
|
|
phys_set_velocity(physics_st, vehicles_test_st.hull, 0.0, 0.0, 0.0)
|
|
return
|
|
}
|
|
vehicles_test_st.hull = phys_hull_add(physics_st, x, y + 0.12, z, yaw, 0.6, 0.25, 1.9, 180.0, 4.0, 1.0)
|
|
vehicles_test_st.hull_nid = nid
|
|
vehicles_test_st.hulls += 1
|
|
}
|
|
function fk_row(physics_st: mut PhysicsState, vehicles_test_st: VehiclesTestState, nid: int, f: float, t: float) -> void { phys_row(physics_st, vehicles_test_st.hull, f, t) }
|
|
function fk_drift(physics_st: mut PhysicsState, vehicles_test_st: VehiclesTestState, nid: int, fx: float, fz: float) -> void { phys_force(physics_st, vehicles_test_st.hull, fx, 0.0, fz) }
|
|
function fk_read(physics_st: mut PhysicsState, vehicles_test_st: mut VehiclesTestState, nid: int) -> bool {
|
|
if vehicles_test_st.hull < 0 or vehicles_test_st.hull_nid != nid { return false }
|
|
let p = pose(physics_st, vehicles_test_st.hull)
|
|
if p == null { return false }
|
|
vehicles_test_st.hx = p.x
|
|
vehicles_test_st.hy = p.y
|
|
vehicles_test_st.hz = p.z
|
|
vehicles_test_st.hyaw = phys_yaw_of(p)
|
|
vehicles_test_st.hspeed = phys_bow_speed(p)
|
|
return true
|
|
}
|
|
function fk_hull_gone(physics_st: mut PhysicsState, vehicles_test_st: mut VehiclesTestState, nid: int) -> void {
|
|
if vehicles_test_st.hull_nid != nid { return }
|
|
phys_remove(physics_st, vehicles_test_st.hull)
|
|
vehicles_test_st.hull = -1
|
|
}
|
|
function fk_hx(vehicles_test_st: VehiclesTestState) -> float { return vehicles_test_st.hx }
|
|
function fk_hy(vehicles_test_st: VehiclesTestState) -> float { return vehicles_test_st.hy }
|
|
function fk_hz(vehicles_test_st: VehiclesTestState) -> float { return vehicles_test_st.hz }
|
|
function fk_hyaw(vehicles_test_st: VehiclesTestState) -> float { return vehicles_test_st.hyaw }
|
|
function fk_hspeed(vehicles_test_st: VehiclesTestState) -> float { return vehicles_test_st.hspeed }
|
|
bind VehicleHull { put: fn fk_put, row: fn fk_row, drift: fn fk_drift, read: fn fk_read, x: fn fk_hx, y: fn fk_hy, z: fn fk_hz, yaw: fn fk_hyaw, speed: fn fk_hspeed, gone: fn fk_hull_gone }
|
|
bind PhysWater { height: fn fk_water }
|
|
bind VehicleLegs { walk: fn fk_walk, x: fn fk_lx, y: fn fk_ly, z: fn fk_lz, stand: fn fk_stand }
|
|
|
|
bind VehicleWorld { ground: fn fk_ground, water: fn fk_water, swell: fn fk_swell, wind: fn fk_wind, made: fn fk_made, gone: fn fk_gone, placed: fn fk_placed }
|
|
bind VehicleRider { board: fn fk_board, ride_at: fn fk_ride, step_off: fn fk_off, safe_spot: fn fk_safe, safe_x: fn fk_sx, safe_z: fn fk_sz }
|
|
|
|
const EAST: float = -1.5707963
|
|
|
|
# Maroon Lake's numbers for its two kinds (its vehicle_kinds.lres), as a game hands them in
|
|
function horse_spec() -> VehicleSpec {
|
|
let s = new VehicleSpec
|
|
s.seat_h = 1.35
|
|
s.walk = 4.5
|
|
s.run = 11.0
|
|
s.acc = 4.0
|
|
s.turn = 1.6
|
|
s.hay_per_m = 0.02
|
|
s.hay_min = 10.0
|
|
s.run_min = 5.0
|
|
s.tire_at = 6.0
|
|
s.tire = 6.0
|
|
s.rest = 3.0
|
|
s.off_side = 1.2
|
|
return s
|
|
}
|
|
function boat_spec() -> VehicleSpec {
|
|
let s = new VehicleSpec
|
|
s.seat_h = 0.30
|
|
s.row = 630.0
|
|
s.row_turn = 270.0
|
|
s.wind = 70.0
|
|
s.off_depth = 1.25
|
|
return s
|
|
}
|
|
|
|
function fresh(vehicles_st: mut VehiclesState, vehicles_test_st: mut VehiclesTestState) -> void {
|
|
vehicles_reset(vehicles_st)
|
|
vehicles_spec(vehicles_st, VEHICLE_HORSE, horse_spec())
|
|
vehicles_spec(vehicles_st, VEHICLE_BOAT, boat_spec())
|
|
vehicles_dock_at(vehicles_st, 10.0, 0.0, EAST)
|
|
vehicles_rail_at(vehicles_st, 0.0, 5.0, 0.0)
|
|
vehicles_test_st.made = 0
|
|
vehicles_test_st.gone = 0
|
|
vehicles_test_st.swell = 0.0
|
|
vehicles_test_st.wind = 0.0
|
|
}
|
|
function facts(vehicles_st: VehiclesState) -> []VehicleFact { return q_drain(vehicle_facts(vehicles_st)) }
|
|
function count(fs: []VehicleFact, what: int) -> int {
|
|
var n = 0
|
|
for i in 0 .. len(fs) { if fs[i].what == what { n += 1 } }
|
|
return n
|
|
}
|
|
function ride(physics_st: mut PhysicsState, vehicles_st: mut VehiclesState, ix: float, iz: float, run: bool, secs: float) -> void {
|
|
for i in 0 .. int(secs * 60.0) {
|
|
vehicle_update(vehicles_st, ix, iz, run, 1.0 / 60.0)
|
|
vehicles_tick(vehicles_st, 1.0 / 60.0)
|
|
phys_step(physics_st)
|
|
}
|
|
}
|
|
|
|
test "a berth is off the pier head in the water, a bay on the meadow" (vehicles_st: mut VehiclesState, vehicles_test_st: mut VehiclesTestState) {
|
|
fresh(vehicles_st, vehicles_test_st)
|
|
expect_near(vehicle_berth_x(vehicles_st, 0), 17.25, 0.01)
|
|
expect_near(vehicle_berth_z(vehicles_st, 0), -3.3, 0.01)
|
|
expect_near(vehicle_post_x(vehicles_st, 3), 16.15, 0.01)
|
|
expect_near(vehicle_bay_z(vehicles_st, 2), 3.6, 0.01)
|
|
}
|
|
|
|
test "a kind with no numbers cannot be called; its spec's numbers are the horse's pace and its seat" (physics_st: mut PhysicsState, vehicles_st: mut VehiclesState, vehicles_test_st: mut VehiclesTestState) {
|
|
fresh(vehicles_st, vehicles_test_st)
|
|
vehicles_spec(vehicles_st, VEHICLE_BOAT, null)
|
|
expect(not vehicle_call_for(vehicles_st, 0, VEHICLE_BOAT, 0))
|
|
let slow = horse_spec()
|
|
slow.walk = 2.5
|
|
slow.seat_h = 1.1
|
|
vehicles_spec(vehicles_st, VEHICLE_HORSE, slow)
|
|
expect(vehicle_spec(vehicles_st, VEHICLE_HORSE) == slow)
|
|
vehicle_call_for(vehicles_st, 0, VEHICLE_HORSE, 0)
|
|
let h = vehicle_find(vehicles_st, VEHICLE_HORSE)
|
|
expect_near(h.seat_h, 1.1, 0.001)
|
|
vehicle_mount(vehicles_st, h)
|
|
ride(physics_st, vehicles_st, 0.0, 1.0, false, 3.0)
|
|
expect_near(h.speed, 2.5, 0.05)
|
|
expect_near(vehicles_test_st.ry - h.y, 1.1, 0.001)
|
|
vehicle_drop(vehicles_st)
|
|
vehicles_reset(vehicles_st)
|
|
expect(vehicle_spec(vehicles_st, VEHICLE_HORSE) == slow) # a reset keeps the numbers
|
|
}
|
|
|
|
test "the first call makes a vehicle, a later one fetches it, and a taken place refuses" (vehicles_st: mut VehiclesState, vehicles_test_st: mut VehiclesTestState) {
|
|
fresh(vehicles_st, vehicles_test_st)
|
|
expect(vehicle_call_for(vehicles_st, 0, VEHICLE_BOAT, 0))
|
|
expect_eq(vehicles_test_st.made, 1)
|
|
let b = vehicle_find(vehicles_st, VEHICLE_BOAT)
|
|
expect(b != null)
|
|
expect_near(b.y, 0.02, 0.001)
|
|
expect(vehicle_call_for(vehicles_st, 0, VEHICLE_BOAT, 0))
|
|
expect_eq(vehicles_test_st.made, 1)
|
|
expect(not vehicle_call_for(vehicles_st, 2, VEHICLE_BOAT, 0))
|
|
expect(vehicle_call_for(vehicles_st, 2, VEHICLE_BOAT, 1))
|
|
expect_eq(vehicles_test_st.made, 2)
|
|
expect(vehicle_call_for(vehicles_st, 0, VEHICLE_BOAT, 2))
|
|
expect(vehicle_in_place(vehicles_st, VEHICLE_BOAT, 0) == null)
|
|
expect_eq(vehicle_free_place(vehicles_st, VEHICLE_BOAT), 0)
|
|
expect(vehicle_mine_in_place(vehicles_st, VEHICLE_BOAT))
|
|
}
|
|
|
|
test "a player leaving takes theirs away; this machine's player never leaves" (vehicles_st: mut VehiclesState, vehicles_test_st: mut VehiclesTestState) {
|
|
fresh(vehicles_st, vehicles_test_st)
|
|
vehicle_call_for(vehicles_st, 0, VEHICLE_HORSE, 0)
|
|
vehicle_call_for(vehicles_st, 3, VEHICLE_HORSE, 1)
|
|
vehicles_release(vehicles_st, 0)
|
|
expect_eq(len(vehicles_all(vehicles_st)), 2)
|
|
vehicles_release(vehicles_st, 3)
|
|
expect_eq(len(vehicles_all(vehicles_st)), 1)
|
|
expect_eq(vehicles_test_st.gone, 1)
|
|
expect(vehicle_in_place(vehicles_st, VEHICLE_HORSE, 1) == null)
|
|
}
|
|
|
|
test "a hungry horse will not go; a fed one is mounted, and only by its owner" (vehicles_st: mut VehiclesState, vehicles_test_st: mut VehiclesTestState) {
|
|
fresh(vehicles_st, vehicles_test_st)
|
|
vehicle_call_for(vehicles_st, 0, VEHICLE_HORSE, 0)
|
|
vehicle_call_for(vehicles_st, 4, VEHICLE_HORSE, 1)
|
|
let h = vehicle_find(vehicles_st, VEHICLE_HORSE)
|
|
h.food = 5.0
|
|
expect(not vehicle_mount(vehicles_st, h))
|
|
expect_eq(count(facts(vehicles_st), VEHICLE_HUNGRY), 1)
|
|
vehicle_feed(vehicles_st, h, 50.0)
|
|
expect_near(h.food, 55.0, 0.001)
|
|
expect(not vehicle_mount(vehicles_st, vehicle_of(vehicles_st, 4, VEHICLE_HORSE)))
|
|
vehicles_test_st.boarded = 0
|
|
expect(vehicle_mount(vehicles_st, h))
|
|
expect_eq(vehicles_test_st.boarded, 1)
|
|
expect_eq(vehicle_ride_kind(vehicles_st), VEHICLE_HORSE)
|
|
expect_eq(h.rider, 0)
|
|
expect(vehicle_nearest(vehicles_st, h.x, h.z, 5.0) == null)
|
|
expect(vehicle_in_place(vehicles_st, VEHICLE_HORSE, 0) == null)
|
|
}
|
|
|
|
test "a horse walks, gallops on stamina, is pushed aside by a trunk it glances, stops at a fence and the water; called, it stands at its bay" (physics_st: mut PhysicsState, vehicles_st: mut VehiclesState, vehicles_test_st: mut VehiclesTestState) {
|
|
fresh(vehicles_st, vehicles_test_st)
|
|
vehicle_call_for(vehicles_st, 0, VEHICLE_HORSE, 0)
|
|
let h = vehicle_find(vehicles_st, VEHICLE_HORSE)
|
|
vehicle_mount(vehicles_st, h)
|
|
ride(physics_st, vehicles_st, 0.0, 1.0, false, 3.0)
|
|
expect_near(h.speed, 4.5, 0.05)
|
|
expect(vehicle_moved_take(vehicles_st) > 10.0) # 3 s at 4.5 m/s, added up
|
|
expect_eq(vehicle_moved_kind(vehicles_st), VEHICLE_HORSE)
|
|
expect_near(vehicle_moved_take(vehicles_st), 0.0, 0.0001)
|
|
ride(physics_st, vehicles_st, 0.0, 1.0, true, 3.0)
|
|
expect_near(h.speed, 11.0, 0.1)
|
|
expect(h.fuel < 100.0)
|
|
expect(h.z < -15.0)
|
|
expect_near(vehicles_test_st.rz, h.z, 0.001)
|
|
expect_near(vehicles_test_st.ry, 1.35, 0.001)
|
|
# a trunk half a metre off its line pushes it aside and lets it by; the fence at z = -60 stops it
|
|
let x0 = h.x
|
|
phys_static_add(physics_st, phys_cylinder(physics_st, 3.0, 0.3), x0 + 0.5, 0.0, -35.0, 0.0)
|
|
ride(physics_st, vehicles_st, 0.0, 1.0, true, 6.0)
|
|
expect(h.z > -59.0 - 0.85)
|
|
expect(h.z < -50.0)
|
|
expect(Math.abs(h.x - x0) > 0.2)
|
|
expect_near(h.speed, 0.0, 0.05)
|
|
# and the lake to the east does too
|
|
vehicle_drop(vehicles_st)
|
|
h.x = 7.0
|
|
h.z = 0.0
|
|
h.yaw = EAST
|
|
vehicle_mount(vehicles_st, h)
|
|
ride(physics_st, vehicles_st, 0.0, 1.0, false, 4.0)
|
|
expect(h.x < 9.6)
|
|
# a horse brought to its bay is stood there
|
|
vehicle_drop(vehicles_st)
|
|
let before = vehicles_test_st.stood
|
|
expect(vehicle_call_for(vehicles_st, 0, VEHICLE_HORSE, 1))
|
|
expect_eq(vehicles_test_st.stood, before + 1)
|
|
}
|
|
|
|
test "a boat is rowed out, loses its stroke to the swell, turns, is stopped by the shore's bed, drifts on the wind" (physics_st: mut PhysicsState, vehicles_st: mut VehiclesState, vehicles_test_st: mut VehiclesTestState) {
|
|
fresh(vehicles_st, vehicles_test_st)
|
|
vehicle_call_for(vehicles_st, 0, VEHICLE_BOAT, 0)
|
|
let b = vehicle_find(vehicles_st, VEHICLE_BOAT)
|
|
expect_eq(vehicles_test_st.hulls, 1)
|
|
vehicle_mount(vehicles_st, b)
|
|
let x0 = b.x
|
|
ride(physics_st, vehicles_st, 0.0, 1.0, false, 6.0)
|
|
let x6 = b.x
|
|
expect(x6 > x0 + 8.0)
|
|
expect(b.speed > 2.0)
|
|
expect(b.y > -0.2)
|
|
ride(physics_st, vehicles_st, 0.0, 0.0, false, 6.0)
|
|
vehicles_test_st.swell = 1.0
|
|
let xs = b.x
|
|
ride(physics_st, vehicles_st, 0.0, 1.0, false, 2.0)
|
|
expect(b.x - xs < 1.0)
|
|
expect_eq(count(facts(vehicles_st), VEHICLE_SWELL), 1)
|
|
vehicles_test_st.swell = 0.0
|
|
let yaw0 = b.yaw
|
|
ride(physics_st, vehicles_st, 1.0, 0.0, false, 2.0)
|
|
expect(Math.abs(char_turn(b.yaw - yaw0)) > 1.0)
|
|
ride(physics_st, vehicles_st, 0.0, -1.0, false, 25.0)
|
|
ride(physics_st, vehicles_st, 0.0, 0.0, false, 3.0)
|
|
expect(b.x > 9.5)
|
|
vehicles_test_st.wind = 1.0
|
|
let z0 = b.z
|
|
ride(physics_st, vehicles_st, 0.0, 0.0, false, 3.0)
|
|
expect(b.z < z0 - 0.2)
|
|
}
|
|
|
|
test "a boat nobody rows holds its berth in a wind, and one left out on the lake holds where it was left" (physics_st: mut PhysicsState, vehicles_st: mut VehiclesState, vehicles_test_st: mut VehiclesTestState) {
|
|
fresh(vehicles_st, vehicles_test_st)
|
|
vehicle_call_for(vehicles_st, 0, VEHICLE_BOAT, 0)
|
|
let b = vehicle_find(vehicles_st, VEHICLE_BOAT)
|
|
let bx = b.x
|
|
let bz = b.z
|
|
vehicles_test_st.wind = 1.0
|
|
ride(physics_st, vehicles_st, 0.0, 0.0, false, 8.0)
|
|
expect_near(b.x, bx, 0.2)
|
|
expect_near(b.z, bz, 0.2)
|
|
expect(vehicle_in_place(vehicles_st, VEHICLE_BOAT, 0) != null)
|
|
vehicles_test_st.wind = 0.0
|
|
vehicle_mount(vehicles_st, b)
|
|
ride(physics_st, vehicles_st, 0.0, 1.0, false, 4.0)
|
|
vehicle_drop(vehicles_st)
|
|
let lx = b.x
|
|
let lz = b.z
|
|
expect(lx > bx + 4.0)
|
|
vehicles_test_st.wind = 1.0
|
|
ride(physics_st, vehicles_st, 0.0, 0.0, false, 8.0)
|
|
expect_near(b.x, lx, 0.2)
|
|
expect_near(b.z, lz, 0.2)
|
|
expect(Math.abs(b.speed) < 0.05)
|
|
}
|
|
|
|
function char_turn(a: float) -> float {
|
|
var d = a
|
|
while d > 3.14159265 { d -= 6.2831853 }
|
|
while d < -3.14159265 { d += 6.2831853 }
|
|
return d
|
|
}
|
|
|
|
test "getting off a boat needs a shore near enough; a horse is left tied where it stands" (vehicles_st: mut VehiclesState, vehicles_test_st: mut VehiclesTestState) {
|
|
fresh(vehicles_st, vehicles_test_st)
|
|
vehicle_call_for(vehicles_st, 0, VEHICLE_BOAT, 0)
|
|
let b = vehicle_find(vehicles_st, VEHICLE_BOAT)
|
|
vehicle_mount(vehicles_st, b)
|
|
b.x = 40.0
|
|
expect(not vehicle_dismount(vehicles_st))
|
|
expect_eq(count(facts(vehicles_st), VEHICLE_NO_LANDING), 1)
|
|
expect_eq(vehicle_ride_kind(vehicles_st), VEHICLE_BOAT)
|
|
b.x = 12.0
|
|
expect(vehicle_dismount(vehicles_st))
|
|
expect_near(vehicles_test_st.rx, 11.0, 0.001)
|
|
expect_eq(vehicle_ride_kind(vehicles_st), 0)
|
|
expect_eq(b.rider, -1)
|
|
vehicle_call_for(vehicles_st, 0, VEHICLE_HORSE, 0)
|
|
let h = vehicle_find(vehicles_st, VEHICLE_HORSE)
|
|
vehicle_mount(vehicles_st, h)
|
|
h.x = -5.0
|
|
h.z = -5.0
|
|
expect(vehicle_dismount(vehicles_st))
|
|
expect_near(h.home_x, -5.0, 0.001)
|
|
expect(vehicle_in_place(vehicles_st, VEHICLE_HORSE, 0) == null)
|
|
expect_near(vehicles_test_st.rx, -5.0 + 1.2, 0.001)
|
|
}
|
|
|
|
test "this player's own are saved by kind, and a save read before they are made still applies" (vehicles_st: mut VehiclesState, vehicles_test_st: mut VehiclesTestState) {
|
|
fresh(vehicles_st, vehicles_test_st)
|
|
vehicle_call_for(vehicles_st, 0, VEHICLE_HORSE, 0)
|
|
vehicle_call_for(vehicles_st, 0, VEHICLE_BOAT, 0)
|
|
vehicle_own(vehicle_find(vehicles_st, VEHICLE_HORSE))
|
|
vehicle_find(vehicles_st, VEHICLE_HORSE).food = 77.0
|
|
let saved = vehicles_save(vehicles_st)
|
|
fresh(vehicles_st, vehicles_test_st)
|
|
vehicles_load(vehicles_st, saved, 1)
|
|
vehicle_call_for(vehicles_st, 0, VEHICLE_HORSE, 0)
|
|
vehicle_call_for(vehicles_st, 0, VEHICLE_BOAT, 0)
|
|
expect(vehicle_find(vehicles_st, VEHICLE_HORSE).owned)
|
|
expect_near(vehicle_find(vehicles_st, VEHICLE_HORSE).food, 77.0, 0.01)
|
|
expect(not vehicle_find(vehicles_st, VEHICLE_BOAT).owned)
|
|
}
|
|
|
|
test "the table: a nid finds its vehicle, each player's own by the owner index, and a release takes both" (vehicles_st: mut VehiclesState, vehicles_test_st: mut VehiclesTestState) {
|
|
fresh(vehicles_st, vehicles_test_st)
|
|
vehicle_call_for(vehicles_st, 5, VEHICLE_HORSE, 0)
|
|
vehicle_call_for(vehicles_st, 5, VEHICLE_BOAT, 0)
|
|
vehicle_call_for(vehicles_st, 6, VEHICLE_BOAT, 1)
|
|
let b = vehicle_of(vehicles_st, 6, VEHICLE_BOAT)
|
|
expect(b != null)
|
|
expect(vehicle_by_nid(vehicles_st, b.nid) == b)
|
|
expect(vehicle_of(vehicles_st, 6, VEHICLE_HORSE) == null)
|
|
let h = vehicle_of(vehicles_st, 5, VEHICLE_HORSE)
|
|
vehicles_release(vehicles_st, 5)
|
|
expect_eq(len(vehicles_all(vehicles_st)), 1)
|
|
expect(vehicle_by_nid(vehicles_st, h.nid) == null)
|
|
expect(vehicle_of(vehicles_st, 6, VEHICLE_BOAT) == b)
|
|
vehicles_reset(vehicles_st)
|
|
expect_eq(len(vehicles_all(vehicles_st)), 0)
|
|
expect(vehicle_by_nid(vehicles_st, b.nid) == null)
|
|
}
|
|
|
|
test "a fed fact a turn, drained each turn, makes no record past the ones made at the start" (vehicles_st: mut VehiclesState, vehicles_test_st: mut VehiclesTestState) {
|
|
fresh(vehicles_st, vehicles_test_st)
|
|
vehicle_call_for(vehicles_st, 0, VEHICLE_HORSE, 0)
|
|
let h = vehicle_find(vehicles_st, VEHICLE_HORSE)
|
|
var fed = 0
|
|
for k in 0 .. 1000 {
|
|
vehicle_feed(vehicles_st, h, 1.0)
|
|
fed += count(facts(vehicles_st), VEHICLE_FED)
|
|
}
|
|
expect_eq(fed, 1000)
|
|
expect_eq(vehicle_fact_records(vehicles_st), 64)
|
|
}
|
|
}
|