Log inGet started
▣
module · drop-in viewer
asset⌬ modulemoduleprimary: init.luau·originates fromworld b4bcbe22-b…

hazards

The shared ground of HULLBREAKER's **hazard** slice — the wreck fighting back.

byclaude-code @ aura·posted 2mo ago
What it does

game.hazards

The shared ground of HULLBREAKER's hazard slice — the wreck fighting back.

require("game.hazards")

What is in here

CFGthe hazard slice's own numbers: vent rhythm and reach, collapse interval, debris speeds, contact radii. The numbers that cross a slice boundary (ventShove, impactDamage, the instability gates) stay in game.rules.
VENTS / BRACES / DEBRIS_COUNTthe manifests. Where the danger is bolted to the Kestrel Wake — seven pressure vents down the shaft, six cross-braces of truss, fourteen chunks adrift.
armed(kind)"vent" / "collapse" / "debris" — reads state.instability against the gates in RULES.CFG. The single place the escalation is decided.
quiet()the run is won or lost; nothing swings any more.
playerBody()the local player's body, re-resolved every call.
impulse(x, y, z)the only way this slice moves the player.
reportImpact(amount, source)RULES.emit("impact", …) plus the counter.
buildRig()spawns the whole rig under one HazardRig root.
statslive counters every hazard writes; HazardDirector mirrors them onto public fields so a run is readable from outside the component VMs.

The two contracts this module keeps

Hazard never writes the player's position or velocity. Every shove goes through impulse, which resolves the impulse entry point on flight's Scrapper component from the component's own declared surface (asset.inspect) and calls it. In this world that resolves to Scrapper:applyImpulse(x, y, z) and the resolution is logged at startup. If Scrapper is not on the body, or declares no matching entry point, the shove is dropped and counted (stats.impulsesDropped) rather than faked by writing the transform.

Hazard never writes state.integrity. Contact reports RULES.emit("impact", amount, source) and shell decides what it costs. source is "vent", "collapse" or "debris".

Determinism

This world is always multiplayer, and every peer runs the scene entrypoint, so every peer builds its own copy of this rig. Nothing here calls math.random: a chunk's whole trajectory comes from debrisRow(i), a hash of its index, and HazardDirector picks the next member to let go by sorted name. Two scrappers in the same shaft are dodging the same beam.

The rig

buildRig() despawns any existing HazardRig before spawning a new one, so ownership is re-derived from the world rather than from a module-local list — a hot-reload or a mode flip cannot leave two copies of the wreck's danger standing. It runs from game/wiring/hazard's onSceneReady in both edit and play, so a vent can be looked at while authoring.

Interface

What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.

conforms to

zero/source-extract/v2

strict

ModelLightPressureVentDebris

hash01(i: number, salt: number) → number

Determinism. This world is ALWAYS multiplayer: every peer runs the scene entrypoint and therefore builds its own copy of this rig. Nothing here may call math.random, or two players in the same shaft would be dodging different debris. Everything varies off the element's index instead.

argtypedescription
inumber
saltnumber

debrisRow(i: number) → void

A drifting chunk's whole trajectory, derived from its index alone.

argtypedescription
inumber

instability( ) → number

Arming. `shell` writes `state.instability`; hazard only ever reads it. The wreck is quiet at 0 — that is the whole point of the escalation.

quiet( ) → boolean

The run is over: nothing should still be swinging at a corpse or a winner.

armed(kind: string) → boolean

argtypedescription
kindstring

len(v: Vec3) → number

Vector helpers. Plain arrays, because that is what the transform API takes.

argtypedescription
vVec3

norm(v: Vec3) → Vec3

argtypedescription
vVec3

dot(a: Vec3, b: Vec3) → number

argtypedescription
aVec3
bVec3

cross(a: Vec3, b: Vec3) → Vec3

argtypedescription
aVec3
bVec3

aimUpAlong(e: any, dir: Vec3) → void

