MoveObjectRecord

How a world file stores its objects: one 47-byte record per pickup, enemy, lift or crate, kept as sparse metadata type 1 on the object's start cell.

Location

Headerinclude/GalaxyEggbert/MoveObjectRecord.hpp
Sourcesrc/GalaxyEggbert/MoveObjectRecord.cpp
NamespaceGalaxyEggbert
Object typesGalaxyEggbert::Def::ObjectType (include/GalaxyEggbert/Def/ObjectType.hpp), the original game's numbering

Purpose

Everything in a level that moves or can be taken (the original game's "MoveObjects": treasure chests, eggs, keys, lifts, crates, enemies, vehicles, the exit...) is not a voxel. It is stored in the .vwr file as a sparse extra-metadata record of type kMoveObjectMetadataType = 1, anchored at the cell floor(posStartX/Y/Z). The anchor only buckets the record into a chunk; the exact float positions are inside the payload. MoveObjectRecord is the plain struct, and three free functions encode, collect and remove it.

Coordinates are raw grid space (0-99), like every other World coordinate. Y is the centre of the occupied cell: an object standing on a floor block at Y = 0 has Y = 1. WorldRuntime converts records to its live MobileObjSpec list and applies the −50 render shift on X and Z; see Object Lifecycle.

Public API

Verified in MoveObjectRecord.hpp as of 2026-11-10.

Field / functionDescription
Def::ObjectType typeWhat the object is (default ObjectType0). Stored as one byte.
std::uint16_t visualIconOptional object-m.png tile override for variants whose look the type alone cannot give (for example the secret wooden case camouflaged as terrain). 0 = derive the icon from type and animation phase.
posStartX/Y/Z, posEndX/Y/ZPatrol end points. posEnd == posStart means the object does not move.
float speed = 1.5fKept for compatibility; patrols are timed by the tick fields below.
stepAdvanceTicks = 60, stepRecedeTicks = 60, timeStopStartTicks = 40, timeStopEndTicks = 40The original's four-phase patrol: dwell at start, advance, dwell at end, recede, in 20 Hz ticks. The defaults are placeholders for hand-made worlds, not values from the original.
constexpr std::uint16_t kMoveObjectMetadataType = 1The metadata type of every object record.
void PlaceMoveObject(Worlds::World&, const MoveObjectRecord&)Encodes and stores the record at its anchor cell. A second object on the same anchor cell replaces the first. Throws std::out_of_range if the anchor is outside the world.
std::vector<MoveObjectRecord> CollectMoveObjects(const Worlds::World&)Decodes every object in the world. A payload of any length other than 45 or 47 bytes throws std::runtime_error.
bool RemoveMoveObject(Worlds::World&, x, y, z)Removes the object anchored at that cell (the editor's delete).

Payload layout

Little-endian, fixed size:

OffsetSizeField
01type
112posStartX, posStartY, posStartZ (float32)
1312posEndX, posEndY, posEndZ (float32)
254speed
2916stepAdvanceTicks, stepRecedeTicks, timeStopStartTicks, timeStopEndTicks
452visualIcon (uint16)

The older 45-byte form without visualIcon is still read, with the icon set to 0. Writers always produce 47 bytes.

Usage

From WorldRuntime::ResyncFromWorld() (src/GalaxyEggbert/Game/WorldRuntime.cpp), turning records into live objects:

mobileObjects_.clear();
for (const auto& record : CollectMoveObjects(*world_))
{
    if (!IsSupportedObjectType(record.type) ||
        GetObjectDefinition(record.type).placementKind == ObjectPlacementKind::Null)
        continue;
    MobileObjSpec spec;
    spec.type = record.type;
    spec.visualIcon = record.visualIcon;
    spec.posStartX = record.posStartX - static_cast<float>(kWorldCenterX);
    ...
    mobileObjects_.push_back(spec);
}

Writers: the in-game WorldEditor (object placement and removal), tools/GenerateSampleWorld3D.cpp, and the Python world builder tools/worldkit.py, which writes the same bytes. tools/vwr_inspect.py lists them.

Sibling codecs

Two smaller record types follow the same pattern (struct + place/collect functions over a World):

CodecTypePayloadNotes
BigDecorRecord
include/GalaxyEggbert/BigDecorRecord.hpp
32 bytes: the icon (uint16, non-zero)A large scenery picture from explo.png (palms, houses, machines...), Eggbert 2's separate BigDecor layer. Not a voxel. The anchor cell gives its position. PlaceBigDecor() throws std::invalid_argument for icon 0; CollectBigDecor(), RemoveBigDecor(). Some icons are solid in 3D (kSolidBigDecorIcons, BlockDefinitionRegistry).
HintZoneRecord
include/GalaxyEggbert/HintZoneRecord.hpp
44 bytes: recordIndex, sizeX, sizeY, sizeZWhere one of a training mission's hints applies: a box from the anchor (its minimum corner) spanning size cells on each axis. Contains(x, y, z); PlaceHintZone() rejects zero sizes and boxes leaving the world; CollectHintZones() returns them sorted by recordIndex. Added 2026-10-25.

Tests

Related pages