Extending the Game

Where each kind of change goes, which source of truth it starts from, and what to re-run afterwards.

Fidelity first Galaxy Eggbert is a faithful remake. A gameplay change must trace to what Mobile Eggbert does (its Decor.cpp is the authority for numbers and timing), or be recorded as a small, deliberate 3D adaptation (M3D-NNNN) or an owner decision (OR-, MR-, Q-). New media need approval. "It seems plausible" is not a reason. Read the ledgers in plans/audit/ before you start: the question may already be answered.

A block behaviour: the registry first

A block's type number is its icon in Content/icons/object-m.png (0-440). Everything the game knows about a type (render mode, collision mask, animation frames and divisor, hazard kind, water, spring, bridge, switch, door key, teleporter, world-sign index, passable decor) is one BlockDefinition in src/GalaxyEggbert/BlockDefinitionRegistry.cpp (header include/GalaxyEggbert/BlockDefinitionRegistry.hpp). The collision mask is derived from the original's quarter-cell table; deliberate 3D exceptions are named lists in the header, such as kSolidIn3DDecor and kSolidBigDecorIcons.

  1. Change the registry entry (or its named list). Do not add a parallel switch on icon numbers in a consumer.
  2. The consumers follow by themselves: BlupiController (collision, standing), InteractionSystem (hazards and tile reactions), TerrainRenderer and its helpers (render mode, cut-outs, animation), the editor (palette, validation).
  3. Update the expectations in tools/VerifyDefinitionRegistries.cpp, add a movement or interaction test for the new behaviour, and refresh tools/data/blocks.csv, which the Python checkers read (VerifyBlockDefinitionsCsv fails while it is stale).
  4. Re-run the world checks: a tile that becomes solid or passable can open or close a route.

A worked example is the tutorial A Block or Object Behaviour.

An object behaviour

Moving and interactive objects are the original's Def::ObjectType values (0-203, include/GalaxyEggbert/Def/ObjectType.hpp). Their per-type facts live in src/GalaxyEggbert/Game/ObjectDefinitionRegistry.cpp: render mode and texture sheet, placement and semantic kind, patrol timing, and declarative capability flags (dynamiteDestructible, genericContactHazard, balloonPoppableHazard, standardPickupTouch, smallEnemy, levelExit...).

A sound

The game plays the original's 93 sound channels (Content/sounds/sound000.wav to sound092.wav, Def::SoundChannel) with the original's per-channel volume and pitch table (kVolumePitch in src/GalaxyEggbert/Game/Sound.cpp). There are no new sound files: "adding a sound" means playing an existing channel at a moment where the original plays it, with Sound::Play or, positioned in the world, Sound::PlayAt. An example from the history: feat(audio): the lightning's zap (channel 69) under its emitter, as Decor::BlitzActif, which plays channel 69 at each lightning emitter on the cycle ticks the original uses. Music tracks are rendered from the original's MIDI by tools/music/render_music.py; a world's track is stored in its header. See Audio & Music.

A model: through the converters, never by hand

Blupi, his vehicles, the objects and the terrain reliefs are converted from the original Ray Dream scenes (the sibling data repository speedyblupi-data) and are generated files.

Check the match against the sprites (each model's comparison sheet, tools/model_parity.py), run VerifyObjectModelTable, refresh plans/inventory/assets.md with python3 tools/asset_inventory.py, and look at it live (world998, the gallery, shows every object). See Assets & Tools.

A world

Campaign worlds are Python generators in tools/worlds/; the .vwr files in worlds3d/ are their committed output. Change the script, never the file. The full walk-through is the tutorial Authoring a World. A new numbered campaign world beyond the 78 is not a routine task: the hubs, HubDoors, campaigncheck.py and the autoplay chains encode the original's structure. Players' own worlds are made in the 3D World Editor.

An editor feature: UX only

The editor (src/GalaxyEggbert/Editor/, namespace GalaxyEggbert::Editor) is the one place where new UX may be invented. It must not add gameplay: it places the game's existing blocks and objects, applies the original's creature timing, and validates through the same registries. Its palette follows the Eggbert 2 editor's order where the original specifies one; parity findings are ED-NNNN rows in plans/audit/editor_parity.md. Keep editor logic testable without a window and extend VerifyGEWorldEditor, VerifyGEEditorPalette, VerifyGEEditorStorage, VerifyGEEditorObjects or VerifyGEEditorDevices; for flows that need the screen, add or replay a step file in tools/qa_steps/.

What to re-run afterwards

You changedRe-run
a block or object behaviourctest (registries, CSV, movement and interaction verifiers), golden trace, the affected missions' autoplay scripts, tools/verify_demos.sh
anything in the simulationthe playthroughs and the demo replays. If the shipped demos no longer replay as recorded, bump kDemoSimulationRevision in DemoReplay.hpp and record them again (tools/record_demo.sh)
a sound cuectest (VerifyBlupiMovement checks one-shot sound cues), a live listen with QA_SOUND=1
a modelVerifyObjectModelTable, the parity sheet, golden frames if a model in world999 changed
a worldthe generator, the Python checkers, VerifyShippedWorlds, the world's playthrough
the editorthe editor verifiers and the relevant tools/qa_steps/editor_*.txt

Details for each check: Testing & QA. Record what you found in the ledger row it belongs to, and update CURRENT.md when the verified state or a limitation changes.