ludic/packages/ludic.wildlife
2026-09-28 16:49:58 +03:00
..
tests fix(ludic.wildlife): a newcomer takes the record of one of its species removed for good - its row, its key and so the game's drawing, every other field as new 2026-09-28 16:08:23 +03:00
animal.ludic fix(ludic.wildlife): a newcomer takes the record of one of its species removed for good - its row, its key and so the game's drawing, every other field as new 2026-09-28 16:08:23 +03:00
birds.ludic ludic.base, ludic.wildlife, ludic.npc: fact records reused, not made per fact 2026-09-28 01:58:19 +03:00
fact_pool.ludic frame allocs (25.2): a fact record past the 64 made at the start is @alloc_ok, in its own function, in npc, character, physics, vehicles and wildlife 2026-09-28 16:02:42 +03:00
facts.ludic frame allocs (25.2): a fact record past the 64 made at the start is @alloc_ok, in its own function, in npc, character, physics, vehicles and wildlife 2026-09-28 16:02:42 +03:00
ground.ludic ludic.base, ludic.wildlife, ludic.npc: fact records reused, not made per fact 2026-09-28 01:58:19 +03:00
guest.ludic ludic.wildlife: the table a guest draws the host's animals into is made once and emptied per join, not new each time 2026-09-28 16:00:18 +03:00
habitat.ludic wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
index.ludic fix(ludic.wildlife): a newcomer takes the record of one of its species removed for good - its row, its key and so the game's drawing, every other field as new 2026-09-28 16:08:23 +03:00
lures.ludic ludic.base, ludic.wildlife, ludic.npc: fact records reused, not made per fact 2026-09-28 01:58:19 +03:00
migrate.ludic refactor(packages): 0.R5 - a question no longer starts its state - 64 lazy starts ('if st.x == null { st.x = ... }' inside a getter) are the state's defaults, gear__ensure is gone, and ludic migrate state --tighten took mut off 208 package parameters: things_all, thing_find, gear_charge, gear_level, jobs_state and the rest read 2026-09-27 00:51:03 +03:00
near.ludic feat(ecs): wildlife_near / wildlife_near_of into the caller's list, wildlife_alive_of 2026-09-27 21:54:39 +03:00
needs.ludic feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
package.ludic feat(packages): ludic.wildlife - a valley's animals: species handed in as data, ranges placed against the ground and moved with the season, the day's steering, the senses and a net alert that settles, feeding, lures and snares, birds, spawning and repopulation, its own dice, facts and a save section 2026-09-25 06:54:58 +03:00
places.ludic feat(packages): ludic.wildlife - a valley's animals: species handed in as data, ranges placed against the ground and moved with the season, the day's steering, the senses and a net alert that settles, feeding, lures and snares, birds, spawning and repopulation, its own dice, facts and a save section 2026-09-25 06:54:58 +03:00
port.ludic feat(wildlife): 17.5 - an animal walks by the world's way where it has one: WildlifeWorld.way (the next corner toward where it is going, asked every half second or on reaching it) steers wandering and going to water or forage, and goal (a spot it can reach) is a wander's target, seeded by the same two draws so the dice do not move; the defaults are the straight line and the old target. way_test: round a wall by its corners, and a wander's target where the world says 2026-09-27 21:51:35 +03:00
ranges.ludic refactor(packages): 0.R5 - a question no longer starts its state - 64 lazy starts ('if st.x == null { st.x = ... }' inside a getter) are the state's defaults, gear__ensure is gone, and ludic migrate state --tighten took mut off 208 package parameters: things_all, thing_find, gear_charge, gear_level, jobs_state and the rest read 2026-09-27 00:51:03 +03:00
react.ludic ludic.base, ludic.wildlife, ludic.npc: fact records reused, not made per fact 2026-09-28 01:58:19 +03:00
README.md feat(wildlife): 17.5 - an animal walks by the world's way where it has one: WildlifeWorld.way (the next corner toward where it is going, asked every half second or on reaching it) steers wandering and going to water or forage, and goal (a spot it can reach) is a wander's target, seeded by the same two draws so the dice do not move; the defaults are the straight line and the old target. way_test: round a wall by its corners, and a wander's target where the world says 2026-09-27 21:51:35 +03:00
record.ludic wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
reuse.ludic fix(ludic.wildlife): a newcomer takes the record of one of its species removed for good - its row, its key and so the game's drawing, every other field as new 2026-09-28 16:08:23 +03:00
run.ludic wip(ecs): ludic.wildlife on a Table<WildAnimal> - UNBUILT 2026-09-27 21:21:29 +03:00
senses.ludic refactor(packages): 0.R5 - a question no longer starts its state - 64 lazy starts ('if st.x == null { st.x = ... }' inside a getter) are the state's defaults, gear__ensure is gone, and ludic migrate state --tighten took mut off 208 package parameters: things_all, thing_find, gear_charge, gear_level, jobs_state and the rest read 2026-09-27 00:51:03 +03:00
spawn.ludic wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
species.ludic capacities (25.5a): @max on Physics' bounded lists - every fact pool and free list (1024), the walker slots (64), the shape and body-owner tables (262144), npc's walkers and posts (64) 2026-09-28 16:49:58 +03:00
steer.ludic ludic.wildlife (18.2): a walker a crowd moves 2026-09-28 00:27:12 +03:00
store.ludic fix(ludic.wildlife): a newcomer takes the record of one of its species removed for good - its row, its key and so the game's drawing, every other field as new 2026-09-28 16:08:23 +03:00
system.ludic refactor(packages): 0.R5 - a question no longer starts its state - 64 lazy starts ('if st.x == null { st.x = ... }' inside a getter) are the state's defaults, gear__ensure is gone, and ludic migrate state --tighten took mut off 208 package parameters: things_all, thing_find, gear_charge, gear_level, jobs_state and the rest read 2026-09-27 00:51:03 +03:00
table.ludic frame allocs (25.2): physics, npc, vehicles and wildlife's bounded growth declared - id tables to their most, a walker slot, a guest's copies, a player's own vehicle, a table at a load 2026-09-28 16:02:42 +03:00
walked.ludic ludic.wildlife (18.3): wildlife_listed - still one of this state's animals, so a crowd lets go of one a load or a clear removed 2026-09-28 00:36:31 +03:00
want.ludic feat(wildlife): 17.5 - an animal walks by the world's way where it has one: WildlifeWorld.way (the next corner toward where it is going, asked every half second or on reaching it) steers wandering and going to water or forage, and goal (a spot it can reach) is a wander's target, seeded by the same two draws so the dice do not move; the defaults are the straight line and the old target. way_test: round a wall by its corners, and a wander's target where the world says 2026-09-27 21:51:35 +03:00
way.ludic ludic.wildlife (18.2): a walker a crowd moves 2026-09-28 00:27:12 +03:00

