49 lines
5.5 KiB
JSON
49 lines
5.5 KiB
JSON
{
|
||
"_comment": "RESEARCH — time-based tech, one project at a time (maxConcurrent). The command deck's RESEARCH button opens the full-screen research console (js/ui/ResearchWindow.js). Categories are the tab row; each category's TECH TREE lives in its own file under data/research/ — the file basename is the config section AND the category id (data/research/exploration.json → section 'exploration'). A tree is a branching DAG drawn top→down: `nodes` is a flat map (data order matters — roots/children lay out left-to-right in this order), each node's `requires` names the parents that must be RESEARCHED before it is (the DAG edges — authoritative). Research rules: start an available node → its `duration` (timeUnit) runs; when it completes the node is unlocked, its `effects` apply (the scene applies them — the seam), and its children become available. `unlocks` is what the tech OPENS beyond follow-on tech: `builds` = ids in data/builds.json the tech makes available (the build's own `requires` — \"category/node id\" — is the authoritative gate; the declaration here is what the console's UNLOCKS readout and the test validate), `research` = readable mirror of the children's `requires` edges. `starting` names nodes a fresh run already owns. A node may additionally carry `quest` (see _questGate) — an optional STORY-QUEST reward-claim gate layered on `requires`. Keys starting with `_` are documentation, not data.",
|
||
"enabled": true,
|
||
"timeUnit": "seconds",
|
||
"maxConcurrent": 1,
|
||
"defaultCategory": "system",
|
||
"categories": [
|
||
{ "id": "system", "label": "System", "accent": "#5fd4ff", "dynamic": true },
|
||
{ "id": "mining", "label": "Mining", "accent": "#ffc94d" },
|
||
{ "id": "cargo", "label": "Cargo", "accent": "#7ce8a4" },
|
||
{ "id": "exploration", "label": "Exploration", "accent": "#00e5ff" }
|
||
],
|
||
"_dynamic": "A category flagged `dynamic: true` has NO data/research/<id>.json — its tree is built at runtime per situation (js/research/SystemCategory.js: one tech pair per solar system, named after the system the player is in — its Map tech is granted on entry, its jumpgate tech researchable once every NAV point — planets, space stations, jump gates — is discovered, and activation flips the gates on, here and in the linked systems' return gates). Static categories keep the one-file-per-category rule; the tests exempt dynamic ones from the tree-file check.",
|
||
"video": {
|
||
"_comment": "The console's left-panel feed: loops muted while the window is open. aspect is [w, h] — the file is 544×800 (a 2:3 portrait).",
|
||
"file": "assets/videos/research-computer.mp4",
|
||
"aspect": [2, 3]
|
||
},
|
||
"fx": {
|
||
"_comment": "Window dressing. glitch = occasional RGB-split slice bursts; sweep = the scan band that crawls down the video feed; tabShimmer = the READY-RESEARCH cue on the category tabs (the deck's RESEARCH-slot shimmer's twin — data/actionbar.json → animation.shimmer, js/ui/ShimmerCue.js).",
|
||
"glitch": { "enabled": true, "intervalMs": [6000, 13000], "durationMs": [220, 420] },
|
||
"sweep": { "enabled": true, "everyMs": [4200, 8600], "durationMs": 1500 },
|
||
"tabShimmer": {
|
||
"_comment": "Per frame while the console is open + no project in flight (maxConcurrent 1): every category tab whose tree still holds an available tech wears the edge comet (ResearchWindow._paintCues). The face sweep is deck-only (faceMs/gapMs 0 — the comet owns its window); tabs are staggered i*420 ms. color is the comm pulse's yellow family.",
|
||
"enabled": true,
|
||
"color": "#ffd84d",
|
||
"periodMs": 4200,
|
||
"faceMs": 0,
|
||
"gapMs": 0,
|
||
"edgeMs": 1500,
|
||
"dashFrac": 0.3
|
||
}
|
||
},
|
||
"_nodeTemplate": {
|
||
"label": "Tech name",
|
||
"description": "What this tech is and what it does — shown in the detail readout at the bottom of the console.",
|
||
"icon": "tether",
|
||
"duration": 120,
|
||
"requires": ["parent-tech-id"],
|
||
"unlocks": {
|
||
"builds": ["build-id-in-builds-json"],
|
||
"research": ["child-tech-id"]
|
||
},
|
||
"effects": { "tether": { "level": 2 } }
|
||
},
|
||
"_questGate": "Optional node field — `quest: <quest id>`: a STORY-QUEST gate on top of `requires`. The named quest (data/quests.json) must have its reward CLAIMED (the dossier's CLAIM action — the same paid-out witness the run's chain uses to grant the next quest) before the node is researchable. The model stays pure: the scene supplies the live predicate, and js/research/ResearchModel.js → attachQuestGates wires it into the tree's optional `available`/`lockNote` hooks (isAvailable consults the hook like any extra gate — the SYSTEM category's chart gate is the same seam; the console's LOCKED readout shows the quest's title via lockNote). In use: mining/premium_arm + mining/advanced_storage (data/research/mining.json) and cargo/cargo_bay_8 (data/research/cargo.json) wait on 'special_delivery'.",
|
||
"_buildGate": "Optional node field — `build: <build id>`: a BUILD-COMPLETION gate on top of `requires`. The named build (data/builds.json) must be BUILT before the node is researchable (for a ship upgrade, built anywhere — the scene's live predicate reads its BuildState built set). Same pure seam as the quest gate: js/research/ResearchModel.js → attachBuildGates wires it into the same optional `available`/`lockNote` hooks (the console's LOCKED readout shows `<BUILD LABEL> BUILD REQUIRED` via lockNote). The two gates compose (each is checked first in the order it was attached). In use: cargo/cargo_bay_12 waits on the mining-storage-improved build, cargo/cargo_bay_16 on mining-storage-advanced (data/research/cargo.json)."
|
||
}
|