Auto-install level-1 tether on first landing of any world
- ensureLandingTether runs before SurfaceScene launch so the surface HUD already reads Tether Level 1; free + instant, no console step - Extend tether lookups (_applyBuildEffects, tetherLevelFor, _rematerializeBuiltTethers, applyRestore) to include systemStations so station tethers anchor, level up, and re-anchor like planets' - Idempotent: home seed, existing build record, or an already-hosted tether all short-circuit; adopt rather than double-anchor - tether-l1 targets now ["planet", "station"]; update descriptions in builds.json, README, PROJECT_NOTES - Reorder MapWindow tabs to CURRENT SYSTEM / GALAXY / SYSTEM (galaxy is the live middle tab) and keep SYSTEM as the armed standby target
This commit is contained in:
parent
0e83021542
commit
a4fa37ca46
|
|
@ -136,8 +136,10 @@ node dev/server.mjs 8080
|
|||
(research, tether level); installed ones read **BUILT ✓**. Selecting a
|
||||
build shows its dossier (glyph + description) with the **highlighted
|
||||
cost** and a **BUILD** button that appears only when the build is
|
||||
available and affordable (one build at a time, `maxConcurrent: 1`). The
|
||||
home world starts with **Tether – Level 1** already installed; **Tether
|
||||
available and affordable (one build at a time, `maxConcurrent: 1`).
|
||||
Every world — planet or deep-space station — gets **Tether – Level 1**
|
||||
installed automatically the moment you first land (free + instant, no
|
||||
console step); the home world ships with it from the start. **Tether
|
||||
– Level 2** needs the level-2 tether research plus a world holding a
|
||||
level-1 tether, and installs in **20 s for 200 minerals** — the planet's
|
||||
tether range then grows from 5120 to 10240 px (world state that outlives
|
||||
|
|
|
|||
|
|
@ -46,10 +46,10 @@
|
|||
"builds": {
|
||||
"tether-l1": {
|
||||
"label": "Tether - Level 1",
|
||||
"description": "The world's first tether: a level-1 field (range 5120 px) anchoring the ship's travel rim around the planet. The home world ships with it already in place; on every other world this is the build that installs it.",
|
||||
"description": "The world's first tether: a level-1 field (range 5120 px) anchoring the ship's travel rim around the world. Installed automatically the moment you first land — free + instant, on planets and deep-space stations alike (GameScene.ensureLandingTether); the home world ships with it already in place.",
|
||||
"icon": "tether",
|
||||
"category": "planet",
|
||||
"targets": ["planet"],
|
||||
"targets": ["planet", "station"],
|
||||
"cost": {},
|
||||
"duration": 0,
|
||||
"requires": [],
|
||||
|
|
@ -187,7 +187,7 @@
|
|||
"icon": "tether | tether2 | tether3 | anchor | signal | mining | mining2 | storage | diamond — procedural glyph (js/research/ResearchIcons.js)",
|
||||
"category": "planet | cargo | mining — which tab of the console (registry above)",
|
||||
"targets": ["planet"],
|
||||
"_targets": "[\"planet\"] = installed on the planet's surface; [\"ship\"] = a SHIP upgrade (mining arms/storage): built from any planet's console, the effect is ship-wide and it counts as BUILT wherever the ship lands (never re-bought)",
|
||||
"_targets": "[\"planet\", \"station\"] = installed on the world's surface (a planet / a deep-space station); [\"ship\"] = a SHIP upgrade (mining arms/storage): built from any planet's console, the effect is ship-wide and it counts as BUILT wherever the ship lands (never re-bought)",
|
||||
"cost": {
|
||||
"credits": 0,
|
||||
"minerals": 200
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"_comment": "MAP — the deck's MAP button (to the right of SHIP): the full-screen cartography console, js/ui/MapWindow.js (depth 80, the ResearchWindow idiom). LEFT = the cartography feed (assets/videos/map.mp4, a muted 2:3 loop, the same recipe as the research archive feed). RIGHT = tabs on top (CURRENT SYSTEM — the live system chart, SYSTEM — a CHARTED star's chart, picked from the GALAXY tab; armed until then, and GALAXY — the whole-galaxy chart: glowing, pulsing stars per archetype, the jump-lane web with traveled lanes bright + flow packets, the charted region's outline — js/ui/GalaxyView.js), and the readout on the bottom (per tab: the system's discovery/resources, or the galaxy's charted stats). THE CHART (the magic, drawn to a canvas texture): the whole solar system — every object's extent (planets, stations, gates AND asteroid clusters, discovered or not) + `bounds.padding` px of padding on each edge — fitted to the plate. Only DISCOVERED objects are drawn (planets / stations / gates / rock fields, small representations). The TETHER RANGE is drawn as the union boundary (Tether.visibleArcs) with a soft zone glow inside, and everything OUTSIDE the tether union is grayed out (a fog-of-war dim pass with feathered rim) — so the chart reads as 'here is what you have charted, and here is the room to expand'. A live SHIP marker + hover tooltips + click-to-autopilot (GameScene.autopilotTo) sit on top. All colors/motion are steered here.",
|
||||
"_comment": "MAP — the deck's MAP button (to the right of SHIP): the full-screen cartography console, js/ui/MapWindow.js (depth 80, the ResearchWindow idiom). LEFT = the cartography feed (assets/videos/map.mp4, a muted 2:3 loop, the same recipe as the research archive feed). RIGHT = tabs on top (CURRENT SYSTEM — the live system chart, GALAXY — the whole-galaxy chart: glowing, pulsing stars per archetype, the jump-lane web with traveled lanes bright + flow packets, the charted region's outline — js/ui/GalaxyView.js, and SYSTEM — a CHARTED star's chart, picked from the GALAXY tab; armed until then), and the readout on the bottom (per tab: the system's discovery/resources, or the galaxy's charted stats). THE CHART (the magic, drawn to a canvas texture): the whole solar system — every object's extent (planets, stations, gates AND asteroid clusters, discovered or not) + `bounds.padding` px of padding on each edge — fitted to the plate. Only DISCOVERED objects are drawn (planets / stations / gates / rock fields, small representations). The TETHER RANGE is drawn as the union boundary (Tether.visibleArcs) with a soft zone glow inside, and everything OUTSIDE the tether union is grayed out (a fog-of-war dim pass with feathered rim) — so the chart reads as 'here is what you have charted, and here is the room to expand'. A live SHIP marker + hover tooltips + click-to-autopilot (GameScene.autopilotTo) sit on top. All colors/motion are steered here.",
|
||||
"enabled": true,
|
||||
"title": "STELLAR CARTOGRAPHY",
|
||||
"meta": "NAV DATA // LIVE FEED",
|
||||
|
|
@ -12,25 +12,25 @@
|
|||
]
|
||||
},
|
||||
"tabs": {
|
||||
"_comment": "The sockets above the chart (top right), equal widths. `currentSystem` is the live system chart (default). `system` is a CHARTED star's chart (the same painter, no ship/tether) — it wakes from standby the first time the player picks that star on the GALAXY tab; until then it's the armed killswitch. `galaxy` is the whole-galaxy chart (js/ui/GalaxyView.js). `standby: true` on ANY socket is the killswitch — the tab renders dimmed with its standbyTag and a press fires the scene's onLocked (a console toast) instead of switching.",
|
||||
"_comment": "The sockets above the chart (top right), equal widths, in this order. `currentSystem` is the live system chart (default). `galaxy` is the whole-galaxy chart (js/ui/GalaxyView.js). `system` is a CHARTED star's chart (the same painter, no ship/tether) — it wakes from standby the first time the player picks that star on the GALAXY tab; until then it's the armed killswitch. `standby: true` on ANY socket is the killswitch — the tab renders dimmed with its standbyTag and a press fires the scene's onLocked (a console toast) instead of switching.",
|
||||
"currentSystem": {
|
||||
"id": "currentSystem",
|
||||
"label": "Current System",
|
||||
"accent": "#00e5ff"
|
||||
},
|
||||
"system": {
|
||||
"id": "system",
|
||||
"label": "System",
|
||||
"accent": "#a8ffc4",
|
||||
"standby": true,
|
||||
"standbyTag": "NO TARGET"
|
||||
},
|
||||
"galaxy": {
|
||||
"id": "galaxy",
|
||||
"label": "Galaxy",
|
||||
"accent": "#ffc94d",
|
||||
"standby": false,
|
||||
"standbyTag": "OFFLINE"
|
||||
},
|
||||
"system": {
|
||||
"id": "system",
|
||||
"label": "System",
|
||||
"accent": "#a8ffc4",
|
||||
"standby": true,
|
||||
"standbyTag": "NO TARGET"
|
||||
}
|
||||
},
|
||||
"galaxyLockedToast": "GALAXY MAP OFFLINE — SECTOR DATA NOT ACQUIRED",
|
||||
|
|
|
|||
|
|
@ -396,12 +396,6 @@ the plate:
|
|||
- **CURRENT SYSTEM** — the system chart (canvas-painted: discovered
|
||||
objects, the tether union, the fog of war), hover + ENGAGE AUTOPILOT,
|
||||
wheel zoom / drag pan. Unchanged by the galaxy work.
|
||||
- **SYSTEM** — the same painter applied to a CHARTED star's system
|
||||
(no ship/tether): pick a charted star on the GALAXY tab to arm it, then
|
||||
click an object on its chart to **SET DESTINATION** (see the Route
|
||||
section below). Locked ("NO SYSTEM TARGET") until a charted star is
|
||||
picked. The shared `ConfirmOverlay` is label-sized to fit the dialog
|
||||
(`baseWidth` + measured width, per-show `width`/`height` overrides).
|
||||
- **GALAXY** (live — `tabs.galaxy.standby` is the killswitch; `true`
|
||||
returns it to the old "OFFLINE" toast) — the whole-galaxy chart,
|
||||
`js/ui/GalaxyView.js`, fed by `GameScene.galaxySnapshot()`
|
||||
|
|
@ -431,6 +425,12 @@ the plate:
|
|||
`jumpThroughGate`); dormant lane / no lane → the readouts. The
|
||||
window is a pure view: the galaxy tab polls `getGalaxy()` on the
|
||||
same 500 ms cycle the system chart is.
|
||||
- **SYSTEM** — the same painter applied to a CHARTED star's system
|
||||
(no ship/tether): pick a charted star on the GALAXY tab to arm it, then
|
||||
click an object on its chart to **SET DESTINATION** (see the Route
|
||||
section below). Locked ("NO SYSTEM TARGET") until a charted star is
|
||||
picked. The shared `ConfirmOverlay` is label-sized to fit the dialog
|
||||
(`baseWidth` + measured width, per-show `width`/`height` overrides).
|
||||
|
||||
**The run's footprint (save-ready):** `GameScene` keeps two
|
||||
registry-backed sets (the `activatedGates` pattern) — `visitedSystems`
|
||||
|
|
@ -905,6 +905,22 @@ must not un-install the home world's level-1 tether. A player resuming a
|
|||
run always has Tether - Level 1 built on home (pinned in
|
||||
`dev/builds.test.mjs`).
|
||||
|
||||
**First-landing auto-install (tether-l1):** `GameScene.ensureLandingTether`
|
||||
runs in `startLanding` — BEFORE the SurfaceScene launch, so the surface
|
||||
HUD's `tetherLevel` snapshot already reads `Tether Level 1`: the world
|
||||
(planet or deep-space station) hosts its level-1 tether the moment the
|
||||
player first lands, with no build-console step (the build is free +
|
||||
instant — `cost: {}`, `duration: 0`). It applies the `tether-l1`
|
||||
effects (anchor + toast) and marks the build installed (the console
|
||||
reads BUILT; the L2 `planetRequires.tetherLevel: 1` gate is met). It is
|
||||
idempotent — the home seed, an installed record, or an existing tether
|
||||
all short-circuit; a world that already hosts a tether without a record
|
||||
is adopted, never anchored twice. The world lookups it feeds
|
||||
(`_applyBuildEffects`, `tetherLevelFor`, `_rematerializeBuiltTethers`)
|
||||
include `systemStations`, so station tethers anchor, level up, and
|
||||
re-anchor across jump cuts like planets' do, and `applyRestore`'s
|
||||
re-key list knows station names too.
|
||||
|
||||
**Ship-scoped builds (the Mining tab) — one-off installs, ship-wide
|
||||
effect:** `targets: ["ship"]` (the mining arms + storage, data/builds.json
|
||||
→ the Mining category) are installed from any planet's console but the
|
||||
|
|
|
|||
|
|
@ -2667,6 +2667,12 @@ export class GameScene extends Phaser.Scene {
|
|||
// the video/music lookups need the number). `station` tells the
|
||||
// surface deck which clip set the frame indexes into (data/landing.json
|
||||
// → videos for planets, stationVideos for station variants).
|
||||
// FIRST LANDING (data/builds.json → tether-l1): the world hosts its
|
||||
// level-1 tether the moment the player lands — free + instant, so no
|
||||
// build-console step (planets and deep-space stations alike). Before
|
||||
// the launch, so the surface HUD's tetherLevel snapshot already
|
||||
// reads 1.
|
||||
this.ensureLandingTether(name);
|
||||
this.scene.launch('SurfaceScene', {
|
||||
frame: Number(target.frame ?? target.sheetFrame ?? 0),
|
||||
name,
|
||||
|
|
@ -2688,6 +2694,7 @@ export class GameScene extends Phaser.Scene {
|
|||
if (!name || !this.tetherField) return 0;
|
||||
const obj =
|
||||
this.systemPlanets.find((p) => p.discoveryName === name) ??
|
||||
this.systemStations.find((s) => s.discoveryName === name) ??
|
||||
(name === this.planet?.discoveryName ? this.planet : null);
|
||||
if (!obj) return 0;
|
||||
const t = this.tetherField.tethers.find(
|
||||
|
|
@ -2696,6 +2703,36 @@ export class GameScene extends Phaser.Scene {
|
|||
return t ? t.level : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FIRST-LANDING INSTALL (data/builds.json → tether-l1): the moment the
|
||||
* player lands on a world — a planet or a deep-space station — the
|
||||
* world hosts its level-1 tether (5120 px, data/tether.json), with no
|
||||
* build-console step: the build is free + instant (cost {}, duration
|
||||
* 0), so the landing simply completes it. The record then reads BUILT
|
||||
* in the console, and the level-2 build's planet gate (a world holding
|
||||
* a level-1 tether) is met. Idempotent — the home world's starting
|
||||
* seed (tether-l1.starting) already records it, and a world that
|
||||
* already hosts a tether is adopted, never anchored twice.
|
||||
*/
|
||||
ensureLandingTether(worldName) {
|
||||
const state = this.buildState;
|
||||
if (!worldName || !state) return;
|
||||
if (state.isBuilt(worldName, 'tether-l1')) return;
|
||||
if (this.tetherLevelFor(worldName) >= 1) {
|
||||
// The world already hosts a tether without a build record — adopt
|
||||
// it (the console reads BUILT, the L2 gate is met) instead of
|
||||
// anchoring a second field.
|
||||
state.markBuilt(worldName, 'tether-l1');
|
||||
return;
|
||||
}
|
||||
const def = defById('tether-l1');
|
||||
if (!def?.effects) return; // build system off / the data is gone
|
||||
this._applyBuildEffects(worldName, def.effects); // anchors + the toast
|
||||
state.markBuilt(worldName, 'tether-l1');
|
||||
this.playSfx('construct'); // the power-up tick — the manual build's voice
|
||||
this._questRefresh(); // a tether requirement may have just landed
|
||||
}
|
||||
|
||||
/**
|
||||
* Mining phase changes (Mining → onPhase): the scene's share of the
|
||||
* sequence — the ship's STATE, the ship lock, the console calls, the sfx.
|
||||
|
|
@ -3976,6 +4013,7 @@ export class GameScene extends Phaser.Scene {
|
|||
// home world's name).
|
||||
const obj =
|
||||
this.systemPlanets.find((p) => p.discoveryName === planetName) ??
|
||||
this.systemStations.find((s) => s.discoveryName === planetName) ??
|
||||
(planetName === this.planet?.discoveryName ? this.planet : null);
|
||||
const t = this.tetherField?.tethers.find(
|
||||
(tt) => tt.label === planetName ||
|
||||
|
|
@ -4402,6 +4440,7 @@ export class GameScene extends Phaser.Scene {
|
|||
for (const [planetName, builds] of this.buildState.built.entries()) {
|
||||
const world =
|
||||
this.systemPlanets.find((p) => p.discoveryName === planetName) ??
|
||||
this.systemStations.find((s) => s.discoveryName === planetName) ??
|
||||
(planetName === this.planet?.discoveryName ? this.planet : null);
|
||||
if (!world) continue; // another system's world — re-anchors on the way back
|
||||
let level = 0;
|
||||
|
|
@ -4467,6 +4506,7 @@ export class GameScene extends Phaser.Scene {
|
|||
const known = [
|
||||
this.planet?.discoveryName,
|
||||
...(this.systemPlanets ?? []).map((p) => p.discoveryName),
|
||||
...(this.systemStations ?? []).map((s) => s.discoveryName),
|
||||
...(this.tetherField?.tethers ?? []).map((t) => t.label),
|
||||
];
|
||||
for (const [k, v] of [...fresh.built.entries()]) {
|
||||
|
|
|
|||
|
|
@ -6,17 +6,16 @@
|
|||
* contract.
|
||||
*
|
||||
* Right column, top to bottom:
|
||||
* • tabs — CURRENT SYSTEM (the live system chart) / SYSTEM (a CHARTED
|
||||
* star's chart — the same painter; armed until the player picks that
|
||||
* star on the GALAXY tab) / GALAXY (the whole-galaxy chart,
|
||||
* js/ui/GalaxyView.js): glowing, pulsing stars per system (colored
|
||||
* • tabs — CURRENT SYSTEM (the live system chart) / GALAXY (the whole-galaxy
|
||||
* chart, js/ui/GalaxyView.js): glowing, pulsing stars per system (colored
|
||||
* by archetype), the jump-lane web (traveled lanes bright with flow
|
||||
* packets, frontier lanes mid, unexplored faint), the charted region
|
||||
* (convex hull of visited systems, inflated), HOME + SHIP markers.
|
||||
* Hover = the star's readout + its link state to the current system;
|
||||
* click a CHARTED star = its chart in the SYSTEM tab (onStarOpen);
|
||||
* uncharted stars are readouts only. Clicking a `standby` socket
|
||||
* fires `onLocked` (toast).
|
||||
* uncharted stars are readouts only) / SYSTEM (a CHARTED star's chart —
|
||||
* the same painter; armed until the player picks that star on the
|
||||
* GALAXY tab). Clicking a `standby` socket fires `onLocked` (toast).
|
||||
* • the CHART — the current system drawn on an offscreen canvas and
|
||||
* shown as a Phaser image:
|
||||
* - frame = the furthest objects (planets, stations, jump gates and
|
||||
|
|
@ -1299,8 +1298,8 @@ export class MapWindow extends Phaser.GameObjects.Container {
|
|||
: [];
|
||||
if (!tabs.length) {
|
||||
tabs.push({ id: 'currentSystem', label: 'Current System', accent: '#00e5ff' });
|
||||
tabs.push({ id: 'system', label: 'System', accent: '#a8ffc4', standby: true, standbyTag: 'NO TARGET' });
|
||||
tabs.push({ id: 'galaxy', label: 'Galaxy', accent: '#ffc94d', standby: true, standbyTag: 'OFFLINE' });
|
||||
tabs.push({ id: 'system', label: 'System', accent: '#a8ffc4', standby: true, standbyTag: 'NO TARGET' });
|
||||
}
|
||||
let x = rightX;
|
||||
const avail = rightW;
|
||||
|
|
@ -1394,11 +1393,11 @@ export class MapWindow extends Phaser.GameObjects.Container {
|
|||
/**
|
||||
* The tab switch — the plate has three lives:
|
||||
* CURRENT SYSTEM — the live system chart (hover, ENGAGE, zoom, pan)
|
||||
* GALAXY — the whole-galaxy chart (GalaxyView: stars, lanes,
|
||||
* the charted region)
|
||||
* SYSTEM — a CHARTED star's chart (the same painter; the ship
|
||||
* marker + course line are hidden — the ship isn't
|
||||
* in that system)
|
||||
* GALAXY — the whole-galaxy chart (GalaxyView: stars, lanes,
|
||||
* the charted region)
|
||||
* The plate's input surface moves between mapImg and the galaxy plate —
|
||||
* one at a time (Phaser 4's topOnly input: both catching would
|
||||
* double-fire). Each life is driven from the same 500 ms poll in
|
||||
|
|
|
|||
Loading…
Reference in New Issue