Round 4 orders 1-5: - ghost keys demolition off setGhost's `mode` arg; the '__remove' sentinel is ignored and REMOVE_DEF is gone from this lane. - locked-build ghost: padlock-amber (not occupied-red — "not yet" and "not here" are different sentences) + a padlock tinted by the gating era. Needed a new ERA_TELL palette: ERA_SKIN.tint is a multiplier tuned to leave DATA's chassis alone, so it can't carry a signal (stream is #ffffff). - aura.ts: cooling coverage as a dashed RECTANGLE. coolRadius is chebyshev, so the cooled region is the footprint grown by r per side; a circle would look better and lie about the corners. - lab placeholder: bench + the "bottled artifact triptych" (§2); consumes MachineDef.accent when present, derivation stays the fallback. Also: - buffer fill reads bandwidth.capacity (v4) instead of re-summing bufferCap — a second copy of the sim's arithmetic is what the scram latch warned about. - lock rule matched to LANE-UI's reading: absent research means nothing has completed, so gated machines padlock. Mine said the opposite; theirs is the literal contract text, and the build bar and ghost disagreeing on the same screen is worse than either answer. - showroom fitted to BOTH axes. Round 3's depth-only fit collapsed the item grid to one 206-tile row once the machine hall grew and ate the depth. Verified against DATA's real v4 data and UI's migrated flow: 6x6 aura on asic-cooler, reel/broadcast era tells, lab accent #ffc24a from data, 29 machines + 60 items in the showroom, 500 entities + 2,000 items at 3.6ms. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
74 lines
3.0 KiB
TypeScript
74 lines
3.0 KiB
TypeScript
/**
|
|
* LANE-RENDER — era skins (style guide §8).
|
|
*
|
|
* "Reel Beds = sepia/brass/celluloid; Broadcast = phosphor green + wood-grain TV;
|
|
* Disc = rainbow-on-black gloss; Stream = flat white plastic + LED; Fortress =
|
|
* blinding matte white."
|
|
*
|
|
* Plumbing only this round. A machine's era is DERIVED from which tech unlocks it, so
|
|
* when LANE-DATA adds reel/broadcast techs those machines re-skin with zero renderer
|
|
* work. Machines no tech unlocks are starting equipment and take the default.
|
|
*
|
|
* The skin touches the CHASSIS only — the emissive accent stays the colour of what the
|
|
* machine makes, because that's the resource, not the decade.
|
|
*
|
|
* `stream` is deliberately the identity skin (same roughness/metalness as the base body
|
|
* material, no tint), so today's look is byte-for-byte unchanged until data says otherwise.
|
|
*/
|
|
import * as THREE from 'three';
|
|
import type { GameData, TechDef } from '../contracts';
|
|
|
|
export type Era = TechDef['era'];
|
|
export const DEFAULT_ERA: Era = 'stream';
|
|
|
|
export interface EraSkin {
|
|
/** Multiplied into the chassis colour — 0xffffff leaves DATA's value alone. */
|
|
tint: number;
|
|
roughness: number;
|
|
metalness: number;
|
|
}
|
|
|
|
export const ERA_SKIN: Record<Era, EraSkin> = {
|
|
reel: { tint: 0xffd2a0, roughness: 0.65, metalness: 0.45 }, // sepia/brass/celluloid
|
|
broadcast: { tint: 0xc4e8c4, roughness: 0.8, metalness: 0.1 }, // phosphor + woodgrain
|
|
disc: { tint: 0x9aa0b8, roughness: 0.2, metalness: 0.65 }, // rainbow-on-black gloss
|
|
stream: { tint: 0xffffff, roughness: 0.85, metalness: 0.15 }, // identity — the default
|
|
fortress: { tint: 0xffffff, roughness: 0.95, metalness: 0.0 }, // blinding matte white
|
|
};
|
|
|
|
/**
|
|
* machine id -> era, via the tech that unlocks it. Anything unmapped is starting
|
|
* equipment (no tech gates it) and takes DEFAULT_ERA.
|
|
*/
|
|
export function eraIndex(data: GameData): Map<string, Era> {
|
|
const out = new Map<string, Era>();
|
|
const machines = new Set(data.machines.map((m) => m.id));
|
|
for (const t of data.tech) {
|
|
for (const u of t.unlocks) if (machines.has(u)) out.set(u, t.era);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/** Skin a chassis material in place. Accents must never be passed here. */
|
|
export function applyEraSkin(mat: THREE.MeshStandardMaterial, era: Era): void {
|
|
const skin = ERA_SKIN[era] ?? ERA_SKIN[DEFAULT_ERA];
|
|
mat.color.multiply(new THREE.Color(skin.tint));
|
|
mat.roughness = skin.roughness;
|
|
mat.metalness = skin.metalness;
|
|
}
|
|
|
|
/**
|
|
* Era read as a SIGNAL rather than a surface — used by the locked-build ghost to say
|
|
* *which* era is holding a machine back. `ERA_SKIN.tint` can't do this job: it's a
|
|
* multiplier tuned to leave DATA's chassis alone (stream is literally white), so three
|
|
* of the five eras would tell you nothing. These are chosen to be told apart at a
|
|
* glance, in codex order.
|
|
*/
|
|
export const ERA_TELL: Record<Era, number> = {
|
|
reel: 0xd8a24a, // brass
|
|
broadcast: 0x5fd08a, // phosphor
|
|
disc: 0x6fd7ff, // iridescent
|
|
stream: 0xdfe6f2, // flat plastic white
|
|
fortress: 0xf2f7ff, // blinding
|
|
};
|