Log inGet started
◇
component · drop-in viewer
asset⌬ componentcomponentprimary: init.luau·originates fromworld bdf6a40a-f…

Stage

The place **GANESHA: GUARDIAN OF THE PORTAL** happens. One instance, on the scene's `MountWorld`. Builds and re-grades the eight arenas from `G.ARENAS`, the festival town and the portal, and stages every beat from the darkness through the portal, the tunnel, the transformation an…

by◐satish·posted 25d ago
What it does

Stage — the world slice

The place GANESHA: GUARDIAN OF THE PORTAL happens. One instance, on the scene's MountWorld. Builds and re-grades the eight arenas from G.ARENAS, the festival town and the portal, and stages every beat from the darkness through the portal, the tunnel, the transformation and the blessing.

Public fields

None. Everything it needs is in game.Game.

Methods

  • :showArena(index) — rebuild the arena for 1..8, immediately.
  • :arenaId() — which arena is standing, by id ("grassland", "festival", "blessing", …).

Lifecycle

  • start() — reaches MountWorld, subscribes to the bus, stands up arena 1 and, while the act is TITLE, the dark festival the player opens in.
  • update(dt) — runs the beat queue, and watches for the guardian standing in the portal so it can raise PORTAL_NEAR and the "Enter the portal" prompt.
  • onDestroy() — unsubscribes and despawns everything it placed.

Notes

  • Everything it places is temporary and hangs under one root it created. The world is shared, the scene is the plan pass's, and a slice must not put runtime objects into either.
  • Geometry is code. The discs, boulders, crystals and spires are generated as runtime meshes with per-face normals, so the flat-shaded look is a property of the generator rather than of a texture. The engine re-runs that code on every load, which is exactly why none of it is frozen into an asset.
  • Materials are made from the component, so the engine files them under the runtime store and re-creates them per load; they are never accepted into the scene.
  • Lighting is re-graded, not rebuilt. lighting.setup writes the scene's own sun, ambient and sky, and this slice sets them per arena.

Interface

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

conforms to

zero/source-extract/v2

