WorldRuntime

The world as it is being played: the voxel grid, the live object list, BigDecor, spawn point and the animation clock.

Location

Headersrc/GalaxyEggbert/Game/WorldRuntime.hpp
Sourcessrc/GalaxyEggbert/Game/WorldRuntime.cpp
NamespaceGalaxyEggbert::Game
Engine dependencyNone. Plain C++ over the engine-independent world model; it is linked into command-line verifiers.

Older pages called this class GEWorldRuntime; the GE prefixes were dropped when the game code moved to src/GalaxyEggbert/Game/.

Purpose

WorldRuntime turns a world file into the state the game plays on. It owns a World (the 100 × 100 × 100 voxel grid) and derives from it everything that changes during play or that other systems look up every tick: the list of moving objects (MobileObjSpec), the BigDecor scenery and its solid boxes, the training-hint zones, the spawn point, the sky region and the mission number. It also keeps the world's animation clock, which advances at the original game's 20 Hz reference rate, and offers a few pure helpers that encode the original's timing and mission arithmetic.

Collaborators

Uses

Used by

Key members

A selection of the public API of WorldRuntime.hpp (verified 2026-11-10):

MemberSignatureWhat it does
kWorldCenterX, kWorldCenterZstatic constexpr int = 50Offset between raw grid cells and centred world coordinates (see below).
LoadFromVwrFilebool LoadFromVwrFile(const std::string& path)Loads a .vwr world. On failure returns false.
LoadFromVwrStreambool LoadFromVwrStream(std::istream& in)The same from bytes already in memory, so mission loading and resume/demo validation read one identical snapshot. Resets the animation clock, then calls ResyncFromWorld().
LoadFromMobileEggbertFilebool LoadFromMobileEggbertFile(const std::string& path)Reads an original 2D .txt world; kept as a reference path for verifiers, not used by the game.
ResyncFromWorldvoid ResyncFromWorld()Re-derives sky region, mission number, spawn, hint zones, BigDecor cells and the object list from the current World, without disk I/O. Used after editor changes.
Updatevoid Update(float dt)Advances the 20 Hz animation tick and every object's phase.
GetWorld, GetWorldMutableconst Worlds::World& GetWorld() const
Worlds::World& GetWorldMutable()
The voxel grid; mutable access for tile reactions, the editor and tests.
GetMobileObjects, GetMobileObjectsMutableconst std::vector<MobileObjSpec>& GetMobileObjects() constThe live objects (lifts, enemies, pickups, crates, effects).
GetAnimPhaseint GetAnimPhase() constThe raw 20 Hz tick count; renderers divide it by each tile's own divisor.
GetSpawnTileX/Y/Z, GetSpawnRenderX/Y/Z, GetSpawnYaw, HasExplicitSpawnPointint / float / bool gettersSpawn in raw cells and in centred coordinates; the yaw of the yellow player start.
GetSkyRegion, GetMissionNumber, OverrideMissionNumberint ...() const, void OverrideMissionNumber(int) noexceptHeader fields of the world; a galaxy world is played as the mission of its place in the galaxy.
GetBigDecorCells, SolidBigDecorBoxes, SetBigDecorShapesconst std::vector<BigDecorSpec>& ..., std::vector<SolidBox> ... constSparse BigDecor scenery and, for the solid models, collision boxes for Blupi and crates (shapes measured by the host from the models).
GetHintZonesconst std::vector<HintZoneRecord>& GetHintZones() constTraining-hint zones in record order.
TryActivateSwitchstd::optional<bool> TryActivateSwitch(float, float, float, bool blupiOnGround)Toggles the switch under Blupi and every saw within X ±20 on the same row; returns the new state so the caller plays the sound.
TryConsumeFan, LastConsumedFanstd::optional<std::uint16_t> TryConsumeFan(float, float, float)Touching a fan's rotor removes the fan and its wind column; the caller applies death or immunity.
FindTeleportDestinationbool FindTeleportDestination(std::uint16_t icon, float, float, float, float&, float&, float&) constThe other teleporter with the same icon (330-333); Blupi lands one cell below it.
IsBlitzActiveAtPhase, IsCrusherActiveAtPhase, IsTempPassableAtPhasestatic bool ...(int animPhase) noexceptThe original's hazard cycles as pure functions of the phase.
ComputeWorldSelectTarget, ComputeMissionBack, ComputeWinExitTargetstatic int ...(int currentMission, ...) noexceptMission arithmetic of signs, Back and the exit (see below).

