import { AssetHandle, Process, ScopedProcessQueue, Scene, ServiceKey, Plugin, RendererAdapter, EngineContext, SystemScheduler, Vec2, RendererUIHit, RendererPointerEventType } from '@yagejs/core'; import { Container, ApplicationOptions as ApplicationOptions$1, TextStyleOptions, Filter as Filter$1, Application as Application$1, Graphics, Texture, ColorSource, BLEND_MODES, DestroyOptions as DestroyOptions$1, AnimatedSprite, BitmapFont, BitmapText, SplitBitmapText, SplitText, Sprite, Text, FillGradient, NineSliceSprite as NineSliceSprite$1, Particle as Particle$1, ParticleContainer as ParticleContainer$1, PointData, EventMode } from 'pixi.js'; /** A loaded texture resource managed by the renderer. */ type TextureResource = Texture; /** A typed asset handle for a texture resource. */ type TextureHandle = AssetHandle; /** A typed handle for any asset loaded by the renderer asset pipeline. */ type RendererAsset = AssetHandle; /** Any texture reference accepted by renderer helpers and components. */ type TextureInput = string | TextureResource | TextureHandle; /** * An asset-backed texture reference. The key or handle must resolve to a * preloaded asset or a `registerTexture` entry. */ type TextureRef = string | TextureHandle; /** The mutable graphics drawing context used by renderer draw callbacks. */ type GraphicsContext = Graphics; /** A display container used by renderer and UI APIs. */ type DisplayContainer = Container; /** A sprite display object. */ type DisplaySprite = Sprite; /** A text display object. */ type DisplayText = Text; /** A bitmap-font text display object. */ type DisplayBitmapText = BitmapText; /** An animated-sprite display object (`AnimatedSpriteComponent`'s render object). */ type DisplayAnimatedSprite = AnimatedSprite; /** A per-character/word/line split canvas-text display object (`SplitTextComponent`'s canvas render object). */ type DisplaySplitText = SplitText; /** A per-character/word/line split bitmap-text display object (`SplitTextComponent`'s bitmap render object). */ type DisplaySplitBitmapText = SplitBitmapText; /** A nine-slice display object, as built by `createNineSlice` / `UINineSlice`. */ type NineSliceSprite = NineSliceSprite$1; /** A pixi filter (or chain of filters), as attached through the effects API or `rawFilter`. */ type Filter = Filter$1; /** A GPU-batched particle container, as owned by `@yagejs-addons`/`@yagejs/particles`' emitter components. */ type ParticleContainer = ParticleContainer$1; /** A single GPU-batched particle, as pooled and emitted by `@yagejs/particles`. */ type Particle = Particle$1; /** The underlying PixiJS `Application` instance — see `RendererPlugin.application`. */ type Application = Application$1; /** Options accepted by the PixiJS `Application` — see `RendererConfig.pixi`. */ type ApplicationOptions = ApplicationOptions$1; /** Options for `Container.destroy()`, as returned by a visual component's `destroyOptions()` hook. */ type DestroyOptions = DestroyOptions$1; /** A loaded bitmap font (parsed `.fnt`/`.xml` + glyph atlas). */ type BitmapFontResource = BitmapFont; /** A typed asset handle for a bitmap font resource. */ type BitmapFontHandle = AssetHandle; /** The `FontFace`s registered when a web font (`.ttf`/`.woff`) is loaded. */ type WebFontResource = FontFace[]; /** A typed asset handle for a web font loaded for canvas `Text`. */ type WebFontHandle = AssetHandle; /** Text styling accepted by UI and text APIs. */ type TextStyle = TextStyleOptions; /** A gradient fill usable anywhere a PixiJS fill style is accepted. */ type GradientFill = FillGradient; /** Generic color input accepted by UI helpers. */ type ColorValue = ColorSource; /** * How a display object's pixels combine with what is already drawn beneath * it — accepted by every visual component's `blendMode` option. * * `"normal"`, `"add"`, `"multiply"`, `"screen"`, `"erase"`, `"min"`, * `"max"`, `"none"` and the `-npm` variants are GPU-native and work as * soon as the renderer starts. The photoshop-style rest (`"darken"`, * `"overlay"`, `"color-dodge"`, ...) are filter-backed and need a * side-effect import in the game's entry file: * * ```ts * import "pixi.js/advanced-blend-modes"; * ``` * * Without it the renderer logs a warning and draws the object normally. */ type BlendMode = BLEND_MODES; /** Point-like data used by UI callbacks and options. */ type PointLike = PointData; /** Options for slicing a texture into frame textures. */ interface TextureSliceOptions { frameWidth: number; frameHeight?: number; startX?: number; startY?: number; columns?: number; count?: number; gapX?: number; gapY?: number; } /** * The runtime handle returned by `addEffect`. Lets the caller toggle, remove, * or fade an effect without tracking its slot in the underlying pixi * `filters` array. * * Effect factories may return a richer handle that extends this — see each * factory's typed return for extras like `setThickness` or `trigger`. */ interface EffectHandle { /** Remove the effect immediately. Idempotent. */ remove(): void; /** Toggle the effect without removing it. Flips the underlying filter's `.enabled`. */ setEnabled(on: boolean): void; /** Whether the effect is currently enabled. */ readonly enabled: boolean; /** Set the effect's primary intensity. Values are clamped to 0–1. */ setIntensity(value: number): void; /** Tween the effect's primary intensity 0 → 1 over `duration` seconds. */ fadeIn(duration: number): Process; /** Tween the effect's primary intensity → 0 over `duration` seconds. */ fadeOut(duration: number): Process; /** * Schedule a `Process` scoped to this effect's lifetime. The process is * routed through the same `ScopedProcessQueue` the effect's fades use — * pauses with the owning scene, time-scales with it, and is cancelled * automatically when the effect is removed. * * Useful for factories that need timed work (e.g. a one-shot trigger ramp, * a uniform animator) without asking the caller to wire `step(dt)`. The * returned `Process` can be cancelled early; otherwise it tears down with * the effect. */ run(p: Process): Process; } /** * Scope identifier for an effect target. Passed to `Effect.onAttach` so * factories can specialize behavior per scope (e.g. clamp size to viewport * on `screen`, no-op on `component`). */ type EffectScope = "component" | "layer" | "scene" | "screen"; /** Context passed to `Effect.onAttach`. */ interface EffectTarget { /** The pixi container the effect's filter is attached to. */ readonly displayObject: DisplayContainer; /** Which kind of scope owns the host stack. */ readonly scope: EffectScope; } /** * The shape an effect factory returns. The renderer's `EffectStack` consumes * this to build the user-facing `EffectHandle`. * * Factories own: * - the `Filter` they push onto `target.filters`, * - the `getIntensity` / `setIntensity` accessors used by `fadeIn` / `fadeOut`, * - any factory-specific extras spread onto the handle via `buildExtras`. * * `getIntensity` / `setIntensity` should target the effect's most natural * "strength" axis (BloomFilter `strength`, OutlineFilter `thickness`, etc). * For shader-style effects with no clean scalar, route through the filter's * built-in `alpha` uniform — every pixi `Filter` carries one and it scales * the filter's whole contribution. */ interface Effect { /** * The pixi filter (or chain of filters) pushed onto the target's `filters` * array. A single `Filter` is the common case; pass an array when an * effect needs more than one pass — e.g. a shader filter wrapped in an * `AlphaFilter` for fade. Order matters: pixi processes filters in array * order, so each later filter sees the previous one's output. */ filter: Filter | Filter[]; /** Read the current primary intensity. */ getIntensity(): number; /** Set the primary intensity. */ setIntensity(value: number): void; /** Optional setup callback fired after the filter is attached. */ onAttach?(target: EffectTarget): void; /** Optional teardown callback fired before the filter is detached. */ onDetach?(): void; /** * Optional factory for typed extras spread onto the final handle. Receives * the base `EffectHandle` so extras can compose against `remove` / `setEnabled` / * `fadeIn` / `fadeOut` / `run` if needed. * * **Pure at call time.** Return closures freely — they're invoked by user * code later and may have side effects then (e.g. `hitFlash.trigger()` * calls `base.run(...)` from inside the closure). What this hook should * NOT do is invoke side effects *eagerly* during the `buildExtras` call * itself — no `base.run(...)`, no scheduling, no external-state mutation * at build time. Use `onActivate` for attach-time side effects. */ buildExtras?(base: EffectHandle): Omit; /** * Optional activation callback fired once the handle is fully built (after * `buildExtras` has merged its keys onto the handle). Use this to schedule * per-effect `Process`es via `base.run(...)` — they are auto-cancelled when * the effect is removed. The right place for self-scheduled tickers (e.g. * a shader-uniform animator) so callers don't have to wire `step(dt)`. */ onActivate?(base: EffectHandle): void; } /** * Zero-argument constructor for an `Effect`. User-facing factories like * `hitFlash(opts)` close over their options and return an `EffectFactory`. * The renderer calls the factory once at attach time. */ type EffectFactory = () => Effect; /** * Named effect definition. The callable shape `(options) => EffectFactory` * is what users invoke at call sites (`bloom({ threshold: 0.8 })`); the * `name` property lets callers find an attached effect later. */ interface EffectDefinition { (options: O): EffectFactory; readonly name: string; } /** * Define a named effect preset. The returned callable accepts the preset's * options and yields an `EffectFactory` whose built effect carries the name * used by `EffectsHost.findEffect()`. * * Names are conventionally prefixed (`yage:hitFlash`, `yage:bloom`, …); the * `@yagejs/effects` package uses `yage:` for its hero presets. */ declare function defineEffect(spec: { name: string; factory: (options: O) => Effect; }): EffectDefinition; /** * Per-attachment-site holder for an `EffectStack`. Every scope (component, * layer, scene, screen) carries its own `EffectsHost` exposed as `.fx`, so * the user-facing API for adding / finding / fading effects is identical * regardless of where the effect lives. * * The underlying `EffectStack` is built lazily on first `addEffect` so sites * that never use effects pay zero cost. * * `getContainer` is a thunk because some sites (component scope) wire the * host at construction time but the underlying display object may itself be * created in the same constructor — the thunk removes "is this stable yet?" * thinking. `makeQueue` is optional because some test setups omit a * `ProcessSystem`; calling `addEffect` then throws a clear error. */ declare class EffectsHost { private readonly getContainer; private readonly scope; private readonly makeQueue; private _stack; constructor(getContainer: () => DisplayContainer, scope: EffectScope, makeQueue: (() => ScopedProcessQueue) | undefined); /** * Attach a visual effect. Returns a typed handle for fading, removal, * intensity, and any per-effect extras the factory exposes. */ addEffect(factory: EffectFactory): H; /** * Recover the handle for the first effect attached here whose underlying * definition is `definition`. Returns `null` if no match exists. */ findEffect(definition: EffectDefinition): H | null; /** Number of attached effects on this host. */ get size(): number; /** * @internal — torn down by the owning site (component/layer/scene/screen) * before its container is destroyed, so user-assigned external filters * survive the EffectStack teardown. */ destroy(): void; private _ensureStack; } /** A rectangle in virtual-space pixels. */ interface VirtualRect { x: number; y: number; width: number; height: number; } /** * A rectangle in canvas CSS pixels (what the DOM sees). Structurally identical * to {@link VirtualRect}; distinguished at the type level so signatures can * signal which coordinate space a rect lives in. */ type CanvasRect = VirtualRect; /** Options for `RendererPlugin.createRenderTarget`. */ interface RenderTargetOptions { /** Buffer width, in the source container's own coordinates. */ width: number; /** Buffer height, in the source container's own coordinates. */ height: number; /** * Texel density as a fraction of the renderer's own resolution. `1` * (the default) gives the buffer the same sharpness as the canvas; `0.5` * allocates a quarter of the texels and stretches them back over the same * area. * * The texture still measures `width × height` whatever the value, so * lowering it costs sharpness and never layout. Worth doing for content * that is already soft — gradients, blurred glows — and not for text or * pixel art. */ resolutionScale?: number; /** Antialias the buffer's own edges. Default `false`. */ antialias?: boolean; /** Colour the buffer is cleared to before each render. Default: transparent. */ clearColor?: ColorValue; /** Label on the underlying texture, shown in the Pixi devtools. */ label?: string; /** Targets sampled by this pass. Drawing waits until these targets are clean. */ dependsOn?: readonly RenderTargetHandle[]; } /** * An offscreen texture the game draws into on its own schedule, returned by * `RendererPlugin.createRenderTarget`. * * The buffer starts out needing a render and is redrawn only when the game * asks — call {@link renderIfNeeded} every frame and {@link invalidate} * whenever the source content changes, or call {@link render} directly for a * buffer that must follow moving content. * * Drawing happens in the source container's own coordinate space: a child at * local `(100, 50)` lands at texture pixel `(100, 50)`. Ancestor transforms * do not reach the buffer, so neither the camera nor the responsive-fit * scale moves or scales what is drawn. Position the source's children in the * space you want the texture to hold. * * Keep the source out of the live scene graph. Pixi promotes a rendered * container to a render group, which changes how it batches where it is * parented, and content drawn into a buffer is normally shown through the * buffer's texture rather than twice. */ interface RenderTargetHandle { /** The texture the buffer draws into. Show it with a `SpriteComponent`, a mask, or a filter. */ readonly texture: TextureResource; /** * The container drawn into the buffer. The game owns it: {@link destroy} * frees the texture and leaves this container alone. */ readonly source: DisplayContainer; /** Buffer width in source coordinates. */ readonly width: number; /** Buffer height in source coordinates. */ readonly height: number; /** Texels per source pixel actually allocated — the renderer's resolution times `resolutionScale`. */ readonly resolution: number; /** Whether a render is pending. */ readonly needsRender: boolean; /** Mark the buffer stale so the next {@link renderIfNeeded} redraws it. */ invalidate(): void; /** * Draw the source into the buffer now and clear the pending flag. * Defers while the source is hidden, an input target is pending, or drawing is disabled. * The request stays pending until an allowed draw or explicit stage capture. * Deferred targets flush in creation order; create inputs before consumers. */ render(): void; /** * Draw the source only when a render is pending. Returns whether it drew, * so a caller can skip downstream work on an unchanged buffer. */ renderIfNeeded(): boolean; /** * Resize the buffer and mark it stale. Anything already showing the * texture picks up the new size on its next draw. */ resize(width: number, height: number, resolutionScale?: number): void; /** * Destroy the texture and its GPU memory. Safe to call repeatedly. Frees * the texture only: destroy the source container yourself, and call * `unregisterTexture(key)` for any key the texture was registered under, or * that key keeps resolving a destroyed texture. */ destroy(): void; } /** Coordinate space a layer lives in. See `LayerDef.space`. */ type LayerSpace = "world" | "screen"; /** * Depth-key function applied to a layer's children before each render. * Receives a Pixi `Container` and returns the **paint order key** — lower * values render first (behind), higher values render on top. Identical * semantics to writing `container.zIndex = key` by hand, but applied to * every child of the layer each frame. * * Default behaviour (no `sort`) is **insertion order** — entities render * in the order their visual containers were added. Use `ySort` for the * classic top-down 2D depth rule, or compose your own depth-key for * isometric / layered-depth setups. * * The key is written to `container.zIndex` by `DisplaySystem` after * transform sync, then Pixi's own render-time `sortChildren()` orders * the layer by zIndex — so the resulting paint order is exactly what * Pixi shows for any manually-zIndexed scene, no separate sort path. */ type LayerSortFn = (c: DisplayContainer) => number; /** * Declarative layer definition attached to a Scene subclass via * `readonly layers = [...]`. The renderer augments `Scene` in core (via * `scene-augmentation.ts`) so this field is typed without core depending * on renderer. * * A layer's `space` controls whether cameras transform it: * - `"world"` (default) — cameras auto-bind the layer (unless spawned with * `autoBind: false`), so it scrolls/zooms with the world. * - `"screen"` — cameras skip the layer on auto-bind, so it stays fixed * to the viewport. Use for HUD, menus, dialogs, or any UI you want * anchored to the screen. * * UI plugins auto-provision a screen-space `"ui"` layer when no layer is * explicitly declared, so a single `new UISurface(...)` keeps working with * zero layer wiring. */ interface LayerDef { /** * Unique layer name within a scene. `"default"` is special — every * scene's render tree auto-creates a layer called `"default"` at order 0, * and any sprite/text/graphics with no explicit `layer` renders there. * Declaring `{ name: "default", order: 0, sort, space, isRenderGroup }` * configures that pre-created layer (e.g. `{ name: "default", order: 0, * sort: ySort }` to depth-sort the layer your entities are already on). * `order` is required, but the value declared for `"default"` is ignored * — `"default"` is the order-0 layer by definition. * Use any other name to add a separate custom layer. */ name: string; /** Draw order within the scene. Lower values render first (behind higher values). */ order: number; /** * Coordinate space the layer lives in. Default: `"world"`. * * - `"world"`: included in a camera's auto-bindings, so it scrolls and * zooms with the camera. Use for gameplay layers (background, entities, * foreground), parallax, and diegetic UI that should follow an entity. * Naming the layer in a camera's `bindings` changes how it follows, * not whether it does, unless the camera sets `autoBind: false`. * - `"screen"`: excluded from auto-bindings; stays fixed to the viewport. * Use for HUD, menus, dialogs, and other screen-anchored UI. Cameras * can still opt in explicitly by naming the layer in their `bindings`. */ space?: LayerSpace; /** * Depth-key function applied to the layer's children. When set, * `DisplaySystem` writes `child.zIndex = sort(child)` for every child * each frame, and flips `container.sortableChildren = true` so Pixi's * render pipeline orders the layer by zIndex. Default: unset (insertion * order). * * Use `ySort` for the classic top-down depth rule, or * `ySortBy(getOffset)` to anchor each sprite's sort key at a per-entity * Y offset (Godot's `y_sort_origin` pattern — matches a sprite's * apparent "footprint" instead of its top-left). * * Game code that manually writes `child.zIndex` on individual sprites * doesn't need `sort` — Pixi already sorts them once `sortableChildren` * is on. `sort` is for the common case where the key is a function of * the sprite's current state (position, depth offset, etc.) and needs * to be recomputed each frame. */ sort?: LayerSortFn; /** * Promote the layer's container to a Pixi v8 render group boundary so its * children render as a separate pass with their own uniform scope. Default: * `false`. * * Why this exists: when a filter is applied to anything inside a render * group, Pixi composes the filter's `uWorldTransformMatrix` from the active * render group's transform and leaves it on the global uniform stack until * the group ends. Sibling render passes that read `globalUniforms` directly — * notably `@pixi/tilemap`'s `TilemapPipe.execute` (which pulls * `_activeUniforms.at(-1)`) — pick up that polluted matrix and visibly drift. * * Set `isRenderGroup: true` on layers that contain filtered content AND on * any sibling layer whose contents must stay unaffected (e.g. a canopy * tilemap above filtered entities). Both layers then sit inside their own * uniform scope, and the filter's transform stays scoped to its source. */ isRenderGroup?: boolean; } /** * Internal value returned by a `MaskFactory`. Bundles the pixi node that will * serve as the mask with the metadata `attachMask` needs to wire it up * correctly. * * @internal — consumers use `rectMask` / `spriteMask` / `graphicsMask` and * never construct this directly. */ interface Mask { /** The pixi `Container` (typically `Graphics` or `Sprite`) used as the mask. */ readonly node: DisplayContainer; /** * `true` → `attachMask` destroys `node` on `remove()`. * `false` (spriteMask) → caller retains ownership; `remove()` only detaches. */ readonly owned: boolean; /** * `true` → `attachMask` parents `node` under the target via `addChild` * (Pixi requires the mask to be in the display tree). * `false` (spriteMask) → assumes the caller already placed `node` somewhere * appropriate. */ readonly attachToTarget: boolean; /** Initial inverse-mask state. */ readonly inverse: boolean; /** Optional re-draw callback; present only on `graphicsMask`. */ redraw?(): void; } /** * Zero-argument factory that produces a fresh `Mask`. User-facing helpers * (`rectMask(opts)`, etc.) close over their config and return one of these; * `attachMask` calls it once at attach time. */ type MaskFactory = () => Mask; /** * Public handle returned by `setMask` / `attachMask`. Idempotent on * `remove()`. Mirrors the {@link EffectHandle} ergonomics so the two APIs * feel like siblings without sharing a stack abstraction (masks are * exclusive — one per container — so they don't compose like effects do). */ interface MaskHandle { /** * Detach the mask from its target. Destroys the underlying mask node when * the factory owned it (rect/graphics) and leaves user-supplied nodes * (sprite) intact. Safe to call repeatedly. */ remove(): void; /** * Toggle inverse-mask mode. Forwards to pixi v8 * `Container.setMask({ mask, inverse })`. */ setInverse(on: boolean): void; /** Current inverse state. */ readonly inverse: boolean; /** * Re-run the original draw function on a `graphicsMask`. No-op for * `rectMask` / `spriteMask`. Call this whenever the data the draw closure * depends on changes (e.g. after a layout pass updates dimensions). */ redraw(): void; } /** * Factory that produces a fresh `ScopedProcessQueue` instance — called once * per `EffectStack` so each stack's cancellation scope stays isolated. */ type EffectQueueFactory = () => ScopedProcessQueue; /** Options for creating a layer. */ interface CreateLayerOptions { /** Per-layer override for PixiJS event mode. Falls back to the manager default. */ eventMode?: EventMode; /** * Coordinate space. `"world"` (default) layers are auto-bound by every * camera unless it sets `autoBind: false`; `"screen"` layers are skipped * so they stay fixed to the viewport. Cameras can still explicitly bind screen-space layers by * naming them in `bindings`. */ space?: LayerSpace; /** * Promote the layer's container to a Pixi v8 render group. See * `LayerDef.isRenderGroup` for the full rationale — isolates filter * uniforms from sibling layers that read `globalUniforms` directly. */ isRenderGroup?: boolean; /** * Depth-key function. See `LayerDef.sort` — when set, `DisplaySystem` * writes `child.zIndex = sort(child)` on every child each frame, and * flips `container.sortableChildren = true` so Pixi orders the layer * by zIndex at render time. Default: undefined (insertion order). */ sort?: LayerSortFn; } /** A named rendering layer — a pixi container at a given draw order. */ declare class RenderLayer { readonly name: string; readonly order: number; readonly container: DisplayContainer; /** Coordinate space — see `CreateLayerOptions.space`. */ readonly space: LayerSpace; private _sort; /** * Layer-scope effects host. `.fx.addEffect(...)` applies a filter to every * entity rendered through this layer (one full-screen render pass per * layer-scope effect, regardless of how many entities are in the layer). * Fades pause with the owning scene and are scaled by its `timeScale`, * matching component-scope behavior. Effects survive until the scene * exits or the handle is `.remove()`d. */ readonly fx: EffectsHost; private _mask; constructor(name: string, order: number, container: DisplayContainer, space?: LayerSpace, queueFactory?: EffectQueueFactory, sort?: LayerSortFn); /** * Per-frame depth-key function, or `undefined` for insertion-order * rendering. Set via `LayerDef.sort` / `CreateLayerOptions.sort` at * creation, or {@link setSort} at runtime. `DisplaySystem` writes * `child.zIndex = sort(child)` on every child each Render phase. */ get sort(): LayerSortFn | undefined; /** * Set (or clear) this layer's depth-key function at runtime, flipping * `container.sortableChildren` to match. Useful for opting the * auto-created `"default"` layer into `ySort` after the scene is live, * or toggling depth sorting on dynamically. * * Passing `undefined` stops the per-frame re-sort, but does **not** * restore the original insertion order: Pixi reorders `container.children` * in place while sorting is on, so clearing `sortableChildren` only halts * further sorting — children keep their current (last-sorted) order and * `zIndex`. Re-establish paint order yourself if you need the original * sequence back. */ setSort(sort: LayerSortFn | undefined): void; /** * Attach a mask to this layer's container, replacing any existing mask. * Returns a handle for inverse toggling, redraw (graphicsMask), or * removal. Torn down on scene exit. */ setMask(factory: MaskFactory): MaskHandle; /** Detach and destroy the layer-scope mask, if any. */ clearMask(): void; /** * Tear down any layer-scope mask. Called by `RenderLayerManager` before * the layer's container is destroyed so the owned mask Graphics gets * cleaned up exactly once. * @internal */ _destroyMask(): void; } /** * Manages named render layers for a single scene. All layers are children * of a single root container. Camera transforms are applied per-layer * by the DisplaySystem based on CameraEntity bindings. */ declare class RenderLayerManager { private layers; private readonly rootContainer; private readonly _defaultLayer; private readonly _defaultEventMode; private readonly _queueFactory; constructor(root: DisplayContainer, defaultEventMode?: EventMode, queueFactory?: EffectQueueFactory, /** * Config for the auto-created `"default"` layer (order 0). Lets a scene * that declares `{ name: "default", sort, space, isRenderGroup }` * configure the pre-created layer instead of the declaration being a * no-op. The provider derives this from the matching `LayerDef`. */ defaultLayerOptions?: CreateLayerOptions); /** Create a new named layer. Throws if `name` already exists. */ create(name: string, order: number, opts?: CreateLayerOptions): RenderLayer; /** * Create a layer from a declarative `LayerDef`. Fields on the def * (`space`, `sortableChildren`) take precedence over the runtime `opts` * so a scene's declaration stays authoritative; `opts` is primarily for * plugin-side overrides when auto-provisioning a layer the scene didn't * declare (via `ensureLayer`). */ createFromDef(def: LayerDef, opts?: CreateLayerOptions): RenderLayer; /** Get a layer by name. Throws if not found. */ get(name: string): RenderLayer; /** Get a layer by name, returning undefined if not found. */ tryGet(name: string): RenderLayer | undefined; /** Get an existing layer, or create it if it doesn't exist. */ getOrCreate(name: string, order: number, opts?: CreateLayerOptions): RenderLayer; /** The default layer (order 0). */ get defaultLayer(): RenderLayer; /** All layers sorted by draw order. */ getAll(): readonly RenderLayer[]; /** The root container holding all layers. */ get root(): DisplayContainer; /** * Tear down every layer's effect stack. Call BEFORE the root container is * destroyed so external (user-assigned) filters get preserved by each * stack's destroy logic instead of being clobbered by the container * teardown. */ destroyEffects(): void; /** * Tear down every layer's mask. Call BEFORE the root container is * destroyed so owned mask Graphics get destroyed exactly once. */ destroyMasks(): void; /** Clear internal state. Call after the root container has been destroyed. */ destroy(): void; private sortLayers; } /** * Options for `ensureLayer` beyond the declarative `LayerDef`. Used by * plugins (e.g. UI) to auto-provision screen-space layers so they stay * fixed to the viewport under the default camera. */ type EnsureLayerOptions = Pick; /** * Scene-owned render tree. Created by `SceneRenderTreeProvider` when a scene * enters. Scoped DI: components resolve via `this.use(SceneRenderTreeKey)`. */ interface SceneRenderTree { /** The single root container for the scene. Lives under the renderer's * world-root container (which sits under `app.stage` and holds the fit * transform). */ readonly root: DisplayContainer; /** Get a layer by name. Throws if not found. */ get(name: string): RenderLayer; /** Get a layer by name, or undefined if not found. */ tryGet(name: string): RenderLayer | undefined; /** All layers, sorted by draw order. */ getAll(): readonly RenderLayer[]; /** The auto-created "default" layer (order 0). */ readonly defaultLayer: RenderLayer; /** * Get an existing layer or create it from the given definition. Used by * plugins like UI that auto-provision a layer if the game didn't declare * one explicitly. Pass `{ space: "screen" }` so a default camera leaves * the layer fixed to the viewport (e.g. screen-space HUD). * Existing layers keep their settings. A differing requested order warns in * development once per tree, layer name, and expected order. */ ensureLayer(def: LayerDef, opts?: EnsureLayerOptions): RenderLayer; /** * Scene-scope effects host — `.fx.addEffect(...)` attaches a filter to the * entire per-scene root container, after layer-scope effects have composited. * Common use: scene-wide CRT, color grade, vignette. Survives until the * scene exits or the handle is `.remove()`d. * * `.fx.findEffect(definition)` finds an attached effect when the caller no * longer has its original handle. */ readonly fx: EffectsHost; /** * Attach one effect to several layers and control it through one handle. * The factory is called once per layer, so this is one filter pass per * listed layer: N fullscreen layers cost N fullscreen passes per frame. For * a single layer prefer `tree.get(name).fx.addEffect(...)`. * * Handle methods fan out to every layer; values (`enabled`, return values * of extras such as `trigger`) come from the first listed layer. Layers * with different camera bindings receive the same layer-local arguments. * Throws when `layers` is empty or names a layer the scene does not have. * * ```ts * const grade = tree.addLayerEffect(colorGrade({ preset: "night" }), [ * "background", * "world", * "props", * ]); * grade.fadeOut(1); // fades on all three * ``` */ addLayerEffect(factory: EffectFactory, layers: readonly string[]): H; /** * Attach a scene-scope mask, replacing any existing one. Clips the entire * per-scene root. Torn down on scene exit. */ setMask(factory: MaskFactory): MaskHandle; /** Detach and destroy the scene-scope mask, if any. */ clearMask(): void; /** * Draw `node` after this scene's layers, outside every layer- and * scene-scope effect and mask, while it keeps its logical parent (position, * alpha, visibility, camera). Screen-scope effects still cover it. Draw * order among nodes attached this way is attach order. `node` must be in * this scene's tree. * * Visual components expose the same thing as * `VisualComponentOptions.renderAboveEffects`; use this for a display * object the game parents into the tree itself. */ renderAboveEffects(node: DisplayContainer): void; /** Undo `renderAboveEffects`. No-op when `node` is not attached. */ renderWithEffects(node: DisplayContainer): void; } /** * Provider that materializes and tears down per-scene render trees. Held * engine-globally so the inspector and renderer-owned tools can enumerate * trees. */ interface SceneRenderTreeProvider { createForScene(scene: Scene): SceneRenderTree; destroyForScene(scene: Scene): void; /** Look up the render tree for a given scene. */ getTree(scene: Scene): SceneRenderTree | undefined; /** Iterate every live scene/tree pair. */ allTrees(): Iterable<[Scene, SceneRenderTree]>; /** Reorder the scene's container to render on top of its root peers. */ bringSceneToFront?(scene: Scene): void; } /** * Engine-scope key for the render-tree provider. Used by cross-scene tools * (inspector, debug plugin, tests) to enumerate or ensure layers. */ declare const SceneRenderTreeProviderKey: ServiceKey; /** * Scene-scope key for the active scene's render tree. Registered by the * renderer's `beforeEnter` hook; components resolve with `this.use()`. */ declare const SceneRenderTreeKey: ServiceKey; declare module "@yagejs/core" { interface Scene { /** * Declarative render-layer definitions. Materialized into a * `SceneRenderTree` by the renderer's `beforeEnter` hook. */ readonly layers?: readonly LayerDef[]; } } /** The region {@link RendererPlugin.createTexture} bakes, in pixels. */ interface CreateTextureOptions { /** Width of the baked texture, measured from x = 0. */ readonly width: number; /** Height of the baked texture, measured from y = 0. */ readonly height: number; } /** RendererPlugin wraps PixiJS v8 behind the YAGE plugin interface. */ declare class RendererPlugin implements Plugin, RendererAdapter { readonly name = "renderer"; /** Whether automatic canvas and render-target drawing is enabled. */ drawingEnabled: boolean; readonly version = "4.0.0"; private _app; private readonly _config; private readonly _virtualWidth; private readonly _virtualHeight; private _provider; private _tickerFn; private _unregisterHooks; private _fitController; /** * Holds the fit transform (scale + offset) and parents every per-scene * render tree. Sits as the only "world" child of `app.stage`, which we * deliberately keep at identity. * * Why this layer exists: in Pixi v8 the active render group's transform * is fed to shaders via `uWorldTransformMatrix`, and `@pixi/tilemap`'s * pipe composes `uProjection × uWorldTransformMatrix × tilemap.worldTransform`. * `tilemap.worldTransform` is already cumulative from root, so any * non-identity transform on the active render group (= `app.stage` by * default) is applied twice — silently mis-scaling tile rendering vs. * Sprites/Graphics, which the batched renderer pre-transforms on CPU. * Pushing the fit onto a non-render-group child (this `_worldRoot`) * keeps `uWorldTransformMatrix` identity at render time. Stage-direct * children (transition overlays, screen-scope effects) keep their * intended canvas-pixel coordinates. */ private _worldRoot; private _installed; private _processSystem; /** * `PointerEvent.buttons` the synthetic pointer currently holds. A move * reports it, so a drag between a press and its release looks to a handler * the way a person's drag does. */ private _syntheticPointerButtons; /** * Screen-scope effects host — `.fx.addEffect(...)` attaches a filter to * `app.stage`, so it persists across scene transitions and composites * everything the renderer draws (every scene, every layer). Common use: * screen-wide post-process like vignette or chromatic aberration. * * The handle survives scene changes; remove it explicitly when no longer * wanted, or it lives until the renderer plugin is destroyed. */ fx: EffectsHost; private _unregisterFacetContributor; private _context; private _unregisterFullscreenListener; private _unregisterOrientationListener; private _unregisterSceneVisibility; /** * Snapshot of `TextureStyle.defaultOptions.scaleMode` captured before * `pixelArtPreset` flips it to `"nearest"`. Pixi's defaults live on a * module-level singleton, so without restoring this on destroy the * mutation leaks across plugin lifecycles — any later `RendererPlugin` * instance (or any texture loaded after teardown) silently inherits * nearest sampling even with `pixelArtPreset: false`. The companion * `_scaleModeCaptured` flag distinguishes "preset was off, leave the * field alone" from "preset was on and the original value was * `undefined`" — both round-trip back through the same restore. */ private _originalScaleMode; private _scaleModeCaptured; private _prevDefaultTextStyle; private _defaultTextStyleCaptured; constructor(config: RendererConfig); install(context: EngineContext): Promise; registerSystems(scheduler: SystemScheduler): void; onStart(): void; onDestroy(): void; /** The PixiJS Application instance. */ get application(): Application; /** The canvas element. */ get canvas(): HTMLCanvasElement; /** * The container that carries the responsive-fit transform — the natural * parent for things that should live inside the virtual play area * (transition overlays scoped to the play rect, world-space HUD, etc.). * Children operate in virtual-space pixels. * * Use `app.stage` instead when the geometry must cover the canvas * including letterbox / expand bars (full-screen dip-to-color overlays). */ get worldRoot(): DisplayContainer; /** Virtual resolution size. */ get virtualSize(): { width: number; height: number; }; /** Current canvas size in CSS pixels. Changes on host resize under responsive fit. */ get canvasSize(): { width: number; height: number; }; /** Current fit configuration. */ get fit(): RendererFitOptions; /** * Change the fit mode and/or target at runtime. Omitting `target` keeps the * element the fit currently observes, so switching mode alone does not send * the fit back to its default host. */ setFit(options: RendererFitOptions): void; /** * Convert CSS pixels relative to the canvas top-left into virtual-space * pixels. Inverts the stage transform currently applied by the fit controller. */ canvasToVirtual(x: number, y: number): Vec2; /** * Virtual-space pixels → CSS pixels relative to the canvas top-left. * Symmetric with {@link canvasToVirtual}; useful when mapping virtual * coordinates back out to DOM overlays or pointer regions. */ virtualToCanvas(x: number, y: number): Vec2; /** * Hit-test at virtual-space `(x, y)` and return `true` when the topmost * interactive Pixi container resolves to a consuming policy. The nearest * explicit boolean wins; UI without an explicit ancestor defaults to true. * The input drain uses this to claim presses landing on UI surfaces. * * Scope: this only sees surfaces marked via `markPointerConsumeContainer` — * `@yagejs/ui` primitives (`UIPanel`, `UIButton`, …) plus any visual * component (`Sprite`, `AnimatedSprite`, `Graphics`, `Text`, `SplitText`) * configured with `interactive: { consumeOnInteraction: true }`. A plain * sprite is not a consume surface. UI drawn on raw Pixi containers outside * those paths — such as the `@yagejs-addons/dialogue` box, which never * marks its containers — is not detected. Dialogue-aware callers should * gate on `DialogueController.isActive()` / `isChoosing()` instead. * * Coordinates are supplied in virtual space (matching how the input plugin * stores pointer positions); they are converted to canvas space via * `FitController.virtualToCanvas` before being forwarded to * `EventBoundary.hitTest`, which per the Pixi v8 spec expects canvas- * relative ("world space above the boundary") coordinates. At fit ratio 1 * the two spaces coincide; at any other ratio (mobile / responsive) the * conversion is required for the hit-test to land on the correct surface. */ hitTestUI(x: number, y: number): boolean; /** * The hit test behind {@link hitTestUI}, reporting what it found: the * topmost interactive container and its ancestors, innermost first, plus * their resolved consumption policy. `null` when nothing * interactive sits under `(x, y)`. * * Callers that address a container — matching it against an Inspector * user-interface snapshot, for one — need the chain rather than the * boolean. Same coordinate contract as {@link hitTestUI}: `(x, y)` is in * virtual space. */ hitTestUIPath(x: number, y: number): RendererUIHit | null; /** * Whether a frame has been drawn. Pixi roots its event boundary at the last * object rendered and drops an event silently when there is none, so a * pointer dispatched before the first frame reaches nothing. */ hasRenderedFrame(): boolean; /** * Deliver a pointer event at virtual-space `point`, so Pixi hit-tests and * delivers it the way it does for a person clicking: a `@yagejs/ui` button * runs its `onClick`, and stacking order, a disabled button's pointer mode * and clipping all apply. `button` is the mouse button a press or a release * carries; a move omits it and reports whichever buttons a press left held. * * The event goes to the canvas, never to the window: Pixi renames a * pointer-up whose composed path starts elsewhere to `pointerupoutside`, * which skips a button's click path. It bubbles, because `@yagejs/input` * listens for pointer-up on the window in the bubble phase — so the engine * receives the same event and applies it at the next drain, one frame after * the handler has already run. * * One primary mouse pointer, id {@link SYNTHETIC_POINTER_ID}. Multi-touch * is not reachable this way. */ dispatchPointerEvent(type: RendererPointerEventType, point: { x: number; y: number; }, button?: 0 | 1 | 2): void; /** * Sub-rectangle of the declared virtual space that is actually on-screen. * Use this to anchor HUD / UI that must stay inside the play area; use * {@link visibleCanvasRect} if your HUD is allowed to live in the bars. * Gameplay queries should stay on `virtualSize`. * * Under `letterbox` / `expand` / `stretch` this equals the full virtual * rect. Under `cover` the long axis is cropped by the canvas edges. */ get visibleVirtualRect(): VirtualRect; /** * Rectangles of virtual space that are currently off-screen — the * complement of {@link visibleVirtualRect} inside `virtualSize`. Use * these for effects that need to reason about cropped regions (e.g. * fog-of-war overlays at the visible boundary). * * Empty under `letterbox` / `expand` / `stretch`. Under `cover`, returns 1–2 strips. */ get croppedVirtualRects(): readonly VirtualRect[]; /** * Where the declared virtual rectangle sits on the canvas, in CSS pixels. * Use for DOM overlays positioned over the play area, cropping screenshots * to gameplay, or mapping CSS-coord hit regions. The rect may extend past * the canvas (negative coords, dimensions larger than `canvasSize`) under * `cover`. */ get virtualCanvasRect(): CanvasRect; /** * Full canvas extent expressed in virtual-space pixels — unlike * {@link visibleVirtualRect}, not clamped to the declared virtual rect. * Under `letterbox` / `expand` on an off-aspect host this extends past * `virtualSize` on the unscaled axis (useful for drawing backdrops that * fill the bars). Under `cover` it equals `visibleVirtualRect`; under * `stretch` it equals the virtual rect. */ get visibleCanvasRect(): VirtualRect; /** * Rectangles of the visible canvas OUTSIDE the declared virtual rect — * the letterbox/expand "bars" expressed in virtual-space pixels. * * Populated under `letterbox` and `expand` whenever aspect mismatches; * empty under `cover` and `stretch`. Under `expand` these are the * play-adjacent strips the game is expected to draw into (fog, parallax, * HUD). * * Under `letterbox` they only report where the background-color bars land. * Every scene layer is clipped to the virtual rect under that mode, so * content placed at these coordinates on a scene layer is invisible. To * draw in the bars, parent a container directly on `renderer.application.stage` and * position it in canvas pixels. */ get extendedVirtualRects(): readonly VirtualRect[]; /** The per-scene render tree provider. */ get sceneRenderTrees(): SceneRenderTreeProvider; /** * Bake a texture once by drawing into a temporary graphics context. The * result never changes again; for a buffer the game redraws, use * {@link createRenderTarget}. * * With `size`, the texture measures exactly that region of the drawing, * starting at `(0, 0)`: a 4-frame strip of 32-pixel cells is `width: 128, * height: 32` however much of each cell the drawing covers. Without it the * texture is the drawn bounds, so its top-left corner is the first pixel * drawn rather than `(0, 0)`, and a drawing that starts at `(10, 4)` bakes * shifted by that much. * * The pixels live only on the GPU, with no image to reload from. Calling * `update()` on the returned texture's `source` re-uploads it empty and * everything drawn from it turns transparent. */ createTexture(draw: (graphics: GraphicsContext) => void, size?: CreateTextureOptions): TextureResource; /** * Create an offscreen buffer that draws `source` into a texture the game * owns and redraws on its own schedule — the repeatable counterpart of * {@link createTexture}, with control over when the buffer refreshes and * how many texels it gets. * * Composite the result by showing `target.texture` (a `SpriteComponent`, a * mask, a filter input). See {@link RenderTargetHandle} for the coordinate * space the source is drawn in and the reasons to keep it out of the live * scene graph. */ createRenderTarget(source: DisplayContainer, options: RenderTargetOptions): RenderTargetHandle; /** Draw the current stage and pending targets without advancing time. */ render(): void; /** Capture the current stage and pending targets without advancing time. */ captureCanvas(): HTMLCanvasElement; /** * Request fullscreen for the renderer's host element. Targets the * configured `container` when present (so DOM overlays placed * alongside the canvas remain inside the fullscreened area), falling * back to the canvas itself otherwise. Wraps `Element.requestFullscreen` * with the legacy `webkitRequestFullscreen` fallback for iOS Safari. * * Must be called from a user-gesture handler (click, touch, key); * browsers reject the returned Promise with a `TypeError` otherwise. */ requestFullscreen(): Promise; /** Exit fullscreen. No-op if the page isn't currently fullscreen. */ exitFullscreen(): Promise; /** * Whether the renderer's host element is currently the fullscreen * element. Reads live from the DOM, so this stays accurate when the * user exits fullscreen via Esc or the browser UI. */ get isFullscreen(): boolean; /** * Current device orientation. Returns `null` when neither the * `screen.orientation` API nor the legacy `window.orientation` angle * is available — typical of headless tests and very old browsers. */ get orientation(): OrientationType | null; private fullscreenTarget; private installSceneVisibilityListeners; private installFullscreenListener; private installOrientationListener; private startFit; /** * Resolve the fit target. Returns `null` when no reasonable host can be * inferred — the controller then applies a one-shot transform against * `config.width × config.height` without observing. We intentionally do * NOT fall through to `document.body`: a `ResizeObserver` on `body` fires * on any page layout change (font loads, text reflows, dynamic content), * not just viewport resizes, which would silently re-layout the canvas * every frame. Callers that want full-page fit must opt in explicitly * via `fit: { target: document.body }`. */ private resolveFitTarget; } /** Service key for the RendererPlugin. */ declare const RendererKey: ServiceKey; /** * Scale mode for the responsive-fit API. * - `letterbox` — preserve aspect, fit inside the host, paint bars with the background color. * - `expand` — same scaling as `letterbox` (virtual rect always fully visible), but * the bar region is left to the game to draw into. Pair with * `RendererPlugin.extendedVirtualRects` / `visibleCanvasRect` to render fog, parallax, * or a decorative backdrop into the extra space. * - `cover` — preserve aspect, fill the host; content overflows the virtual rect on the long axis. * - `stretch` — non-uniform scale; the virtual rect is squashed/pulled to match the host. */ type FitMode = "letterbox" | "expand" | "cover" | "stretch"; /** Options for the responsive-fit API. */ interface RendererFitOptions { /** Scale mode. See {@link FitMode}. */ mode: FitMode; /** * Element whose size the canvas matches. Defaults, in order: * {@link RendererConfig.container}, then `canvas.parentElement`. If neither * is available the controller falls back to a one-shot transform against * the initial `width × height` and installs no `ResizeObserver` — pass * `{ target: document.body }` explicitly for full-page fit. */ target?: HTMLElement; } /** Configuration for the renderer plugin. */ interface RendererConfig { /** Initial canvas width in CSS pixels before responsive `fit` takes over. Backing-store size is `width × resolution`. */ width: number; /** Initial canvas height in CSS pixels before responsive `fit` takes over. Backing-store size is `height × resolution`. */ height: number; /** Game coordinate width (default: width). */ virtualWidth?: number; /** Game coordinate height (default: height). */ virtualHeight?: number; /** Background color as a hex number. */ backgroundColor?: number; /** Existing canvas element to use. */ canvas?: HTMLCanvasElement; /** Container element — canvas will be auto-appended here. */ container?: HTMLElement; /** Device pixel ratio. Defaults to `window.devicePixelRatio` for crisp rendering on HiDPI displays. Set to 1 to disable. */ resolution?: number; /** Additional PixiJS Application options. */ pixi?: Partial; /** * Responsive fit. Defaults to `{ mode: "letterbox" }` against the resolved * target (see {@link RendererFitOptions.target}), so the canvas is * responsive out of the box — override to change mode or pin to a specific * host. See {@link RendererFitOptions}. */ fit?: RendererFitOptions; /** * One-flag preset for pixel-art games. When `true`, the plugin: * * - Sets `TextureStyle.defaultOptions.scaleMode = "nearest"` so freshly * loaded textures sample without bilinear blur. * - Enables `roundPixels` on the Pixi `Application` so subpixel transforms * don't smear sprite edges. * - Applies `image-rendering: pixelated` (with `-webkit-optimize-contrast` * for Safari) to the canvas element so the browser scales the backing * store with nearest-neighbor, not bicubic. * * Composes with `pixi`: explicit `pixi.roundPixels: false` wins over the * preset. Default: `false`. */ pixelArtPreset?: boolean; /** * Engine-level default text style, applied as the base under every * `TextComponent` / `UIText` `style` (per-text values win). Lets you set an * app-wide font / fill without importing pixi to touch * `TextStyle.defaultTextStyle`. `resolution` is not a style property — pass it * per `TextComponent`. `@yagejs/ui`'s `UIPlugin({ defaultTextStyle })` * can layer a UI-only override on top. */ defaultTextStyle?: TextStyle; } export { type RendererConfig as $, type Application as A, type BlendMode as B, type ColorValue as C, type DisplayContainer as D, EffectsHost as E, type Filter as F, type GraphicsContext as G, type EffectDefinition as H, type EffectTarget as I, type EnsureLayerOptions as J, type FitMode as K, type LayerSortFn as L, type MaskFactory as M, type NineSliceSprite as N, type LayerDef as O, type LayerSpace as P, type Particle as Q, type RendererAsset as R, type SceneRenderTree as S, type TextureRef as T, type ParticleContainer as U, type PointLike as V, type WebFontHandle as W, RenderLayer as X, RenderLayerManager as Y, type RenderTargetHandle as Z, type RenderTargetOptions as _, type MaskHandle as a, type RendererFitOptions as a0, RendererKey as a1, RendererPlugin as a2, SceneRenderTreeKey as a3, SceneRenderTreeProviderKey as a4, type VirtualRect as a5, type WebFontResource as a6, defineEffect as a7, type DestroyOptions as b, type DisplaySprite as c, type DisplayText as d, type DisplayBitmapText as e, type TextStyle as f, type DisplaySplitText as g, type DisplaySplitBitmapText as h, type GradientFill as i, type TextureSliceOptions as j, type DisplayAnimatedSprite as k, type SceneRenderTreeProvider as l, type TextureInput as m, type BitmapFontHandle as n, type TextureResource as o, type TextureHandle as p, type EffectScope as q, type EffectHandle as r, type EffectFactory as s, type Mask as t, type ApplicationOptions as u, type BitmapFontResource as v, type CanvasRect as w, type CreateLayerOptions as x, type CreateTextureOptions as y, type Effect as z };