import * as _energy8platform_shell_html from '@energy8platform/shell/html';
import { Container, ApplicationOptions, Application, Texture, AnimatedSprite, Spritesheet } from 'pixi.js';
import { CasinoGameSDK, InitData, GameConfigData, SessionData } from '@energy8platform/game-sdk';
export { AnywhereWinData, BalanceData, BridgeDestroyedError, BridgeNotReadyError, GameConfigData, InitData, PaylineData, PlayParams, PlayResultData, SDKError, SessionData, SymbolData, TimeoutError, WinLineData } from '@energy8platform/game-sdk';
import { LoadingScreenConfig, AssetManifest, PlatformSession } from '@energy8platform/platform-core';
export { AssetBundle, AssetEntry, AssetManifest, LoadingScreenConfig } from '@energy8platform/platform-core';
export { DevBridge, DevBridgeConfig, ReplayConfig, ReplayLaunch } from '@energy8platform/platform-core/dev-bridge';
export { ActionDefinition, GameDefinition, LuaEngineConfig, LuaPlayResult, TransitionRule } from '@energy8platform/platform-core/lua';
declare enum ScaleMode {
/** Fit inside container, maintain aspect ratio (letterbox/pillarbox) */
FIT = "FIT",
/** Fill container, maintain aspect ratio (crop edges) */
FILL = "FILL",
/** Stretch to fill (distorts) */
STRETCH = "STRETCH"
}
declare enum Orientation {
LANDSCAPE = "landscape",
PORTRAIT = "portrait",
ANY = "any"
}
interface AudioConfig {
/** Default volumes per category (0..1) */
music?: number;
sfx?: number;
ui?: number;
ambient?: number;
/** Persist mute state in localStorage */
persist?: boolean;
/** LocalStorage key prefix */
storageKey?: string;
}
interface GameApplicationConfig {
/** Container element or CSS selector to mount canvas into */
container?: HTMLElement | string;
/** Reference design width (fallback: GameConfigData.viewport.width or 1920) */
designWidth?: number;
/** Reference design height (fallback: GameConfigData.viewport.height or 1080) */
designHeight?: number;
/** How to scale the game to fit the container */
scaleMode?: ScaleMode;
/** Preferred orientation */
orientation?: Orientation;
/** Loading screen configuration */
loading?: LoadingScreenConfig;
/** Asset manifest — what to load */
manifest?: AssetManifest;
/** Audio configuration */
audio?: AudioConfig;
/** SDK options. Set to false to disable SDK (offline/development mode) */
sdk?: {
parentOrigin?: string;
timeout?: number;
debug?: boolean;
/** Use in-memory channel instead of postMessage (no iframe required) */
devMode?: boolean;
} | false;
/** PixiJS Application options (pass-through) */
pixi?: Partial;
/** Enable debug overlay (FPS, draw calls) */
debug?: boolean;
/** When set, GameApplication mounts the branded game shell after the SDK handshake. */
shell?: _energy8platform_shell_html.ShellConfig | false;
}
interface SceneConstructor {
new (): IScene;
[key: string]: any;
}
interface IScene {
/** Root display container for this scene */
readonly container: Container;
/** @internal GameApplication reference — set by SceneManager */
__engineApp?: any;
/** Called when the scene is entered */
onEnter?(data?: unknown): Promise | void;
/** Called when the scene is exited */
onExit?(): Promise | void;
/** Called every frame */
onUpdate?(dt: number): void;
/** Called when viewport resizes */
onResize?(width: number, height: number): void;
/** Called when the scene is destroyed */
onDestroy?(): void;
}
declare enum TransitionType {
NONE = "none",
FADE = "fade",
SLIDE_LEFT = "slide-left",
SLIDE_RIGHT = "slide-right"
}
interface TransitionConfig {
type: TransitionType;
duration?: number;
easing?: (t: number) => number;
}
interface GameEngineEvents {
/** Fired when engine initialization is complete */
initialized: void;
/** Fired when all assets are loaded */
loaded: void;
/** Fired when the engine starts running */
started: void;
/** Fired on viewport resize */
resize: {
width: number;
height: number;
};
/** Fired on orientation change */
orientationChange: Orientation;
/** Fired on scene change */
sceneChange: {
from: string | null;
to: string;
};
/** Fired when player balance changes (forwarded from SDK) */
balanceUpdate: {
balance: number;
};
/** Fired on error */
error: Error;
/** Fired when engine is destroyed */
destroyed: void;
}
type EasingFunction = (t: number) => number;
interface TweenOptions {
duration: number;
easing?: EasingFunction;
delay?: number;
repeat?: number;
yoyo?: boolean;
onUpdate?: (progress: number) => void;
onComplete?: () => void;
}
/**
* Minimal typed event emitter.
* Used internally by GameApplication, SceneManager, AudioManager, etc.
*
* Supports `void` event types — events that carry no data can be emitted
* without arguments: `emitter.emit('eventName')`.
*/
declare class EventEmitter {
private listeners;
on(event: K, handler: (data: TEvents[K]) => void): this;
once(event: K, handler: (data: TEvents[K]) => void): this;
off(event: K, handler: (data: TEvents[K]) => void): this;
emit(...args: TEvents[K] extends void ? [event: K] : [event: K, data: TEvents[K]]): void;
removeAllListeners(event?: keyof TEvents): this;
}
interface SceneEntry {
scene: IScene;
key: string;
}
interface SceneManagerEvents {
change: {
from: string | null;
to: string;
};
}
/**
* Manages the scene stack and transitions between scenes.
*
* @example
* ```ts
* const scenes = new SceneManager(app.stage);
* scenes.register('loading', LoadingScene);
* scenes.register('game', GameScene);
* await scenes.goto('loading');
* ```
*/
declare class SceneManager extends EventEmitter {
private static MAX_TRANSITION_DEPTH;
/** Root container that scenes are added to */
root: Container;
private registry;
private stack;
private _transitionDepth;
/** Current viewport dimensions — set by ViewportManager */
private _width;
private _height;
/** @internal GameApplication reference — passed to scenes */
private _app;
constructor(root?: Container);
/** @internal Set the root container (called by GameApplication after PixiJS init) */
setRoot(root: Container): void;
/** @internal Set the app reference (called by GameApplication) */
setApp(app: any): void;
/** Register a scene class by key */
register(key: string, ctor: SceneConstructor): this;
/** Get the current (topmost) scene entry */
get current(): SceneEntry | null;
/** Get the current scene key */
get currentKey(): string | null;
/** Whether a scene transition is in progress */
get isTransitioning(): boolean;
/**
* Navigate to a scene, replacing the entire stack.
*/
goto(key: string, data?: unknown, transition?: TransitionConfig): Promise;
/**
* Push a scene onto the stack (the previous scene stays underneath).
* Useful for overlays, modals, pause screens.
*/
push(key: string, data?: unknown, transition?: TransitionConfig): Promise;
/**
* Pop the top scene from the stack.
*/
pop(transition?: TransitionConfig): Promise;
/**
* Replace the top scene with a new one.
*/
replace(key: string, data?: unknown, transition?: TransitionConfig): Promise;
/**
* Called every frame by GameApplication.
*/
update(dt: number): void;
/**
* Called on viewport resize.
*/
resize(width: number, height: number): void;
/**
* Destroy all scenes and clear the manager.
*/
destroy(): void;
private createScene;
private pushInternal;
private popInternal;
private transitionIn;
private transitionOut;
}
/**
* Manages game asset loading with progress tracking, bundle support, and
* automatic base path resolution from SDK's assetsUrl.
*
* Wraps PixiJS Assets API with a typed, game-oriented interface.
*
* @example
* ```ts
* const assets = new AssetManager('https://cdn.example.com/game/', manifest);
* await assets.init();
* await assets.loadBundle('preload', (p) => console.log(p));
* const texture = assets.get('hero');
* ```
*/
declare class AssetManager {
private _initialized;
private _basePath;
private _manifest;
private _loadedBundles;
constructor(basePath?: string, manifest?: AssetManifest);
/** Whether the asset system has been initialized */
get initialized(): boolean;
/** Base path for all assets (usually from SDK's assetsUrl) */
get basePath(): string;
/** Set of loaded bundle names */
get loadedBundles(): ReadonlySet;
/**
* Initialize the asset system.
* Must be called before loading any assets.
*/
init(): Promise;
/**
* Load a single bundle by name.
*
* @param name - Bundle name (must exist in the manifest)
* @param onProgress - Progress callback (0..1)
* @returns Loaded assets map
*/
loadBundle(name: string, onProgress?: (progress: number) => void): Promise>;
/**
* Load multiple bundles simultaneously.
* Progress is aggregated across all bundles.
*
* @param names - Bundle names
* @param onProgress - Progress callback (0..1)
*/
loadBundles(names: string[], onProgress?: (progress: number) => void): Promise>;
/**
* Load individual assets by URL or alias.
*
* @param urls - Asset URLs or aliases
* @param onProgress - Progress callback (0..1)
*/
load(urls: string | string[], onProgress?: (progress: number) => void): Promise;
/**
* Get a loaded asset synchronously from cache.
*
* @param alias - Asset alias
* @throws if not loaded
*/
get(alias: string): T;
/**
* Unload a bundle to free memory.
*/
unloadBundle(name: string): Promise;
/**
* Start background loading a bundle (low-priority preload).
* Useful for loading bonus round assets while player is in base game.
*/
backgroundLoad(name: string): Promise;
/**
* Get all bundle names from the manifest.
*/
getBundleNames(): string[];
/**
* Check if a bundle is loaded.
*/
isBundleLoaded(name: string): boolean;
private ensureInitialized;
}
type AudioCategoryName = 'music' | 'sfx' | 'ui' | 'ambient';
/**
* Manages all game audio: music, SFX, UI sounds, ambient.
*
* Optional dependency on @pixi/sound — if not installed, AudioManager
* operates as a silent no-op (graceful degradation).
*
* Features:
* - Per-category volume control (music, sfx, ui, ambient)
* - Music crossfade and looping
* - Mobile audio unlock on first interaction
* - Mute state persistence in localStorage
* - Global mute/unmute
*
* @example
* ```ts
* const audio = new AudioManager({ music: 0.5, sfx: 0.8 });
* await audio.init();
* audio.playMusic('bg-music');
* audio.play('spin-click', 'sfx');
* ```
*/
declare class AudioManager {
private _soundModule;
private _initialized;
private _globalMuted;
private _persist;
private _storageKey;
private _categories;
private _masterGain;
private _currentMusic;
/** Duck factor (0..1) from duckMusic/unduckMusic. A presentation state, not a player setting. */
private _musicDuck;
/** Crossfade ramp (0..1) for the track that is fading IN. 1 whenever no fade is running. */
private _musicFade;
/** Generation counter so a superseded crossfade ramp stops writing over the new track's. */
private _musicFadeToken;
private _unlocked;
private _unlockHandler;
constructor(config?: AudioConfig);
/** Whether the audio system is initialized */
get initialized(): boolean;
/** Whether audio is globally muted */
get muted(): boolean;
/**
* Initialize the audio system.
* Dynamically imports @pixi/sound to keep it optional.
*/
init(): Promise;
/**
* Play a sound effect.
*
* @param alias - Sound alias (must be loaded via AssetManager)
* @param category - Audio category (default: 'sfx')
* @param options - Additional play options
*/
play(alias: string, category?: AudioCategoryName, options?: {
volume?: number;
loop?: boolean;
speed?: number;
}): void;
/**
* Play background music with optional crossfade.
*
* @param alias - Music alias
* @param fadeDuration - Crossfade duration in ms (default: 500)
*/
playMusic(alias: string, fadeDuration?: number): void;
/**
* Stop current music.
*/
stopMusic(): void;
/**
* Stop all sounds.
*/
stopAll(): void;
/** Global gain (0..1) folded into every category's effective volume. Driven by the shell's
* 'master' settingChange. Does not affect the persisted per-category volumes. */
setMasterVolume(volume: number): void;
getMasterVolume(): number;
/**
* Set volume for a category.
*/
setVolume(category: AudioCategoryName, volume: number): void;
/**
* Get volume for a category.
*/
getVolume(category: AudioCategoryName): number;
/**
* Mute a specific category.
*/
muteCategory(category: AudioCategoryName): void;
/**
* Unmute a specific category.
*/
unmuteCategory(category: AudioCategoryName): void;
/**
* Toggle mute for a category.
*/
toggleCategory(category: AudioCategoryName): boolean;
/**
* Mute all audio globally.
*/
muteAll(): void;
/**
* Unmute all audio globally.
*/
unmuteAll(): void;
/**
* Toggle global mute.
*/
toggleMute(): boolean;
/**
* Duck music volume (e.g., during big win presentation).
*
* @param factor - Volume multiplier (0..1), e.g. 0.3 = 30% of normal
*/
duckMusic(factor: number): void;
/**
* Restore music to normal volume after ducking.
*/
unduckMusic(): void;
/**
* Destroy the audio manager and free resources.
*/
destroy(): void;
/**
* Smoothly fade a sound's volume from `fromVol` to `toVol` over `durationMs`.
*/
private fadeVolume;
/**
* The SOUND-layer gain for the running music track.
*
* @pixi/sound resolves a playing instance as `instance × sound × global` (WebAudioInstance.
* refresh). Each of those three has exactly ONE owner here, which is what keeps the mixer honest:
* global — the master gain (`applyVolumes`)
* sound — music: this function. sfx: untouched, left at 1.
* instance — sfx: the per-call volume × the sfx category. music: always 1.
* Everything that can move music volume — the player's slider, the category mute, a big-win duck,
* a crossfade — is a term below, so they compose instead of overwriting each other.
*/
private musicGain;
/** Push `musicGain()` at the current track. Safe before it starts playing and with none playing. */
private applyMusicGain;
/** Current SOUND-layer volume of `alias`, or 0 when it cannot be read. */
private soundVolumeOf;
/** Ramp the crossfade term 0 → 1 over `durationMs`, recomposing the gain each frame so a slider
* drag or a duck landing mid-fade is honoured rather than overwritten when the fade ends. */
private rampMusicFade;
private applyVolumes;
private setupMobileUnlock;
private removeMobileUnlock;
private saveState;
private restoreState;
}
interface InputEvents {
tap: {
x: number;
y: number;
};
press: {
x: number;
y: number;
};
release: {
x: number;
y: number;
};
move: {
x: number;
y: number;
};
swipe: {
direction: 'up' | 'down' | 'left' | 'right';
velocity: number;
};
keydown: {
key: string;
code: string;
};
keyup: {
key: string;
code: string;
};
}
/**
* Unified input manager for touch, mouse, and keyboard.
*
* Features:
* - Unified pointer events (works with touch + mouse)
* - Swipe gesture detection
* - Keyboard input with isKeyDown state
* - Input locking (block input during animations)
*
* @example
* ```ts
* const input = new InputManager(app.canvas);
*
* input.on('tap', ({ x, y }) => console.log('Tapped at', x, y));
* input.on('swipe', ({ direction }) => console.log('Swiped', direction));
* input.on('keydown', ({ key }) => {
* if (key === ' ') spin();
* });
*
* // Block input during animations
* input.lock();
* await playAnimation();
* input.unlock();
* ```
*/
declare class InputManager extends EventEmitter {
private _canvas;
private _locked;
private _keysDown;
private _destroyed;
private _viewportScale;
private _viewportOffsetX;
private _viewportOffsetY;
private _pointerStart;
private _swipeThreshold;
private _swipeMaxTime;
constructor(canvas: HTMLCanvasElement);
/** Whether input is currently locked */
get locked(): boolean;
/** Lock all input (e.g., during animations) */
lock(): void;
/** Unlock input */
unlock(): void;
/** Check if a key is currently pressed */
isKeyDown(key: string): boolean;
/**
* Update the viewport transform used for DOM→world coordinate mapping.
* Called automatically by GameApplication when ViewportManager emits resize.
*/
setViewportTransform(scale: number, offsetX: number, offsetY: number): void;
/**
* Convert a DOM canvas position to game-world coordinates,
* accounting for viewport scaling and offset.
*/
getWorldPosition(canvasX: number, canvasY: number): {
x: number;
y: number;
};
/** Destroy the input manager */
destroy(): void;
private setupPointerEvents;
private onPointerDown;
private onPointerUp;
private onPointerMove;
private getCanvasPosition;
private setupKeyboardEvents;
private onKeyDown;
private onKeyUp;
}
interface ViewportConfig {
designWidth: number;
designHeight: number;
scaleMode: ScaleMode;
orientation: Orientation;
}
interface ViewportEvents {
resize: {
width: number;
height: number;
scale: number;
};
orientationChange: Orientation;
}
/**
* Manages responsive scaling of the game canvas to fit its container.
*
* Supports three scale modes:
* - **FIT** — letterbox/pillarbox to maintain aspect ratio (industry standard)
* - **FILL** — fill container, crop edges
* - **STRETCH** — stretch to fill (distorts)
*
* Also handles:
* - Orientation detection (landscape/portrait)
* - Safe areas (mobile notch)
* - ResizeObserver for smooth container resizing
*
* @example
* ```ts
* const viewport = new ViewportManager(app, container, {
* designWidth: 1920,
* designHeight: 1080,
* scaleMode: ScaleMode.FIT,
* orientation: Orientation.LANDSCAPE,
* });
*
* viewport.on('resize', ({ width, height, scale }) => {
* console.log(`New size: ${width}x${height} @ ${scale}x`);
* });
* ```
*/
declare class ViewportManager extends EventEmitter {
private _app;
private _container;
private _config;
private _target;
private _resizeObserver;
private _currentOrientation;
private _currentWidth;
private _currentHeight;
private _currentScale;
private _destroyed;
private _resizeTimeout;
constructor(app: Application, container: HTMLElement, config: ViewportConfig, target?: Container);
/** Current canvas width in game units */
get width(): number;
/** Current canvas height in game units */
get height(): number;
/** Current scale factor */
get scale(): number;
/** Current orientation */
get orientation(): Orientation;
/** Design reference width */
get designWidth(): number;
/** Design reference height */
get designHeight(): number;
/**
* Force a resize calculation. Called automatically on container size change.
*/
refresh(): void;
/**
* Destroy the viewport manager.
*/
destroy(): void;
private setupObserver;
private onWindowResize;
private debouncedRefresh;
}
/**
* FPS overlay for debugging performance.
*
* Shows FPS, frame time, and draw call count in the corner of the screen.
*
* @example
* ```ts
* const fps = new FPSOverlay(app);
* fps.show();
* ```
*/
declare class FPSOverlay {
private _app;
private _container;
private _fpsText;
private _visible;
private _samples;
private _maxSamples;
private _lastUpdate;
private _tickFn;
constructor(app: Application);
/** Show the FPS overlay */
show(): void;
/** Hide the FPS overlay */
hide(): void;
/** Toggle visibility */
toggle(): void;
/** Destroy the overlay */
destroy(): void;
}
/**
* The main entry point for a game built on @energy8platform/game-engine.
*
* Orchestrates the full lifecycle:
* 1. Create PixiJS Application
* 2. Initialize SDK (or run offline)
* 3. Show CSS preloader → Canvas loading screen with progress bar
* 4. Load asset manifest
* 5. Transition to the first game scene
*
* @example
* ```ts
* import { GameApplication, ScaleMode } from '@energy8platform/game-engine';
* import { GameScene } from './scenes/GameScene';
*
* const game = new GameApplication({
* container: '#game',
* designWidth: 1920,
* designHeight: 1080,
* scaleMode: ScaleMode.FIT,
* manifest: { bundles: [
* { name: 'preload', assets: [{ alias: 'logo', src: 'logo.png' }] },
* { name: 'game', assets: [{ alias: 'bg', src: 'background.png' }] },
* ]},
* loading: { tapToStart: true },
* });
*
* game.scenes.register('game', GameScene);
* await game.start('game');
* ```
*/
declare class GameApplication extends EventEmitter {
/** PixiJS Application instance */
app: Application;
/** Scene manager */
scenes: SceneManager;
/** Asset manager */
assets: AssetManager;
/** Audio manager */
audio: AudioManager;
/** Input manager */
input: InputManager;
/** Viewport manager */
viewport: ViewportManager;
/** Scaled world root (holds scenes). Transformed by the ViewportManager to fit the design
* resolution; lives below the UI layer on app.stage. */
worldRoot: Container;
/** Unscaled, screen-space UI layer. Sits above {@link worldRoot} and is NOT touched by the
* viewport transform — children fill the real screen (e.g. the host's shell + overlay). */
uiLayer: Container;
/** SDK instance (null in offline mode) */
sdk: CasinoGameSDK | null;
/** FPS overlay instance (only when debug: true) */
fpsOverlay: FPSOverlay | null;
/** Data received from SDK initialization */
initData: InitData | null;
/** Platform session (SDK + optional DevBridge). null until start() runs. */
platformSession: PlatformSession | null;
/** Branded game shell (only when config.shell is set). */
shell?: _energy8platform_shell_html.GameShell;
/** Configuration */
readonly config: GameApplicationConfig;
private _running;
private _destroyed;
private _container;
constructor(config?: GameApplicationConfig);
/** Current game config from SDK (or null in offline mode) */
get gameConfig(): GameConfigData | null;
/** Current session data */
get session(): SessionData | null;
/** Current balance */
get balance(): number;
/** Current currency */
get currency(): string;
/** Whether the engine is running */
get isRunning(): boolean;
/**
* Start the game engine. This is the main entry point.
*
* @param firstScene - Key of the first scene to show after loading (must be registered)
* @param sceneData - Optional data to pass to the first scene's onEnter
*/
start(firstScene: string, sceneData?: unknown): Promise;
/**
* Destroy the engine and free all resources.
*/
destroy(): Promise;
private resolveContainer;
private initPixi;
private initSDK;
private applySDKConfig;
private initSubSystems;
private loadAssets;
}
/**
* Base class for all scenes.
* Provides a root PixiJS Container and lifecycle hooks.
*
* @example
* ```ts
* class MenuScene extends Scene {
* async onEnter() {
* const bg = Sprite.from('menu-bg');
* this.container.addChild(bg);
* }
*
* onUpdate(dt: number) {
* // per-frame logic
* }
*
* onResize(width: number, height: number) {
* // reposition UI
* }
* }
* ```
*/
declare abstract class Scene implements IScene {
readonly container: Container;
constructor();
/** Called when this scene becomes active. Override in subclass. */
onEnter?(data?: unknown): Promise | void;
/** Called when this scene is deactivated. Override in subclass. */
onExit?(): Promise | void;
/** Called every frame with delta time (in seconds). Override in subclass. */
onUpdate?(dt: number): void;
/** Called when the viewport resizes. Override in subclass. */
onResize?(width: number, height: number): void;
/** Cleanup — called when the scene is permanently removed. */
onDestroy?(): void;
}
/**
* Lightweight tween system integrated with PixiJS Ticker.
* Zero external dependencies — no GSAP required.
*
* All tweens return a Promise that resolves on completion.
*
* @example
* ```ts
* // Fade in a sprite
* await Tween.to(sprite, { alpha: 1, y: 100 }, 500, Easing.easeOutBack);
*
* // Move and wait
* await Tween.to(sprite, { x: 500 }, 300);
*
* // From a starting value
* await Tween.from(sprite, { scale: 0, alpha: 0 }, 400);
* ```
*/
declare class Tween {
private static _tweens;
private static _tickerAdded;
/**
* Animate properties from current values to target values.
*
* @param target - Object to animate (Sprite, Container, etc.)
* @param props - Target property values
* @param duration - Duration in milliseconds
* @param easing - Easing function (default: easeOutQuad)
* @param onUpdate - Progress callback (0..1)
*/
static to(target: any, props: Record, duration: number, easing?: EasingFunction, onUpdate?: (progress: number) => void): Promise;
/**
* Animate properties from given values to current values.
*/
static from(target: any, props: Record, duration: number, easing?: EasingFunction, onUpdate?: (progress: number) => void): Promise;
/**
* Animate from one set of values to another.
*/
static fromTo(target: any, fromProps: Record, toProps: Record, duration: number, easing?: EasingFunction, onUpdate?: (progress: number) => void): Promise;
/**
* Wait for a given duration (useful in timelines).
* Uses PixiJS Ticker for consistent timing with other tweens.
*/
static delay(ms: number): Promise;
/**
* Kill all tweens on a target.
*/
static killTweensOf(target: any): void;
/**
* Kill all active tweens.
*/
static killAll(): void;
/** Number of active tweens */
static get activeTweens(): number;
/**
* Reset the tween system — kill all tweens and remove the ticker.
* Useful for cleanup between game instances, tests, or hot-reload.
*/
static reset(): void;
private static ensureTicker;
private static tick;
/**
* Get a potentially nested property (supports 'scale.x', 'position.y', etc.)
*/
private static getProperty;
/**
* Set a potentially nested property.
*/
private static setProperty;
}
/**
* Sequential/parallel animation timeline built on top of Tween.
*
* @example
* ```ts
* const timeline = new Timeline();
*
* // Sequential: one after another
* timeline
* .to(sprite1, { alpha: 1 }, 300)
* .to(sprite2, { x: 200 }, 500)
* .delay(200)
* .call(() => console.log('done'));
*
* // Parallel: all at once
* timeline.parallel(
* () => Tween.to(sprite1, { x: 100 }, 300),
* () => Tween.to(sprite2, { y: 200 }, 300),
* );
*
* await timeline.play();
* ```
*/
declare class Timeline {
private _steps;
private _playing;
private _cancelled;
/**
* Add a tween step (animate to target values).
*/
to(target: any, props: Record, duration: number, easing?: EasingFunction): this;
/**
* Add a tween step (animate from given values).
*/
from(target: any, props: Record, duration: number, easing?: EasingFunction): this;
/**
* Add a delay step.
*/
delay(ms: number): this;
/**
* Add a callback step.
*/
call(fn: () => void | Promise): this;
/**
* Add a parallel step — all functions run simultaneously,
* step completes when all are done.
*/
parallel(...fns: Array<() => Promise>): this;
/**
* Play the timeline from start.
* Returns a promise that resolves when all steps complete.
*/
play(): Promise;
/**
* Cancel the timeline.
*/
cancel(): void;
/**
* Clear all steps.
*/
clear(): this;
/** Whether the timeline is currently playing */
get isPlaying(): boolean;
}
/**
* Collection of easing functions for use with Tween and Timeline.
*
* All functions take a progress value t (0..1) and return the eased value.
*/
declare const Easing: Record & {
readonly linear: EasingFunction;
readonly easeInQuad: EasingFunction;
readonly easeOutQuad: EasingFunction;
readonly easeInOutQuad: EasingFunction;
readonly easeInCubic: EasingFunction;
readonly easeOutCubic: EasingFunction;
readonly easeInOutCubic: EasingFunction;
readonly easeInQuart: EasingFunction;
readonly easeOutQuart: EasingFunction;
readonly easeInOutQuart: EasingFunction;
readonly easeInSine: EasingFunction;
readonly easeOutSine: EasingFunction;
readonly easeInOutSine: EasingFunction;
readonly easeInExpo: EasingFunction;
readonly easeOutExpo: EasingFunction;
readonly easeInOutExpo: EasingFunction;
readonly easeInBack: EasingFunction;
readonly easeOutBack: EasingFunction;
readonly easeInOutBack: EasingFunction;
readonly easeOutBounce: EasingFunction;
readonly easeInBounce: EasingFunction;
readonly easeInOutBounce: EasingFunction;
readonly easeOutElastic: EasingFunction;
readonly easeInElastic: EasingFunction;
};
/**
* Helper for working with Spine animations in PixiJS v8.
*
* Optional — only works if @esotericsoftware/spine-pixi-v8 is installed.
* Provides convenience methods for creating and playing Spine animations.
*
* @example
* ```ts
* // Load spine assets via AssetManager first
* const spine = SpineHelper.create('char-data', 'char-atlas');
* app.stage.addChild(spine);
* await SpineHelper.playAnimation(spine, 'idle', true);
* await SpineHelper.playAnimation(spine, 'win');
* ```
*/
declare class SpineHelper {
private static _SpineClass;
private static _loaded;
/**
* Ensure the Spine module is loaded.
* Called automatically by create/playAnimation.
*/
static ensureLoaded(): Promise;
/**
* Create a Spine display object.
*
* @param skeletonAlias - Alias of the loaded .skel/.json asset
* @param atlasAlias - Alias of the loaded .atlas asset
* @param options - Additional Spine options
* @returns Spine container (or null if Spine is not available)
*/
static create(skeletonAlias: string, atlasAlias: string, options?: {
scale?: number;
autoUpdate?: boolean;
}): Promise;
/**
* Play a named animation on a Spine object.
* Returns a promise that resolves when the animation completes.
* For looping animations, the promise never resolves.
*
* @param spine - Spine display object
* @param animationName - Name of the animation
* @param loop - Whether to loop (default: false)
* @param track - Animation track (default: 0)
*/
static playAnimation(spine: any, animationName: string, loop?: boolean, track?: number): Promise;
/**
* Queue an animation after the current one finishes.
*
* @param spine - Spine display object
* @param animationName - Animation name
* @param delay - Mix duration / delay before starting
* @param loop - Loop the queued animation
* @param track - Animation track
*/
static addAnimation(spine: any, animationName: string, delay?: number, loop?: boolean, track?: number): void;
/**
* Get all animation names available on a Spine skeleton.
*/
static getAnimationNames(spine: any): string[];
/**
* Get all skin names available on a Spine skeleton.
*/
static getSkinNames(spine: any): string[];
/**
* Set the active skin on a Spine skeleton.
*/
static setSkin(spine: any, skinName: string): void;
}
interface SpriteAnimationConfig {
/** Frames per second (default: 24) */
fps?: number;
/** Whether to loop (default: true) */
loop?: boolean;
/** Start playing immediately (default: true) */
autoPlay?: boolean;
/** Callback when animation completes (non-looping) */
onComplete?: () => void;
/** Anchor point (default: 0.5 = center) */
anchor?: number | {
x: number;
y: number;
};
}
/**
* Helper for creating frame-based animations from spritesheets.
*
* Wraps PixiJS `AnimatedSprite` with a convenient API for
* common iGaming effects: coin showers, symbol animations,
* sparkle trails, win celebrations.
*
* Cheaper than Spine for simple frame sequences.
*
* @example
* ```ts
* // From an array of textures
* const coinAnim = SpriteAnimation.create(coinTextures, {
* fps: 30,
* loop: true,
* });
* scene.addChild(coinAnim);
*
* // From a spritesheet with a naming pattern
* const sheet = Assets.get('effects');
* const sparkle = SpriteAnimation.fromSpritesheet(sheet, 'sparkle_');
* sparkle.play();
*
* // From a numbered range
* const explosion = SpriteAnimation.fromRange(sheet, 'explosion_{i}', 0, 24, {
* fps: 60,
* loop: false,
* onComplete: () => explosion.destroy(),
* });
* ```
*/
declare class SpriteAnimation {
/**
* Create an animated sprite from an array of textures.
*
* @param textures - Array of PixiJS Textures
* @param config - Animation options
* @returns Configured AnimatedSprite
*/
static create(textures: Texture[], config?: SpriteAnimationConfig): AnimatedSprite;
/**
* Create an animated sprite from a spritesheet using a name prefix.
*
* Collects all textures whose keys start with `prefix`, sorted alphabetically.
*
* @param sheet - PixiJS Spritesheet instance
* @param prefix - Texture name prefix (e.g., 'coin_')
* @param config - Animation options
* @returns Configured AnimatedSprite
*/
static fromSpritesheet(sheet: Spritesheet, prefix: string, config?: SpriteAnimationConfig): AnimatedSprite;
/**
* Create an animated sprite from a numbered range of frames.
*
* The `pattern` string should contain `{i}` as a placeholder for the frame number.
* Numbers are zero-padded to match the length of `start`.
*
* @param sheet - PixiJS Spritesheet instance
* @param pattern - Frame name pattern, e.g. 'explosion_{i}'
* @param start - Start frame index (inclusive)
* @param end - End frame index (inclusive)
* @param config - Animation options
* @returns Configured AnimatedSprite
*/
static fromRange(sheet: Spritesheet, pattern: string, start: number, end: number, config?: SpriteAnimationConfig): AnimatedSprite;
/**
* Create an AnimatedSprite from texture aliases (loaded via AssetManager).
*
* @param aliases - Array of texture aliases
* @param config - Animation options
* @returns Configured AnimatedSprite
*/
static fromAliases(aliases: string[], config?: SpriteAnimationConfig): AnimatedSprite;
/**
* Play a one-shot animation and auto-destroy when complete.
* Useful for fire-and-forget effects like coin bursts.
*
* @param textures - Array of textures
* @param config - Animation options (loop will be forced to false)
* @returns Promise that resolves when animation completes
*/
static playOnce(textures: Texture[], config?: SpriteAnimationConfig): {
sprite: AnimatedSprite;
finished: Promise;
};
/**
* Get all textures from a spritesheet that start with a given prefix.
* Results are sorted alphabetically by key.
*/
static getTexturesByPrefix(sheet: Spritesheet, prefix: string): Texture[];
}
/**
* Built-in loading screen.
*
* It does NOT render its own overlay — the CSS preloader created at boot
* (`createPlatformSession`/`GameApplication.start`) stays on screen, and this
* scene merely drives it: asset-load progress → `setCSSPreloaderProgress`,
* tap-to-start → `waitCSSPreloaderTap`, then fades it out via
* `removeCSSPreloader` before entering the game. One continuous overlay from
* boot to gameplay — no second logo, no mid-load flash.
*
* When the game supplied its own overlay (`loading.externalOverlay`, e.g.
* Artube's `LoaderViewController`), this scene is also the HAND-OVER point: that
* overlay covered the gap this scene's existence ends — the bundle download,
* Pixi init and the SDK handshake, none of which the engine can paint over. The
* first thing `onEnter` does is mount the preloader, wait for it to be painted,
* and dismiss the game's overlay. Everything after that line is identical on
* every platform.
*/
declare class LoadingScene extends Scene {
private _engine;
private _targetScene;
private _targetData?;
private _config;
private _displayedProgress;
private _targetProgress;
private _loadingComplete;
private _startTime;
onEnter(data?: unknown): Promise;
onUpdate(dt: number): void;
onResize(_width: number, _height: number): void;
onDestroy(): void;
/**
* Swap a game-supplied loading overlay for the engine's own loading screen.
*
* The overlay (Artube's) has been on screen since before this bundle was fetched, covering a gap
* nothing of ours could. Its job ends here, at the first frame the engine paints; the player then
* gets the game's own brand, progress bar and tap-to-start, exactly as on every other target.
*
* The order of the four steps is the whole design, and each is wrong on its own:
*
* 0. Wait out whatever the overlay is still owed on screen (`externalOverlayMinDisplayTime`,
* default 1.5s, plus room for a phase crossfade already in flight). The gap this overlay
* covers can be under half a second, which is not long enough for a partner's brand to
* register. Waiting here — BEFORE mounting ours — rather than after is what keeps the two
* screens' timelines from overlapping: our splash and brand floor start when the player can
* actually see them, not behind someone else's overlay. On any boot slower than the floor
* this step costs nothing, and on a non-Artube target it is not reached at all.
* 1. Mount the preloader, opaque and full-bleed, while theirs is still up. Both are on screen
* together for a few frames, so there is never a moment with neither, whatever happens next.
* 2. Wait for that frame to actually be PAINTED — mounting only queues it. Dismissing theirs
* before the paint is precisely the flash of bare background this ordering exists to avoid.
* Two `requestAnimationFrame`s: the first callback runs before the frame it belongs to is
* composited, the second after. Two frames is also enough for Pixi's own rAF-driven ticker
* to have rendered this scene at least once, so "the loading scene has painted" is literally
* true by the time step 3 runs.
* 3. Only then dismiss theirs. Their `hideLoader()` plays a 0.3s fade and removes the element.
* Not waiting for that fade is deliberate — it is an animation on someone else's element,
* and blocking a boot on it would be a hang waiting to happen.
*
* Which of the two is visually on top is the host page's business, not ours, and it does NOT
* change the guarantee. On a typical game page (`#game { position: fixed; inset: 0 }`) the fixed
* container establishes a stacking context, so the preloader's z-index is scoped inside it and
* Artube's `position: fixed; z-index: 9999` sits above — their fade then crossfades onto our
* loading screen, which is what was observed live and looks right. On a page where ours wins
* instead, their fade simply plays underneath, unseen. Either way the seam is covered, because
* what step 2 buys is that OUR screen is already painted before theirs starts going away.
*/
private takeOverFromExternalOverlay;
/** Resolves after the browser has composited at least one frame (see step 2 above). */
private nextPaint;
private updateLoaderBar;
/**
* Smoothly animate the displayed progress from its current value to `target`
* over `durationMs` using an easeOutCubic curve.
*/
private animateProgressTo;
/** The DOM element hosting the canvas + preloader overlay. */
private hostElement;
private transitionToGame;
}
export { AssetManager, AudioManager, Easing, EventEmitter, FPSOverlay, GameApplication, InputManager, LoadingScene, Orientation, ScaleMode, Scene, SceneManager, SpineHelper, SpriteAnimation, Timeline, TransitionType, Tween, ViewportManager };
export type { AudioConfig, EasingFunction, GameApplicationConfig, GameEngineEvents, IScene, SceneConstructor, SpriteAnimationConfig, TransitionConfig, TweenOptions };