Skip to content

jengine-lantern

Torch-lit descent - darkness as the mechanic, gameplay light sampled from the same model the renderer draws.

KindProject package: linked into the builds of the projects that list it
Sourcepackages/jengine-lantern
Version0.1.0
Entry pointjengine_lantern::LanternPackage
Script namespaceLantern
Listed byprojects/arcade-classics

List it in the top-level "packages" array of project.jengine:

{
"packages": [
{ "name": "jengine-lantern", "source": { "type": "path", "path": "../../packages/jengine-lantern" } }
]
}

Then rebuild the editor viewport for the project (jengine viewport-build projects/<name>, or the editor’s Rebuild viewport banner). Native and web builds link it on their own.

A scene that uses its components must be a v2 scene ("version": 2): a v1 scene has no field for a package component and keeps it as inert custom data.

A script can check that the package is linked with if Lantern == nil then return end.

Scene keyRust type
td_runTdRunpackage only: needs a v2 scene

Commands run in every runtime and return nothing; a package publishes what scripts read onto components (see the notes below). The flat name also works.

CallFlat name
Lantern.descend(e, level)td_descend
Lantern.move(e, dx, dy)td_move
Lantern.candle(e)td_candle
  • projects/arcade-classics/assets/scenes/tallow.scene

From packages/jengine-lantern/src/lib.rs.

Tallow Deep - a lantern-lit descent where darkness is the mechanic.

  • [mine]: the level, and the rock boxes that cast its shadows.
  • [run]: the descent - a lantern that burns down, candles you leave behind, and things that only come when it is dark.
  • [content]: levels as recipes, and refusing one that cannot be lit.

What this package does that the others do not

Section titled “What this package does that the others do not”

It authors the renderer’s lighting inputs from Rust, every frame. The other arcade packages write sprites; this one writes PointLight2D, LightOccluder and AmbientLight entities too, because the thing being tested is the lighting pass itself and its limits.

Two consequences are worth knowing before reading the code.

The rock rectangles are the draw list and the occluder list. One greedy merge in mine::Mine::rock_rects feeds both, so what you see is by construction what casts. Deriving them separately would be the same bug as a bot with its own idea of a blast radius: two copies of one rule, drifting where nobody looks. It is also a large saving - a 41x33 cave is about eighty rectangles rather than eight hundred tiles.

The occluder entities are a pool that only grows. It holds as many boxes as any frame has wanted (run::Run::live_boxes, cut to the renderer’s occluder pool - see mine); surplus members are switched off with LightOccluder.enabled rather than moved or despawned, and a disabled occluder contributes nothing at all. (It was exactly sixteen, the whole budget, until the renderer’s occluder set went per light in Sep 2026.)

Lighting here is the composite path, not lit_sprite: plain sprites drawn normally, then scene.rgb * light. That is the path jengine_core::renderer::lighting::light_at models, which is why the lurkers’ idea of “dark” is the same number the screen shows.