orbit/js/quests/TutorialGuide.js

70 lines
3.4 KiB
JavaScript

/**
* TutorialGuide — the starter quest's POINTING FINGER: which control is
* asking for the player's attention right now, given the run's records.
*
* Pure: no Phaser, no config, no DOM — node-testable (the QuestState /
* ResearchState shape). The scene (GameScene._tutorialTarget) feeds it
* the live records and applies the result per frame (ActionBar and
* ResearchWindow's setHighlight — the pulsing rims). The targets' names
* (the deck slots, the category + node) ride on data/quests.json →
* tutorial (the data's master switch + the re-targetable ids).
*
* The beats (the starter quest's opening arc — research the Tether
* Level 2, data/quests.json → start_your_journey):
* 'scan' the welcome sequence is acknowledged — oac-intro →
* oac-tether → oac-scan all on the run's comms played-ledger
* (data/comms.json, trigger 'newGame') — and the player
* hasn't pressed SCAN yet;
* 'research' OAC's 'research a Tether Level 2 upgrade' line
* (oac-upgrade, trigger 'firstScan') is on the ledger and
* the research console is CLOSED;
* 'console' the same beat with the console OPEN — the window's
* setHighlight paints the category tab's rim, and the node's
* plate's rim once that category is the one on screen;
* null REST — the comms still own the screen (nothing has been
* heard yet), or the goal (the named node's research) is in
* flight / done.
*
* Derived from the records only — nothing extra is saved; a loaded run
* picks the same beat back up. `scanPressed` is the scene-local one-shot
* covering the window between a SCAN press and oac-upgrade's start (the
* comm's own played-ledger mark is the authoritative witness once it has
* started — from then on the beat is 'research'/'console').
*/
/** The 'newGame' welcome sequence, in play order (data/comms.json). */
export const WELCOME_COMMS = ['oac-intro', 'oac-tether', 'oac-scan'];
/** The scan reaction (trigger 'firstScan') — the 'research a Tether
* Level 2 upgrade' line (data/comms.json). */
export const UPGRADE_COMM = 'oac-upgrade';
/**
* @param {{
* enabled?: boolean, false → rest (the data's master switch)
* starterGranted?: boolean, the starter quest is on the quest ledger
* hasPlayed?: (id: string) => boolean,
* the run's comms played-ledger
* scanPressed?: boolean, the deck's SCAN has been pressed this beat
* consoleOpen?: boolean, the research console is on screen
* nodeResolving?: boolean, the goal node's research is in flight/done
* }} [r]
* @returns {'scan'|'research'|'console'|null} the beat to point at
*/
export function tutorialTarget(r = {}) {
if (r.enabled === false) return null;
if (r.starterGranted !== true) return null;
if (typeof r.hasPlayed !== 'function') return null;
// The goal is underway / done — the finger rests (the completion
// ignition + toasts carry the news from here on).
if (r.nodeResolving === true) return null;
// Beat 2 — OAC has said the 'research a Tether Level 2' line.
if (r.hasPlayed(UPGRADE_COMM)) {
return r.consoleOpen ? 'console' : 'research';
}
// Beat 1 — the welcome is acknowledged and SCAN hasn't been pressed.
if (WELCOME_COMMS.every((id) => r.hasPlayed(id)) && r.scanPressed !== true) {
return 'scan';
}
return null;
}