jengine-rampart
Wave defence where the searchlight is the targeting - one light set read by the shader and by the game.
| Kind | Project package: linked into the builds of the projects that list it |
| Source | packages/jengine-rampart |
| Version | 0.1.0 |
| Entry point | jengine_rampart::RampartPackage |
| Script namespace | Rampart |
| Listed by | projects/arcade-classics |
Using it
Section titled “Using it”List it in the top-level "packages" array of project.jengine:
{ "packages": [ { "name": "jengine-rampart", "source": { "type": "path", "path": "../../packages/jengine-rampart" } } ]}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 Rampart == nil then return end.
Components
Section titled “Components”| Scene key | Rust type | |
|---|---|---|
dr_field | Rampart | package only: needs a v2 scene |
Script commands
Section titled “Script commands”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.
| Call | Flat name |
|---|---|
Rampart.start(e, which) | dr_start |
Rampart.quit(e) | dr_quit |
Rampart.aim(e, x, y) | dr_aim |
Rampart.turn(e, dir) | dr_turn |
Scenes that use it
Section titled “Scenes that use it”projects/arcade-classics/assets/scenes/rampart.scene
From packages/jengine-rampart/src/lib.rs.
Dusk Rampart - wave defence where the searchlight is the targeting.
- [
beams]: the light set. One list, read by the shader and by the game. - [
wall]: the rampart, its segments, and the shadow it casts. - [
run]: the match - attackers, turrets, waves, and the reveal rule. - [
content]: the stage and foe tables, and the two refusals.
What this game is here to prove
Section titled “What this game is here to prove”Three engine subsystems had shipped in an Engine Test Lab scene and in no
game at all: renderer materials, the shader graph, and spot
lights. A feature whose only exercise is a Test Lab scene is one nobody
re-checks after the commit that added it - which is the standing
VectorPath had when Coral Drive found it is drawn by one runtime of four.
The design claim is Tallow Deep’s, moved to a light it did not cover:
the searchlight you can see is the searchlight the game reads. A
searchlight in a defence game is a cone the player watches and a rule that
decides who is revealed, and written twice those drift invisibly - the
screen shows an attacker in the beam while the simulation calls it hidden,
and what gets reported is that the game feels unfair. So there is one
[beams::Beams]: beams::Beams::light_data hands it to
renderer::lighting::light_at, and sync_lights hands the same values
to the renderer as components. Neither is derived from the other.
That needed engine work first. light_at was point-lights-only until Sep
2026 - a deliberate hole, on the grounds that nothing used spot lights and
a wrong answer is worse than a missing one. renderer::lighting::spot_light_at
is the CPU twin now, pinned to spot_light.wgsl by
cargo test -p jengine-core --test cpu_light_matches_the_shader.
The second claim is the light count. MAX_LIGHTS_PER_KIND is 256 and
nothing in the repo had gone near it; Tallow Deep runs a lantern and a few
candles. Every shot here makes a real point-light muzzle flash, so a firing
line puts dozens on screen at once.
Why the genre fits the occluder budget
Section titled “Why the genre fits the occluder budget”A LightOccluder is a closed outline, so the cheapest one is four
segments. A rampart is a handful of long straight boxes, and
wall::Wall::occluders merges abutting segments into runs, so an
unbroken wall is one box. Until Sep 2026 the renderer kept sixty-four
segments for every light, and that was why the genre fitted where a cave
did not. The pool is 448 edges now, and when dozens of lights share one
wall the pass hands them all one list of its distinct edges
(pack_light_occluders), so the wall is paid for once, not per light.
Component ("version": 2 scenes)
Section titled “Component ("version": 2 scenes)”dr_field, on an entity with a transform.
"dr_field": { "tables": { "stages": "data/dr_stages", "foes": "data/dr_foes" }, "which": 0}Script commands
Section titled “Script commands”| Call | Does |
|---|---|
Rampart.start(e, which) | begin (or restart) a stage |
Rampart.aim(e, x, y) | aim the beam at a world point, for a mouse |
Rampart.turn(e, dir) | turn the beam, for keys: -1, 0 or 1 |
Rampart.quit(e) | abandon the defence |
Both aiming calls are limited by the stage’s turn rate inside run::Run,
so a mouse cannot teleport the beam - that limit is the only cost the game
has, and a script must not be able to spend around it.