pixi-reels 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.md +167 -0
- package/dist/SpineSymbol-B40TevSr.cjs +2 -0
- package/dist/SpineSymbol-B40TevSr.cjs.map +1 -0
- package/dist/SpineSymbol-D2jhCzFW.js +82 -0
- package/dist/SpineSymbol-D2jhCzFW.js.map +1 -0
- package/dist/config/SpeedPresets.d.ts +49 -0
- package/dist/config/SpeedPresets.d.ts.map +1 -0
- package/dist/config/defaults.d.ts +12 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/types.d.ts +93 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/core/Reel.d.ts +106 -0
- package/dist/core/Reel.d.ts.map +1 -0
- package/dist/core/ReelMotion.d.ts +43 -0
- package/dist/core/ReelMotion.d.ts.map +1 -0
- package/dist/core/ReelSet.d.ts +101 -0
- package/dist/core/ReelSet.d.ts.map +1 -0
- package/dist/core/ReelSetBuilder.d.ts +102 -0
- package/dist/core/ReelSetBuilder.d.ts.map +1 -0
- package/dist/core/ReelViewport.d.ts +43 -0
- package/dist/core/ReelViewport.d.ts.map +1 -0
- package/dist/core/StopSequencer.d.ts +26 -0
- package/dist/core/StopSequencer.d.ts.map +1 -0
- package/dist/debug/debug.d.ts +65 -0
- package/dist/debug/debug.d.ts.map +1 -0
- package/dist/events/EventEmitter.d.ts +25 -0
- package/dist/events/EventEmitter.d.ts.map +1 -0
- package/dist/events/ReelEvents.d.ts +40 -0
- package/dist/events/ReelEvents.d.ts.map +1 -0
- package/dist/frame/FrameBuilder.d.ts +50 -0
- package/dist/frame/FrameBuilder.d.ts.map +1 -0
- package/dist/frame/OffsetCalculator.d.ts +19 -0
- package/dist/frame/OffsetCalculator.d.ts.map +1 -0
- package/dist/frame/RandomSymbolProvider.d.ts +27 -0
- package/dist/frame/RandomSymbolProvider.d.ts.map +1 -0
- package/dist/index.cjs +5 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1454 -0
- package/dist/index.js.map +1 -0
- package/dist/pool/ObjectPool.d.ts +36 -0
- package/dist/pool/ObjectPool.d.ts.map +1 -0
- package/dist/speed/SpeedManager.d.ts +38 -0
- package/dist/speed/SpeedManager.d.ts.map +1 -0
- package/dist/spin/SpinController.d.ts +71 -0
- package/dist/spin/SpinController.d.ts.map +1 -0
- package/dist/spin/modes/CascadeMode.d.ts +16 -0
- package/dist/spin/modes/CascadeMode.d.ts.map +1 -0
- package/dist/spin/modes/ImmediateMode.d.ts +10 -0
- package/dist/spin/modes/ImmediateMode.d.ts.map +1 -0
- package/dist/spin/modes/SpinningMode.d.ts +18 -0
- package/dist/spin/modes/SpinningMode.d.ts.map +1 -0
- package/dist/spin/modes/StandardMode.d.ts +10 -0
- package/dist/spin/modes/StandardMode.d.ts.map +1 -0
- package/dist/spin/phases/AnticipationPhase.d.ts +24 -0
- package/dist/spin/phases/AnticipationPhase.d.ts.map +1 -0
- package/dist/spin/phases/PhaseFactory.d.ts +21 -0
- package/dist/spin/phases/PhaseFactory.d.ts.map +1 -0
- package/dist/spin/phases/ReelPhase.d.ts +39 -0
- package/dist/spin/phases/ReelPhase.d.ts.map +1 -0
- package/dist/spin/phases/SpinPhase.d.ts +25 -0
- package/dist/spin/phases/SpinPhase.d.ts.map +1 -0
- package/dist/spin/phases/StartPhase.d.ts +26 -0
- package/dist/spin/phases/StartPhase.d.ts.map +1 -0
- package/dist/spin/phases/StopPhase.d.ts +37 -0
- package/dist/spin/phases/StopPhase.d.ts.map +1 -0
- package/dist/spine/SpineReelSymbol.d.ts +111 -0
- package/dist/spine/SpineReelSymbol.d.ts.map +1 -0
- package/dist/spine/index.d.ts +5 -0
- package/dist/spine/index.d.ts.map +1 -0
- package/dist/spine.cjs +2 -0
- package/dist/spine.cjs.map +1 -0
- package/dist/spine.js +123 -0
- package/dist/spine.js.map +1 -0
- package/dist/spotlight/SymbolSpotlight.d.ts +70 -0
- package/dist/spotlight/SymbolSpotlight.d.ts.map +1 -0
- package/dist/symbols/AnimatedSpriteSymbol.d.ts +30 -0
- package/dist/symbols/AnimatedSpriteSymbol.d.ts.map +1 -0
- package/dist/symbols/ReelSymbol.d.ts +84 -0
- package/dist/symbols/ReelSymbol.d.ts.map +1 -0
- package/dist/symbols/SpineSymbol.d.ts +34 -0
- package/dist/symbols/SpineSymbol.d.ts.map +1 -0
- package/dist/symbols/SpriteSymbol.d.ts +29 -0
- package/dist/symbols/SpriteSymbol.d.ts.map +1 -0
- package/dist/symbols/SymbolFactory.d.ts +20 -0
- package/dist/symbols/SymbolFactory.d.ts.map +1 -0
- package/dist/symbols/SymbolRegistry.d.ts +25 -0
- package/dist/symbols/SymbolRegistry.d.ts.map +1 -0
- package/dist/testing/FakeTicker.d.ts +45 -0
- package/dist/testing/FakeTicker.d.ts.map +1 -0
- package/dist/testing/HeadlessSymbol.d.ts +28 -0
- package/dist/testing/HeadlessSymbol.d.ts.map +1 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/testHarness.d.ts +70 -0
- package/dist/testing/testHarness.d.ts.map +1 -0
- package/dist/utils/Disposable.d.ts +10 -0
- package/dist/utils/Disposable.d.ts.map +1 -0
- package/dist/utils/TickerRef.d.ts +30 -0
- package/dist/utils/TickerRef.d.ts.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ReelPhase } from './ReelPhase.js';
|
|
2
|
+
export interface AnticipationPhaseConfig {
|
|
3
|
+
/** Duration override in ms. Uses speed profile anticipationDelay if not set. */
|
|
4
|
+
duration?: number;
|
|
5
|
+
/** Speed multiplier during anticipation. Default: 0.3 (30% of spin speed). */
|
|
6
|
+
speedMultiplier?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Anticipation phase: slow-down tease before a reel stops.
|
|
10
|
+
*
|
|
11
|
+
* Decelerates to a fraction of spin speed, holds for a duration, then hands
|
|
12
|
+
* off to StopPhase. StopPhase resets speed to full spin speed at the start
|
|
13
|
+
* of its spin-out stage, so leaving speed low here is fine.
|
|
14
|
+
*/
|
|
15
|
+
export declare class AnticipationPhase extends ReelPhase<AnticipationPhaseConfig> {
|
|
16
|
+
readonly name = "anticipation";
|
|
17
|
+
readonly skippable = true;
|
|
18
|
+
private _tween;
|
|
19
|
+
protected onEnter(config: AnticipationPhaseConfig): void;
|
|
20
|
+
update(_deltaMs: number): void;
|
|
21
|
+
protected onSkip(): void;
|
|
22
|
+
private _kill;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=AnticipationPhase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnticipationPhase.d.ts","sourceRoot":"","sources":["../../../src/spin/phases/AnticipationPhase.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,MAAM,WAAW,uBAAuB;IACtC,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,qBAAa,iBAAkB,SAAQ,SAAS,CAAC,uBAAuB,CAAC;IACvE,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,SAAS,QAAQ;IAE1B,OAAO,CAAC,MAAM,CAAmC;IAEjD,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI;IAqBxD,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAI9B,SAAS,CAAC,MAAM,IAAI,IAAI;IAKxB,OAAO,CAAC,KAAK;CAMd"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Reel } from '../../core/Reel.js';
|
|
2
|
+
import { SpeedProfile } from '../../config/types.js';
|
|
3
|
+
import { ReelPhase } from './ReelPhase.js';
|
|
4
|
+
type PhaseConstructor<T extends ReelPhase<any> = ReelPhase<any>> = new (reel: Reel, speed: SpeedProfile) => T;
|
|
5
|
+
/**
|
|
6
|
+
* Factory for creating reel phase instances.
|
|
7
|
+
*
|
|
8
|
+
* Ships with all four default phases pre-registered.
|
|
9
|
+
* Users can override any phase by registering a custom constructor.
|
|
10
|
+
*/
|
|
11
|
+
export declare class PhaseFactory {
|
|
12
|
+
private _registry;
|
|
13
|
+
constructor();
|
|
14
|
+
/** Register or override a phase type. */
|
|
15
|
+
register<T extends ReelPhase<any>>(name: string, PhaseClass: PhaseConstructor<T>): void;
|
|
16
|
+
/** Create a phase instance for a reel. */
|
|
17
|
+
create<T extends ReelPhase<any> = ReelPhase<any>>(name: string, reel: Reel, speed: SpeedProfile): T;
|
|
18
|
+
has(name: string): boolean;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=PhaseFactory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PhaseFactory.d.ts","sourceRoot":"","sources":["../../../src/spin/phases/PhaseFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAM3C,KAAK,gBAAgB,CAAC,CAAC,SAAS,SAAS,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,IAC7D,KAAK,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,KAAK,CAAC,CAAC;AAE7C;;;;;GAKG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,SAAS,CAAuC;;IAUxD,yCAAyC;IACzC,QAAQ,CAAC,CAAC,SAAS,SAAS,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAAI;IAIvF,0CAA0C;IAC1C,MAAM,CAAC,CAAC,SAAS,SAAS,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,EAC9C,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,YAAY,GAClB,CAAC;IAUJ,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAG3B"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Reel } from '../../core/Reel.js';
|
|
2
|
+
import { SpeedProfile } from '../../config/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Abstract base for reel spin phases.
|
|
5
|
+
*
|
|
6
|
+
* Each phase represents one stage of the spin lifecycle:
|
|
7
|
+
* START → SPIN → ANTICIPATION → STOP.
|
|
8
|
+
*
|
|
9
|
+
* Phases are entered and exited by SpinController, and can be skipped
|
|
10
|
+
* if marked as skippable and the user triggers skip/slam-stop.
|
|
11
|
+
*
|
|
12
|
+
* @typeParam TConfig - Phase-specific configuration type.
|
|
13
|
+
*/
|
|
14
|
+
export declare abstract class ReelPhase<TConfig = void> {
|
|
15
|
+
abstract readonly name: string;
|
|
16
|
+
abstract readonly skippable: boolean;
|
|
17
|
+
protected _reel: Reel;
|
|
18
|
+
protected _speed: SpeedProfile;
|
|
19
|
+
protected _resolve: (() => void) | null;
|
|
20
|
+
protected _isActive: boolean;
|
|
21
|
+
constructor(reel: Reel, speed: SpeedProfile);
|
|
22
|
+
get reel(): Reel;
|
|
23
|
+
get isActive(): boolean;
|
|
24
|
+
/** Enter the phase. Returns a promise that resolves when the phase is complete. */
|
|
25
|
+
run(config: TConfig): Promise<void>;
|
|
26
|
+
/** Skip the phase immediately (if skippable). */
|
|
27
|
+
skip(): void;
|
|
28
|
+
/** Force-complete the phase regardless of skippable flag. */
|
|
29
|
+
forceComplete(): void;
|
|
30
|
+
/** Called each frame while the phase is active. */
|
|
31
|
+
abstract update(deltaMs: number): void;
|
|
32
|
+
/** Subclass: set up the phase (start tweens, set speed, etc). */
|
|
33
|
+
protected abstract onEnter(config: TConfig): void;
|
|
34
|
+
/** Subclass: clean up when skipped or force-completed. */
|
|
35
|
+
protected abstract onSkip(): void;
|
|
36
|
+
/** Call when the phase naturally completes. */
|
|
37
|
+
protected _complete(): void;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=ReelPhase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReelPhase.d.ts","sourceRoot":"","sources":["../../../src/spin/phases/ReelPhase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,8BAAsB,SAAS,CAAC,OAAO,GAAG,IAAI;IAC5C,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAErC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC;IACtB,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC;IAC/B,SAAS,CAAC,QAAQ,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAQ;IAC/C,SAAS,CAAC,SAAS,UAAS;gBAEhB,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY;IAK3C,IAAI,IAAI,IAAI,IAAI,CAEf;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,mFAAmF;IAC7E,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAczC,iDAAiD;IACjD,IAAI,IAAI,IAAI;IAMZ,6DAA6D;IAC7D,aAAa,IAAI,IAAI;IAMrB,mDAAmD;IACnD,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAEtC,iEAAiE;IACjE,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAEjD,0DAA0D;IAC1D,SAAS,CAAC,QAAQ,CAAC,MAAM,IAAI,IAAI;IAEjC,+CAA+C;IAC/C,SAAS,CAAC,SAAS,IAAI,IAAI;CAO5B"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ReelPhase } from './ReelPhase.js';
|
|
2
|
+
export interface SpinPhaseConfig {
|
|
3
|
+
/** Minimum time to spin before allowing stop. Overrides speed profile if set. */
|
|
4
|
+
minimumSpinTime?: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Continuous spinning at constant speed.
|
|
8
|
+
*
|
|
9
|
+
* Runs until externally resolved (when setResult arrives). Tracks minimum
|
|
10
|
+
* spin time via ticker accumulation so it behaves consistently when the tab
|
|
11
|
+
* is hidden (no reliance on wall-clock performance.now()).
|
|
12
|
+
*/
|
|
13
|
+
export declare class SpinPhase extends ReelPhase<SpinPhaseConfig> {
|
|
14
|
+
readonly name = "spin";
|
|
15
|
+
readonly skippable = false;
|
|
16
|
+
private _elapsed;
|
|
17
|
+
private _minTime;
|
|
18
|
+
private _readyToStop;
|
|
19
|
+
protected onEnter(config: SpinPhaseConfig): void;
|
|
20
|
+
update(deltaMs: number): void;
|
|
21
|
+
/** Signal that this phase should end (called by SpinController when result arrives). */
|
|
22
|
+
resolve(): void;
|
|
23
|
+
protected onSkip(): void;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=SpinPhase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpinPhase.d.ts","sourceRoot":"","sources":["../../../src/spin/phases/SpinPhase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,qBAAa,SAAU,SAAQ,SAAS,CAAC,eAAe,CAAC;IACvD,QAAQ,CAAC,IAAI,UAAU;IACvB,QAAQ,CAAC,SAAS,SAAS;IAE3B,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,YAAY,CAAS;IAE7B,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAMhD,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAO7B,wFAAwF;IACxF,OAAO,IAAI,IAAI;IAOf,SAAS,CAAC,MAAM,IAAI,IAAI;CAGzB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ReelPhase } from './ReelPhase.js';
|
|
2
|
+
import { SpinningMode } from '../modes/SpinningMode.js';
|
|
3
|
+
export interface StartPhaseConfig {
|
|
4
|
+
/** Spinning mode to set on enter. */
|
|
5
|
+
spinningMode: SpinningMode;
|
|
6
|
+
/** Delay before this reel starts (for staggered start). */
|
|
7
|
+
delay?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Accelerates the reel from rest to full spin speed.
|
|
11
|
+
*
|
|
12
|
+
* Optionally performs a brief step-back (reel reverses a tiny amount) before
|
|
13
|
+
* accelerating upward, giving the classic slot machine "pull" feel.
|
|
14
|
+
*/
|
|
15
|
+
export declare class StartPhase extends ReelPhase<StartPhaseConfig> {
|
|
16
|
+
readonly name = "start";
|
|
17
|
+
readonly skippable = true;
|
|
18
|
+
private _tween;
|
|
19
|
+
private _delayedCall;
|
|
20
|
+
protected onEnter(config: StartPhaseConfig): void;
|
|
21
|
+
private _launch;
|
|
22
|
+
update(_deltaMs: number): void;
|
|
23
|
+
protected onSkip(): void;
|
|
24
|
+
private _kill;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=StartPhase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StartPhase.d.ts","sourceRoot":"","sources":["../../../src/spin/phases/StartPhase.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAC/B,qCAAqC;IACrC,YAAY,EAAE,YAAY,CAAC;IAC3B,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,qBAAa,UAAW,SAAQ,SAAS,CAAC,gBAAgB,CAAC;IACzD,QAAQ,CAAC,IAAI,WAAW;IACxB,QAAQ,CAAC,SAAS,QAAQ;IAE1B,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,YAAY,CAAgC;IAEpD,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI;IAejD,OAAO,CAAC,OAAO;IA6Bf,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAI9B,SAAS,CAAC,MAAM,IAAI,IAAI;IAKxB,OAAO,CAAC,KAAK;CAUd"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ReelPhase } from './ReelPhase.js';
|
|
2
|
+
export interface StopPhaseConfig {
|
|
3
|
+
/** Target symbols for this reel (full frame including buffers, top-to-bottom). */
|
|
4
|
+
targetFrame: string[];
|
|
5
|
+
/** Delay before this reel starts stopping (for staggered stop). */
|
|
6
|
+
delay?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Stops the reel on the target frame with a weighted, slot-machine feel.
|
|
10
|
+
*
|
|
11
|
+
* Sequence:
|
|
12
|
+
* 1. Wait for the staggered delay.
|
|
13
|
+
* 2. Keep spinning at full speed with `isStopping` flagged. The target frame
|
|
14
|
+
* is loaded into the StopSequencer; each wrap event at the top of the
|
|
15
|
+
* reel pulls the next frame symbol — so targets arrive in the visible
|
|
16
|
+
* area naturally, carrying the full momentum of the spin.
|
|
17
|
+
* 3. When the sequencer is exhausted, snap to grid and bounce:
|
|
18
|
+
* - overshoot downward by `bounceDistance` with `power1.out`
|
|
19
|
+
* - settle back upward with `power1.out`
|
|
20
|
+
* Both legs share a duration for a symmetric, weighty landing.
|
|
21
|
+
*/
|
|
22
|
+
export declare class StopPhase extends ReelPhase<StopPhaseConfig> {
|
|
23
|
+
readonly name = "stop";
|
|
24
|
+
readonly skippable = true;
|
|
25
|
+
private _config;
|
|
26
|
+
private _delayTween;
|
|
27
|
+
private _bounceTween;
|
|
28
|
+
private _stage;
|
|
29
|
+
private _baseY;
|
|
30
|
+
protected onEnter(config: StopPhaseConfig): void;
|
|
31
|
+
private _beginSpinOut;
|
|
32
|
+
update(_deltaMs: number): void;
|
|
33
|
+
private _landAndBounce;
|
|
34
|
+
protected onSkip(): void;
|
|
35
|
+
private _killTweens;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=StopPhase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StopPhase.d.ts","sourceRoot":"","sources":["../../../src/spin/phases/StopPhase.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,MAAM,WAAW,eAAe;IAC9B,kFAAkF;IAClF,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,SAAU,SAAQ,SAAS,CAAC,eAAe,CAAC;IACvD,QAAQ,CAAC,IAAI,UAAU;IACvB,QAAQ,CAAC,SAAS,QAAQ;IAE1B,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,YAAY,CAAmC;IACvD,OAAO,CAAC,MAAM,CAAuD;IACrE,OAAO,CAAC,MAAM,CAAK;IAEnB,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAahD,OAAO,CAAC,aAAa;IAcrB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAS9B,OAAO,CAAC,cAAc;IAoCtB,SAAS,CAAC,MAAM,IAAI,IAAI;IAkBxB,OAAO,CAAC,WAAW;CAUpB"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Spine, TrackEntry } from '@esotericsoftware/spine-pixi-v8';
|
|
2
|
+
import { ReelSymbol } from '../symbols/ReelSymbol.js';
|
|
3
|
+
/**
|
|
4
|
+
* Per-symbol overrides so a skeleton with unusual animation names still works.
|
|
5
|
+
*
|
|
6
|
+
* Example: Bonanza's `low_1` has a typo (`ide` instead of `idle`) —
|
|
7
|
+
* `{ low1: { idle: 'ide' } }` fixes it without touching the asset.
|
|
8
|
+
*/
|
|
9
|
+
export type SymbolAnimOverrides = Record<string, Partial<Record<'idle' | 'landing' | 'win' | 'out' | 'blur', string>>>;
|
|
10
|
+
export interface SpineReelSymbolOptions {
|
|
11
|
+
/** Map of symbolId -> { skeletonAlias, atlasAlias } */
|
|
12
|
+
spineMap: Record<string, {
|
|
13
|
+
skeleton: string;
|
|
14
|
+
atlas: string;
|
|
15
|
+
}>;
|
|
16
|
+
/** Default idle animation name. Default: 'idle'. */
|
|
17
|
+
idleAnimation?: string;
|
|
18
|
+
/** Default win animation name. Default: 'win'. */
|
|
19
|
+
winAnimation?: string;
|
|
20
|
+
/** Default landing (one-shot) animation name. Default: 'landing'. */
|
|
21
|
+
landingAnimation?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Default "exit" animation name — used as the cascade pop / disintegrate.
|
|
24
|
+
* If the skeleton doesn't have it, callers should fall back to an alpha tween.
|
|
25
|
+
* Default: 'disintegration'.
|
|
26
|
+
*/
|
|
27
|
+
outAnimation?: string;
|
|
28
|
+
/** Default "blur" (fast spin) animation name. Default: 'blur'. */
|
|
29
|
+
blurAnimation?: string;
|
|
30
|
+
/** Per-symbol overrides (see SymbolAnimOverrides). */
|
|
31
|
+
animations?: SymbolAnimOverrides;
|
|
32
|
+
/** Scale for spine instances. Default: 1. */
|
|
33
|
+
scale?: number;
|
|
34
|
+
/**
|
|
35
|
+
* If true, automatically plays the `blur` animation when the owning reel
|
|
36
|
+
* enters the spin phase, and reverts to idle when it lands. Requires the
|
|
37
|
+
* skeleton to have a `blur` animation (or the overridden name). Default: false.
|
|
38
|
+
*/
|
|
39
|
+
autoPlayBlur?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* If true, automatically plays the `landing` animation concurrently with
|
|
42
|
+
* the stop-phase bounce. Requires the skeleton to have a `landing`
|
|
43
|
+
* animation (or the overridden name). Default: false.
|
|
44
|
+
*/
|
|
45
|
+
autoPlayLanding?: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* ReelSymbol implementation using Spine 2D skeletons.
|
|
49
|
+
*
|
|
50
|
+
* Caches one Spine instance per symbolId for instant swapping, plays idle on
|
|
51
|
+
* activate, and exposes the canonical set of one-shot animations (`landing`,
|
|
52
|
+
* `win`, `out`, reactions). Modeled on the Bonanza / Hold & Win slot-game
|
|
53
|
+
* conventions — drop in any skeleton that follows the same vocabulary.
|
|
54
|
+
*
|
|
55
|
+
* Import from the `pixi-reels/spine` subpath so non-Spine consumers can
|
|
56
|
+
* tree-shake this module and `@esotericsoftware/spine-pixi-v8` out of their
|
|
57
|
+
* production bundle:
|
|
58
|
+
*
|
|
59
|
+
* ```ts
|
|
60
|
+
* import { SpineReelSymbol } from 'pixi-reels/spine';
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare class SpineReelSymbol extends ReelSymbol {
|
|
64
|
+
private _spines;
|
|
65
|
+
private _currentSpine;
|
|
66
|
+
private _spineMap;
|
|
67
|
+
private _defaultAnims;
|
|
68
|
+
private _overrides;
|
|
69
|
+
private _scale;
|
|
70
|
+
private _autoPlayBlur;
|
|
71
|
+
private _autoPlayLanding;
|
|
72
|
+
private _oneShotResolve;
|
|
73
|
+
private _cellWidth;
|
|
74
|
+
private _cellHeight;
|
|
75
|
+
constructor(options: SpineReelSymbolOptions);
|
|
76
|
+
onReelSpinStart(): void;
|
|
77
|
+
onReelSpinEnd(): void;
|
|
78
|
+
onReelLanded(): void;
|
|
79
|
+
/** Resolve an animation name for the current symbol, respecting overrides. */
|
|
80
|
+
private _animNameFor;
|
|
81
|
+
protected onActivate(symbolId: string): void;
|
|
82
|
+
protected onDeactivate(): void;
|
|
83
|
+
/** Play the win animation on track 0. Returns when it completes. */
|
|
84
|
+
playWin(): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Play the landing animation (one-shot). Call this when the reel settles —
|
|
87
|
+
* typically inside a `spin:reelLanded` listener.
|
|
88
|
+
*/
|
|
89
|
+
playLanding(): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Play the exit / disintegrate animation. Returns a promise that resolves
|
|
92
|
+
* when it completes. Use in cascades instead of the default alpha fade.
|
|
93
|
+
*/
|
|
94
|
+
playOut(): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Swap the primary track to the blur animation for the SPIN phase. Reverts
|
|
97
|
+
* to idle automatically on `stopAnimation()` or next activate.
|
|
98
|
+
*/
|
|
99
|
+
playBlur(): void;
|
|
100
|
+
/** Play an arbitrary animation on a given track. Non-blocking. */
|
|
101
|
+
playOnTrack(track: number, animName: string, loop?: boolean): TrackEntry | null;
|
|
102
|
+
stopAnimation(): void;
|
|
103
|
+
/** Access the underlying Spine — for advanced needs (reactions, events). */
|
|
104
|
+
get spine(): Spine | null;
|
|
105
|
+
private _currentListener;
|
|
106
|
+
private _playOneShot;
|
|
107
|
+
resize(width: number, height: number): void;
|
|
108
|
+
private _positionSpine;
|
|
109
|
+
protected onDestroy(): void;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=SpineReelSymbol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpineReelSymbol.d.ts","sourceRoot":"","sources":["../../src/spine/SpineReelSymbol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,CACtC,MAAM,EACN,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,EAAE,MAAM,CAAC,CAAC,CACrE,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9D,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,eAAgB,SAAQ,UAAU;IAC7C,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,SAAS,CAAsD;IACvE,OAAO,CAAC,aAAa,CAA4E;IACjG,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAU;IAC/B,OAAO,CAAC,gBAAgB,CAAU;IAClC,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,WAAW,CAAK;gBAEZ,OAAO,EAAE,sBAAsB;IAgBlC,eAAe,IAAI,IAAI;IAIvB,aAAa,IAAI,IAAI;IAIrB,YAAY,IAAI,IAAI;IAM7B,8EAA8E;IAC9E,OAAO,CAAC,YAAY;IAKpB,SAAS,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IA0B5C,SAAS,CAAC,YAAY,IAAI,IAAI;IAoB9B,oEAAoE;IAC9D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAIlC;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;;OAGG;IACH,QAAQ,IAAI,IAAI;IAOhB,kEAAkE;IAClE,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,UAAQ,GAAG,UAAU,GAAG,IAAI;IAM7E,aAAa,IAAI,IAAI;IAcrB,4EAA4E;IAC5E,IAAI,KAAK,IAAI,KAAK,GAAG,IAAI,CAExB;IAID,OAAO,CAAC,gBAAgB,CAEjB;YAEO,YAAY;IAkC1B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAM3C,OAAO,CAAC,cAAc;cAKH,SAAS,IAAI,IAAI;CAOrC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { SpineSymbol } from '../symbols/SpineSymbol.js';
|
|
2
|
+
export type { SpineSymbolOptions } from '../symbols/SpineSymbol.js';
|
|
3
|
+
export { SpineReelSymbol } from './SpineReelSymbol.js';
|
|
4
|
+
export type { SpineReelSymbolOptions, SymbolAnimOverrides, } from './SpineReelSymbol.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/spine/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,YAAY,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EACV,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC"}
|
package/dist/spine.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./SpineSymbol-B40TevSr.cjs`);let t=require(`@esotericsoftware/spine-pixi-v8`);var n=class extends e.n{_spines=new Map;_currentSpine=null;_spineMap;_defaultAnims;_overrides;_scale;_autoPlayBlur;_autoPlayLanding;_oneShotResolve=null;_cellWidth=0;_cellHeight=0;constructor(e){super(),this._spineMap=e.spineMap,this._defaultAnims={idle:e.idleAnimation??`idle`,win:e.winAnimation??`win`,landing:e.landingAnimation??`landing`,out:e.outAnimation??`disintegration`,blur:e.blurAnimation??`blur`},this._overrides=e.animations??{},this._scale=e.scale??1,this._autoPlayBlur=e.autoPlayBlur??!1,this._autoPlayLanding=e.autoPlayLanding??!1}onReelSpinStart(){this._autoPlayBlur&&this.playBlur()}onReelSpinEnd(){this._autoPlayBlur&&this.stopAnimation()}onReelLanded(){this._autoPlayLanding&&this.playLanding()}_animNameFor(e){return this._overrides[this.symbolId]?.[e]??this._defaultAnims[e]}onActivate(e){this._currentSpine&&(this._currentSpine.visible=!1);let n=this._spines.get(e);if(!n){let r=this._spineMap[e];if(!r)return;n=t.Spine.from({skeleton:r.skeleton,atlas:r.atlas}),n.scale.set(this._scale),this.view.addChild(n),this._spines.set(e,n)}this._positionSpine(n),n.visible=!0,this._currentSpine=n,this._oneShotResolve=null;let r=this._animNameFor(`idle`);n.skeleton.data.findAnimation(r)&&n.state.setAnimation(0,r,!0)}onDeactivate(){if(this._currentSpine&&=(this._currentSpine.state.clearTracks(),this._currentSpine.state.removeListener(this._currentListener),this._currentSpine.skeleton.setToSetupPose(),this._currentSpine.visible=!1,null),this._oneShotResolve){let e=this._oneShotResolve;this._oneShotResolve=null,e()}}async playWin(){return this._playOneShot(this._animNameFor(`win`),0,!0)}async playLanding(){return this._playOneShot(this._animNameFor(`landing`),0,!0)}async playOut(){return this._playOneShot(this._animNameFor(`out`),0,!1)}playBlur(){if(!this._currentSpine)return;let e=this._animNameFor(`blur`);this._currentSpine.skeleton.data.findAnimation(e)&&this._currentSpine.state.setAnimation(0,e,!0)}playOnTrack(e,t,n=!1){return!this._currentSpine||!this._currentSpine.skeleton.data.findAnimation(t)?null:this._currentSpine.state.setAnimation(e,t,n)}stopAnimation(){if(!this._currentSpine)return;this._currentSpine.state.removeListener(this._currentListener);let e=this._animNameFor(`idle`);if(this._currentSpine.skeleton.data.findAnimation(e)&&this._currentSpine.state.setAnimation(0,e,!0),this._oneShotResolve){let e=this._oneShotResolve;this._oneShotResolve=null,e()}}get spine(){return this._currentSpine}_currentListener={};async _playOneShot(e,t,n){if(!this._currentSpine)return;let r=this._currentSpine;if(r.skeleton.data.findAnimation(e))return new Promise(i=>{this._oneShotResolve=i;let a=r.state.setAnimation(t,e,!1),o={complete:e=>{if(e===a){if(r.state.removeListener(this._currentListener),n){let e=this._animNameFor(`idle`);r.skeleton.data.findAnimation(e)&&r.state.setAnimation(t,e,!0)}if(this._oneShotResolve){let e=this._oneShotResolve;this._oneShotResolve=null,e()}}}};this._currentListener=o,r.state.addListener(o)})}resize(e,t){this._cellWidth=e,this._cellHeight=t;for(let[,e]of this._spines)this._positionSpine(e)}_positionSpine(e){e.x=this._cellWidth/2,e.y=this._cellHeight/2}onDestroy(){for(let[,e]of this._spines)e.state.clearListeners(),e.destroy();this._spines.clear()}};exports.SpineReelSymbol=n,exports.SpineSymbol=e.t;
|
|
2
|
+
//# sourceMappingURL=spine.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spine.cjs","names":[],"sources":["../src/spine/SpineReelSymbol.ts"],"sourcesContent":["import { Spine, type TrackEntry } from '@esotericsoftware/spine-pixi-v8';\nimport { ReelSymbol } from '../symbols/ReelSymbol.js';\n\n/**\n * Per-symbol overrides so a skeleton with unusual animation names still works.\n *\n * Example: Bonanza's `low_1` has a typo (`ide` instead of `idle`) —\n * `{ low1: { idle: 'ide' } }` fixes it without touching the asset.\n */\nexport type SymbolAnimOverrides = Record<\n string,\n Partial<Record<'idle' | 'landing' | 'win' | 'out' | 'blur', string>>\n>;\n\nexport interface SpineReelSymbolOptions {\n /** Map of symbolId -> { skeletonAlias, atlasAlias } */\n spineMap: Record<string, { skeleton: string; atlas: string }>;\n /** Default idle animation name. Default: 'idle'. */\n idleAnimation?: string;\n /** Default win animation name. Default: 'win'. */\n winAnimation?: string;\n /** Default landing (one-shot) animation name. Default: 'landing'. */\n landingAnimation?: string;\n /**\n * Default \"exit\" animation name — used as the cascade pop / disintegrate.\n * If the skeleton doesn't have it, callers should fall back to an alpha tween.\n * Default: 'disintegration'.\n */\n outAnimation?: string;\n /** Default \"blur\" (fast spin) animation name. Default: 'blur'. */\n blurAnimation?: string;\n /** Per-symbol overrides (see SymbolAnimOverrides). */\n animations?: SymbolAnimOverrides;\n /** Scale for spine instances. Default: 1. */\n scale?: number;\n /**\n * If true, automatically plays the `blur` animation when the owning reel\n * enters the spin phase, and reverts to idle when it lands. Requires the\n * skeleton to have a `blur` animation (or the overridden name). Default: false.\n */\n autoPlayBlur?: boolean;\n /**\n * If true, automatically plays the `landing` animation concurrently with\n * the stop-phase bounce. Requires the skeleton to have a `landing`\n * animation (or the overridden name). Default: false.\n */\n autoPlayLanding?: boolean;\n}\n\n/**\n * ReelSymbol implementation using Spine 2D skeletons.\n *\n * Caches one Spine instance per symbolId for instant swapping, plays idle on\n * activate, and exposes the canonical set of one-shot animations (`landing`,\n * `win`, `out`, reactions). Modeled on the Bonanza / Hold & Win slot-game\n * conventions — drop in any skeleton that follows the same vocabulary.\n *\n * Import from the `pixi-reels/spine` subpath so non-Spine consumers can\n * tree-shake this module and `@esotericsoftware/spine-pixi-v8` out of their\n * production bundle:\n *\n * ```ts\n * import { SpineReelSymbol } from 'pixi-reels/spine';\n * ```\n */\nexport class SpineReelSymbol extends ReelSymbol {\n private _spines = new Map<string, Spine>();\n private _currentSpine: Spine | null = null;\n private _spineMap: Record<string, { skeleton: string; atlas: string }>;\n private _defaultAnims: { idle: string; win: string; landing: string; out: string; blur: string };\n private _overrides: SymbolAnimOverrides;\n private _scale: number;\n private _autoPlayBlur: boolean;\n private _autoPlayLanding: boolean;\n private _oneShotResolve: (() => void) | null = null;\n private _cellWidth = 0;\n private _cellHeight = 0;\n\n constructor(options: SpineReelSymbolOptions) {\n super();\n this._spineMap = options.spineMap;\n this._defaultAnims = {\n idle: options.idleAnimation ?? 'idle',\n win: options.winAnimation ?? 'win',\n landing: options.landingAnimation ?? 'landing',\n out: options.outAnimation ?? 'disintegration',\n blur: options.blurAnimation ?? 'blur',\n };\n this._overrides = options.animations ?? {};\n this._scale = options.scale ?? 1;\n this._autoPlayBlur = options.autoPlayBlur ?? false;\n this._autoPlayLanding = options.autoPlayLanding ?? false;\n }\n\n override onReelSpinStart(): void {\n if (this._autoPlayBlur) this.playBlur();\n }\n\n override onReelSpinEnd(): void {\n if (this._autoPlayBlur) this.stopAnimation();\n }\n\n override onReelLanded(): void {\n if (this._autoPlayLanding) {\n void this.playLanding();\n }\n }\n\n /** Resolve an animation name for the current symbol, respecting overrides. */\n private _animNameFor(role: 'idle' | 'landing' | 'win' | 'out' | 'blur'): string {\n const override = this._overrides[this.symbolId]?.[role];\n return override ?? this._defaultAnims[role];\n }\n\n protected onActivate(symbolId: string): void {\n if (this._currentSpine) this._currentSpine.visible = false;\n\n let spine = this._spines.get(symbolId);\n if (!spine) {\n const cfg = this._spineMap[symbolId];\n if (!cfg) return;\n spine = Spine.from({ skeleton: cfg.skeleton, atlas: cfg.atlas });\n spine.scale.set(this._scale);\n this.view.addChild(spine);\n this._spines.set(symbolId, spine);\n }\n\n this._positionSpine(spine);\n spine.visible = true;\n this._currentSpine = spine;\n\n // Clear any lingering one-shot resolve from a prior pool use\n this._oneShotResolve = null;\n\n const idleName = this._animNameFor('idle');\n if (spine.skeleton.data.findAnimation(idleName)) {\n spine.state.setAnimation(0, idleName, true);\n }\n }\n\n protected onDeactivate(): void {\n if (this._currentSpine) {\n this._currentSpine.state.clearTracks();\n this._currentSpine.state.removeListener(this._currentListener);\n // Reset the skeleton to its setup pose, otherwise a symbol that ends on\n // an invisible \"out\" frame (e.g. after `playOut()` / disintegrate) is\n // still invisible when the pool reassigns it on the next spin.\n this._currentSpine.skeleton.setToSetupPose();\n this._currentSpine.visible = false;\n this._currentSpine = null;\n }\n if (this._oneShotResolve) {\n const fn = this._oneShotResolve;\n this._oneShotResolve = null;\n fn();\n }\n }\n\n // -- Canonical one-shot animations ---------------------------------------\n\n /** Play the win animation on track 0. Returns when it completes. */\n async playWin(): Promise<void> {\n return this._playOneShot(this._animNameFor('win'), 0, true);\n }\n\n /**\n * Play the landing animation (one-shot). Call this when the reel settles —\n * typically inside a `spin:reelLanded` listener.\n */\n async playLanding(): Promise<void> {\n return this._playOneShot(this._animNameFor('landing'), 0, true);\n }\n\n /**\n * Play the exit / disintegrate animation. Returns a promise that resolves\n * when it completes. Use in cascades instead of the default alpha fade.\n */\n async playOut(): Promise<void> {\n return this._playOneShot(this._animNameFor('out'), 0, false);\n }\n\n /**\n * Swap the primary track to the blur animation for the SPIN phase. Reverts\n * to idle automatically on `stopAnimation()` or next activate.\n */\n playBlur(): void {\n if (!this._currentSpine) return;\n const name = this._animNameFor('blur');\n if (!this._currentSpine.skeleton.data.findAnimation(name)) return;\n this._currentSpine.state.setAnimation(0, name, true);\n }\n\n /** Play an arbitrary animation on a given track. Non-blocking. */\n playOnTrack(track: number, animName: string, loop = false): TrackEntry | null {\n if (!this._currentSpine) return null;\n if (!this._currentSpine.skeleton.data.findAnimation(animName)) return null;\n return this._currentSpine.state.setAnimation(track, animName, loop);\n }\n\n stopAnimation(): void {\n if (!this._currentSpine) return;\n this._currentSpine.state.removeListener(this._currentListener);\n const idleName = this._animNameFor('idle');\n if (this._currentSpine.skeleton.data.findAnimation(idleName)) {\n this._currentSpine.state.setAnimation(0, idleName, true);\n }\n if (this._oneShotResolve) {\n const fn = this._oneShotResolve;\n this._oneShotResolve = null;\n fn();\n }\n }\n\n /** Access the underlying Spine — for advanced needs (reactions, events). */\n get spine(): Spine | null {\n return this._currentSpine;\n }\n\n // -- Internals -----------------------------------------------------------\n\n private _currentListener: {\n complete?: (entry: TrackEntry) => void;\n } = {};\n\n private async _playOneShot(\n animName: string,\n track: number,\n returnToIdle: boolean,\n ): Promise<void> {\n if (!this._currentSpine) return;\n const spine = this._currentSpine;\n if (!spine.skeleton.data.findAnimation(animName)) return;\n\n return new Promise<void>((resolve) => {\n this._oneShotResolve = resolve;\n const entry = spine.state.setAnimation(track, animName, false);\n const listener = {\n complete: (done: TrackEntry) => {\n if (done !== entry) return;\n spine.state.removeListener(this._currentListener);\n if (returnToIdle) {\n const idleName = this._animNameFor('idle');\n if (spine.skeleton.data.findAnimation(idleName)) {\n spine.state.setAnimation(track, idleName, true);\n }\n }\n if (this._oneShotResolve) {\n const fn = this._oneShotResolve;\n this._oneShotResolve = null;\n fn();\n }\n },\n };\n this._currentListener = listener;\n spine.state.addListener(listener);\n });\n }\n\n resize(width: number, height: number): void {\n this._cellWidth = width;\n this._cellHeight = height;\n for (const [, spine] of this._spines) this._positionSpine(spine);\n }\n\n private _positionSpine(spine: Spine): void {\n spine.x = this._cellWidth / 2;\n spine.y = this._cellHeight / 2;\n }\n\n protected override onDestroy(): void {\n for (const [, spine] of this._spines) {\n spine.state.clearListeners();\n spine.destroy();\n }\n this._spines.clear();\n }\n}\n"],"mappings":"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"}
|
package/dist/spine.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { n as e, t } from "./SpineSymbol-D2jhCzFW.js";
|
|
2
|
+
import { Spine as n } from "@esotericsoftware/spine-pixi-v8";
|
|
3
|
+
//#region src/spine/SpineReelSymbol.ts
|
|
4
|
+
var r = class extends e {
|
|
5
|
+
_spines = /* @__PURE__ */ new Map();
|
|
6
|
+
_currentSpine = null;
|
|
7
|
+
_spineMap;
|
|
8
|
+
_defaultAnims;
|
|
9
|
+
_overrides;
|
|
10
|
+
_scale;
|
|
11
|
+
_autoPlayBlur;
|
|
12
|
+
_autoPlayLanding;
|
|
13
|
+
_oneShotResolve = null;
|
|
14
|
+
_cellWidth = 0;
|
|
15
|
+
_cellHeight = 0;
|
|
16
|
+
constructor(e) {
|
|
17
|
+
super(), this._spineMap = e.spineMap, this._defaultAnims = {
|
|
18
|
+
idle: e.idleAnimation ?? "idle",
|
|
19
|
+
win: e.winAnimation ?? "win",
|
|
20
|
+
landing: e.landingAnimation ?? "landing",
|
|
21
|
+
out: e.outAnimation ?? "disintegration",
|
|
22
|
+
blur: e.blurAnimation ?? "blur"
|
|
23
|
+
}, this._overrides = e.animations ?? {}, this._scale = e.scale ?? 1, this._autoPlayBlur = e.autoPlayBlur ?? !1, this._autoPlayLanding = e.autoPlayLanding ?? !1;
|
|
24
|
+
}
|
|
25
|
+
onReelSpinStart() {
|
|
26
|
+
this._autoPlayBlur && this.playBlur();
|
|
27
|
+
}
|
|
28
|
+
onReelSpinEnd() {
|
|
29
|
+
this._autoPlayBlur && this.stopAnimation();
|
|
30
|
+
}
|
|
31
|
+
onReelLanded() {
|
|
32
|
+
this._autoPlayLanding && this.playLanding();
|
|
33
|
+
}
|
|
34
|
+
_animNameFor(e) {
|
|
35
|
+
return this._overrides[this.symbolId]?.[e] ?? this._defaultAnims[e];
|
|
36
|
+
}
|
|
37
|
+
onActivate(e) {
|
|
38
|
+
this._currentSpine && (this._currentSpine.visible = !1);
|
|
39
|
+
let t = this._spines.get(e);
|
|
40
|
+
if (!t) {
|
|
41
|
+
let r = this._spineMap[e];
|
|
42
|
+
if (!r) return;
|
|
43
|
+
t = n.from({
|
|
44
|
+
skeleton: r.skeleton,
|
|
45
|
+
atlas: r.atlas
|
|
46
|
+
}), t.scale.set(this._scale), this.view.addChild(t), this._spines.set(e, t);
|
|
47
|
+
}
|
|
48
|
+
this._positionSpine(t), t.visible = !0, this._currentSpine = t, this._oneShotResolve = null;
|
|
49
|
+
let r = this._animNameFor("idle");
|
|
50
|
+
t.skeleton.data.findAnimation(r) && t.state.setAnimation(0, r, !0);
|
|
51
|
+
}
|
|
52
|
+
onDeactivate() {
|
|
53
|
+
if (this._currentSpine &&= (this._currentSpine.state.clearTracks(), this._currentSpine.state.removeListener(this._currentListener), this._currentSpine.skeleton.setToSetupPose(), this._currentSpine.visible = !1, null), this._oneShotResolve) {
|
|
54
|
+
let e = this._oneShotResolve;
|
|
55
|
+
this._oneShotResolve = null, e();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async playWin() {
|
|
59
|
+
return this._playOneShot(this._animNameFor("win"), 0, !0);
|
|
60
|
+
}
|
|
61
|
+
async playLanding() {
|
|
62
|
+
return this._playOneShot(this._animNameFor("landing"), 0, !0);
|
|
63
|
+
}
|
|
64
|
+
async playOut() {
|
|
65
|
+
return this._playOneShot(this._animNameFor("out"), 0, !1);
|
|
66
|
+
}
|
|
67
|
+
playBlur() {
|
|
68
|
+
if (!this._currentSpine) return;
|
|
69
|
+
let e = this._animNameFor("blur");
|
|
70
|
+
this._currentSpine.skeleton.data.findAnimation(e) && this._currentSpine.state.setAnimation(0, e, !0);
|
|
71
|
+
}
|
|
72
|
+
playOnTrack(e, t, n = !1) {
|
|
73
|
+
return !this._currentSpine || !this._currentSpine.skeleton.data.findAnimation(t) ? null : this._currentSpine.state.setAnimation(e, t, n);
|
|
74
|
+
}
|
|
75
|
+
stopAnimation() {
|
|
76
|
+
if (!this._currentSpine) return;
|
|
77
|
+
this._currentSpine.state.removeListener(this._currentListener);
|
|
78
|
+
let e = this._animNameFor("idle");
|
|
79
|
+
if (this._currentSpine.skeleton.data.findAnimation(e) && this._currentSpine.state.setAnimation(0, e, !0), this._oneShotResolve) {
|
|
80
|
+
let e = this._oneShotResolve;
|
|
81
|
+
this._oneShotResolve = null, e();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
get spine() {
|
|
85
|
+
return this._currentSpine;
|
|
86
|
+
}
|
|
87
|
+
_currentListener = {};
|
|
88
|
+
async _playOneShot(e, t, n) {
|
|
89
|
+
if (!this._currentSpine) return;
|
|
90
|
+
let r = this._currentSpine;
|
|
91
|
+
if (r.skeleton.data.findAnimation(e)) return new Promise((i) => {
|
|
92
|
+
this._oneShotResolve = i;
|
|
93
|
+
let a = r.state.setAnimation(t, e, !1), o = { complete: (e) => {
|
|
94
|
+
if (e === a) {
|
|
95
|
+
if (r.state.removeListener(this._currentListener), n) {
|
|
96
|
+
let e = this._animNameFor("idle");
|
|
97
|
+
r.skeleton.data.findAnimation(e) && r.state.setAnimation(t, e, !0);
|
|
98
|
+
}
|
|
99
|
+
if (this._oneShotResolve) {
|
|
100
|
+
let e = this._oneShotResolve;
|
|
101
|
+
this._oneShotResolve = null, e();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} };
|
|
105
|
+
this._currentListener = o, r.state.addListener(o);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
resize(e, t) {
|
|
109
|
+
this._cellWidth = e, this._cellHeight = t;
|
|
110
|
+
for (let [, e] of this._spines) this._positionSpine(e);
|
|
111
|
+
}
|
|
112
|
+
_positionSpine(e) {
|
|
113
|
+
e.x = this._cellWidth / 2, e.y = this._cellHeight / 2;
|
|
114
|
+
}
|
|
115
|
+
onDestroy() {
|
|
116
|
+
for (let [, e] of this._spines) e.state.clearListeners(), e.destroy();
|
|
117
|
+
this._spines.clear();
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
//#endregion
|
|
121
|
+
export { r as SpineReelSymbol, t as SpineSymbol };
|
|
122
|
+
|
|
123
|
+
//# sourceMappingURL=spine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spine.js","names":[],"sources":["../src/spine/SpineReelSymbol.ts"],"sourcesContent":["import { Spine, type TrackEntry } from '@esotericsoftware/spine-pixi-v8';\nimport { ReelSymbol } from '../symbols/ReelSymbol.js';\n\n/**\n * Per-symbol overrides so a skeleton with unusual animation names still works.\n *\n * Example: Bonanza's `low_1` has a typo (`ide` instead of `idle`) —\n * `{ low1: { idle: 'ide' } }` fixes it without touching the asset.\n */\nexport type SymbolAnimOverrides = Record<\n string,\n Partial<Record<'idle' | 'landing' | 'win' | 'out' | 'blur', string>>\n>;\n\nexport interface SpineReelSymbolOptions {\n /** Map of symbolId -> { skeletonAlias, atlasAlias } */\n spineMap: Record<string, { skeleton: string; atlas: string }>;\n /** Default idle animation name. Default: 'idle'. */\n idleAnimation?: string;\n /** Default win animation name. Default: 'win'. */\n winAnimation?: string;\n /** Default landing (one-shot) animation name. Default: 'landing'. */\n landingAnimation?: string;\n /**\n * Default \"exit\" animation name — used as the cascade pop / disintegrate.\n * If the skeleton doesn't have it, callers should fall back to an alpha tween.\n * Default: 'disintegration'.\n */\n outAnimation?: string;\n /** Default \"blur\" (fast spin) animation name. Default: 'blur'. */\n blurAnimation?: string;\n /** Per-symbol overrides (see SymbolAnimOverrides). */\n animations?: SymbolAnimOverrides;\n /** Scale for spine instances. Default: 1. */\n scale?: number;\n /**\n * If true, automatically plays the `blur` animation when the owning reel\n * enters the spin phase, and reverts to idle when it lands. Requires the\n * skeleton to have a `blur` animation (or the overridden name). Default: false.\n */\n autoPlayBlur?: boolean;\n /**\n * If true, automatically plays the `landing` animation concurrently with\n * the stop-phase bounce. Requires the skeleton to have a `landing`\n * animation (or the overridden name). Default: false.\n */\n autoPlayLanding?: boolean;\n}\n\n/**\n * ReelSymbol implementation using Spine 2D skeletons.\n *\n * Caches one Spine instance per symbolId for instant swapping, plays idle on\n * activate, and exposes the canonical set of one-shot animations (`landing`,\n * `win`, `out`, reactions). Modeled on the Bonanza / Hold & Win slot-game\n * conventions — drop in any skeleton that follows the same vocabulary.\n *\n * Import from the `pixi-reels/spine` subpath so non-Spine consumers can\n * tree-shake this module and `@esotericsoftware/spine-pixi-v8` out of their\n * production bundle:\n *\n * ```ts\n * import { SpineReelSymbol } from 'pixi-reels/spine';\n * ```\n */\nexport class SpineReelSymbol extends ReelSymbol {\n private _spines = new Map<string, Spine>();\n private _currentSpine: Spine | null = null;\n private _spineMap: Record<string, { skeleton: string; atlas: string }>;\n private _defaultAnims: { idle: string; win: string; landing: string; out: string; blur: string };\n private _overrides: SymbolAnimOverrides;\n private _scale: number;\n private _autoPlayBlur: boolean;\n private _autoPlayLanding: boolean;\n private _oneShotResolve: (() => void) | null = null;\n private _cellWidth = 0;\n private _cellHeight = 0;\n\n constructor(options: SpineReelSymbolOptions) {\n super();\n this._spineMap = options.spineMap;\n this._defaultAnims = {\n idle: options.idleAnimation ?? 'idle',\n win: options.winAnimation ?? 'win',\n landing: options.landingAnimation ?? 'landing',\n out: options.outAnimation ?? 'disintegration',\n blur: options.blurAnimation ?? 'blur',\n };\n this._overrides = options.animations ?? {};\n this._scale = options.scale ?? 1;\n this._autoPlayBlur = options.autoPlayBlur ?? false;\n this._autoPlayLanding = options.autoPlayLanding ?? false;\n }\n\n override onReelSpinStart(): void {\n if (this._autoPlayBlur) this.playBlur();\n }\n\n override onReelSpinEnd(): void {\n if (this._autoPlayBlur) this.stopAnimation();\n }\n\n override onReelLanded(): void {\n if (this._autoPlayLanding) {\n void this.playLanding();\n }\n }\n\n /** Resolve an animation name for the current symbol, respecting overrides. */\n private _animNameFor(role: 'idle' | 'landing' | 'win' | 'out' | 'blur'): string {\n const override = this._overrides[this.symbolId]?.[role];\n return override ?? this._defaultAnims[role];\n }\n\n protected onActivate(symbolId: string): void {\n if (this._currentSpine) this._currentSpine.visible = false;\n\n let spine = this._spines.get(symbolId);\n if (!spine) {\n const cfg = this._spineMap[symbolId];\n if (!cfg) return;\n spine = Spine.from({ skeleton: cfg.skeleton, atlas: cfg.atlas });\n spine.scale.set(this._scale);\n this.view.addChild(spine);\n this._spines.set(symbolId, spine);\n }\n\n this._positionSpine(spine);\n spine.visible = true;\n this._currentSpine = spine;\n\n // Clear any lingering one-shot resolve from a prior pool use\n this._oneShotResolve = null;\n\n const idleName = this._animNameFor('idle');\n if (spine.skeleton.data.findAnimation(idleName)) {\n spine.state.setAnimation(0, idleName, true);\n }\n }\n\n protected onDeactivate(): void {\n if (this._currentSpine) {\n this._currentSpine.state.clearTracks();\n this._currentSpine.state.removeListener(this._currentListener);\n // Reset the skeleton to its setup pose, otherwise a symbol that ends on\n // an invisible \"out\" frame (e.g. after `playOut()` / disintegrate) is\n // still invisible when the pool reassigns it on the next spin.\n this._currentSpine.skeleton.setToSetupPose();\n this._currentSpine.visible = false;\n this._currentSpine = null;\n }\n if (this._oneShotResolve) {\n const fn = this._oneShotResolve;\n this._oneShotResolve = null;\n fn();\n }\n }\n\n // -- Canonical one-shot animations ---------------------------------------\n\n /** Play the win animation on track 0. Returns when it completes. */\n async playWin(): Promise<void> {\n return this._playOneShot(this._animNameFor('win'), 0, true);\n }\n\n /**\n * Play the landing animation (one-shot). Call this when the reel settles —\n * typically inside a `spin:reelLanded` listener.\n */\n async playLanding(): Promise<void> {\n return this._playOneShot(this._animNameFor('landing'), 0, true);\n }\n\n /**\n * Play the exit / disintegrate animation. Returns a promise that resolves\n * when it completes. Use in cascades instead of the default alpha fade.\n */\n async playOut(): Promise<void> {\n return this._playOneShot(this._animNameFor('out'), 0, false);\n }\n\n /**\n * Swap the primary track to the blur animation for the SPIN phase. Reverts\n * to idle automatically on `stopAnimation()` or next activate.\n */\n playBlur(): void {\n if (!this._currentSpine) return;\n const name = this._animNameFor('blur');\n if (!this._currentSpine.skeleton.data.findAnimation(name)) return;\n this._currentSpine.state.setAnimation(0, name, true);\n }\n\n /** Play an arbitrary animation on a given track. Non-blocking. */\n playOnTrack(track: number, animName: string, loop = false): TrackEntry | null {\n if (!this._currentSpine) return null;\n if (!this._currentSpine.skeleton.data.findAnimation(animName)) return null;\n return this._currentSpine.state.setAnimation(track, animName, loop);\n }\n\n stopAnimation(): void {\n if (!this._currentSpine) return;\n this._currentSpine.state.removeListener(this._currentListener);\n const idleName = this._animNameFor('idle');\n if (this._currentSpine.skeleton.data.findAnimation(idleName)) {\n this._currentSpine.state.setAnimation(0, idleName, true);\n }\n if (this._oneShotResolve) {\n const fn = this._oneShotResolve;\n this._oneShotResolve = null;\n fn();\n }\n }\n\n /** Access the underlying Spine — for advanced needs (reactions, events). */\n get spine(): Spine | null {\n return this._currentSpine;\n }\n\n // -- Internals -----------------------------------------------------------\n\n private _currentListener: {\n complete?: (entry: TrackEntry) => void;\n } = {};\n\n private async _playOneShot(\n animName: string,\n track: number,\n returnToIdle: boolean,\n ): Promise<void> {\n if (!this._currentSpine) return;\n const spine = this._currentSpine;\n if (!spine.skeleton.data.findAnimation(animName)) return;\n\n return new Promise<void>((resolve) => {\n this._oneShotResolve = resolve;\n const entry = spine.state.setAnimation(track, animName, false);\n const listener = {\n complete: (done: TrackEntry) => {\n if (done !== entry) return;\n spine.state.removeListener(this._currentListener);\n if (returnToIdle) {\n const idleName = this._animNameFor('idle');\n if (spine.skeleton.data.findAnimation(idleName)) {\n spine.state.setAnimation(track, idleName, true);\n }\n }\n if (this._oneShotResolve) {\n const fn = this._oneShotResolve;\n this._oneShotResolve = null;\n fn();\n }\n },\n };\n this._currentListener = listener;\n spine.state.addListener(listener);\n });\n }\n\n resize(width: number, height: number): void {\n this._cellWidth = width;\n this._cellHeight = height;\n for (const [, spine] of this._spines) this._positionSpine(spine);\n }\n\n private _positionSpine(spine: Spine): void {\n spine.x = this._cellWidth / 2;\n spine.y = this._cellHeight / 2;\n }\n\n protected override onDestroy(): void {\n for (const [, spine] of this._spines) {\n spine.state.clearListeners();\n spine.destroy();\n }\n this._spines.clear();\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Reel } from '../core/Reel.js';
|
|
2
|
+
import { ReelViewport } from '../core/ReelViewport.js';
|
|
3
|
+
import { SymbolPosition } from '../events/ReelEvents.js';
|
|
4
|
+
import { Disposable } from '../utils/Disposable.js';
|
|
5
|
+
export interface SpotlightOptions {
|
|
6
|
+
/** Opacity of the dim overlay (0-1). Default: 0.5. */
|
|
7
|
+
dimAmount?: number;
|
|
8
|
+
/** Whether to play win animation on spotlighted symbols. Default: true. */
|
|
9
|
+
playWinAnimation?: boolean;
|
|
10
|
+
/** Whether to re-parent symbols above the mask. Default: true. */
|
|
11
|
+
promoteAboveMask?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface WinLine {
|
|
14
|
+
positions: SymbolPosition[];
|
|
15
|
+
}
|
|
16
|
+
export interface CycleOptions extends SpotlightOptions {
|
|
17
|
+
/** Milliseconds to display each win line. Default: 2000. */
|
|
18
|
+
displayDuration?: number;
|
|
19
|
+
/** Milliseconds between lines. Default: 300. */
|
|
20
|
+
gapDuration?: number;
|
|
21
|
+
/** Number of cycles (-1 for infinite). Default: 1. */
|
|
22
|
+
cycles?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The "we just won" visual primitive.
|
|
26
|
+
*
|
|
27
|
+
* The spotlight is what turns a landed grid into a celebration. Given a
|
|
28
|
+
* list of winning cell positions, it:
|
|
29
|
+
*
|
|
30
|
+
* 1. Fades in the dim overlay behind everything (everything that is
|
|
31
|
+
* not winning visually sinks into the background).
|
|
32
|
+
* 2. Re-parents each winning `ReelSymbol` into the viewport's
|
|
33
|
+
* spotlight layer so its animation isn't clipped by the reel mask.
|
|
34
|
+
* 3. Calls `playWin()` on each winner (your symbol class's one-shot).
|
|
35
|
+
* 4. When you call `hide()` or the cycle ends, it puts every symbol
|
|
36
|
+
* back where it came from and removes the dim overlay.
|
|
37
|
+
*
|
|
38
|
+
* Two modes:
|
|
39
|
+
* - `show(positions, options)` — one-shot. Cell highlight + promote +
|
|
40
|
+
* play win. Returns when the animation fully ends.
|
|
41
|
+
* - `cycle(lines, options)` — iterate multiple win lines with a
|
|
42
|
+
* configurable per-line duration and gap, optionally repeating.
|
|
43
|
+
*
|
|
44
|
+
* Win detection is NOT part of this. pixi-reels never computes wins —
|
|
45
|
+
* your server / game code decides which cells are winners and passes
|
|
46
|
+
* them here. See [ADR 007](../../docs/adr/007-scope.md).
|
|
47
|
+
*/
|
|
48
|
+
export declare class SymbolSpotlight implements Disposable {
|
|
49
|
+
private _reels;
|
|
50
|
+
private _viewport;
|
|
51
|
+
private _promoted;
|
|
52
|
+
private _isActive;
|
|
53
|
+
private _isDestroyed;
|
|
54
|
+
private _cycleAbort;
|
|
55
|
+
constructor(reels: Reel[], viewport: ReelViewport);
|
|
56
|
+
get isActive(): boolean;
|
|
57
|
+
get isDestroyed(): boolean;
|
|
58
|
+
/** Show spotlight on specific positions. */
|
|
59
|
+
show(positions: SymbolPosition[], options?: SpotlightOptions): Promise<void>;
|
|
60
|
+
/** Hide the spotlight and return symbols to their original positions. */
|
|
61
|
+
hide(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Cycle through win lines, showing each for a duration.
|
|
64
|
+
* Returns when all cycles complete or when hide() is called.
|
|
65
|
+
*/
|
|
66
|
+
cycle(winLines: WinLine[], options?: CycleOptions): Promise<void>;
|
|
67
|
+
destroy(): void;
|
|
68
|
+
private _wait;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=SymbolSpotlight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SymbolSpotlight.d.ts","sourceRoot":"","sources":["../../src/spotlight/SymbolSpotlight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,WAAW,gBAAgB;IAC/B,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,YAAa,SAAQ,gBAAgB;IACpD,4DAA4D;IAC5D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAQD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,eAAgB,YAAW,UAAU;IAChD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,SAAS,CAAwB;IACzC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAgC;gBAEvC,KAAK,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,YAAY;IAKjD,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,4CAA4C;IACtC,IAAI,CAAC,SAAS,EAAE,cAAc,EAAE,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IA8CtF,yEAAyE;IACzE,IAAI,IAAI,IAAI;IAyBZ;;;OAGG;IACG,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IA0B3E,OAAO,IAAI,IAAI;IAMf,OAAO,CAAC,KAAK;CAad"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Texture } from 'pixi.js';
|
|
2
|
+
import { ReelSymbol } from './ReelSymbol.js';
|
|
3
|
+
export interface AnimatedSpriteSymbolOptions {
|
|
4
|
+
/** Map of symbolId → array of frame textures. */
|
|
5
|
+
frames: Record<string, Texture[]>;
|
|
6
|
+
/** Playback speed (frames per second multiplier). Default: 1. */
|
|
7
|
+
animationSpeed?: number;
|
|
8
|
+
/** Anchor point. Default: { x: 0.5, y: 0.5 }. */
|
|
9
|
+
anchor?: {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Symbol implementation using PixiJS AnimatedSprite.
|
|
16
|
+
* Swaps frame arrays on activate. Win animation plays the full sequence.
|
|
17
|
+
*/
|
|
18
|
+
export declare class AnimatedSpriteSymbol extends ReelSymbol {
|
|
19
|
+
private _animSprite;
|
|
20
|
+
private _frames;
|
|
21
|
+
private _animationSpeed;
|
|
22
|
+
private _winResolve;
|
|
23
|
+
constructor(options: AnimatedSpriteSymbolOptions);
|
|
24
|
+
protected onActivate(symbolId: string): void;
|
|
25
|
+
protected onDeactivate(): void;
|
|
26
|
+
playWin(): Promise<void>;
|
|
27
|
+
stopAnimation(): void;
|
|
28
|
+
resize(width: number, height: number): void;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=AnimatedSpriteSymbol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnimatedSpriteSymbol.d.ts","sourceRoot":"","sources":["../../src/symbols/AnimatedSpriteSymbol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,WAAW,2BAA2B;IAC1C,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IAClC,iEAAiE;IACjE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iDAAiD;IACjD,MAAM,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACnC;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,UAAU;IAClD,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,WAAW,CAA6B;gBAEpC,OAAO,EAAE,2BAA2B;IAehD,SAAS,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQ5C,SAAS,CAAC,YAAY,IAAI,IAAI;IAKxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAa9B,aAAa,IAAI,IAAI;IASrB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;CAI5C"}
|