@energy8platform/game-engine 0.38.0 → 0.40.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/README.md +82 -2
- package/dist/devtools.cjs.js +36 -4
- package/dist/devtools.cjs.js.map +1 -1
- package/dist/devtools.d.ts +67 -0
- package/dist/devtools.esm.js +36 -4
- package/dist/devtools.esm.js.map +1 -1
- package/dist/host.cjs.js +276 -109
- package/dist/host.cjs.js.map +1 -1
- package/dist/host.d.ts +5 -0
- package/dist/host.esm.js +276 -109
- package/dist/host.esm.js.map +1 -1
- package/dist/reel-panel-client.cjs.js +36 -4
- package/dist/reel-panel-client.cjs.js.map +1 -1
- package/dist/reel-panel-client.esm.js +36 -4
- package/dist/reel-panel-client.esm.js.map +1 -1
- package/dist/slot.cjs.js +229 -43
- package/dist/slot.cjs.js.map +1 -1
- package/dist/slot.d.ts +123 -8
- package/dist/slot.esm.js +229 -44
- package/dist/slot.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/host/autoplay.ts +25 -3
- package/src/host/connectionRecovery.ts +113 -0
- package/src/host/createSlotGame.ts +83 -103
- package/src/host/playError.ts +23 -0
- package/src/host/resumeDrain.ts +156 -0
- package/src/host/shellConfig.ts +9 -0
- package/src/slot/config/ReelSystemConfig.ts +96 -4
- package/src/slot/devtools/fieldSchema.ts +7 -0
- package/src/slot/index.ts +5 -0
- package/src/slot/motion/AnticipationController.ts +62 -17
- package/src/slot/motion/SpinEngine.ts +151 -23
- package/src/slot/system/ReelSystem.ts +59 -9
|
@@ -9,8 +9,16 @@ import { Tween } from '../../animation';
|
|
|
9
9
|
import { ReelGrid } from '../grid/ReelGrid';
|
|
10
10
|
import type { CellData } from '../grid/SymbolCell';
|
|
11
11
|
import type { SymbolResolver } from '../grid/SymbolView';
|
|
12
|
-
import {
|
|
13
|
-
|
|
12
|
+
import {
|
|
13
|
+
SpinEngine,
|
|
14
|
+
type ReelStopPlan,
|
|
15
|
+
type SpinData,
|
|
16
|
+
type SpinRunOpts,
|
|
17
|
+
} from '../motion/SpinEngine';
|
|
18
|
+
import {
|
|
19
|
+
AnticipationController,
|
|
20
|
+
type AnticipationDecision,
|
|
21
|
+
} from '../motion/AnticipationController';
|
|
14
22
|
import { TumbleController, type TumbleStep } from '../cascade/TumbleController';
|
|
15
23
|
import { ReelStepController, type ReelStepData } from '../cascade/ReelStepController';
|
|
16
24
|
import { FEATURES, FEATURE_LIST, type FeatureContext, type ReelFeature } from '../features';
|
|
@@ -65,6 +73,14 @@ export interface ReelSystem {
|
|
|
65
73
|
/** Replace the whole config. */
|
|
66
74
|
setConfig(config: ReelSystemConfig): void;
|
|
67
75
|
spin(target: CellData[][], opts?: SpinRunOpts): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* The schedule `spin(target, opts)` WOULD run, without running it — same anticipation decision,
|
|
78
|
+
* same numbers. Schedule landing sounds / camera moves against this instead of re-deriving the
|
|
79
|
+
* engine's formula. (`spin`'s `onPlan` hands you the same array once the spin is under way.)
|
|
80
|
+
*/
|
|
81
|
+
planSpin(target: CellData[][], opts?: SpinRunOpts): ReelStopPlan[];
|
|
82
|
+
/** The anticipation decision `spin(target, opts)` would use (run options override the config). */
|
|
83
|
+
anticipationFor(target: CellData[][], opts?: SpinRunOpts): AnticipationDecision;
|
|
68
84
|
/** Run a cascade chain. With `freeSpins` + `cascade.multiplier.persistInFreeSpins`, the multiplier
|
|
69
85
|
* carries over instead of resetting. Generic in the step type, so `onStep` hands back the game's
|
|
70
86
|
* own step (with its per-step win) rather than the bare TumbleStep. */
|
|
@@ -191,6 +207,36 @@ export function createReelSystem(opts: CreateReelSystemOptions): ReelSystem {
|
|
|
191
207
|
return { grid, resolve, cfg: config, fx, board, freeSpins, log };
|
|
192
208
|
}
|
|
193
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Which reels get the anticipation treatment for this spin. An explicit `anticipateReels` on the
|
|
212
|
+
* run options WINS over the configured decision — passing it is how a game drives anticipation
|
|
213
|
+
* from its own logic. Omit it and the configured `AnticipationController` decides.
|
|
214
|
+
*/
|
|
215
|
+
function resolveAnticipation(target: CellData[][], runOpts?: SpinRunOpts): AnticipationDecision {
|
|
216
|
+
const explicit = runOpts?.anticipateReels;
|
|
217
|
+
if (!explicit) return anticipation.decide(target);
|
|
218
|
+
if (!explicit.length) return { active: false, reels: [], slowdown: 1, holdMs: 0 };
|
|
219
|
+
return {
|
|
220
|
+
active: true,
|
|
221
|
+
reels: explicit.slice(),
|
|
222
|
+
slowdown: runOpts?.anticipateSlowdown ?? config.anticipation.slowdownFactor,
|
|
223
|
+
holdMs: runOpts?.anticipateHoldMs ?? config.anticipation.holdMs,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Fold a resolved decision back onto the caller's run options (everything else passes through). */
|
|
228
|
+
function mergeAnticipation(
|
|
229
|
+
runOpts: SpinRunOpts | undefined,
|
|
230
|
+
decision: AnticipationDecision,
|
|
231
|
+
): SpinRunOpts {
|
|
232
|
+
return {
|
|
233
|
+
...runOpts,
|
|
234
|
+
anticipateReels: decision.active ? decision.reels : undefined,
|
|
235
|
+
anticipateSlowdown: decision.slowdown,
|
|
236
|
+
anticipateHoldMs: decision.holdMs,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
194
240
|
buildGrid();
|
|
195
241
|
|
|
196
242
|
const api: ReelSystem = {
|
|
@@ -237,20 +283,24 @@ export function createReelSystem(opts: CreateReelSystemOptions): ReelSystem {
|
|
|
237
283
|
api.setConfig(mergeReelConfig(config, partial));
|
|
238
284
|
},
|
|
239
285
|
|
|
286
|
+
anticipationFor(target, runOpts) {
|
|
287
|
+
return resolveAnticipation(target, runOpts);
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
planSpin(target, runOpts) {
|
|
291
|
+
const decision = resolveAnticipation(target, runOpts);
|
|
292
|
+
return spin.plan({ targetGrid: target }, mergeAnticipation(runOpts, decision));
|
|
293
|
+
},
|
|
294
|
+
|
|
240
295
|
async spin(target, runOpts) {
|
|
241
296
|
const data: SpinData = { targetGrid: target };
|
|
242
|
-
const decision =
|
|
297
|
+
const decision = resolveAnticipation(target, runOpts);
|
|
243
298
|
let resetZoom: (() => Promise<void>) | null = null;
|
|
244
299
|
if (decision.active) {
|
|
245
300
|
log?.(`Anticipation on reels [${decision.reels.join(', ')}]`);
|
|
246
301
|
resetZoom = await anticipation.zoomIn(grid);
|
|
247
302
|
}
|
|
248
|
-
await spin.run(data,
|
|
249
|
-
...runOpts,
|
|
250
|
-
anticipateReels: decision.active ? decision.reels : undefined,
|
|
251
|
-
anticipateSlowdown: decision.slowdown,
|
|
252
|
-
anticipateHoldMs: decision.holdMs,
|
|
253
|
-
});
|
|
303
|
+
await spin.run(data, mergeAnticipation(runOpts, decision));
|
|
254
304
|
if (resetZoom) await resetZoom();
|
|
255
305
|
board = target;
|
|
256
306
|
},
|