A line is cast at a point the FishingWorld port says is water (depth, or afloat), bait (asked, then said as FISHING_BAIT_USED for the game to take) halves the wait, the bite picks a species - a legend only on a lure, likelier in deep water and with skill, else an everyday one by weight - and the fight is a marker, a green band that moves above Relaxed and closes on Hard or for a fish whose data says so, a Gentle ring of one press, and a legend's runs and the line it takes back. Length and weight come from FishSpecies data. The package never draws, speaks or touches a pack; every step is a FishingFact (CAUGHT, BAIT_USED, LINE_SNAPPED, SLACK, MISSED, ...). Its dice are its own Rng. A legend on Gentle fights as a legend (the old ring lost him at once). Uses ludic.base only; thirteen tests over a fake lake. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
69 lines
3.8 KiB
Markdown
69 lines
3.8 KiB
Markdown
# ludic.fishing
|
|
|
|
One angler's line: a cast into water deep enough, the wait for a bite (bait halves it), the strike,
|
|
the reel's fight - a marker sweeping a bar and a green band to press in, a Gentle ring of one
|
|
press, a legend that runs and takes line back - and the landing, with the species, length and
|
|
weight from data. Uses [`ludic.base`](../ludic.base/README.md) and nothing else.
|
|
|
|
```ludic
|
|
import "ludic.fishing"
|
|
```
|
|
|
|
The package never draws, never speaks and never touches a pack. It reports what happened as facts
|
|
(`fishing_facts()`), and the game takes the bait, puts the fish in the pack, shows it and says the
|
|
line. Its dice are its own `Rng`.
|
|
|
|
## The species
|
|
|
|
`FishSpecies` is an `open registry` the game fills, `FISH_<KEY>` in the order written:
|
|
|
|
```ludic
|
|
def FishSpecies cutthroat { weight: 0.45 }
|
|
def FishSpecies brook { weight: 0.25, cm_min: 20, cm_spread: 10 }
|
|
def FishSpecies old_maroon { legend: true, cm_min: 70, cm_spread: 25, skill_cm: 0.0, need: 5, runs: 2, fight: 30.0, speed: 1.3, speed_per_hit: 0.22, closes: 0.12 }
|
|
```
|
|
|
|
| field | |
|
|
| --- | --- |
|
|
| `weight` | how often it takes, among the everyday ones |
|
|
| `cm_min`, `cm_spread`, `skill_cm` | length: `cm_min` + up to `cm_spread`, + `skill_cm` per point of skill |
|
|
| `kg_k` | weight is `kg_k x cm^3` (a trout by default) |
|
|
| `legend`, `chance`, `chance_deep` | takes only a lure, at `chance` in the shallows and `chance_deep` past the deep line or afloat (+ skill) |
|
|
| `need`, `runs`, `fight` | crossings to land it, runs it makes, seconds before the line goes slack |
|
|
| `speed`, `speed_per_hit`, `closes`, `floor` | the marker's sweep, and a window that narrows with each crossing won (at every rung) |
|
|
|
|
## The port
|
|
|
|
```ludic
|
|
export port FishingWorld {
|
|
depth: fn(float, float) -> float # metres of water at a point
|
|
afloat: fn() -> bool # in a boat (unbound: no)
|
|
has_bait: fn() -> bool # (unbound: no)
|
|
has_lure: fn() -> bool # (unbound: no)
|
|
skill: fn() -> float # gear or hands (unbound: 0)
|
|
bite_mult: fn() -> float # the wait, x (unbound: 1)
|
|
window: fn(int) -> float # the green's half width for a species (unbound: 0.12)
|
|
level: fn() -> int # FISHING_RELAXED / _STANDARD / _HARD / _GENTLE (unbound: relaxed)
|
|
}
|
|
```
|
|
|
|
## API
|
|
|
|
| | |
|
|
| --- | --- |
|
|
| `fishing_config(min_depth, deep, seed)` | 0.15 m to cast, 2.5 m is deep, the line's seed |
|
|
| `fishing_cast(x, z) -> int` | `FISHING_CAST_OK`, `FISHING_NOT_WATER`, `FISHING_BUSY` |
|
|
| `fishing_press()` | the one button: waiting it reels in, on a bite it strikes, on a fish it reels |
|
|
| `fishing_run(dt)`, `fishing_stop()`, `fishing_set_wait(secs)` | real seconds of line; put away; the bite in exactly `secs` |
|
|
| `fishing_state()` (`FISHING_OFF`, `_WAIT`, `_BITE_ON`, `_REEL`), `fishing_active()`, `fishing_species()`, `fishing_cm()`, `fishing_line_x()`, `fishing_line_z()`, `fishing_baited()`, `fishing_lured()` | the line |
|
|
| `fishing_marker()`, `fishing_window()`, `fishing_window_lo()`, `fishing_window_hi()`, `fishing_hits()`, `fishing_need()`, `fishing_running()`, `fishing_gentle()`, `fishing_ring()` | the fight, for a HUD |
|
|
| `fishing_caught()`, `fishing_biggest()`, `fishing_legend_caught()`, `fishing_record_set(n, cm, legend)` | the record |
|
|
| `fishing_weight_of(species, cm)`, `fishing_legend()`, `fishing_water_at(x, z)` | questions |
|
|
| `fishing_facts() -> Queue<FishingFact>` | `{ what, species, cm, kg, hits, need, bait, lure, x, z }`: `FISHING_CAST`, `_BAIT_USED`, `_BITE`, `_MISSED`, `_HOOKED`, `_GOOD`, `_CHECKED`, `_TURNED`, `_RUNS`, `_TIRED`, `_REELED_IN`, `_CAUGHT`, `_LINE_SNAPPED`, `_SLACK` |
|
|
| `fishing_reset()`, `fishing_system() -> System` | `"fishing"`, `PH_SIMULATE`: its tick, and the record in its save section (a line in the water is not saved) |
|
|
|
|
## Tests
|
|
|
|
```bash
|
|
ludic test packages/ludic.fishing
|
|
```
|