From 3e0bb9ee682b6702ef711b6e101ab7cb3b8e44d1 Mon Sep 17 00:00:00 2001 From: monsterrobotparty Date: Sat, 25 Jul 2026 12:22:04 +1000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=AA=20the=20doors:=20six=20front-door?= =?UTF-8?q?=20openings=20=E2=80=94=20the=20rig,=20the=20deal,=20the=20dawn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The front door becomes a skin: the first-visit card grows from three doors to six, the walked door is REMEMBERED (gs_door) and opens the same way on every arrival, and it's re-choosable any time (right-click sky β†’ πŸšͺ the doors, or say "doors"). Each door is one DOORS registry entry + one act β€” delete the entry, the door is gone. The classics (unchanged acts): 🌱 start from zero Β· πŸŒ… first light Β· β›ˆ the full storm. The new gig doors: - πŸŽͺ the rig β€” walk on stage: first light applied, beat panel + GODSPEED already open, and the sleeper fold tightened so only the ~8 liveliest uncabled hands stay in the field (8th-liveliest flowDisp sets the bar). - πŸƒ the deal β€” the world deals five cables from its liveliest feeds (family-diverse) onto a shuffled spread of ten dests, ONE card always ⚑pulsing (Γ·16 or Γ·8); "πŸƒ deal again" chip; every arrival deals fresh; each hand is an ungodly mark so ⌘Z takes it back. Voice: "deal" redeal while the deal is your door. - πŸŒ’ the dawn β€” first light applied, then the players take the stage one at a time (~1.25s apart, cabled first, then by liveliness, 18 arrivals), each narrated in the ticker; cables only draw once their player is on stage (drawCables skips hidden sources); when the entrance ends the sleeper fold reconciles the rest. Verified live (static serve + temp hook, removed & grep-verified 0): six cards render; deal β†’ exactly 5 routes, 1 pulsing, chip visible, gs_door saved; redeal changes the hand, marks undo, ⌘Z restores the previous hand exactly; rig β†’ beat+GODSPEED open, field folded tight, deal furniture closed; dawn β†’ 24/24 hidden at curtain, players arrive one-by-one with ticker narration and their cables, finale reconciles to 0 hidden; zero console errors; T O B G S + ⌘Z regression green; grimoire + manual regenerated. Co-Authored-By: Claude Fable 5 --- viz/index.html | 167 ++++++++++++++++++++++++++++++++++++++++-------- viz/manual.html | 10 ++- 2 files changed, 150 insertions(+), 27 deletions(-) diff --git a/viz/index.html b/viz/index.html index 294d33f..fde35ba 100644 --- a/viz/index.html +++ b/viz/index.html @@ -669,6 +669,11 @@ background: rgba(20,28,44,0.82); color: rgba(160,180,215,0.75); border: 1px solid rgba(120,150,200,0.22); border-radius: 9px; padding: 4px 10px; font-size: 11px; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); } #sleepchip:hover { color: #cfe0ff; border-color: rgba(140,170,230,0.5); } + /* πŸƒ the deal β€” the redeal chip sits above the sleeper chip */ + #dealchip { position: fixed; left: 14px; bottom: 86px; z-index: 12; display: none; cursor: pointer; + background: rgba(28,24,46,0.85); color: rgba(200,185,235,0.85); border: 1px solid rgba(160,140,220,0.3); + border-radius: 9px; padding: 4px 10px; font-size: 11px; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); } + #dealchip:hover { color: #e8ddff; border-color: rgba(180,160,240,0.6); }
βš™
@@ -1240,6 +1245,7 @@
  • πŸ•Š PSALM β€” sing, and the instrument answers. Right-click the sky β†’ πŸ•Š PSALM, allow the microphone, and sing. The pitch is heard, folded to the scale you're in (so you're never out of key), and played as notes β€” a πŸ•Š chip shows the note leaving your throat. And the duet with ⚑ GODSPEED: with the arp running, each note you sing latches into the held chord, one at a time β€” hum four notes and the machine arpeggiates the chord you sang. It listens locally: the analysis happens in the page, in its own silence, and nothing ever leaves the machine β€” no recording, no upload, just your voice becoming the instrument's.
  • ✦ CANON β€” the shelf of worked examples. Open βš™ β†’ DIMENSIONS and at the top of the list, above your own saved worlds, sit four ✦ factory dimensions that ship with the instrument β€” the heartbeat (a boom-bap kit with the swing and the kick's duck), the wheel within the wheel (WHEELS, made audible), the squeeze (the market pumping GODSQUASH), and quiet arrival (the earth echo, slow and sparse). Load one to hear a feature in its element, then save your own version under a new name β€” the ✦ ones are read-only, the same for everyone, a place to start rather than a thing to overwrite.
  • πŸͺ spheres β€” the music of the spheres. Press S (or right-click the sky β†’ πŸͺ spheres) and turn it on: the eight planets join the field as modulation sources, the slowest LFOs there are. Each planet offers three signals β€” phase (its heliocentric longitude, a sawtooth one full turn per orbit), dist (its eccentric breathing, in and out between perihelion and aphelion β€” Mercury and Mars swing hard, Venus barely), and beat (its synodic dance against Earth β€” Venus and Earth clasp every 583.9 days, the pentagram cycle). Route any of them like any source, or just say "mars drive filter." One knob rules time: the rate, from 1 to 3650 days of sky per bar β€” at the default thirty, Venus' year is about seven and a half bars and Jupiter's a hundred and forty-four, real orbits compressed into slow musical arcs. And the ☸ orbital ratios button sets the drum lanes' wheels to the true resonances β€” 4 : 8 : 16 (Io, Europa and Ganymede, locked at 1 : 2 : 4) and 8 : 13 (Venus to Earth, the pentagram in wheel form), so the polymeter you hear is one the solar system actually keeps. It is all computed here, at the instrument's own clock β€” the same JPL ephemeris SOLARGOD uses, vendored in, no network and no server, so the sky is deterministic: the same bar always shows the same heaven, and a saved patch reloads the identical sky.
  • +
  • πŸšͺ The doors β€” how the world opens. The front door is a skin like any other. On your first visit a card offers six doors, and the one you walk is remembered β€” every arrival after that opens the same way (right-click the sky β†’ πŸšͺ the doors to choose again, or say "doors"). The classics: 🌱 start from zero (an empty field, build it yourself), πŸŒ… first light (a gentle arrangement), β›ˆ the full storm (every hand at once). And the gig doors: πŸŽͺ the rig β€” you walk on stage with the beat panel and GODSPEED already open and the field folded to the eight liveliest hands, everything within reach; πŸƒ the deal β€” the world deals you five cables from its liveliest feeds onto five voices, one card always pulsing, with a πŸƒ deal again chip (and ⌘Z takes any hand back) β€” redeal until it sings, and every arrival deals fresh; πŸŒ’ the dawn β€” the players take the stage one at a time, liveliest first, the field assembling itself over half a minute like an orchestra walking on. Same instrument behind every door β€” only the meeting changes.
  • ⚑ Pulse β€” make the slow world playable. Some of the planet's truest signals barely move β€” the national debt, the Moon's declination, a glacier of a currency. Right-click a node, open its cables, and tap the ⚑ chip on any cable to cycle Γ·16 Β· Γ·8 Β· Γ·4 Β· bar: the cable now throbs on the godtime clock, and the slow value becomes the depth of that throb. The debt stops being a frozen offset and becomes a sixteenth-note pressure whose weight is the debt itself β€” slow data as character, the clock as motion. Off by default; the world's own tempo is still the truth unless you ask for the heartbeat.
  • πŸ’€ The sleeper fold β€” and speed at a glance. The field folds away quiet feeds β€” sources with no cables, no signal and nothing to say for a minute β€” behind a small πŸ’€ chip (bottom-left) that shows the count and brings them back with one click. The moment a folded feed speaks, it wakes itself and rejoins the field: the world breathes in and out of view. And every cable now moves at the data's own rate β€” streaming feeds race, glacial ones crawl β€” with the words to match: hover a column and each source row reads its speed (streaming Β· β‰ˆ every 3 bars Β· asleep, still 12m), and a node's right-click readout says the same. What speed is that thing going at? Now it just tells you.
  • πŸ“½ The stage β€” send the visual to any screen. Right-click the sky β†’ the stage and a clean black mirror window opens, fed live from the canvas. Drag it to a projector, an AirPlay display, a second monitor β€” macOS treats it like any window β€” and double-click for fullscreen. The stage has a camera: the whole view by default (whatever you're in β€” a skin, the wheel, mind mode's POV), or right-click any node β†’ project this node and the camera glides after that one sphere through its orbits, or project its station to frame a whole wheel and its satellites. Change cameras live from the menus while the projector runs; the audience sees the camera breathe from subject to subject.
  • @@ -1299,7 +1305,7 @@
    1. Press β™ͺ (top-right, or F2) to wake OMNI β€” the whole audio graph builds on the first press.
    2. -
    3. Your first visit opens gently in πŸŒ… first light. Right-click the sky β†’ πŸŒ— mood β€” how much world to move between first light, ambient drift (all atmosphere, no drums) and the full storm (every hand at once).
    4. +
    5. Your first visit offers πŸšͺ six doors β€” from start from zero to the rig, the deal and the dawn β€” and remembers the one you walk. Right-click the sky β†’ πŸŒ— mood β€” how much world to move between first light, ambient drift (all atmosphere, no drums) and the full storm (every hand at once), or πŸšͺ the doors to change how the world opens.
    6. Play notes three ways: enter chakra view (C) and click a wheel's core for a 12-key console (play it with the Q…P row or a MIDI keyboard); open πŸŽ› tracks (T) to sequence a song; or just wire the world's data to play it hands-free.
    7. Shape the character: swap the pluck voice (🎸 string voice / πŸŽ› GODSONIQ), or drag any feed onto crush, wavetable.morph or granular.density.
    @@ -1358,7 +1364,7 @@

    Building from zero β€” the empty instrument

    -

    The full instrument is a sensory flood by design. Zero is the other door: an empty field with nothing wired, where you build the whole thing by hand, one cable at a time. Same matrix underneath β€” a calmer face on it. On your first visit the three-door card offers it (🌱 start from zero); after that, press F11, or βš™ β†’ DIMENSIONS β†’ ✦ dimension zero, or the πŸŒ— mood menu β†’ 0 zero.

    +

    The full instrument is a sensory flood by design. Zero is the other door: an empty field with nothing wired, where you build the whole thing by hand, one cable at a time. Same matrix underneath β€” a calmer face on it. On your first visit the doors card offers it (🌱 start from zero); after that, press F11, or βš™ β†’ DIMENSIONS β†’ ✦ dimension zero, the πŸŒ— mood menu β†’ 0 zero, or πŸšͺ the doors.

    1. Down each side sit seven vibe-rings β€” the world grouped into the seven elements (left) and seven domains (right). Each ring breathes with its group's live average. Click one to open its drawer.
    2. @@ -3978,9 +3984,107 @@ document.body.appendChild(wrap); } - // The three doors, shown once on a first-ever visit (replaces the silent drop - // into first light). Backdrop/esc = first light, the gentle default. - function showFirstVisitCard() { + // ---- πŸšͺ THE DOORS β€” how the world opens ----------------------------------- + // The front door is a skin like any other: six openings, each a different way + // to meet the same instrument. Your door is remembered (gs_door) and walked + // again on every arrival; right-click the sky β†’ πŸšͺ the doors to choose again. + // Each door is ONE registry entry + one act β€” delete the entry, the door is gone. + let doorMode = null; // "rig" | "deal" | "dawn" while that door shapes the field (session-only) + let dawn = null; // the entrance in progress: { queue, next, t } + + function doorRig() { // πŸŽͺ walk on stage: performance surfaces up, the field folded tight + doorMode = "rig"; + const _m = ungodly.muted; ungodly.muted = true; applyMood("first light"); ungodly.muted = _m; + sleepFold = true; + openBeat(); openGodspeed(); + eventTicker.unshift({ text: "πŸŽͺ the rig β€” beat + GODSPEED up, the field folded to the liveliest hands. β™ͺ to sound, S for the spheres.", tAdded: now(), src: "sys" }); + } + + // πŸƒ the deal's deck: dests worth being dealt, [key, amount, curve] + const DEAL_DESTS = [ + ["filter.cutoff", 0.6, "scurve"], ["pad.brightness", 0.5, "lin"], ["reverb.size", 0.45, "lin"], + ["delay.feedback", 0.4, "exp"], ["lfo.rate", 0.4, "lin"], ["drone.voices", 0.5, "lin"], + ["wavetable.morph", 0.45, "lin"], ["glitch", 0.25, "exp3"], ["master.space", 0.5, "scurve"], ["saturation", 0.35, "exp"]]; + let dealChipEl = null; + function dealChip(show) { + if (!dealChipEl) { + dealChipEl = document.createElement("div"); dealChipEl.id = "dealchip"; dealChipEl.textContent = "πŸƒ deal again"; + dealChipEl.title = "the world deals a fresh hand β€” five cables, one pulsing (⌘Z takes it back)"; + dealChipEl.onclick = () => doorDeal(false); + document.body.appendChild(dealChipEl); + } + dealChipEl.style.display = show ? "block" : "none"; + } + function doorDeal(silent) { // πŸƒ the world deals a hand: five lively sources onto five voices + const pool = [...sources.values()].filter(n => !/^drum\./.test(n.key)); + pool.sort((a, b) => ((b.flowDisp || 0) * 2 + (b.norm || 0)) - ((a.flowDisp || 0) * 2 + (a.norm || 0))); + if (!pool.length) { eventTicker.unshift({ text: "πŸƒ the world hasn't spoken yet β€” try the deal again in a moment", tAdded: now(), src: "sys" }); return; } + doorMode = "deal"; + if (!silent) ungodlyMark("the deal"); + loadPatch({ routes: [], tweaks: {}, seqs: {} }); + seedBeat(); // a kit waits in the beat panel, off until you press β–Ά + const picked = [], seenFam = {}; + for (const n of pool) { if (picked.length >= 5) break; if (seenFam[n.family]) continue; seenFam[n.family] = 1; picked.push(n); } + for (const n of pool) { if (picked.length >= 5) break; if (picked.indexOf(n) < 0) picked.push(n); } + const deck = DEAL_DESTS.slice(); + for (let i = deck.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); const sw = deck[i]; deck[i] = deck[j]; deck[j] = sw; } + const pulseCard = Math.floor(Math.random() * picked.length); // one card is always dealt pulsing + picked.forEach((n, i) => { + const d = deck[i]; + registerRoute({ source: n.key, dest: d[0], amount: d[1], curve: d[2], pulse: i === pulseCard ? (Math.random() < 0.5 ? 16 : 8) : 0 }); + }); + layoutDirty = true; + dealChip(true); + eventTicker.unshift({ text: "πŸƒ the deal: " + picked.map(n => n.label || n.key).join(" Β· ") + " β€” one card pulses. β™ͺ to hear it.", tAdded: now(), src: "sys" }); + } + + function doorDawn() { // πŸŒ’ the players take the stage one at a time, liveliest first + doorMode = "dawn"; + const _m = ungodly.muted; ungodly.muted = true; applyMood("first light"); ungodly.muted = _m; + const cabled = new Set(); + for (const r of routesArr) cabled.add(r.source); + const list = [...sources.values()]; + list.sort((a, b) => (cabled.has(b.key) - cabled.has(a.key)) || ((b.flowDisp || 0) - (a.flowDisp || 0))); + for (const n of list) n.hidden = true; + dawn = { queue: list.slice(0, 18), next: 0, t: 0 }; + layoutDirty = true; + eventTicker.unshift({ text: "πŸŒ’ the dawn β€” the world takes the stage, one hand at a time.", tAdded: now(), src: "sys" }); + } + function dawnPass(t) { // one arrival per beat-and-a-bit; then the fold takes over + if (!dawn) return; + if (!dawn.t) dawn.t = t + 1.0; + if (t < dawn.t) return; + const n = dawn.queue[dawn.next++]; + if (n) { + n.hidden = false; layoutDirty = true; + eventTicker.unshift({ text: "πŸŒ’ " + (n.label || n.key) + " takes the stage", tAdded: now(), src: "sys" }); + } + dawn.t = t + 1.25; + if (dawn.next >= dawn.queue.length) { + dawn = null; // the entrance is over β€” sleepFoldPass reconciles the rest + eventTicker.unshift({ text: "πŸŒ… the world is on stage.", tAdded: now(), src: "sys" }); + } + } + + const DOORS = [ + { key: "zero", emoji: "🌱", title: "start from zero", desc: "an empty field. build the instrument yourself, one cable at a time.", act: () => enterZero() }, + { key: "light", emoji: "πŸŒ…", title: "first light", desc: "a gentle arrangement β€” a bass line, soft hats, a small melody. ease in.", act: () => applyMood("first light") }, + { key: "storm", emoji: "β›ˆ", title: "the full storm", desc: "every one of the world's hands on it at once. the factory instrument.", act: () => applyMood("the full storm") }, + { key: "rig", emoji: "πŸŽͺ", title: "the rig", desc: "walk on stage: beat + GODSPEED open, the field folded to the liveliest hands.", act: () => doorRig() }, + { key: "deal", emoji: "πŸƒ", title: "the deal", desc: "the world deals you five cables β€” one always pulsing. redeal until it sings.", act: () => doorDeal(false) }, + { key: "dawn", emoji: "πŸŒ’", title: "the dawn", desc: "the players take the stage one at a time, liveliest first. watch it assemble.", act: () => doorDawn() }, + ]; + function walkDoor(key, save) { + const d = DOORS.filter(x => x.key === key)[0]; + if (!d) return; + doorMode = null; dawn = null; dealChip(false); // a new door closes the old one's furniture + if (save) try { localStorage.setItem("gs_door", key); } catch (e) {} + d.act(); + } + + // The doors card β€” first-ever visit, and any time from the sky menu. + // First visit: esc/backdrop = first light (the gentle default). Re-choosing: esc just closes. + function showDoorsCard(again) { if (document.getElementById("firstcard")) return; const wrap = document.createElement("div"); wrap.id = "firstcard"; @@ -3988,10 +4092,10 @@ alignItems: "center", justifyContent: "center", background: "rgba(6,10,20,0.72)", backdropFilter: "blur(8px)", WebkitBackdropFilter: "blur(8px)" }); const card = document.createElement("div"); - Object.assign(card.style, { maxWidth: "640px", width: "88%", textAlign: "center", + Object.assign(card.style, { maxWidth: "760px", width: "92%", textAlign: "center", color: "#d6e2f4", fontFamily: "inherit" }); const h = document.createElement("div"); - h.textContent = "one world, three doors"; + h.textContent = "one world, " + ["", "one", "two", "three", "four", "five", "six", "seven", "eight"][DOORS.length] + " doors"; Object.assign(h.style, { fontWeight: "800", fontSize: "clamp(22px,4vw,32px)", letterSpacing: "2px", marginBottom: "6px" }); const sub = document.createElement("div"); sub.textContent = "the planet is already playing. choose how you meet it."; @@ -3999,32 +4103,28 @@ card.appendChild(h); card.appendChild(sub); const row = document.createElement("div"); Object.assign(row.style, { display: "flex", gap: "14px", flexWrap: "wrap", justifyContent: "center" }); - const doors = [ - ["🌱", "start from zero", "an empty field. build the instrument yourself, one cable at a time.", () => enterZero()], - ["πŸŒ…", "first light", "a gentle arrangement β€” a bass line, soft hats, a small melody. ease in.", () => applyMood("first light")], - ["β›ˆ", "the full storm", "every one of the world's hands on it at once. the factory instrument.", () => applyMood("the full storm")], - ]; - for (const [emoji, title, desc, act] of doors) { + for (const d of DOORS) { const b = document.createElement("div"); Object.assign(b.style, { flex: "1 1 170px", minWidth: "160px", maxWidth: "200px", cursor: "pointer", padding: "20px 16px", borderRadius: "16px", background: "rgba(20,28,44,0.7)", border: "1px solid rgba(120,150,200,0.28)", transition: "transform .12s, border-color .12s" }); b.onmouseenter = () => { b.style.transform = "translateY(-3px)"; b.style.borderColor = "rgba(150,200,255,0.7)"; }; b.onmouseleave = () => { b.style.transform = ""; b.style.borderColor = "rgba(120,150,200,0.28)"; }; - b.innerHTML = '
      ' + emoji + '
      ' - + '
      ' + title + '
      ' - + '
      ' + desc + '
      '; - b.onclick = () => { wrap.remove(); document.removeEventListener("keydown", onEsc, true); act(); }; + b.innerHTML = '
      ' + d.emoji + '
      ' + + '
      ' + d.title + '
      ' + + '
      ' + d.desc + '
      '; + b.onclick = () => { wrap.remove(); document.removeEventListener("keydown", onEsc, true); walkDoor(d.key, true); }; row.appendChild(b); } card.appendChild(row); const foot = document.createElement("div"); - foot.textContent = "you can switch any time β€” right-click the sky β†’ πŸŒ— mood"; + foot.textContent = "your door is remembered β€” right-click the sky β†’ πŸšͺ the doors to choose again"; Object.assign(foot.style, { marginTop: "22px", fontSize: "11px", color: "rgba(150,170,205,0.55)" }); card.appendChild(foot); wrap.appendChild(card); - const onEsc = (e) => { if (e.key === "Escape") { wrap.remove(); document.removeEventListener("keydown", onEsc, true); applyMood("first light"); } }; - wrap.onclick = (e) => { if (e.target === wrap) { wrap.remove(); document.removeEventListener("keydown", onEsc, true); applyMood("first light"); } }; + const close = () => { wrap.remove(); document.removeEventListener("keydown", onEsc, true); if (!again) applyMood("first light"); }; + const onEsc = (e) => { if (e.key === "Escape") close(); }; + wrap.onclick = (e) => { if (e.target === wrap) close(); }; document.addEventListener("keydown", onEsc, true); document.body.appendChild(wrap); } @@ -4914,13 +5014,21 @@ if (sleepChipEl._t !== txt) { sleepChipEl._t = txt; sleepChipEl.textContent = txt; sleepChipEl.style.display = txt ? "block" : "none"; } } function sleepFoldPass(t) { + if (dawn) { sleepChip(0); return; } // πŸŒ’ during the entrance, the dawn owns the field const canFold = !chakraMode && !zeroMode && connected; const cabled = new Set(); for (const r of routesArr) cabled.add(r.source); + let onStage = null; // πŸŽͺ the rig folds harder: only the liveliest uncabled hands stay + if (canFold && doorMode === "rig") { + const flows = [...sources.values()].filter(n => !cabled.has(n.key)).map(n => n.flowDisp || 0).sort((a, b) => b - a); + onStage = flows.length > 8 ? Math.max(flows[7], 0.02) : 0; // the 8th-liveliest sets the bar (never below the quiet line) + } let slept = 0; for (const n of sources.values()) { - const asleep = canFold && !cabled.has(n.key) && !selection.has(n.key) && soloKey !== n.key && - (n.norm || 0) <= 0.02 && (t - (n._lastMoveT != null ? n._lastMoveT : -1e9)) > 45; + const quiet = onStage != null + ? (n.flowDisp || 0) < onStage + : (n.norm || 0) <= 0.02 && (t - (n._lastMoveT != null ? n._lastMoveT : -1e9)) > 45; + const asleep = canFold && !cabled.has(n.key) && !selection.has(n.key) && soloKey !== n.key && quiet; if (asleep) slept++; const hide = asleep && sleepFold; if (hide !== !!n.hidden) { n.hidden = hide; layoutDirty = true; } @@ -5190,6 +5298,8 @@ if (has("stop") || has("pause")) { godspeakBeat(false); godspeakSay("beat stopped"); return; } if (has("play") || has("start") || has("go")) { godspeakBeat(true); godspeakSay("beat playing"); return; } } + if (has("doors") || has("doorway")) { showDoorsCard(true); godspeakSay("πŸšͺ the doors"); return; } + if (has("deal") && doorMode === "deal") { doorDeal(false); godspeakSay("πŸƒ dealt"); return; } // "deal again" β€” only while the deal is your door if (has("spheres") || has("planets")) { // πŸͺ RESONANCE β€” the sky by voice if (has("off") || (has("stop") && !has("start"))) { spheresEnable(false); godspeakSay("spheres off"); return; } if (has("faster") || has("quicker")) { spheresSetRate(spheres.rate * 2); godspeakSay("spheres faster β†’ " + spheres.rate + " d/bar"); return; } @@ -5222,13 +5332,18 @@ // clobber what you're playing. A reload no longer drops you into the wall. if (!moodAppliedThisLoad) { moodAppliedThisLoad = true; - let firstEver = false, saved = "first light"; + let firstEver = false, saved = "first light", door = null; try { firstEver = !localStorage.getItem("gs_seen"); } catch (e) {} try { saved = localStorage.getItem("gs_mood") || "first light"; } catch (e) {} + try { door = localStorage.getItem("gs_door"); } catch (e) {} setTimeout(() => { if (firstEver) { try { localStorage.setItem("gs_seen", "1"); } catch (e) {} - showFirstVisitCard(); // three doors: zero / first light / storm + showDoorsCard(false); // πŸšͺ the doors: zero / light / storm / rig / deal / dawn + } else if (door === "rig" || door === "dawn") { + walkDoor(door, false); // πŸšͺ walk your remembered door (its mood-mark is muted inside) + } else if (door === "deal") { + doorDeal(true); // a fresh hand every arrival β€” silent, no undo mark for the load itself } else if (saved === "0 zero") { enterZero(true); // a returning zero-builder comes back to zero, build intact (C2) } else { @@ -5502,6 +5617,7 @@ } } + dawnPass(t); // πŸŒ’ the entrance, if one is playing sleepFoldPass(t); // πŸ’€ quiet feeds fold; they wake themselves when the world speaks if (layoutDirty) doLayout(); spheresFrame(); // πŸͺ RESONANCE β€” refresh the sphere sources on the great-year clock @@ -5588,7 +5704,7 @@ for (const r of routesArr) { const s = sources.get(r.source); const d = dests.get(r.dest); - if (!s || !d) continue; + if (!s || !d || s.hidden) continue; // πŸŒ’ no wire to a player still in the wings const a = r.activeDisp; const va = Math.max(a, 0.10); // a patched cable NEVER vanishes: a silent source still shows its wire @@ -8256,6 +8372,7 @@ () => { beat.open ? closeBeat() : openBeat(); }, "B"); ctxItem("βͺ UNGODLY β€” take it back" + (ungodly.past.length ? " (" + ungodly.past.length + ")" : ""), () => ungodlyUndo(), "⌘Z"); + ctxItem("πŸšͺ the doors β€” how the world opens", () => showDoorsCard(true)); const moodSub = ctxSub("πŸŒ— mood β€” how much world"); const z0 = document.createElement("div"); z0.className = "it"; z0.textContent = zeroMode ? "0 zero β€” you're building it (a mood leaves)" : "0 zero β€” build from nothing"; diff --git a/viz/manual.html b/viz/manual.html index e542449..d1e522c 100644 --- a/viz/manual.html +++ b/viz/manual.html @@ -673,6 +673,11 @@ background: rgba(20,28,44,0.82); color: rgba(160,180,215,0.75); border: 1px solid rgba(120,150,200,0.22); border-radius: 9px; padding: 4px 10px; font-size: 11px; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); } #sleepchip:hover { color: #cfe0ff; border-color: rgba(140,170,230,0.5); } + /* πŸƒ the deal β€” the redeal chip sits above the sleeper chip */ + #dealchip { position: fixed; left: 14px; bottom: 86px; z-index: 12; display: none; cursor: pointer; + background: rgba(28,24,46,0.85); color: rgba(200,185,235,0.85); border: 1px solid rgba(160,140,220,0.3); + border-radius: 9px; padding: 4px 10px; font-size: 11px; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); } + #dealchip:hover { color: #e8ddff; border-color: rgba(180,160,240,0.6); }