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

state

Everything that crosses a lane boundary in part 9 ("THE FARJAN"), as **pure functions of part-local `t`** and nothing else. No state, no side effects, no entity lookups, no `ctx`.

byzero-proxy @ DESKTOP-DB3UJOJ·posted 3mo ago
What it does

preseed.tides.state — the TIDES shared contract

Everything that crosses a lane boundary in part 9 ("THE FARJAN"), as pure functions of part-local t and nothing else. No state, no side effects, no entity lookups, no ctx.

local State = require("preseed.tides.state")

State.ship(t)     --> { pos = {x,y,z}, forward = {x,0,z}, speed, headingDeg }
State.sea(t)      --> { windSpeed, windDirection, choppiness, amplitude,
                        standingWaveRatio, foamDecay, significantHeight }
State.weather(t)  --> { squall01, coverage, cloudBase, cloudTop, wetness, rain01 }
State.light(t)    --> { sunElevDeg, sunAzimuthDeg, timeOfDay, sunColor, sunIntensity,
                        ambientColor, ambientIntensity, keyOcclusion01, exposure }
State.palette(t)  --> { skyHorizon, skyZenith, skyGround, cloudLit, cloudShadow,
                        waterBody, foam, fogColor, hullRed, upperworksWhite,
                        antifoul, bootTop, cabinEmissive }

Why pure

Hot-reload rebinds a module's function bodies without re-running its module-level chunk. Module-level mutable state in a shared file therefore keeps the old build's values while running the new build's code, and the bug looks like it is in whichever lane read it last. A file of pure functions cannot do that.

It is also what makes the wake derivable. Because ship(t) is pure, the wake at time t is exactly the locus { ship(t - a) : a in [0, 26] } — sample it and a scrub, a seek or a hot-reload all produce the identical wake. An accumulating wake is empty after every seek, and this demo is scrubbed constantly.

No lane may add module-level mutable state to this file.

Ownership

FunctionOwner (may retune the numbers)Readers
shipSEA (track & speed)CAMERA, SEA, FERRY
seaSEASKY, CAMERA
weatherSKYLIGHT, SEA, FERRY
lightLIGHTSKY, SEA, FERRY
paletteLIGHTeveryone

A lane needing a new field appends it. Nobody renames or removes one.

Fixed facts

  • Axes: +X = ahead (bow), -X = astern, +Y = up, -Z = port, +Z = starboard. The sun sits just off her starboard bow.
  • Music: 192 BPM, 4/4 — BAR = 1.25 s. Thirty seconds is exactly 24 bars. Nothing changes on a number that is not a multiple of 1.25.
  • pos.y is the calm-water datum, not her true height. preseed.Buoyant owns her vertical; read the entity for her real y.
  • ProceduralSky owns the sun's direction via timeOfDay. sunAzimuthDeg is the intended bearing the clouds are lit against, not an instruction to write a direction onto the Light.

Interface

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

conforms to

zero/source-extract/v2

preseed/tides/state.module — THE FARJAN: the shared contract for part 9. Everything that crosses a lane boundary in TIDES lives here, and it is nothing but PURE FUNCTIONS OF PART-LOCAL `t`. No state, no side effects, no entity lookups, no ctx. That is deliberate. Hot-reload rebinds a module's function bodies WITHOUT re-running its module-level chunk, so any module-level mutable state in a shared file silently keeps the OLD build's values while running the NEW build's code. A file of pure functions cannot do that. No lane may add module-level mutable state here. It also makes the wake derivable rather than accumulated: because ship(t) is pure, the wake at time t is exactly the locus { ship(t - a) : a in [0, 26] }. A scrub backwards, a seek, or a hot-reload produces the identical wake. The demo is scrubbed constantly; an accumulating wake would be empty after every seek. OWNERSHIP — who may retune the numbers: ship -> SEA (track and speed) read by CAMERA, SEA, FERRY sea -> SEA read by SKY, CAMERA weather -> SKY read by LIGHT, SEA, FERRY light -> LIGHT read by SKY, SEA, FERRY palette -> LIGHT read by everyone A lane needing a new field APPENDS it. Nobody renames or removes one. AXES, fixed and source-derived: +X = ahead (bow), -X = astern, +Y = up, -Z = port, +Z = starboard. The sun sits just off her starboard bow. The music is 192 BPM, 4/4 — BAR = 1.25 s, BEAT = 0.3125 s. Thirty seconds is exactly 24 bars. Nothing in this part changes on a number that is not a multiple of 1.25.

clamp01(x: number) → number

argtypedescription
xnumber

smoothstep(a: number, b: number, x: number) → number

argtypedescription
anumber
bnumber
xnumber

track(keys: { { number } }, t: number) → number

Piecewise-linear over { {t, value}, ... }, clamped at both ends.

argtypedescription
keys{ { number } }
tnumber

ctrack(keys: { { any } }, t: number) → void

Piecewise-linear over { {t, {r,g,b}}, ... }, clamped at both ends.

argtypedescription
keys{ { any } }
tnumber

shipZ(t: number) → number

argtypedescription
tnumber

ship(t: number) → void

Her track, as a pure function of part-local t. pos is PART-LOCAL: world = ctx.at(unpack(pos)). pos.y is the CALM-WATER DATUM, not her true height. `preseed.Buoyant` owns her vertical; a lane needing her real y reads the entity. She was measured at y = -2.21 in a trough with the current sea; expect +/-4 m in the squall.

argtypedescription
tnumber

sea(t: number) → void

Sea state. windDirection is in radians and blows toward +x/+z: the squall overtakes her from the port quarter.

