From 85b81982b08a4f6a45841b47c523a3e291f08240 Mon Sep 17 00:00:00 2001 From: type-two Date: Sat, 1 Aug 2026 18:16:30 +1000 Subject: [PATCH] Tutorial: goal-driven quest cards, optional and persistent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nine-step tutorial card that watches sim state and advances itself: wire the PC, first video, cooling, the ALGO HEAT treadmill, releasing a model version, the VC deal, building a rig, shipping + hosting, and a graduation card. Steps the save has already accomplished are skipped automatically; โ“ chip toggles it off/on (persisted), New Game resets it. No text dumps โ€” each card is one goal and why it matters. Co-Authored-By: Claude Fable 5 --- src/main.ts | 9 ++- src/ui/hud.css | 15 +++++ src/ui/hud.ts | 1 + src/ui/tutorial.ts | 149 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 src/ui/tutorial.ts diff --git a/src/main.ts b/src/main.ts index 79516d4..a1bf9a7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,6 +6,7 @@ import { DEF } from "./sim/catalog"; import { Scene } from "./render/scene"; import { Hud } from "./ui/hud"; import { Panels } from "./ui/panels"; +import { Tutorial } from "./ui/tutorial"; import { sfx } from "./ui/sfx"; import { unlockGen, unlockModality, setSpec, setDataSource, unlockRnd, buyUpgrade } from "./sim/sim"; import { DataSource } from "./sim/research"; @@ -36,7 +37,7 @@ async function boot() { }, onAlloc: (v) => { state.allocTraining = v; }, onAllocHost: (v) => { state.allocHosting = v; }, - onNewGame: () => { wiped = true; clearSave(); location.reload(); }, + onNewGame: () => { wiped = true; clearSave(); tutorial.reset(); location.reload(); }, onRaise: () => { const err = raiseRound(state); if (err) { pushEvent(state, err, "info"); sfx.error(); } else sfx.money(); @@ -58,6 +59,11 @@ async function boot() { }); hud.bindBreaker(state); const panels = new Panels(state); + const tutorial = new Tutorial(); + const tutChip = hud.root.querySelector("#tut-toggle") as HTMLButtonElement; + const syncTutChip = () => tutChip.classList.toggle("on", tutorial.enabled); + tutChip.onclick = () => { tutorial.setEnabled(!tutorial.enabled); syncTutChip(); }; + syncTutChip(); (hud.root.querySelector("#lab-btn") as HTMLButtonElement).onclick = () => panels.toggle("lab"); (hud.root.querySelector("#rnd-btn") as HTMLButtonElement).onclick = () => panels.toggle("rnd"); (hud.root.querySelector("#market-btn") as HTMLButtonElement).onclick = () => panels.toggle("market"); @@ -160,6 +166,7 @@ async function boot() { let hudTicks = 0; setInterval(() => { hud.update(state); + tutorial.update(state); if (++hudTicks % 5 === 0 && (panels.open || panels.selectedDevice !== null)) panels.render(); }, 200); hud.update(state); diff --git a/src/ui/hud.css b/src/ui/hud.css index ad33f81..f2a2d6d 100644 --- a/src/ui/hud.css +++ b/src/ui/hud.css @@ -127,6 +127,21 @@ #devpanel .devbtns { display: flex; gap: 6px; margin-bottom: 4px; } #devpanel h4 { margin: 8px 0 4px; font-size: 10px; color: var(--dim); letter-spacing: 0.15em; } +/* tutorial quest card */ +#tutorial { position: absolute; left: 10px; top: 46vh; width: 250px; } +.tutcard { padding: 10px 12px; border-color: var(--purple) !important; pointer-events: auto; } +.tutcard.tutdone { border-color: var(--green) !important; } +.tuthead { display: flex; justify-content: space-between; align-items: center; + font-size: 9px; letter-spacing: 0.18em; color: var(--purple); margin-bottom: 5px; } +.tutcard.tutdone .tuthead { color: var(--green); } +.tuthead button { background: none; border: 0; color: var(--dim); cursor: pointer; font: inherit; } +.tuttitle { font-weight: 700; font-size: 12.5px; margin-bottom: 4px; } +.tutbody { font-size: 11px; line-height: 1.45; color: #c9c9dc; } +.tutbody b { color: var(--cyan); } +.tutskip { background: none; border: 0; color: var(--dim); font: inherit; font-size: 10px; + cursor: pointer; margin-top: 6px; padding: 0; } +.tutskip:hover { color: var(--text); } + #name-modal { position: fixed; inset: 0; background: rgba(5, 5, 10, 0.85); display: flex; align-items: center; justify-content: center; pointer-events: auto; } #name-box { padding: 28px 32px; text-align: center; width: 360px; } #name-box h2 { margin: 0 0 4px; letter-spacing: 0.3em; color: var(--purple); } diff --git a/src/ui/hud.ts b/src/ui/hud.ts index 258fe30..1aa5f1c 100644 --- a/src/ui/hud.ts +++ b/src/ui/hud.ts @@ -76,6 +76,7 @@ export class Hud { +