BlupiController
Blupi's body: movement, collision, jumping, swimming, vehicles, powers and the animation state that picks his clip.
Location
| Header | src/GalaxyEggbert/Game/BlupiController.hpp |
|---|---|
| Sources | src/GalaxyEggbert/Game/BlupiController.cpp |
| Namespace | GalaxyEggbert::Game |
| Engine dependency | None. It sees only a Worlds::World, SolidBoxes and plain floats and booleans. |
Older pages called this class GEBlupiController and described an invisible collision point. Since then Blupi is drawn
as a 3D model converted from the original Ray Dream scenes; the controller decides which clip that model plays. (The class comment
at the top of the header still describes the early placeholder; the code is current.)
Purpose
BlupiController reproduces the original game's movement rules in 3D. Each call of Step() turns one tick of
steering input into a new position and status: tank-style turning (135 °/s) and walking (2.5 cells/s once under way, after
the original's four-tick ease-in), the original's jump speeds and gravity, ledges and walls, crouching and looking up, swimming at the
surface and under water, hanging on bars, being squashed or ballooned, riding lifts and crates, six vehicle modes, secret powers, and
the one-shot actions (switch, dynamite, pickups, death). It also keeps an AnimState from which the host chooses Blupi's
model clip, and it reports one-shot cues (head bump, bar grab, turn, idle fidget) as events for the host to play as sounds.
Speeds are written in the original's units: 64 pixels per block and 20 ticks per second, so 8 px/tick becomes
8 × 20 / 64 = 2.5 blocks per second.
Collaborators
Uses
- World and BlockDefinitionRegistry (collision masks, water, springs, bars, hazards).
SolidBox.hpp(solid BigDecor),BarRows.hpp,LavaHeight.hpp,FanGeometry.hpp,BlockModels.hpp,PlateRotationMetadata.hpp.- WorldRuntime's centre constants (
kWorldCenterX/Z) only.
Used by
- GalaxyEggbertGame: owns one
blupi_, feeds it input, the terrain phase, crate cells and lift state every tick, and applies InteractionSystem events to it. VerifyBlupiMovementdrives it with scripted input;WorldRuntimereads its body constants for fan contact.
InteractionSystem does not use it: the two meet only in the host.
Key members
The header is about 1 800 lines, most of it constants with the original's values and their sources. A selection of the
public API of BlupiController.hpp (verified 2026-11-10):
| Member | Signature | What it does |
|---|---|---|
Step | void Step(const Worlds::World& world, float turnInput, float moveInput, bool jumpPressed, bool crouchHeld, bool lookUpHeld, float dt, bool tempPassable = false, bool inSurfWater = false, bool inDeepWater = false, bool pushingCrate = false, bool holdingCrate = false) | One tick of movement and collision. Turn and move inputs are −1/0/+1; terrain facts (vanished Temp tiles, water) are computed by the caller and passed in. |
SetPosition | void SetPosition(float x, float y, float z) noexcept | Moves Blupi; he stays airborne (teleports and other callers). |
SettleOnGround | void SettleOnGround(const Worlds::World& world, bool tempPassable = false) | After a spawn or respawn, establishes standing before the first input tick (the original's PlayPrepare), so walking starts at once; a start in open air still falls. |
GetX/GetY/GetZ, GetYaw, SetYaw, GetVelocityY, IsOnGround | getters, void SetYaw(float) noexcept | Position (centred X/Z, feet Y), heading (0 faces −Z), vertical speed, ground contact. |
GetAnimState | AnimState GetAnimState() const noexcept | Which of the original's actions Blupi is in (see below). |
GetAnimIcon, GetDisplayAnimIcon | int ...() const noexcept | The original sprite icon for the current frame (sprite fallback, first-person indicator). |
EventsThisFrame | const std::vector<Event>& EventsThisFrame() const noexcept | Sound cues of this tick (HeadBumped, BarGrabbed, TurnStarted, IdleFidget, CrateGripped, ...). |
GetVehicleMode, IsInVehicle, TriggerMount, TriggerDismount, CanLeaveVehicle | bool TriggerMount(VehicleMode mode, bool inNage, bool inSurf) noexcept | Vehicles: None, Helicopter, Jeep, Tank, Skateboard, Overcraft, each with the original's speeds. |
IsSurf, IsNage, GetWaterGaugeLevel, JustDrowned | bool ...() const noexcept | Floating at the surface, swimming under water, the air gauge (100, red below 25). |
TriggerCrush, TriggerBalloon, PopBalloon | bool ...() noexcept | Squashed by a crusher; carried by a wasp's balloon. |
TriggerShield, TriggerPower, TriggerCloud, TriggerHide, TriggerInvert | bool ...() noexcept | Secret powers and the inverted controls, each with the original's gate; true when granted. |
TriggerTeleport, TriggerBye, TriggerSpringBounce, TriggerOneShotAnim | bool TriggerTeleport(std::uint16_t icon) noexcept, ... | Teleporter transit, the exit wave, spring bounce, one-shot actions such as FireTank. |
TriggerDeathLock, ConsumeDeathLockResolved | bool TriggerDeathLock(DeathCause cause, bool shouldRespawn) noexcept | Freezes Blupi for a death (Clear1-4, Glu, Drown); the host respawns when it resolves. |
SetCrateCells, SetSolidBoxes, FollowCrate | void SetCrateCells(std::vector<std::array<float, 3>> cells), ... | Crates and solid BigDecor count as solid for him; the caller updates them before each Step(). |
SetRidingLift, SetLiftTops, SetLiftUnder, SetTerrainAnimationPhase | void ...(...) noexcept | Per-tick facts from the lifts and the terrain clock. |
IsClearAt, CanStandAt | static bool ...(const Worlds::World&, float x, float y, float z, ...) | Collision queries shared with the crate code. |
CheatVehicle, DisableGhost, SetCheatSuperBlupi | void CheatVehicle(VehicleMode mode, const Worlds::World* world = nullptr) noexcept, ... | Entry points for the typed cheats (vehicles, ghost, megablupi). |
| constants | kMoveSpeed = 2.5f, kTurnSpeed = 0.75π, kJumpSpeed = 12.0f, kGravity = 25.0f, kStepLimit = 4/64, kHeadHeight = 49/64, kBodyHalfWidth = 18/64 | Blocks per second, radians per second, block units. |
AnimState
An enum class AnimState : std::uint8_t with one value per live action of the original's table_blupi:
Stop, March, Jump, Air, Down, Up; the squashed and balloon states; each
vehicle's stop/march pair; StopNage/MarchNage and StopSurf/MarchSurf for swimming;
StopSuspend, MarchSuspend, JumpSuspend on bars; actions such as Push, Switch,
TakeDynamite, PutDynamite, FireTank, HelicoGlu; the mockery and reaction poses
(Mockery, JumpAie, Ouf1a ... Ouf5, Vertigo, Recede, Advance);
Win, Turn, Bye, Teleporting, DeathLocked, PickupBusy, and the short
transitions StopMarch and StopJump. The Blupi model in avatars3d/blupi/ has a clip for each state.
How it is used
The host prepares the tick's facts, then steps him once per tick with the scaled simulation time:
const float stepStartFeetY = blupi_.GetY();
...
blupi_.SetTerrainAnimationPhase(worldRuntime_.GetAnimPhase());
blupi_.Step(worldRuntime_.GetWorld(), steering, moveInput, jumpPressed,
crouchHeld, lookUpHeld, simDt, tempPassable, inSurfWater, inDeepWater,
wasPushingCrate_, holdingCrate_);
Afterwards it reads EventsThisFrame() for sounds, compares before/after state (landing, water entry, teleport end)
and runs the actions. Mission starts go through PlaceBlupi, which sets the crate cells and calls
SettleOnGround.
Movement facts worth knowing
- No step-up. As in the original's
Decor::TestPath, a one-block step stops Blupi until he jumps.kStepLimitis a 4-pixel tolerance so seams between almost equal heights do not snag him (it was a whole block until 2026-10-25). - Swimming. In water there is no gravity: Jump or Right Shift swims up, Left Shift down. At the surface he floats until he dives; Jump with open air above makes the weak jump out onto a bank. The air gauge drains only under water.
- Vehicles have their own maximum speeds, acceleration and gravity: the jeep and tank fall heavier than Blupi,
and the jeep with ground under one end only rolls off the edge nose first, tipping up to 45°; the helicopter sinks slowly when idle; the overcraft climbs only with ground within 80 original
pixels below it. Water, a spring or the helicopter hitting a ceiling wrecks the vehicle (applied by the host's
WreckVehicle()). - Bars. On a hanging bar he moves along it, can jump off, and does not re-grab for a moment after letting go.
Rules and invariants
- Engine independence. No CNA, cna-engine, camera or input type appears in the header or source. Terrain facts
that need
WorldRuntime(the Temp phase, water at the feet) are passed in as booleans. This is what letsVerifyBlupiMovementrun without a window. - Deterministic. The same inputs and
dtgive the same path; the golden trace, autoplay scripts, the resume log and demos rely on it. - The original's numbers. Speeds, durations and gates come from the reference game and carry their sources in comments; changing one changes the timing of every playthrough script.
Tests
VerifyBlupiMovement(ctest, runs from the repository root): drivesStep()againstworlds3d/world999.vwrwith scripted input. It covers walking and the first-tick start afterSettleOnGround, jumps, gravity and falls, walls, crates and footprints as support, temporary platforms, swimming, vehicles (including penetration recovery for foot, jeep, tank and helicopter) and the one-shot sound cues.VerifyInteractionSystemalso creates controllers to check deaths and blasts end to end.- The golden trace (
tools/verify_golden_trace.sh) records position, vertical speed, ground contact and animation per tick for a fixed walk and jump; any change in movement shows up there.