MobileObjSpec

One moving or interactive object, in plain floats. The fields that matter most:

FieldMeaning
type, visualIconDef::ObjectType; an optional tile override (0 keeps the type's own icon).
posStartX/Y/Z, posEndX/Y/ZThe path's two ends (centred X/Z; Y is the centre of the occupied cell, so an object on a floor at Y=0 has Y=1).
currentX/Y/Z, activeLive position (what is drawn) and whether the object still exists (false once collected).
phasePer-object animation tick at 20 Hz, read by GetObjIcon.
stepAdvanceTicks, stepRecedeTicks, timeStopStartTicks, timeStopEndTicks, patrolStep, patrolTimeThe original's patrol state machine in 20 Hz ticks: dwell at start, advance, dwell at end, recede. Lifts follow it too; speed no longer moves anything.
firedByBlupiA glue ball Blupi fired himself never hits him.

Coordinates and the world centre

Files, editor and tools use raw grid cells 0-99. The simulation and the renderer use centred coordinates: X and Z minus kWorldCenterX/kWorldCenterZ (50), Y unchanged, so the world sits around the origin. ResyncFromWorld() applies the shift when it builds MobileObjSpecs and BigDecor cells, and the spawn is offered both ways (GetSpawnTileX raw, GetSpawnRenderX centred). Blupi's position in BlupiController is centred too; crate cells and solid boxes handed to it are raw.

Animation phase

Update(dt) accumulates time and advances animPhase_ once per 1/20 s, the original's reference tick; each object's phase advances by dt × 20. Animated tiles divide this tick by their own divisor (from BlockDefinitionRegistry) before choosing a frame. The hazard helpers read the same tick:

bool WorldRuntime::IsBlitzActiveAtPhase(int animPhase) noexcept
{
    const int cycle = ((animPhase % 100) + 100) % 100; // defensive: handle a negative phase
    return cycle % 2 == 0 && cycle < 50;
}

Lightning is lethal on even ticks of the first half of a 100-tick cycle; a crusher when (phase/3)%10 <= 2; a vanishing (Temp) tile is passable when (phase/4)%20 >= 18. The host passes the Temp result to BlupiController::Step as a plain bool.

How it is used

The host loads each mission from bytes it has already read, then re-applies the galaxy mission number when needed:

std::istringstream world(bytes ? *bytes : std::string{}, std::ios::in | std::ios::binary);
if (!bytes || !worldRuntime_.LoadFromVwrStream(world))
{
    ...
    return;
}
loadedMissionPath_ = path;
loadedMissionHash_ = ResumeLog::HashBytes(*bytes);
if (galaxyGamer_ >= 0) worldRuntime_.OverrideMissionNumber(missionNumber); // its place in the galaxy

Every Play tick starts with worldRuntime_.Update(simDt); InteractionSystem::Update then works on GetMobileObjectsMutable().

Mission arithmetic: from the main hub (mission 1) sign N leads to world N×10 (sign 13 to the showcase, world 999, except in a player's galaxy); inside a world hub sign N leads to mission hub+N and sign 199 back to the main hub. ComputeMissionBack returns to the mission's hub, or from a hub to the main hub. ComputeWinExitTarget(1) is 199 (the bonus world) and ComputeWinExitTarget(199) is 1.

Rules and invariants

Tests

Related pages