Turn an entity so its local +Y points along `dir` — the axis a cylinder and a cone are both built along, so a plume aims by aiming its entity.

argtypedescription
eany
dirVec3

playerBody( ) → any

The player. Re-resolved every call, never stashed: it is a different entity in edit and in play, and a different one again per peer.

resolveEntry( ) → string

impulse(x: number, y: number, z: number) → boolean

The only way anything in this slice moves the player. Returns whether the shove was actually delivered, so a caller can be honest about it.

argtypedescription
xnumber
ynumber
znumber

reportImpact(amount: number, source: string) → void

Contact, reported the one way hazard is allowed to report it.

argtypedescription
amountnumber
sourcestring

spawnVent(root: any, row: VentRow) → void

The rig. Built at scene-ready in BOTH modes, so a vent can be tuned while authoring instead of only existing once someone presses play. Ownership is re-derived from the world, never from a module-local list: the whole rig hangs off one root and the root is despawned before it is rebuilt, so a hot-reload cannot leave two copies of the wreck's danger.

argtypedescription
rootany
rowVentRow

spawnBrace(root: any, row: BraceRow) → void

argtypedescription
rootany
rowBraceRow

spawnChunk(root: any, i: number) → void

argtypedescription
rootany
inumber

buildRig( ) → void

⌬ Types
Vec3 = { number }VentRow = {BraceRow = {

Sub-parts

Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.

Showing up to 3 levels deep. Open a part to see what it holds further down.

19+items
◇
component · born here
❒asset
# Debris Anything loose in the shaft that can hit you. Spawned by `game.hazards.buildRig()`; not authored into the scene. Two kinds, one behaviour, because a structural member that has let go **is** debris. | `kind` | while `released` is false | once released | |---|---|---| | `"brace"` | a cross-member of the truss: static structure, part of the wreck, holding. Visible from the first second of the run at instability 0. | sweeps the shaft on the drift it was built with, at `hazards.CFG.braceSpeed`, tumbling. Leaves the shaft volume and goes dark — a collapsed member does not come back. | | `"chunk"` | (chunks start released; the director sleeps and wakes the entity instead) | crosses the route rather than running along it, and wraps back in at the far side, so the shaft never quietly clears itself. | `HazardDirector` decides **when** a brace lets go and **how many** chunks are awake — a disabled entity does not tick, so a sleeping chunk cannot wake itself. ## Contact A sphere overlap against the local player's body, `radius + suitRadius`, one hit per `hitCooldownSec`. On contact it: - emits `impact(damage, "collapse")` or `impact(damage, "debris")` — `shell` owns what that costs; - calls `hazards.impulse` with the piece's own velocity plus a kick away from it, so being hit by a moving mass sends you the way it was going. It never writes the player's transform and never writes `state.integrity`. ## Fields worth reading from outside `released`, `hits`, `travelled` (metres since release).
▲ 0↑ born
▣
module · born here
❒asset
# rules <!-- zero:scaffolded-readme --> <!-- Replace the line below with what this module provides and who consumes it, then delete both comment lines. --> TODO: describe this module.
▲ 0↑ born
◇
component · born here
❒asset
# PressureVent A relief port on the Kestrel Wake that no longer holds. Spawned by `game.hazards.buildRig()`; not authored into the scene. A vent is a **rhythm, not an ambush**. It rests, then the lip lights up and a stub of plume shows for a beat and a quarter, then it blows for a beat and a third. The glow is the contract with the player: you can see it coming — and if you are still hauling a tonne and a half of plate down the middle of the shaft when it goes, seeing it coming was not enough. ## Escalation Armed from `instability` 1 (`RULES.CFG.ventAtInstability`). The rest between bursts shortens with every plate taken off the wreck; the charge and the burst stay the same length, because those are the two windows a player has learned to read. At instability 0 it never cycles and the plume is not drawn at all. ## What it does to you Inside the jet cone it calls `hazards.impulse` every frame — the shove has to be sustained to take you off your line, not a single knock. Two things keep it a hazard rather than an ejector seat: - **Radial falloff.** Strongest down the axis, nothing at the rim, so clipping the edge of a plume costs a correction and not the level. - **A per-burst budget** (`hazards.CFG.ventMaxDeltaV`, 6.5 m/s). In a game with no drag an uncapped jet is a catapult: sit in one for its whole burst and you leave the wreck. With the cap a vent costs a known amount of speed to correct — about two seconds on the RCS brake. The first frame the player is caught it emits `impact(impactAmount, "vent")` once. It never writes the player's transform and never writes `state.integrity`. ## Fields worth reading from outside `firing`, `charge` (0..1 warning ramp), `fires`, `hits`. `look` can hang the lip's emissive off `charge`; `shell` can warn off `firing`.
▲ 0↑ born
◇
component · born here
❒asset
# Scrapper The suit. Everything that moves the player in HULLBREAKER happens in this component and nowhere else. Vacuum has no drag, so `Scrapper` integrates a velocity that nothing ever takes away. Thrust adds to it, the RCS brake subtracts from it and charges propellant for the privilege, and letting go of the controls changes nothing at all. Clamp a plate and its mass folds into every one of those numbers: you start slower, you stop far slower, and the shaft is narrower than you remember. Put it on the player's body. The scene's PlayerPrototype clones one per joining scrapper, and each instance answers only to the client that owns its body. ## Controls | Control | Keyboard | Gamepad | Touch | |---|---|---|---| | Move (thrust in the helmet's plane) | `WASD` | left stick | left stick | | Look | mouse | right stick | drag, right of screen | | Thrust up | `Space` | right shoulder | **Thrust Up** | | Thrust down | `Ctrl` / `C` | left shoulder | **Thrust Down** | | Burn (hard acceleration) | `Shift` | right trigger | **Burn** | | RCS brake | `Q` | left trigger | **RCS Brake** | | Magclamp (grab / release) | `F` | west face button | **Magclamp** | Move and look come from `@builtin::inputMaps.default`; the other five are `game.scrapper`, which composes beside it. ## What other systems call ```lua local suit = entity(bodyId).component.get("Scrapper") suit:applyImpulse(dx, dy, dz) -- world-space delta-v, m/s suit:applyForce(dx, dy, dz, dt) -- the same, held for dt seconds suit:velocityAt() -> vx, vy, vz suit:isCarrying() -> carrying, tonnes, pieceId suit:halt() -- bring the suit to rest, spending no propellant ``` `applyImpulse` is the only sanctioned way to move the scrapper from outside this slice — a pressure vent, a collapsing beam, a piece of debris all push the player through it. The shove is scaled by whatever is on the clamp, so a loaded scrapper is harder to knock off line, and the plate is carried through the shove with them. ## What it reports `speed`, `carrying`, `carriedPiece` and `clampReady` are live public fields a HUD can read every frame. `carrying` is also mirrored onto `game.rules`' run state, and the clamp announces itself on the event bus as `clamped`, `released` and `deposited`.
▲ 0↑ born
backing path · game/hazards.module

Problems

Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.

0problems
No problems reported on this asset or on the parts this page shows. Parts it leaves out are not counted here.
⌬ZeroMind agent review · awaiting first pass
Findings
Reviewer findings (handle · model · tag · quoted note) appear here once the per-pass review log lands. Today only the rolled-up agent_score is exposed.
usability—
did it work as advertised
quality—
authoring polish + cohesion
performance—
frame & memory budget held
agent review score
—
/ 100
awaiting first pass
usability × 0.40
+ quality × 0.35
+ performance × 0.25
± compat factor

Usability ratings

Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".

—%no reports yet
Sign in to report whether this part worked for you.
Discussion

Scoped to this part · feeds back into the world's score.

0comments
Sign in to post.sign in
No comments yet. Be the first.