feat(ecs): ludic.vehicles on a Table<Vehicle>
The vehicles are rows of a ludic.base Table with kind and owner as columns, every player's own by an owner index and a nid map: vehicle_of walks only that player's rows, vehicle_by_nid is a map lookup, and a player who leaves has their rows removed instead of the whole list rebuilt. ve_add takes the owner, so the owner is filed once and never assigned around the table. Positions stay on the record: a boat moves every frame and there are at most two a player. vehicles_test holds the table (9 tests); the game builds against it and lab/unit passes (88 tests). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
52270ab5d1
commit
045bf22e10
8 changed files with 87 additions and 18 deletions
|
|
@ -1,10 +1,11 @@
|
||||||
# call.ludic - a player's boat or horse brought to a place: made there the first time, fetched there
|
# call.ludic - a player's boat or horse brought to a place: made there the first time, fetched there
|
||||||
# after; gone when its player leaves
|
# after; gone when its player leaves
|
||||||
function ve_add(vehicles_st: VehiclesState, kind: int, x: float, z: float, yaw: float) -> Vehicle {
|
function ve_add(vehicles_st: VehiclesState, kind: int, owner: int, x: float, z: float, yaw: float) -> Vehicle {
|
||||||
if kind != VEHICLE_HORSE and kind != VEHICLE_BOAT { return null }
|
if kind != VEHICLE_HORSE and kind != VEHICLE_BOAT { return null }
|
||||||
let v = new Vehicle
|
let v = new Vehicle
|
||||||
v.nid = VehicleWorld.next_id()
|
v.nid = VehicleWorld.next_id()
|
||||||
v.kind = kind
|
v.kind = kind
|
||||||
|
v.owner = owner
|
||||||
v.yaw = yaw
|
v.yaw = yaw
|
||||||
if kind == VEHICLE_HORSE {
|
if kind == VEHICLE_HORSE {
|
||||||
v.seat_h = 1.35
|
v.seat_h = 1.35
|
||||||
|
|
@ -14,7 +15,7 @@ function ve_add(vehicles_st: VehiclesState, kind: int, x: float, z: float, yaw:
|
||||||
v.seat_h = 0.30
|
v.seat_h = 0.30
|
||||||
}
|
}
|
||||||
ve_put(v, x, z)
|
ve_put(v, x, z)
|
||||||
push(ve_all(vehicles_st), v)
|
ve_file(vehicles_st.ve, v)
|
||||||
ve_settle(v)
|
ve_settle(v)
|
||||||
VehicleWorld.made(v)
|
VehicleWorld.made(v)
|
||||||
return v
|
return v
|
||||||
|
|
@ -51,9 +52,8 @@ export function vehicle_call_for(vehicles_st: VehiclesState, pid: int, kind: int
|
||||||
var yaw = vehicles_st.ve_dock_yaw
|
var yaw = vehicles_st.ve_dock_yaw
|
||||||
if kind == VEHICLE_HORSE { yaw = vehicles_st.ve_rail_yaw }
|
if kind == VEHICLE_HORSE { yaw = vehicles_st.ve_rail_yaw }
|
||||||
if v == null {
|
if v == null {
|
||||||
let made = ve_add(vehicles_st, kind, vehicle_place_x(vehicles_st, kind, k), vehicle_place_z(vehicles_st, kind, k), yaw)
|
let made = ve_add(vehicles_st, kind, pid, vehicle_place_x(vehicles_st, kind, k), vehicle_place_z(vehicles_st, kind, k), yaw)
|
||||||
if made == null { return false }
|
if made == null { return false }
|
||||||
made.owner = pid
|
|
||||||
if pid == 0 { ve_apply_kept(vehicles_st, made) }
|
if pid == 0 { ve_apply_kept(vehicles_st, made) }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -68,10 +68,10 @@ export function vehicle_call_for(vehicles_st: VehiclesState, pid: int, kind: int
|
||||||
# a player left: their boat and horse go with them, and their places are free
|
# a player left: their boat and horse go with them, and their places are free
|
||||||
export function vehicles_release(vehicles_st: mut VehiclesState, pid: int) -> void {
|
export function vehicles_release(vehicles_st: mut VehiclesState, pid: int) -> void {
|
||||||
if pid == 0 { return }
|
if pid == 0 { return }
|
||||||
let keep = new []Vehicle
|
let rows = ix_rows(vehicles_st.ve.owners, pid)
|
||||||
let l = ve_all(vehicles_st)
|
while len(rows) > 0 {
|
||||||
for i in 0 .. len(l) {
|
let v = ve_all(vehicles_st)[rows[len(rows) - 1]]
|
||||||
if l[i].owner != pid { push(keep, l[i]) } else { ve_gone(l[i]) }
|
ve_gone(v)
|
||||||
|
ve_unfile(vehicles_st.ve, v)
|
||||||
}
|
}
|
||||||
vehicles_st.ve_list = keep
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ module ludic_vehicles uses ludic_base
|
||||||
numbers float
|
numbers float
|
||||||
import "ludic.base"
|
import "ludic.base"
|
||||||
import "kinds.ludic"
|
import "kinds.ludic"
|
||||||
|
import "table.ludic"
|
||||||
import "ports.ludic"
|
import "ports.ludic"
|
||||||
import "state.ludic"
|
import "state.ludic"
|
||||||
import "places.ludic"
|
import "places.ludic"
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ export property Vehicle {
|
||||||
rider: int = -1 # the player on it, -1 nobody
|
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
|
||||||
home_z: float = 0.0
|
home_z: float = 0.0
|
||||||
|
ent: int = -1 # its handle in the table
|
||||||
|
tab: Table<Vehicle> = null
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VEHICLE_MOUNTED: int = 0
|
export const VEHICLE_MOUNTED: int = 0
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ export state VehiclesState {
|
||||||
ve_ids: int = 0
|
ve_ids: int = 0
|
||||||
ve_sx: float = 0.0
|
ve_sx: float = 0.0
|
||||||
ve_sz: float = 0.0
|
ve_sz: float = 0.0
|
||||||
ve_list: []Vehicle = new []Vehicle
|
ve: VehicleTable = ve__new()
|
||||||
ve_cur: Vehicle = null
|
ve_cur: Vehicle = null
|
||||||
ve_dock_x: float = 0.0
|
ve_dock_x: float = 0.0
|
||||||
ve_dock_z: float = 0.0
|
ve_dock_z: float = 0.0
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ function ve_say(vehicles_st: VehiclesState, what: int, v: Vehicle, n: float) ->
|
||||||
q_push(vehicle_facts(vehicles_st), f)
|
q_push(vehicle_facts(vehicles_st), f)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ve_all(vehicles_st: VehiclesState) -> []Vehicle { return vehicles_st.ve_list }
|
function ve_all(vehicles_st: VehiclesState) -> []Vehicle { return vehicles_st.ve.tab.rec }
|
||||||
|
|
||||||
# every vehicle in the valley, to draw and to aim at (change one only through the verbs)
|
# every vehicle in the valley, to draw and to aim at (change one only through the verbs)
|
||||||
export function vehicles_all(vehicles_st: VehiclesState) -> []Vehicle { return ve_all(vehicles_st) }
|
export function vehicles_all(vehicles_st: VehiclesState) -> []Vehicle { return ve_all(vehicles_st) }
|
||||||
|
|
@ -32,8 +32,9 @@ export function vehicle_ride_kind(vehicles_st: VehiclesState) -> int {
|
||||||
export function vehicle_current(vehicles_st: VehiclesState) -> Vehicle { return vehicles_st.ve_cur }
|
export function vehicle_current(vehicles_st: VehiclesState) -> Vehicle { return vehicles_st.ve_cur }
|
||||||
|
|
||||||
export function vehicle_of(vehicles_st: VehiclesState, pid: int, kind: int) -> Vehicle {
|
export function vehicle_of(vehicles_st: VehiclesState, pid: int, kind: int) -> Vehicle {
|
||||||
|
let rows = ix_rows(vehicles_st.ve.owners, pid)
|
||||||
let l = ve_all(vehicles_st)
|
let l = ve_all(vehicles_st)
|
||||||
for i in 0 .. len(l) { if l[i].kind == kind and l[i].owner == pid { return l[i] } }
|
for k in 0 .. len(rows) { if l[rows[k]].kind == kind { return l[rows[k]] } }
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@
|
||||||
# and the horse's hay); where they lie is not saved - they are called again. The boats' hulls are
|
# and the horse's hay); where they lie is not saved - they are called again. The boats' hulls are
|
||||||
# read back and pushed by the wind in vehicles_tick, which the game calls where the world is run
|
# read back and pushed by the wind in vehicles_tick, which the game calls where the world is run
|
||||||
export function vehicles_reset(vehicles_st: mut VehiclesState) -> void {
|
export function vehicles_reset(vehicles_st: mut VehiclesState) -> void {
|
||||||
if vehicles_st.ve_list != null {
|
let l = ve_all(vehicles_st)
|
||||||
for i in 0 .. len(vehicles_st.ve_list) {
|
for i in 0 .. len(l) {
|
||||||
let v = vehicles_st.ve_list[i]
|
let v = l[i]
|
||||||
if v.kind == VEHICLE_BOAT { VehicleHull.gone(v.nid) } else { VehicleLegs.gone(v.nid) }
|
if v.kind == VEHICLE_BOAT { VehicleHull.gone(v.nid) } else { VehicleLegs.gone(v.nid) }
|
||||||
}
|
v.ent = -1
|
||||||
}
|
}
|
||||||
vehicles_st.ve_list = new []Vehicle
|
tb_clear(vehicles_st.ve.tab)
|
||||||
|
imap_clear(vehicles_st.ve.nids)
|
||||||
vehicles_st.ve_cur = null
|
vehicles_st.ve_cur = null
|
||||||
vehicles_st.ve_have_dock = false
|
vehicles_st.ve_have_dock = false
|
||||||
vehicles_st.ve_swell_said = 0.0
|
vehicles_st.ve_swell_said = 0.0
|
||||||
|
|
|
||||||
45
packages/ludic.vehicles/table.ludic
Normal file
45
packages/ludic.vehicles/table.ludic
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
# table.ludic - the vehicles, rows of a ludic.base Table: kind and owner as columns, each player's
|
||||||
|
# own by index, and a nid to its handle. Where one is stays on its record: a boat moves every frame
|
||||||
|
# and there are never more than two a player to search.
|
||||||
|
export const VE_KIND: int = 0 # int columns
|
||||||
|
export const VE_OWNER: int = 1
|
||||||
|
|
||||||
|
export property VehicleTable {
|
||||||
|
tab: Table<Vehicle> = null
|
||||||
|
owners: IntIndex = null # every vehicle by the player it belongs to
|
||||||
|
nids: IntMap = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function ve__new() -> VehicleTable {
|
||||||
|
let w = new VehicleTable
|
||||||
|
let tb: Table<Vehicle> = table_new(0, 2)
|
||||||
|
w.tab = tb
|
||||||
|
w.owners = tb_index(tb, VE_OWNER, -1)
|
||||||
|
w.nids = imap_new()
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
# a new row for v, its kind and owner from its record
|
||||||
|
function ve_file(w: VehicleTable, v: Vehicle) -> void {
|
||||||
|
v.tab = w.tab
|
||||||
|
v.ent = tb_add(w.tab, v)
|
||||||
|
let r = tb_row(w.tab, v.ent)
|
||||||
|
w.tab.i[VE_KIND][r] = v.kind
|
||||||
|
w.tab.i[VE_OWNER][r] = v.owner
|
||||||
|
tb_refile(w.tab, r)
|
||||||
|
imap_put(w.nids, v.nid, v.ent)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ve_unfile(w: VehicleTable, v: Vehicle) -> void {
|
||||||
|
imap_del(w.nids, v.nid)
|
||||||
|
tb_remove(w.tab, v.ent)
|
||||||
|
v.ent = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
# the vehicle a message names, or null
|
||||||
|
export function vehicle_by_nid(vehicles_st: VehiclesState, nid: int) -> Vehicle {
|
||||||
|
let tb = vehicles_st.ve.tab
|
||||||
|
let r = tb_row(tb, imap_get(vehicles_st.ve.nids, nid, -1))
|
||||||
|
if r < 0 or tb.rec[r].nid != nid { return null }
|
||||||
|
return tb.rec[r]
|
||||||
|
}
|
||||||
|
|
@ -344,4 +344,23 @@ program VehiclesTest {
|
||||||
expect_near(vehicle_find(vehicles_st, VEHICLE_HORSE).food, 77.0, 0.01)
|
expect_near(vehicle_find(vehicles_st, VEHICLE_HORSE).food, 77.0, 0.01)
|
||||||
expect(not vehicle_find(vehicles_st, VEHICLE_BOAT).owned)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue