{"version":3,"file":"SpineSymbol-Fe0Fz1JW.cjs","names":[],"sources":["../src/symbols/SpineSymbol.ts"],"sourcesContent":["import { ReelSymbol } from './ReelSymbol.js';\n\n// Spine types imported dynamically. this is an optional peer dependency\nlet SpineClass: any = null;\n\n// Kick off the optional import at module init and keep the promise so callers\n// can wait for it. Without this, constructing a SpineSymbol on a cold start\n// (before the dynamic import resolves) throws the \"not installed\" error even\n// when Spine IS installed - it just hadn't finished loading yet.\nconst spineReady: Promise<void> = (async () => {\n  try {\n    const spineModule = await import('@esotericsoftware/spine-pixi-v8');\n    SpineClass = spineModule.Spine;\n  } catch {\n    // Spine not available. SpineSymbol will throw on construction.\n  }\n})();\n\n/**\n * Resolves once the optional `@esotericsoftware/spine-pixi-v8` import has\n * settled (whether or not it was installed). Await this before constructing\n * `SpineSymbol`s if you build the reel set immediately on app start, so a cold\n * load can't throw the misleading \"not installed\" error.\n */\nexport function whenSpineReady(): Promise<void> {\n  return spineReady;\n}\n\nexport interface SpineSymbolOptions {\n  /** Map of symbolId → SkeletonData. */\n  skeletonDataMap: Record<string, any>;\n  /** Default animation name to play in idle. Default: 'idle'. */\n  idleAnimation?: string;\n  /** Animation name to play on win. Default: 'win'. */\n  winAnimation?: string;\n  /** Default skin name. Default: 'default'. */\n  defaultSkin?: string;\n  /**\n   * Fail loud on unmapped animations. When `true`, activating or winning with\n   * a configured animation name that the skeleton doesn't define throws instead\n   * of silently showing nothing (catches typos like `ide` vs `idle`). Default\n   * `false` to preserve the lenient behavior. See ADR 011.\n   */\n  strict?: boolean;\n}\n\n/**\n * Symbol implementation using Spine 2D skeletal animation.\n *\n * Requires `@esotericsoftware/spine-pixi-v8` as an optional peer dependency.\n * If Spine is not installed, constructing a SpineSymbol will throw.\n */\nexport class SpineSymbol extends ReelSymbol {\n  private _spine: any = null;\n  private _skeletonDataMap: Record<string, any>;\n  private _idleAnimation: string;\n  private _winAnimation: string;\n  private _defaultSkin: string;\n  private _winResolve: (() => void) | null = null;\n  private _currentSkeletonKey: string = '';\n  private _strict: boolean;\n\n  constructor(options: SpineSymbolOptions) {\n    super();\n    if (!SpineClass) {\n      throw new Error(\n        'SpineSymbol could not use @esotericsoftware/spine-pixi-v8. Either it is ' +\n        'not installed (npm install @esotericsoftware/spine-pixi-v8), or its ' +\n        'dynamic import has not resolved yet on a cold start — await ' +\n        'whenSpineReady() before constructing SpineSymbols.',\n      );\n    }\n    this._skeletonDataMap = options.skeletonDataMap;\n    this._idleAnimation = options.idleAnimation ?? 'idle';\n    this._winAnimation = options.winAnimation ?? 'win';\n    this._defaultSkin = options.defaultSkin ?? 'default';\n    this._strict = options.strict ?? false;\n  }\n\n  protected onActivate(symbolId: string): void {\n    const skeletonData = this._skeletonDataMap[symbolId];\n    if (!skeletonData) return;\n\n    // Reuse existing spine if same skeleton data\n    if (this._currentSkeletonKey !== symbolId) {\n      if (this._spine) {\n        this.view.removeChild(this._spine);\n        this._spine.destroy();\n      }\n      this._spine = new SpineClass({ skeletonData });\n      this.view.addChild(this._spine);\n      this._currentSkeletonKey = symbolId;\n    }\n\n    if (this._spine.skeleton.data.findSkin(this._defaultSkin)) {\n      this._spine.skeleton.setSkinByName(this._defaultSkin);\n      this._spine.skeleton.setSlotsToSetupPose();\n    }\n    if (this._spine.skeleton.data.findAnimation(this._idleAnimation)) {\n      this._spine.state.setAnimation(0, this._idleAnimation, true);\n    } else if (this._strict) {\n      throw new Error(\n        `SpineSymbol(strict): idle animation '${this._idleAnimation}' not found ` +\n        `on skeleton '${symbolId}'.`,\n      );\n    }\n  }\n\n  protected onDeactivate(): void {\n    if (this._spine) {\n      this._spine.state.clearListeners();\n      this._spine.state.clearTracks();\n    }\n    // Settle any pending playWin so awaiters don't hang when the symbol is\n    // recycled mid-animation. Mirrors the same fix in SpineReelSymbol.\n    if (this._winResolve) {\n      const r = this._winResolve;\n      this._winResolve = null;\n      r();\n    }\n  }\n\n  async playWin(): Promise<void> {\n    if (!this._spine) return;\n    if (!this._spine.skeleton.data.findAnimation(this._winAnimation)) {\n      if (this._strict) {\n        throw new Error(\n          `SpineSymbol(strict): win animation '${this._winAnimation}' not found ` +\n          `on skeleton '${this._currentSkeletonKey}'.`,\n        );\n      }\n      return;\n    }\n\n    return new Promise<void>((resolve) => {\n      this._winResolve = resolve;\n      const entry = this._spine.state.setAnimation(0, this._winAnimation, false);\n      this._spine.state.addListener({\n        complete: (trackEntry: any) => {\n          if (trackEntry === entry) {\n            this._spine.state.clearListeners();\n            if (this._spine.skeleton.data.findAnimation(this._idleAnimation)) {\n              this._spine.state.setAnimation(0, this._idleAnimation, true);\n            }\n            this._winResolve = null;\n            resolve();\n          }\n        },\n      });\n    });\n  }\n\n  stopAnimation(): void {\n    if (!this._spine) return;\n    this._spine.state.clearListeners();\n    if (this._spine.skeleton.data.findAnimation(this._idleAnimation)) {\n      this._spine.state.setAnimation(0, this._idleAnimation, true);\n    }\n    if (this._winResolve) {\n      this._winResolve();\n      this._winResolve = null;\n    }\n  }\n\n  resize(width: number, height: number): void {\n    if (!this._spine) return;\n    const bounds = this._spine.getBounds();\n    if (bounds.width > 0 && bounds.height > 0) {\n      this._spine.scale.set(\n        width / bounds.width,\n        height / bounds.height,\n      );\n    }\n  }\n\n  protected override onDestroy(): void {\n    if (this._spine) {\n      this._spine.state.clearListeners();\n      this._spine.destroy();\n      this._spine = null;\n    }\n    if (this._winResolve) {\n      const r = this._winResolve;\n      this._winResolve = null;\n      r();\n    }\n  }\n}\n"],"mappings":"6CAGA,IAAI,EAAkB,KAMhB,GAA6B,SAAY,CAC7C,GAAI,CAEF,GADoB,MAAM,OAAO,oCACR,WACnB,MAGN,CAQJ,SAAgB,GAAgC,CAC9C,OAAO,EA2BT,IAAa,EAAb,cAAiC,EAAA,CAAW,CAC1C,OAAsB,KACtB,iBACA,eACA,cACA,aACA,YAA2C,KAC3C,oBAAsC,GACtC,QAEA,YAAY,EAA6B,CAEvC,GADA,OAAO,CACH,CAAC,EACH,MAAU,MACR,6PAID,CAEH,KAAK,iBAAmB,EAAQ,gBAChC,KAAK,eAAiB,EAAQ,eAAiB,OAC/C,KAAK,cAAgB,EAAQ,cAAgB,MAC7C,KAAK,aAAe,EAAQ,aAAe,UAC3C,KAAK,QAAU,EAAQ,QAAU,GAGnC,WAAqB,EAAwB,CAC3C,IAAM,EAAe,KAAK,iBAAiB,GACtC,KAiBL,IAdI,KAAK,sBAAwB,IAC3B,KAAK,SACP,KAAK,KAAK,YAAY,KAAK,OAAO,CAClC,KAAK,OAAO,SAAS,EAEvB,KAAK,OAAS,IAAI,EAAW,CAAE,eAAc,CAAC,CAC9C,KAAK,KAAK,SAAS,KAAK,OAAO,CAC/B,KAAK,oBAAsB,GAGzB,KAAK,OAAO,SAAS,KAAK,SAAS,KAAK,aAAa,GACvD,KAAK,OAAO,SAAS,cAAc,KAAK,aAAa,CACrD,KAAK,OAAO,SAAS,qBAAqB,EAExC,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,eAAe,CAC9D,KAAK,OAAO,MAAM,aAAa,EAAG,KAAK,eAAgB,GAAK,SACnD,KAAK,QACd,MAAU,MACR,wCAAwC,KAAK,eAAe,2BAC5C,EAAS,IAC1B,EAIL,cAA+B,CAO7B,GANI,KAAK,SACP,KAAK,OAAO,MAAM,gBAAgB,CAClC,KAAK,OAAO,MAAM,aAAa,EAI7B,KAAK,YAAa,CACpB,IAAM,EAAI,KAAK,YACf,KAAK,YAAc,KACnB,GAAG,EAIP,MAAM,SAAyB,CACxB,QAAK,OACV,IAAI,CAAC,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,cAAc,CAAE,CAChE,GAAI,KAAK,QACP,MAAU,MACR,uCAAuC,KAAK,cAAc,2BAC1C,KAAK,oBAAoB,IAC1C,CAEH,OAGF,OAAO,IAAI,QAAe,GAAY,CACpC,KAAK,YAAc,EACnB,IAAM,EAAQ,KAAK,OAAO,MAAM,aAAa,EAAG,KAAK,cAAe,GAAM,CAC1E,KAAK,OAAO,MAAM,YAAY,CAC5B,SAAW,GAAoB,CACzB,IAAe,IACjB,KAAK,OAAO,MAAM,gBAAgB,CAC9B,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,eAAe,EAC9D,KAAK,OAAO,MAAM,aAAa,EAAG,KAAK,eAAgB,GAAK,CAE9D,KAAK,YAAc,KACnB,GAAS,GAGd,CAAC,EACF,EAGJ,eAAsB,CACf,KAAK,SACV,KAAK,OAAO,MAAM,gBAAgB,CAC9B,KAAK,OAAO,SAAS,KAAK,cAAc,KAAK,eAAe,EAC9D,KAAK,OAAO,MAAM,aAAa,EAAG,KAAK,eAAgB,GAAK,CAE9D,AAEE,KAAK,eADL,KAAK,aAAa,CACC,OAIvB,OAAO,EAAe,EAAsB,CAC1C,GAAI,CAAC,KAAK,OAAQ,OAClB,IAAM,EAAS,KAAK,OAAO,WAAW,CAClC,EAAO,MAAQ,GAAK,EAAO,OAAS,GACtC,KAAK,OAAO,MAAM,IAChB,EAAQ,EAAO,MACf,EAAS,EAAO,OACjB,CAIL,WAAqC,CAMnC,GALA,AAGE,KAAK,UAFL,KAAK,OAAO,MAAM,gBAAgB,CAClC,KAAK,OAAO,SAAS,CACP,MAEZ,KAAK,YAAa,CACpB,IAAM,EAAI,KAAK,YACf,KAAK,YAAc,KACnB,GAAG"}