ludic.wildlife

The animals of a valley: the ranges each species keeps to, and every animal's day - wander about home, go down to water and forage when thirst and hunger climb, notice the players and decide what to make of them, flee, charge, come to food and lures, walk into snares; birds circle, glide down, peck and lift off. Uses ludic.base and nothing else.

import "ludic.wildlife"

The package owns the animals' state, the steering, the senses, the alert, feeding and lures, spawning within ranges, its own dice (an Rng, never Random.*) and its own save section. It never draws anything: the game makes an actor when the port says an animal was born and keys its drawing by the animal's key, and it never decides what anything is worth - a feeding, a catch, a shove, a print are facts the game drains.

Species are data

The game hands the table in, once: wildlife_species_add(s) per species, in its own order, so the entry's place is the species number. A WildSpecies is key, walk, run, flee (its nerve, m), home (how far it roams), habitat (WILD_MEADOW, _FOREST, _SHORE, _CLIFF), tracks, size (0 small - a snare holds it), diet (WILD_PLANTS, _FISH, _HAY, _SEED), flies, tame, charges, forget (alert points a second it lets go of), land_shy, land_chance, keep_away, range (how wide one of its ranges is) and scale.

The port

export port WildlifeWorld {        # every member has a default: a flat dry meadow at noon, one silent player
  ground, water, slope: fn(float, float) -> float     # the ground, the water's surface, the gradient
  forest: fn(float, float) -> bool
  sight: fn(ax, ay, az, bx, by, bz) -> bool           # nothing between the two points
  push: fn(x, z, r, feet, head) -> bool, pushed_x, pushed_z   # a walker slid clear of still things
  players: fn() -> int                                # slots; player_on(p), player_x / _y / _z(p)
  hidden, noise, scent, stillness                     # per player: in a hide, 0..1, 0..1, WILD_MOVING / _STILL / _SITTING
  hour, light, winterness, wind, wind_dir: fn() -> float   # the clock, how well it sees now, the season, the weather
  wariness, charge_reach: fn() -> float               # every nerve times, how close a charger lets you come
  taught: fn() -> bool                                # feeding is being taught: food carries 175 m, feeders mind less
  about: fn(WildAnimal) -> int                        # WILD_ABOUT, WILD_AWAY (lying up) or WILD_GONE (a legend's hours ended)
  ranges, per_range: fn(int) -> int                   # a species' ranges on this map, and how many live in each
  spot: fn(WildAnimal, bool, WildSpot) -> bool        # water (true) or forage for it, filled in
  lures: fn() -> int                                  # how many things to ask `lure` about
  lure: fn(int, WildLure) -> bool                     # the i-th, filled in, when it is food, a lure or an empty snare
  present: fn(int) -> bool                            # a spot or lure by uid is still there
  next_id: fn() -> int                                # an animal's id across machines
  born: fn(WildAnimal) -> void                        # draw it
  placed: fn(WildRange, int) -> void                  # a range was set out: put its water and forage down
  way: fn(WildAnimal, float, float) -> bool           # the next corner of its way toward (x, z), into way_x / _z
  goal: fn(WildAnimal, x, z, r, seed) -> bool         # a spot about r from (x, z) it can walk to, into way_x / _z
}

