-
Notifications
You must be signed in to change notification settings - Fork 0
Provenance
Where each crucible package came from: the files that were reviewed and
consolidated, and what deliberately stayed behind in each repo. Pandemonium
was reviewed at its feat/audio-cues branch; every other repo at trunk.
The last four rows (view, rng, ring, paint) came from a second pass
over the family. The rule of thumb, in order: if two or more repos carry the
same display-free logic, extract it; if it is nearly the same but carries
app-defined values, make the engine side generic and let each app keep its
vocabulary; otherwise leave it, and say why (see Deliberately not
consolidated below).
| Package | Consolidated from | Stays app-side |
|---|---|---|
geom |
vivarium internal/geom/vec2.go; the Vec2/Coord/Room types and clamp helpers repeated in pandemonium, nemesis, and hegemony |
— |
hud |
pandemonium internal/hud/overlay.go and nemesis internal/hud/overlay.go (byte-identical) |
— |
canvas |
nemesis internal/gui/canvas.go (pandemonium composes menus in its renderer the same way) |
game-specific screens drawn on it |
menu |
nemesis internal/gui/menu.go + menuflow.go; pandemonium internal/gui/menu.go. Made display-free when autobahn needed the model behind raylib |
the actual title/pause/settings menus, wired as menu.Item values |
menu/ebiteninput |
the Ebiten key polling lifted out of menu in that split, so the model stayed usable by any front-end and Ebiten callers kept the same bindings |
which keys each app binds |
store |
nemesis internal/gui/settings.go + records.go; pandemonium internal/gui/settings.go + records.go (load/save/path plumbing) |
the Settings/Records structs, their clamping and summaries |
status |
pandemonium internal/status/source.go (Line, Source contract) |
the cue vocabulary, band policy, and scripted tables |
narrate |
pandemonium internal/phrasing/phrasing.go
|
personas.json, the cue→event/data mapping |
telemetry |
pandemonium internal/telemetry/bus.go; nemesis internal/telemetry/telemetry.go (bus mechanics: fan-out, nil filtering, bounded feed) |
event types, path/profile aggregation, feed wording |
synth |
pandemonium internal/audio/synth.go; nemesis internal/audio/synth.go (render loop, envelopes, pan, and the shared cue shapes) |
cue enums, CueFor mappings, each game's sound design |
record |
rubix internal/gui/record.go; galapagos internal/gui/record.go (superset of both); gambit internal/gui/record.go; pandemonium internal/gui/screenshot.go; pandemonium tools/demogen's encodeMP4 (as the video mode behind an .mp4 --record path) |
recording keybinds and drivers |
hub |
rubix internal/cli/coord.go + internal/gui/link.go; hegemony internal/cli/coord.go + internal/gui/link.go; nemesis internal/cli/coord.go + internal/gui/link.go
|
each app's Msg type and its Route policy |
worldgen |
pandemonium internal/world/bsp.go + connectivity.go; nemesis internal/world/bsp.go + connectivity.go
|
app-specific carving beyond the shared passes |
level |
the engine-owned world model: pandemonium internal/world/level.go, tile.go, heights.go (levels, dais, ceilings), lowwall.go, the lift ledge + kinematics, generate.go (attempt/validate pipeline, spawn/farthest-exit), theme.go, sky.go; nemesis tile.go (vent/console/locker → TileVent/TileSwitch/TileCover), doors.go, vents.go (improved: centred mouths instead of corner-biased, branching tree networks via nearest-carved-tunnel Dijkstra, depth bias keeping tunnels off wall faces) |
items, markers, hazards, gates/keys, arenas, light moods and flicker, runtime door/lift state |
raycast |
nemesis internal/render/walls.go (DDA) + sprites.go (projection); pandemonium internal/render/camera.go + columns.go (the height-aware column walk, as WalkColumn over a painter interface) |
texturing, shading, framebuffers, sliding-door column logic (built on the exported boundary helpers) |
camera |
vivarium internal/gui/camera.go
|
input wiring |
view |
galapagos internal/core/camera.go — the display-free pan/zoom math, adopted back as a type alias; camera now layers the Ebiten draw transform over it |
the Ebiten draw transform (stays in camera) |
rng |
galapagos internal/sim/rng.go + hegemony internal/sim/rng.go (the PCG stream constructor), and gambit's three agent seeders |
each app's stream ids and seed policy |
ring |
galapagos internal/sim/telemetry.go (Ring[T]) + vivarium's two hand-rolled bounded histories |
what each ring holds |
paint |
pandemonium internal/render/{walls,columns,sprites,tint}.go + nemesis internal/render/{renderer,hud}.go (colour brightness scale + full-frame blend) |
each game's distance-shading curve (art direction) |
keymap |
galapagos Keymap []string + drawKeymap; vivarium's two hard-coded overlay hint lines; rubix segments + wrapHelp; gambit's hints bar — the shared "key: action" control-bar layout (format, wrap, bottom-anchored stacking), kept display-free via a caller-supplied width measure |
which keys each app binds and what they do |
window |
the SetWindowSize/SetWindowTitle/SetWindowResizingMode setup every app's gui package repeated, unified so every window is resizable with the same policy (imports Ebiten, like menu/camera) |
each app's title and computed size |
demo |
the shared mechanics of nemesis's and pandemonium's tools/demogen: the capture loop (Clip), the contact-sheet tiling behind stationsMontage and the stills grids (Montage), and pandemonium's demoPalette brightness fan-out (Ramp) |
every clip's content — which levels, staging, bot inputs, palette colours and crops each game shows off |
spatial |
vivarium internal/sim/grid.go (the toroidal bucket grid) and the open-plane grid autobahn's endless city needs |
what each grid holds, and the cell size, which is tuned per world |
netplay |
autobahn internal/netplay — host/join, gob framing, snapshot cadence — made generic over the snapshot and input types |
the snapshot and input types themselves, and anything both ends can derive from a seed |
pinhole |
the perspective camera model behind autobahn internal/vision.Camera, generalised into forward projection and inverse ground ranging; completes the family's camera set alongside view and raycast
|
the detection classes and the pixel scanner, which are autobahn's own perception vocabulary |
Things that look shared but stay in each app, and why:
-
HUD / data panel rendering (galapagos, vivarium, hegemony, gambit, rubix).
Blocked two ways: only
menuandcameramay import Ebiten, so a panel renderer cannot become a package without breaking that rule; and the panels are genuinely bespoke (different data, different layouts) with no shared logic, only a shared look. The display-free kernels behind them did move: the rolling history a sparkline plots, asring; and the control-hint bar's layout, askeymap(it takes a width-measure callback, so it positions the "key: action" hints without importing Ebiten). Text screens that do not sit on a live GL frame already havecanvas. -
Sprite frame / direction picking (pandemonium, nemesis). Both choose a
sprite per entity, but by different schemes (a
bands × dirsface table vs. a state-and-timealienFrame). Gameplay presentation, app-side by the engine rules. -
Notice / narration text (pandemonium, nemesis).
notice()andEvent.Line()turn events into player-facing strings — the app-defined message vocabulary the engine must not grow. The mechanism is already shared (telemetry.Bus,hud,status,narrate); only the words stay home. -
Telemetry event types (all sims). Each
Event/Observationis app vocabulary; the generic fan-out istelemetry.Bus. -
Distance-shading curves (pandemonium, nemesis). How brightness falls off
with distance is art direction — different formulas, different constants.
Only the pixel op (
paint.Scale) is shared. -
Neural networks (vivarium
neural, galapagosnn). Genuinely different nets (recurrent per-agent brains vs. feed-forward NEAT genomes); the shared surface is too thin to be worth a common package.
The CLI entrypoint also stays out of crucible: the root command and the
completion subcommand (copied verbatim across rubix, gambit, and vivarium
today) are CLI plumbing, not engine code, so each app keeps its own
internal/cli. CONVENTIONS.md holds the family to a single shape for them.
Dependencies consumed rather than consolidated: ordinex sorts the hub's window ids, off the per-frame hot path — render-time sorts keep their in-place insertion sorts deliberately.
retrievium is deliberately not a crucible dependency. Its searchers do
exact-match membership on a sorted slice, and every such list in the engine
(tile kinds, window ids, a handful of shells) is small enough that a linear
scan is as good — a binary-search dependency here would be decoration, not
value. Its honest fits are app-local exact-match lookups, called out in the
gambit and nemesis guides. The one search the engine genuinely wants is
threshold selection — pick an index by cumulative weight — which is a
different search (upper-bound, not exact-match) and lives in
worldgen.WeightedChoice on the standard library's sort.Search.