wip(0.S3): the packages migrated by ludic migrate state packages - every package test green

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-25 14:02:21 +03:00
parent 1e8b5b0523
commit 07505e7ef2
294 changed files with 14996 additions and 14675 deletions

View file

@ -1,36 +1,36 @@
# birds.ludic - a bird circles its home at its own height, comes down now and then (for seed from
# further off than for nothing), glides in rather than dropping, pecks and drinks, and lifts off
export function wildlife_tick_bird(a: WildAnimal, dt: float) -> void {
export function wildlife_tick_bird(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal, dt: float) -> void {
a.timer = a.timer - dt
let dh = wl_near(a.x, a.z)
let dh = wl_near(wildlife_st, a.x, a.z)
if a.state == WILD_LAND {
wl_land(a, dh, dt)
wl_land(base_st, wildlife_st, a, dh, dt)
return
}
if a.state == WILD_PERCH {
wl_perch(a, dh, dt)
wl_perch(base_st, wildlife_st, a, dh, dt)
return
}
wl_circle(a, dt)
let s = wl_sp(a.sp)
if wl_bird_seed(a) and dh > s.flee * 0.7 and wl_bird_spot_ok(wl_best.x, wl_best.z) {
let off = 1.0 + wl_rnd() * 2.0
let oa = wl_rnd() * WL_TAU
wl_come_down(a, wl_best.x + Math.sin(oa) * off, wl_best.z + Math.cos(oa) * off)
wl_circle(wildlife_st, a, dt)
let s = wl_sp(wildlife_st, a.sp)
if wl_bird_seed(wildlife_st, a) and dh > s.flee * 0.7 and wl_bird_spot_ok(wildlife_st.wl_best.x, wildlife_st.wl_best.z) {
let off = 1.0 + wl_rnd(wildlife_st) * 2.0
let oa = wl_rnd(wildlife_st) * WL_TAU
wl_come_down(a, wildlife_st.wl_best.x + Math.sin(oa) * off, wildlife_st.wl_best.z + Math.cos(oa) * off)
return
}
if a.timer < 0.0 and dh > s.land_shy {
let sx = a.x + (wl_rnd() - 0.5) * 24.0
let sz = a.z + (wl_rnd() - 0.5) * 24.0
if wl_rnd() < s.land_chance and wl_bird_spot_ok(sx, sz) {
let sx = a.x + (wl_rnd(wildlife_st) - 0.5) * 24.0
let sz = a.z + (wl_rnd(wildlife_st) - 0.5) * 24.0
if wl_rnd(wildlife_st) < s.land_chance and wl_bird_spot_ok(sx, sz) {
wl_come_down(a, sx, sz)
return
}
}
if a.timer < 0.0 {
a.timer = 30.0
a.home_x = a.home_x + (wl_rnd() - 0.5) * 120.0
a.home_z = a.home_z + (wl_rnd() - 0.5) * 120.0
a.home_x = a.home_x + (wl_rnd(wildlife_st) - 0.5) * 120.0
a.home_z = a.home_z + (wl_rnd(wildlife_st) - 0.5) * 120.0
}
}
@ -42,8 +42,8 @@ function wl_come_down(a: WildAnimal, x: float, z: float) -> void {
}
# a circle about home, climbing to its height: the eagle wide and high, the jay low
function wl_circle(a: WildAnimal, dt: float) -> void {
let s = wl_sp(a.sp)
function wl_circle(wildlife_st: mut WildlifeState, a: WildAnimal, dt: float) -> void {
let s = wl_sp(wildlife_st, a.sp)
a.fly_h = a.fly_h + (8.0 + s.home * 0.12 - a.fly_h) * Math.min(1.0, 0.4 * dt)
let r = s.home * 0.5
let ang = Math.atan2(a.z - a.home_z, a.x - a.home_x) + s.walk / r * dt
@ -57,45 +57,45 @@ function wl_circle(a: WildAnimal, dt: float) -> void {
a.bank = 0.35
}
function wl_take_off(a: WildAnimal, h: float) -> void {
function wl_take_off(wildlife_st: mut WildlifeState, a: WildAnimal, h: float) -> void {
a.state = WILD_FLY
a.timer = 20.0 + wl_rnd() * 40.0
a.timer = 20.0 + wl_rnd(wildlife_st) * 40.0
a.fly_h = Math.max(a.fly_h, h)
}
function wl_land(a: WildAnimal, dh: float, dt: float) -> void {
let s = wl_sp(a.sp)
function wl_land(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal, dh: float, dt: float) -> void {
let s = wl_sp(wildlife_st, a.sp)
a.fly_h = Math.max(0.0, a.fly_h - dt * 3.2)
a.speed = s.walk * 0.8
wl_face(a, a.tx, a.tz, 2.4, dt)
wl_step(a, dt)
wl_step(base_st, wildlife_st, a, dt)
a.y = WildlifeWorld.ground(a.x, a.z) + a.fly_h
a.bank = 0.2 * Math.clamp(a.fly_h / 4.0, 0.0, 1.0)
if dh < s.flee or a.timer < -20.0 {
wl_take_off(a, 0.0)
wl_take_off(wildlife_st, a, 0.0)
return
}
if a.fly_h < 0.15 and wl_at_target(a, 2.0) {
a.state = WILD_PERCH
a.fly_h = 0.0
a.timer = 8.0 + wl_rnd() * 16.0
a.timer = 8.0 + wl_rnd(wildlife_st) * 16.0
a.bank = 0.0
}
}
# on the ground it pecks at seed or takes a drink at the water's edge; it leaves no droppings
function wl_perch(a: WildAnimal, dh: float, dt: float) -> void {
function wl_perch(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal, dh: float, dt: float) -> void {
a.speed = 0.0
a.y = WildlifeWorld.ground(a.x, a.z)
a.bank = 0.0
if wl_bird_seed(a) and Math.sqrt((wl_best.x - a.x) * (wl_best.x - a.x) + (wl_best.z - a.z) * (wl_best.z - a.z)) < 3.0 {
if wl_bird_seed(wildlife_st, a) and Math.sqrt((wildlife_st.wl_best.x - a.x) * (wildlife_st.wl_best.x - a.x) + (wildlife_st.wl_best.z - a.z) * (wildlife_st.wl_best.z - a.z)) < 3.0 {
a.hunger = Math.max(a.hunger - 30.0 * dt, 0.0)
a.timer = Math.max(a.timer, 1.0)
if wl_rnd() < 0.12 * dt and WildlifeWorld.present(wl_best.uid) { wl_ate(a, wl_best.uid, wl_best.owner, WILD_SEED) }
if wl_rnd(wildlife_st) < 0.12 * dt and WildlifeWorld.present(wildlife_st.wl_best.uid) { wl_ate(base_st, wildlife_st, a, wildlife_st.wl_best.uid, wildlife_st.wl_best.owner, WILD_SEED) }
} else if wl_wet(a.x - Math.sin(a.yaw) * 2.0, a.z - Math.cos(a.yaw) * 2.0, 0.05) {
a.thirst = Math.max(a.thirst - 30.0 * dt, 0.0)
}
if a.timer < 0.0 or dh < wl_sp(a.sp).flee { wl_take_off(a, 0.5) }
if a.timer < 0.0 or dh < wl_sp(wildlife_st, a.sp).flee { wl_take_off(wildlife_st, a, 0.5) }
}
# somewhere a bird would put down: flat and dry

View file

@ -27,14 +27,13 @@ export property WildFact {
diet: int = 0
}
var wl_facts: Queue<WildFact> = null
export function wildlife_facts() -> Queue<WildFact> {
if wl_facts == null { wl_facts = queue_new("wildlife.facts") }
return wl_facts
export function wildlife_facts(base_st: mut BaseState, wildlife_st: mut WildlifeState) -> Queue<WildFact> {
if wildlife_st.wl_facts == null { wildlife_st.wl_facts = queue_new(base_st, "wildlife.facts") }
return wildlife_st.wl_facts
}
function wl_fact(what: int, a: WildAnimal) -> WildFact {
function wl_fact(base_st: mut BaseState, wildlife_st: mut WildlifeState, what: int, a: WildAnimal) -> WildFact {
let f = new WildFact
f.what = what
f.key = a.key
@ -45,6 +44,6 @@ function wl_fact(what: int, a: WildAnimal) -> WildFact {
f.z = a.z
f.yaw = a.yaw
f.scale = a.scale
q_push(wildlife_facts(), f)
q_push(base_st, wildlife_facts(base_st, wildlife_st), f)
return f
}

View file

@ -1,87 +1,87 @@
# ground.ludic - a walker's tick: its needs, the alert, then the first of charging, reacting, going
# to its spot, walking to something set out, eating, idling and wandering that has it this tick
export function wildlife_tick_ground(a: WildAnimal, dt: float, gh: float) -> void {
wl_needs(a, gh)
let dh = wl_near(a.x, a.z)
let np = wl_near_i
export function wildlife_tick_ground(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal, dt: float, gh: float) -> void {
wl_needs(base_st, wildlife_st, a, gh)
let dh = wl_near(wildlife_st, a.x, a.z)
let np = wildlife_st.wl_near_i
a.timer = a.timer - dt
if a.calm > 0.0 { a.calm = a.calm - gh * 60.0 }
if a.spook > 0.0 { a.spook = a.spook - gh * 60.0 }
wl_alert(a, np, dh, dt)
wl_alert(wildlife_st, a, np, dh, dt)
if a.state == WILD_TRAPPED {
a.speed = 0.0
return
}
if wl_charge(a, np, dh, dt) { return }
wl_posture(a, np, dt)
if wl_react(a, np, dt) { return }
if wl_seek(a, dt) { return }
if wl_drink(a, dt, gh) { return }
if wl_approach(a, dt) { return }
if wl_eat(a) { return }
let s = wl_sp(a.sp)
if wl_charge(base_st, wildlife_st, a, np, dh, dt) { return }
wl_posture(base_st, wildlife_st, a, np, dt)
if wl_react(base_st, wildlife_st, a, np, dt) { return }
if wl_seek(base_st, wildlife_st, a, dt) { return }
if wl_drink(wildlife_st, a, dt, gh) { return }
if wl_approach(base_st, wildlife_st, a, dt) { return }
if wl_eat(base_st, wildlife_st, a) { return }
let s = wl_sp(wildlife_st, a.sp)
if a.state == WILD_IDLE {
a.speed = 0.0
if a.timer < 0.0 {
if a.timer > -6.0 and s.tracks and wl_rnd() < 0.06 { wl_fact(WILD_BED, a) } # a long sit leaves crushed grass
if a.timer > -6.0 and s.tracks and wl_rnd(wildlife_st) < 0.06 { wl_fact(base_st, wildlife_st, WILD_BED, a) } # a long sit leaves crushed grass
a.state = WILD_WANDER
a.timer = 6.0 + wl_rnd() * 10.0
wl_clamp(a)
wl_pick_target(a, s.home)
a.timer = 6.0 + wl_rnd(wildlife_st) * 10.0
wl_clamp(wildlife_st, a)
wl_pick_target(wildlife_st, a, s.home)
}
return
}
wl_face(a, a.tx, a.tz, 2.0, dt)
a.speed = s.walk
wl_step(a, dt)
wl_step(base_st, wildlife_st, a, dt)
if wl_at_target(a, 1.5) or a.timer < 0.0 {
a.state = WILD_IDLE
a.timer = 3.0 + wl_rnd() * 8.0
a.timer = 3.0 + wl_rnd(wildlife_st) * 8.0
}
}
# a charger close enough comes at the nearest player; true while it is charging
function wl_charge(a: WildAnimal, np: int, dh: float, dt: float) -> bool {
let s = wl_sp(a.sp)
function wl_charge(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal, np: int, dh: float, dt: float) -> bool {
let s = wl_sp(wildlife_st, a.sp)
if s.charges and a.state != WILD_CHARGE and dh < WildlifeWorld.charge_reach() and not (a.calm > 0.0) {
a.state = WILD_CHARGE
a.timer = 2.5
wl_fact(WILD_CHARGED, a).player = np
wl_fact(base_st, wildlife_st, WILD_CHARGED, a).player = np
}
if a.state != WILD_CHARGE { return false }
wl_face(a, WildlifeWorld.player_x(np), WildlifeWorld.player_z(np), 4.0, dt)
a.speed = s.run
wl_step(a, dt)
wl_step(base_st, wildlife_st, a, dt)
if dh < 2.6 {
wl_fact(WILD_SHOVED, a).player = np
wl_wander(a, 8.0)
wl_fact(base_st, wildlife_st, WILD_SHOVED, a).player = np
wl_wander(wildlife_st, a, 8.0)
a.calm = 30.0
}
if a.timer < 0.0 { wl_wander(a, 6.0) }
if a.timer < 0.0 { wl_wander(wildlife_st, a, 6.0) }
return true
}
# Four postures read off the alert. Staring is a REACTION, not a condition: each times out and
# hands the animal back to its business, and only a fresh rise above `seen` looks up again.
function wl_posture(a: WildAnimal, np: int, dt: float) -> void {
if wl_sp(a.sp).tame or a.state == WILD_EAT { return }
function wl_posture(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal, np: int, dt: float) -> void {
if wl_sp(wildlife_st, a.sp).tame or a.state == WILD_EAT { return }
if a.alert > 95.0 {
if a.state != WILD_FLEE {
a.state = WILD_FLEE
a.timer = 3.0 + wl_rnd() * 3.0
a.spook = 120.0 + wl_rnd() * 180.0
a.timer = 3.0 + wl_rnd(wildlife_st) * 3.0
a.spook = 120.0 + wl_rnd(wildlife_st) * 180.0
a.alert = 70.0
wl_fact(WILD_FLED, a).player = np
wl_fact(base_st, wildlife_st, WILD_FLED, a).player = np
}
} else if a.alert > 60.0 {
if a.state != WILD_WARY and a.state != WILD_FLEE and a.alert > a.seen {
a.state = WILD_WARY
a.timer = 2.0 + wl_rnd() * 2.0
a.timer = 2.0 + wl_rnd(wildlife_st) * 2.0
}
} else if a.alert > 25.0 {
if a.state != WILD_ALERT and a.state != WILD_WARY and a.alert > a.seen {
a.state = WILD_ALERT
a.timer = 1.5 + wl_rnd() * 2.5
a.timer = 1.5 + wl_rnd(wildlife_st) * 2.5
}
}
if a.alert > a.seen { a.seen = a.alert }

View file

@ -1,17 +1,16 @@
# guest.ludic - a machine that draws another's animals puts its own away whole, and they come back
# as they were when it stops; in between the list is the copies it is handed (wildlife_make)
var wl_own: []WildAnimal = null
export function wildlife_put_away() -> void {
if wl_own != null { return }
wl_own = wildlife_all()
wl_list = new []WildAnimal
export function wildlife_put_away(wildlife_st: mut WildlifeState) -> void {
if wildlife_st.wl_own != null { return }
wildlife_st.wl_own = wildlife_all(wildlife_st)
wildlife_st.wl_list = new []WildAnimal
}
export function wildlife_bring_back() -> void {
if wl_own == null { return }
wl_list = wl_own
wl_own = null
export function wildlife_bring_back(wildlife_st: mut WildlifeState) -> void {
if wildlife_st.wl_own == null { return }
wildlife_st.wl_list = wildlife_st.wl_own
wildlife_st.wl_own = null
}
export function wildlife_away() -> bool { return wl_own != null }
export function wildlife_away(wildlife_st: WildlifeState) -> bool { return wildlife_st.wl_own != null }

View file

@ -1,7 +1,5 @@
# habitat.ludic - the right kind of ground for a species, asked of the port's ground, water, slope
# and forest; and a spot of it at a distance from a point, off the package's dice
var wl_px: float = 0.0
var wl_pz: float = 0.0
function wl_wet(x: float, z: float, depth: float) -> bool {
return WildlifeWorld.ground(x, z) < WildlifeWorld.water(x, z) - depth
@ -19,20 +17,20 @@ export function wildlife_habitat_ok(hab: int, x: float, z: float) -> bool {
}
# a spot of `hab` d0..d1 metres from (x0, z0); where it is, is wildlife_spot_x / _z
export function wildlife_find_spot(hab: int, x0: float, z0: float, d0: float, d1: float) -> bool {
export function wildlife_find_spot(wildlife_st: mut WildlifeState, hab: int, x0: float, z0: float, d0: float, d1: float) -> bool {
for t in 0 .. 60 {
let ang = wl_rnd() * WL_TAU
let d = d0 + wl_rnd() * (d1 - d0)
let ang = wl_rnd(wildlife_st) * WL_TAU
let d = d0 + wl_rnd(wildlife_st) * (d1 - d0)
let x = x0 + Math.cos(ang) * d
let z = z0 + Math.sin(ang) * d
if wildlife_habitat_ok(hab, x, z) {
wl_px = x
wl_pz = z
wildlife_st.wl_px = x
wildlife_st.wl_pz = z
return true
}
}
return false
}
export function wildlife_spot_x() -> float { return wl_px }
export function wildlife_spot_z() -> float { return wl_pz }
export function wildlife_spot_x(wildlife_st: WildlifeState) -> float { return wildlife_st.wl_px }
export function wildlife_spot_z(wildlife_st: WildlifeState) -> float { return wildlife_st.wl_pz }

View file

@ -1,13 +1,9 @@
# lures.ludic - what an animal would walk to: food of its diet, a lure of its diet, a snare it is
# small enough for (half the time: a curiosity), within 70 m - 175 m for food while feeding is taught
var wl_ask: WildLure = null
var wl_best: WildLure = null
var wl_seed_any: bool = false
var wl_seed_t: float = 0.0
function wl_lure_scratch() -> void {
if wl_ask == null { wl_ask = new WildLure }
if wl_best == null { wl_best = new WildLure }
function wl_lure_scratch(wildlife_st: mut WildlifeState) -> void {
if wildlife_st.wl_ask == null { wildlife_st.wl_ask = new WildLure }
if wildlife_st.wl_best == null { wildlife_st.wl_best = new WildLure }
}
function wl_copy_lure(from: WildLure, to: WildLure) -> void {
@ -20,82 +16,82 @@ function wl_copy_lure(from: WildLure, to: WildLure) -> void {
to.owner = from.owner
}
function wl_draws(a: WildAnimal, l: WildLure) -> bool {
let s = wl_sp(a.sp)
if l.kind == WILD_SNARE { return s.size <= l.size and wl_rnd() > 0.5 }
function wl_draws(wildlife_st: mut WildlifeState, a: WildAnimal, l: WildLure) -> bool {
let s = wl_sp(wildlife_st, a.sp)
if l.kind == WILD_SNARE { return s.size <= l.size and wl_rnd(wildlife_st) > 0.5 }
return l.diet == s.diet and l.diet != WILD_SEED
}
# the nearest thing set out that draws it, into `wl_best`; false when none does
function wl_attraction(a: WildAnimal) -> bool {
wl_lure_scratch()
function wl_attraction(wildlife_st: mut WildlifeState, a: WildAnimal) -> bool {
wl_lure_scratch(wildlife_st)
var bd = 70.0
var got = false
let taught = WildlifeWorld.taught()
for i in 0 .. WildlifeWorld.lures() {
if not WildlifeWorld.lure(i, wl_ask) or not wl_draws(a, wl_ask) { continue }
let dx = wl_ask.x - a.x
let dz = wl_ask.z - a.z
if not WildlifeWorld.lure(i, wildlife_st.wl_ask) or not wl_draws(wildlife_st, a, wildlife_st.wl_ask) { continue }
let dx = wildlife_st.wl_ask.x - a.x
let dz = wildlife_st.wl_ask.z - a.z
var d = Math.sqrt(dx * dx + dz * dz)
if taught and wl_ask.kind == WILD_FEED { d = d * 0.4 }
if taught and wildlife_st.wl_ask.kind == WILD_FEED { d = d * 0.4 }
if d < bd {
bd = d
got = true
wl_copy_lure(wl_ask, wl_best)
wl_copy_lure(wildlife_st.wl_ask, wildlife_st.wl_best)
}
}
return got
}
function wl_take_lure(a: WildAnimal) -> void {
a.lure = wl_best.uid
a.lure_kind = wl_best.kind
a.lure_x = wl_best.x
a.lure_z = wl_best.z
a.lure_owner = wl_best.owner
function wl_take_lure(wildlife_st: WildlifeState, a: WildAnimal) -> void {
a.lure = wildlife_st.wl_best.uid
a.lure_kind = wildlife_st.wl_best.kind
a.lure_x = wildlife_st.wl_best.x
a.lure_z = wildlife_st.wl_best.z
a.lure_owner = wildlife_st.wl_best.owner
a.state = WILD_APPROACH
}
# Is there ANY seed down? Asked once a second for the whole flock rather than per bird per tick.
function wl_seed_scan(dt: float) -> void {
wl_seed_t = wl_seed_t - dt
if wl_seed_t > 0.0 { return }
wl_seed_t = 1.0
wl_seed_any = false
wl_lure_scratch()
function wl_seed_scan(wildlife_st: mut WildlifeState, dt: float) -> void {
wildlife_st.wl_seed_t = wildlife_st.wl_seed_t - dt
if wildlife_st.wl_seed_t > 0.0 { return }
wildlife_st.wl_seed_t = 1.0
wildlife_st.wl_seed_any = false
wl_lure_scratch(wildlife_st)
for i in 0 .. WildlifeWorld.lures() {
if WildlifeWorld.lure(i, wl_ask) and wl_ask.kind == WILD_FEED and wl_ask.diet == WILD_SEED {
wl_seed_any = true
if WildlifeWorld.lure(i, wildlife_st.wl_ask) and wildlife_st.wl_ask.kind == WILD_FEED and wildlife_st.wl_ask.diet == WILD_SEED {
wildlife_st.wl_seed_any = true
return
}
}
}
# the nearest seed within 120 m, into `wl_best`: the one thing that brings an animal TO you
function wl_bird_seed(a: WildAnimal) -> bool {
if not wl_seed_any { return false }
wl_lure_scratch()
function wl_bird_seed(wildlife_st: mut WildlifeState, a: WildAnimal) -> bool {
if not wildlife_st.wl_seed_any { return false }
wl_lure_scratch(wildlife_st)
var bd = 120.0
var got = false
for i in 0 .. WildlifeWorld.lures() {
if not WildlifeWorld.lure(i, wl_ask) or wl_ask.kind != WILD_FEED or wl_ask.diet != WILD_SEED { continue }
let dx = wl_ask.x - a.x
let dz = wl_ask.z - a.z
if not WildlifeWorld.lure(i, wildlife_st.wl_ask) or wildlife_st.wl_ask.kind != WILD_FEED or wildlife_st.wl_ask.diet != WILD_SEED { continue }
let dx = wildlife_st.wl_ask.x - a.x
let dz = wildlife_st.wl_ask.z - a.z
let d = Math.sqrt(dx * dx + dz * dz)
if d < bd {
bd = d
got = true
wl_copy_lure(wl_ask, wl_best)
wl_copy_lure(wildlife_st.wl_ask, wildlife_st.wl_best)
}
}
return got
}
# it ate what was set out: calm for three game hours, and the game credits whoever left it
function wl_ate(a: WildAnimal, uid: int, owner: int, diet: int) -> void {
function wl_ate(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal, uid: int, owner: int, diet: int) -> void {
a.calm = 180.0
a.fed += 1
let f = wl_fact(WILD_FED, a)
let f = wl_fact(base_st, wildlife_st, WILD_FED, a)
f.lure = uid
f.owner = owner
f.diet = diet

View file

@ -1,18 +1,18 @@
# migrate.ludic - each morning the ranges slide toward where the season wants them, and the animals
# that live in them are dragged along rather than teleported; an animal keeps inside its own
export function wildlife_ranges_migrate() -> void {
let rs = wildlife_ranges()
export function wildlife_ranges_migrate(wildlife_st: mut WildlifeState) -> void {
let rs = wildlife_ranges(wildlife_st)
let w = WildlifeWorld.winterness()
for i in 0 .. len(rs) {
rs[i].x = Math.lerp(rs[i].sx, rs[i].wx, w)
rs[i].z = Math.lerp(rs[i].sz, rs[i].wz, w)
}
let all = wildlife_all()
let all = wildlife_all(wildlife_st)
for i in 0 .. len(all) {
let a = all[i]
let r = wildlife_range(a.rg)
let r = wildlife_range(wildlife_st, a.rg)
if r == null { continue }
wl_clamp(a)
wl_clamp(wildlife_st, a)
if wildlife_range_dist(r, a.home_x, a.home_z) > r.r * 1.2 {
a.home_x = r.x
a.home_z = r.z
@ -24,8 +24,8 @@ export function wildlife_ranges_migrate() -> void {
}
# its home kept inside its range
function wl_clamp(a: WildAnimal) -> void {
let r = wildlife_range(a.rg)
function wl_clamp(wildlife_st: mut WildlifeState, a: WildAnimal) -> void {
let r = wildlife_range(wildlife_st, a.rg)
if r == null { return }
let d = wildlife_range_dist(r, a.home_x, a.home_z)
if d < r.r { return }
@ -34,26 +34,26 @@ function wl_clamp(a: WildAnimal) -> void {
}
# how many live in range `i`, legends apart
export function wildlife_range_alive(i: int) -> int {
export function wildlife_range_alive(wildlife_st: mut WildlifeState, i: int) -> int {
var n = 0
let all = wildlife_all()
let all = wildlife_all(wildlife_st)
for k in 0 .. len(all) { if all[k].alive and not all[k].legend and all[k].rg == i { n += 1 } }
return n
}
# how many are alive in the whole valley, legends apart
export function wildlife_total() -> int {
export function wildlife_total(wildlife_st: mut WildlifeState) -> int {
var n = 0
let all = wildlife_all()
let all = wildlife_all(wildlife_st)
for k in 0 .. len(all) { if all[k].alive and not all[k].legend { n += 1 } }
return n
}
# the nearest range of `sp` a player has found, to (x, z), or -1
export function wildlife_range_found_near(sp: int, x: float, z: float) -> int {
export function wildlife_range_found_near(wildlife_st: mut WildlifeState, sp: int, x: float, z: float) -> int {
var best = -1
var bd = 99999.0
let rs = wildlife_ranges()
let rs = wildlife_ranges(wildlife_st)
for i in 0 .. len(rs) {
if rs[i].sp != sp or not rs[i].found { continue }
let d = wildlife_range_dist(rs[i], x, z)

View file

@ -1,6 +1,6 @@
# needs.ludic - thirst and hunger climb with the game hours (midday is thirsty work, a run costs more
# than a graze, dawn and dusk are when they go down to drink), and a walker leaves droppings
function wl_needs(a: WildAnimal, gh: float) -> void {
function wl_needs(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal, gh: float) -> void {
let h = WildlifeWorld.hour()
var thirst = 8.0
var hunger = 5.0
@ -12,11 +12,11 @@ function wl_needs(a: WildAnimal, gh: float) -> void {
if (h > 5.0 and h < 8.0) or (h > 18.0 and h < 21.0) { thirst = thirst * 1.8 }
a.thirst = Math.min(a.thirst + thirst * gh, 100.0)
a.hunger = Math.min(a.hunger + hunger * gh, 100.0)
if not wl_sp(a.sp).tracks { return }
if not wl_sp(wildlife_st, a.sp).tracks { return }
a.scat_t = a.scat_t - gh * 60.0
if a.scat_t > 0.0 { return }
a.scat_t = 120.0 + wl_rnd() * 300.0
wl_near(a.x, a.z)
if wl_pdist(wl_near_i, a) > 500.0 { return } # further than 500 m from everyone: nobody would find it
wl_fact(WILD_SCAT, a)
a.scat_t = 120.0 + wl_rnd(wildlife_st) * 300.0
wl_near(wildlife_st, a.x, a.z)
if wl_pdist(wildlife_st.wl_near_i, a) > 500.0 { return } # further than 500 m from everyone: nobody would find it
wl_fact(base_st, wildlife_st, WILD_SCAT, a)
}

View file

@ -66,8 +66,7 @@ function wl_no_spot(a: WildAnimal, water: bool, s: WildSpot) -> bool { return fa
function wl_no_lure(i: int, l: WildLure) -> bool { return false }
function wl_nothing(a: WildAnimal) -> void { }
function wl_range_nothing(r: WildRange, i: int) -> void { }
var wl_ids: int = 0
function wl_next_id() -> int {
wl_ids += 1
return wl_ids
function wl_next_id(wildlife_st: mut WildlifeState) -> int {
wildlife_st.wl_ids += 1
return wildlife_st.wl_ids
}

View file

@ -1,16 +1,14 @@
# ranges.ludic - a species keeps to discs placed once, against the ground, 120-700 m out from where
# the valley starts; each winters lower, toward the timber, as far down as it summered high
var wl_ranges: []WildRange = null
var wl_found: []int = null # found flags a load brought before the ranges were set out
export function wildlife_ranges() -> []WildRange {
if wl_ranges == null { wl_ranges = new []WildRange }
return wl_ranges
export function wildlife_ranges(wildlife_st: mut WildlifeState) -> []WildRange {
if wildlife_st.wl_ranges == null { wildlife_st.wl_ranges = new []WildRange }
return wildlife_st.wl_ranges
}
export function wildlife_range(i: int) -> WildRange {
if i < 0 or i >= len(wildlife_ranges()) { return null }
return wildlife_ranges()[i]
export function wildlife_range(wildlife_st: mut WildlifeState, i: int) -> WildRange {
if i < 0 or i >= len(wildlife_ranges(wildlife_st)) { return null }
return wildlife_ranges(wildlife_st)[i]
}
export function wildlife_range_dist(r: WildRange, x: float, z: float) -> float {
@ -19,42 +17,42 @@ export function wildlife_range_dist(r: WildRange, x: float, z: float) -> float {
return Math.sqrt(dx * dx + dz * dz)
}
function wl_range_clashes(sp: int, x: float, z: float) -> bool {
let rs = wildlife_ranges()
for j in 0 .. len(rs) { if rs[j].sp == sp and wildlife_range_dist(rs[j], x, z) < wl_sp(sp).range * 1.6 { return true } }
function wl_range_clashes(wildlife_st: mut WildlifeState, sp: int, x: float, z: float) -> bool {
let rs = wildlife_ranges(wildlife_st)
for j in 0 .. len(rs) { if rs[j].sp == sp and wildlife_range_dist(rs[j], x, z) < wl_sp(wildlife_st, sp).range * 1.6 { return true } }
return false
}
function wl_range_try(sp: int, x0: float, z0: float) -> bool {
let s = wl_sp(sp)
function wl_range_try(wildlife_st: mut WildlifeState, sp: int, x0: float, z0: float) -> bool {
let s = wl_sp(wildlife_st, sp)
for tries in 0 .. 80 {
if not wildlife_find_spot(s.habitat, x0, z0, 120.0, 700.0) { continue }
let dx = wl_px - x0
let dz = wl_pz - z0
if Math.sqrt(dx * dx + dz * dz) < s.keep_away or wl_range_clashes(sp, wl_px, wl_pz) { continue }
if not wildlife_find_spot(wildlife_st, s.habitat, x0, z0, 120.0, 700.0) { continue }
let dx = wildlife_st.wl_px - x0
let dz = wildlife_st.wl_pz - z0
if Math.sqrt(dx * dx + dz * dz) < s.keep_away or wl_range_clashes(wildlife_st, sp, wildlife_st.wl_px, wildlife_st.wl_pz) { continue }
let r = new WildRange
r.sp = sp
r.x = wl_px
r.z = wl_pz
r.x = wildlife_st.wl_px
r.z = wildlife_st.wl_pz
r.r = s.range
r.sx = wl_px
r.sz = wl_pz
r.sx = wildlife_st.wl_px
r.sz = wildlife_st.wl_pz
wl_pick_winter(r)
push(wildlife_ranges(), r)
push(wildlife_ranges(wildlife_st), r)
return true
}
return false
}
# every species' ranges about (x0, z0), then the game told of each (WildlifeWorld.placed)
export function wildlife_ranges_setup(x0: float, z0: float) -> void {
wildlife_seed(613)
wl_ranges = new []WildRange
for sp in 0 .. wildlife_species_count() {
for k in 0 .. WildlifeWorld.ranges(sp) { if not wl_range_try(sp, x0, z0) { break } }
export function wildlife_ranges_setup(wildlife_st: mut WildlifeState, x0: float, z0: float) -> void {
wildlife_seed(wildlife_st, 613)
wildlife_st.wl_ranges = new []WildRange
for sp in 0 .. wildlife_species_count(wildlife_st) {
for k in 0 .. WildlifeWorld.ranges(sp) { if not wl_range_try(wildlife_st, sp, x0, z0) { break } }
}
wl_found_apply()
let rs = wildlife_ranges()
wl_found_apply(wildlife_st)
let rs = wildlife_ranges(wildlife_st)
for i in 0 .. len(rs) { WildlifeWorld.placed(rs[i], i) }
}
@ -85,13 +83,13 @@ function wl_pick_winter(r: WildRange) -> void {
}
# a player stood at (x, z): any range they are inside is found
export function wildlife_ranges_visit(x: float, z: float) -> void {
let rs = wildlife_ranges()
export function wildlife_ranges_visit(wildlife_st: mut WildlifeState, x: float, z: float) -> void {
let rs = wildlife_ranges(wildlife_st)
for i in 0 .. len(rs) { if not rs[i].found and wildlife_range_dist(rs[i], x, z) < rs[i].r { rs[i].found = true } }
}
function wl_found_apply() -> void {
if wl_found == null { return }
let rs = wildlife_ranges()
for i in 0 .. len(rs) { if i < len(wl_found) { rs[i].found = wl_found[i] != 0 } }
function wl_found_apply(wildlife_st: mut WildlifeState) -> void {
if wildlife_st.wl_found == null { return }
let rs = wildlife_ranges(wildlife_st)
for i in 0 .. len(rs) { if i < len(wildlife_st.wl_found) { rs[i].found = wildlife_st.wl_found[i] != 0 } }
}

View file

@ -1,27 +1,27 @@
# react.ludic - alert (it stops and looks: the window the camera wants), wary (it puts ground between
# you without running) and fleeing; true when it is doing one of them, which is all it does this tick
function wl_react(a: WildAnimal, np: int, dt: float) -> bool {
let s = wl_sp(a.sp)
function wl_react(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal, np: int, dt: float) -> bool {
let s = wl_sp(wildlife_st, a.sp)
if a.state == WILD_ALERT {
a.speed = 0.0
wl_face(a, a.sus_x, a.sus_z, 2.2, dt)
if a.timer < 0.0 { wl_wander(a, 3.0 + wl_rnd() * 4.0) }
if a.timer < 0.0 { wl_wander(wildlife_st, a, 3.0 + wl_rnd(wildlife_st) * 4.0) }
return true
}
if a.state == WILD_WARY {
wl_face(a, a.x + (a.x - a.sus_x), a.z + (a.z - a.sus_z), 2.6, dt)
a.speed = s.walk * 0.8
wl_step(a, dt)
if a.timer < 0.0 { wl_wander(a, 3.0 + wl_rnd() * 4.0) }
wl_step(base_st, wildlife_st, a, dt)
if a.timer < 0.0 { wl_wander(wildlife_st, a, 3.0 + wl_rnd(wildlife_st) * 4.0) }
return true
}
if a.state == WILD_FLEE {
wl_face(a, a.x + (a.x - WildlifeWorld.player_x(np)), a.z + (a.z - WildlifeWorld.player_z(np)), 5.0, dt)
a.speed = s.run
wl_step(a, dt)
wl_step(base_st, wildlife_st, a, dt)
if a.timer < 0.0 {
wl_rehome(a)
wl_wander(a, 5.0)
wl_rehome(wildlife_st, a)
wl_wander(wildlife_st, a, 5.0)
}
return true
}
@ -29,8 +29,8 @@ function wl_react(a: WildAnimal, np: int, dt: float) -> bool {
}
# walking to (or reached) something set out; true while it is
function wl_approach(a: WildAnimal, dt: float) -> bool {
if (a.state == WILD_IDLE or a.state == WILD_WANDER) and wl_attraction(a) { wl_take_lure(a) }
function wl_approach(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal, dt: float) -> bool {
if (a.state == WILD_IDLE or a.state == WILD_WANDER) and wl_attraction(wildlife_st, a) { wl_take_lure(wildlife_st, a) }
if a.state != WILD_APPROACH { return false }
if a.lure == 0 or not WildlifeWorld.present(a.lure) {
a.lure = 0
@ -39,20 +39,20 @@ function wl_approach(a: WildAnimal, dt: float) -> bool {
return true
}
wl_face(a, a.lure_x, a.lure_z, 3.0, dt)
a.speed = wl_sp(a.sp).walk
a.speed = wl_sp(wildlife_st, a.sp).walk
let dx = a.lure_x - a.x
let dz = a.lure_z - a.z
if Math.sqrt(dx * dx + dz * dz) < 1.3 {
a.speed = 0.0
wl_arrive(a)
wl_arrive(base_st, wildlife_st, a)
return true
}
wl_step(a, dt)
wl_step(base_st, wildlife_st, a, dt)
return true
}
# at a snare it is held; at food it eats for 14 s; at a lure it stands curious for 9
function wl_arrive(a: WildAnimal) -> void {
function wl_arrive(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal) -> void {
a.state = WILD_EAT
a.timer = 9.0
if a.lure_kind == WILD_FEED { a.timer = 14.0 }
@ -61,19 +61,19 @@ function wl_arrive(a: WildAnimal) -> void {
a.x = a.lure_x
a.z = a.lure_z
a.y = WildlifeWorld.ground(a.x, a.z)
let f = wl_fact(WILD_CAUGHT, a)
let f = wl_fact(base_st, wildlife_st, WILD_CAUGHT, a)
f.lure = a.lure
f.owner = a.lure_owner
}
# eating what it came to; when it is done, food is credited and it wanders off
function wl_eat(a: WildAnimal) -> bool {
function wl_eat(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal) -> bool {
if a.state != WILD_EAT { return false }
a.speed = 0.0
if a.timer < 0.0 {
if a.lure != 0 and a.lure_kind == WILD_FEED and WildlifeWorld.present(a.lure) { wl_ate(a, a.lure, a.lure_owner, wl_sp(a.sp).diet) }
if a.lure != 0 and a.lure_kind == WILD_FEED and WildlifeWorld.present(a.lure) { wl_ate(base_st, wildlife_st, a, a.lure, a.lure_owner, wl_sp(wildlife_st, a.sp).diet) }
a.lure = 0
wl_wander(a, 4.0)
wl_wander(wildlife_st, a, 4.0)
}
return true
}

View file

@ -1,34 +1,31 @@
# record.ludic - what this player has met: the species seen up close and the species fed, as masks
# by species number, and whether anything has been fed at all
var wl_seen: int = 0
var wl_fed: int = 0
var wl_fed_any: bool = false
export function wildlife_seen(sp: int) -> bool { return (wl_seen & (1 << sp)) != 0 }
export function wildlife_seen_mask() -> int { return wl_seen }
export function wildlife_fed_mask() -> int { return wl_fed }
export function wildlife_fed_any() -> bool { return wl_fed_any }
export function wildlife_seen(wildlife_st: WildlifeState, sp: int) -> bool { return (wildlife_st.wl_seen & (1 << sp)) != 0 }
export function wildlife_seen_mask(wildlife_st: WildlifeState) -> int { return wildlife_st.wl_seen }
export function wildlife_fed_mask(wildlife_st: WildlifeState) -> int { return wildlife_st.wl_fed }
export function wildlife_fed_any(wildlife_st: WildlifeState) -> bool { return wildlife_st.wl_fed_any }
# seen up close (a guest's own pass, droppings read, a sighting here); true the first time
export function wildlife_saw(sp: int) -> bool {
if wildlife_seen(sp) { return false }
wl_seen = wl_seen | (1 << sp)
export function wildlife_saw(wildlife_st: mut WildlifeState, sp: int) -> bool {
if wildlife_seen(wildlife_st, sp) { return false }
wildlife_st.wl_seen = wildlife_st.wl_seen | (1 << sp)
return true
}
export function wildlife_set_seen(mask: int) -> void { wl_seen = mask }
export function wildlife_set_seen(wildlife_st: mut WildlifeState, mask: int) -> void { wildlife_st.wl_seen = mask }
# a species fed, credited to this player
export function wildlife_mark_fed(sp: int) -> void {
wl_fed = wl_fed | (1 << sp)
wl_fed_any = true
export function wildlife_mark_fed(wildlife_st: mut WildlifeState, sp: int) -> void {
wildlife_st.wl_fed = wildlife_st.wl_fed | (1 << sp)
wildlife_st.wl_fed_any = true
}
# nothing fed yet (a test that feeds again), or the flag as given
export function wildlife_set_fed_any(on: bool) -> void { wl_fed_any = on }
export function wildlife_set_fed_any(wildlife_st: mut WildlifeState, on: bool) -> void { wildlife_st.wl_fed_any = on }
function wl_record_reset() -> void {
wl_seen = 0
wl_fed = 0
wl_fed_any = false
function wl_record_reset(wildlife_st: mut WildlifeState) -> void {
wildlife_st.wl_seen = 0
wildlife_st.wl_fed = 0
wildlife_st.wl_fed_any = false
}

View file

@ -1,12 +1,12 @@
# run.ludic - one step of every animal: the port says whether it is about at this hour; anything
# within 900 m of a player thinks; the gait follows the ground covered; player 0 sees what is near
export function wildlife_tick(dt: float, gh: float) -> void {
wl_seed_scan(dt)
let all = wildlife_all()
for i in 0 .. len(all) { wl_tick_one(all[i], dt, gh) }
export function wildlife_tick(base_st: mut BaseState, wildlife_st: mut WildlifeState, dt: float, gh: float) -> void {
wl_seed_scan(wildlife_st, dt)
let all = wildlife_all(wildlife_st)
for i in 0 .. len(all) { wl_tick_one(base_st, wildlife_st, all[i], dt, gh) }
}
function wl_tick_one(a: WildAnimal, dt: float, gh: float) -> void {
function wl_tick_one(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal, dt: float, gh: float) -> void {
if not a.alive { return }
let about = WildlifeWorld.about(a)
if about == WILD_AWAY {
@ -15,26 +15,26 @@ function wl_tick_one(a: WildAnimal, dt: float, gh: float) -> void {
}
if about == WILD_GONE {
wildlife_remove(a)
wl_fact(WILD_WENT, a)
wl_fact(base_st, wildlife_st, WILD_WENT, a)
return
}
a.shown = true
let dh = wl_near(a.x, a.z)
let dh = wl_near(wildlife_st, a.x, a.z)
if dh > 900.0 { return }
let s = wl_sp(a.sp)
if wl_hold { }
let s = wl_sp(wildlife_st, a.sp)
if wildlife_st.wl_hold { }
else if a.pinned { a.speed = 0.0 }
else if s.flies { wildlife_tick_bird(a, dt) } else { wildlife_tick_ground(a, dt, gh) }
else if s.flies { wildlife_tick_bird(base_st, wildlife_st, a, dt) } else { wildlife_tick_ground(base_st, wildlife_st, a, dt, gh) }
var stride = 1.2 * a.scale
if s.flies { stride = 0.6 }
a.phase = (a.phase + a.speed * dt / stride * WL_TAU) % WL_TAU
if WildlifeWorld.player_on(0) and wl_pdist(0, a) < 60.0 and wildlife_saw(a.sp) { wl_fact(WILD_SPOTTED, a) }
if WildlifeWorld.player_on(0) and wl_pdist(0, a) < 60.0 and wildlife_saw(wildlife_st, a.sp) { wl_fact(base_st, wildlife_st, WILD_SPOTTED, a) }
}
# the gait advanced by a copy the game moves itself (a guest's copy of the host's animal)
export function wildlife_stride(a: WildAnimal, dt: float) -> void {
export function wildlife_stride(wildlife_st: mut WildlifeState, a: WildAnimal, dt: float) -> void {
var stride = 1.2 * a.scale
if wl_sp(a.sp).flies { stride = 0.6 }
if wl_sp(wildlife_st, a.sp).flies { stride = 0.6 }
a.phase = (a.phase + a.speed * dt / stride * WL_TAU) % WL_TAU
}
@ -48,8 +48,8 @@ export function wildlife_release(a: WildAnimal) -> void {
}
# the animal a snare holds, by the snare's uid
export function wildlife_in_snare(uid: int) -> WildAnimal {
let all = wildlife_all()
export function wildlife_in_snare(wildlife_st: mut WildlifeState, uid: int) -> WildAnimal {
let all = wildlife_all(wildlife_st)
for i in 0 .. len(all) {
let a = all[i]
if a.alive and a.state == WILD_TRAPPED and a.lure == uid { return a }

View file

@ -1,18 +1,17 @@
# senses.ludic - what an animal makes of one player: sight (a clear line, closer is worse, the dark
# helps), scent (downwind, further than sight), noise; the rise is NET of how fast it forgets, so
# an alert settles where the distance puts it instead of climbing to the top and sticking
var wl_near_i: int = 0
# the nearest player's distance; `wl_near_i` says who
function wl_near(x: float, z: float) -> float {
function wl_near(wildlife_st: mut WildlifeState, x: float, z: float) -> float {
var best = 999999999999.0
wl_near_i = 0
wildlife_st.wl_near_i = 0
for p in 0 .. WildlifeWorld.players() {
if not WildlifeWorld.player_on(p) { continue }
let d = wl_pdist2(p, x, z)
if d < best {
best = d
wl_near_i = p
wildlife_st.wl_near_i = p
}
}
return Math.sqrt(best)
@ -47,8 +46,8 @@ function wl_near_sq(d: float, reach: float) -> float {
}
# alert points a second from player p at `dh` metres; 0 when nothing of them reaches it
export function wildlife_alert_rate(a: WildAnimal, p: int, dh: float) -> float {
let s = wl_sp(a.sp)
export function wildlife_alert_rate(wildlife_st: mut WildlifeState, a: WildAnimal, p: int, dh: float) -> float {
let s = wl_sp(wildlife_st, a.sp)
if s.tame or a.calm > 0.0 { return 0.0 }
if (a.state == WILD_APPROACH or a.state == WILD_EAT) and a.lure != 0 and a.lure_kind == WILD_FEED and dh > 5.0 and WildlifeWorld.taught() { return 0.0 }
var reach = s.flee * WildlifeWorld.wariness()
@ -66,8 +65,8 @@ export function wildlife_alert_rate(a: WildAnimal, p: int, dh: float) -> float {
}
# how fast it forgets player p: standing still buys it, sitting down buys more
export function wildlife_alert_decay(a: WildAnimal, p: int) -> float {
var d = wl_sp(a.sp).forget
export function wildlife_alert_decay(wildlife_st: mut WildlifeState, a: WildAnimal, p: int) -> float {
var d = wl_sp(wildlife_st, a.sp).forget
let still = WildlifeWorld.stillness(p)
if still == WILD_SITTING { d = d * 2.5 } else if still == WILD_STILL { d = d * 1.6 }
if a.spook > 0.0 { d = d * 0.7 }
@ -75,14 +74,14 @@ export function wildlife_alert_decay(a: WildAnimal, p: int) -> float {
}
# the alert rises and falls with the player who nets it the most, and it watches them
function wl_alert(a: WildAnimal, np: int, dh: float, dt: float) -> void {
var rise = wildlife_alert_rate(a, np, dh)
var fall = wildlife_alert_decay(a, np)
function wl_alert(wildlife_st: mut WildlifeState, a: WildAnimal, np: int, dh: float, dt: float) -> void {
var rise = wildlife_alert_rate(wildlife_st, a, np, dh)
var fall = wildlife_alert_decay(wildlife_st, a, np)
var who = np
for p in 0 .. WildlifeWorld.players() {
if p == np or not WildlifeWorld.player_on(p) { continue }
let r2 = wildlife_alert_rate(a, p, wl_pdist(p, a))
let f2 = wildlife_alert_decay(a, p)
let r2 = wildlife_alert_rate(wildlife_st, a, p, wl_pdist(p, a))
let f2 = wildlife_alert_decay(wildlife_st, a, p)
if r2 - f2 > rise - fall {
rise = r2
fall = f2

View file

@ -1,21 +1,21 @@
# spawn.ludic - a valley filled: ranges set out, then each range to its target on its own habitat.
# Each morning the ranges move with the season and every range under its target gets ONE back,
# out of every player's sight: a valley recovers, it does not refill.
export function wildlife_spawn(x0: float, z0: float) -> void {
wildlife_ranges_setup(x0, z0)
wildlife_seed(97)
let rs = wildlife_ranges()
for i in 0 .. len(rs) { wl_fill(i, WildlifeWorld.per_range(rs[i].sp)) }
wildlife_ranges_migrate()
export function wildlife_spawn(wildlife_st: mut WildlifeState, x0: float, z0: float) -> void {
wildlife_ranges_setup(wildlife_st, x0, z0)
wildlife_seed(wildlife_st, 97)
let rs = wildlife_ranges(wildlife_st)
for i in 0 .. len(rs) { wl_fill(wildlife_st, i, WildlifeWorld.per_range(rs[i].sp)) }
wildlife_ranges_migrate(wildlife_st)
}
# up to `want` more in range i; returns how many went in
function wl_fill(i: int, want: int) -> int {
let r = wildlife_range(i)
function wl_fill(wildlife_st: mut WildlifeState, i: int, want: int) -> int {
let r = wildlife_range(wildlife_st, i)
var made = 0
for k in 0 .. want {
if not wildlife_find_spot(wl_sp(r.sp).habitat, r.x, r.z, 0.0, r.r) { continue }
let a = wildlife_new(r.sp, wl_px, wl_pz, false)
if not wildlife_find_spot(wildlife_st, wl_sp(wildlife_st, r.sp).habitat, r.x, r.z, 0.0, r.r) { continue }
let a = wildlife_new(wildlife_st, r.sp, wildlife_st.wl_px, wildlife_st.wl_pz, false)
if a != null {
a.rg = i
made += 1
@ -25,22 +25,22 @@ function wl_fill(i: int, want: int) -> int {
}
# each morning: the ranges slide with the season, and the valley gets back what it lost
export function wildlife_morning() -> int {
wildlife_ranges_migrate()
return wildlife_repopulate()
export function wildlife_morning(wildlife_st: mut WildlifeState) -> int {
wildlife_ranges_migrate(wildlife_st)
return wildlife_repopulate(wildlife_st)
}
# one newcomer for each range under its target, never within 200 m of a player; how many came
export function wildlife_repopulate() -> int {
let rs = wildlife_ranges()
export function wildlife_repopulate(wildlife_st: mut WildlifeState) -> int {
let rs = wildlife_ranges(wildlife_st)
var added = 0
for i in 0 .. len(rs) {
let want = WildlifeWorld.per_range(rs[i].sp)
if want == 0 or wildlife_range_alive(i) >= want { continue }
if want == 0 or wildlife_range_alive(wildlife_st, i) >= want { continue }
for t in 0 .. 12 {
if not wildlife_find_spot(wl_sp(rs[i].sp).habitat, rs[i].x, rs[i].z, 0.0, rs[i].r) { continue }
if wl_near(wl_px, wl_pz) < 200.0 { continue }
let a = wildlife_new(rs[i].sp, wl_px, wl_pz, false)
if not wildlife_find_spot(wildlife_st, wl_sp(wildlife_st, rs[i].sp).habitat, rs[i].x, rs[i].z, 0.0, rs[i].r) { continue }
if wl_near(wildlife_st, wildlife_st.wl_px, wildlife_st.wl_pz) < 200.0 { continue }
let a = wildlife_new(wildlife_st, rs[i].sp, wildlife_st.wl_px, wildlife_st.wl_pz, false)
if a != null {
a.rg = i
added += 1

View file

@ -32,26 +32,48 @@ export property WildSpecies {
scale: float = 1.0 # its size, times
}
var wl_species: []WildSpecies = null
function wl_sp_all() -> []WildSpecies {
if wl_species == null { wl_species = new []WildSpecies }
return wl_species
export state WildlifeState {
wl_facts: Queue<WildFact> = null
wl_own: []WildAnimal = null
wl_px: float = 0.0
wl_pz: float = 0.0
wl_ask: WildLure = null
wl_best: WildLure = null
wl_seed_any: bool = false
wl_seed_t: float = 0.0
wl_ids: int = 0
wl_ranges: []WildRange = null
wl_found: []int = null # found flags a load brought before the ranges were set out
wl_seen: int = 0
wl_fed: int = 0
wl_fed_any: bool = false
wl_near_i: int = 0
wl_species: []WildSpecies = null
wl_list: []WildAnimal = null
wl_keys: int = 0
wl_dice: Rng = null
wl_hold: bool = false # staged: nobody thinks, everybody keeps their speed
wl_spot_ask: WildSpot = null
}
export function wildlife_species_clear() -> void { wl_species = new []WildSpecies }
function wl_sp_all(wildlife_st: mut WildlifeState) -> []WildSpecies {
if wildlife_st.wl_species == null { wildlife_st.wl_species = new []WildSpecies }
return wildlife_st.wl_species
}
export function wildlife_species_clear(wildlife_st: mut WildlifeState) -> void { wildlife_st.wl_species = new []WildSpecies }
# the next species; returns its number
export function wildlife_species_add(s: WildSpecies) -> int {
push(wl_sp_all(), s)
return len(wl_sp_all()) - 1
export function wildlife_species_add(wildlife_st: mut WildlifeState, s: WildSpecies) -> int {
push(wl_sp_all(wildlife_st), s)
return len(wl_sp_all(wildlife_st)) - 1
}
export function wildlife_species_count() -> int { return len(wl_sp_all()) }
export function wildlife_species_count(wildlife_st: mut WildlifeState) -> int { return len(wl_sp_all(wildlife_st)) }
export function wildlife_species(sp: int) -> WildSpecies {
if sp < 0 or sp >= len(wl_sp_all()) { return null }
return wl_sp_all()[sp]
export function wildlife_species(wildlife_st: mut WildlifeState, sp: int) -> WildSpecies {
if sp < 0 or sp >= len(wl_sp_all(wildlife_st)) { return null }
return wl_sp_all(wildlife_st)[sp]
}
function wl_sp(sp: int) -> WildSpecies { return wl_sp_all()[sp] }
function wl_sp(wildlife_st: mut WildlifeState, sp: int) -> WildSpecies { return wl_sp_all(wildlife_st)[sp] }

View file

@ -14,7 +14,7 @@ function wl_face(a: WildAnimal, tx: float, tz: float, rate: float, dt: float) ->
}
# true when it moved
function wl_step(a: WildAnimal, dt: float) -> bool {
function wl_step(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal, dt: float) -> bool {
let d = a.speed * dt
if d < 0.0001 { return false }
let nx = a.x - Math.sin(a.yaw) * d
@ -29,16 +29,16 @@ function wl_step(a: WildAnimal, dt: float) -> bool {
a.z = nz
a.y = nh
a.track_d = a.track_d + d
if wl_sp(a.sp).tracks and a.track_d > 2.8 {
if wl_sp(wildlife_st, a.sp).tracks and a.track_d > 2.8 {
a.track_d = 0.0
wl_fact(WILD_PRINT, a)
wl_fact(base_st, wildlife_st, WILD_PRINT, a)
}
return true
}
function wl_pick_target(a: WildAnimal, radius: float) -> void {
let ang = wl_rnd() * WL_TAU
let d = wl_rnd() * radius
function wl_pick_target(wildlife_st: mut WildlifeState, a: WildAnimal, radius: float) -> void {
let ang = wl_rnd(wildlife_st) * WL_TAU
let d = wl_rnd(wildlife_st) * radius
a.tx = a.home_x + Math.cos(ang) * d
a.tz = a.home_z + Math.sin(ang) * d
}
@ -50,15 +50,15 @@ function wl_at_target(a: WildAnimal, r: float) -> bool {
}
# back to wandering about home after `t` seconds of whatever it was doing
function wl_wander(a: WildAnimal, t: float) -> void {
function wl_wander(wildlife_st: mut WildlifeState, a: WildAnimal, t: float) -> void {
a.state = WILD_WANDER
a.timer = t
wl_pick_target(a, wl_sp(a.sp).home)
wl_pick_target(wildlife_st, a, wl_sp(wildlife_st, a.sp).home)
}
# home where it stands now, kept inside its range
function wl_rehome(a: WildAnimal) -> void {
function wl_rehome(wildlife_st: mut WildlifeState, a: WildAnimal) -> void {
a.home_x = a.x
a.home_z = a.z
wl_clamp(a)
wl_clamp(wildlife_st, a)
}

View file

@ -1,37 +1,33 @@
# store.ludic - the animals, their own dice, and the verbs that make and take one away
var wl_list: []WildAnimal = null
var wl_keys: int = 0
var wl_dice: Rng = null
var wl_hold: bool = false # staged: nobody thinks, everybody keeps their speed
const WL_TAU: float = 6.2831853
export function wildlife_all() -> []WildAnimal {
if wl_list == null { wl_list = new []WildAnimal }
return wl_list
export function wildlife_all(wildlife_st: mut WildlifeState) -> []WildAnimal {
if wildlife_st.wl_list == null { wildlife_st.wl_list = new []WildAnimal }
return wildlife_st.wl_list
}
export function wildlife_count_all() -> int { return len(wildlife_all()) }
export function wildlife_count_all(wildlife_st: mut WildlifeState) -> int { return len(wildlife_all(wildlife_st)) }
# a new valley: every animal forgotten, quietly (the game has already let its drawings go)
export function wildlife_clear() -> void { wl_list = new []WildAnimal }
export function wildlife_clear(wildlife_st: mut WildlifeState) -> void { wildlife_st.wl_list = new []WildAnimal }
function wl_rng() -> Rng {
if wl_dice == null { wl_dice = rng_new(97) }
return wl_dice
function wl_rng(wildlife_st: mut WildlifeState) -> Rng {
if wildlife_st.wl_dice == null { wildlife_st.wl_dice = rng_new(97) }
return wildlife_st.wl_dice
}
function wl_rnd() -> float { return rng_float(wl_rng()) }
function wl_rnd(wildlife_st: mut WildlifeState) -> float { return rng_float(wl_rng(wildlife_st)) }
# the package's dice from a known place (a spawn, a test)
export function wildlife_seed(seed: int) -> void { rng_seed(wl_rng(), seed) }
export function wildlife_seed(wildlife_st: mut WildlifeState, seed: int) -> void { rng_seed(wl_rng(wildlife_st), seed) }
# an individual exactly as given: the host's own, or the host's on a guest
export function wildlife_make(sp: int, x: float, z: float, legend: bool, yaw: float, variant: int, scale: float, nid: int) -> WildAnimal {
let s = wildlife_species(sp)
export function wildlife_make(wildlife_st: mut WildlifeState, sp: int, x: float, z: float, legend: bool, yaw: float, variant: int, scale: float, nid: int) -> WildAnimal {
let s = wildlife_species(wildlife_st, sp)
if s == null { return null }
let a = new WildAnimal
wl_keys += 1
a.key = wl_keys
wildlife_st.wl_keys += 1
a.key = wildlife_st.wl_keys
a.nid = nid
a.sp = sp
a.x = x
@ -46,27 +42,27 @@ export function wildlife_make(sp: int, x: float, z: float, legend: bool, yaw: fl
a.scale = scale
a.legend = legend
a.state = WILD_IDLE
push(wildlife_all(), a)
push(wildlife_all(wildlife_st), a)
WildlifeWorld.born(a)
return a
}
# a new individual of a species at (x, z), off the package's dice
export function wildlife_new(sp: int, x: float, z: float, legend: bool) -> WildAnimal {
let s = wildlife_species(sp)
export function wildlife_new(wildlife_st: mut WildlifeState, sp: int, x: float, z: float, legend: bool) -> WildAnimal {
let s = wildlife_species(wildlife_st, sp)
if s == null { return null }
let yaw = wl_rnd() * WL_TAU
let variant = rng_between(wl_rng(), 0, 11)
let scale = s.scale * (0.88 + wl_rnd() * 0.24)
let a = wildlife_make(sp, x, z, legend, yaw, variant, scale, WildlifeWorld.next_id())
a.thirst = wl_rnd() * 50.0
a.hunger = wl_rnd() * 50.0
a.scat_t = 40.0 + wl_rnd() * 180.0
let yaw = wl_rnd(wildlife_st) * WL_TAU
let variant = rng_between(wl_rng(wildlife_st), 0, 11)
let scale = s.scale * (0.88 + wl_rnd(wildlife_st) * 0.24)
let a = wildlife_make(wildlife_st, sp, x, z, legend, yaw, variant, scale, WildlifeWorld.next_id())
a.thirst = wl_rnd(wildlife_st) * 50.0
a.hunger = wl_rnd(wildlife_st) * 50.0
a.scat_t = 40.0 + wl_rnd(wildlife_st) * 180.0
if s.flies {
a.state = WILD_FLY
a.fly_h = 10.0 + wl_rnd() * 20.0
a.fly_h = 10.0 + wl_rnd(wildlife_st) * 20.0
}
a.timer = wl_rnd() * 6.0
a.timer = wl_rnd(wildlife_st) * 6.0
return a
}
@ -78,18 +74,18 @@ export function wildlife_remove(a: WildAnimal) -> void {
}
# staged for a picture: nobody thinks, and everybody keeps the speed they were given
export function wildlife_hold(on: bool) -> void { wl_hold = on }
export function wildlife_hold(wildlife_st: mut WildlifeState, on: bool) -> void { wildlife_st.wl_hold = on }
export function wildlife_by_nid(nid: int) -> WildAnimal {
let all = wildlife_all()
export function wildlife_by_nid(wildlife_st: mut WildlifeState, nid: int) -> WildAnimal {
let all = wildlife_all(wildlife_st)
for i in 0 .. len(all) { if all[i].nid == nid { return all[i] } }
return null
}
# how many of a species are alive, legends apart
export function wildlife_count(sp: int) -> int {
export function wildlife_count(wildlife_st: mut WildlifeState, sp: int) -> int {
var n = 0
let all = wildlife_all()
let all = wildlife_all(wildlife_st)
for i in 0 .. len(all) { if all[i].alive and all[i].sp == sp and not all[i].legend { n += 1 } }
return n
}

View file

@ -1,21 +1,21 @@
# system.ludic - the wildlife as a system: its save section is this player's record (the species
# seen and fed) and which ranges they have found; the animals themselves are respawned with the
# valley, so a reset forgets the record and nothing else
export function wildlife_reset() -> void {
wl_record_reset()
wl_found = null
let rs = wildlife_ranges()
export function wildlife_reset(base_st: mut BaseState, wildlife_st: mut WildlifeState) -> void {
wl_record_reset(wildlife_st)
wildlife_st.wl_found = null
let rs = wildlife_ranges(wildlife_st)
for i in 0 .. len(rs) { rs[i].found = false }
q_clear(wildlife_facts())
q_clear(base_st, wildlife_facts(base_st, wildlife_st))
}
export function wildlife_save() -> Val {
export function wildlife_save(wildlife_st: mut WildlifeState) -> Val {
let v = Value.object()
sv_put_int(v, "seen", wl_seen)
sv_put_int(v, "fed", wl_fed)
sv_put_bool(v, "fed_any", wl_fed_any)
sv_put_int(v, "seen", wildlife_st.wl_seen)
sv_put_int(v, "fed", wildlife_st.wl_fed)
sv_put_bool(v, "fed_any", wildlife_st.wl_fed_any)
let found = new []int
let rs = wildlife_ranges()
let rs = wildlife_ranges(wildlife_st)
for i in 0 .. len(rs) {
var f = 0
if rs[i].found { f = 1 }
@ -25,13 +25,13 @@ export function wildlife_save() -> Val {
return v
}
export function wildlife_load(v: Val, version: int) -> void {
wildlife_reset()
wl_seen = sv_int(v, "seen", 0)
wl_fed = sv_int(v, "fed", 0)
wl_fed_any = sv_bool(v, "fed_any", false)
wl_found = sv_ints(v, "found")
wl_found_apply()
export function wildlife_load(base_st: mut BaseState, wildlife_st: mut WildlifeState, v: Val, version: int) -> void {
wildlife_reset(base_st, wildlife_st)
wildlife_st.wl_seen = sv_int(v, "seen", 0)
wildlife_st.wl_fed = sv_int(v, "fed", 0)
wildlife_st.wl_fed_any = sv_bool(v, "fed_any", false)
wildlife_st.wl_found = sv_ints(v, "found")
wl_found_apply(wildlife_st)
}
# PH_SIMULATE with no tick of its own: the game steps it (wildlife_tick) where the world is owned

View file

@ -1,37 +1,39 @@
# fake.ludic - a toy valley for the tests: flat ground at 10 m with a lake west of x = -300 (its
# surface at 10.5), forest north of z = 400, up to two players, and a table of things set out
var fk_px: []float = null
var fk_pz: []float = null
var fk_on: []bool = null
var fk_noise: float = 0.0
var fk_scent: float = 0.0
var fk_still: int = 0
var fk_hidden: bool = false
var fk_blocked: bool = false
var fk_wind: float = 0.0
var fk_wind_dir: float = 0.0
var fk_winter: float = 0.0
var fk_about: int = 0
var fk_born: int = 0
var fk_lures: []WildLure = null
var fk_gone: int = 0 # a uid the port says is no longer there
export state WildlifeTestsFakeState {
fk_px: []float = null
fk_pz: []float = null
fk_on: []bool = null
fk_noise: float = 0.0
fk_scent: float = 0.0
fk_still: int = 0
fk_hidden: bool = false
fk_blocked: bool = false
fk_wind: float = 0.0
fk_wind_dir: float = 0.0
fk_winter: float = 0.0
fk_about: int = 0
fk_born: int = 0
fk_lures: []WildLure = null
fk_gone: int = 0 # a uid the port says is no longer there
}
function fk_setup() -> void {
fk_px = floats(2)
fk_pz = floats(2)
fk_on = new []bool
push(fk_on, true)
push(fk_on, false)
fk_px[0] = 5000.0
fk_pz[0] = 5000.0
fk_lures = new []WildLure
wildlife_clear()
wildlife_species_clear()
fk_species()
function fk_setup(wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) -> void {
wildlife_tests_fake_st.fk_px = floats(2)
wildlife_tests_fake_st.fk_pz = floats(2)
wildlife_tests_fake_st.fk_on = new []bool
push(wildlife_tests_fake_st.fk_on, true)
push(wildlife_tests_fake_st.fk_on, false)
wildlife_tests_fake_st.fk_px[0] = 5000.0
wildlife_tests_fake_st.fk_pz[0] = 5000.0
wildlife_tests_fake_st.fk_lures = new []WildLure
wildlife_clear(wildlife_st)
wildlife_species_clear(wildlife_st)
fk_species(wildlife_st)
}
# 0 deer (meadow, tracks, medium), 1 bear (forest, charges), 2 hare (small), 3 jay (flies)
function fk_species() -> void {
function fk_species(wildlife_st: mut WildlifeState) -> void {
let d = new WildSpecies
d.key = "deer"
d.walk = 1.1
@ -41,7 +43,7 @@ function fk_species() -> void {
d.size = 1
d.range = 150.0
d.keep_away = 120.0
wildlife_species_add(d)
wildlife_species_add(wildlife_st, d)
let b = new WildSpecies
b.key = "bear"
b.habitat = WILD_FOREST
@ -50,10 +52,10 @@ function fk_species() -> void {
b.diet = WILD_FISH
b.forget = 1.6
b.run = 6.0
wildlife_species_add(b)
wildlife_species_add(wildlife_st, b)
let h = new WildSpecies
h.key = "hare"
wildlife_species_add(h)
wildlife_species_add(wildlife_st, h)
let j = new WildSpecies
j.key = "jay"
j.flies = true
@ -61,7 +63,7 @@ function fk_species() -> void {
j.flee = 8.0
j.land_shy = 0.0
j.land_chance = 1.0
wildlife_species_add(j)
wildlife_species_add(wildlife_st, j)
}
function fk_ground(x: float, z: float) -> float { return 10.0 }
@ -70,24 +72,24 @@ function fk_water(x: float, z: float) -> float {
return 0.0
}
function fk_forest(x: float, z: float) -> bool { return z > 400.0 }
function fk_sight(ax: float, ay: float, az: float, bx: float, by: float, bz: float) -> bool { return not fk_blocked }
function fk_sight(wildlife_tests_fake_st: WildlifeTestsFakeState, ax: float, ay: float, az: float, bx: float, by: float, bz: float) -> bool { return not wildlife_tests_fake_st.fk_blocked }
function fk_players() -> int { return 2 }
function fk_player_on(p: int) -> bool { return fk_on[p] }
function fk_x(p: int) -> float { return fk_px[p] }
function fk_player_on(wildlife_tests_fake_st: WildlifeTestsFakeState, p: int) -> bool { return wildlife_tests_fake_st.fk_on[p] }
function fk_x(wildlife_tests_fake_st: WildlifeTestsFakeState, p: int) -> float { return wildlife_tests_fake_st.fk_px[p] }
function fk_y(p: int) -> float { return 10.0 }
function fk_z(p: int) -> float { return fk_pz[p] }
function fk_is_hidden(p: int) -> bool { return fk_hidden }
function fk_noise_of(p: int) -> float { return fk_noise }
function fk_scent_of(p: int) -> float { return fk_scent }
function fk_still_of(p: int) -> int { return fk_still }
function fk_wind_of() -> float { return fk_wind }
function fk_wind_to() -> float { return fk_wind_dir }
function fk_winter_of() -> float { return fk_winter }
function fk_about_of(a: WildAnimal) -> int { return fk_about }
function fk_z(wildlife_tests_fake_st: WildlifeTestsFakeState, p: int) -> float { return wildlife_tests_fake_st.fk_pz[p] }
function fk_is_hidden(wildlife_tests_fake_st: WildlifeTestsFakeState, p: int) -> bool { return wildlife_tests_fake_st.fk_hidden }
function fk_noise_of(wildlife_tests_fake_st: WildlifeTestsFakeState, p: int) -> float { return wildlife_tests_fake_st.fk_noise }
function fk_scent_of(wildlife_tests_fake_st: WildlifeTestsFakeState, p: int) -> float { return wildlife_tests_fake_st.fk_scent }
function fk_still_of(wildlife_tests_fake_st: WildlifeTestsFakeState, p: int) -> int { return wildlife_tests_fake_st.fk_still }
function fk_wind_of(wildlife_tests_fake_st: WildlifeTestsFakeState) -> float { return wildlife_tests_fake_st.fk_wind }
function fk_wind_to(wildlife_tests_fake_st: WildlifeTestsFakeState) -> float { return wildlife_tests_fake_st.fk_wind_dir }
function fk_winter_of(wildlife_tests_fake_st: WildlifeTestsFakeState) -> float { return wildlife_tests_fake_st.fk_winter }
function fk_about_of(wildlife_tests_fake_st: WildlifeTestsFakeState, a: WildAnimal) -> int { return wildlife_tests_fake_st.fk_about }
function fk_one(sp: int) -> int { return 1 }
function fk_count() -> int { return len(fk_lures) }
function fk_lure(i: int, l: WildLure) -> bool {
let f = fk_lures[i]
function fk_count(wildlife_tests_fake_st: WildlifeTestsFakeState) -> int { return len(wildlife_tests_fake_st.fk_lures) }
function fk_lure(wildlife_tests_fake_st: WildlifeTestsFakeState, i: int, l: WildLure) -> bool {
let f = wildlife_tests_fake_st.fk_lures[i]
l.uid = f.uid
l.kind = f.kind
l.diet = f.diet
@ -95,10 +97,10 @@ function fk_lure(i: int, l: WildLure) -> bool {
l.x = f.x
l.z = f.z
l.owner = f.owner
return f.uid != fk_gone
return f.uid != wildlife_tests_fake_st.fk_gone
}
function fk_present(uid: int) -> bool { return uid != fk_gone }
function fk_on_born(a: WildAnimal) -> void { fk_born += 1 }
function fk_present(wildlife_tests_fake_st: WildlifeTestsFakeState, uid: int) -> bool { return uid != wildlife_tests_fake_st.fk_gone }
function fk_on_born(wildlife_tests_fake_st: mut WildlifeTestsFakeState, a: WildAnimal) -> void { wildlife_tests_fake_st.fk_born += 1 }
function fk_spot(a: WildAnimal, water: bool, s: WildSpot) -> bool {
s.uid = 900
s.x = a.x + 30.0

View file

@ -19,8 +19,8 @@ program LuresTest {
spot: fn fk_spot
}
function facts() -> []WildFact { return q_drain(wildlife_facts()) }
function set_out(uid: int, kind: int, diet: int, x: float, z: float, owner: int) -> WildLure {
function facts(base_st: mut BaseState, wildlife_st: mut WildlifeState) -> []WildFact { return q_drain(base_st, wildlife_facts(base_st, wildlife_st)) }
function set_out(wildlife_tests_fake_st: mut WildlifeTestsFakeState, uid: int, kind: int, diet: int, x: float, z: float, owner: int) -> WildLure {
let l = new WildLure
l.uid = uid
l.kind = kind
@ -28,24 +28,24 @@ program LuresTest {
l.x = x
l.z = z
l.owner = owner
push(fk_lures, l)
push(wildlife_tests_fake_st.fk_lures, l)
return l
}
function idle(sp: int, x: float, z: float) -> WildAnimal {
let a = wildlife_make(sp, x, z, false, 0.0, 0, 1.0, 7)
function idle(wildlife_st: mut WildlifeState, sp: int, x: float, z: float) -> WildAnimal {
let a = wildlife_make(wildlife_st, sp, x, z, false, 0.0, 0, 1.0, 7)
a.timer = 30.0
return a
}
function go(a: WildAnimal, secs: float) -> void {
for k in 0 .. int(secs * 20.0) { wildlife_tick_ground(a, 0.05, 0.05 / 60.0) }
function go(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal, secs: float) -> void {
for k in 0 .. int(secs * 20.0) { wildlife_tick_ground(base_st, wildlife_st, a, 0.05, 0.05 / 60.0) }
}
test "food of its diet draws it in, it eats, and the fact names what and whose" {
fk_setup()
set_out(41, WILD_FEED, WILD_PLANTS, 20.0, 0.0, 3)
let a = idle(0, 0.0, 0.0)
go(a, 40.0)
let fs = facts()
test "food of its diet draws it in, it eats, and the fact names what and whose" (base_st: mut BaseState, wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
set_out(wildlife_tests_fake_st, 41, WILD_FEED, WILD_PLANTS, 20.0, 0.0, 3)
let a = idle(wildlife_st, 0, 0.0, 0.0)
go(base_st, wildlife_st, a, 40.0)
let fs = facts(base_st, wildlife_st)
var fed = 0
for i in 0 .. len(fs) {
if fs[i].what == WILD_FED {
@ -58,70 +58,70 @@ program LuresTest {
expect(a.calm > 0.0)
}
test "food of another diet is left alone" {
fk_setup()
set_out(41, WILD_FEED, WILD_FISH, 20.0, 0.0, -1)
let a = idle(0, 0.0, 0.0)
go(a, 5.0)
test "food of another diet is left alone" (base_st: mut BaseState, wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
set_out(wildlife_tests_fake_st, 41, WILD_FEED, WILD_FISH, 20.0, 0.0, -1)
let a = idle(wildlife_st, 0, 0.0, 0.0)
go(base_st, wildlife_st, a, 5.0)
expect(a.state != WILD_APPROACH)
expect_eq(a.lure, 0)
}
test "food taken away while it walks is given up" {
fk_setup()
set_out(41, WILD_FEED, WILD_PLANTS, 50.0, 0.0, -1)
let a = idle(0, 0.0, 0.0)
go(a, 1.0)
test "food taken away while it walks is given up" (base_st: mut BaseState, wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
set_out(wildlife_tests_fake_st, 41, WILD_FEED, WILD_PLANTS, 50.0, 0.0, -1)
let a = idle(wildlife_st, 0, 0.0, 0.0)
go(base_st, wildlife_st, a, 1.0)
expect_eq(a.state, WILD_APPROACH)
fk_gone = 41
go(a, 0.2)
wildlife_tests_fake_st.fk_gone = 41
go(base_st, wildlife_st, a, 0.2)
expect(a.state != WILD_APPROACH)
}
test "a snare holds a small animal, the port can find it, and a release lets it go" {
fk_setup()
let l = set_out(55, WILD_SNARE, 0, 10.0, 0.0, -1)
test "a snare holds a small animal, the port can find it, and a release lets it go" (base_st: mut BaseState, wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
let l = set_out(wildlife_tests_fake_st, 55, WILD_SNARE, 0, 10.0, 0.0, -1)
l.size = 0
let h = idle(2, 0.0, 0.0)
let h = idle(wildlife_st, 2, 0.0, 0.0)
for k in 0 .. 40 {
if h.state == WILD_TRAPPED { break }
go(h, 2.0)
go(base_st, wildlife_st, h, 2.0)
}
expect_eq(h.state, WILD_TRAPPED)
expect(wildlife_in_snare(55) == h)
let fs = facts()
expect(wildlife_in_snare(wildlife_st, 55) == h)
let fs = facts(base_st, wildlife_st)
var held = false
for i in 0 .. len(fs) { if fs[i].what == WILD_CAUGHT and fs[i].lure == 55 { held = true } }
expect(held)
wildlife_release(h)
expect_eq(h.state, WILD_FLEE)
expect(wildlife_in_snare(55) == null)
expect(wildlife_in_snare(wildlife_st, 55) == null)
}
test "a thirsty animal walks to the water the port offers, and drinks it down" {
fk_setup()
let a = idle(0, 0.0, 0.0)
test "a thirsty animal walks to the water the port offers, and drinks it down" (base_st: mut BaseState, wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
let a = idle(wildlife_st, 0, 0.0, 0.0)
a.thirst = 90.0
a.state = WILD_WANDER
go(a, 0.1)
go(base_st, wildlife_st, a, 0.1)
expect_eq(a.state, WILD_GO_DRINK)
expect_eq(a.spot, 900)
go(a, 40.0)
go(base_st, wildlife_st, a, 40.0)
expect(a.thirst < 60.0)
}
test "seed down brings a bird out of the sky and it takes it" {
fk_setup()
set_out(77, WILD_FEED, WILD_SEED, 30.0, 0.0, -1)
let j = wildlife_new(3, 0.0, 0.0, false)
fk_px[0] = 200.0 # within 900 m: an animal nobody is near does not think
fk_pz[0] = 0.0
test "seed down brings a bird out of the sky and it takes it" (base_st: mut BaseState, wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
set_out(wildlife_tests_fake_st, 77, WILD_FEED, WILD_SEED, 30.0, 0.0, -1)
let j = wildlife_new(wildlife_st, 3, 0.0, 0.0, false)
wildlife_tests_fake_st.fk_px[0] = 200.0 # within 900 m: an animal nobody is near does not think
wildlife_tests_fake_st.fk_pz[0] = 0.0
var landed = false
var ate = false
for k in 0 .. 4000 {
wildlife_tick(0.05, 0.0)
wildlife_tick(base_st, wildlife_st, 0.05, 0.0)
if j.state == WILD_PERCH { landed = true }
let fs = facts()
let fs = facts(base_st, wildlife_st)
for i in 0 .. len(fs) { if fs[i].what == WILD_FED and fs[i].diet == WILD_SEED { ate = true } }
if ate { break }
}

View file

@ -31,180 +31,180 @@ program WildlifeTest {
spot: fn fk_spot
}
function facts() -> []WildFact { return q_drain(wildlife_facts()) }
function facts(base_st: mut BaseState, wildlife_st: mut WildlifeState) -> []WildFact { return q_drain(base_st, wildlife_facts(base_st, wildlife_st)) }
function count(fs: []WildFact, what: int) -> int {
var n = 0
for i in 0 .. len(fs) { if fs[i].what == what { n += 1 } }
return n
}
function deer_at(x: float, z: float) -> WildAnimal {
let a = wildlife_make(0, x, z, false, 0.0, 0, 1.0, 1)
function deer_at(wildlife_st: mut WildlifeState, x: float, z: float) -> WildAnimal {
let a = wildlife_make(wildlife_st, 0, x, z, false, 0.0, 0, 1.0, 1)
a.state = WILD_IDLE
a.timer = 30.0
return a
}
function player_at(x: float, z: float) -> void {
fk_px[0] = x
fk_pz[0] = z
function player_at(wildlife_tests_fake_st: mut WildlifeTestsFakeState, x: float, z: float) -> void {
wildlife_tests_fake_st.fk_px[0] = x
wildlife_tests_fake_st.fk_pz[0] = z
}
function go(a: WildAnimal, secs: float) -> void {
for k in 0 .. int(secs * 20.0) { wildlife_tick_ground(a, 0.05, 0.05 / 60.0) }
function go(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal, secs: float) -> void {
for k in 0 .. int(secs * 20.0) { wildlife_tick_ground(base_st, wildlife_st, a, 0.05, 0.05 / 60.0) }
}
test "the table handed in numbers the species, and a new animal is born through the port" {
fk_setup()
expect_eq(wildlife_species_count(), 4)
expect(wildlife_species(3).flies)
let a = wildlife_new(0, 10.0, 20.0, false)
expect_eq(fk_born, 1)
test "the table handed in numbers the species, and a new animal is born through the port" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
expect_eq(wildlife_species_count(wildlife_st), 4)
expect(wildlife_species(wildlife_st, 3).flies)
let a = wildlife_new(wildlife_st, 0, 10.0, 20.0, false)
expect_eq(wildlife_tests_fake_st.fk_born, 1)
expect_near(a.y, 10.0, 0.001)
expect(a.variant >= 0 and a.variant <= 11)
expect(a.scale > 0.87 and a.scale < 1.13)
let j = wildlife_new(3, 0.0, 0.0, false)
let j = wildlife_new(wildlife_st, 3, 0.0, 0.0, false)
expect_eq(j.state, WILD_FLY)
expect(wildlife_new(9, 0.0, 0.0, false) == null)
expect(wildlife_new(wildlife_st, 9, 0.0, 0.0, false) == null)
}
test "a spawn sets ranges out on their own habitat, clear of the start, and fills them" {
fk_setup()
wildlife_spawn(0.0, 0.0)
let rs = wildlife_ranges()
test "a spawn sets ranges out on their own habitat, clear of the start, and fills them" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
wildlife_spawn(wildlife_st, 0.0, 0.0)
let rs = wildlife_ranges(wildlife_st)
expect(len(rs) >= 6)
for i in 0 .. len(rs) {
expect(wildlife_range_dist(rs[i], 0.0, 0.0) >= wildlife_species(rs[i].sp).keep_away)
expect(wildlife_range_dist(rs[i], 0.0, 0.0) >= wildlife_species(wildlife_st, rs[i].sp).keep_away)
if rs[i].sp == 1 { expect(rs[i].z > 400.0) }
expect(rs[i].sx > -300.0)
}
expect(wildlife_count(0) >= 1)
expect_eq(wildlife_count_all(), wildlife_count(0) + wildlife_count(1) + wildlife_count(2) + wildlife_count(3))
expect(wildlife_count(wildlife_st, 0) >= 1)
expect_eq(wildlife_count_all(wildlife_st), wildlife_count(wildlife_st, 0) + wildlife_count(wildlife_st, 1) + wildlife_count(wildlife_st, 2) + wildlife_count(wildlife_st, 3))
}
test "a player far off makes no difference that lasts: the alert settles under the unaware line" {
fk_setup()
let a = deer_at(0.0, 0.0)
player_at(55.0, 0.0)
fk_noise = 0.3
go(a, 60.0)
test "a player far off makes no difference that lasts: the alert settles under the unaware line" (base_st: mut BaseState, wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
let a = deer_at(wildlife_st, 0.0, 0.0)
player_at(wildlife_tests_fake_st, 55.0, 0.0)
wildlife_tests_fake_st.fk_noise = 0.3
go(base_st, wildlife_st, a, 60.0)
expect(a.alert < 25.0)
expect(a.state != WILD_FLEE)
}
test "close and in the open it is flushed, runs, and says so" {
fk_setup()
let a = deer_at(0.0, 0.0)
player_at(8.0, 0.0)
fk_noise = 0.8
go(a, 10.0)
test "close and in the open it is flushed, runs, and says so" (base_st: mut BaseState, wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
let a = deer_at(wildlife_st, 0.0, 0.0)
player_at(wildlife_tests_fake_st, 8.0, 0.0)
wildlife_tests_fake_st.fk_noise = 0.8
go(base_st, wildlife_st, a, 10.0)
expect(a.spook > 0.0)
let fs = facts()
let fs = facts(base_st, wildlife_st)
expect(count(fs, WILD_FLED) >= 1)
expect(a.x < 0.0)
}
test "cover hides a player from sight, a hide from everything but the nose" {
fk_setup()
let a = deer_at(0.0, 0.0)
let open = wildlife_alert_rate(a, 0, 30.0)
player_at(30.0, 0.0)
let open2 = wildlife_alert_rate(a, 0, 30.0)
fk_blocked = true
let covered = wildlife_alert_rate(a, 0, 30.0)
fk_blocked = false
fk_hidden = true
let hidden = wildlife_alert_rate(a, 0, 30.0)
test "cover hides a player from sight, a hide from everything but the nose" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
let a = deer_at(wildlife_st, 0.0, 0.0)
let open = wildlife_alert_rate(wildlife_st, a, 0, 30.0)
player_at(wildlife_tests_fake_st, 30.0, 0.0)
let open2 = wildlife_alert_rate(wildlife_st, a, 0, 30.0)
wildlife_tests_fake_st.fk_blocked = true
let covered = wildlife_alert_rate(wildlife_st, a, 0, 30.0)
wildlife_tests_fake_st.fk_blocked = false
wildlife_tests_fake_st.fk_hidden = true
let hidden = wildlife_alert_rate(wildlife_st, a, 0, 30.0)
expect(open2 > 0.0)
expect(open >= open2)
expect_near(covered, 0.0, 0.0001)
expect_near(hidden, 0.0, 0.0001)
}
test "sitting still makes it forget faster, and the wind carries scent only downwind" {
fk_setup()
let a = deer_at(0.0, 0.0)
let moving = wildlife_alert_decay(a, 0)
fk_still = WILD_SITTING
expect(wildlife_alert_decay(a, 0) > moving * 2.0)
player_at(0.0, -60.0)
fk_blocked = true
fk_scent = 1.0
fk_wind = 0.5
fk_wind_dir = 3.1415927 # blowing toward +z: from the player to the deer
test "sitting still makes it forget faster, and the wind carries scent only downwind" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
let a = deer_at(wildlife_st, 0.0, 0.0)
let moving = wildlife_alert_decay(wildlife_st, a, 0)
wildlife_tests_fake_st.fk_still = WILD_SITTING
expect(wildlife_alert_decay(wildlife_st, a, 0) > moving * 2.0)
player_at(wildlife_tests_fake_st, 0.0, -60.0)
wildlife_tests_fake_st.fk_blocked = true
wildlife_tests_fake_st.fk_scent = 1.0
wildlife_tests_fake_st.fk_wind = 0.5
wildlife_tests_fake_st.fk_wind_dir = 3.1415927 # blowing toward +z: from the player to the deer
expect(wildlife_downwind(a, 0) > 0.9)
expect(wildlife_alert_rate(a, 0, 60.0) > 0.0)
fk_wind_dir = 0.0
expect_near(wildlife_alert_rate(a, 0, 60.0), 0.0, 0.0001)
expect(wildlife_alert_rate(wildlife_st, a, 0, 60.0) > 0.0)
wildlife_tests_fake_st.fk_wind_dir = 0.0
expect_near(wildlife_alert_rate(wildlife_st, a, 0, 60.0), 0.0, 0.0001)
}
test "a charger comes at a player inside its reach, and a shove is a fact for the game" {
fk_setup()
let b = wildlife_make(1, 0.0, 0.0, false, 0.0, 0, 1.0, 2)
player_at(6.0, 0.0)
wildlife_tick_ground(b, 0.05, 0.001)
test "a charger comes at a player inside its reach, and a shove is a fact for the game" (base_st: mut BaseState, wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
let b = wildlife_make(wildlife_st, 1, 0.0, 0.0, false, 0.0, 0, 1.0, 2)
player_at(wildlife_tests_fake_st, 6.0, 0.0)
wildlife_tick_ground(base_st, wildlife_st, b, 0.05, 0.001)
expect_eq(b.state, WILD_CHARGE)
go(b, 2.0)
let fs = facts()
go(base_st, wildlife_st, b, 2.0)
let fs = facts(base_st, wildlife_st)
expect_eq(count(fs, WILD_CHARGED), 1)
expect_eq(count(fs, WILD_SHOVED), 1)
expect(b.calm > 0.0)
}
test "away is hidden and skipped, gone is removed and said" {
fk_setup()
let a = deer_at(0.0, 0.0)
player_at(10.0, 0.0)
fk_about = WILD_AWAY
wildlife_tick(0.05, 0.001)
test "away is hidden and skipped, gone is removed and said" (base_st: mut BaseState, wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
let a = deer_at(wildlife_st, 0.0, 0.0)
player_at(wildlife_tests_fake_st, 10.0, 0.0)
wildlife_tests_fake_st.fk_about = WILD_AWAY
wildlife_tick(base_st, wildlife_st, 0.05, 0.001)
expect(not a.shown and a.alive)
fk_about = WILD_GONE
wildlife_tick(0.05, 0.001)
wildlife_tests_fake_st.fk_about = WILD_GONE
wildlife_tick(base_st, wildlife_st, 0.05, 0.001)
expect(not a.alive)
expect_eq(count(facts(), WILD_WENT), 1)
expect_eq(count(facts(base_st, wildlife_st), WILD_WENT), 1)
}
test "a walker leaves a print every 2.8 m, and player 0 spots a species once" {
fk_setup()
let a = deer_at(0.0, 0.0)
player_at(40.0, 0.0)
fk_blocked = true
test "a walker leaves a print every 2.8 m, and player 0 spots a species once" (base_st: mut BaseState, wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
let a = deer_at(wildlife_st, 0.0, 0.0)
player_at(wildlife_tests_fake_st, 40.0, 0.0)
wildlife_tests_fake_st.fk_blocked = true
a.state = WILD_WANDER
a.tx = 0.0
a.tz = -100.0
a.timer = 100.0
for k in 0 .. 200 { wildlife_tick(0.05, 0.0) }
let fs = facts()
for k in 0 .. 200 { wildlife_tick(base_st, wildlife_st, 0.05, 0.0) }
let fs = facts(base_st, wildlife_st)
expect(count(fs, WILD_PRINT) >= 3)
expect_eq(count(fs, WILD_SPOTTED), 1)
expect(wildlife_seen(0))
expect(wildlife_seen(wildlife_st, 0))
}
test "the record and the found ranges come back from the save section" {
fk_setup()
wildlife_spawn(0.0, 0.0)
wildlife_mark_fed(2)
wildlife_saw(0)
let r = wildlife_ranges()[0]
wildlife_ranges_visit(r.x, r.z)
let v = wildlife_save()
wildlife_reset()
expect(not wildlife_fed_any() and not wildlife_ranges()[0].found)
wildlife_load(v, 1)
expect(wildlife_fed_any())
expect_eq(wildlife_fed_mask(), 4)
expect(wildlife_seen(0))
expect(wildlife_ranges()[0].found)
test "the record and the found ranges come back from the save section" (base_st: mut BaseState, wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
wildlife_spawn(wildlife_st, 0.0, 0.0)
wildlife_mark_fed(wildlife_st, 2)
wildlife_saw(wildlife_st, 0)
let r = wildlife_ranges(wildlife_st)[0]
wildlife_ranges_visit(wildlife_st, r.x, r.z)
let v = wildlife_save(wildlife_st)
wildlife_reset(base_st, wildlife_st)
expect(not wildlife_fed_any(wildlife_st) and not wildlife_ranges(wildlife_st)[0].found)
wildlife_load(base_st, wildlife_st, v, 1)
expect(wildlife_fed_any(wildlife_st))
expect_eq(wildlife_fed_mask(wildlife_st), 4)
expect(wildlife_seen(wildlife_st, 0))
expect(wildlife_ranges(wildlife_st)[0].found)
}
test "a morning moves the ranges toward winter and brings one back to a range that lost its own" {
fk_setup()
wildlife_spawn(0.0, 0.0)
let r = wildlife_ranges()[0]
test "a morning moves the ranges toward winter and brings one back to a range that lost its own" (wildlife_st: mut WildlifeState, wildlife_tests_fake_st: mut WildlifeTestsFakeState) {
fk_setup(wildlife_st, wildlife_tests_fake_st)
wildlife_spawn(wildlife_st, 0.0, 0.0)
let r = wildlife_ranges(wildlife_st)[0]
let x0 = r.x
let all = wildlife_all()
let all = wildlife_all(wildlife_st)
for i in 0 .. len(all) { if all[i].rg == 0 { wildlife_remove(all[i]) } }
fk_winter = 1.0
let added = wildlife_morning()
wildlife_tests_fake_st.fk_winter = 1.0
let added = wildlife_morning(wildlife_st)
expect(added >= 1)
expect_eq(wildlife_range_alive(0), 1)
expect_eq(wildlife_range_alive(wildlife_st, 0), 1)
expect_near(r.x, r.wx, 0.001)
expect(r.x != x0 or r.wx == r.sx)
}

View file

@ -1,9 +1,8 @@
# want.ludic - thirst and hunger over sixty send it to the water or the forage the game offers for
# it (a hungry one on its way turns to food put down nearer); it drinks or grazes there
var wl_spot_ask: WildSpot = null
function wl_seek(a: WildAnimal, dt: float) -> bool {
if a.state == WILD_GO_FEED and wl_attraction(a) and wl_best.kind == WILD_FEED { wl_take_lure(a) }
function wl_seek(base_st: mut BaseState, wildlife_st: mut WildlifeState, a: WildAnimal, dt: float) -> bool {
if a.state == WILD_GO_FEED and wl_attraction(wildlife_st, a) and wildlife_st.wl_best.kind == WILD_FEED { wl_take_lure(wildlife_st, a) }
if a.state != WILD_GO_DRINK and a.state != WILD_GO_FEED { return false }
if a.spot == 0 or not WildlifeWorld.present(a.spot) {
a.spot = 0
@ -12,13 +11,13 @@ function wl_seek(a: WildAnimal, dt: float) -> bool {
return true
}
wl_face(a, a.spot_x, a.spot_z, 2.0, dt)
a.speed = wl_sp(a.sp).walk
a.speed = wl_sp(wildlife_st, a.sp).walk
a.tx = a.spot_x
a.tz = a.spot_z
wl_step(a, dt)
wl_step(base_st, wildlife_st, a, dt)
if wl_at_target(a, 2.0) {
a.state = WILD_DRINK
a.timer = 6.0 + wl_rnd() * 10.0
a.timer = 6.0 + wl_rnd(wildlife_st) * 10.0
}
a.timer = a.timer - dt
if a.timer < -600.0 {
@ -29,26 +28,26 @@ function wl_seek(a: WildAnimal, dt: float) -> bool {
}
# true while it drinks or grazes, or when it has just set off
function wl_drink(a: WildAnimal, dt: float, gh: float) -> bool {
function wl_drink(wildlife_st: mut WildlifeState, a: WildAnimal, dt: float, gh: float) -> bool {
if a.state == WILD_DRINK {
a.speed = 0.0
a.timer = a.timer - dt
if a.spot != 0 and a.spot_water { a.thirst = Math.max(a.thirst - 600.0 * gh, 0.0) } else { a.hunger = Math.max(a.hunger - 600.0 * gh, 0.0) }
if a.timer < 0.0 or Math.min(a.thirst, a.hunger) < 1.0 {
wl_rehome(a)
wl_wander(a, 6.0)
wl_rehome(wildlife_st, a)
wl_wander(wildlife_st, a, 6.0)
}
return true
}
if a.state != WILD_IDLE and a.state != WILD_WANDER { return false }
if not (a.thirst > 60.0 or a.hunger > 60.0) { return false }
if wl_spot_ask == null { wl_spot_ask = new WildSpot }
if wildlife_st.wl_spot_ask == null { wildlife_st.wl_spot_ask = new WildSpot }
let water = a.thirst > a.hunger
if not WildlifeWorld.spot(a, water, wl_spot_ask) { return false }
a.spot = wl_spot_ask.uid
a.spot_x = wl_spot_ask.x
a.spot_z = wl_spot_ask.z
a.spot_water = wl_spot_ask.water
if not WildlifeWorld.spot(a, water, wildlife_st.wl_spot_ask) { return false }
a.spot = wildlife_st.wl_spot_ask.uid
a.spot_x = wildlife_st.wl_spot_ask.x
a.spot_z = wildlife_st.wl_spot_ask.z
a.spot_water = wildlife_st.wl_spot_ask.water
a.state = WILD_GO_DRINK
if not water { a.state = WILD_GO_FEED }
a.timer = 120.0