An animal walks by the world's way where the world has one. Wandering and going to water or forage face the next corner way gives (asked again every half second, or on reaching the corner), and a wander's target is a spot goal says it can reach, seeded by the same two draws the old target took - so binding them changes where an animal walks and never the order of the dice. With no answer (the defaults) it walks straight and turns 75 degrees off water and a climb, as before. Maroon Lake binds both to its navmesh, walked by the species' habitat's taste.

API

wildlife_species_clear(), wildlife_species_add(s) -> int, wildlife_species(sp), wildlife_species_count() the table
WildAnimal, wildlife_all(), wildlife_count(sp), wildlife_total(), wildlife_by_nid(nid), wildlife_count_all() the animals; state is WILD_IDLE .. WILD_LAND
wildlife_new(sp, x, z, legend), wildlife_make(sp, x, z, legend, yaw, variant, scale, nid), wildlife_remove(a), wildlife_clear() making one off the package's dice or exactly as given (a guest's copy), and taking one away
wildlife_spawn(x0, z0), wildlife_morning() -> int, wildlife_repopulate() -> int, wildlife_seed(n) a valley filled; each morning the ranges move with the season and each range under its target gets one back, out of every player's sight
WildRange, wildlife_ranges(), wildlife_range(i), wildlife_range_dist(r, x, z), wildlife_range_alive(i), wildlife_ranges_visit(x, z), wildlife_range_found_near(sp, x, z), wildlife_ranges_migrate() ranges, found by standing in them
wildlife_habitat_ok(hab, x, z), wildlife_find_spot(hab, x0, z0, d0, d1), wildlife_spot_x(), wildlife_spot_z() the right ground, and a spot of it
wildlife_tick(dt, gh), wildlife_tick_ground(a, dt, gh), wildlife_tick_bird(a, dt), wildlife_stride(a, dt), wildlife_hold(on) the step; pinned on an animal leaves it where it stands
wildlife_alert_rate(a, p, d), wildlife_alert_decay(a, p), wildlife_downwind(a, p), wildlife_can_see(a, p) what it makes of player p
wildlife_in_snare(uid), wildlife_release(a) a snare's catch
wildlife_saw(sp) -> bool, wildlife_seen(sp), wildlife_seen_mask(), wildlife_set_seen(m), wildlife_mark_fed(sp), wildlife_fed_mask(), wildlife_fed_any(), wildlife_set_fed_any(b) this player's record
wildlife_put_away(), wildlife_bring_back(), wildlife_away() a machine drawing another's animals keeps its own aside
wildlife_facts() -> Queue<WildFact> WILD_SPOTTED, _FLED, _FED (lure, owner, diet), _CAUGHT (lure, owner), _CHARGED, _SHOVED (player), _PRINT, _SCAT, _BED, _WENT
wildlife_system() -> System "wildlife", PH_SIMULATE, no tick (the game steps it where the world is owned): reset, and its save section - the record and the found ranges

The alert is net: what a player's sight, scent and noise add is weighed against what the animal forgets every tick, so it SETTLES at a level the distance sets rather than climbing to 100 and sticking there - wildlife_test holds where it settles, not only how it rises.

Tests

ludic test packages/ludic.wildlife