zones_set / zones_clear / zones_merge, zones_contains / zones_nearest and the clusters' bounding circles, over fixed arrays of ZONES_MAX (16). WildlifeWorld.active freezes an animal outside (no state change, no dice), NpcWorld.active a walker (and no birth outside), VehicleWorld.active a moored boat, and ThingsWorld.restocks keeps a morning's restock inside; every default is "everywhere", so an unbound game is unchanged. Tests: the merge (near players one cluster, far two, a player leaving splits a chain), contains, nearest, the radius; wildlife, npc, vehicles and things pass as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
85 lines
4.4 KiB
Text
85 lines
4.4 KiB
Text
# port.ludic - what the people ask the place. Unbound: a flat dry field at noon on day one, one
|
|
# player at the origin, nobody wanted, every name free, and nothing drawn
|
|
export port NpcWorld {
|
|
ground: fn(float, float) -> float = fn nw_zero2
|
|
water: fn(float, float) -> float = fn nw_dry # the water's surface there
|
|
slope: fn(float, float) -> float = fn nw_zero2
|
|
allowed: fn(float, float) -> bool = fn nw_yes2 # a person may stop here (not the player's camp)
|
|
active: fn(float, float) -> bool = fn nw_yes2 # simulated here: outside, a walker is frozen and none is born
|
|
# (x, z, r, feet, head): true when a still thing takes a body of radius r there; pushed_x / _z say where
|
|
push: fn(float, float, float, float, float) -> bool = fn nw_no_push
|
|
pushed_x: fn() -> float = fn nw_asked_x
|
|
pushed_z: fn() -> float = fn nw_asked_z
|
|
clear: fn(float, float, float, float, float, float) -> bool = fn nw_clear # nothing between two points
|
|
hour: fn() -> float = fn nw_noon
|
|
day: fn() -> int = fn nw_day_one
|
|
players: fn() -> int = fn nw_one # player slots
|
|
player_on: fn(int) -> bool = fn nw_first
|
|
player_x: fn(int) -> float = fn nw_zero1
|
|
player_z: fn(int) -> float = fn nw_zero1
|
|
want: fn() -> int = fn nw_none # walkers the place wants at this hour
|
|
extra: fn() -> int = fn nw_no_kind # a kind to bring out whatever the crowd (-1)
|
|
origin_x: fn() -> float = fn nw_zero0 # where anyone is put when nowhere else will do
|
|
origin_z: fn() -> float = fn nw_zero0
|
|
next_id: fn() -> int = fn nw_next_id
|
|
body_of: fn(int) -> int = fn nw_body_of # a look seed's body
|
|
taken: fn(string) -> bool = fn nw_free # a name the place already uses
|
|
born: fn(NpcPerson) -> void = fn nw_nothing # a new one (or a slot reused): draw it
|
|
gone: fn(NpcPerson) -> void = fn nw_nothing # left: stop drawing it
|
|
# (p, x, z): the next corner of this person's way toward (x, z) into way_x / _z; false, the
|
|
# straight line with its steps round (a navmesh's path by a person's taste, in Maroon Lake)
|
|
way: fn(NpcPerson, float, float) -> bool = fn nw_no_way
|
|
way_x: fn() -> float = fn nw_asked_x
|
|
way_z: fn() -> float = fn nw_asked_z
|
|
}
|
|
|
|
# what the story says through them: the words of a line whose `fill` is set ("" when it has nothing
|
|
# to say now), and a greeting (a vendor's, when talked to)
|
|
export port NpcTalk {
|
|
say: fn(NpcPerson, int) -> string = fn nw_silent
|
|
greeting: fn(NpcPerson) -> string = fn nw_hello
|
|
}
|
|
|
|
function nw_zero0() -> float { return 0.0 }
|
|
function nw_zero1(p: int) -> float { return 0.0 }
|
|
function nw_zero2(x: float, z: float) -> float { return 0.0 }
|
|
function nw_dry(x: float, z: float) -> float { return -1000.0 }
|
|
function nw_yes2(x: float, z: float) -> bool { return true }
|
|
function nw_no_push(x: float, z: float, r: float, y0: float, y1: float) -> bool { return false }
|
|
function nw_asked_x(npc_st: NpcState) -> float { return npc_st.np_ask_x }
|
|
function nw_asked_z(npc_st: NpcState) -> float { return npc_st.np_ask_z }
|
|
function nw_clear(ax: float, ay: float, az: float, bx: float, by: float, bz: float) -> bool { return true }
|
|
function nw_noon() -> float { return 12.0 }
|
|
function nw_day_one() -> int { return 1 }
|
|
function nw_one() -> int { return 1 }
|
|
function nw_first(p: int) -> bool { return p == 0 }
|
|
function nw_none() -> int { return 0 }
|
|
function nw_no_kind() -> int { return -1 }
|
|
function nw_body_of(look: int) -> int { return look % 2 }
|
|
function nw_free(n: string) -> bool { return false }
|
|
function nw_nothing(p: NpcPerson) -> void { }
|
|
function nw_silent(p: NpcPerson, line: int) -> string { return "" }
|
|
function nw_hello(p: NpcPerson) -> string { return "" }
|
|
function nw_next_id(npc_st: mut NpcState) -> int {
|
|
npc_st.nw_ids += 1
|
|
return npc_st.nw_ids
|
|
}
|
|
|
|
# the nearest player on and their distance (npc_near_i: -1 when nobody is on)
|
|
|
|
export function npc_near(npc_st: mut NpcState, x: float, z: float) -> float {
|
|
var bd = 1000000.0
|
|
npc_st.np_near_i = -1
|
|
for i in 0 .. NpcWorld.players() {
|
|
if not NpcWorld.player_on(i) { continue }
|
|
let d = npc_d(x, z, NpcWorld.player_x(i), NpcWorld.player_z(i))
|
|
if d < bd {
|
|
bd = d
|
|
npc_st.np_near_i = i
|
|
}
|
|
}
|
|
return bd
|
|
}
|
|
|
|
export function npc_near_i(npc_st: NpcState) -> int { return npc_st.np_near_i }
|
|
function nw_no_way(p: NpcPerson, x: float, z: float) -> bool { return false }
|