Object Lifecycle

A MoveObject's path: world record → MobileObjSpec → InteractionSystem → drawn as a model or a billboard.

"Objects" are everything that is not terrain: treasure chests, eggs, keys, the exit, lifts, crates, enemies, vehicles, power-ups, shots and short-lived effects. They use the original game's ObjectType numbers (include/GalaxyEggbert/Def/ObjectType.hpp, 0-203), and what each type can do is declared in the ObjectDefinitionRegistry. Described as of 2026-11-10.

1. Stored: a type 1 record in the world

In a .vwr file an object is a 47-byte MoveObjectRecord stored as sparse metadata type 1 on the cell under its start position: type, start and end points in raw grid floats, four patrol timings in 20 Hz ticks, and an optional icon override. The WorldEditor and tools/worldkit.py write them.

2. Loaded: MobileObjSpec in WorldRuntime

WorldRuntime::ResyncFromWorld() (run by every load and after editor changes) calls CollectMoveObjects() and builds the live list returned by GetMobileObjects() / GetMobileObjectsMutable():

MobileObjSpec fieldRole
type, visualIconWhat it is; the optional icon override.
posStart*, posEnd*The path's ends (render space). Never changed by play.
currentX/Y/ZWhere it is now: patrols, pushed crates and homing followers move this.
activeFalse once taken, destroyed or expired. The record stays in the list; some spawns reuse an inactive slot.
phaseIts own animation clock, in 20 Hz ticks.
stepAdvanceTicks, stepRecedeTicks, timeStopStartTicks, timeStopEndTicks, patrolStep, patrolTimeThe patrol state machine (below).
firedByBlupiA glue ball Blupi fired himself never hits him.

WorldRuntime::Update(dt) advances the world's 20 Hz animation tick and every object's phase by dt × 20.

3. Simulated: InteractionSystem::Update()

Once per tick in Play, after BlupiController::Step() has given Blupi his final position, the host calls interaction_.Update(dt, worldRuntime_, blupiX, blupiY, blupiZ, ...) with many flags describing Blupi's state (crouching, ballooned, invincible, in a vehicle, hidden, focused, ghost...). InteractionSystem (src/GalaxyEggbert/Game/InteractionSystem.{hpp,cpp}) has no access to the controller, camera or graphics: it changes objects and world blocks, plays sounds, and reports events (EventsThisFrame()) that the host applies to Blupi, the camera and the HUD.

Patrols and lifts

Patrolling types (registry flag patrolMotion: 2, 3, 4, 20, 32, 33, 44, 54) and lifts (1, 47, 48) follow the original's four-phase cycle in AdvancePatrolStep(): 1 rest at start, 2 advance, 3 rest at end, 4 recede. A lift carries Blupi: the system reports IsRidingLift(), RideDeltaX/Z() and RideStandY(), and the host calls BlupiController::RideLift(); the conveyor lifts 47/48 add their nudge. In the editor, AdvanceEditorPlatforms() previews the same motion without contacts.

Crates

Crates (type 12) are pushed and pulled by UpdateCratePush() (rules in CratePush.hpp). Every tick the host hands their cells to the controller (SetCrateCells()), so they are solid for Blupi (see Collision).

Contact with enemies and hazards

A kill does not respawn Blupi directly: the system raises DeathLockRequested, the host starts the death animation lock, and the life is resolved when it ends (section 4).

Pickups

standardPickupTouch types (treasure, egg, keys, power-ups, the exit...) are taken on contact. As in the original the object disappears at once but the reward is applied when its little "voyage" to the HUD ends (BeginVoyage()). Some pickups need the action button (lollipop, drink); some are reusable and come back where they were (RespawnPickupItem()). Types 7 and 21 are level exits; ExitReached() starts the win cheer.

Shots and dynamite

Retirement and effects

Destroyed objects go through Retire(): active = false, and some types (bulldozer, crate, spider, fish, bird, invert potion, followers) fly away as rotating debris (ByeByePieces()). Explosions, splashes, sparkles and similar effects are themselves short-lived objects appended to the same list.

4. What resets objects

EventObjectsBlupi
Death with lives leftUnchanged: what was taken stays taken, what was destroyed stays gone, patrols continue.After the death lock he reappears at his last safe position (GetValidX/Y/Z(), settled on the ground), and a life flies from the HUD to him.
Last life lost (Lost screen)Unchanged. As in the original (DoorsLost()), the level is not reloaded; treasure and keys found are kept and lives are reset to 3.Return puts him back at the world's start.
LoadMission() (new mission, re-entry, Continue, "Original")Rebuilt from the world's records; a fresh InteractionSystem (only lives carried over).A fresh controller at the start or hub arrival.
Editor changeResyncFromWorld() rebuilds the list from the edited world.Unchanged.

5. Drawn: model or billboard

The host turns the simulation into a draw list each frame (src/GalaxyEggbertCNA/GalaxyEggbertGameScene.cpp fills a SceneFrame, which WorldRenderer draws). For every active object:

  1. ResolveObjectVisual(type, phase, visualIcon) gives the render mode (Hidden, Billboard, SolidCube), the sprite sheet (element.png, object-m.png, explo.png, blupi.png, blupi1.png) and the icon for the current animation phase (GetObjIcon() in Game/ObjectIcons.cpp). Patrolling enemies take their per-direction icon from their patrol state.
  2. ObjectModelTable::Find(sheet, icon) looks the icon up in the table read from avatars3d/objects/*/icons.txt. If found, the object is drawn as that 3D model, posed at the listed time of its "Frames" clip, mirrored and turned as listed, and turned further by the cell's object-turn record (metadata type 5).
  3. Otherwise the icon is drawn as a camera-facing billboard cut from its sheet. The game keeps choosing icons exactly as the original; the model table only decides how an icon is shown.

Lifts, conveyor lifts, parked vehicles and the clay bridge have dedicated model paths; Blupi himself uses his skinned model (Rendering).

Tests

Related pages