game.world.Stage — the place GANESHA: GUARDIAN OF THE PORTAL happens. Boots onto `MountWorld`. Builds and re-grades the eight arenas from `G.ARENAS`, the festival town and the portal, and stages every beat from the darkness through the transformation to the blessing. It also owns the cinematic camera poses for those beats. Everything it spawns hangs under one root it created, is `temporary` (the world is shared and the scene is not this slice's to write), and is despawned when the arena changes or the component goes away. It reads `G.state` and listens on the bus; it never reaches into another slice.

Model

mat(name: string, rgb: { number }, emissive: number?, rough: number?) → void

One matte flat-shaded material per colour, made once and reused. Created from the component, so the engine files them under the runtime store and re-creates them every load — they are never accepted into the scene.

argtypedescription
namestring
rgb{ number }
emissivenumber?
roughnumber?

gold( ) → any

mesh(key: string, positions: { number }, indices: { number }) → void

Flat-shaded low-poly shapes made as runtime meshes. The engine re-runs this code on every load, which is exactly why these are not frozen into assets.

argtypedescription
keystring
positions{ number }
indices{ number }

rng(seed: number) → void

argtypedescription
seednumber

disc(key: string, radius: number, sides: number, y: number) → any

A flat n-gon disc, `sides`-sided, centred at the origin, lying in XZ.

argtypedescription
keystring
radiusnumber
sidesnumber
ynumber

rock(key: string, radius: number, seed: number) → any

A boulder: a jittered low-poly ball, which reads as faceted rock once flat.

argtypedescription
keystring
radiusnumber
seednumber

crystal(key: string, height: number, radius: number) → any

A crystal: a hexagonal bipyramid, the arena's glowing accent.

argtypedescription
keystring
heightnumber
radiusnumber

spire(key: string, height: number, radius: number, sides: number) → any

A tapered spire: the tree trunk, a temple gopuram, a distant peak.

argtypedescription
keystring
heightnumber
radiusnumber
sidesnumber

part(parent: any, name: string, model: any, material: any, pos: { number }, scale: { number }?, rotY: number?) → void

argtypedescription
parentany
namestring
modelany
materialany
pos{ number }
scale{ number }?
rotYnumber?

buildArena(index: number) → void

argtypedescription
indexnumber

gradeArena(arena: any) → void

argtypedescription
arenaany

buildPortal(parent: any, at: { number }, scale: number) → void

argtypedescription
parentany
at{ number }
scalenumber

buildFestival(dark: boolean) → void

argtypedescription
darkboolean

buildBlessing( ) → void

syncStage( ) → void

What should be standing, given the act and the arena. One place decides, so the town and the arena can never both be up.

pushBeat(seconds: number, fn: () → void

argtypedescription
secondsnumber
fn(

onActChanged(payload: any) → void

argtypedescription
payloadany

onArenaChanged(_payload: any) → void

argtypedescription
_payloadany

awake( ) → void

start( ) → void

update(dt: ?) → void

argtypedescription
dt?

onDestroy( ) → void

showArena(index: number) → void

Rebuild the arena for an index (1..8). For the interface's arena card.

argtypedescription
indexnumber

arenaId( ) → string

Which arena is standing, by id.

Sub-parts

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

7items
▣
module · born here
❒asset
# Game The spine of **GANESHA: GUARDIAN OF THE PORTAL**. One shared module every slice requires: the tuning numbers, the eight Rakshasas and their arenas, the eleven quests, the story machine's acts, the mount points, and the event bus. It draws nothing, moves nothing and sounds nothing — it is the contract that lets a slice be written without knowing who else is in the world. `require` returns one shared instance per world, so `G.state` read in the hero slice is the same table the interface slice reads, and `G.on` / `G.emit` is how one slice learns what another did. ## Exports - `G.TITLE`, `G.SUBTITLE` — the title card's own words. - `G.ART_STYLE` — the one art-direction sentence every visual decision answers to. - `G.PALETTE` (hex, for the interface) and `G.RGB` (0-1 triples, for lights/materials). - `G.ACT` / `G.ACT_ORDER` — the story machine's phases. - `G.QUESTS` — the eleven quests, in order: `{ title, objective }`. - `G.PLAYER` — the guardian's 2000 HP, speeds, stamina, jump. - `G.ATTACKS` / `G.ATTACK_ORDER` / `G.ABILITY_BAR` — the four attacks with damage, timing, reach, arc and cooldown. - `G.FOES` — the eight Rakshasas: health, speed, damage, reach, behaviour, tells. - `G.ARENAS` — the eight battlefields: ground colour, sky, sun, fog, props. - `G.BEATS` — timings for the beats between fights. - `G.state` — the shared run state (act, quest, arena, health, foe, banner…). - `G.EV` — event names; `G.on(name, fn)` and `G.emit(name, payload)`. - `G.setAct` / `G.setQuest` / `G.setArena` — the only writers of those fields. - `G.banner` / `G.toast` / `G.kick` / `G.freeze` — the presentation verbs. - `G.MOUNTS` / `G.SLICES` — where each slice is booted. - `G.clamp` / `G.lerp` / `G.damp` / `G.angleDelta` / `G.horizDist` / `G.yawTo`. - `G.arena()` / `G.foeAt(i)` / `G.questForArena(i)` / `G.restart()`. ## Usage ```luau local G = require("game.Game") G.on(G.EV.FOE_DIED, function(foe) G.setQuest(G.questForArena(G.state.arena)) end) if G.state.act == G.ACT.FIGHT then local arena = G.arena() local tuning = G.foeAt(G.state.arena) end ``` ## Notes - **State has one writer per field.** The hero slice owns `health`, `stamina` and `cooldowns`; the foes slice owns `foe` and `bossBar`; the world slice owns `act` transitions from its own beats. Announce changes with `G.emit` so the interface and the sound slice hear them. - **The bus is per world, not per frame.** Subscribe in `start()` and keep the unsubscribe function the subscribe returns; drop it in `onDestroy()` or a reload will leave two listeners running. - **Arenas are 1-indexed and match `G.FOES` by `arena.foe`.**
▲ 0↑ born
backing path · game/world/Stage.component

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.