375 lines
13 KiB
JSON
375 lines
13 KiB
JSON
{
|
|
"_comment": "SHIP CONSOLE \u2014 the command deck's SHIP button (js/ui/ShipWindow.js, depth 80, the MapWindow/QuestWindow idiom). LEFT = the bay camera (assets/videos/ship.mp4, a muted 2:3 loop; the clip is authored separately \u2014 a missing file leaves the NO SIGNAL plate up and the console still works). RIGHT = the tab row (Ship Status / Skill Tree / Cargo Holds \u2014 `standby: true` renders the socket dimmed with its standbyTag and a press fires the locked toast instead of switching) over the tab's plate. The Ship Status plate reads the ship's LIVE numbers through the `status.rows` table below: code owns behavior, this file owns labels, units, order, formatting and where each number comes from (`from` = a namespaced path \u2014 `config:<data path>`, `ship:<Ship field path>`, `live:<CombatSystem field>` read while a fight is running and off the maxima otherwise; `op: reciprocal` turns a cooldown into a rate). Ship Skill Tree stays OFFLINE until the class/skill layer lands; Cargo Holds lists the trade bays + the mineral aggregate, and a filled bay opens the good's scout summary (js/economy/TradeScout.js \u2014 the buy/sell screens' report).",
|
|
"enabled": true,
|
|
"title": "SHIP SYSTEMS",
|
|
"meta": "HULL & HARDWARE // LIVE TELEMETRY",
|
|
"video": {
|
|
"_comment": "The bay camera feed (same contract as map.video / quests.video): `file` is a clip under assets/videos/ (or a full relative path / URL). Muted, looping, 2:3 portrait. A missing file leaves the NO SIGNAL plate up.",
|
|
"file": "ship.mp4",
|
|
"aspect": [
|
|
2,
|
|
3
|
|
],
|
|
"tag": "BAY CAMERA // LIVE FEED",
|
|
"noSignalTag": "BAY CAMERA OFFLINE"
|
|
},
|
|
"tabs": [
|
|
{
|
|
"id": "status",
|
|
"label": "Ship Status",
|
|
"accent": "#7ce8a4"
|
|
},
|
|
{
|
|
"id": "skills",
|
|
"label": "Skill Tree",
|
|
"accent": "#ffc94d",
|
|
"standby": true,
|
|
"standbyTag": "OFFLINE"
|
|
},
|
|
{
|
|
"id": "cargo",
|
|
"label": "Cargo Holds",
|
|
"accent": "#00e5ff"
|
|
}
|
|
],
|
|
"skillsLockedToast": "SKILL TREE OFFLINE \u2014 NO CLASS ON FILE",
|
|
"skillsPanel": {
|
|
"_comment": "The OFFLINE tab's body (the final structure stays visible): the locked plate's headline + the copy that explains what lands there.",
|
|
"headline": "SKILL TREE OFFLINE",
|
|
"note": "COMBAT SKILLS ARM WHEN THE CLASS LAYER LANDS.\nCHOOSE A CLASS AT COMBAT LEVEL 5 \u2014 THE TREE GROWS FROM THERE."
|
|
},
|
|
"glitch": {
|
|
"_comment": "The console's glitch voice (the quests window's): intervalMs between bursts, durationMs the burst holds, slices RGB bands across the feed, maxOffset px of slide.",
|
|
"enabled": true,
|
|
"intervalMs": [
|
|
4200,
|
|
9200
|
|
],
|
|
"durationMs": [
|
|
110,
|
|
260
|
|
],
|
|
"slices": [
|
|
1,
|
|
4
|
|
],
|
|
"maxOffset": 9
|
|
},
|
|
"sweep": {
|
|
"_comment": "The scan band that crawls down the feed while the console is up.",
|
|
"enabled": true,
|
|
"intervalMs": [
|
|
5200,
|
|
9800
|
|
],
|
|
"durationMs": [
|
|
1400,
|
|
2200
|
|
]
|
|
},
|
|
"status": {
|
|
"_comment": "The SHIP STATUS tab. readout = the hardware table (order = draw order); level = the combat record + the level 0\u21925 journey.",
|
|
"hardwareTitle": "HARDWARE READOUT",
|
|
"groups": [
|
|
{
|
|
"id": "armor",
|
|
"label": "ARMOR"
|
|
},
|
|
{
|
|
"id": "flight",
|
|
"label": "FLIGHT"
|
|
},
|
|
{
|
|
"id": "guns",
|
|
"label": "GUNS"
|
|
},
|
|
{
|
|
"id": "hold",
|
|
"label": "HOLD"
|
|
}
|
|
],
|
|
"rows": [
|
|
{
|
|
"_comment": "HULL \u2014 the ship's max structure (data/ship.json \u2192 stats.hullIntegrity) with the live combat hull as the meter fill.",
|
|
"id": "hull",
|
|
"group": "armor",
|
|
"label": "HULL STRENGTH",
|
|
"from": "live:hullMax",
|
|
"unit": "HP",
|
|
"format": "int",
|
|
"meter": {
|
|
"of": "live:hull"
|
|
},
|
|
"accent": "#ff2d6f",
|
|
"note": "STRUCTURAL LIMIT",
|
|
"hotBelow": 0.34
|
|
},
|
|
{
|
|
"_comment": "CURRENT HULL \u2014 the armor actually on the ship right now (the live fight's hull; full strength with no fight running).",
|
|
"id": "hullNow",
|
|
"group": "armor",
|
|
"label": "CURRENT HULL",
|
|
"from": "live:hull",
|
|
"unit": "HP",
|
|
"format": "int",
|
|
"meter": {
|
|
"_comment": "The bar is the SAME armor meter as HULL STRENGTH (current over max) \u2014 written as an explicit pair because this row's own value is the numerator.",
|
|
"fill": "live:hull",
|
|
"max": "live:hullMax"
|
|
},
|
|
"accent": "#ff7ba0",
|
|
"nominalLabel": "NOMINAL",
|
|
"hotBelow": 0.34
|
|
},
|
|
{
|
|
"id": "shields",
|
|
"group": "armor",
|
|
"label": "SHIELDS",
|
|
"from": "live:shieldsMax",
|
|
"unit": "HP",
|
|
"format": "int",
|
|
"accent": "#00e5ff",
|
|
"hotBelow": 0.34
|
|
},
|
|
{
|
|
"_comment": "SHIELD LEVEL \u2014 the live shield bar (recharges after shieldRegenDelay s without a hit at shieldRegenRate/s).",
|
|
"id": "shieldNow",
|
|
"group": "armor",
|
|
"label": "SHIELD LEVEL",
|
|
"from": "live:shields",
|
|
"unit": "HP",
|
|
"format": "int",
|
|
"meter": {
|
|
"_comment": "Same meter as SHIELDS (current over max).",
|
|
"fill": "live:shields",
|
|
"max": "live:shieldsMax"
|
|
},
|
|
"accent": "#78b4ff",
|
|
"nominalLabel": "FULL",
|
|
"hotBelow": 0.34
|
|
},
|
|
{
|
|
"id": "shieldRegen",
|
|
"group": "armor",
|
|
"label": "SHIELD RECHARGE",
|
|
"from": "config:combat.playerShip.shieldRegenRate",
|
|
"unit": "/s",
|
|
"format": "int",
|
|
"accent": "#00e5ff",
|
|
"meta": {
|
|
"_comment": "The recharge's dead time after a hit (data/combat.json \u2192 playerShip.shieldRegenDelay), drawn as the row's meta line.",
|
|
"label": "ARM AFTER",
|
|
"from": "config:combat.playerShip.shieldRegenDelay",
|
|
"unit": "s",
|
|
"format": "int"
|
|
}
|
|
},
|
|
{
|
|
"id": "accel",
|
|
"group": "flight",
|
|
"label": "ACCELERATION",
|
|
"from": "config:ship.thrust",
|
|
"unit": "px/s\u00b2",
|
|
"format": "int",
|
|
"accent": "#7ce8a4"
|
|
},
|
|
{
|
|
"id": "maxSpeed",
|
|
"group": "flight",
|
|
"label": "MAX VELOCITY",
|
|
"from": "config:ship.maxSpeed",
|
|
"unit": "px/s",
|
|
"format": "int",
|
|
"accent": "#7ce8a4"
|
|
},
|
|
{
|
|
"id": "turn",
|
|
"group": "flight",
|
|
"label": "TURN RATE",
|
|
"from": "config:ship.rotSpeed",
|
|
"unit": "rad/s",
|
|
"format": "1dp",
|
|
"accent": "#7ce8a4"
|
|
},
|
|
{
|
|
"_comment": "FIRE RATE \u2014 the cannon's shots per second (the reciprocal of data/combat.json \u2192 player.weapon.cooldown).",
|
|
"id": "fireRate",
|
|
"group": "guns",
|
|
"label": "FIRE RATE",
|
|
"from": "config:combat.player.weapon.cooldown",
|
|
"op": "reciprocal",
|
|
"unit": "/s",
|
|
"format": "2dp",
|
|
"accent": "#ffc94d"
|
|
},
|
|
{
|
|
"id": "damage",
|
|
"group": "guns",
|
|
"label": "DAMAGE",
|
|
"from": "config:combat.player.weapon.damage",
|
|
"unit": "/hit",
|
|
"format": "int",
|
|
"accent": "#ffc94d"
|
|
},
|
|
{
|
|
"_comment": "DPS \u2014 damage \u00d7 fire rate (the row's meta read, not a stored number).",
|
|
"id": "dps",
|
|
"group": "guns",
|
|
"label": "DPS",
|
|
"from": [
|
|
{
|
|
"from": "config:combat.player.weapon.damage"
|
|
},
|
|
{
|
|
"from": "config:combat.player.weapon.cooldown",
|
|
"op": "reciprocal"
|
|
}
|
|
],
|
|
"op": "product",
|
|
"unit": "/s",
|
|
"format": "1dp",
|
|
"accent": "#ffd84d",
|
|
"meta": {
|
|
"label": "SHOT SPEED",
|
|
"from": "config:combat.player.weapon.bulletSpeed",
|
|
"unit": "px/s",
|
|
"format": "int"
|
|
}
|
|
},
|
|
{
|
|
"id": "cargoBays",
|
|
"group": "hold",
|
|
"label": "CARGO BAYS",
|
|
"from": "ship:stats.cargoHold",
|
|
"unit": "bays",
|
|
"format": "int",
|
|
"meter": {
|
|
"of": "ship:cargo.used"
|
|
},
|
|
"accent": "#00e5ff",
|
|
"meta": {
|
|
"label": "GRID",
|
|
"from": "config:economy.cargo.maxSlots",
|
|
"unit": "SLOTS",
|
|
"format": "int"
|
|
}
|
|
},
|
|
{
|
|
"id": "mineralHold",
|
|
"group": "hold",
|
|
"label": "MINERAL STORAGE",
|
|
"from": "ship:stats.mineralStorage",
|
|
"unit": "u",
|
|
"format": "int",
|
|
"meter": {
|
|
"of": "ship:minerals"
|
|
},
|
|
"accent": "#78b4ff"
|
|
},
|
|
{
|
|
"id": "miningSpeed",
|
|
"group": "hold",
|
|
"label": "MINING RATE",
|
|
"from": "ship:stats.miningSpeed",
|
|
"unit": "\u00d7 base",
|
|
"format": "2dp",
|
|
"accent": "#a8ffc4"
|
|
}
|
|
],
|
|
"creditsLabel": "CREDITS",
|
|
"noCombatNote": "DRIVE DARK \u2014 NO CONTACTS"
|
|
},
|
|
"level": {
|
|
"_comment": "The combat record (js/combat/XpManager.js \u2014 the curve is data/progression.json). The journey plate shows levels `from`\u2026`to` as a timeline of milestone nodes with the XP fill between them; at `classAt` (and above) the timeline gives way to CHOOSE YOUR CLASS, and once a class is on file (playerClass.name) it shows the class + insignia instead. Every string lives here.",
|
|
"title": "COMBAT RECORD",
|
|
"levelWord": "LEVEL",
|
|
"xpLabel": "XP",
|
|
"nextLabel": "NEXT LV",
|
|
"maxLabel": "MAX LEVEL",
|
|
"journeyTitle": "THE JOURNEY // LV {from}\u2013{to}",
|
|
"from": 0,
|
|
"to": 5,
|
|
"classAt": 5,
|
|
"milestones": [
|
|
{
|
|
"level": 0,
|
|
"label": "SADDLE",
|
|
"note": "FIRST SEAT"
|
|
},
|
|
{
|
|
"level": 1,
|
|
"label": "SOLO",
|
|
"note": "FIRST KILL"
|
|
},
|
|
{
|
|
"level": 2,
|
|
"label": "SCUFFED",
|
|
"note": "HULL HOLDING"
|
|
},
|
|
{
|
|
"level": 3,
|
|
"label": "HUNTED",
|
|
"note": "ON THE BOARD"
|
|
},
|
|
{
|
|
"level": 4,
|
|
"label": "VETERAN",
|
|
"note": "GUNS HOT"
|
|
},
|
|
{
|
|
"level": 5,
|
|
"label": "CHOSEN",
|
|
"note": "PICK A CLASS"
|
|
}
|
|
],
|
|
"chooseClass": {
|
|
"_comment": "The level-5 door: the timeline's replacement. The press fires the scene's onChooseClass seam (the class-selection flow lands with the class layer \u2014 a toast for now, never a dead click).",
|
|
"label": "CHOOSE YOUR CLASS",
|
|
"hint": "LEVEL {classAt} REACHED \u2014 THE CLASS LAYER AWAITS",
|
|
"toast": "CLASS SELECT OFFLINE \u2014 THE CLASS LAYER LANDS NEXT"
|
|
},
|
|
"classPlate": {
|
|
"_comment": "The class + insignia slot (data-driven seam: shipClass/insignia come from the save once class selection exists; until then the console shows the CHOOSE YOUR CLASS door).",
|
|
"label": "CLASS",
|
|
"noneLabel": "NO CLASS ON FILE"
|
|
}
|
|
},
|
|
"cargo": {
|
|
"_comment": "The CARGO HOLDS tab. The hold is trade goods only \u2014 one good per bay, in fill order (js/economy/CargoHold.js); locked bays are the hold's upgrade room. MINERALS is the aggregate aboard vs capacity (the Mineral HUD's numbers \u2014 js/entities/Ship.js). A filled bay is tappable: it opens the good's summary + best exits (data below \u2192 detail).",
|
|
"holdTitle": "TRADE BAYS",
|
|
"holdMeta": "{used} / {capacity} BAYS \u00b7 {room} FREE",
|
|
"emptyNote": "HOLD EMPTY \u2014 EVERY BAY IS CLEAN",
|
|
"noBaysNote": "NO CARGO BAYS INSTALLED \u2014 UPGRADE THE HOLD",
|
|
"lockedTag": "LOCKED",
|
|
"manifestTag": "MANIFEST \u25b8",
|
|
"lockedRoomMeta": "+{locked} LOCKED BAYS \u00b7 UPGRADE THE HOLD AT HARGUN ({slots} TOTAL)",
|
|
"mineralsTitle": "MINERAL HOLDS",
|
|
"mineralsMeta": "{minerals} / {capacity} u",
|
|
"emptyMineralsNote": "NO ORE ABOARD",
|
|
"creditsLabel": "CREDITS",
|
|
"detail": {
|
|
"_comment": "The selected good's summary (the shop scout's report \u2014 js/economy/TradeScout.js scoutGood). `baseline` is the profit reference with no shop under it: baseValue = the catalog value (data/goods.json), buy = this system's own buy price.",
|
|
"title": "BAY MANIFEST",
|
|
"categoryLabel": "CLASS",
|
|
"tierLabel": "T",
|
|
"valueLabel": "BASE VALUE",
|
|
"originLabel": "MADE IN",
|
|
"hubLabel": "HUB POOL",
|
|
"producerLabel": "MADE OF",
|
|
"demandLabel": "SOUGHT ON",
|
|
"exitsTitle": "BEST EXITS",
|
|
"exitsEmpty": "NO CHARTED EXIT YET \u2014 EVERY CAPITAL BUYS EVERYTHING",
|
|
"exitMeta": "{hops} HOPS",
|
|
"profitBar": {
|
|
"_comment": "the per-unit margin rail under each exit row \u2014 scaled against the list's best row, so goods of different value compare the same way",
|
|
"track": "#14324f",
|
|
"fill": "#7ce8a4",
|
|
"fillNeg": "#ff5a6e"
|
|
},
|
|
"baseline": "baseValue",
|
|
"topN": 3,
|
|
"backLabel": "BACK TO BAYS"
|
|
}
|
|
}
|
|
}
|