diff --git a/fktry/MASTERPLAN.md b/fktry/MASTERPLAN.md index 836b36f..dbd7ae6 100644 --- a/fktry/MASTERPLAN.md +++ b/fktry/MASTERPLAN.md @@ -52,12 +52,19 @@ codex wins. missing data. Build to the contract, stub the rest, note the stub. - If the app won't boot because of someone else's in-flight work, note it, build against the contracts anyway, and verify with `npm test`/`check` this round. +- **Dev server ports are assigned per lane** so five parallel sessions never collide: + `npm run dev -- --port ` — SIM 8151, RENDER 8152, UI 8153, SCREEN 8154, + DATA 8155. 8150 belongs to the orchestrator. Do not create or edit + `.claude/launch.json` (orchestrator-owned, lives outside the repo). ## Milestones (orchestrator's map — lanes don't self-assign ahead) -- **M1 — FLOW** (rounds 1-2): place extractor → belts → demuxer → quantizer → mosh - reactor → shipper; items visibly ride belts; MELT ships; THE SCREEN corrupts in - response. The whole fantasy in miniature. +- **M1 — FLOW** (rounds 1-2): the full canon melt chain — ore → demuxer → + {quantizer, subsampler, p-caster, dct-press} → gop-assembler → mosh reactor → + shipper. RULING (round 1): LANE-DATA's codex-faithful chain replaces the original + 6-machine sketch; the ~50-entity build IS the fantasy. A deadlock-free reference + factory ships as the official demo (SIM round 2). Items visibly ride belts; MELT + ships; THE SCREEN corrupts item-specifically. - **M2 — PRESSURE**: bandwidth economy + brownouts, heat/throttling, HF dust piles, first wildlife (mosquito swarms), commissions via fax UI. - **M3 — DEPTH**: tech tree + eras, bloom loops, subsampler bleed, moiré growth, diff --git a/fktry/lanes/LANE-DATA.md b/fktry/lanes/LANE-DATA.md index 2b4b493..c3aa800 100644 --- a/fktry/lanes/LANE-DATA.md +++ b/fktry/lanes/LANE-DATA.md @@ -26,40 +26,46 @@ REQUESTs. --- -## CURRENT ORDERS — Round 1 (goal: milestone M1 "FLOW" + M2 prep) +## CURRENT ORDERS — Round 2 (goal: contracts-v2 content + M2 "PRESSURE" economy) -The seed JSONs in `data/` are orchestrator placeholders — correct them, then grow: +Round 1 review: the validator's fault-injection and the FORWARD_CANON allowlist were +exactly right. Verdicts on your report: **M1 IS your canon chain** — MASTERPLAN +updated, no shortcut recipe, the depth is the game. Your four inventions +(`press-anchor-slab`, `color-plate` + `v-hold-roll`, `artifact-bottler`, +`lane-splitter`) are **CANONIZED**. Dials-as-recipes STAYS (right call; a real dial is +a v3+ contract discussion). CONTRACT REQUEST GRANTED: `MachineDef.heatPerTick?` exists +now — and your validator should now also accept `color?`, `flavor?`, `bufferCap?`, +`CommissionDef.repeat?`. -1. **Validator first** (`src/sim/data-validate.test.ts`, vitest): every recipe's - machine + items exist; every machine's recipes exist and point back at it; every - tech unlock exists; commission wants exist; ids unique + kebab-case; colors valid - hex; footprints ≥1×1. This test is the lane's handshake — it must fail loudly on - any dangling reference. -2. **Items** → full §2 + §3 catalog: all raw resources, refined intermediates, frames, - the five science packs, and tier-2 products (melt, bloom-concentrate, - macroblock-bricks, ringing-halos, static-canister, moire-crystal, false-color- - speckle, judder-cams, explosive-hits, gamut paints ×3). Colors from codex vibes. -3. **Machines** → full §4 catalog (demosaicer, dct-press, subsampler, mv-extractor, - p-caster, gop-assembler, bloom-duplicator, field-loom, telecine-puller, - hex-splicer, software-decoder, buffer-tank...). Machines beyond M1's seven should - exist in data even though sim/render only generically support crafter/power kinds - — that's fine, they'll work as generic crafters until given bespoke mechanics. -4. **Recipes** → wire the economy: ore → luma/chroma/mv-flux (demux), luma → coeff - packs + hf-dust (quantize, generates bandwidth), mv-flux → delta-wafers, slab + - wafers → gop-crate, gop-crate → melt + reclaimed slab (mosh), coeff overdrive → - macroblock-bricks + double hf-dust, hf-dust → static-canister, science pack - assembly ×3 (analog/spatial/temporal). Include at least two genuinely absurd - ratios for comedy that still balance. -5. **Tech tree v1** → `disc` and `stream` era nodes: unlock quantizer tiers, mosh - reactor, bloom duplicator, subsampler grades. Costs in science packs. -6. **Commissions** → 8-10 in full fax voice (§6 has three exemplars). Escalating - absurdity and quantity; at least one demanding a *live* hazard product you know - isn't shippable yet (mark it `"wants": {"mosquito-swarm": 3}` — forward canon; - the validator should allow items that exist in data even if no recipe makes them). +1. **`data/CANON-DELTA.md`** (new file, yours): ledger of everything invented beyond + the codex — round 1's four + whatever follows. The lore doc absorbs it in one + orchestrator pass later; until then this is the single place canon-drift lives. +2. **Art direction pass**: `color` on all 21 machines (renderer precedence: + your color > derived-from-outputs) and `flavor` on all 21 — codex one-liner each, + house voice. This kills UI's hardcoded lookup and RENDER's black-on-black problem. +3. **Heat economy**: `heatPerTick` on software-decoder, and scale its `powerGen` so + hot-but-mighty vs ASIC cool-and-steady is a real choice; a pinch of `heat` on + hex-splicer recipes. SIM implements throttle/scram this round — your numbers decide + whether anyone ever dares run two software decoders side by side. +4. **`bufferCap` on buffer-tank** — one tank should ride out roughly 10 seconds of + typical M1-chain deficit. Show your arithmetic in CANON-DELTA. +5. **Commission queue content** (SIM lands queue + `repeat` this round): reorder for + a difficulty ramp; add 2-3 `repeat:true` standing orders, including **THE + CORRECTION BUYBACK** — pays generous bandwidth for anchor-slabs, fax written in The + Correction's voice: polite, clean, quietly horrifying. This is the sellout loop + (ship your anchors for power now, watch your SCREEN go clean — SCREEN lane already + sanitizes on anchor shipments). +6. **Container line**: `moov-shard` / `offset-ribbon` / `sync-key` items + recipes + assembling `container-pack` (unblocks `stream-hex-splicing`); prune the + FORWARD_CANON allowlist as they gain recipes. +7. **Bloom line**: `bloom-grade-1..5` items + bloom-duplicator recipes + (grade n + delta-wafers → grade n+1); grade 5 is commission bait. Validator covers + all new content. -**Definition of done:** `npm test` green including your validator; dev server still -boots with expanded data (placeholders render for everything); every entry traceable -to a codex section. NOTES list what you invented beyond the codex. +**Definition of done:** validator green over the expanded catalog; every machine has +color + flavor; the queue tells a story from first-taste to the first buyback; SIM's +reference-factory test still passes against your rebalanced numbers (coordinate via +NOTES if a ratio change breaks their layout). --- diff --git a/fktry/lanes/LANE-RENDER.md b/fktry/lanes/LANE-RENDER.md index 2228d71..9a30d80 100644 --- a/fktry/lanes/LANE-RENDER.md +++ b/fktry/lanes/LANE-RENDER.md @@ -25,37 +25,33 @@ you never simulate. If motion looks wrong, interpolate better or note it — don --- -## CURRENT ORDERS — Round 1 (goal: milestone M1 "FLOW") +## CURRENT ORDERS — Round 2 (goal: finish M1 polish, open M2 "PRESSURE") -Replace the stub in `src/render/index.ts`: +Round 1 review: excellent — the hot-swap verification and the fog/CAM_DIST honesty +note were exactly right. Verdicts: req 1 GRANTED (`BeltItem.id?` in contracts v2 — +use when present, keep order-matching as fallback until v3 hardens it). Req 2 GRANTED +(`MachineDef.color?` — precedence: `def.color` > your derived-from-outputs > +kind accent; your derivation stays as the fallback, it was the right instinct). +Req 3 DONE (main.ts ignores non-left buttons; drop your camera.ts workaround or keep +it as belt-and-braces, your call). -1. **Camera + controls.** Orthographic isometric-style view. Pan: middle-drag or WASD. - Zoom: wheel (clamped ~8..40 world units). Window resize handled. -2. **Ground.** Tile grid over the 64×64 world; subtle Stream-Crust styling (dark - `#0a0812` base, faint `#181425` lines) — see style guide §8. A soft vignette or - fog so the world doesn't just end. -3. **Asset registry + hot-swap.** `registry.ts`: maps asset key → THREE.Object3D - factory. Default: procedural placeholder (footprint-sized beveled box, body color - from data, one emissive accent, tiny idle animation — e.g. quantizer jaw bob). - On init, attempt `GLTFLoader` for `public/assets/models/.glb`; if present it - replaces the placeholder factory. Also poll or re-check on a 10s timer in dev so - assets dropped in mid-session appear without reload (note what you implement). -4. **Entities.** Sync scene to `snap.entities` (add/remove/rotate). Belts get a flat - directional piece with an animated arrow/chevron texture (shader or UV scroll). -5. **Belt items.** InstancedMesh per item type (colored octahedron/box placeholder), - positioned from `BeltItem.t` + belt entity pos/dir, interpolated with `alpha` for - butter-smooth motion at 30 tps sim. -6. **Picking + ghost.** `pickTile` via raycast to the ground plane. `setGhost` shows - the selected machine's placeholder at the hovered tile, tinted cyan; red tint if - tiles occupied (you can read occupancy from `snap.entities` footprints). -7. **Feedback dressing (cheap but load-bearing):** jammed machines get a small - blinking amber light; brownout dims the whole scene ~30% and flickers lights - (listen to snapshot.bandwidth.brownout). +1. **Belt topology visuals**: corner/junction pieces chosen from neighbor analysis — + pure presentation, sim truth unchanged. Splitters (mechanics land this round in + SIM) get a visible output-cycling tell. +2. **Heat**: shimmer + dull-red emissive ramp driven by `EntityState.heat`; throttled + = idle animation visibly slowed; scram = dead-dark + blinking amber until restart. +3. **Buffer tank**: visible fill level (shader window or scale trick) proportional to + the tank's share of `snapshot.bandwidth.stored`. +4. **AnimationMixer**: play clip 0 on rigged GLBs with an idle loop — MODELBEAST + assets are imminent; verify with any animated GLB you can find or make. +5. **Exact cargo interpolation** via `BeltItem.id` — delete the one-tick approximation. +6. **Multi-mesh belt GLBs**: merge-or-group fix (your call, note it) so a fancy belt + asset can't half-render. -**Definition of done:** dev server shows the factory; placing a line from the UI (or -console dispatch) shows machines + items gliding down belts smoothly; a GLB dropped -into `public/assets/models/quantizer.glb` replaces the placeholder. `npm run check` -clean. NOTES written. +**Definition of done:** reference factory (SIM ships it this round — coordinate via +their `src/sim/reference.ts` export in a dev page or test harness) looks ALIVE: corners +read as corners, heat reads as heat, tanks read as tanks; perf standing rule still met +under `?devscene=stress`. --- diff --git a/fktry/lanes/LANE-SCREEN.md b/fktry/lanes/LANE-SCREEN.md index 37e20ca..d8b9671 100644 --- a/fktry/lanes/LANE-SCREEN.md +++ b/fktry/lanes/LANE-SCREEN.md @@ -27,33 +27,36 @@ seed stock — port, don't move). --- -## CURRENT ORDERS — Round 1 (goal: milestone M1 "FLOW") +## CURRENT ORDERS — Round 2 (goal: artifact fidelity, M2 "PRESSURE" hooks) -Replace the stub in `src/screen/index.ts`: +Round 1 review: the pixel-reading verification set the bar for the fleet. Verdicts: +your anchor-slab PROPOSAL — option (b) ADOPTED as M3 fiction (The Correction buyback; +DATA writes the standing order this round); LANE-UI surfaces a SANITISING chip +meanwhile; keep potency −2.0 as shipped. Debug hook: KEEP, gated behind +`import.meta.env.DEV`. The 2-red you flagged was mid-round churn — HEAD is 70/70. +The launch.json affair: resolved, per-lane ports are in MASTERPLAN now; you're on 8154. -1. **Port the GLYTCH shader stack** into a proper module (`glitchStack.ts`): vertex + - fragment shader with the 8 uniforms, compiled against your base texture. Keep - param semantics identical to the prototype (it's tuned; respect the tuning). -2. **Corruption ledger.** `onEvents`: each `shipped` event adds to a per-item tally. - Map items → param pressure in `corruptionMap.ts`, e.g. `melt → u_mosh+u_melt`, - `chroma-slurry → u_chroma`, `hf-dust → u_noise`, `anchor-slab → REDUCES overall - corruption slightly` (shipping clean anchors sanitizes — nice tension, note it for - the orchestrator). Unknown items → small generic noise bump. Pressure eases toward - its tally-driven target (lerp) so shipments feel like waves, not switches. -3. **Escalation curve.** Total corruption 0→1 over roughly the first 100 shipped - items (tune by feel), log-ish so early shipments feel huge. Above 0.8, add - compound wobble (params cross-modulate slowly) so a mature factory's SCREEN looks - *alive*, not maxed-out static. -4. **Brownout reaction.** On `brownout:true`, freeze-frame + heavy dropout lines + - desync judder until it lifts. The factory's pain shows in the sky. -5. **Idle charm.** With zero corruption, the clean feed runs bland loops with a - "NOTHING IS WRONG" chyron. First shipment ever interrupts it with a single frame - of full-white glitch — the awakening. Cheap, unforgettable. +1. **Artifact passes the 8 params can't express** (your list, adopted wholesale): + - legible 8×8 **macroblock quantization** pass — bricks/coeff overdrive must READ + as a blocky grid, not generic noise; + - edge-detected **ringing halos** (Gibbs oscillation hugging contrast edges); + - **moiré interference lattice**. + Wire signatures into corruptionMap: `macroblock-bricks`, `ringing-halos`, + `moire-crystal`. Keep the per-item legibility bar from round 1: shipping 40 bricks + should look UNMISTAKABLY different from 40 slurry. +2. **Shipment lands**: a ~0.4s stamp/silhouette flash in the item's chip color + composited on the feed per `shipped` event (batch bursts: one stamp per event, not + per unit — a 40-unit shipment is one big stamp, not 40 flickers). +3. **Scram wipe**: `scram` events (new in contracts v2) get a one-frame white + "TRACKING LOST" bar wipe. Cheap, from the event stream you already have. +4. **OPTIONAL — signal strain**: buffer tanks land this round; if you want THE SCREEN + to tremble as `bandwidth.stored` drains BEFORE brownout, file the snapshot-access + CONTRACT REQUEST you deferred (`frame(timeMs, snap?)`) and build it next round — + or skip it. Your call; the seam is noted and I'll grant it in v3 if asked. -**Definition of done:** dev build — screen starts sterile; console-dispatch a few -shipments (or wait for the real chain) and corruption rises, item-specifically; -brownout visibly seizes it. `npm run check` clean, frame budget respected (note -measured ms). NOTES written. +**Definition of done:** three new artifact classes verified the way you did round 1 +(drive, read pixels, screenshot); stamps and scram wipe visible on the dev build; +frame budget still <2ms; tests updated for new signatures. --- diff --git a/fktry/lanes/LANE-SIM.md b/fktry/lanes/LANE-SIM.md index 47fe600..a76afb3 100644 --- a/fktry/lanes/LANE-SIM.md +++ b/fktry/lanes/LANE-SIM.md @@ -21,44 +21,39 @@ playable through unit tests. You are the lane the other four trust with their li --- -## CURRENT ORDERS — Round 1 (goal: milestone M1 "FLOW") +## CURRENT ORDERS — Round 2 (goal: finish M1, open M2 "PRESSURE") -Replace the stub in `src/sim/index.ts` with a real implementation: +Round 1 review: outstanding. Verdicts on your report — **rank-routing STAYS** (it made +M1 flow; your proposal 2, auto-fallback, is REJECTED for exactly the reason you gave: +it deletes the byproduct tension). Proposal 3 (starved) APPROVED. Contracts v2 landed +for you: `BeltItem.id?`, `CommissionDef.repeat?`, `MachineDef.heatPerTick?/bufferCap?`, +`SimEvent 'scram'`. Extractor seam tiles: deferred to M3 worldgen, stop worrying. -1. **World + placement.** Tile grid (start 64×64, world-centered). `place` command: - validate footprint fits and tiles are free, spawn `EntityState`, emit `placed`. - `remove` (emit `removed`, refund nothing yet), `rotate`, `setPaused`. Invalid - commands are silently dropped (UI will preview validity later). -2. **Belts.** `kind:'belt'` entities carry up to 2 items each as `BeltItem` (t: 0..1), - moving at `beltSpeed` tiles/sec. Items transfer to the belt/machine the belt points - into (dir). Items on a belt keep minimum spacing 0.45. Belt→machine: push into - `inputBuf` if the machine's current recipe wants that item and buffer < 2× recipe - need; otherwise the item waits (belt backs up — this is core Factorio feel, get it - right). Machine→belt: outputs pop onto an adjacent belt facing away, one item per - available slot. -3. **Crafting.** Machines with recipes: when `inputBuf` covers `inputs`, consume, - run `ticks` (progress 0..1), emit `crafted`, deposit `outputs` into `outputBuf`. - If `outputBuf` holds ≥ 4× outputs, machine stalls with `jammed:"output full"` - (emit `jammed` once per stall, not per tick). Extractors need no inputs. Machines - with multiple recipes use `setRecipe`; default to `recipes[0]`. -4. **Bandwidth (power) v1.** Sum powerGen (power kind) vs powerDraw of all entities + - active recipe `bandwidth` (note: negative recipe bandwidth GENERATES — quantizer - compression pays power, per lore). If draw > gen: global speed multiplier = - gen/draw for all progress and belt movement (brownout), emit `brownout` on/off - edges, set `snapshot.bandwidth` accordingly. No storage/buffer tanks yet. -5. **Shipping.** `kind:'shipper'` consumes ANY item arriving in its input, increments - `shippedTotal`, emits `shipped`. Track the single active commission (first in - data): fill `commissionProgress`, emit `commissionDone` once satisfied. -6. **Tests** (minimum): placement collision; belt line moves an item end to end in - the expected tick count; belt back-pressure (full line stops cleanly, no item - loss/dupe); demux recipe consumes 2 ore → 1 luma + 1 slurry; brownout halves - speed at draw=2×gen; determinism (two sims, same seed+commands, 500 ticks, - deep-equal snapshots); shipping increments and fires commissionDone. +1. **Reference factory.** `src/sim/reference.ts` exporting + `referenceFactory(data: GameData): Command[]` — your deadlock-free M1 layout + (second quantizer on crime, second assembler on i-only). Test: ships melt within + 2,000 ticks AND is still shipping at 20,000 (the deadlock regression net). This + becomes the official demo; I wire it to a dev hook in main.ts next round. +2. **Splitters.** `kind:'splitter'` becomes real: 1 input, round-robin across outward- + facing adjacent belts; a blocked output is skipped, not waited on; your rank cache + traces THROUGH splitters (rank = best reachable downstream). This is the sanctioned + surplus-voiding tool — the rank rule elsewhere is unchanged. +3. **Buffer tanks.** `kind:'buffer'` + `bufferCap`: charge when gen>draw, discharge to + cover deficit, brownout only when tanks run dry; `snapshot.bandwidth.stored` = pool. +4. **Heat v1.** Accumulate `heatPerTick` while active + `RecipeDef.heat` per craft; + ambient dissipation; heat>0.7 → per-machine throttle (curve is your call, note it); + heat=1 → scram: machine stops, emits `{kind:'scram',on:true}`, restarts below 0.5 + with `on:false`. `EntityState.heat` is already in the contract — make it real. +5. **Commission queue.** Data-order activation; `commissionDone` advances; `repeat:true` + re-enters at the back of the queue. Progress counts only while active (keep). +6. **`jammed:'starved'`** — recipe idle with empty inputs, edge-triggered like + 'output full'. +7. **`BeltItem.id`** — stable per-item ids in every snapshot (optional field now; + hardens to required in v3, so just always set it). -**Definition of done:** with the dev server running, the orchestrator can (via UI or -console `__fktryBus.dispatch(...)`) place extractor→belts→demuxer→quantizer→ -mosh-reactor→shipper and watch `shippedTotal.melt` climb. All tests green. NOTES -written (decisions, gotchas, what you'd tackle in round 2). +**Definition of done:** all of the above tested (determinism suite still green); +reference factory test proves melt at scale with zero deadlock; heat demonstrably +throttles and scrams the software decoder in a test. --- diff --git a/fktry/lanes/LANE-UI.md b/fktry/lanes/LANE-UI.md index e401822..9ae2698 100644 --- a/fktry/lanes/LANE-UI.md +++ b/fktry/lanes/LANE-UI.md @@ -24,36 +24,32 @@ dispatch Commands via the bus and read state from `update(snap, events)`. --- -## CURRENT ORDERS — Round 1 (goal: milestone M1 "FLOW") +## CURRENT ORDERS — Round 2 (goal: finish M1 interaction, open M2 "PRESSURE") -Replace the stub in `src/ui/index.ts`: +Round 1 review: strong, and the honesty section was the best in the fleet. Verdicts: +CONTRACT REQUEST 1 GRANTED — `UIBus.pickTile` is live, wired to the renderer in +main.ts. Req 2 GRANTED (`MachineDef.color?`). Req 3 GRANTED as `MachineDef.flavor?` — +LANE-DATA fills it this round; read data first, keep voice.ts as fallback. Req 4 +GRANTED — `happy-dom` is installed; use `// @vitest-environment happy-dom` per test +file (no root config change needed). Build-bar paging PROPOSAL: APPROVED. -1. **Build bar** (bottom center): one button per machine in `GameData.machines` - (icon = colored square from placeholder color + name), hotkeys 1-9. Click/hotkey - selects for placement → sets `bus._sel` (see main.ts) `{def, dir}`; `R` rotates the - pending dir; `Esc`/right-click clears selection. Selected button shows hot state. -2. **Top strip**: bandwidth meter (gen vs draw bar that goes amber near 100%, red + - "BROWNOUT" flash when over), shipped-total ticker, tick/paused indicator. - `Space` toggles pause (dispatch `setPaused`). -3. **Inspector** (click an entity with nothing selected to build): panel showing - machine name, codex flavor line (hard-code a lookup of 1-liners per machine for - now — request codex excerpts in data via NOTES if you want them properly), recipe - with input/output item chips + live buffer counts, progress bar, jam reason in - amber. Close with Esc. (Selecting an entity: you'll need the hovered tile — reuse - `renderer.pickTile` via a `pointerdown` listener on `#ui`'s capture phase or - request a cleaner hook via CONTRACT REQUEST if this fights main.ts's handler; a - pragmatic v1 is fine.) -4. **Commission fax** (top right, under THE SCREEN): the active commission's flavor - text on a fax-paper styled card, `wants` as item chips with progress `3/40`, - satisfying it plays an aggressively mundane "FAX SENT" stamp animation on - `commissionDone`. -5. **Event toasts** (bottom right): `jammed`, `brownout`, `commissionDone` become - small toast lines in the house voice, max 4 on screen, 6s TTL. +1. **Click-to-inspect** via `bus.pickTile`. Delete the TAB-cycle fallback and + `?uifixture` — the sim is real now; drive a brownout by building. +2. **Build bar paging** (your design): kind tabs, `[`/`]` to page, 1-9 within the + active page — every machine hotkey-reachable at any catalog size. +3. **Inspector upgrades**: recipe picker (`setRecipe` finally gets its UI), heat bar + with THROTTLING/SCRAM states, and starved-vs-backed-up wording (sim distinguishes + `'starved'` from `'output full'` this round). +4. **Commission queue UI**: current fax + a "NEXT IN TRAY" peek; `repeat:true` + commissions get a STANDING ORDER stamp. When anchor-slabs ship, show a SANITISING + chip (hardcoding the id is fine this round — the fiction becomes The Correction's + buyback in M3). +5. **Tests, minimum 10**: chips, roster/footprint hit-testing, selection, hotkey map — + under happy-dom. -**Definition of done:** full M1 chain placeable entirely with mouse + hotkeys, no -console needed; brownout is legible from the top strip alone; fax completes when melt -ships. `npm run check` clean, no console errors, pointer fall-through verified. -NOTES written. +**Definition of done:** the reference factory (SIM ships `src/sim/reference.ts` this +round) is inspectable end to end by mouse alone; every machine reachable by hotkey; +tests green; fixture deleted. --- diff --git a/fktry/src/contracts.ts b/fktry/src/contracts.ts index 31a3878..eab9586 100644 --- a/fktry/src/contracts.ts +++ b/fktry/src/contracts.ts @@ -51,6 +51,10 @@ export interface MachineDef { powerGen?: number; beltSpeed?: number; // tiles/sec, kind==='belt' asset: string; // codex asset key for renderer mesh registry, e.g. "quantizer" + color?: string; // v2: art-direction accent; renderer falls back to derived + flavor?: string; // v2: codex one-liner for the inspector (DATA's material) + heatPerTick?: number; // v2: heat while active (the software decoder's whole bit) + bufferCap?: number; // v2: bandwidth storage, kind==='buffer' } export interface TechDef { @@ -65,6 +69,7 @@ export interface CommissionDef { flavor: string; // the fax text — deadpan absurd wants: Record; rewardBandwidth: number; + repeat?: boolean; // v2: standing order — re-activates after completion } export interface GameData { @@ -94,6 +99,8 @@ export interface BeltItem { item: string; entity: number; // belt entity id t: number; // 0..1 progress along the belt tile + id?: number; // v2: stable per-item id for exact render interpolation + // (optional this round; hardens to required in v3) } export interface SimSnapshot { @@ -121,7 +128,8 @@ export type SimEvent = | { kind: 'brownout'; on: boolean; tick: number } | { kind: 'placed'; entity: number; def: string; tick: number } | { kind: 'removed'; entity: number; def: string; tick: number } - | { kind: 'commissionDone'; commission: string; tick: number }; + | { kind: 'commissionDone'; commission: string; tick: number } + | { kind: 'scram'; entity: number; on: boolean; tick: number }; // v2: heat shutdown edge // ---------------------------------------------------------------- module interfaces @@ -144,6 +152,8 @@ export interface UIBus { dispatch(cmd: Command): void; /** current build selection; renderer ghost + click-to-place read this */ selectedBuild(): { def: string; dir: Dir } | null; + /** v2: world picking, plumbed from the renderer — UI's click-to-inspect */ + pickTile(clientX: number, clientY: number): Vec2 | null; } export interface UI { diff --git a/fktry/src/main.ts b/fktry/src/main.ts index d3a1f7e..391eae9 100644 --- a/fktry/src/main.ts +++ b/fktry/src/main.ts @@ -25,6 +25,7 @@ const bus: UIBus & { _sel: { def: string; dir: Dir } | null } = { _sel: null, dispatch: (cmd: Command) => sim.enqueue(cmd), selectedBuild: () => bus._sel, + pickTile: (x, y) => renderer.pickTile(x, y), }; // LANE-UI sets the build selection through this hook: (window as any).__fktryBus = bus; @@ -42,6 +43,7 @@ async function boot() { renderer.setGhost(sel?.def ?? null, renderer.pickTile(e.clientX, e.clientY), sel?.dir ?? 0); }); game.addEventListener('pointerdown', (e) => { + if (e.button !== 0) return; // pan/context buttons never place const sel = bus.selectedBuild(); const tile = renderer.pickTile(e.clientX, e.clientY); if (sel && tile) sim.enqueue({ kind: 'place', def: sel.def, pos: tile, dir: sel.dir }); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ae37f3d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,123 @@ +{ + "name": "glytch", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "happy-dom": "^20.10.6" + } + }, + "node_modules/@types/node": { + "version": "26.1.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz", + "integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/whatwg-mimetype": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz", + "integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/buffer-image-size": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/buffer-image-size/-/buffer-image-size-0.6.4.tgz", + "integrity": "sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/happy-dom": { + "version": "20.10.6", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.10.6.tgz", + "integrity": "sha512-6QD0ilzDDt93tX44y8tbmZdAcdTRYDhUP+Asgi6pC8Pp5IA3cvaZGyoVN/EGtlq9ziT65iPuBBn3ASLr6hCgVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": ">=20.0.0", + "@types/whatwg-mimetype": "^3.0.2", + "@types/ws": "^8.18.1", + "buffer-image-size": "^0.6.4", + "entities": "^7.0.1", + "whatwg-mimetype": "^3.0.0", + "ws": "^8.21.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ws": { + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..4f9b791 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "happy-dom": "^20.10.6" + } +}