WorldEditor
One session of the in-game 3D world editor: browse a player's worlds, fly, place, undo, save and play-test.
Location
| Header | src/GalaxyEggbert/Editor/WorldEditor.hpp |
|---|---|
| Sources | src/GalaxyEggbert/Editor/WorldEditor.cpp (browser, session, drawing), src/GalaxyEggbert/Editor/WorldEditorUpdate.cpp (per-frame input, camera, aiming, placement, history) |
| Namespace | GalaxyEggbert::Editor |
| Engine dependency | Uses CNA (XNA KeyboardState, MouseState, GraphicsDevice) and cna-engine (Camera, DebugDraw). It does not own a WorldRuntime: the host loads or creates the world and passes the World in every frame. |
Purpose
WorldEditor is the in-game 3D world editor. One object lives inside the game host for the whole run and switches between two modes. While browsing (IsBrowsing()) it drives the world screen of the selected player (ten slots, older and shared worlds) or the galaxy screen (the player's own campaign). While editing it flies a free camera over the loaded world, aims a ray through the cursor, places and removes blocks, objects, BigDecor and Blupi starts from the palette, edits objects' routes and timing, keeps an undo history, copies and pastes boxes, and saves and play-tests the world.
The editor is the project's one approved exception to the faithful-remake rule: it may add content-authoring conveniences (a 3D camera, copy and paste, backups, thumbnails, sharing) but no gameplay mechanics. What it can place and how it validates follows the same registries the game uses (BlockDefinitionRegistry, ObjectDefinitionRegistry). It was called GEWorldEditor until the editor moved into the GalaxyEggbert::Editor namespace and lost its GE prefixes; the verifiers keep the old names (VerifyGEWorldEditor and so on).
Collaborators
The editor is split into small parts under src/GalaxyEggbert/Editor/; WorldEditor composes them.
| Part | Header | Role |
|---|---|---|
EditCommandStack, EditCommand | EditCommandStack.hpp | Undo/redo. One tagged EditCommand per user action (BlockEdit, MoveObjectEdit, SkyRegionEdit, SpawnPointEdit, MusicEdit, BigDecorEdit, TurnEdit, MetadataEdit, Group); the last 200 are kept. |
Clipboard, CopyBox, TurnedQuarter, MirroredX, PasteBox | Clipboard.hpp | Copy a box with its blocks, turns, objects and BigDecor; turn it a quarter or mirror it on X; stamp it back as one Group command. Hint zones and the spawn point are not carried. |
BoxRegion, NormalizeAndClamp | BoxRegion.hpp | An inclusive cuboid from two corner cells, clamped to the world: box fill, copy and Shift-extended selections. |
EditorPalette, EditorPaletteLayout, EditorPaletteInput, EditorPaletteRenderer | EditorPalette*.hpp | What is selected (block, object, Blupi start or BigDecor), the on-screen actions (Stop, play-test, X/Y/Z offsets, PLACE, delete), layout rectangles, hit-testing and drawing. |
PaletteCategories | PaletteCategories.hpp | Ten groups in Eggbert 2's menu order (Scenery, Technical blocks, Rock and terrain, Buildings, Hazards, Moving mechanisms, Treasures, Keys and progression, Vehicles, Characters and goals) plus Galaxy's Background group (32 sky regions, then music: none and tracks 1-10). |
Raycast | VoxelRaycast.hpp | Amanatides-Woo voxel DDA in raw grid space to the first non-air cell. On an empty world the aim falls on y = 0. |
AutoTile (namespace) | AutoTile.hpp | The original editor's auto-tiling (Decor::AdaptBorder) in 3D: a placed or emptied cell and its six neighbours are re-tiled (rock, girders, grass, bars, brick, slime, palace, pipes). |
VariantFamilies, BigDecorVariantFamilies, VariantCycler | EditorVariants.hpp | The original's variant families: a click on the block the previous click placed cycles it; Ctrl places without cycling. |
EditorDeviceControls | EditorDeviceControls.hpp | Keyboard-free front end (touch, gamepad, a Tools menu). It turns those devices into the same keys, mouse and EditorNavigation the editor reads. Owned by the host. |
EditorCommandKeys | EditorCommandKeys.hpp | Queues short V, O, Delete and Backspace presses between ticks so each one is a separate edit. Owned by the host. |
EditorHighlightRenderer, ObjectPathRenderer | EditorHighlightRenderer.hpp, ObjectPathRenderer.hpp | The translucent cell, box and selection overlays; the numbered 1 and 2 cubes at a moving object's route ends (ObjectPathMarkers.hpp). |
EditorBrowserScreen, GalaxyBrowserScreen | EditorBrowserScreen.hpp, GalaxyBrowserScreen.hpp | The world screen (slots, older and shared worlds, Backups, Export, Import, Galaxy) and the galaxy screen (main hub, bonus world, 14 worlds with 8 missions each, naming, export, import, delete). |
| Storage functions | CustomWorldStorage.hpp | Slot paths, backups, thumbnails, seeding, the galaxy's files: see CustomWorldStorage. |
Uses also: World and MoveObjectRecord (the data it edits), GetObjectDefinition (exit and motion capabilities), WorldRuntime::kWorldCenterX/Z (raw grid to render space).
Used by: GalaxyEggbertGame (member worldEditor_) and the editor verifiers in tools/.
Key members
A selection of the public API of WorldEditor.hpp, as of 2026-11-10. XNA and cna-engine namespaces are left out of the signatures for space.
| Member | Signature | What it does |
|---|---|---|
EnterBrowser | void EnterBrowser(int gamerSlot) | Starts browsing for player 0-2: seeds slot 1 from the showcase world and a free slot from the gallery world, rescans both screens. |
ShowGalaxy, ShowSlots | void ShowGalaxy(), void ShowSlots() noexcept | Switch the browser between the galaxy screen and the slots. |
IsBrowsing | bool IsBrowsing() const noexcept | Browsing (no world loaded) versus editing. |
UpdateBrowsing | BrowserRequest UpdateBrowsing(const MouseState& mouse, int viewportWidth, int viewportHeight, bool backPressed = false) | Drives the active screen. Returns a BrowserRequest: shouldOpen/openPath, shouldCreateNew/createPath, galaxyMission, galaxyImported, shouldReturnToMenu. The host does the loading. |
ExitBrowser, EnterEditing | void ExitBrowser() noexcept, void EnterEditing(float startX, float startY, float startZ) noexcept | Leave browsing; reset the camera (render space) and the session state, including the undo history. |
SetWorldPath, GetWorldPath | void SetWorldPath(std::filesystem::path path) noexcept, const std::filesystem::path& GetWorldPath() const noexcept | The file that Enter saves and the dice play-tests. |
Update | void Update(const KeyboardState& keyboard, const MouseState& mouse, float dt, int viewportWidth, int viewportHeight, CnaEngine::Graphics::Camera& camera, Worlds::World& world, const Navigation& navigation = {}) | One editing frame: camera, palette, aim, then the first handler that takes the input (extended selection, clipboard, placement, removal, save/undo/stop, box fill, background, music, turn, variant, object editing). |
ConsumeNeedsPresentationRebuild | bool ConsumeNeedsPresentationRebuild() noexcept | True once after an edit changed the world; the host rebuilds the presentation. |
ConsumePlayTestRequested | bool ConsumePlayTestRequested() noexcept | True once after the dice saved the world, but only after the save's thumbnail was taken. |
ConsumeThumbnailRequest | std::filesystem::path ConsumeThumbnailRequest() noexcept | The PNG the host writes the editor's view to after drawing this frame; empty when none. |
BeginOutsideSession, EndOutsideSession | void BeginOutsideSession(std::filesystem::path backupDir, std::filesystem::path keepCopyIn = {}), void EndOutsideSession() noexcept | A session on a world opened from play (the xnjttjpo cheat): backups go to backupDir, each save is also copied into keepCopyIn, no thumbnail. |
ConsumeLeaveRequested | bool ConsumeLeaveRequested() noexcept | Stop in an outside session: the host returns to the mission. |
CurrentEditTarget | std::optional<EditTarget> CurrentEditTarget(const Worlds::World& world) const | What Turn, Delete and the red selection tint act on: a block, object, BigDecor or Blupi start (with its colour) at a cell. |
CurrentClipboard, IsPasting | const Clipboard* CurrentClipboard() const noexcept, bool IsPasting() const noexcept | The copy made with C (kept across worlds of one run) and whether pasting is on. |
DrawWorldOverlays, Draw | void DrawWorldOverlays(GraphicsDevice&, const Camera&, const Worlds::World&), void Draw(GraphicsDevice&, const Camera&, int viewportWidth, int viewportHeight, const Worlds::World&) | Depth-tested overlays drawn with the world (route cubes, aimed cell or box, selection); then the status lines, palette and axis names over the finished frame. |
LiftPreset (free function) | std::optional<LiftTiming> LiftPreset(Def::ObjectType type, int buttonIcon) noexcept | A lift's advance, recede and rest ticks (20 Hz) by its palette entry, from the PC editor: slow 20/20/20, normal 10/10/10, fast 6/6/3, fast/slow 5/22/2. |
How it is used
GalaxyEggbertGame enters the Editor game phase in three ways:
- The main menu's editor button:
EnterBrowser(selected gamer),ShowSlots(). - The main menu's Galaxy button when the player's galaxy has no main hub yet (
StartGalaxy()), or the world screen's Galaxy button: the galaxy screen. - The
xnjttjpocheat typed in Play (OpenMissionInEditor()): for a campaign worldBeginOutsideSession(CampaignBackupsDir(), CampaignEditsDir())after keeping the shipped original; for a galaxy world backups stay beside the file. Stop returns to the mission, which restarts from its beginning with what was saved.
Each tick in the Editor phase the host either drives the browser and acts on its request, or runs an editing frame and acts on the editor's one-shot signals (from src/GalaxyEggbertCNA/GalaxyEggbertGameUpdate.cpp, shortened):
if (worldEditor_.IsBrowsing())
{
const auto request = worldEditor_.UpdateBrowsing(
mouse, viewport.getWidthProperty(), viewport.getHeightProperty(),
phaseKeys.IsKeyDown(Keys::Escape));
...
else if (request.shouldOpen)
{
LoadCustomWorldForEditing(request.openPath);
}
...
}
else
{
worldEditor_.Update(phaseKeys, mouse, dt,
viewport.getWidthProperty(), viewport.getHeightProperty(), camera_,
worldRuntime_.GetWorldMutable(), editorNavigation);
...
if (worldEditor_.ConsumeNeedsPresentationRebuild())
{
RebuildWorldPresentation();
}
InteractionSystem::AdvanceEditorPlatforms(worldRuntime_, dt);
if (worldEditor_.ConsumePlayTestRequested())
{
LoadCustomWorldForPlayTest(worldEditor_.GetWorldPath());
}
else if (worldEditor_.ConsumeLeaveRequested() && editorMission_ != 0)
{
LeaveMissionEditor();
}
}
A new world is created by the host (CreateEditorStarterWorld(), or CreateGalaxyStarterWorld(mission) for a galaxy place), saved, then opened like any other. While editing, the world clock still runs, so lifts preview their patrol. A play-test loads the saved file with a fresh InteractionSystem and BlupiController; Pause's Menu or Back, or the return button on the Win/Lost screen, reopens the same world in the editor. Before an editing frame the keyboard, mouse, touches and gamepad pass through EditorDeviceControls and EditorCommandKeys.
Controls
| Device | Bindings (from WorldEditorUpdate.cpp and EditorDeviceControls.cpp) |
|---|---|
| Keyboard and mouse | W/A/S/D fly, Ctrl up, Z down (Shift alone also lowers); right button looks, wheel zooms. Left click or Space places at the preview; middle click or Backspace removes. NumPad 4/6, 8/2 and 7/9 move the preview on X, Z and Y, NumPad 5 resets it; Shift with them extends a 3D region that Place fills. Enter saves, U undo, R redo, F box fill (Escape cancels), C copy, P paste, Q turn the copy, X mirror it, Ctrl with a click places without cycling. Left/Right arrows step the background, M the music, O turns, V cycles the variant, H shows the keys. G, T, Tab, + and - pick an object, set its route end and adjust its ticks; Delete removes it. F10 or the Stop sign stops. |
| Gamepad | Left stick flies, right stick looks, RT/LT change height, A places, X removes, B cancels, D-pad and shoulder buttons move the preview, Y switches to a menu cursor, Start saves, Back stops. |
| Touch | On-screen Move and Up/Down controls and a Tools menu (Save, Undo, Redo, Select 3D, Remove, Turn, Variant, Copy, Paste, Box fill, Music, Help, Stop and the object commands); dragging over the world turns the view, a tap aims and places. |
Rules and invariants
- No new mechanics. The editor only places what the game already plays. Palette entries without a verified Galaxy implementation keep their Eggbert 2 position and show "Not yet implemented."
- Every change is a command. Each mutation is pushed onto
EditCommandStackas one command per user action (a box fill or a paste is one);MarkMutated()sets the rebuild and unsaved flags together. - The host owns loading.
WorldEditornever loads or creates a world itself; it raises requests (BrowserRequest,Consume*()) that the host consumes, the same pending-signal idiom as InteractionSystem. - Unsaved work is protected. Stop with unsaved changes needs a second press. The first save after a world was opened backs up the previous file.
- One exit per world. Placing a red exit arrow or a final golden key, directly or in a paste, replaces the previous one (the original editor's
PlaceDecor10), in one undoable step. - Coordinates. The world and its records use raw grid cells (0-99). Drawing uses render space, raw minus
WorldRuntime::kWorldCenterX/Z. The camera's yaw followsBlupiController: yaw 0 faces -Z.
Tests
Five verifiers drive the real editor without a window. They share one object library (GEEditorVerifySupport in CMakeLists.txt), are registered by ge_add_editor_verify and run in the build directory's verify-run/, because they write customworlds/ relative to their working directory.
| Test | Covers |
|---|---|
VerifyGEWorldEditor | Placement, auto-tiling, variant cycling, turning, lift ends, copy/turn/mirror/paste, music, a mixed undo/redo history, outside (xnjttjpo) sessions, cursor aiming, the single-exit rule. |
VerifyGEEditorDevices | EditorDeviceControls: gamepad and touch input mapped to navigation and keys. |
VerifyGEEditorPalette | Categories in source order, layout, hit-testing, placeable blocks and objects, BigDecor mode. |
VerifyGEEditorStorage | Slots, seeding, backups, export and import, the browser screens, the galaxy (see CustomWorldStorage). |
VerifyGEEditorObjects | Object placement, removal and timing edits. |
For a look at the real thing, tools/qa_live.sh runs the step files in tools/qa_steps/editor_*.txt (play-test, copy and paste, lifts, backups, thumbnails, auto-tiling, cursor aiming). See Testing.
Related pages
- 3D World Editor: the editor from the player's side
- Your Own Galaxy
- CustomWorldStorage: where the edited worlds are kept
- GalaxyEggbertGame: the host and its
Editorphase - World, MoveObjectRecord, BlockDefinitionRegistry, ObjectDefinitionRegistry
- Modifying levels
- Cheats & Diagnostics:
xnjttjpo