95 lines
7.4 KiB
Markdown
95 lines
7.4 KiB
Markdown
# 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`](../ludic.base/README.md) and nothing else.
|
|
|
|
```ludic
|
|
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
|
|
|
|
```ludic
|
|
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
|
|
habitat_in: fn(sp, x, z) -> int # the map's painted habitat: 1 in, 0 out, -1 none painted
|
|
range_at: fn(sp, k, WildRange) -> bool # the map's k-th painted range (x, z, r; r 0 = the species')
|
|
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.
|
|
|
|
**A map may paint where a species lives.** Where `habitat_in` answers 0 or 1, a species' spots,
|
|
ranges and newcomers keep inside its painted areas (never under water) instead of its kind's rule;
|
|
where it answers -1 the rule stands. A range `range_at` answers is set out as painted, drawing no
|
|
dice; the ones it does not answer are placed by the dice as before. The package never reads the
|
|
map's tables: the game answers both from its Areas.
|
|
|
|
## 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_habitat_of(sp, hab, x, z)`, `wildlife_find_spot(hab, x0, z0, d0, d1)`, `wildlife_find_spot_of(sp, hab, ...)`, `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
|
|
|
|
```bash
|
|
ludic test packages/ludic.wildlife
|
|
```
|