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 @@
# queries.ludic - what the world asks the body
export function needs_level(k: int) -> float {
export function needs_level(needs_st: mut NeedsState, k: int) -> float {
if not nd_ok(k) { return 0.0 }
return nd_lv()[k]
return nd_lv(needs_st)[k]
}
export function needs_warmth() -> float { return nd_lv()[NEED_WARMTH] }
export function needs_food() -> float { return nd_lv()[NEED_FOOD] }
export function needs_water() -> float { return nd_lv()[NEED_WATER] }
export function needs_energy() -> float { return nd_lv()[NEED_ENERGY] }
export function needs_warmth(needs_st: mut NeedsState) -> float { return nd_lv(needs_st)[NEED_WARMTH] }
export function needs_food(needs_st: mut NeedsState) -> float { return nd_lv(needs_st)[NEED_FOOD] }
export function needs_water(needs_st: mut NeedsState) -> float { return nd_lv(needs_st)[NEED_WATER] }
export function needs_energy(needs_st: mut NeedsState) -> float { return nd_lv(needs_st)[NEED_ENERGY] }
# seconds left before a collapse (0: no countdown), and the need it is for
export function needs_last_legs() -> float { return nd_legs }
export function needs_last_need() -> int { return nd_last }
export function needs_collapses() -> int { return nd_collapses }
export function needs_last_legs(needs_st: NeedsState) -> float { return needs_st.nd_legs }
export function needs_last_need(needs_st: NeedsState) -> int { return needs_st.nd_last }
export function needs_collapses(needs_st: NeedsState) -> int { return needs_st.nd_collapses }
# the walk's pace, times: the ramp of each need, and 0.6 more on last legs
export function needs_pace() -> float {
let lv = nd_lv()
export function needs_pace(needs_st: mut NeedsState) -> float {
let lv = nd_lv(needs_st)
var m = nd_pace(lv[NEED_WARMTH]) * nd_pace(lv[NEED_FOOD])
if NeedsWorld.thirst() { m = m * nd_pace(lv[NEED_WATER]) }
if nd_legs > 0.0 { m = m * 0.6 }
if needs_st.nd_legs > 0.0 { m = m * 0.6 }
return m
}
# running needs legs, calories and a body that is not already in trouble
export function needs_can_run() -> bool {
let lv = nd_lv()
export function needs_can_run(needs_st: mut NeedsState) -> bool {
let lv = nd_lv(needs_st)
return lv[NEED_ENERGY] > 8.0 and lv[NEED_FOOD] > 20.0 and lv[NEED_WARMTH] > 20.0 and lv[NEED_WATER] > 20.0
}
# the lowest of the four
export function needs_lowest() -> float {
let lv = nd_lv()
export function needs_lowest(needs_st: mut NeedsState) -> float {
let lv = nd_lv(needs_st)
return Math.min(Math.min(lv[NEED_WARMTH], lv[NEED_FOOD]), Math.min(lv[NEED_WATER], lv[NEED_ENERGY]))
}

View file

@ -1,15 +1,15 @@
# run.ludic - `gh` game hours of whatever the body is doing, taken out of the four bars
function nd_add(k: int, d: float) -> void {
let lv = nd_lv()
function nd_add(needs_st: mut NeedsState, k: int, d: float) -> void {
let lv = nd_lv(needs_st)
lv[k] = Math.clamp(lv[k] + d, 0.0, 100.0)
}
export function needs_run(gh: float) -> void {
export function needs_run(needs_st: mut NeedsState, gh: float) -> void {
let a = NeedsWorld.activity()
nd_add(NEED_FOOD, (NeedsWorld.bonus(NEED_FOOD) - nd_food_rate(a) * NeedsWorld.drain(NEED_FOOD)) * gh)
nd_add(needs_st, NEED_FOOD, (NeedsWorld.bonus(NEED_FOOD) - nd_food_rate(a) * NeedsWorld.drain(NEED_FOOD)) * gh)
if NeedsWorld.thirst() {
nd_add(NEED_WATER, (NeedsWorld.bonus(NEED_WATER) - nd_water_rate(a) * NeedsWorld.drain(NEED_WATER)) * gh)
} else { nd_lv()[NEED_WATER] = 100.0 }
nd_add(NEED_ENERGY, (nd_energy_rate(a) + NeedsWorld.bonus(NEED_ENERGY)) * gh)
nd_add(NEED_WARMTH, nd_warmth_rate(a, nd_lv()[NEED_WATER]) * gh)
nd_add(needs_st, NEED_WATER, (NeedsWorld.bonus(NEED_WATER) - nd_water_rate(a) * NeedsWorld.drain(NEED_WATER)) * gh)
} else { nd_lv(needs_st)[NEED_WATER] = 100.0 }
nd_add(needs_st, NEED_ENERGY, (nd_energy_rate(a) + NeedsWorld.bonus(NEED_ENERGY)) * gh)
nd_add(needs_st, NEED_WARMTH, nd_warmth_rate(a, nd_lv(needs_st)[NEED_WATER]) * gh)
}

View file

@ -51,38 +51,40 @@ export property NeedFact {
need: int = 0
}
var nd_level: []float = null
var nd_legs: float = 0.0 # seconds left before a collapse, 0 = none
var nd_last: int = 0 # the need the countdown is for
var nd_collapses: int = 0
var nd_facts: Queue<NeedFact> = null
function nd_lv() -> []float {
if nd_level == null {
nd_level = floats(NEED_COUNT)
nd_fill()
}
return nd_level
export state NeedsState {
nd_level: []float = null
nd_legs: float = 0.0 # seconds left before a collapse, 0 = none
nd_last: int = 0 # the need the countdown is for
nd_collapses: int = 0
nd_facts: Queue<NeedFact> = null
}
function nd_fill() -> void {
nd_level[NEED_WARMTH] = 100.0
nd_level[NEED_FOOD] = 70.0
nd_level[NEED_WATER] = 85.0
nd_level[NEED_ENERGY] = 100.0
function nd_lv(needs_st: mut NeedsState) -> []float {
if needs_st.nd_level == null {
needs_st.nd_level = floats(NEED_COUNT)
nd_fill(needs_st)
}
return needs_st.nd_level
}
function nd_fill(needs_st: mut NeedsState) -> void {
needs_st.nd_level[NEED_WARMTH] = 100.0
needs_st.nd_level[NEED_FOOD] = 70.0
needs_st.nd_level[NEED_WATER] = 85.0
needs_st.nd_level[NEED_ENERGY] = 100.0
}
# the facts, oldest first; the game drains them into its notices and its collapse
export function needs_facts() -> Queue<NeedFact> {
if nd_facts == null { nd_facts = queue_new("needs.facts") }
return nd_facts
export function needs_facts(base_st: mut BaseState, needs_st: mut NeedsState) -> Queue<NeedFact> {
if needs_st.nd_facts == null { needs_st.nd_facts = queue_new(base_st, "needs.facts") }
return needs_st.nd_facts
}
function nd_fact(what: int, need: int) -> void {
function nd_fact(base_st: mut BaseState, needs_st: mut NeedsState, what: int, need: int) -> void {
let f = new NeedFact
f.what = what
f.need = need
q_push(needs_facts(), f)
q_push(base_st, needs_facts(base_st, needs_st), f)
}
function nd_ok(k: int) -> bool { return k >= 0 and k < NEED_COUNT }

View file

@ -1,27 +1,27 @@
# system.ludic - the needs as a system: PH_SIMULATE, the drain then the watch, and its own save
# section (the countdown is not saved: a loaded body starts it again if a need is still empty)
function needs_tick(t: Tick) -> void {
needs_run(t.hours)
needs_watch(t.dt)
function needs_tick(base_st: mut BaseState, needs_st: mut NeedsState, t: Tick) -> void {
needs_run(needs_st, t.hours)
needs_watch(base_st, needs_st, t.dt)
}
export function needs_save() -> Val {
export function needs_save(needs_st: mut NeedsState) -> Val {
let v = Value.object()
sv_put_float(v, "warmth", needs_warmth())
sv_put_float(v, "food", needs_food())
sv_put_float(v, "water", needs_water())
sv_put_float(v, "energy", needs_energy())
sv_put_int(v, "collapses", nd_collapses)
sv_put_float(v, "warmth", needs_warmth(needs_st))
sv_put_float(v, "food", needs_food(needs_st))
sv_put_float(v, "water", needs_water(needs_st))
sv_put_float(v, "energy", needs_energy(needs_st))
sv_put_int(v, "collapses", needs_st.nd_collapses)
return v
}
export function needs_load(v: Val, version: int) -> void {
needs_reset()
needs_set(NEED_WARMTH, sv_float(v, "warmth", 100.0))
needs_set(NEED_FOOD, sv_float(v, "food", 70.0))
needs_set(NEED_WATER, sv_float(v, "water", 85.0))
needs_set(NEED_ENERGY, sv_float(v, "energy", 100.0))
nd_collapses = sv_int(v, "collapses", 0)
export function needs_load(base_st: mut BaseState, needs_st: mut NeedsState, v: Val, version: int) -> void {
needs_reset(base_st, needs_st)
needs_set(needs_st, NEED_WARMTH, sv_float(v, "warmth", 100.0))
needs_set(needs_st, NEED_FOOD, sv_float(v, "food", 70.0))
needs_set(needs_st, NEED_WATER, sv_float(v, "water", 85.0))
needs_set(needs_st, NEED_ENERGY, sv_float(v, "energy", 100.0))
needs_st.nd_collapses = sv_int(v, "collapses", 0)
}
export function needs_system() -> System {

View file

@ -4,29 +4,31 @@ import "ludic.needs"
import "ludic.base"
program NeedsTest {
numbers float
var act: int = 0
var air: float = -2.0
var keep: float = 0.0
var hot: bool = false
var thirst: bool = true
var gentle: bool = false
var held: bool = false
var food_mult: float = 1.0
var warm_bonus: float = 0.0
state NeedstestState {
act: int = 0
air: float = -2.0
keep: float = 0.0
hot: bool = false
thirst: bool = true
gentle: bool = false
held: bool = false
food_mult: float = 1.0
warm_bonus: float = 0.0
}
function fake_act() -> int { return act }
function fake_air() -> float { return air }
function fake_keep() -> float { return keep }
function fake_hot() -> bool { return hot }
function fake_thirst() -> bool { return thirst }
function fake_gentle() -> bool { return gentle }
function fake_held() -> bool { return held }
function fake_drain(k: int) -> float {
if k == NEED_FOOD { return food_mult }
function fake_act(needstest_st: NeedstestState) -> int { return needstest_st.act }
function fake_air(needstest_st: NeedstestState) -> float { return needstest_st.air }
function fake_keep(needstest_st: NeedstestState) -> float { return needstest_st.keep }
function fake_hot(needstest_st: NeedstestState) -> bool { return needstest_st.hot }
function fake_thirst(needstest_st: NeedstestState) -> bool { return needstest_st.thirst }
function fake_gentle(needstest_st: NeedstestState) -> bool { return needstest_st.gentle }
function fake_held(needstest_st: NeedstestState) -> bool { return needstest_st.held }
function fake_drain(needstest_st: NeedstestState, k: int) -> float {
if k == NEED_FOOD { return needstest_st.food_mult }
return 1.0
}
function fake_bonus(k: int) -> float {
if k == NEED_WARMTH { return warm_bonus }
function fake_bonus(needstest_st: NeedstestState, k: int) -> float {
if k == NEED_WARMTH { return needstest_st.warm_bonus }
return 0.0
}
@ -42,169 +44,169 @@ program NeedsTest {
bonus: fn fake_bonus
}
function fresh() -> void { needs_reset() }
function hours(h: float) -> void { for i in 0 .. int(h * 10.0) { needs_run(0.1) } }
function facts() -> []NeedFact { return q_drain(needs_facts()) }
function fresh(base_st: mut BaseState, needs_st: mut NeedsState) -> void { needs_reset(base_st, needs_st) }
function hours(needs_st: mut NeedsState, h: float) -> void { for i in 0 .. int(h * 10.0) { needs_run(needs_st, 0.1) } }
function facts(base_st: mut BaseState, needs_st: mut NeedsState) -> []NeedFact { return q_drain(base_st, needs_facts(base_st, needs_st)) }
test "a new body is warm, rested, fed and watered" {
fresh()
expect(needs_warmth() == 100.0)
expect(needs_energy() == 100.0)
expect(needs_food() == 70.0)
expect(needs_water() == 85.0)
expect(needs_pace() == 1.0)
expect(needs_can_run())
test "a new body is warm, rested, fed and watered" (base_st: mut BaseState, needs_st: mut NeedsState) {
fresh(base_st, needs_st)
expect(needs_warmth(needs_st) == 100.0)
expect(needs_energy(needs_st) == 100.0)
expect(needs_food(needs_st) == 70.0)
expect(needs_water(needs_st) == 85.0)
expect(needs_pace(needs_st) == 1.0)
expect(needs_can_run(needs_st))
}
test "a walking day empties a full bar of food in about a day, a run much sooner" {
fresh()
needs_set(NEED_FOOD, 100.0)
act = NEEDS_WALK
hours(12.0)
expect_near(needs_food(), 52.0, 0.5)
needs_set(NEED_FOOD, 100.0)
act = NEEDS_RUN
hours(12.0)
expect(needs_food() < 20.0)
test "a walking day empties a full bar of food in about a day, a run much sooner" (base_st: mut BaseState, needs_st: mut NeedsState, needstest_st: mut NeedstestState) {
fresh(base_st, needs_st)
needs_set(needs_st, NEED_FOOD, 100.0)
needstest_st.act = NEEDS_WALK
hours(needs_st, 12.0)
expect_near(needs_food(needs_st), 52.0, 0.5)
needs_set(needs_st, NEED_FOOD, 100.0)
needstest_st.act = NEEDS_RUN
hours(needs_st, 12.0)
expect(needs_food(needs_st) < 20.0)
}
test "the sun and the lake make thirst faster, but never past the cap" {
fresh()
needs_set(NEED_WATER, 100.0)
act = NEEDS_SWIM_HARD
hot = true
hours(1.0)
expect_near(needs_water(), 92.5, 0.01)
test "the sun and the lake make thirst faster, but never past the cap" (base_st: mut BaseState, needs_st: mut NeedsState, needstest_st: mut NeedstestState) {
fresh(base_st, needs_st)
needs_set(needs_st, NEED_WATER, 100.0)
needstest_st.act = NEEDS_SWIM_HARD
needstest_st.hot = true
hours(needs_st, 1.0)
expect_near(needs_water(needs_st), 92.5, 0.01)
}
test "thirst off holds water full" {
fresh()
thirst = false
needs_set(NEED_WATER, 10.0)
hours(1.0)
expect(needs_water() == 100.0)
test "thirst off holds water full" (base_st: mut BaseState, needs_st: mut NeedsState, needstest_st: mut NeedstestState) {
fresh(base_st, needs_st)
needstest_st.thirst = false
needs_set(needs_st, NEED_WATER, 10.0)
hours(needs_st, 1.0)
expect(needs_water(needs_st) == 100.0)
}
test "the port's drain and bonus are what the bars get" {
fresh()
food_mult = 2.0
hours(1.0)
expect_near(needs_food(), 62.0, 0.01)
warm_bonus = 10.0
air = 0.0
needs_set(NEED_WARMTH, 50.0)
hours(1.0)
expect_near(needs_warmth(), 60.0, 0.01)
test "the port's drain and bonus are what the bars get" (base_st: mut BaseState, needs_st: mut NeedsState, needstest_st: mut NeedstestState) {
fresh(base_st, needs_st)
needstest_st.food_mult = 2.0
hours(needs_st, 1.0)
expect_near(needs_food(needs_st), 62.0, 0.01)
needstest_st.warm_bonus = 10.0
needstest_st.air = 0.0
needs_set(needs_st, NEED_WARMTH, 50.0)
hours(needs_st, 1.0)
expect_near(needs_warmth(needs_st), 60.0, 0.01)
}
test "clothing keeps what is being lost, and a dry body loses more" {
fresh()
air = -20.0
keep = 0.5
hours(1.0)
expect_near(needs_warmth(), 90.0, 0.01)
needs_set(NEED_WARMTH, 100.0)
needs_set(NEED_WATER, 10.0)
keep = 0.0
hours(1.0)
expect_near(needs_warmth(), 75.0, 0.01)
test "clothing keeps what is being lost, and a dry body loses more" (base_st: mut BaseState, needs_st: mut NeedsState, needstest_st: mut NeedstestState) {
fresh(base_st, needs_st)
needstest_st.air = -20.0
needstest_st.keep = 0.5
hours(needs_st, 1.0)
expect_near(needs_warmth(needs_st), 90.0, 0.01)
needs_set(needs_st, NEED_WARMTH, 100.0)
needs_set(needs_st, NEED_WATER, 10.0)
needstest_st.keep = 0.0
hours(needs_st, 1.0)
expect_near(needs_warmth(needs_st), 75.0, 0.01)
}
test "sitting gives energy back, swimming takes it" {
fresh()
needs_set(NEED_ENERGY, 40.0)
act = NEEDS_SIT
hours(1.0)
expect_near(needs_energy(), 80.0, 0.01)
act = NEEDS_SWIM
hours(1.0)
expect_near(needs_energy(), 52.0, 0.01)
test "sitting gives energy back, swimming takes it" (base_st: mut BaseState, needs_st: mut NeedsState, needstest_st: mut NeedstestState) {
fresh(base_st, needs_st)
needs_set(needs_st, NEED_ENERGY, 40.0)
needstest_st.act = NEEDS_SIT
hours(needs_st, 1.0)
expect_near(needs_energy(needs_st), 80.0, 0.01)
needstest_st.act = NEEDS_SWIM
hours(needs_st, 1.0)
expect_near(needs_energy(needs_st), 52.0, 0.01)
}
test "meals fill, and every bar is held to 0..100" {
fresh()
needs_meal(50.0, 50.0, 10.0)
expect(needs_food() == 100.0)
expect(needs_water() == 100.0)
needs_feed(NEED_ENERGY, -150.0)
expect(needs_energy() == 0.0)
needs_floor(NEED_ENERGY, 30.0)
expect(needs_energy() == 30.0)
test "meals fill, and every bar is held to 0..100" (base_st: mut BaseState, needs_st: mut NeedsState) {
fresh(base_st, needs_st)
needs_meal(needs_st, 50.0, 50.0, 10.0)
expect(needs_food(needs_st) == 100.0)
expect(needs_water(needs_st) == 100.0)
needs_feed(needs_st, NEED_ENERGY, -150.0)
expect(needs_energy(needs_st) == 0.0)
needs_floor(needs_st, NEED_ENERGY, 30.0)
expect(needs_energy(needs_st) == 30.0)
}
test "a need at zero is a countdown, then a collapse, as facts" {
fresh()
needs_set(NEED_WATER, 0.0)
needs_watch(0.1)
let a = facts()
test "a need at zero is a countdown, then a collapse, as facts" (base_st: mut BaseState, needs_st: mut NeedsState) {
fresh(base_st, needs_st)
needs_set(needs_st, NEED_WATER, 0.0)
needs_watch(base_st, needs_st, 0.1)
let a = facts(base_st, needs_st)
expect_eq(len(a), 1)
expect_eq(a[0].what, NEEDS_CRITICAL)
expect_eq(a[0].need, NEED_WATER)
expect(needs_last_legs() > 0.0)
expect(needs_pace() < 0.6)
expect(needs_last_legs(needs_st) > 0.0)
expect(needs_pace(needs_st) < 0.6)
var i = 0
while q_len(needs_facts()) == 0 and i < 200 {
needs_watch(0.1)
while q_len(needs_facts(base_st, needs_st)) == 0 and i < 200 {
needs_watch(base_st, needs_st, 0.1)
i += 1
}
let b = facts()
let b = facts(base_st, needs_st)
expect_eq(len(b), 1)
expect_eq(b[0].what, NEEDS_COLLAPSED)
expect_eq(b[0].need, NEED_WATER)
expect(i >= 99 and i <= 101)
expect_eq(needs_collapses(), 0)
needs_went_down()
expect_eq(needs_collapses(), 1)
expect_eq(needs_collapses(needs_st), 0)
needs_went_down(needs_st)
expect_eq(needs_collapses(needs_st), 1)
}
test "a drink in time calls the countdown off" {
fresh()
needs_set(NEED_FOOD, 0.0)
needs_watch(0.1)
needs_feed(NEED_FOOD, 30.0)
needs_watch(0.1)
let f = facts()
test "a drink in time calls the countdown off" (base_st: mut BaseState, needs_st: mut NeedsState) {
fresh(base_st, needs_st)
needs_set(needs_st, NEED_FOOD, 0.0)
needs_watch(base_st, needs_st, 0.1)
needs_feed(needs_st, NEED_FOOD, 30.0)
needs_watch(base_st, needs_st, 0.1)
let f = facts(base_st, needs_st)
expect_eq(len(f), 2)
expect_eq(f[1].what, NEEDS_RESTORED)
expect(needs_last_legs() == 0.0)
expect(needs_last_legs(needs_st) == 0.0)
}
test "gentle never counts down, and a body already down waits" {
fresh()
gentle = true
needs_set(NEED_WARMTH, 0.0)
needs_watch(0.1)
expect(needs_warmth() == 22.0)
expect_eq(q_len(needs_facts()), 0)
gentle = false
held = true
needs_set(NEED_WARMTH, 0.0)
needs_watch(0.1)
expect_eq(q_len(needs_facts()), 0)
test "gentle never counts down, and a body already down waits" (base_st: mut BaseState, needs_st: mut NeedsState, needstest_st: mut NeedstestState) {
fresh(base_st, needs_st)
needstest_st.gentle = true
needs_set(needs_st, NEED_WARMTH, 0.0)
needs_watch(base_st, needs_st, 0.1)
expect(needs_warmth(needs_st) == 22.0)
expect_eq(q_len(needs_facts(base_st, needs_st)), 0)
needstest_st.gentle = false
needstest_st.held = true
needs_set(needs_st, NEED_WARMTH, 0.0)
needs_watch(base_st, needs_st, 0.1)
expect_eq(q_len(needs_facts(base_st, needs_st)), 0)
}
test "a hand up costs the body but leaves nothing below twenty" {
fresh()
needs_set(NEED_FOOD, 25.0)
needs_helped_up()
expect(needs_food() == 20.0)
expect(needs_warmth() == 85.0)
expect(needs_energy() == 75.0)
test "a hand up costs the body but leaves nothing below twenty" (base_st: mut BaseState, needs_st: mut NeedsState) {
fresh(base_st, needs_st)
needs_set(needs_st, NEED_FOOD, 25.0)
needs_helped_up(needs_st)
expect(needs_food(needs_st) == 20.0)
expect(needs_warmth(needs_st) == 85.0)
expect(needs_energy(needs_st) == 75.0)
}
test "a save section reads back the same body" {
fresh()
needs_set(NEED_FOOD, 33.5)
needs_set(NEED_WARMTH, 12.25)
needs_went_down()
test "a save section reads back the same body" (base_st: mut BaseState, needs_st: mut NeedsState) {
fresh(base_st, needs_st)
needs_set(needs_st, NEED_FOOD, 33.5)
needs_set(needs_st, NEED_WARMTH, 12.25)
needs_went_down(needs_st)
let root = save_tree()
save_section(root, "needs", 1, needs_save())
save_section(root, "needs", 1, needs_save(needs_st))
let text = save_encode(root)
needs_reset()
needs_reset(base_st, needs_st)
let n = load_section(save_decode(text), "needs")
needs_load(n.data, n.version)
expect(needs_food() == 33.5)
expect(needs_warmth() == 12.25)
expect_eq(needs_collapses(), 1)
needs_load(base_st, needs_st, n.data, n.version)
expect(needs_food(needs_st) == 33.5)
expect(needs_warmth(needs_st) == 12.25)
expect_eq(needs_collapses(needs_st), 1)
}
}

View file

@ -1,58 +1,58 @@
# verbs.ludic - the only way the needs change from outside; each is held to 0..100
# `n` more of a need (less, when negative): a meal, a drink, a shove from a bear
export function needs_feed(k: int, n: float) -> void {
if nd_ok(k) { nd_add(k, n) }
export function needs_feed(needs_st: mut NeedsState, k: int, n: float) -> void {
if nd_ok(k) { nd_add(needs_st, k, n) }
}
# a meal: food, water and energy at once
export function needs_meal(food: float, water: float, energy: float) -> void {
nd_add(NEED_FOOD, food)
nd_add(NEED_WATER, water)
nd_add(NEED_ENERGY, energy)
export function needs_meal(needs_st: mut NeedsState, food: float, water: float, energy: float) -> void {
nd_add(needs_st, NEED_FOOD, food)
nd_add(needs_st, NEED_WATER, water)
nd_add(needs_st, NEED_ENERGY, energy)
}
export function needs_set(k: int, v: float) -> void {
if nd_ok(k) { nd_lv()[k] = Math.clamp(v, 0.0, 100.0) }
export function needs_set(needs_st: mut NeedsState, k: int, v: float) -> void {
if nd_ok(k) { nd_lv(needs_st)[k] = Math.clamp(v, 0.0, 100.0) }
}
# at least `v`
export function needs_floor(k: int, v: float) -> void {
if nd_ok(k) and nd_lv()[k] < v { needs_set(k, v) }
export function needs_floor(needs_st: mut NeedsState, k: int, v: float) -> void {
if nd_ok(k) and nd_lv(needs_st)[k] < v { needs_set(needs_st, k, v) }
}
# a night: rested to what it was worth, hungrier and thirstier for it, but never empty
export function needs_after_night(rested: float, food_lost: float, water_lost: float) -> void {
let lv = nd_lv()
export function needs_after_night(needs_st: mut NeedsState, rested: float, food_lost: float, water_lost: float) -> void {
let lv = nd_lv(needs_st)
lv[NEED_ENERGY] = Math.clamp(rested, 0.0, 100.0)
lv[NEED_FOOD] = Math.max(lv[NEED_FOOD] - food_lost, 6.0)
if water_lost > 0.0 { lv[NEED_WATER] = Math.max(lv[NEED_WATER] - water_lost, 5.0) }
}
# the countdown called off, and nothing said (a wake, a load, a test)
export function needs_calm() -> void { nd_legs = 0.0 }
export function needs_calm(needs_st: mut NeedsState) -> void { needs_st.nd_legs = 0.0 }
# the body went down, however: one more collapse, and no countdown running
export function needs_went_down() -> void {
nd_collapses += 1
nd_legs = 0.0
export function needs_went_down(needs_st: mut NeedsState) -> void {
needs_st.nd_collapses += 1
needs_st.nd_legs = 0.0
}
# a hand up: the cost is in the body, and nothing goes below twenty
export function needs_helped_up() -> void {
let lv = nd_lv()
export function needs_helped_up(needs_st: mut NeedsState) -> void {
let lv = nd_lv(needs_st)
lv[NEED_WARMTH] = Math.max(20.0, lv[NEED_WARMTH] - 15.0)
lv[NEED_FOOD] = Math.max(20.0, lv[NEED_FOOD] - 15.0)
lv[NEED_WATER] = Math.max(20.0, lv[NEED_WATER] - 15.0)
lv[NEED_ENERGY] = Math.max(20.0, lv[NEED_ENERGY] - 25.0)
nd_legs = 0.0
needs_st.nd_legs = 0.0
}
# a new trip: fed, watered, rested and warm, and never walked out
export function needs_reset() -> void {
nd_lv()
nd_fill()
nd_legs = 0.0
nd_last = 0
nd_collapses = 0
q_clear(needs_facts())
export function needs_reset(base_st: mut BaseState, needs_st: mut NeedsState) -> void {
nd_lv(needs_st)
nd_fill(needs_st)
needs_st.nd_legs = 0.0
needs_st.nd_last = 0
needs_st.nd_collapses = 0
q_clear(base_st, needs_facts(base_st, needs_st))
}

View file

@ -3,51 +3,51 @@
const LAST_LEGS: float = 10.0
const GENTLE_FLOOR: float = 22.0
function nd_empty() -> int {
let lv = nd_lv()
function nd_empty(needs_st: mut NeedsState) -> int {
let lv = nd_lv(needs_st)
if lv[NEED_WARMTH] == 0.0 { return NEED_WARMTH }
if lv[NEED_FOOD] == 0.0 { return NEED_FOOD }
if lv[NEED_WATER] == 0.0 { return NEED_WATER }
return -1
}
function nd_clear() -> bool {
let lv = nd_lv()
function nd_clear(needs_st: mut NeedsState) -> bool {
let lv = nd_lv(needs_st)
return lv[NEED_WARMTH] > 5.0 and lv[NEED_FOOD] > 5.0 and lv[NEED_WATER] > 5.0
}
# Gentle: a need that runs down stops just above where the legs start to go
function nd_gentle() -> void {
let lv = nd_lv()
function nd_gentle(needs_st: mut NeedsState) -> void {
let lv = nd_lv(needs_st)
lv[NEED_WARMTH] = Math.max(lv[NEED_WARMTH], GENTLE_FLOOR)
lv[NEED_FOOD] = Math.max(lv[NEED_FOOD], GENTLE_FLOOR)
lv[NEED_WATER] = Math.max(lv[NEED_WATER], GENTLE_FLOOR)
nd_legs = 0.0
needs_st.nd_legs = 0.0
}
# `dt` real seconds; run it after everything this frame that could fill a need
export function needs_watch(dt: float) -> void {
export function needs_watch(base_st: mut BaseState, needs_st: mut NeedsState, dt: float) -> void {
if NeedsWorld.held() { return }
if NeedsWorld.gentle() {
nd_gentle()
nd_gentle(needs_st)
return
}
let need = nd_empty()
let need = nd_empty(needs_st)
if need < 0 {
if nd_legs > 0.0 and nd_clear() {
nd_legs = 0.0
nd_fact(NEEDS_RESTORED, nd_last)
if needs_st.nd_legs > 0.0 and nd_clear(needs_st) {
needs_st.nd_legs = 0.0
nd_fact(base_st, needs_st, NEEDS_RESTORED, needs_st.nd_last)
}
return
}
if nd_legs == 0.0 {
nd_legs = LAST_LEGS
nd_last = need
nd_fact(NEEDS_CRITICAL, need)
if needs_st.nd_legs == 0.0 {
needs_st.nd_legs = LAST_LEGS
needs_st.nd_last = need
nd_fact(base_st, needs_st, NEEDS_CRITICAL, need)
return
}
nd_legs = nd_legs - dt
if nd_legs > 0.0 { return }
nd_legs = 0.0
nd_fact(NEEDS_COLLAPSED, nd_last)
needs_st.nd_legs = needs_st.nd_legs - dt
if needs_st.nd_legs > 0.0 { return }
needs_st.nd_legs = 0.0
nd_fact(base_st, needs_st, NEEDS_COLLAPSED, needs_st.nd_last)
}