argtypedescription
tnumber

weather(t: number) → void

The weather. squall01 is the single scalar "how much squall is on us" that LIGHT cuts the key against and SEA/FERRY scale off.

argtypedescription
tnumber

lightAnchors( ) → void

The ProceduralSky component owns the sun's DIRECTION, derived from timeOfDay. A direction written onto the Light is overwritten the next time the sky syncs, leaving shadows and the glitter track disagreeing. sunAzimuthDeg below is the INTENDED bearing (just off her starboard bow) and is what the clouds are lit against; the hour is what actually orients it. EVERY TUNABLE IN THIS SECTION LIVES INSIDE A FUNCTION BODY, and that is not a style choice. A hot-reload swaps a module's function bodies WITHOUT re-running its module-level chunk, so a number kept in a module-level local is the OLD build's number for as long as the VM lives — you retune it, the file says the new value, and the picture keeps the old one. Measured the hard way this round: an azimuth edited at module level did not move the sun until the whole part was torn down. Anchors returned from a function are re-read every call, so a retune is one edit and one frame.

sunAzimuthDeg( ) → number

THE SUN'S BEARING, measured from +X toward +Z: 90 = abeam to starboard, 180 = dead astern, 270 = abeam to port. 150 deg — astern and off her STARBOARD quarter — and this is the single most consequential number in the part, so here is what it buys and what it cost, measured. A sun on her PORT quarter (round 1 carried 232) cannot key any surface a camera on her starboard side can see: dot(+Z, toSun) = sin(232) = -0.79, so every camera-facing plane on the ship is in shadow by arithmetic, not by accident. That is exactly what three independent reads of round 1 measured and it is why the ship rendered as an ambient wash — three mutually orthogonal superstructure faces within 1.02 of each other, and a horizontal deck BRIGHTER than the vertical the sun was supposed to be on. At 140 the same starboard side takes sin(140) = 0.64 of the key: a raking light down her whole length at 2-4 deg elevation, which is what makes plate seams, strakes and the boot top read, and it leaves the decks (cos of an 87 deg incidence, 0.05) dark against them. Her transom and quarter take 0.77 of it, so the wake in beat 4 is lit from beyond it. And it sits 2 deg off the brief's own beat-5 look bearing, which puts the sun essentially dead behind her in HERO 5: she silhouettes, the rim runs down her edge, and the slot's shaft lays its burning stripe on the water IN FRAME rather than 90 deg off the side of it. WHAT IT COSTS, stated plainly: the disc is no longer inside the brief's beat-1/beat-2 stations (60 deg and 119 deg off those look bearings), so the glitter track those beats describe running INTO the lens now runs across the frame instead. The trade is a lit ship and lit foam in every beat against a 195/255 disc in two of them, and it is taken deliberately: the brief asks both for a sun the camera looks INTO in beats 1-2 and for a starboard side legible in beat 3, and no single fixed sun does both. SKY's clouds, the sun slot and the fog lobe all read this same bearing through `S.sunDirection`, so they follow it rather than fight it.

keyOcclusion(t: number) → number

keyOcclusion01: how much of the key the weather is eating. 0 to t=24, then closing over 24.0-26.25, then 0.15 for the slot at 26.25-27.5, then 0.85. This is the one function in the contract with a deliberate near- discontinuity, and it lands on bar 21.

argtypedescription
tnumber

slotOpen01(t: number) → number

How open the sun slot is, 0..1. Appended by LIGHT, and it MIRRORS the window `sky.module` carves the wedge over — bar 22, with a short ramp either side so the shaft arrives and leaves rather than switching. The two must agree: this is what tells LIGHT that the key is allowed back up, and the wedge is what makes the shaft visible, so a window here that the sky does not carve is a key with no shaft in the picture to justify it. LIGHT needs its own copy because the measurement it normally drives the key from CANNOT SEE THE SLOT. The wedge is carved with a `far` term that only opens beyond 2.2 km from the box centre, and she is inside that radius, under the rain veil, for the whole of the last beat. The slot is a hole at the HORIZON — it is what lays the burning stripe on the water behind her — and the probe marching outward from her stands in the veil the whole way to it.

argtypedescription
tnumber

light(t: number) → void

argtypedescription
tnumber

sunDirection(t: number) → void

The unit vector pointing TOWARD the sun, as the engine's own sky model resolves it from `timeOfDay`. Appended by SKY, because the clouds, the sun slot and the fog's in-scatter lobe all have to be aimed at the disc the audience can actually see, and the disc is a function of the hour. THE DIRECTIONAL LIGHT IS THE AUTHORITY, NOT THE SKY. `src/engine/systems/ sky.rs` refuses to sync the sun onto the lighting config whenever a directional Light entity exists — "a Light entity with LightType:: Directional is the sole authoritative writer for lighting.dir_*". This part spawns `tid_sun`, so the sky never orients anything, and a Light spawned without a direction keeps the default straight-down one. Measured in this scene at timeOfDay 18.42: `tid_sun` direction (0, -1, 0). So LIGHT must WRITE this direction onto the Light — the opposite of what a sky-owns-the-sun reading would suggest — or the visible disc and the shading disagree by ninety degrees. `-S.sunDirection(t)` is the value the Light wants, since a Light's direction is the way the light TRAVELS.

argtypedescription
tnumber

palette(t: number) → void

argtypedescription
tnumber

Sub-parts

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

3items
This part has no composite children. See the Files segment for its leaf payloads.
backing path · preseed/tides/state.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. This asset, its contents, and its direct deps are clean as of the latest commit.
⌬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.