feat(packages): ludic.jobs - written jobs gated by standing and story, boards of posts rolled by the day on their own dice, progress counted from events and read back from state, handing in, rewards through a port, facts, a save section per scope
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
e146c7a337
commit
411206e45a
14 changed files with 984 additions and 0 deletions
74
packages/ludic.jobs/README.md
Normal file
74
packages/ludic.jobs/README.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# ludic.jobs
|
||||
|
||||
Work a player takes on for pay: written jobs offered by standing and story, boards of posts rolled
|
||||
by the day, progress counted from what happens and read back from what the state proves, handing
|
||||
in, and the reward paid through the game. Uses [`ludic.base`](../ludic.base/README.md) and nothing
|
||||
else.
|
||||
|
||||
```ludic
|
||||
import "ludic.jobs"
|
||||
```
|
||||
|
||||
A job is `(kind, param, need)` plus a reward (`money`, `rep`), a scope (`JOBS_SELF` yours,
|
||||
`JOBS_PARTY` the party's) and a gate (`minrep` in its scope, `minstage` of the game's story). What a
|
||||
kind measures is the game's: it counts a deed with `jobs_count(kind, param, n)`, and answers
|
||||
`JobsWorld.evidence(kind, param)` with what the state already proves (-1: nothing). The game fills
|
||||
the registries - `def Jobs from "jobs.lres"`, `def JobBoards from "boards.lres"` - and words every
|
||||
line itself.
|
||||
|
||||
## The rules it keeps
|
||||
|
||||
1. **Events count, state proves, progress only goes up.** A job under way takes `jobs_count` deeds
|
||||
and is reconciled every tick against the evidence, taking whichever is further; work done
|
||||
before a written job was taken counts, and a post counts only what came after it went up.
|
||||
2. **A job with a need is ready when it is met**; one with `need: 0` (and no kind) is the game's
|
||||
own play, which says so with `jobs_ready` or pays at once with `jobs_complete`.
|
||||
3. **A board rolls from its own dice.** `jobs_morning()` replaces every empty, stale (`keep` days)
|
||||
or earlier-finished post, seeding the jobs' `Rng` from the day, the board, the slot and how many
|
||||
posts came before, and asking `JobsWorld.roll(board, slot)`, which draws with `jobs_roll` and
|
||||
posts with `jobs_post`. The same day rolls the same board on every machine.
|
||||
4. **Scopes have keepers.** Where `JobsWorld.owns(scope)` is false (a guest's party jobs),
|
||||
`jobs_count`, the recheck and the morning leave them alone; the keeper applies another member's
|
||||
deed with `jobs_count_in(scope, ...)` and a guest takes the keeper's word with `jobs_set` /
|
||||
`jobs_post_set`.
|
||||
5. **Paid once, through a port.** `jobs_hand_in` / `jobs_post_hand_in` (or the moment an `auto`
|
||||
board's post is met) calls `JobsPay.reward(scope, money, rep)`.
|
||||
|
||||
## The ports
|
||||
|
||||
```ludic
|
||||
export port JobsWorld {
|
||||
rep: fn(int) -> int # standing in a scope (unbound 0)
|
||||
stage: fn() -> int # the story's stage (0)
|
||||
open: fn(int) -> bool # a job the game allows today (all)
|
||||
evidence: fn(int, int) -> int # (kind, param) as the state proves it (-1)
|
||||
day: fn() -> int # (1)
|
||||
owns: fn(int) -> bool # this machine keeps the scope (all)
|
||||
roll: fn(int, int) -> void # (board, slot): post with jobs_post (nothing)
|
||||
}
|
||||
export port JobsPay { reward: fn(int, int, int) -> void } # (scope, money, rep), required
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| `JobDef { key, group, vendor, kind, param, need, money, rep, minrep, minstage, scope, title, text, how, icon }`, `open registry Jobs ... as JOB` | a written job (`group` is the game's list, `JOBS_GROUPS` of them) |
|
||||
| `JobBoard { key, slots, keep, auto, refill, scope }`, `open registry JobBoards ... as BOARD` | a board of posts |
|
||||
| `JOBS_OFFERED`, `JOBS_ACTIVE`, `JOBS_READY`, `JOBS_DONE`; `JOBS_SELF`, `JOBS_PARTY` | states and scopes |
|
||||
| `jobs_state(i)`, `jobs_have(i)`, `jobs_offered(i)`, `jobs_under_way(i)`, `jobs_ready_now(i)` | a job |
|
||||
| `jobs_take(i)`, `jobs_ready(i)`, `jobs_hand_in(i)`, `jobs_complete(i)`, `jobs_set(i, state, have)` | its verbs |
|
||||
| `jobs_set_have(i, v)`, `jobs_add_have(i, n)`, `jobs_mark(i, bit)` | the game's own play |
|
||||
| `jobs_count(kind, param, n)`, `jobs_count_in(scope, kind, param, n)`, `jobs_recheck()` | progress |
|
||||
| `jobs_at(group, vendor)`, `jobs_taken(group)`, `jobs_shown(group, most)`, `jobs_first_open(group)`, `jobs_done(group)`, `jobs_set_done(group, n)` | lists and counts |
|
||||
| `jobs_morning()`, `jobs_roll(lo, hi)`, `jobs_post(b, s, kind, param, need, money, rep)`, `jobs_config_seed(seed)` | posting |
|
||||
| `jobs_slots(b)`, `jobs_posted(b)`, `jobs_post_kind / _param / _need / _have / _money / _rep / _state / _day(b, s)`, `jobs_posts_done(b)`, `jobs_total(b)` | the boards |
|
||||
| `jobs_post_hand_in(b, s)`, `jobs_post_set(b, s, ...)`, `jobs_set_total(b, n)` | a post's verbs |
|
||||
| `jobs_facts() -> Queue<JobsFact>` | `{ what: JOBS_F_OFFERED / _PROGRESSED / _COMPLETED / _HANDED_IN, job, board, slot, kind, param, have, need, money, rep, scope }` |
|
||||
| `jobs_system()`, `jobs_party_system()` | `"jobs"` (yours, the tick) and `"jobs.party"` (the party's), each saving its scope's jobs and boards by key |
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
ludic test packages/ludic.jobs
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue