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 };