# Research and discoveries in your game (research.samples) ## What it is for `research.samples` turns items the player brings back into discoveries. A **sample** is an item of `inventory.items`; studying one at a lab takes a moment, uses the item up and yields one **finding** drawn among those the sample can give. A finding is a document: a name, a rarity label and the actions it runs when found (unlock a recipe, a structure, a species, give an item, set a variable, emit an event). The kit knows nothing of what is studied or learned: that belongs to the other kits of the game. A codex keeps what was found and shows the rest as unknown. It suits exploration, archaeology, science, cooking, alien technology and detective games. ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "research.samples" } gripforge_gamekit_configure { project_id: "gkp_…", id: "research.samples", config: { "needsLab": true, "labReach": 5 } } ``` It requires `inventory.items` and `game.engine`, so installing it brings them. Targets: web and the Rust kit host (native player and the engine wrappers). ## First example Two collections in the game data, and an `inventory.items` item `shard`: ```json "samples": [ { "id": "shard", "name": "Shard", "item": "shard", "seconds": 0.5, "findings": ["map", "rune"] } ], "findings": [ { "id": "map", "name": "Map", "weight": 3 }, { "id": "rune", "name": "Rune" } ] ``` With the default `needsLab`, `study("shard")` is refused with reason `lab` until a lab is open (`placeLab` then `open`), then with `item` while the bag has no shard. With a shard held it answers `{ ok: true }`; a second call answers `busy`. After 0.5 s `research.found` fires with one finding. Both findings are found once, so the third study ends with `research.nothing`, taking its shard. ## Settings and defaults | Setting | Type | Default | Meaning | |---|---|---|---| | `needsLab` | boolean | `true` | A study needs an open lab. Off: samples are studied anywhere, from the screen the research key opens. | | `labReach` | number (1 to 30) | `4` | The screen of a lab closes when the player walks further than this many metres. A running study goes on. | | `labLabel` | string | `Use {name}` | Prompt of a lab; `{name}` is its label. | Input: `research_menu` (key J) opens or closes the research screen. Without a lab it shows the codex alone, unless `needsLab` is off. ## Data: samples and findings A sample (collection `samples`) has `id`, `name`, `item` (required: one is used up per study), `findings` (required: ids it can yield; a finding may be listed by several samples), `seconds` (study length, default 5; 0 is at once), `description` and `fallback` (actions run when nothing is left to find). A finding (collection `findings`) has `id`, `name` (required), `description`, `rarity` (a free display label such as common, rare, unique), `weight` (share of the draw among the findings left, default 1; 0 is never drawn, only granted), `once` (found a single time, default true; false lets it come again, e.g. a material) and `actions` run when found. The draw depends only on the game seed and the number of draws made, so the same game gives the same findings and a loaded game continues as the saved one would have. A finding found in one sample leaves the draws of every sample that lists it. ## Lab, service and verbs A lab is any entity carrying `research.lab` (`{ label? }`): an editor instance with the `research_lab` component, a placed structure whose components carry `research.lab`, or `placeLab` from a script. With `interaction.use` it offers the `labLabel` prompt and using it opens the research screen. Service `research.samples`: - `samples()`: the samples with `held`, `found`, `total` and `exhausted`. - `findings()`: the findings with `found` (times found). - `study(id)`: `{ ok, reason? }`; reasons `unknown`, `lab`, `busy`, `item`. - `job()`: `{ sample, left, total, ratio }` or null. - `found(finding)`: times found. `grant(finding)`: counts a finding and runs its actions, no study. - `open(lab?)`, `close()`, `current()`, `placeLab(at, yaw?, label?)`. Trigger actions: `grant_finding { finding }`, `study_sample { sample }` (one draw at once, as a reward: no lab, no time, no item used). Conditions: `finding_found { finding, count? }`, `sample_exhausted { sample }`. ## Events and variables | Name | Detail | |---|---| | `research.started` | `{ sample, seconds }`: one item was taken; the finding comes after `seconds`. | | `research.found` | `{ sample, finding, name, rarity, first }`; `sample` is empty for a grant. `first`: never found before. | | `research.nothing` | `{ sample }`: nothing left to find (or a tap cancelled); the sample `fallback` runs. | | `research.failed` | `{ sample, reason }`: `unknown`, `item`, `busy`, `lab`, or the reason of a cancelling tap (`refused` by default). | | `research.opened` | `{ lab }`: empty when opened by the key. | | `research.closed` | The screen closed (key, walked away, game paused, or `close()`). | | `research.open` | Variable: the screen is open. | | `research.found` | Variable (number): distinct findings found. | | `research.studying` | Variable (string): the sample being studied, empty when none. | It listens to `interaction.requested`, `entity.spawned` and `game.phase` (the screen closes when the game pauses). The saved state is the count of each finding, the running study and the number of draws. ## Hooks and modding | Hook | Payload | Mutable | Effect | |---|---|---|---| | `research.study` | `{ sample, item, seconds, lab }` | `seconds` | Before the item is taken. Cancelled: nothing is taken and `research.failed` carries the tap reason. | | `research.find` | `{ sample, finding, rarity, first }` | `finding` | Before a finding is counted. Change `finding` to another finding id; cancelled: nothing is found (a study ends as `research.nothing`, a grant answers false). | ```json { "id": "quick_ore", "hook": "research.study", "match": { "sample": "ore" }, "mul": { "seconds": 0.5 } } ``` A mod may override `needsLab`, `labReach` and `labLabel`, and add, patch or remove entries of `samples` and `findings`. ## When it does not do what you expect - `research.failed` with `lab`: no lab is open and `needsLab` is on; open one or turn `needsLab` off. - `research.failed` with `item`: the bag holds none of the sample `item`. - A finding never comes again: `once` defaults to true; set `"once": false`. - A finding never appears: its `weight` is 0 (granted only), or no sample lists it. ## Limits - One study at a time. - The kit does not define what a finding does: its `actions` call the other kits. - No built-in lab model: provide a `research_lab` asset. ## Versions Kit 1.1.1; service `research.samples` 1.0.0. Hooks and moddable data since 1.1.0 (`game.engine` 1.2).