orbit/data/gates.json

88 lines
14 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"_comment": "JUMP GATES — the galaxy's highway layer (js/galaxy/JumpNetwork.js for the network, js/galaxy/SystemGenerator.js → layoutGates for the in-system placement). NETWORK: every system holds between minGates and maxGates jump gates; every gate jumps to one of the system's `neighborPool` nearest stars on the 2-D map (the roster's x/y plane). The network is a degree-limited spanning tree of that neighbor graph (tree edges run BOTH ways); `shortcuts` is OFF — no mesh, no one-way links, no closed loops: the tree has EXACTLY ONE route between any two systems, so the galaxy reads as a MAZE of dead ends and long hauls. The deliberate exception is `bridges` (below): cross-branch middle-belt links that open loops in the MIDDLE of the galaxy so one branch doesn't have to trek back to the trunk (home) to reach another (the faction design — data/factions.json). Strong connectivity still holds by construction (a bidirected tree — from any star you can reach any other, no closed systems, no trapped sets), and every jump is to a nearby star with a RETURN gate (the destination's gate pointing back — the player can always jump back the way they came). No system ever holds more than maxGates gates. A BARREN system (objectCount → 0, no planets/stations) is a DEAD-END LEAF of the tree — exactly one gate, in and out the same way (JumpNetwork takes the barren set from the per-system composition roll and never lets a barren node adopt children; the repair pass prefers non-barren attach targets). PLACEMENT (anchored systems): each gate sits EXACTLY one of the radius steps spanning `anchorMinDistance`..`anchorMaxDistance` (default 3500–4608 px) — CAPPED by the level-`anchorTetherLevel` tether range — from an ANCHOR — a PLANET, or the home world in the starting system. The band keeps the gate CLEARLY inside the level-1 tether of data/tether.json — ≤ 5120 − edgeMargin (512 px rim margin, so no gate sits at the edge of a tether region), hence STRICTLY < 5000 px from the anchor (Rule 1); a per-gate preferred step (a seeded fork) makes the distance VARY between gates — clearance/gap pressure only ever pushes a gate outward from its preference; all hard by construction. Free-space stations are deliberately NOT anchors: the build console lives on a WORLD, so every gate must be tether-reachable from a planet the player can build and expand out from (the composition roll demotes a station to a planet when a system's budget would otherwise run out, guaranteeing the anchor exists). Candidates are tried in facing quality (ray-circle intersection → circle point aimed at the star → ±75° scan) so each gate is on the target side of its anchor (within 90° of the system→star bearing) — the direction rule is soft, so a gate reads as 'facing that star' without the exact ray always being free. PLACEMENT (barren systems — no anchor, data/systems.json → objectCount.barren): the single gate sits ON the ray from the center toward its destination, `barrenDistance` px out (stepped outward within the minRadius..maxRadius band if the gap forces it), facing its destination; its payload is 1–2 asteroid clusters drifting inside the gate's level-1 tether (data/asteroids.json → barren). ACTIVITY: every gate record carries `active` (DEFAULT FALSE — the activation mechanic is future work). When a gate is activated, a level-1 tether (5120 px, data/tether.json) attaches to it: an active gate is a TETHER ANCHOR in its own right. In a barren system that tether is the player's entire room to move (the player arrives AT the gate — the center is empty: the star is invisible flavor, never rendered); in an anchored system it simply adds another anchor circle. A gate keeps `size` + `clearance` (px, center-to-center) from any solid disc (planets + free-space stations + the home world), 2·size + `gateGap` from any other gate, and stays between `minRadius` and `maxRadius` from the center. size = the gate's keepout radius (px). shipClearance = the ship's keepout from the gate (the solid rule, like planets). theme.color = the HUD/compass color (an inactive gate renders dimmed). typeLabel = the discovery/compass label. texture/frameWidth/frameHeight = the spritesheet (assets/images/jumpgate.png, 256×256 frames): frame 0 = the gate body (ring + pylons, drawn static), frame 1 = the active swirl (see swirl); sprite scale = (size×2)/frameWidth puts the ring's outer edge on the keepout disc. A missing sheet falls back to the built-in procedural gate (console note).",
"enabled": true,
"minGates": 1,
"maxGates": 3,
"neighborPool": 8,
"shortcuts": false,
"bridges": {
"_comment": "BRIDGES — the cross-branch links (js/galaxy/JumpNetwork.js, the bridge pass after the spanning tree). With shortcuts OFF the tree is a pure maze: branch-to-branch travel (a branch = a top-level subtree of the home system) means ALL the way back to the trunk. Bridges cut that: for each PAIR of distinct branches, `perPair` bidirectional gate-pairs join two systems of the middle belt (d in dMin..dMax — the home→far diagonal's middle slice, data/galaxy.json → distribution.zones), so the branches interconnect around the middle of the galaxy (a triangle for three branches) and a branch-to-branch trip is one extra hop, not a trek to the trunk — the faction design's \"reach each other without routing through home\" (data/factions.json). HARD RULES: both ends must sit in each other's `neighborPool` symmetric union (the LOCAL rule — a bridge is still a gate to a NEAR star, like any other), both ends non-barren (a dead-end leaf keeps its single gate), and both ends keep degree ≤ maxGates (the cap never breaks). SOFT: the middle-belt preference — an out-of-band pool pair is the fallback, and a pair with NO pool candidate degrades (a console note; the maze still connects the pair). Each bridge runs BOTH ways (a return gate at each end — the \"every jump has a return gate\" rule); planRoute's Dijkstra then prefers the shorter of bridge vs trunk automatically. Deterministic: same roster ⇒ same bridges (index tiebreaks).",
"enabled": true,
"perPair": 2,
"dMin": 0.37,
"dMax": 0.63
},
"size": 96,
"texture": "assets/images/jumpgate.png",
"frameWidth": 256,
"frameHeight": 256,
"shipClearance": 50,
"clearance": 256,
"gateGap": 192,
"minRadius": 2048,
"maxRadius": 20480,
"anchorTetherLevel": 1,
"anchorMinDistance": 3500,
"anchorMaxDistance": 4608,
"barrenDistance": 8192,
"typeLabel": "Jump Gate",
"theme": { "color": "#5fd4ff" },
"swirl": {
"_comment": "ACTIVE-GATE SWIRL — frame 1 of `texture` (the swirling energy disc that fills the gate's mouth). Shown only when the gate is active (activation below): it spins slowly CLOCKWISE at spinSpeed (rad/s — one revolution every 2π/spinSpeed s) and its alpha breathes between alphaMin and alphaMax, one breath cycle every 2π/breathRate s. Dormant gates show only frame 0 — the body is always drawn at full alpha, and the swirl's absence is the 'this one is live' tell.",
"spinSpeed": 0.3,
"alphaMin": 0.6,
"alphaMax": 0.9,
"breathRate": 1.8
},
"activation": {
"_comment": "ACTIVATION (the SYSTEM research category — js/research/SystemCategory.js builds the per-system tree from this): '{system} Map' is granted the moment the player is in the system (duration 0 — 'starting'), and 'Unlock Jumpgates' (researchDuration, in research.timeUnit seconds) becomes researchable once EVERY NAV point of the system is discovered (the central body, all planets, all space stations, all jump gates — the scene's discoverable set minus the asteroid clusters; data/game.json → discovery.distance). Completing it activates the system's gates AND the return gates in the systems they connect to — and per the ACTIVITY rule above, each activated gate anchors a level-`tetherLevel` tether at its own position (a TETHER ANCHOR in its own right; that tether saves/restores with the run's tether list). The map label/description copy is templated: `{system}` is replaced with the system name. The gates tech's label is FIXED (no `{system}`) — the console's node button is too narrow for the system name; the map tech carries it.",
"researchDuration": 45,
"tetherLevel": 1,
"mapTech": {
"label": "{system} Map",
"description": "Added the Solar System of {system} to the onboard NAV System. Discover all NAV points to unlock the system Jumpgates."
},
"gatesTech": {
"label": "Unlock Jumpgates",
"description": "With system NAV data complete, we have enough information to plot courses through this system's jumpgates."
}
},
"jump": {
"_comment": "THE JUMP (GameScene.jumpThroughGate): CLICK FLOW — a click on ANY gate opens the GATE COMM WINDOW (GameScene.openGateCommsPanel → the shared comms panel, js/ui/CommsPanel.js, in its gate variant: the LINK line reads ACTIVE/DORMANT, CANCEL just closes it) and REQUEST JUMP — a grayed ghost while the gate is dormant or with `enabled` off — transports the run to the connected system; the save pipeline in miniature (captureState → swap in the destination system + arrival position → prepareLoad → scene restart), so discovery/research/builds/minerals/playtime/activatedGates all carry over. ARRIVAL: the ship materialises near the destination's RETURN gate — the gate in the destination whose destination is the system just left (returnGateFor, js/galaxy/JumpTravel.js) — offset back along its facing (the gate faces the star we came from) by radius + shipClearance + ship radius + `arrivalGap`, i.e. just clear of its keepout, inside its activated-gate tether (per ACTIVITY above). One-way SHORTCUT jumps have no return gate (JumpNetwork: tree edges run both ways, shortcuts don't) — those land on the destination's star (its home-tether origin). The jump plays a full-screen one-shot clip (`video`, cover-scaled over the theme background) between the two systems — the destination is already staged behind it, so the scene restarts the moment the clip ends (or errors, or stalls); `videoVolume` is 0..1 (0 = silent). A DOUBLE-CLICK (two quick presses) skips the rest of the clip, same as the landing/takeoff clips. If `video` is empty/missing the jump falls back to the short `jumpDelayMs` cut. `enabled` is the feature switch; dormantToast is the dormant-state copy (dev/jump-travel.test.mjs pins its {system} placeholder); the toasts are templated ({dest}/{system}).",
"enabled": true,
"arrivalGap": 128,
"video": "assets/videos/jump.mp4",
"videoVolume": 1,
"jumpDelayMs": 420,
"toast": "JUMP — {dest}",
"dormantToast": "JUMP GATE DORMANT — CHART {system} TO ACTIVATE ITS JUMP GATES",
"miningToast": "CANNOT JUMP WHILE MINING",
"combatToast": "CANNOT JUMP WHILE UNDER ATTACK"
},
"route": {
"_comment": "ROUTE (js/galaxy/Route.js + GameScene): the player's plotted course to a SET DESTINATION (SYSTEM tab of the MAP console). The run persists only the destination (systemId + objectId); the path from the current system is derived (planRoute — the jump network is a spanning tree, so it is the unique route). While a route is active, the compass marks the NEXT STOP in ORANGE (compassColor), replacing that object's ordinary arrow, and shows it even while still UNDISCOVERED (it is the thing to find): in an intermediate system the next stop is that system's jump gate toward the destination (typeLabel 'Route Gate'); in the DESTINATION system the next stop is the destination OBJECT itself (destTypeLabel) — the last hop is across the system to the world, and it stays orange until the ship actually reaches it (GameScene._checkDestinationReached fires reachedToast, then the route clears). The NEXT STOP in this system ALSO wears an in-world orange pulse the moment it is on screen (js/entities/RoutePulse.js — GameScene marks it each frame): the FULL pulse on the route GATE (pulse), the SAME pulse SUBDUED on the destination OBJECT in the destination system (objectPulse). A jump to any other system re-plots from the detour (replotToast). A destination with NO specific object clears the moment its system is entered. setToast fires when the destination is set ({dest}/{hops}).",
"compassColor": "#ff8c1a",
"typeLabel": "Route Gate",
"destTypeLabel": "Destination",
"pulse": {
"_comment": "ROUTE GATE PULSE (js/entities/RoutePulse.js via JumpGate.setRouteTarget): while a destination is set and the route's next stop in the CURRENT system is a jump gate (GameScene.routeNextGate), that gate wears this animated pulse around its keepout ring — the in-world 'your destination is this gate' mark, visible the moment the gate is on screen (the compass's orange arrow is the off-screen half). A steady target ring at baseRadius × the gate's keepout size, plus two radar ripples (staggered half a period) that expand a further ×expand past it and fade out; one ripple cycle every period seconds. The color is compassColor (the route's orange). A DORMANT gate gets the pulse too — the player still has to find the gate to use it. The destination OBJECT (planet/space station) wears the SAME pulse in a subdued form — see objectPulse.",
"baseRadius": 1.18,
"expand": 0.55,
"period": 1.6,
"ringAlpha": 0.5,
"rippleAlpha": 0.85
},
"objectPulse": {
"_comment": "DESTINATION OBJECT PULSE (js/entities/RoutePulse.js via Planet.setRouteTarget / Station.setRouteTarget): in the DESTINATION system the route's last stop is the destination OBJECT (a planet or a space station — GameScene._routeDestinationEntity). It wears the SAME pulse as the route gate, SUBDUED: the same shape + timing language, but lower alpha, a smaller reach and a slower cycle — 'the place to go', without shouting like the gate's full pulse (pulse). The geometry still scales with the object's keepout radius (a station and a gas giant read the same).",
"baseRadius": 1.12,
"expand": 0.4,
"period": 1.8,
"ringAlpha": 0.35,
"rippleAlpha": 0.55
},
"setToast": "DESTINATION SET — {dest} · {hops} HOP(S)",
"reachedToast": "DESTINATION REACHED — {dest}",
"replotToast": "ROUTE RE-PLOTTED — {hops} HOP(S) TO {dest}"
}
}