diff --git a/packages/ludic.vehicles/README.md b/packages/ludic.vehicles/README.md index fd8dece7..ff25aa91 100644 --- a/packages/ludic.vehicles/README.md +++ b/packages/ludic.vehicles/README.md @@ -26,8 +26,9 @@ through the game). - **A boat is a body in the game's physics** (`VehicleHull`, phase 16; a buoyant Jolt hull in Maroon Lake). The package rows it - the stick forward and back is a stroke along the bow, left and right the oars pulled against each other - and reads back where the hull is and how fast it goes. The - water floats it, the wind (`VehicleWorld.wind`) drifts every boat, ridden or not, and the lake bed - stops it at the shore; the swell at the sail limit takes the outward share of a stroke. + water floats it, the wind (`VehicleWorld.wind`) drifts a rowed boat, and the lake bed stops it at + the shore. A boat nobody rows is moored where it was called or left (`home_x`, `home_z`, + `home_yaw`): its hull is put back there whenever it strays, so it never crosses the lake alone; the swell at the sail limit takes the outward share of a stroke. - **A horse walks on legs in the game's physics** (`VehicleLegs`; a Jolt walker in Maroon Lake). The package decides its pace - a walk, a canter on stamina, hay for every metre - and never steps it into water; the legs meet the ground, its steps and slopes, and whatever stands in its way. What @@ -74,7 +75,7 @@ export port VehicleRider { | `vehicle_mount(v) -> bool`, `vehicle_dismount() -> bool`, `vehicle_drop()` | on and off | | `vehicle_update(ix, iz, run, dt)` | a frame of riding from the rider's stick (bind it where the body hands its stick over) | | `vehicle_own(v)`, `vehicle_feed(v, hay)`, `vehicle_follow(v, x, y, z, yaw)`, `vehicles_release(pid)`, `vehicle_restore(kind, owned, food)` | hiring, hay, a tied horse's wandering, a player leaving, a saved word | -| `vehicles_tick(dt)` | every boat's hull read back and pushed by the wind (the game calls it where the world is run) | +| `vehicles_tick(dt)` | every boat's hull read back: a rowed one pushed by the wind, one nobody rows held at its mooring (the game calls it where the world is run) | | `vehicle_facts() -> Queue` | `{ what, nid, kind, owner, x, z, v }`: `VEHICLE_MOUNTED`, `_DISMOUNTED`, `_NO_LANDING`, `_MOVED` (v: metres), `_SWELL` (every 14 s), `_HUNGRY`, `_FED` | | `vehicles_reset()`, `vehicles_system() -> System` | `"vehicles"`: this player's own saved by kind (hired, hay) - where they lie is not saved | diff --git a/packages/ludic.vehicles/call.ludic b/packages/ludic.vehicles/call.ludic index bb39b736..91a4d583 100644 --- a/packages/ludic.vehicles/call.ludic +++ b/packages/ludic.vehicles/call.ludic @@ -39,8 +39,14 @@ function ve_put(v: Vehicle, x: float, z: float) -> void { v.z = z v.speed = 0.0 if v.kind == VEHICLE_BOAT { v.y = VehicleWorld.water(x, z) + 0.02 } else { v.y = VehicleWorld.ground(x, z) } - v.home_x = x - v.home_z = z + ve_moor(v) +} + +# a boat nobody rows holds here (vehicles_tick); a horse is tied here +function ve_moor(v: Vehicle) -> void { + v.home_x = v.x + v.home_z = v.z + v.home_yaw = v.yaw } # Bring player pid's boat or horse to place k. False when the place is somebody else's, or it is @@ -59,8 +65,8 @@ export function vehicle_call_for(vehicles_st: VehiclesState, pid: int, kind: int return true } if v.rider >= 0 { return false } - ve_put(v, vehicle_place_x(vehicles_st, kind, k), vehicle_place_z(vehicles_st, kind, k)) v.yaw = yaw + ve_put(v, vehicle_place_x(vehicles_st, kind, k), vehicle_place_z(vehicles_st, kind, k)) ve_settle(v) VehicleWorld.placed(v) return true diff --git a/packages/ludic.vehicles/kinds.ludic b/packages/ludic.vehicles/kinds.ludic index 0d469d65..f81081bb 100644 --- a/packages/ludic.vehicles/kinds.ludic +++ b/packages/ludic.vehicles/kinds.ludic @@ -17,8 +17,9 @@ export property Vehicle { owned: bool = false # hired or rented for the trip owner: int = 0 # the player it belongs to (0: this machine's) rider: int = -1 # the player on it, -1 nobody - home_x: float = 0.0 # a horse is tied where its home is + home_x: float = 0.0 # a horse is tied where its home is; a boat nobody rows is moored there home_z: float = 0.0 + home_yaw: float = 0.0 # and a moored boat faces as it was left ent: int = -1 # its handle in the table tab: Table = null } diff --git a/packages/ludic.vehicles/mount.ludic b/packages/ludic.vehicles/mount.ludic index 841ef8cc..bac79cae 100644 --- a/packages/ludic.vehicles/mount.ludic +++ b/packages/ludic.vehicles/mount.ludic @@ -28,6 +28,7 @@ export function vehicle_dismount(vehicles_st: mut VehiclesState) -> bool { return false } VehicleRider.step_off(VehicleRider.safe_x(), VehicleRider.safe_z()) + ve_moor(v) } else { let wx = v.x + Math.cos(v.yaw) * 1.2 let wz = v.z - Math.sin(v.yaw) * 1.2 @@ -49,6 +50,7 @@ export function vehicle_drop(vehicles_st: mut VehiclesState) -> void { if vehicles_st.ve_cur != null { vehicles_st.ve_cur.rider = -1 vehicles_st.ve_cur.speed = 0.0 + if vehicles_st.ve_cur.kind == VEHICLE_BOAT { ve_moor(vehicles_st.ve_cur) } } vehicles_st.ve_cur = null } diff --git a/packages/ludic.vehicles/ride.ludic b/packages/ludic.vehicles/ride.ludic index 8a663d91..53eb95f5 100644 --- a/packages/ludic.vehicles/ride.ludic +++ b/packages/ludic.vehicles/ride.ludic @@ -51,19 +51,35 @@ function ve_swell(vehicles_st: mut VehiclesState, v: Vehicle, iz: float, dt: flo return take } -# every boat: its hull read back, and the wind pushing it across the lake, ridden or not +# every boat: a rowed one pushed by the wind across the lake; one nobody rows moored where it was +# called or left, its hull read back and held there 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 v.kind != VEHICLE_BOAT { continue } + if v.rider >= 0 { 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) - } + continue } + ve_hull_read(v) + ve_hold(v) + VehicleWorld.placed(v) } } + +# a moored hull that has moved off its mooring (a knock, the swell) is put back on it, at rest +function ve_hold(v: Vehicle) -> void { + let dx = v.x - v.home_x + let dz = v.z - v.home_z + let dyaw = Math.abs(Math.atan2(Math.sin(v.yaw - v.home_yaw), Math.cos(v.yaw - v.home_yaw))) + if dx * dx + dz * dz < 0.0004 and dyaw < 0.01 and Math.abs(v.speed) < 0.01 { return } + v.x = v.home_x + v.z = v.home_z + v.yaw = v.home_yaw + v.y = VehicleWorld.water(v.x, v.z) + 0.02 + v.speed = 0.0 + VehicleHull.put(v.nid, v.x, v.y, v.z, v.yaw) +} diff --git a/packages/ludic.vehicles/tests/vehicles_test.ludic b/packages/ludic.vehicles/tests/vehicles_test.ludic index 7ff6263e..b14dc079 100644 --- a/packages/ludic.vehicles/tests/vehicles_test.ludic +++ b/packages/ludic.vehicles/tests/vehicles_test.ludic @@ -309,6 +309,31 @@ program VehiclesTest { 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 }