ludic/packages/ludic.wildlife/table.ludic
Orkuncakilkaya 5ac3d488a4 wip(ecs): ludic.wildlife on a Table<WildAnimal> - UNBUILT
Written while builds are held (agents share 16 GB; no build or run until the user lifts it), so
it has not been compiled or tested. The animals are rows of a ludic.base Table: x, z, species and
alive as columns, a 32 m grid over the living, the living by species, and a nid map.
wildlife_by_nid is a map lookup instead of a scan of every animal ever made, wildlife_count walks
only its species, wildlife_nearest asks the grid, wildlife_set_alive writes the flag and the row
together, and wildlife_refile files the tick's moves once after it (wildlife_verify holds the
columns to the records). A guest puts its whole table away and brings it back. A test case covers
them; to run once builds are allowed: ludic test packages/ludic.wildlife.

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

91 lines
3 KiB
Text

# table.ludic - the animals, rows of a ludic.base Table: where each stands, its species and whether
# it lives are columns, with a grid over the living, the living by species, and a nid to its handle.
# The AI moves an animal by its record; wildlife_refile files every move once, after the tick.
export const WA_X: int = 0 # float columns
export const WA_Z: int = 1
export const WA_SP: int = 0 # int columns
export const WA_ALIVE: int = 1 # 1 while alive
const WA_CELL: float = 32.0
export property WildTable {
tab: Table<WildAnimal> = null
grid: Grid = null
species: IntIndex = null # the living, by species
nids: IntMap = null
}
function wl__new() -> WildTable {
let w = new WildTable
let tb: Table<WildAnimal> = table_new(2, 2)
w.tab = tb
w.grid = tb_grid(tb, WA_X, WA_Z, WA_ALIVE, WA_CELL)
w.species = tb_index(tb, WA_SP, WA_ALIVE)
w.nids = imap_new()
return w
}
function wl_row(a: WildAnimal) -> int {
if a == null or a.tab == null { return -1 }
return tb_row(a.tab, a.ent)
}
# a new row for `a`, its columns from its record
function wl_file(w: WildTable, a: WildAnimal) -> void {
a.tab = w.tab
a.ent = tb_add(w.tab, a)
let r = wl_row(a)
let tb = w.tab
tb.f[WA_X][r] = a.x
tb.f[WA_Z][r] = a.z
tb.i[WA_SP][r] = a.sp
var on = 0
if a.alive { on = 1 }
tb.i[WA_ALIVE][r] = on
tb_refile(tb, r)
if a.nid != 0 { imap_put(w.nids, a.nid, a.ent) }
}
# alive or not, the record and the row together (a copy the game is handed says so too)
export function wildlife_set_alive(a: WildAnimal, on: bool) -> void {
if a == null { return }
a.alive = on
let r = wl_row(a)
if r < 0 { return }
var v = 0
if on { v = 1 }
tb_set_i(a.tab, WA_ALIVE, r, v)
}
# every row whose record moved since it was filed, refiled: once after a tick that moves them all,
# and by the game after it moves the copies a guest is handed
export function wildlife_refile(wildlife_st: mut WildlifeState) -> void {
let tb = wildlife_st.wl.tab
let xs = tb.f[WA_X]
let zs = tb.f[WA_Z]
for r in 0 .. tb_len(tb) {
let a = tb.rec[r]
if xs[r] != a.x or zs[r] != a.z { tb_set_xz(tb, WA_X, WA_Z, r, a.x, a.z) }
}
}
# after a refile, the columns say what the records say; the count of rows that do not
export function wildlife_verify(wildlife_st: WildlifeState) -> int {
let tb = wildlife_st.wl.tab
var bad = 0
for r in 0 .. tb_len(tb) {
let a = tb.rec[r]
var on = 0
if a.alive { on = 1 }
if tb.f[WA_X][r] != a.x or tb.f[WA_Z][r] != a.z or tb.i[WA_SP][r] != a.sp or tb.i[WA_ALIVE][r] != on or a.ent != tb.ent[r] { bad += 1 }
}
return bad
}
# the nearest living animal of a species (below 0: any) within maxr (0: any distance), or null
export function wildlife_nearest(wildlife_st: WildlifeState, sp: int, x: float, z: float, maxr: float) -> WildAnimal {
let w = wildlife_st.wl
var r = -1
if sp < 0 { r = tb_nearest(w.tab, w.grid, x, z, maxr, -1, 0) } else { r = tb_nearest_of(w.tab, w.grid, w.species, x, z, maxr, sp) }
if r < 0 { return null }
return w.tab.rec[r]
}