initial
6
.babelrc
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"presets": [
|
||||
"@babel/preset-env",
|
||||
"@babel/preset-typescript"
|
||||
]
|
||||
}
|
||||
6
.barrelsby.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"directory": "./src",
|
||||
"exclude": ["index.ts","**/stories/**"],
|
||||
"delete": true,
|
||||
"exportDefault": false
|
||||
}
|
||||
12
.editorconfig
Normal file
@@ -0,0 +1,12 @@
|
||||
# This file is for unifying the coding style for different editors and IDEs.
|
||||
# More information at http://EditorConfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[{*.json,bower.json,.travis.yml}]
|
||||
8
.eslintignore
Normal file
@@ -0,0 +1,8 @@
|
||||
/docs/**
|
||||
**/dist/**
|
||||
**/lib/**
|
||||
**/types/**
|
||||
temp
|
||||
docs
|
||||
dist
|
||||
node_modules
|
||||
51
.eslintrc.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"extends": ["@pixi/eslint-config"],
|
||||
"plugins": ["jsdoc", "no-mixed-operators"],
|
||||
"settings": {
|
||||
"jsdoc": {
|
||||
"mode": "typescript",
|
||||
"tagNamePreference": {
|
||||
"method": "method",
|
||||
"function": "function",
|
||||
"extends": "extends",
|
||||
"typeParam": "typeParam",
|
||||
"api": "api"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unused-expressions": [1, {"allowShortCircuit": true, "allowTernary": true}],
|
||||
"no-mixed-operators": "off",
|
||||
"no-mixed-operators/no-mixed-operators": 1,
|
||||
"@typescript-eslint/type-annotation-spacing": 1,
|
||||
"jsdoc/multiline-blocks": [
|
||||
1,
|
||||
{ "noMultilineBlocks": true, "minimumLengthForMultiline": 115 }
|
||||
],
|
||||
"jsdoc/check-access": 1,
|
||||
"jsdoc/check-alignment": 1,
|
||||
"jsdoc/check-param-names": 1,
|
||||
"jsdoc/check-property-names": 1,
|
||||
"jsdoc/check-tag-names": 1,
|
||||
"jsdoc/check-types": 1,
|
||||
"jsdoc/check-values": 1,
|
||||
"jsdoc/empty-tags": 1,
|
||||
"jsdoc/implements-on-classes": 1,
|
||||
"jsdoc/no-multi-asterisks": [1, { "allowWhitespace": true }],
|
||||
"jsdoc/require-param": 1,
|
||||
"jsdoc/require-param-description": 0,
|
||||
"jsdoc/require-param-name": 1,
|
||||
"jsdoc/require-param-type": [
|
||||
"warn",
|
||||
{ "contexts": ["TSMethodSignature"] }
|
||||
],
|
||||
"jsdoc/require-property": 1,
|
||||
"jsdoc/require-property-description": 1,
|
||||
"jsdoc/require-property-name": 1,
|
||||
"jsdoc/require-property-type": 1,
|
||||
"jsdoc/require-returns-description": 1,
|
||||
"jsdoc/tag-lines": 1,
|
||||
"jsdoc/valid-types": 1,
|
||||
"max-len": ["warn", { "code": 150 }]
|
||||
}
|
||||
}
|
||||
7
.gitattributes
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
*.js text eol=lf
|
||||
*.ts text eol=lf
|
||||
*.json text eol=lf
|
||||
*.yml text eol=lf
|
||||
*.md text eol=lf
|
||||
*.txt text eol=lf
|
||||
43
.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# sublime text files
|
||||
*.sublime*
|
||||
*.vscode*
|
||||
*.*~*.TMP
|
||||
test/lib
|
||||
|
||||
|
||||
# temp files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
Desktop.ini
|
||||
npm-debug.log
|
||||
|
||||
# project files
|
||||
.project
|
||||
|
||||
# vim swap files
|
||||
*.sw*
|
||||
|
||||
# emacs temp files
|
||||
*~
|
||||
\#*#
|
||||
|
||||
# project ignores
|
||||
!.gitkeep
|
||||
*__temp
|
||||
node_modules
|
||||
bin/
|
||||
lib/
|
||||
dist/
|
||||
coverage/
|
||||
temp
|
||||
yarn.lock
|
||||
pnpm-lock.yaml
|
||||
|
||||
# jetBrains IDE ignores
|
||||
.idea
|
||||
.vs-code
|
||||
.eslintcache
|
||||
docs/
|
||||
example.api.json*
|
||||
|
||||
.npmrc
|
||||
4
.gitlab-ci.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
include:
|
||||
- project: infrastructure/gitlab_ci_templates
|
||||
file: entrypoints/front/npm-entrypoint.gitlab-ci.yml
|
||||
ref: main
|
||||
37
.storybook/main.js
Normal file
@@ -0,0 +1,37 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
stories: ['../src/stories/**/*.stories.@(ts|tsx|js|jsx|mdx)'],
|
||||
staticDirs: ['../src/stories/assets'],
|
||||
output: '../docs/',
|
||||
logLevel: 'debug',
|
||||
addons: [
|
||||
'@storybook/addon-docs',
|
||||
'@storybook/addon-actions',
|
||||
'@storybook/addon-backgrounds',
|
||||
'@storybook/addon-controls',
|
||||
'@storybook/addon-viewport',
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-highlight',
|
||||
'@storybook/addon-storysource',
|
||||
'@storybook/addon-webpack5-compiler-babel',
|
||||
'@chromatic-com/storybook'
|
||||
],
|
||||
core: {
|
||||
channelOptions: { allowFunction: false, maxDepth: 10 },
|
||||
disableTelemetry: true,
|
||||
},
|
||||
features: {
|
||||
buildStoriesJson: true,
|
||||
breakingChangesV7: true,
|
||||
babelModeV7: true,
|
||||
},
|
||||
framework: '@pixi/storybook-webpack5',
|
||||
webpackFinal: async (config) => {
|
||||
config.resolve.alias = {
|
||||
...config.resolve.alias,
|
||||
src: path.resolve(__dirname, '../src'),
|
||||
};
|
||||
return config;
|
||||
},
|
||||
};
|
||||
26
.storybook/preview.js
Normal file
@@ -0,0 +1,26 @@
|
||||
export const parameters = {
|
||||
layout: 'fullscreen',
|
||||
pixi: {
|
||||
applicationOptions: {
|
||||
backgroundAlpha: 0,
|
||||
resolution: 2,
|
||||
antialias: true,
|
||||
},
|
||||
},
|
||||
backgrounds: {
|
||||
default: 'Dark',
|
||||
values: [
|
||||
{
|
||||
name: 'Dark',
|
||||
value: '#1b1c1d',
|
||||
},
|
||||
{
|
||||
name: 'Light',
|
||||
value: '#dddddd',
|
||||
},
|
||||
],
|
||||
},
|
||||
docs: {
|
||||
iframeHeight: 600, // Устанавливает высоту для всех историй в документации
|
||||
},
|
||||
};
|
||||
61
CHANGELOG.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
|
||||
|
||||
## [1.2.2]
|
||||
### Added
|
||||
- SingleSlotReelsSpinSystem settings now expose `slotStopDelay` to pause between individual slot stops within a column while `reelStopDelay` continues to pace the first slot in each column, enabling layered timing without manual timers.
|
||||
- HoldAndWin stories expose `slotStopDelay` as a dedicated control to preview staggered slot timing.
|
||||
- HoldAndWin stories configure fractional steps for all numeric controls to enable fine-grained tuning during demos.
|
||||
- Added a stub `MaskingSystem` and wired it into Reels spin stories to prepare future masking orchestration work.
|
||||
- Introduced `BlurSymbolsSystem` to centralise blur orchestration for reels symbols.
|
||||
- Added dedicated Storybook demos for `MaskingSystem` and `BlurSymbolsSystem`, now mirroring the reel spin setup while piping Storybook controls through each system's `setSettings` method for live configuration tweaks.
|
||||
|
||||
### Changed
|
||||
- Reels reel states no longer toggle blur directly; `ReelsSpinSystem` marks blur intent while `BlurSymbolsSystem` applies filters.
|
||||
- `BlurSymbolsSystem` removes cached filter areas, zeroes padding, and supports runtime velocity/kernel/offset configuration through `setSettings`.
|
||||
- `MaskingSystem` exposes an `enabled` flag so Storybook demos and games can disable masking without removing the system from st
|
||||
ates.
|
||||
|
||||
### Fixed
|
||||
- SingleSlotReelsSpinSystem trims transient slot symbols and collapses each delayed reel to its landed symbol so duplicates cannot appear when `slotStopDelay` is enabled.
|
||||
- Column stop scheduling now respects `reelStopDelay` for the first slot in each column even while previous columns finish their queued slots, keeping staggered visuals aligned across reels.
|
||||
- Reel symbols remain visible when blur and masking are enabled together by removing conflicting bitmap caching from the blur toggle.
|
||||
- Motion blur applied to masked reel symbols now clips to the mask bounds so blur streaks no longer extend beyond the masked area.
|
||||
|
||||
## [1.2.1] - 2025-10-20
|
||||
### Added
|
||||
- Story: DoubleHoldAndWinStory showing two HoldAndWinMachine instances stacked vertically, each with different initMap/screenMap.
|
||||
|
||||
### Changed
|
||||
- HoldAndWin stories: scaled all MachineContainer instances to 0.5 (half size). In DoubleHoldAndWinStory both containers are scaled; spacing uses containerTop.height and respects scale. centerView alignment remains correct.
|
||||
|
||||
## [1.2.0] - 2025-10-20
|
||||
### Added
|
||||
- Introduced HoldAndWin machine scaffold and Storybook story (features/HoldAndWin).
|
||||
- Single-slot reels support:
|
||||
- SingleSlotReelsSpinSystem
|
||||
- SingleSlotReel
|
||||
- Hold-and-win demo assets and manifest:
|
||||
- configs/slotMachine.json
|
||||
- symbols, anticipation, winline assets, bitmap fonts, textures
|
||||
- Auto-trigger spin in HoldAndWin story with generated screenMap and applied spin settings.
|
||||
|
||||
### Changed
|
||||
- ReelsSpinSystem: support preventReelsSpin map to disable start/stop for selected reels; skip prevented reels in the spin flow.
|
||||
- ReelsMachine: integrate new maps/behavior for single-slot reels and preventReelsSpin handling.
|
||||
- Reel: adjustments to support single-slot behavior.
|
||||
- createScreenInputMap: generate maps aligned with the new HoldAndWin config.
|
||||
- SlotMachineConfig: extended with properties required by HoldAndWin and updated spinning behavior.
|
||||
- index.ts: export newly added modules.
|
||||
- ReelsSpinSystem story: updated with example demonstrating disabled first/last reels.
|
||||
|
||||
## [1.1.23] - 2025-10-20
|
||||
### Added
|
||||
- Initialize CHANGELOG for the current branch.
|
||||
|
||||
### Changed
|
||||
- Bump package version to 1.1.23.
|
||||
|
||||
22
README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# SlotMachines Framework
|
||||
|
||||
## SingleSlotReelsSpinSystem settings
|
||||
|
||||
`SingleSlotReelsSpinSystem` extends the base reel system with per-slot timing controls. In addition to the common spin settings, it now supports a `slotStopDelay` option that waits after each slot in a column actually stops before triggering the next slot in that same column. Column-to-column pacing continues to use `reelStopDelay`, so the first slot of each column still fires on the familiar cadence while the remaining slots inherit the intra-column delay. This enables sequential stopping visuals without manual timers and keeps the machine symbol map in sync as every slot lands. When a delay is configured the system trims transient filler symbols and collapses each reel to the final landed symbol, so no duplicate entities remain on screen while the column settles. A dedicated `slotStopDelay` knob is now available in `HoldAndWin.stories.ts` to preview the staggered stop cadence directly in Storybook, and every numeric control in that story uses fractional steps for precise tuning.
|
||||
|
||||
## SlotMultipliersSystem
|
||||
|
||||
`SlotMultipliersSystem` renders a configurable overlay entity for every reel cell based on `machine.maps.slotMultipliers`. The default `SlotMultiplierEntity` boots a Spine asset with `in`/`out` animations, injects a centred red label into the `placehoder` slot, and automatically hides itself when the outro finishes. The accompanying Storybook story (`SlotMultipliersSystem.stories.ts`) wires the feature into `HoldAndWinMachine`, generates random multiplier maps on every spin, and demonstrates how to swap the display entity via the system settings. The machine itself does not create or register the system; consumers should instantiate `SlotMultipliersSystem`, push it into the relevant state lists, and supply their preferred entity class when wiring machines together.
|
||||
|
||||
The system now keeps overlays alive across state transitions so multipliers that remain active do not replay their `in` animation. Clear `slotMultipliers` (or unregister the system) when the overlays should disappear entirely.
|
||||
|
||||
## MaskingSystem
|
||||
|
||||
`MaskingSystem` manages a reusable rectangular mask sized through system settings (defaulting to the machine configuration). When active it walks through every child entity each frame and synchronises their `mask` property with their `isMasked` flag: entities opt-in by toggling the flag to `true`, at which point the system applies its mask if no other mask is present; when the flag returns to `false` the system restores the entity's original rendering by clearing its mask. The mask graphic is created once, attached to the machine container, tinted red for easy debugging, and automatically refreshed when the system enters or leaves a state. Set the new `enabled` setting to `false` to globally disable masking without deregistering the system; the Storybook controls mirror this flag so designers can toggle masks while keeping the state machine intact.
|
||||
|
||||
`ReelsSpinSystem` now toggles `isMasked` for each symbol while its reel state is `Spinning` and keeps the flag active through deceleration, clearing it only once the reel enters `Bouncing` or `Stopped`. This keeps the mask in place for every symbol that is still visibly moving and ensures it disappears as soon as the reel finishes its bounce or comes to rest.
|
||||
|
||||
Storybook now ships focused demos for the masking and blur orchestration helpers:
|
||||
|
||||
- `src/stories/MaskingSystem.stories.ts` mirrors the standard spin story boot flow and forwards the enable/size controls straight into `MaskingSystem#setSettings`, making it easy to iterate on viewport dimensions while reels spin.
|
||||
- `src/stories/BlurSymbolsSystem.stories.ts` reuses the same template but exposes motion blur velocity, kernel size, and offset knobs that feed into `BlurSymbolsSystem#setSettings` for rapid strength tuning.
|
||||
24637
package-lock.json
generated
Normal file
115
package.json
Normal file
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"name": "@popiplay/slot-machines",
|
||||
"version": "1.2.1",
|
||||
"description": "It is a library that contains slot machines and their components, that are extensible to allow them to be used in any project",
|
||||
"homepage": "https://gitlab.popiplay.dev/fe/npm/slot-machines#readme",
|
||||
"bugs": "https://gitlab.popiplay.dev/fe/npm/slot-machines/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://gitlab.popiplay.dev/fe/npm/slot-machines.git"
|
||||
},
|
||||
"license": "ISC",
|
||||
"author": "Andrey Sharshov <andrey.sharshov@popiplay.com>",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./lib/index.mjs",
|
||||
"require": "./lib/index.js"
|
||||
},
|
||||
"./*": "./lib/*"
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"module": "./lib/index.mjs",
|
||||
"types": "./lib/index.d.ts",
|
||||
"files": [
|
||||
"lib",
|
||||
"dist/"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "npm i && npm run storybook",
|
||||
"build": "xs build",
|
||||
"clean": "xs clean",
|
||||
"deploy": "xs deploy",
|
||||
"docs": "xs docs && npm run storybook:build",
|
||||
"lint": "xs lint --max-warnings 0",
|
||||
"lint:fix": "xs lint --fix",
|
||||
"prepare": "husky install",
|
||||
"release": "npm i && xs bump,build,docs,publish,git-push",
|
||||
"serve": "xs serve",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"storybook:build": "storybook build --output-dir docs/storybook",
|
||||
"types": "xs types",
|
||||
"watch": "xs watch",
|
||||
"generate-barrels": "barrelsby --directory ./src --exclude index.ts, stories --delete --exportDefault false --singleQuotes"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,js,mjs}": [
|
||||
"npm run lint:fix --"
|
||||
]
|
||||
},
|
||||
"extensionConfig": {
|
||||
"lint": [
|
||||
"src"
|
||||
],
|
||||
"docsName": "Popiplay Slot Machines",
|
||||
"docsTitle": "Popiplay Slot Machines",
|
||||
"docsDescription": "API Documentation for Slot Machines components made with PixiJS",
|
||||
"docsKeyword": "PixiJS, SlotMachine, components"
|
||||
},
|
||||
"keywords": [
|
||||
"state",
|
||||
"machine"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@pixi/filter-motion-blur": "^5.1.1",
|
||||
"@popiplay/state-machine": "^1.0.6",
|
||||
"pixi-spine": "^4.0.4",
|
||||
"pixi.js": "^7.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.26.0",
|
||||
"@babel/preset-react": "^7.25.9",
|
||||
"@babel/preset-typescript": "^7.26.0",
|
||||
"@chromatic-com/storybook": "^3.2.1",
|
||||
"@mdx-js/loader": "^3.1.0",
|
||||
"@mdx-js/react": "^3.1.0",
|
||||
"@pixi/extension-scripts": "^2.4.1",
|
||||
"@pixi/filter-motion-blur": "^5.1.1",
|
||||
"@pixi/storybook-renderer": "^1.0.0",
|
||||
"@pixi/storybook-webpack5": "^1.0.0",
|
||||
"@popiplay/state-machine": "^1.0.5",
|
||||
"@rollup/plugin-commonjs": "^28.0.1",
|
||||
"@storybook/addon-docs": "^8.4.0",
|
||||
"@storybook/addon-essentials": "^8.4.0",
|
||||
"@storybook/addon-interactions": "^8.4.0",
|
||||
"@storybook/addon-links": "^8.4.0",
|
||||
"@storybook/addon-storysource": "^8.4.0",
|
||||
"@storybook/addon-webpack5-compiler-babel": "^3.0.3",
|
||||
"@storybook/test": "^8.4.0",
|
||||
"@storybook/types": "^8.4.0",
|
||||
"@types/babel__core": "^7.20.5",
|
||||
"@types/jest": "^29.5.14",
|
||||
"babel-loader": "^9.2.1",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-plugin-jsdoc": "^50.4.3",
|
||||
"eslint-plugin-no-mixed-operators": "^1.1.1",
|
||||
"husky": "^9.1.6",
|
||||
"jest": "^29.7.0",
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"lint-staged": "^15.2.10",
|
||||
"storybook": "^8.4.0",
|
||||
"tsc-alias": "^1.8.10",
|
||||
"typescript": "^5.6.3",
|
||||
"pixi-spine": "^4.0.4",
|
||||
"pixi.js": "^7.4.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"@popiplay:registry": "https://gitlab.popiplay.dev/api/v4/projects/fe%2Fnpm%2Fslot-machines/packages/npm/"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
12
src/entities/AnticipationBack.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {Spine} from "pixi-spine";
|
||||
import {Assets} from "pixi.js";
|
||||
import AnticipationFrame from "./AnticipationFrame";
|
||||
|
||||
|
||||
export default class AnticipationBack extends AnticipationFrame {
|
||||
public override create() {
|
||||
this.spine = new Spine(Assets.get("anticipation_back").spineData);
|
||||
this.addChild(this.spine);
|
||||
this.spine.autoUpdate = false;
|
||||
}
|
||||
}
|
||||
37
src/entities/AnticipationFrame.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Assets, Container } from 'pixi.js';
|
||||
import { ITrackEntry, Spine } from 'pixi-spine';
|
||||
|
||||
export default class AnticipationFrame extends Container
|
||||
{
|
||||
protected spine: Spine;
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
this.create();
|
||||
}
|
||||
|
||||
protected create()
|
||||
{
|
||||
this.spine = new Spine(Assets.get('anticipation').spineData);
|
||||
this.addChild(this.spine);
|
||||
this.spine.autoUpdate = false;
|
||||
}
|
||||
|
||||
update(dt: number): void
|
||||
{
|
||||
this.spine.update(dt * 0.001);
|
||||
}
|
||||
|
||||
show(): ITrackEntry
|
||||
{
|
||||
this.spine.state.setEmptyAnimation(0, 0.1);
|
||||
this.spine.state.addAnimation(0, 'in', false, 0);
|
||||
|
||||
return this.spine.state.addAnimation(0, 'idle', true, 0);
|
||||
}
|
||||
|
||||
hide(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'out', false);
|
||||
}
|
||||
}
|
||||
12
src/entities/AnticipationFront.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {Spine} from "pixi-spine";
|
||||
import {Assets} from "pixi.js";
|
||||
import AnticipationFrame from "./AnticipationFrame";
|
||||
|
||||
|
||||
export default class AnticipationFront extends AnticipationFrame {
|
||||
public override create() {
|
||||
this.spine = new Spine(Assets.get("anticipation_front").spineData);
|
||||
this.addChild(this.spine);
|
||||
this.spine.autoUpdate = false;
|
||||
}
|
||||
}
|
||||
74
src/entities/BasicWin.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { Container, Text } from 'pixi.js';
|
||||
import { Spine } from 'pixi-spine';
|
||||
|
||||
export default class BasicWin extends Container
|
||||
{
|
||||
spine: Spine;
|
||||
betValueText: Text;
|
||||
isShown: boolean;
|
||||
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
|
||||
this._create();
|
||||
}
|
||||
|
||||
_create()
|
||||
{
|
||||
this.betValueText = new Text();
|
||||
this.betValueText.style = {
|
||||
fill: 0xffffff,
|
||||
stroke: 0x000000,
|
||||
strokeThickness: 3,
|
||||
fontSize: 40,
|
||||
fontFamily: 'Arial',
|
||||
letterSpacing: 0,
|
||||
};
|
||||
this.betValueText.anchor.set(0.5);
|
||||
this.addChild(this.betValueText);
|
||||
this.value = '';
|
||||
this.isShown = false;
|
||||
this.betValueText.visible = false;
|
||||
}
|
||||
|
||||
get value(): string
|
||||
{
|
||||
return this.betValueText.text;
|
||||
}
|
||||
|
||||
set value(formattedValue: string)
|
||||
{
|
||||
this.betValueText.text = formattedValue;
|
||||
}
|
||||
|
||||
update(_dt:number)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
show()
|
||||
{
|
||||
if (this.isShown) return;
|
||||
this.betValueText.visible = true;
|
||||
this.isShown = true;
|
||||
}
|
||||
|
||||
hide()
|
||||
{
|
||||
if (!this.isShown) return;
|
||||
this.isShown = false;
|
||||
this.betValueText.visible = false;
|
||||
}
|
||||
|
||||
hidden()
|
||||
{
|
||||
this.isShown = false;
|
||||
this.betValueText.visible = false;
|
||||
}
|
||||
|
||||
override destroy(_options?: object)
|
||||
{
|
||||
super.destroy(_options);
|
||||
}
|
||||
}
|
||||
45
src/entities/CascadeSymbol.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { ITrackEntry } from 'pixi-spine';
|
||||
import { CanBeBlocked, CanPlayBouncing, CanPlayFalling, CanPlayIdle, CanPlayWin } from '../types/Entity';
|
||||
import Symbol from './Symbol';
|
||||
|
||||
export default class CascadeSymbol extends Symbol implements
|
||||
CanPlayIdle, CanPlayBouncing, CanBeBlocked, CanPlayWin, CanPlayFalling
|
||||
{
|
||||
public isBlocked: boolean = false;
|
||||
|
||||
/**
|
||||
* Triggers the idle animation for the symbol and returns track entry.
|
||||
* @returns A function to check the animation progress.
|
||||
*/
|
||||
idle(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'idle', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the bounce animation and returns track entry.
|
||||
* @returns A function to check the animation progress.
|
||||
*/
|
||||
bounce(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'bounce', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the win animation and returns track entry.
|
||||
* @returns A function to check the animation progress.
|
||||
*/
|
||||
win(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'win', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the falling animation and returns track entry.
|
||||
* @returns A function to check the animation progress.
|
||||
*/
|
||||
falling(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'falling', false);
|
||||
}
|
||||
}
|
||||
43
src/entities/ExpandedSymbol.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { CanPlayIn, CanPlayOut, CanPlaySpin, CanPlayWin } from '../types/Entity';
|
||||
import { ITrackEntry } from '../types/ITrackEntry';
|
||||
import ReelSymbol from './ReelSymbol';
|
||||
|
||||
/**
|
||||
* @class ExpandedSymbol
|
||||
* @extends ReelSymbol
|
||||
* @implements CanPlayWin
|
||||
* @implements CanPlayIn
|
||||
* @implements CanPlayOut
|
||||
* @implements CanPlaySpin
|
||||
* @description Represents an expanded symbol in a slot machine game that can play various animations such as spin, in, and out.
|
||||
*/
|
||||
export default class ExpandedSymbol extends ReelSymbol implements CanPlayWin, CanPlayIn, CanPlayOut, CanPlaySpin
|
||||
{
|
||||
reelIndex: number;
|
||||
/**
|
||||
* Triggers the spin animation for the symbol.
|
||||
* @returns {ITrackEntry} The track entry of the animation.
|
||||
*/
|
||||
public spin(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'spin', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the animation state to 'in' and returns the track entry.
|
||||
* @returns {ITrackEntry} The track entry for the 'in' animation.
|
||||
*/
|
||||
public in(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'in', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the animation state to 'out' and returns the track entry.
|
||||
* @returns {ITrackEntry} The track entry of the animation.
|
||||
*/
|
||||
public out(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'out', false);
|
||||
}
|
||||
}
|
||||
92
src/entities/ReelSymbol.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { ITrackEntry } from 'pixi-spine';
|
||||
import { MaskData, Rectangle } from 'pixi.js';
|
||||
import { CanBeBlocked, CanBeBlurred, CanBeLanded, CanBeMasked, CanPlayBouncing, CanPlayIdle, CanPlayWin } from '../types/Entity';
|
||||
import Symbol from './Symbol';
|
||||
|
||||
export default class ReelSymbol extends Symbol implements
|
||||
CanPlayIdle, CanPlayBouncing, CanBeBlocked, CanPlayWin, CanBeBlurred, CanBeLanded, CanBeMasked
|
||||
{
|
||||
public isBlocked: boolean = false;
|
||||
public isLanded: boolean = false;
|
||||
public isBlurred: boolean = false;
|
||||
public blurEnabled: boolean = false;
|
||||
public isMasked: boolean = false;
|
||||
private filterAreaRect?: Rectangle;
|
||||
|
||||
/**
|
||||
* Triggers the idle animation for the symbol and returns a function to check its progress.
|
||||
* @returns A function to check the animation progress.
|
||||
*/
|
||||
public idle(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'idle', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the bounce animation and returns a function to check its progress.
|
||||
* @returns A function to check the animation progress.
|
||||
*/
|
||||
public bounce(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'bounce', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the win animation and returns a function to check its progress.
|
||||
* @returns A function to check the animation progress.
|
||||
*/
|
||||
public win(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'win', false);
|
||||
}
|
||||
|
||||
public anticipate()
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'anticipation', false);
|
||||
}
|
||||
|
||||
/** Updates the blur clipping area to match the applied mask bounds. */
|
||||
private updateBlurClipping(): void
|
||||
{
|
||||
if (!this.blurEnabled || !this.isMasked)
|
||||
{
|
||||
this.filterArea = null;
|
||||
this.filterAreaRect = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
const mask = this.mask;
|
||||
|
||||
if (!mask || mask instanceof MaskData)
|
||||
{
|
||||
this.filterArea = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const maskBounds = mask.getBounds(true);
|
||||
|
||||
if (!maskBounds.width || !maskBounds.height)
|
||||
{
|
||||
this.filterArea = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.filterAreaRect)
|
||||
{
|
||||
this.filterAreaRect = maskBounds.clone();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.filterAreaRect.copyFrom(maskBounds);
|
||||
}
|
||||
|
||||
this.filterArea = this.filterAreaRect;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public override update(dt: number): void
|
||||
{
|
||||
super.update(dt);
|
||||
this.updateBlurClipping();
|
||||
}
|
||||
}
|
||||
155
src/entities/SlotMultiplierEntity.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import { Assets, Container, Text } from 'pixi.js';
|
||||
import { Spine } from 'pixi-spine';
|
||||
import { CanPlayIn, CanPlayOut, Updatable } from '../types/Entity';
|
||||
import { ITrackEntry } from '../types/ITrackEntry';
|
||||
|
||||
/**
|
||||
* Lightweight overlay entity for slot multipliers backed by a Spine animation.
|
||||
*/
|
||||
export default class SlotMultiplierEntity extends Container implements Updatable, CanPlayIn, CanPlayOut
|
||||
{
|
||||
private spine: Spine;
|
||||
private assetKey: string;
|
||||
private shown: boolean;
|
||||
private label: Text;
|
||||
private valueInternal = 0;
|
||||
|
||||
constructor(assetKey: string = 'slot_multiplier')
|
||||
{
|
||||
super();
|
||||
this.assetKey = assetKey;
|
||||
this.shown = false;
|
||||
this.spine = this.createSpine();
|
||||
this.visible = false;
|
||||
this.addChild(this.spine);
|
||||
this.label = this.createLabel();
|
||||
this.attachLabel();
|
||||
this.registerOutListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes current visibility state used by systems to prevent redundant transitions.
|
||||
*/
|
||||
public isShown(): boolean
|
||||
{
|
||||
return this.shown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the "in" animation and reveals the entity.
|
||||
*/
|
||||
public in(): ITrackEntry
|
||||
{
|
||||
this.visible = true;
|
||||
this.shown = true;
|
||||
|
||||
return this.spine.state.setAnimation(0, 'in', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the "out" animation and keeps the entity visible until completion.
|
||||
*/
|
||||
public out(): ITrackEntry
|
||||
{
|
||||
this.shown = false;
|
||||
|
||||
return this.spine.state.setAnimation(0, 'out', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Current multiplier value displayed by the overlay text.
|
||||
*/
|
||||
public get value(): number
|
||||
{
|
||||
return this.valueInternal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the overlay text with the provided multiplier value.
|
||||
*/
|
||||
public set value(value: number)
|
||||
{
|
||||
this.valueInternal = value;
|
||||
this.label.text = Number.isFinite(value) ? value.toString() + "x" : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the underlying Spine animation.
|
||||
*/
|
||||
public update(dt: number): void
|
||||
{
|
||||
this.spine.update(dt * 0.001);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Spine instance for the multiplier overlay.
|
||||
*/
|
||||
private createSpine(): Spine
|
||||
{
|
||||
const asset = Assets.get(this.assetKey);
|
||||
const spine = new Spine(asset.spineData);
|
||||
spine.autoUpdate = false;
|
||||
|
||||
return spine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a red, centred text label for displaying the multiplier value.
|
||||
*/
|
||||
private createLabel(): Text
|
||||
{
|
||||
const text = new Text('', {
|
||||
fill: 0xffffff,
|
||||
fontSize: 24,
|
||||
});
|
||||
text.anchor.set(0.5);
|
||||
text.scale.set(1, -1)
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the label to the Spine placeholder slot container.
|
||||
*/
|
||||
private attachLabel(): void
|
||||
{
|
||||
const placeholderIndex = this.findPlaceholderSlotIndex();
|
||||
if (placeholderIndex < 0)
|
||||
{
|
||||
this.addChild(this.label);
|
||||
return;
|
||||
}
|
||||
|
||||
const container = this.spine.slotContainers[placeholderIndex];
|
||||
if (!container)
|
||||
{
|
||||
this.addChild(this.label);
|
||||
return;
|
||||
}
|
||||
|
||||
container.addChild(this.label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the placeholder slot index from the Spine skeleton.
|
||||
*/
|
||||
private findPlaceholderSlotIndex(): number
|
||||
{
|
||||
return this.spine.skeleton.findSlotIndex('placeholder');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the container once the "out" animation completes.
|
||||
*/
|
||||
private registerOutListener(): void
|
||||
{
|
||||
this.spine.state.addListener({
|
||||
complete: (entry: ITrackEntry) =>
|
||||
{
|
||||
if (entry.animation.name !== 'out') return;
|
||||
|
||||
this.visible = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
63
src/entities/StaticSymbol.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { ITrackEntry } from 'pixi-spine';
|
||||
import {
|
||||
CanBeBlocked,
|
||||
CanPlayBouncing,
|
||||
CanPlayFalling,
|
||||
CanPlayIdle,
|
||||
CanPlayIn,
|
||||
CanPlayOut,
|
||||
CanPlayWin
|
||||
} from '../types/Entity';
|
||||
import Symbol from './Symbol';
|
||||
|
||||
export default class StaticSymbol extends Symbol implements
|
||||
CanPlayIdle, CanPlayBouncing, CanBeBlocked, CanPlayWin, CanPlayFalling, CanPlayIn, CanPlayOut
|
||||
{
|
||||
public isBlocked: boolean = false;
|
||||
|
||||
/**
|
||||
* Triggers the idle animation for the symbol and returns track entry.
|
||||
* @returns A function to check the animation progress.
|
||||
*/
|
||||
idle(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'idle', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the bounce animation and returns track entry.
|
||||
* @returns A function to check the animation progress.
|
||||
*/
|
||||
bounce(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'bounce', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the win animation and returns track entry.
|
||||
* @returns A function to check the animation progress.
|
||||
*/
|
||||
win(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'win', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the falling animation and returns track entry.
|
||||
* @returns A function to check the animation progress.
|
||||
*/
|
||||
falling(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'falling', false);
|
||||
}
|
||||
|
||||
in(delay = 0)
|
||||
{
|
||||
return this.spine.state.addAnimation(0, 'in', false, delay);
|
||||
}
|
||||
|
||||
out(delay = 0)
|
||||
{
|
||||
return this.spine.state.addAnimation(0, 'out', false, delay);
|
||||
}
|
||||
}
|
||||
89
src/entities/Symbol.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { Assets, Container } from 'pixi.js';
|
||||
import { Spine } from 'pixi-spine';
|
||||
import { Entity, Updatable } from '../types/Entity';
|
||||
import { SymbolConfig } from '../types/SlotMachineConfig';
|
||||
import { getUniqueId } from '../utils/getUniqueId';
|
||||
|
||||
/**
|
||||
* @class Symbol
|
||||
* @extends Container
|
||||
* @description Represents a symbol in the slot machine with various animations and states.
|
||||
*/
|
||||
export default class Symbol extends Container implements Entity, Updatable
|
||||
{
|
||||
protected config: SymbolConfig;
|
||||
private _id?: string;
|
||||
protected spine: Spine;
|
||||
|
||||
/** Returns a unique identifier for the symbol instance. */
|
||||
override get name(): string
|
||||
{
|
||||
if (!this._id)
|
||||
{
|
||||
this._id = getUniqueId();
|
||||
}
|
||||
|
||||
return this._id;
|
||||
}
|
||||
|
||||
/** Gets or sets the animation time scale for the symbol's spine animation. */
|
||||
get timeScale(): number
|
||||
{
|
||||
return this.spine.state.timeScale;
|
||||
}
|
||||
|
||||
set timeScale(timeScale: number)
|
||||
{
|
||||
this.spine.state.timeScale = timeScale;
|
||||
}
|
||||
|
||||
/** Returns the key associated with the symbol configuration. */
|
||||
get key(): string
|
||||
{
|
||||
return this.config.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a symbol with the provided configuration.
|
||||
* @class
|
||||
* @param config - Configuration settings for the symbol, including spine asset and skin.
|
||||
*/
|
||||
constructor(config: SymbolConfig)
|
||||
{
|
||||
super();
|
||||
this.config = config;
|
||||
this._create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes the spine animation for the symbol based on its configuration.
|
||||
* @private
|
||||
*/
|
||||
private _create(): void
|
||||
{
|
||||
this.spine = new Spine(Assets.get(this.config.spine).spineData);
|
||||
this.spine.autoUpdate = false;
|
||||
this.spine.skeleton.setSkinByName(this.config.skin);
|
||||
this.spine.stateData.defaultMix = 0.5;
|
||||
this.addChild(this.spine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the spine animation based on delta time.
|
||||
* @param dt - Delta time in milliseconds since the last update.
|
||||
*/
|
||||
update(dt: number): void
|
||||
{
|
||||
this.spine.update(dt * 0.001);
|
||||
}
|
||||
|
||||
public tint(hexColor: number): void
|
||||
{
|
||||
this.spine.tint = hexColor;
|
||||
}
|
||||
|
||||
public reset()
|
||||
{
|
||||
this.spine.state.setEmptyAnimation(0, 0.1);
|
||||
}
|
||||
}
|
||||
43
src/entities/ValuableExpandedSymbol.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { CanPlayIn, CanPlayOut, CanPlaySpin, CanPlayWin } from '../types/Entity';
|
||||
import { ITrackEntry } from '../types/ITrackEntry';
|
||||
import ValuableReelSymbol from './ValuableReelSymbol';
|
||||
|
||||
/**
|
||||
* @class ValuableExpandedSymbol
|
||||
* @extends ValuableReelSymbol
|
||||
* @implements CanPlayWin
|
||||
* @implements CanPlayIn
|
||||
* @implements CanPlayOut
|
||||
* @implements CanPlaySpin
|
||||
* @description Represents an expanded symbol in a slot machine game that can play various animations such as spin, in, and out.
|
||||
*/
|
||||
export default class ValuableExpandedSymbol extends ValuableReelSymbol implements CanPlayWin, CanPlayIn, CanPlayOut, CanPlaySpin
|
||||
{
|
||||
reelIndex: number;
|
||||
/**
|
||||
* Triggers the spin animation for the symbol.
|
||||
* @returns {ITrackEntry} The track entry of the animation.
|
||||
*/
|
||||
public spin(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'spin', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the animation state to 'in' and returns the track entry.
|
||||
* @returns {ITrackEntry} The track entry for the 'in' animation.
|
||||
*/
|
||||
public in(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'in', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the animation state to 'out' and returns the track entry.
|
||||
* @returns {ITrackEntry} The track entry of the animation.
|
||||
*/
|
||||
public out(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'out', false);
|
||||
}
|
||||
}
|
||||
48
src/entities/ValuableReelSymbol.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import {BitmapText} from 'pixi.js';
|
||||
import { SymbolConfig } from '../types/SlotMachineConfig';
|
||||
import ReelSymbol from './ReelSymbol';
|
||||
|
||||
export default class ValuableReelSymbol extends ReelSymbol
|
||||
{
|
||||
private textObj: BitmapText;
|
||||
private _value: number;
|
||||
constructor(config: SymbolConfig)
|
||||
{
|
||||
super(config);
|
||||
}
|
||||
|
||||
public set value(v: number)
|
||||
{
|
||||
if (v <= 0) return;
|
||||
!this.textObj && this.createValueText();
|
||||
this._value = v;
|
||||
if (this.textObj) {
|
||||
this.textObj.text = `X${v}`;
|
||||
}
|
||||
}
|
||||
|
||||
public get value(): number
|
||||
{
|
||||
return this._value;
|
||||
}
|
||||
|
||||
private createValueText()
|
||||
{
|
||||
if (!this.config.fontStyle || !this.config.fontStyle.fontName) return;
|
||||
this.textObj = new BitmapText(`X${this.config.value}`, this.config.fontStyle);
|
||||
this.textObj.anchor.set(0.5);
|
||||
this.textObj.scale.set(1, -1);
|
||||
const slotIndex = this.spine.skeleton.findSlotIndex('value');
|
||||
|
||||
this.spine.slotContainers[slotIndex].addChild(this.textObj);
|
||||
}
|
||||
|
||||
public override tint(hexColor: number): void
|
||||
{
|
||||
super.tint(hexColor);
|
||||
if (this.textObj)
|
||||
{
|
||||
this.textObj.tint = hexColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
439
src/entities/WebGLWinLine.ts
Normal file
@@ -0,0 +1,439 @@
|
||||
import {
|
||||
Container,
|
||||
Geometry,
|
||||
Mesh,
|
||||
MeshMaterial,
|
||||
MIPMAP_MODES,
|
||||
Point,
|
||||
Program,
|
||||
Texture,
|
||||
WRAP_MODES,
|
||||
} from 'pixi.js';
|
||||
|
||||
class ScrollingMeshMaterial extends MeshMaterial
|
||||
{
|
||||
constructor(texture: Texture)
|
||||
{
|
||||
const vertexSrc = `
|
||||
precision mediump float;
|
||||
attribute vec2 aVertexPosition;
|
||||
attribute vec2 aTextureCoord;
|
||||
uniform mat3 translationMatrix;
|
||||
uniform mat3 projectionMatrix;
|
||||
varying vec2 vTextureCoord;
|
||||
void main(void) {
|
||||
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
|
||||
vTextureCoord = aTextureCoord;
|
||||
}
|
||||
`;
|
||||
|
||||
const fragmentSrc = `
|
||||
precision mediump float;
|
||||
varying vec2 vTextureCoord;
|
||||
uniform sampler2D uSampler;
|
||||
uniform float uTextureOffset;
|
||||
void main(void) {
|
||||
vec2 uv = vec2(vTextureCoord.x + uTextureOffset, vTextureCoord.y);
|
||||
gl_FragColor = texture2D(uSampler, uv);
|
||||
}
|
||||
`;
|
||||
|
||||
const program = Program.from(vertexSrc, fragmentSrc);
|
||||
|
||||
super(texture, {
|
||||
program,
|
||||
uniforms: { uTextureOffset: 0 },
|
||||
});
|
||||
|
||||
// Важно: разрешить повтор, иначе при смещении по U появятся швы
|
||||
if (texture?.baseTexture)
|
||||
{
|
||||
texture.baseTexture.wrapMode = WRAP_MODES.REPEAT;
|
||||
texture.baseTexture.mipmap = MIPMAP_MODES.ON;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Extend this class and override getters: texture, filletRadius, lineThickness, duration. */
|
||||
export default class WebGLWinLine extends Container
|
||||
{
|
||||
private readonly mesh: Mesh;
|
||||
private time: number = 0;
|
||||
public state: 'stopped' | 'animating' = 'stopped';
|
||||
|
||||
get texture(): Texture
|
||||
{
|
||||
return Texture.from('textures/win_line.png');
|
||||
}
|
||||
|
||||
get filletRadius()
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
|
||||
get lineThickness()
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
|
||||
/** duration in seconds */
|
||||
get duration()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
constructor(points: number[][], slotWidth: number = 100, slotHeight: number = 100)
|
||||
{
|
||||
super();
|
||||
|
||||
const centers = points.map(([reel, row]) =>
|
||||
new Point((reel * slotWidth) + (slotWidth / 2), (row * slotHeight) + (slotHeight / 2))
|
||||
);
|
||||
|
||||
const centerCurveRaw = this.computeFilletCurve(centers, this.filletRadius);
|
||||
const centerCurve = dedupeAndColinearCull(centerCurveRaw);
|
||||
|
||||
this.mesh = this.createThickLineMesh(centerCurve, this.lineThickness, this.texture);
|
||||
this.addChild(this.mesh);
|
||||
}
|
||||
|
||||
private computeFilletCurve(points: Point[], radius: number): Point[]
|
||||
{
|
||||
// Без филета/на острых углах важно не плодить нулевые сегменты
|
||||
const result: Point[] = [];
|
||||
|
||||
if (points.length < 2) return [...points];
|
||||
if (points.length === 2) return [points[0], points[1]];
|
||||
const sampleArc = (center: Point, startAngle: number, endAngle: number, segments: number): Point[] =>
|
||||
{
|
||||
const arcPoints: Point[] = [];
|
||||
|
||||
segments = Math.max(2, segments);
|
||||
const delta = (endAngle - startAngle) / (segments - 1);
|
||||
|
||||
for (let i = 0; i < segments; i++)
|
||||
{
|
||||
const a = startAngle + (delta * i);
|
||||
|
||||
arcPoints.push(new Point(center.x + (radius * Math.cos(a)), center.y + (radius * Math.sin(a))));
|
||||
}
|
||||
|
||||
return arcPoints;
|
||||
};
|
||||
|
||||
let prevT2: Point | null = null;
|
||||
|
||||
// --- первый узел ---
|
||||
{
|
||||
const [p0, p1, p2] = [points[0], points[1], points[2]];
|
||||
const d1 = normalizeVector(p0.x - p1.x, p0.y - p1.y);
|
||||
const d2 = normalizeVector(p2.x - p1.x, p2.y - p1.y);
|
||||
const dot = clamp((d1[0] * d2[0]) + (d1[1] * d2[1]), -1, 1);
|
||||
const theta = Math.acos(dot);
|
||||
|
||||
// если угол ≈ 0 (прямая), не создаём псевдо-дугу
|
||||
if (theta < 1e-4)
|
||||
{
|
||||
result.push(p0, p1);
|
||||
prevT2 = p1;
|
||||
}
|
||||
else
|
||||
{
|
||||
const offset = radius / Math.tan(theta / 2);
|
||||
const T1 = new Point(p1.x + (d1[0] * offset), p1.y + (d1[1] * offset));
|
||||
const T2 = new Point(p1.x + (d2[0] * offset), p1.y + (d2[1] * offset));
|
||||
|
||||
result.push(p0, T1);
|
||||
|
||||
const bisector = new Point(d1[0] + d2[0], d1[1] + d2[1]);
|
||||
const bisectorLen = Math.hypot(bisector.x, bisector.y);
|
||||
|
||||
if (bisectorLen > 0)
|
||||
{
|
||||
const bisectorNorm = new Point(bisector.x / bisectorLen, bisector.y / bisectorLen);
|
||||
const centerDistance = radius / Math.sin(theta / 2);
|
||||
const C = new Point(p1.x + (bisectorNorm.x * centerDistance), p1.y + (bisectorNorm.y * centerDistance));
|
||||
const startAngle = Math.atan2(T1.y - C.y, T1.x - C.x);
|
||||
const arcSweep = Math.PI - theta;
|
||||
const endAngle = startAngle + (cross2(T1, T2, C) < 0 ? -arcSweep : arcSweep);
|
||||
const arcPoints = sampleArc(C, startAngle, endAngle, 10);
|
||||
|
||||
result.push(...arcPoints.slice(1));
|
||||
prevT2 = T2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// почти 180°: просто соединяем через вершину
|
||||
result.push(p1);
|
||||
prevT2 = p1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- внутренние узлы ---
|
||||
for (let i = 2; i < points.length - 1; i++)
|
||||
{
|
||||
const pPrev = points[i - 1];
|
||||
const pCurr = points[i];
|
||||
const pNext = points[i + 1];
|
||||
|
||||
const v1 = normalizeVector(pPrev.x - pCurr.x, pPrev.y - pCurr.y);
|
||||
const v2 = normalizeVector(pNext.x - pCurr.x, pNext.y - pCurr.y);
|
||||
const dot = clamp((v1[0] * v2[0]) + (v1[1] * v2[1]), -1, 1);
|
||||
const theta = Math.acos(dot);
|
||||
|
||||
if (theta < 1e-4)
|
||||
{
|
||||
// прямая — не вставляем T1/T2
|
||||
if (prevT2) result.push(pCurr);
|
||||
prevT2 = pCurr;
|
||||
continue;
|
||||
}
|
||||
|
||||
const offset = radius / Math.tan(theta / 2);
|
||||
const T1 = new Point(pCurr.x + (v1[0] * offset), pCurr.y + (v1[1] * offset));
|
||||
const T2 = new Point(pCurr.x + (v2[0] * offset), pCurr.y + (v2[1] * offset));
|
||||
|
||||
if (prevT2) result.push(T1);
|
||||
|
||||
const bisector = new Point(v1[0] + v2[0], v1[1] + v2[1]);
|
||||
const bisectorLen = Math.hypot(bisector.x, bisector.y);
|
||||
|
||||
if (bisectorLen > 0)
|
||||
{
|
||||
const bisectorNorm = new Point(bisector.x / bisectorLen, bisector.y / bisectorLen);
|
||||
const centerDistance = radius / Math.sin(theta / 2);
|
||||
const C = new Point(pCurr.x + (bisectorNorm.x * centerDistance), pCurr.y + (bisectorNorm.y * centerDistance));
|
||||
const startAngle = Math.atan2(T1.y - C.y, T1.x - C.x);
|
||||
const arcSweep = Math.PI - theta;
|
||||
const endAngle = startAngle + (cross2(T1, T2, C) < 0 ? -arcSweep : arcSweep);
|
||||
const arcPoints = sampleArc(C, startAngle, endAngle, 10);
|
||||
|
||||
result.push(...arcPoints.slice(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push(pCurr);
|
||||
}
|
||||
prevT2 = T2;
|
||||
}
|
||||
|
||||
// --- хвост ---
|
||||
const lastPoint = points[points.length - 1];
|
||||
|
||||
if (prevT2)
|
||||
{
|
||||
result.push(prevT2, lastPoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push(lastPoint);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает Mesh c толщиной по центральной кривой.
|
||||
* Митер-нормали с ограничением устраняют «ломы» на острых углах без обязательного скругления.
|
||||
* @param curvePoints
|
||||
* @param thickness
|
||||
* @param texture
|
||||
*/
|
||||
private createThickLineMesh(curvePoints: Point[], thickness: number, texture: Texture): Mesh
|
||||
{
|
||||
const vertices: number[] = [];
|
||||
const uvs: number[] = [];
|
||||
const indices: number[] = [];
|
||||
const n = curvePoints.length;
|
||||
const halfThickness = thickness / 2;
|
||||
|
||||
// накопленные длины для UV
|
||||
let totalLength = 0;
|
||||
const lengths: number[] = [0];
|
||||
|
||||
for (let i = 1; i < n; i++)
|
||||
{
|
||||
const dx = curvePoints[i].x - curvePoints[i - 1].x;
|
||||
const dy = curvePoints[i].y - curvePoints[i - 1].y;
|
||||
const seg = Math.hypot(dx, dy);
|
||||
|
||||
totalLength += seg;
|
||||
lengths.push(totalLength);
|
||||
}
|
||||
|
||||
// —— митер-нормали ——
|
||||
const MITER_LIMIT = 4;
|
||||
|
||||
function unit(x: number, y: number)
|
||||
{
|
||||
const l = Math.hypot(x, y);
|
||||
|
||||
return l > 0 ? { x: x / l, y: y / l } : { x: 0, y: 0 };
|
||||
}
|
||||
function perp(v: {x: number;y: number}) { return { x: -v.y, y: v.x }; }
|
||||
|
||||
function computeNormal(i: number): { x: number; y: number }
|
||||
{
|
||||
if (n === 1) return { x: 0, y: 0 };
|
||||
if (i === 0)
|
||||
{
|
||||
const d = unit(curvePoints[1].x - curvePoints[0].x, curvePoints[1].y - curvePoints[0].y);
|
||||
|
||||
return perp(d);
|
||||
}
|
||||
if (i === n - 1)
|
||||
{
|
||||
const d = unit(curvePoints[n - 1].x - curvePoints[n - 2].x, curvePoints[n - 1].y - curvePoints[n - 2].y);
|
||||
|
||||
return perp(d);
|
||||
}
|
||||
const dPrev = unit(curvePoints[i].x - curvePoints[i - 1].x, curvePoints[i].y - curvePoints[i - 1].y);
|
||||
const dNext = unit(curvePoints[i + 1].x - curvePoints[i].x, curvePoints[i + 1].y - curvePoints[i].y);
|
||||
|
||||
const sum = { x: dPrev.x + dNext.x, y: dPrev.y + dNext.y };
|
||||
const sumLen = Math.hypot(sum.x, sum.y);
|
||||
|
||||
if (sumLen < 1e-6)
|
||||
{
|
||||
// угол ~180°
|
||||
return perp(dNext);
|
||||
}
|
||||
const t = { x: sum.x / sumLen, y: sum.y / sumLen }; // усреднённая касательная
|
||||
const nAvg = perp(t);
|
||||
|
||||
// miter scale = 1 / dot(nAvg, nNext)
|
||||
const nNext = perp(dNext);
|
||||
const denom = (nAvg.x * nNext.x) + (nAvg.y * nNext.y);
|
||||
let miterScale = 1 / Math.max(denom, 1e-4);
|
||||
|
||||
miterScale = Math.min(miterScale, MITER_LIMIT);
|
||||
|
||||
return { x: nAvg.x * miterScale, y: nAvg.y * miterScale };
|
||||
}
|
||||
|
||||
// вершины + UV
|
||||
for (let i = 0; i < n; i++)
|
||||
{
|
||||
const p = curvePoints[i];
|
||||
const normal = computeNormal(i);
|
||||
|
||||
vertices.push(p.x - (normal.x * halfThickness), p.y - (normal.y * halfThickness));
|
||||
vertices.push(p.x + (normal.x * halfThickness), p.y + (normal.y * halfThickness));
|
||||
|
||||
const u = totalLength > 0 ? lengths[i] / totalLength : 0;
|
||||
|
||||
uvs.push(u, 0);
|
||||
uvs.push(u, 1);
|
||||
}
|
||||
|
||||
// индексы (два треугольника на сегмент)
|
||||
for (let i = 0; i < n - 1; i++)
|
||||
{
|
||||
const idx = i * 2;
|
||||
|
||||
indices.push(idx, idx + 1, idx + 2);
|
||||
indices.push(idx + 1, idx + 3, idx + 2);
|
||||
}
|
||||
|
||||
const geometry = new Geometry()
|
||||
.addAttribute('aVertexPosition', vertices, 2)
|
||||
.addAttribute('aTextureCoord', uvs, 2)
|
||||
.addIndex(indices);
|
||||
|
||||
return new Mesh(geometry, new ScrollingMeshMaterial(texture));
|
||||
}
|
||||
|
||||
public update(dt: number)
|
||||
{
|
||||
this.time += dt * 0.001;
|
||||
if (this.state === 'stopped')
|
||||
{
|
||||
this.visible = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state === 'animating')
|
||||
{
|
||||
this.visible = true;
|
||||
const material = this.mesh.material as ScrollingMeshMaterial;
|
||||
|
||||
material.uniforms.uTextureOffset = 1 - (2 * this.time / this.duration);
|
||||
if (material.uniforms.uTextureOffset <= -1)
|
||||
{
|
||||
material.uniforms.uTextureOffset = 1;
|
||||
this.state = 'stopped';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public play()
|
||||
{
|
||||
this.state = 'animating';
|
||||
this.time = 0;
|
||||
const material = this.mesh.material as ScrollingMeshMaterial;
|
||||
|
||||
material.uniforms.uTextureOffset = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- utils ---------- */
|
||||
|
||||
function normalizeVector(x: number, y: number): [number, number]
|
||||
{
|
||||
const length = Math.hypot(x, y);
|
||||
|
||||
return length === 0 ? [0, 0] : [x / length, y / length];
|
||||
}
|
||||
|
||||
function clamp(v: number, a: number, b: number)
|
||||
{
|
||||
return Math.max(a, Math.min(b, v));
|
||||
}
|
||||
|
||||
function cross2(a: Point, b: Point, c: Point)
|
||||
{
|
||||
// cross( (A-C), (B-C) )
|
||||
return ((a.x - c.x) * (b.y - c.y)) - ((a.y - c.y) * (b.x - c.x));
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаляет дубликаты / почти-нулевые сегменты и промежуточные строго коллинеарные точки
|
||||
* @param points
|
||||
* @param eps
|
||||
*/
|
||||
function dedupeAndColinearCull(points: Point[], eps = 1e-4): Point[]
|
||||
{
|
||||
if (points.length <= 1) return [...points];
|
||||
|
||||
const out: Point[] = [];
|
||||
|
||||
for (const p of points)
|
||||
{
|
||||
const last = out[out.length - 1];
|
||||
|
||||
if (!last || Math.hypot(p.x - last.x, p.y - last.y) > eps) out.push(p);
|
||||
}
|
||||
|
||||
if (out.length < 3) return out;
|
||||
|
||||
const pruned: Point[] = [out[0]];
|
||||
|
||||
for (let i = 1; i < out.length - 1; i++)
|
||||
{
|
||||
const a = out[i - 1];
|
||||
const b = out[i];
|
||||
const c = out[i + 1];
|
||||
const abx = b.x - a.x;
|
||||
const aby = b.y - a.y;
|
||||
const bcx = c.x - b.x;
|
||||
const bcy = c.y - b.y;
|
||||
const cross = (abx * bcy) - (aby * bcx);
|
||||
|
||||
if (Math.abs(cross) > eps) pruned.push(b);
|
||||
}
|
||||
pruned.push(out[out.length - 1]);
|
||||
|
||||
return pruned;
|
||||
}
|
||||
98
src/entities/Winline.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { Assets, Container } from 'pixi.js';
|
||||
import { IBone, Spine } from 'pixi-spine';
|
||||
import { Updatable } from '../types/Entity';
|
||||
import { ITrackEntry } from '../types/ITrackEntry';
|
||||
|
||||
interface IBoneExtended extends IBone
|
||||
{
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class WinLine
|
||||
* @extends Container
|
||||
* @implements Updatable
|
||||
* @description
|
||||
* The `WinLine` class represents a visual line in a slot machine game that can be animated to show winning lines.
|
||||
* It extends the `Container` class and implements the `Updatable` interface.
|
||||
* The class uses Spine animations to display the win line and provides methods to set the line's position,
|
||||
* update the animation state, and trigger idle and win animations.
|
||||
*/
|
||||
export default class WinLine extends Container implements Updatable
|
||||
{
|
||||
spine: Spine;
|
||||
slotSize: { x: number, y: number };
|
||||
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
this.spine = new Spine(Assets.get('win_line').spineData);
|
||||
this.spine.autoUpdate = false;
|
||||
this.spine.state.timeScale = 1;
|
||||
this.spine.alpha = 0.8;
|
||||
this.idle();
|
||||
this.addChild(this.spine);
|
||||
|
||||
this.spine.state.addListener({
|
||||
event: (_, event) =>
|
||||
{
|
||||
if (event.data.name === 'end')
|
||||
{
|
||||
this.idle();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.zIndex = 10100;
|
||||
this.slotSize = { x: 200, y: 200 };
|
||||
this.scale.set(0.52);
|
||||
this.position.set(0, this.slotSize.y * 0.5 * this.scale.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the positions of the control bones based on the provided line map.
|
||||
* @param lineMap - An array of numbers representing the slot indices for each reel.
|
||||
* @returns void
|
||||
*/
|
||||
set(lineMap: number[]): void
|
||||
{
|
||||
(this.spine.skeleton.findBone('control_0') as IBoneExtended).y = -lineMap[0] * this.slotSize.y;
|
||||
(this.spine.skeleton.findBone('control_6') as IBoneExtended).y = -lineMap[lineMap.length - 1] * this.slotSize.y;
|
||||
lineMap.forEach((slotIndex: number, reelIndex: number) =>
|
||||
{
|
||||
const control = this.spine.skeleton.findBone(`control_${reelIndex + 1}`) as IBoneExtended;
|
||||
|
||||
if (!control) return;
|
||||
control.y = -slotIndex * this.slotSize.y;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the spine animation with the given delta time.
|
||||
* @param dt - The delta time in milliseconds.
|
||||
* @returns void
|
||||
*/
|
||||
update(dt: number): void
|
||||
{
|
||||
this.spine.update(dt * 0.001);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the animation to 'idle' on the spine state.
|
||||
* @returns {ITrackEntry} The track entry of the animation.
|
||||
*/
|
||||
idle(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'idle', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the 'win' animation on the spine state.
|
||||
* @returns {ITrackEntry} The track entry of the animation.
|
||||
*/
|
||||
win(): ITrackEntry
|
||||
{
|
||||
return this.spine.state.setAnimation(0, 'win', false);
|
||||
}
|
||||
}
|
||||
92
src/events/Events.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* @class Events
|
||||
* @description A basic event handling system that supports adding events, listeners, and emitting events.
|
||||
*/
|
||||
export default class Events
|
||||
{
|
||||
/** Stores events and associated data. */
|
||||
private _events: Record<string, Array<any>>;
|
||||
|
||||
/** Stores listeners for specific events. */
|
||||
private _listeners: Record<string, Array<{ event: string; fn: (data?: any) => void; context: any }>>;
|
||||
|
||||
/** Initializes an empty events storage and listeners storage. */
|
||||
constructor()
|
||||
{
|
||||
this._events = {};
|
||||
this._listeners = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an event with associated data to be emitted later.
|
||||
* @param event
|
||||
* @param data
|
||||
*/
|
||||
addEvent(event: string, data?: any): void
|
||||
{
|
||||
if (!this._events[event])
|
||||
{
|
||||
this._events[event] = [];
|
||||
}
|
||||
this._events[event].push(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener for a specific event.
|
||||
* @param event
|
||||
* @param fn
|
||||
* @param context
|
||||
*/
|
||||
addListener(event: string, fn: (data?: any) => void, context: any): void
|
||||
{
|
||||
if (!this._listeners[event])
|
||||
{
|
||||
this._listeners[event] = [];
|
||||
}
|
||||
this._listeners[event].push({ event, fn, context });
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a specific listener for an event.
|
||||
* @param event
|
||||
* @param fn
|
||||
* @param context
|
||||
*/
|
||||
removeEventListener(event: string, fn: (data?: any) => void, context: any): void
|
||||
{
|
||||
if (!this._listeners[event]) return;
|
||||
this._listeners[event] = this._listeners[event].filter(
|
||||
(listener) => listener.fn !== fn || listener.context !== context
|
||||
);
|
||||
}
|
||||
|
||||
/** Clears all stored events. */
|
||||
clearEvents(): void
|
||||
{
|
||||
this._events = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits all stored events, invoking associated listeners with the event data.
|
||||
* The listeners for each event receive each piece of data associated with that event.
|
||||
*/
|
||||
emitEvents(): void
|
||||
{
|
||||
for (const eventName in this._events)
|
||||
{
|
||||
const dataObjects = this._events[eventName];
|
||||
const listeners = this._listeners[eventName];
|
||||
|
||||
if (!listeners) continue;
|
||||
|
||||
dataObjects.forEach((data) =>
|
||||
{
|
||||
listeners.forEach((listener) =>
|
||||
{
|
||||
listener.fn.call(listener.context, data);
|
||||
});
|
||||
});
|
||||
delete this._events[eventName];
|
||||
}
|
||||
}
|
||||
}
|
||||
153
src/index.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
export { default as AnticipationFrame } from './entities/AnticipationFrame';
|
||||
export * from './entities/AnticipationFrame';
|
||||
export { default as CascadeSymbol } from './entities/CascadeSymbol';
|
||||
export * from './entities/CascadeSymbol';
|
||||
export { default as ExpandedSymbol } from './entities/ExpandedSymbol';
|
||||
export * from './entities/ExpandedSymbol';
|
||||
export { default as ReelSymbol } from './entities/ReelSymbol';
|
||||
export * from './entities/ReelSymbol';
|
||||
export { default as StaticSymbol } from './entities/StaticSymbol';
|
||||
export * from './entities/StaticSymbol';
|
||||
export { default as SlotMultiplierEntity } from './entities/SlotMultiplierEntity';
|
||||
export * from './entities/SlotMultiplierEntity';
|
||||
export { default as Symbol } from './entities/Symbol';
|
||||
export * from './entities/Symbol';
|
||||
export { default as ValuableExpandedSymbol } from './entities/ValuableExpandedSymbol';
|
||||
export * from './entities/ValuableExpandedSymbol';
|
||||
export { default as ValuableReelSymbol } from './entities/ValuableReelSymbol';
|
||||
export * from './entities/ValuableReelSymbol';
|
||||
export { default as Winline } from './entities/Winline';
|
||||
export * from './entities/Winline';
|
||||
export { default as Events } from './events/Events';
|
||||
export * from './events/Events';
|
||||
export { default as BaseMachine } from './slotMachines/BaseMachine';
|
||||
export * from './slotMachines/BaseMachine';
|
||||
export { default as CascadeMachine } from './slotMachines/CascadeMachine';
|
||||
export * from './slotMachines/CascadeMachine';
|
||||
export { default as ReelsMachine } from './slotMachines/ReelsMachine';
|
||||
export * from './slotMachines/ReelsMachine';
|
||||
export { default as StaticReelsMachine } from './slotMachines/StaticReelsMachine';
|
||||
export * from './slotMachines/StaticReelsMachine';
|
||||
export { default as HoldAndWinMachine } from './slotMachines/HoldAndWinMachine';
|
||||
export * from './slotMachines/HoldAndWinMachine';
|
||||
export { default as SpeedModes } from './speedModes/SpeedModes';
|
||||
export { default as BasicStateMachine } from './states/BasicStateMachine';
|
||||
export * from './states/BasicStateMachine';
|
||||
export { default as AnticipationAnimationsSystem } from './systems/AnticipationAnimationsSystem';
|
||||
export * from './systems/AnticipationAnimationsSystem';
|
||||
export { default as AnticipationSymbolsSystem } from './systems/AnticipationSymbolsSystem';
|
||||
export * from './systems/AnticipationSymbolsSystem';
|
||||
export { default as AnticipationSystem } from './systems/AnticipationSystem';
|
||||
export * from './systems/AnticipationSystem';
|
||||
export { default as AnticipationTintingSystem } from './systems/AnticipationTintingSystem';
|
||||
export * from './systems/AnticipationTintingSystem';
|
||||
export { default as BaseSystem } from './systems/BaseSystem';
|
||||
export * from './systems/BaseSystem';
|
||||
export { default as CascadeRefillSystems } from './systems/CascadeRefillSystems';
|
||||
export * from './systems/CascadeRefillSystems';
|
||||
export { default as CascadeSpinSystem } from './systems/CascadeSpinSystem';
|
||||
export * from './systems/CascadeSpinSystem';
|
||||
export { default as AnimatedExpandedSymbolsSystem } from './systems/AnimatedExpandedSymbolsSystem';
|
||||
export * from './systems/AnimatedExpandedSymbolsSystem';
|
||||
export { default as SpinningExpandedSymbolsSystem } from './systems/SpinningExpandedSymbolsSystem';
|
||||
export * from './systems/SpinningExpandedSymbolsSystem';
|
||||
export { default as InitSpinningExpandedSymbolsSystem } from './systems/InitSpinningExpandedSymbolsSystem';
|
||||
export * from './systems/InitSpinningExpandedSymbolsSystem';
|
||||
export { default as GridSystem } from './systems/GridSystem';
|
||||
export * from './systems/GridSystem';
|
||||
export { default as HighlightLandedSymbolSystem } from './systems/HighlightLandedSymbolSystem';
|
||||
export * from './systems/HighlightLandedSymbolSystem';
|
||||
export { default as IdleSystem } from './systems/IdleSystem';
|
||||
export * from './systems/IdleSystem';
|
||||
export { default as InitSystem } from './systems/InitSystem';
|
||||
export * from './systems/InitSystem';
|
||||
export { default as InteractiveGridSystem } from './systems/InteractiveGridSystem';
|
||||
export * from './systems/InteractiveGridSystem';
|
||||
export { default as BlurSymbolsSystem } from './systems/BlurSymbolsSystem';
|
||||
export * from './systems/BlurSymbolsSystem';
|
||||
export { default as LayeringSystem } from './systems/LayeringSystem';
|
||||
export * from './systems/LayeringSystem';
|
||||
export { default as Acceleration } from './systems/reels/Acceleration';
|
||||
export * from './systems/reels/Acceleration';
|
||||
export { default as Bouncing } from './systems/reels/Bouncing';
|
||||
export * from './systems/reels/Bouncing';
|
||||
export { default as Deceleration } from './systems/reels/Deceleration';
|
||||
export * from './systems/reels/Deceleration';
|
||||
export { default as Reel } from './systems/reels/Reel';
|
||||
export * from './systems/reels/Reel';
|
||||
export { default as Spinning } from './systems/reels/Spinning';
|
||||
export * from './systems/reels/Spinning';
|
||||
export * from './systems/reels/states';
|
||||
export { default as Stopped } from './systems/reels/Stopped';
|
||||
export * from './systems/reels/Stopped';
|
||||
export { default as ReelsSpinSystem } from './systems/ReelsSpinSystem';
|
||||
export * from './systems/ReelsSpinSystem';
|
||||
export { default as StaticReelsSpinSystem } from './systems/StaticReelsSpinSystem';
|
||||
export * from './systems/StaticReelsSpinSystem';
|
||||
export { default as SingleSlotReelsSpinSystem } from './systems/SingleSlotReelsSpinSystem';
|
||||
export * from './systems/SingleSlotReelsSpinSystem';
|
||||
export { default as SingleSlotReelsSystem } from './systems/SingleSlotReelsSpinSystem';
|
||||
export { default as SingleSlotReel } from './systems/reels/SingleSlotReel';
|
||||
export * from './systems/reels/SingleSlotReel';
|
||||
export { default as StickySymbolsSystem } from './systems/StickySymbolsSystem';
|
||||
export * from './systems/StickySymbolsSystem';
|
||||
export { default as SlotMultipliersSystem } from './systems/SlotMultipliersSystem';
|
||||
export * from './systems/SlotMultipliersSystem';
|
||||
export { default as TintingSymbolsSystem } from './systems/TintingSymbolsSystem';
|
||||
export * from './systems/TintingSymbolsSystem';
|
||||
export { default as WinningSymbolAnimationsSystem } from './systems/WinningSymbolAnimationsSystem';
|
||||
export * from './systems/WinningSymbolAnimationsSystem';
|
||||
export { default as WinningSymbolTintingSystem } from './systems/WinningSymbolTintingSystem';
|
||||
export * from './systems/WinningSymbolTintingSystem';
|
||||
export * from './types/Entity';
|
||||
export * from './types/ITrackEntry';
|
||||
export * from './types/ScreenInputMap';
|
||||
export * from './types/SlotMachineConfig';
|
||||
export * from './utils/easing/easeInOutBack';
|
||||
export * from './utils/getUniqueId';
|
||||
export { default as GridUtils } from './utils/GridUtils';
|
||||
export * from './utils/GridUtils';
|
||||
export { default as AnimationProgressHook } from './utils/hooks/AnimationProgressHook';
|
||||
export * from './utils/hooks/AnimationProgressHook';
|
||||
export { default as DelayHook } from './utils/hooks/DelayHook';
|
||||
export * from './utils/hooks/DelayHook';
|
||||
export { default as GravityHook } from './utils/hooks/GravityHook';
|
||||
export * from './utils/hooks/GravityHook';
|
||||
export { default as Hook } from './utils/hooks/Hook';
|
||||
export * from './utils/hooks/Hook';
|
||||
export { default as HooksChain } from './utils/hooks/HooksChain';
|
||||
export * from './utils/hooks/HooksChain';
|
||||
export { default as ImmediateHook } from './utils/hooks/ImmediateHook';
|
||||
export * from './utils/hooks/ImmediateHook';
|
||||
export { default as Pool } from './utils/Pool';
|
||||
export * from './utils/Pool';
|
||||
export { default as AllWinSymbolSystem } from './systems/AllWinningSymbolSystem';
|
||||
export * from './systems/AllWinningSymbolSystem';
|
||||
export { default as ResetTintSystem } from './systems/ResetTintSystem';
|
||||
export * from './systems/ResetTintSystem';
|
||||
export { default as ResetWinningSymbolsSystem } from './systems/ResetWinningSymbolsSystem';
|
||||
export * from './systems/ResetWinningSymbolsSystem';
|
||||
export { default as SetAllWinLinesSystem } from './systems/SetAllWinLinesSystem';
|
||||
export * from './systems/SetAllWinLinesSystem';
|
||||
export { default as SetCurrentWinLinesSystem } from './systems/SetCurrentWinLinesSystem';
|
||||
export * from './systems/SetCurrentWinLinesSystem';
|
||||
export { default as SetWinningLineSymbolsSystem } from './systems/SetWinningLineSymbolsSystem';
|
||||
export * from './systems/SetWinningLineSymbolsSystem';
|
||||
export { default as WinLinesAnimationsSystem } from './systems/WinLinesAnimationsSystem';
|
||||
export * from './systems/WinLinesAnimationsSystem';
|
||||
export { default as WinningLinesIncrementSystem } from './systems/WinningLinesIncrementSystem';
|
||||
export * from './systems/WinningLinesIncrementSystem';
|
||||
export { default as WinningLinesResetIncrementSystem } from './systems/WinningLinesResetIncrementSystem';
|
||||
export * from './systems/WinningLinesResetIncrementSystem';
|
||||
export { default as DisplayTotalLinesWinSystem } from './systems/DisplayTotalLinesWinSystem';
|
||||
export * from './systems/DisplayTotalLinesWinSystem';
|
||||
export { default as DisplayLineWinValueSystem } from './systems/DisplayLineWinValueSystem';
|
||||
export * from './systems/DisplayLineWinValueSystem';
|
||||
export { default as BlockExitStateSystem } from './systems/BlockExitStateSystem';
|
||||
export * from './systems/BlockExitStateSystem';
|
||||
export { default as WebGLWinLine } from './entities/WebGLWinLine';
|
||||
export * from './entities/WebGLWinLine';
|
||||
export { default as WinningExpandedSymbolsAnimationSystem } from './systems/WinningExpandedSymbolsAnimationSystem';
|
||||
export * from './systems/WinningExpandedSymbolsAnimationSystem';
|
||||
export { default as MockSystem } from './systems/MockSystem';
|
||||
export * from './systems/MockSystem';
|
||||
192
src/slotMachines/BaseMachine.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
import { Container, DisplayObject } from 'pixi.js';
|
||||
import Events from '../events/Events';
|
||||
import SpeedModes from '../speedModes/SpeedModes';
|
||||
import { Updatable } from '../types/Entity';
|
||||
import { SlotMachineConfig } from '../types/SlotMachineConfig';
|
||||
import GridUtils from '../utils/GridUtils';
|
||||
|
||||
/**
|
||||
* @interface MachineInputs
|
||||
* @description Interface for machine inputs.
|
||||
*/
|
||||
interface MachineInputs
|
||||
{
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface MachineFlags
|
||||
* @description Interface for machine flags.
|
||||
*/
|
||||
interface MachineFlags
|
||||
{
|
||||
[key: string]: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface MachineMaps
|
||||
* @description Interface for machine maps.
|
||||
*/
|
||||
interface MachineMaps
|
||||
{
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface MachineUtils
|
||||
* @description Interface for machine utility objects.
|
||||
*/
|
||||
interface MachineUtils
|
||||
{
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class BaseMachine
|
||||
* @extends Container
|
||||
* @description A base class for creating a stateful machine with various customizable properties and systems.
|
||||
*/
|
||||
export default abstract class BaseMachine extends Container
|
||||
{
|
||||
public config: SlotMachineConfig;
|
||||
public timeScale: number;
|
||||
public events!: Events;
|
||||
public inputs: MachineInputs;
|
||||
public flags: MachineFlags;
|
||||
public maps: MachineMaps;
|
||||
public utils: MachineUtils;
|
||||
public systems: Record<string, any>;
|
||||
public sharedSystemData: Record<string, any>;
|
||||
public speedModes: SpeedModes;
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @param {SlotMachineConfig} config - Configuration object for the BaseMachine instance.
|
||||
*/
|
||||
protected constructor(config: SlotMachineConfig)
|
||||
{
|
||||
super();
|
||||
this.config = config;
|
||||
this.sortableChildren = true;
|
||||
this.timeScale = 1;
|
||||
|
||||
this.inputs = {};
|
||||
this.flags = {};
|
||||
this.maps = {};
|
||||
this.utils = {
|
||||
grid: new GridUtils(this.config),
|
||||
};
|
||||
this.systems = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Core initialization method to set up various components.
|
||||
* Subclasses should override create methods if customization is needed.
|
||||
*/
|
||||
protected initialize(): void
|
||||
{
|
||||
this.createSharedData();
|
||||
this.createEvents();
|
||||
this.createInputs();
|
||||
this.createFlags();
|
||||
this.createMaps();
|
||||
this.createUtils();
|
||||
this.createSystems();
|
||||
this.createStateMachine();
|
||||
this.createSpeedModes();
|
||||
}
|
||||
|
||||
protected createEvents(): void
|
||||
{
|
||||
this.events = new Events();
|
||||
}
|
||||
|
||||
protected createInputs(): void
|
||||
{
|
||||
this.inputs = {};
|
||||
}
|
||||
|
||||
protected createFlags(): void
|
||||
{
|
||||
this.flags = {};
|
||||
}
|
||||
|
||||
protected createMaps(): void
|
||||
{
|
||||
this.maps = {};
|
||||
}
|
||||
|
||||
protected createUtils(): void
|
||||
{
|
||||
this.utils = {
|
||||
grid: new GridUtils(this.config),
|
||||
};
|
||||
}
|
||||
|
||||
protected createSystems(): void
|
||||
{
|
||||
this.systems = {};
|
||||
}
|
||||
|
||||
protected createSharedData(): void
|
||||
{
|
||||
this.sharedSystemData = {};
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes the state machine (to be implemented by subclass).
|
||||
* @throws {Error} Must be implemented in subclass.
|
||||
*/
|
||||
protected abstract createStateMachine(): void;
|
||||
|
||||
/**
|
||||
Initializes different speed modes (to be implemented by subclass).
|
||||
* @throws {Error} Must be implemented in subclass.
|
||||
*/
|
||||
protected createSpeedModes(): void
|
||||
{
|
||||
this.speedModes = new SpeedModes(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a symbol based on the provided key (to be implemented by subclass).
|
||||
* @param {string} key - The unique identifier for the symbol.
|
||||
* @throws {Error} Must be implemented in subclass.
|
||||
*/
|
||||
public abstract createSymbol(key: string): any;
|
||||
|
||||
/**
|
||||
* Updates the machine and its child entities based on a time delta.
|
||||
* @param {number} dt - The delta time for the update cycle.
|
||||
*/
|
||||
public update(dt: number): void
|
||||
{
|
||||
this.speedModes.handleInput(this.inputs);
|
||||
this._updateEntities(dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} dt - The delta time to pass to each child entity's update.
|
||||
*/
|
||||
private _updateEntities(dt: number): void
|
||||
{
|
||||
this.children.forEach((child) =>
|
||||
{
|
||||
if ((child as DisplayObject & Updatable).update)
|
||||
{
|
||||
(child as DisplayObject & Updatable).update(dt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the specified map by setting all elements to null.
|
||||
* @param {string} mapName - The name of the map to clear.
|
||||
*/
|
||||
public nullMap(mapName: string): void
|
||||
{
|
||||
if (!this.maps[mapName]) return;
|
||||
this.maps[mapName].fill(null);
|
||||
}
|
||||
}
|
||||
121
src/slotMachines/CascadeMachine.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import CascadeSymbol from '../entities/CascadeSymbol';
|
||||
import BasicStateMachine from '../states/BasicStateMachine';
|
||||
import CascadeSpinSystem from '../systems/CascadeSpinSystem';
|
||||
import GridSystem from '../systems/GridSystem';
|
||||
import IdleSystem from '../systems/IdleSystem';
|
||||
import InitSystem from '../systems/InitSystem';
|
||||
import InteractiveGridSystem from '../systems/InteractiveGridSystem';
|
||||
import LayeringSystem from '../systems/LayeringSystem';
|
||||
import GridUtils from '../utils/GridUtils';
|
||||
import BaseMachine from './BaseMachine';
|
||||
|
||||
/**
|
||||
* @class CascadeMachine
|
||||
* @extends BaseMachine
|
||||
* @description The main class for managing slot machine reels. Initializes and maintains state, systems,
|
||||
* and utilities to control the reel behavior, from initialization to spinning and stopping.
|
||||
*/
|
||||
export default class CascadeMachine extends BaseMachine
|
||||
{
|
||||
public stateMachine: BasicStateMachine;
|
||||
|
||||
/**
|
||||
* Initializes the reel machine with configuration settings.
|
||||
* Sets up inputs, flags, maps, utilities, systems, and the state machine.
|
||||
* @param {object} config - Configuration settings for the machine.
|
||||
*/
|
||||
constructor(config: any)
|
||||
{
|
||||
super(config);
|
||||
this.initialize();
|
||||
this.stateMachine.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the inputs required for the reel machine.
|
||||
* `initMap` is used for initial symbol mapping, `spin` for triggering spins,
|
||||
* and `screenMap` for tracking symbol positions on the screen.
|
||||
*/
|
||||
protected override createInputs(): void
|
||||
{
|
||||
this.inputs = {
|
||||
initMap: null,
|
||||
spin: false,
|
||||
screenMap: null,
|
||||
turbo: false,
|
||||
fastStop: false,
|
||||
turboForced: false
|
||||
};
|
||||
}
|
||||
|
||||
/** Sets up flags to track the machine's state, such as whether initialization has completed. */
|
||||
protected override createFlags(): void
|
||||
{
|
||||
this.flags = {
|
||||
init: false,
|
||||
isSpinning: false
|
||||
};
|
||||
}
|
||||
|
||||
/** Sets up maps used by the machine, including `symbols`, which holds the names of the symbols on reels. */
|
||||
protected override createMaps(): void
|
||||
{
|
||||
this.maps = {
|
||||
symbols: [],
|
||||
stickySymbols: []
|
||||
};
|
||||
}
|
||||
|
||||
/** Sets up utilities for the machine, such as grid utilities used for positioning symbols. */
|
||||
protected override createUtils(): void
|
||||
{
|
||||
this.utils = {
|
||||
grid: new GridUtils(this.config),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes and registers systems for the reel machine, including initialization,
|
||||
* idle, grid, and spin systems, which manage various aspects of the reel behavior.
|
||||
*/
|
||||
protected override createSystems(): void
|
||||
{
|
||||
this.systems = {
|
||||
init: new InitSystem(this),
|
||||
idle: new IdleSystem(this),
|
||||
grid: new GridSystem(this),
|
||||
spin: new CascadeSpinSystem(this),
|
||||
layering: new LayeringSystem(this),
|
||||
interactive: new InteractiveGridSystem(this),
|
||||
};
|
||||
}
|
||||
|
||||
/** Initializes the state machine for managing reel states and transitions. */
|
||||
protected createStateMachine(): void
|
||||
{
|
||||
this.stateMachine = new BasicStateMachine(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the reel machine and its state based on the delta time.
|
||||
* Processes inputs, updates the state machine, and triggers system updates.
|
||||
* @param {number} dt - Time delta in milliseconds since the last update.
|
||||
*/
|
||||
override update(dt: number): void
|
||||
{
|
||||
this.stateMachine.handleInput(this);
|
||||
this.stateMachine.update(dt);
|
||||
super.update(dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new Symbol instance based on the provided key.
|
||||
* The symbol is retrieved from the machine's configuration.
|
||||
* @param {string} key - The unique key for the symbol configuration.
|
||||
* @returns {symbol} The newly created Symbol instance.
|
||||
*/
|
||||
createSymbol(key: string): CascadeSymbol
|
||||
{
|
||||
return new CascadeSymbol(this.config.symbols[key]);
|
||||
}
|
||||
}
|
||||
91
src/slotMachines/HoldAndWinMachine.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import ValuableReelSymbol from '../entities/ValuableReelSymbol';
|
||||
import BasicStateMachine from '../states/BasicStateMachine';
|
||||
import GridSystem from '../systems/GridSystem';
|
||||
import IdleSystem from '../systems/IdleSystem';
|
||||
import InitSystem from '../systems/InitSystem';
|
||||
import InteractiveGridSystem from '../systems/InteractiveGridSystem';
|
||||
import LayeringSystem from '../systems/LayeringSystem';
|
||||
import GridUtils from '../utils/GridUtils';
|
||||
import BaseMachine from './BaseMachine';
|
||||
import SingleSlotReelsSpinSystem from '../systems/SingleSlotReelsSpinSystem';
|
||||
|
||||
/**
|
||||
* @class HoldAndWinMachine
|
||||
* @extends BaseMachine
|
||||
* @description Minimal Hold & Win machine scaffold extending BaseMachine.
|
||||
*/
|
||||
export default class HoldAndWinMachine extends BaseMachine {
|
||||
public stateMachine: BasicStateMachine;
|
||||
|
||||
constructor(config: any) {
|
||||
super(config);
|
||||
this.initialize();
|
||||
this.stateMachine.init();
|
||||
}
|
||||
|
||||
protected override createInputs(): void {
|
||||
this.inputs = {
|
||||
initMap: null,
|
||||
spin: false,
|
||||
screenMap: null,
|
||||
turbo: false,
|
||||
fastStop: false,
|
||||
turboForced: false,
|
||||
};
|
||||
}
|
||||
|
||||
protected override createFlags(): void {
|
||||
this.flags = {
|
||||
init: false,
|
||||
isSpinning: false,
|
||||
};
|
||||
}
|
||||
|
||||
protected override createSharedData(): void {
|
||||
this.sharedSystemData = {
|
||||
spinSystemState: 'stopped',
|
||||
currentStoppingReel: 0,
|
||||
};
|
||||
}
|
||||
|
||||
protected override createMaps(): void {
|
||||
this.maps = {
|
||||
symbols: [],
|
||||
stickySymbols: [],
|
||||
preventReelStop: [],
|
||||
preventReelsSpin: [],
|
||||
slotMultipliers: [],
|
||||
};
|
||||
}
|
||||
|
||||
protected override createUtils(): void {
|
||||
this.utils = {
|
||||
grid: new GridUtils(this.config),
|
||||
};
|
||||
}
|
||||
|
||||
protected override createSystems(): void {
|
||||
this.systems = {
|
||||
init: new InitSystem(this),
|
||||
idle: new IdleSystem(this),
|
||||
grid: new GridSystem(this),
|
||||
spin: new SingleSlotReelsSpinSystem(this),
|
||||
layering: new LayeringSystem(this),
|
||||
interactive: new InteractiveGridSystem(this),
|
||||
};
|
||||
}
|
||||
|
||||
protected createStateMachine(): void {
|
||||
this.stateMachine = new BasicStateMachine(this);
|
||||
}
|
||||
|
||||
override update(dt: number): void {
|
||||
this.stateMachine.handleInput(this);
|
||||
this.stateMachine.update(dt);
|
||||
super.update(dt);
|
||||
}
|
||||
|
||||
createSymbol(key: string): ValuableReelSymbol {
|
||||
return new ValuableReelSymbol(this.config.symbols[key]);
|
||||
}
|
||||
}
|
||||
182
src/slotMachines/ReelsMachine.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
import AllWinningSymbolSystem from '../systems/AllWinningSymbolSystem';
|
||||
import ValuableReelSymbol from '../entities/ValuableReelSymbol';
|
||||
import ClassicReelsStateMachine from '../states/ClassicReelsStateMachine';
|
||||
import CelebrateSystem from '../systems/CelebrateSystem';
|
||||
import GridSystem from '../systems/GridSystem';
|
||||
import IdleSystem from '../systems/IdleSystem';
|
||||
import InitSystem from '../systems/InitSystem';
|
||||
import InteractiveGridSystem from '../systems/InteractiveGridSystem';
|
||||
import LayeringSystem from '../systems/LayeringSystem';
|
||||
import ReelsSpinSystem from '../systems/ReelsSpinSystem';
|
||||
import ResetTintSystem from '../systems/ResetTintSystem';
|
||||
import ResetWinningSymbolsSystem from '../systems/ResetWinningSymbolsSystem';
|
||||
import SetAllWinLinesSystem from '../systems/SetAllWinLinesSystem';
|
||||
import SetCurrentWinLinesSystem from '../systems/SetCurrentWinLinesSystem';
|
||||
import SetWinningLineSymbolsSystem from '../systems/SetWinningLineSymbolsSystem';
|
||||
import TintingSymbolsSystem from '../systems/TintingSymbolsSystem';
|
||||
import WinLinesAnimationsSystem from '../systems/WinLinesAnimationsSystem';
|
||||
import WinningLinesIncrementSystem from '../systems/WinningLinesIncrementSystem';
|
||||
import WinningLinesResetIncrementSystem from '../systems/WinningLinesResetIncrementSystem';
|
||||
import WinningSymbolAnimationsSystem from '../systems/WinningSymbolAnimationsSystem';
|
||||
import WinningSymbolTintingSystem from '../systems/WinningSymbolTintingSystem';
|
||||
import GridUtils from '../utils/GridUtils';
|
||||
import BaseMachine from './BaseMachine';
|
||||
import DisplayTotalLinesWinSystem from "../systems/DisplayTotalLinesWinSystem";
|
||||
import DisplayLineWinValueSystem from "../systems/DisplayLineWinValueSystem";
|
||||
import BlockExitStateSystem from "../systems/BlockExitStateSystem";
|
||||
import MaskingSystem from '../systems/MaskingSystem';
|
||||
import BlurSymbolsSystem from '../systems/BlurSymbolsSystem';
|
||||
|
||||
/**
|
||||
* @class ReelsMachine
|
||||
* @extends BaseMachine
|
||||
* @description The main class for managing slot machine reels. Initializes and maintains state, systems,
|
||||
* and utilities to control the reel behavior, from initialization to spinning and stopping.
|
||||
*/
|
||||
export default class ReelsMachine extends BaseMachine
|
||||
{
|
||||
public stateMachine: ClassicReelsStateMachine;
|
||||
|
||||
/**
|
||||
* Initializes the reel machine with configuration settings.
|
||||
* Sets up inputs, flags, maps, utilities, systems, and the state machine.
|
||||
* @param {object} config - Configuration settings for the machine.
|
||||
*/
|
||||
constructor(config: any)
|
||||
{
|
||||
super(config);
|
||||
this.initialize();
|
||||
this.stateMachine.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the inputs required for the reel machine.
|
||||
* `initMap` is used for initial symbol mapping, `spin` for triggering spins,
|
||||
* and `screenMap` for tracking symbol positions on the screen.
|
||||
*/
|
||||
protected override createInputs(): void
|
||||
{
|
||||
this.inputs = {
|
||||
initMap: null,
|
||||
spin: false,
|
||||
screenMap: null,
|
||||
turbo: false,
|
||||
fastStop: false,
|
||||
turboForced: false,
|
||||
wins: null,
|
||||
blockExitState: false,
|
||||
};
|
||||
}
|
||||
|
||||
/** Sets up flags to track the machine's state, such as whether initialization has completed. */
|
||||
protected override createFlags(): void
|
||||
{
|
||||
this.flags = {
|
||||
init: false,
|
||||
isSpinning: false,
|
||||
};
|
||||
}
|
||||
|
||||
protected override createSharedData(): void
|
||||
{
|
||||
this.sharedSystemData = {
|
||||
spinSystemState: 'stopped',
|
||||
currentStoppingReel: 0,
|
||||
reelsStates: [],
|
||||
currentWinIndex: 0,
|
||||
animateLines: [],
|
||||
anticipatingSymbolsTests: [],
|
||||
};
|
||||
}
|
||||
|
||||
/** Sets up maps used by the machine, including `symbols`, which holds the names of the symbols on reels. */
|
||||
protected override createMaps(): void
|
||||
{
|
||||
this.maps = {
|
||||
symbols: [],
|
||||
stickySymbols: [],
|
||||
preventReelStop: [],
|
||||
preventReelsSpin: [],
|
||||
anticipatingReels: [],
|
||||
winningSymbols: [],
|
||||
tinting: []
|
||||
};
|
||||
}
|
||||
|
||||
/** Sets up utilities for the machine, such as grid utilities used for positioning symbols. */
|
||||
protected override createUtils(): void
|
||||
{
|
||||
this.utils = {
|
||||
grid: new GridUtils(this.config),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes and registers systems for the reel machine, including initialization,
|
||||
* idle, grid, and spin systems, which manage various aspects of the reel behavior.
|
||||
*/
|
||||
protected override createSystems(): void
|
||||
{
|
||||
this.systems = {
|
||||
init: new InitSystem(this),
|
||||
idle: new IdleSystem(this),
|
||||
grid: new GridSystem(this),
|
||||
spin: new ReelsSpinSystem(this),
|
||||
layering: new LayeringSystem(this),
|
||||
resetWinningSymbols: new ResetWinningSymbolsSystem(this),
|
||||
allWinSymbols: new AllWinningSymbolSystem(this),
|
||||
allWinLinesSet: new SetAllWinLinesSystem(this),
|
||||
winLinesResetIncrement: new WinningLinesResetIncrementSystem(this),
|
||||
winLinesIncrementing: new WinningLinesIncrementSystem(this),
|
||||
setWinLineSymbols: new SetWinningLineSymbolsSystem(this),
|
||||
setWinLine: new SetCurrentWinLinesSystem(this),
|
||||
winLinesAnimations: new WinLinesAnimationsSystem(this),
|
||||
celebrateSymbols: new CelebrateSystem(this),
|
||||
winningSymbolsAnim: new WinningSymbolAnimationsSystem(this),
|
||||
tintWinningSymbolsAnim: new WinningSymbolTintingSystem(this),
|
||||
tinting: new TintingSymbolsSystem(this),
|
||||
resetTinting: new ResetTintSystem(this),
|
||||
displayTotalLinesWin: new DisplayTotalLinesWinSystem(this),
|
||||
displayLineWin: new DisplayLineWinValueSystem(this),
|
||||
interactive: new InteractiveGridSystem(this),
|
||||
blockExitState: new BlockExitStateSystem(this),
|
||||
masking: new MaskingSystem(this),
|
||||
blurSymbols: new BlurSymbolsSystem(this),
|
||||
};
|
||||
}
|
||||
|
||||
/** Initializes the state machine for managing reel states and transitions. */
|
||||
protected createStateMachine(): void
|
||||
{
|
||||
this.stateMachine = new ClassicReelsStateMachine(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the reel machine and its state based on the delta time.
|
||||
* Processes inputs, updates the state machine, and triggers system updates.
|
||||
* @param {number} dt - Time delta in milliseconds since the last update.
|
||||
*/
|
||||
override update(dt: number): void
|
||||
{
|
||||
this.stateMachine.handleInput(this);
|
||||
this.stateMachine.update(dt);
|
||||
super.update(dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new Symbol instance based on the provided key.
|
||||
* The symbol is retrieved from the machine's configuration.
|
||||
* @param {string} key - The unique key for the symbol configuration.
|
||||
* @returns {symbol} The newly created Symbol instance.
|
||||
*/
|
||||
createSymbol(key: string): ValuableReelSymbol
|
||||
{
|
||||
return new ValuableReelSymbol(this.config.symbols[key]);
|
||||
}
|
||||
|
||||
override destroy() {
|
||||
Object.values(this.systems).forEach((system) => {
|
||||
system.destroy()
|
||||
})
|
||||
}
|
||||
}
|
||||
121
src/slotMachines/StaticReelsMachine.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import StaticSymbol from '../entities/StaticSymbol';
|
||||
import BasicStateMachine from '../states/BasicStateMachine';
|
||||
import GridSystem from '../systems/GridSystem';
|
||||
import IdleSystem from '../systems/IdleSystem';
|
||||
import InitSystem from '../systems/InitSystem';
|
||||
import InteractiveGridSystem from '../systems/InteractiveGridSystem';
|
||||
import LayeringSystem from '../systems/LayeringSystem';
|
||||
import StaticReelsSpinSystem from '../systems/StaticReelsSpinSystem';
|
||||
import GridUtils from '../utils/GridUtils';
|
||||
import BaseMachine from './BaseMachine';
|
||||
|
||||
/**
|
||||
* @class StaticReelsMachine
|
||||
* @extends BaseMachine
|
||||
* @description The main class for managing slot machine reels. Initializes and maintains state, systems,
|
||||
* and utilities to control the reel behavior, from initialization to spinning and stopping.
|
||||
*/
|
||||
export default class StaticReelsMachine extends BaseMachine
|
||||
{
|
||||
public stateMachine: BasicStateMachine;
|
||||
|
||||
/**
|
||||
* Initializes the reel machine with configuration settings.
|
||||
* Sets up inputs, flags, maps, utilities, systems, and the state machine.
|
||||
* @param {object} config - Configuration settings for the machine.
|
||||
*/
|
||||
constructor(config: any)
|
||||
{
|
||||
super(config);
|
||||
this.initialize();
|
||||
this.stateMachine.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the inputs required for the reel machine.
|
||||
* `initMap` is used for initial symbol mapping, `spin` for triggering spins,
|
||||
* and `screenMap` for tracking symbol positions on the screen.
|
||||
*/
|
||||
protected override createInputs(): void
|
||||
{
|
||||
this.inputs = {
|
||||
initMap: null,
|
||||
spin: false,
|
||||
screenMap: null,
|
||||
turbo: false,
|
||||
fastStop: false,
|
||||
turboForced: false
|
||||
};
|
||||
}
|
||||
|
||||
/** Sets up flags to track the machine's state, such as whether initialization has completed. */
|
||||
protected override createFlags(): void
|
||||
{
|
||||
this.flags = {
|
||||
init: false,
|
||||
isSpinning: false
|
||||
};
|
||||
}
|
||||
|
||||
/** Sets up maps used by the machine, including `symbols`, which holds the names of the symbols on reels. */
|
||||
protected override createMaps(): void
|
||||
{
|
||||
this.maps = {
|
||||
symbols: [],
|
||||
stickySymbols: []
|
||||
};
|
||||
}
|
||||
|
||||
/** Sets up utilities for the machine, such as grid utilities used for positioning symbols. */
|
||||
protected override createUtils(): void
|
||||
{
|
||||
this.utils = {
|
||||
grid: new GridUtils(this.config),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes and registers systems for the reel machine, including initialization,
|
||||
* idle, grid, and spin systems, which manage various aspects of the reel behavior.
|
||||
*/
|
||||
protected override createSystems(): void
|
||||
{
|
||||
this.systems = {
|
||||
init: new InitSystem(this),
|
||||
idle: new IdleSystem(this),
|
||||
grid: new GridSystem(this),
|
||||
spin: new StaticReelsSpinSystem(this),
|
||||
layering: new LayeringSystem(this),
|
||||
interactive: new InteractiveGridSystem(this),
|
||||
};
|
||||
}
|
||||
|
||||
/** Initializes the state machine for managing reel states and transitions. */
|
||||
protected createStateMachine(): void
|
||||
{
|
||||
this.stateMachine = new BasicStateMachine(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the reel machine and its state based on the delta time.
|
||||
* Processes inputs, updates the state machine, and triggers system updates.
|
||||
* @param {number} dt - Time delta in milliseconds since the last update.
|
||||
*/
|
||||
override update(dt: number): void
|
||||
{
|
||||
this.stateMachine.handleInput(this);
|
||||
this.stateMachine.update(dt);
|
||||
super.update(dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new Symbol instance based on the provided key.
|
||||
* The symbol is retrieved from the machine's configuration.
|
||||
* @param {string} key - The unique key for the symbol configuration.
|
||||
* @returns {symbol} The newly created Symbol instance.
|
||||
*/
|
||||
createSymbol(key: string): StaticSymbol
|
||||
{
|
||||
return new StaticSymbol(this.config.symbols[key]);
|
||||
}
|
||||
}
|
||||
107
src/speedModes/SpeedModes.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import BaseMachine from '../slotMachines/BaseMachine';
|
||||
import { State, StateMachine } from '@popiplay/state-machine';
|
||||
|
||||
const SPEED_MODES = {
|
||||
NORMAL: 0,
|
||||
TURBO: 1
|
||||
};
|
||||
|
||||
export type SpeedModesInput = {
|
||||
turbo: boolean,
|
||||
fastStop: boolean
|
||||
turboForced: boolean;
|
||||
};
|
||||
|
||||
export default class SpeedModes extends StateMachine<BaseMachine>
|
||||
{
|
||||
constructor(slotMachine: BaseMachine)
|
||||
{
|
||||
super(slotMachine);
|
||||
this.provideStates([
|
||||
new NormalMode(this),
|
||||
new TurboMode(this),
|
||||
]);
|
||||
this.checkDependencies(slotMachine);
|
||||
this.setState(SPEED_MODES.NORMAL);
|
||||
}
|
||||
|
||||
applyNormalModeSettings()
|
||||
{
|
||||
// add settings logic in an extended class
|
||||
}
|
||||
|
||||
applyTurboModeSettings()
|
||||
{
|
||||
// add settings logic in an extended class
|
||||
}
|
||||
|
||||
private checkDependencies(slotMachine: BaseMachine): void
|
||||
{
|
||||
if (!slotMachine.inputs.hasOwnProperty('turbo'))
|
||||
{
|
||||
throw new Error('\'turbo\' property is required in the inputs');
|
||||
}
|
||||
if (!slotMachine.inputs.hasOwnProperty('fastStop'))
|
||||
{
|
||||
throw new Error('\'fastStop\' property is required in the inputs');
|
||||
}
|
||||
if (!slotMachine.inputs.hasOwnProperty('turboForced'))
|
||||
{
|
||||
throw new Error('\'fastStop\' property is required in the inputs');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NormalMode extends State
|
||||
{
|
||||
machine: SpeedModes;
|
||||
constructor(machine: SpeedModes)
|
||||
{
|
||||
super('NormalSpeedMode');
|
||||
this.machine = machine;
|
||||
}
|
||||
|
||||
override enter()
|
||||
{
|
||||
this.machine.applyNormalModeSettings();
|
||||
}
|
||||
|
||||
override handleInput(input: SpeedModesInput)
|
||||
{
|
||||
if (input.turbo)
|
||||
{
|
||||
this.machine.setState(SPEED_MODES.TURBO);
|
||||
}
|
||||
else if (input.fastStop)
|
||||
{
|
||||
this.machine.setState(SPEED_MODES.TURBO);
|
||||
}
|
||||
else if (input.turboForced)
|
||||
{
|
||||
this.machine.setState(SPEED_MODES.TURBO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TurboMode extends State
|
||||
{
|
||||
machine: SpeedModes;
|
||||
constructor(machine: SpeedModes)
|
||||
{
|
||||
super('TurboSpeedMode');
|
||||
this.machine = machine;
|
||||
}
|
||||
|
||||
override enter()
|
||||
{
|
||||
this.machine.applyTurboModeSettings();
|
||||
}
|
||||
|
||||
override handleInput(input: SpeedModesInput)
|
||||
{
|
||||
if (!input.turbo && !input.fastStop && !input.turboForced)
|
||||
{
|
||||
this.machine.setState(SPEED_MODES.NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
140
src/states/BasicStateMachine.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import BaseMachine from '../slotMachines/BaseMachine';
|
||||
import { State, StateMachine } from '@popiplay/state-machine';
|
||||
|
||||
export enum STATES
|
||||
{
|
||||
INIT,
|
||||
IDLE,
|
||||
SPINNING
|
||||
}
|
||||
|
||||
type BasicStateMachineStates = InitState | IdleState | SpinningState;
|
||||
|
||||
export default class BasicStateMachine extends StateMachine<BaseMachine, BasicStateMachineStates>
|
||||
{
|
||||
constructor(target: BaseMachine)
|
||||
{
|
||||
super(target);
|
||||
this.provideStates([
|
||||
new InitState(this),
|
||||
new IdleState(this),
|
||||
new SpinningState(this),
|
||||
]);
|
||||
}
|
||||
|
||||
override update(dt: number)
|
||||
{
|
||||
super.update(dt);
|
||||
this.currentState.systems.forEach((systemId) =>
|
||||
{
|
||||
this.target.systems[systemId].update(dt);
|
||||
});
|
||||
}
|
||||
|
||||
onEnter()
|
||||
{
|
||||
this.currentState.systems.forEach((systemId) =>
|
||||
{
|
||||
this.target.systems[systemId].onEnter();
|
||||
});
|
||||
}
|
||||
|
||||
onLeave()
|
||||
{
|
||||
this.currentState.systems.forEach((systemId) =>
|
||||
{
|
||||
this.target.systems[systemId].onLeave();
|
||||
});
|
||||
}
|
||||
|
||||
init()
|
||||
{
|
||||
this.setState(STATES.INIT);
|
||||
}
|
||||
}
|
||||
|
||||
class InitState extends State
|
||||
{
|
||||
private machine: BasicStateMachine;
|
||||
public systems: string[];
|
||||
constructor(machine: BasicStateMachine)
|
||||
{
|
||||
super('InitState');
|
||||
this.machine = machine;
|
||||
this.systems = ['init', 'grid'];
|
||||
}
|
||||
|
||||
override enter()
|
||||
{
|
||||
this.machine.onEnter();
|
||||
}
|
||||
|
||||
override handleInput(input: any)
|
||||
{
|
||||
if (!input.flags.init) return;
|
||||
this.machine.setState(STATES.IDLE);
|
||||
}
|
||||
|
||||
override leave()
|
||||
{
|
||||
this.machine.onLeave();
|
||||
}
|
||||
}
|
||||
|
||||
class IdleState extends State
|
||||
{
|
||||
private machine: BasicStateMachine;
|
||||
public systems: string[];
|
||||
constructor(machine: BasicStateMachine)
|
||||
{
|
||||
super('IdleState');
|
||||
this.machine = machine;
|
||||
this.systems = ['grid', 'idle', 'layering', 'interactive'];
|
||||
}
|
||||
|
||||
override enter()
|
||||
{
|
||||
this.machine.onEnter();
|
||||
}
|
||||
|
||||
override handleInput(target: BaseMachine)
|
||||
{
|
||||
if (target.inputs.spin === true)
|
||||
{
|
||||
this.machine.setState(STATES.SPINNING);
|
||||
}
|
||||
}
|
||||
|
||||
override leave()
|
||||
{
|
||||
this.machine.onLeave();
|
||||
}
|
||||
}
|
||||
|
||||
class SpinningState extends State
|
||||
{
|
||||
private machine: BasicStateMachine;
|
||||
public systems: string[];
|
||||
constructor(machine: BasicStateMachine)
|
||||
{
|
||||
super('SpinningState');
|
||||
this.machine = machine;
|
||||
this.systems = ['spin', 'layering'];
|
||||
}
|
||||
|
||||
override enter()
|
||||
{
|
||||
this.machine.onEnter();
|
||||
}
|
||||
|
||||
override handleInput(target: BaseMachine)
|
||||
{
|
||||
if (!this.systems.every((key) => target.systems[key].state === 'stopped')) return;
|
||||
this.machine.setState(STATES.IDLE);
|
||||
}
|
||||
|
||||
override leave()
|
||||
{
|
||||
this.machine.onLeave();
|
||||
}
|
||||
}
|
||||
345
src/states/ClassicReelsStateMachine.ts
Normal file
@@ -0,0 +1,345 @@
|
||||
import BaseMachine from '../slotMachines/BaseMachine';
|
||||
import { State, StateMachine } from '@popiplay/state-machine';
|
||||
|
||||
export enum STATES
|
||||
{
|
||||
INIT,
|
||||
IDLE,
|
||||
SPINNING,
|
||||
ALL_WINNING_SYMBOLS,
|
||||
CELEBRATE_SYMBOLS,
|
||||
ENDING_ROUND,
|
||||
WINNING_LINES_LOOP,
|
||||
}
|
||||
|
||||
type ClassicReelsStateMachineStates = InitState | IdleState | SpinningState | AllWinningSymbolsState | CelebrateSymbolsState | EndingRoundState | WinningLinesLoopState;
|
||||
|
||||
export default class ClassicReelsStateMachine extends StateMachine<BaseMachine, ClassicReelsStateMachineStates>
|
||||
{
|
||||
constructor(target: BaseMachine)
|
||||
{
|
||||
super(target);
|
||||
this.provideStates([
|
||||
new InitState(this),
|
||||
new IdleState(this),
|
||||
new SpinningState(this),
|
||||
new AllWinningSymbolsState(this),
|
||||
new CelebrateSymbolsState(this),
|
||||
new EndingRoundState(this),
|
||||
new WinningLinesLoopState(this),
|
||||
]);
|
||||
}
|
||||
|
||||
override update(dt: number)
|
||||
{
|
||||
super.update(dt);
|
||||
this.currentState.systems.forEach((systemId) =>
|
||||
{
|
||||
this.target.systems[systemId].update(dt);
|
||||
});
|
||||
}
|
||||
|
||||
onEnter()
|
||||
{
|
||||
console.log("Enter: ", this.currentState.name)
|
||||
this.currentState.systems.forEach((systemId) =>
|
||||
{
|
||||
this.target.systems[systemId].onEnter();
|
||||
});
|
||||
}
|
||||
|
||||
onLeave()
|
||||
{
|
||||
this.currentState.systems.forEach((systemId) =>
|
||||
{
|
||||
this.target.systems[systemId].onLeave();
|
||||
});
|
||||
}
|
||||
|
||||
init()
|
||||
{
|
||||
this.setState(STATES.INIT);
|
||||
}
|
||||
}
|
||||
|
||||
class InitState extends State
|
||||
{
|
||||
private machine: ClassicReelsStateMachine;
|
||||
public systems: string[];
|
||||
constructor(machine: ClassicReelsStateMachine)
|
||||
{
|
||||
super('InitState');
|
||||
this.machine = machine;
|
||||
this.systems = ['init', 'grid', 'blurSymbols'];
|
||||
}
|
||||
|
||||
override enter()
|
||||
{
|
||||
this.machine.onEnter();
|
||||
}
|
||||
|
||||
override handleInput(input: any)
|
||||
{
|
||||
if (!input.flags.init) return;
|
||||
if (input.inputs.wins)
|
||||
{
|
||||
this.machine.setState(STATES.ALL_WINNING_SYMBOLS);
|
||||
}
|
||||
else if (input.inputs.celebrateSymbolIndexes?.length > 0)
|
||||
{
|
||||
this.machine.setState(STATES.CELEBRATE_SYMBOLS);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.machine.setState(STATES.ENDING_ROUND);
|
||||
}
|
||||
}
|
||||
|
||||
override leave()
|
||||
{
|
||||
this.machine.onLeave();
|
||||
}
|
||||
}
|
||||
|
||||
class IdleState extends State
|
||||
{
|
||||
private machine: ClassicReelsStateMachine;
|
||||
public systems: string[];
|
||||
constructor(machine: ClassicReelsStateMachine)
|
||||
{
|
||||
super('IdleState');
|
||||
this.machine = machine;
|
||||
this.systems = [
|
||||
'grid',
|
||||
'idle',
|
||||
'layering',
|
||||
'interactive',
|
||||
'resetWinningSymbols',
|
||||
'resetTinting',
|
||||
'tinting',
|
||||
'winLinesResetIncrement',
|
||||
'blurSymbols'
|
||||
];
|
||||
}
|
||||
|
||||
override enter()
|
||||
{
|
||||
this.machine.onEnter();
|
||||
}
|
||||
|
||||
override handleInput(target: BaseMachine)
|
||||
{
|
||||
if (target.inputs.spin === true)
|
||||
{
|
||||
this.machine.setState(STATES.SPINNING);
|
||||
}
|
||||
}
|
||||
|
||||
override leave()
|
||||
{
|
||||
this.machine.onLeave();
|
||||
}
|
||||
}
|
||||
|
||||
class SpinningState extends State
|
||||
{
|
||||
private machine: ClassicReelsStateMachine;
|
||||
public systems: string[];
|
||||
constructor(machine: ClassicReelsStateMachine)
|
||||
{
|
||||
super('SpinningState');
|
||||
this.machine = machine;
|
||||
this.systems = [
|
||||
'spin',
|
||||
'layering',
|
||||
'resetWinningSymbols',
|
||||
'resetTinting',
|
||||
'tinting',
|
||||
'winLinesResetIncrement',
|
||||
'blurSymbols'
|
||||
];
|
||||
}
|
||||
|
||||
override enter()
|
||||
{
|
||||
this.machine.onEnter();
|
||||
}
|
||||
|
||||
override handleInput(target: BaseMachine)
|
||||
{
|
||||
if (!this.systems.every((key) => target.systems[key].state === 'stopped')) return;
|
||||
if (target.inputs.wins)
|
||||
{
|
||||
this.machine.setState(STATES.ALL_WINNING_SYMBOLS);
|
||||
}
|
||||
else if (target.inputs.celebrateSymbolIndexes?.length > 0)
|
||||
{
|
||||
this.machine.setState(STATES.CELEBRATE_SYMBOLS);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.machine.setState(STATES.ENDING_ROUND);
|
||||
}
|
||||
}
|
||||
|
||||
override leave()
|
||||
{
|
||||
this.machine.onLeave();
|
||||
}
|
||||
}
|
||||
|
||||
class AllWinningSymbolsState extends State
|
||||
{
|
||||
private machine: ClassicReelsStateMachine;
|
||||
public systems: string[];
|
||||
constructor(machine: ClassicReelsStateMachine)
|
||||
{
|
||||
super('AllWinningSymbolsState');
|
||||
this.machine = machine;
|
||||
this.systems = [
|
||||
'resetWinningSymbols',
|
||||
'allWinSymbols',
|
||||
'allWinLinesSet',
|
||||
'winningSymbolsAnim',
|
||||
'tintWinningSymbolsAnim',
|
||||
'tinting',
|
||||
'winLinesAnimations',
|
||||
'displayTotalLinesWin',
|
||||
'blurSymbols'
|
||||
];
|
||||
}
|
||||
|
||||
override enter()
|
||||
{
|
||||
this.machine.onEnter();
|
||||
}
|
||||
|
||||
override handleInput(target: BaseMachine)
|
||||
{
|
||||
if (!this.systems.every((key) => target.systems[key].state === 'stopped')) return;
|
||||
if (target.inputs.celebrateSymbolIndexes?.length > 0)
|
||||
{
|
||||
this.machine.setState(STATES.CELEBRATE_SYMBOLS);
|
||||
} else
|
||||
{
|
||||
this.machine.setState(STATES.ENDING_ROUND);
|
||||
}
|
||||
}
|
||||
|
||||
override leave()
|
||||
{
|
||||
this.machine.onLeave();
|
||||
}
|
||||
}
|
||||
|
||||
class CelebrateSymbolsState extends State
|
||||
{
|
||||
private machine: ClassicReelsStateMachine;
|
||||
public systems: string[];
|
||||
constructor(machine: ClassicReelsStateMachine)
|
||||
{
|
||||
super('CelebrateSymbolsState');
|
||||
this.machine = machine;
|
||||
this.systems = ['celebrateSymbols', 'tinting', 'blurSymbols'];
|
||||
}
|
||||
|
||||
override enter()
|
||||
{
|
||||
this.machine.onEnter();
|
||||
}
|
||||
|
||||
override handleInput(target: BaseMachine)
|
||||
{
|
||||
if (!this.systems.every((key) => target.systems[key].state === 'stopped')) return;
|
||||
this.machine.setState(STATES.ENDING_ROUND);
|
||||
}
|
||||
|
||||
override leave()
|
||||
{
|
||||
this.machine.onLeave();
|
||||
}
|
||||
}
|
||||
|
||||
class EndingRoundState extends State
|
||||
{
|
||||
private machine: ClassicReelsStateMachine;
|
||||
public systems: string[];
|
||||
constructor(machine: ClassicReelsStateMachine)
|
||||
{
|
||||
super('EndingRoundState');
|
||||
this.machine = machine;
|
||||
this.systems = ['blockExitState', 'blurSymbols'];
|
||||
}
|
||||
|
||||
override enter()
|
||||
{
|
||||
this.machine.onEnter();
|
||||
}
|
||||
|
||||
override handleInput(target: BaseMachine)
|
||||
{
|
||||
if (!this.systems.every((key) => target.systems[key].state === 'stopped')) return;
|
||||
if (target.inputs.spin) {
|
||||
this.machine.setState(STATES.SPINNING);
|
||||
}
|
||||
else if (target.inputs.wins)
|
||||
{
|
||||
this.machine.setState(STATES.WINNING_LINES_LOOP);
|
||||
} else
|
||||
{
|
||||
this.machine.setState(STATES.IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
override leave()
|
||||
{
|
||||
this.machine.onLeave();
|
||||
}
|
||||
}
|
||||
|
||||
class WinningLinesLoopState extends State
|
||||
{
|
||||
private machine: ClassicReelsStateMachine;
|
||||
public systems: string[];
|
||||
constructor(machine: ClassicReelsStateMachine)
|
||||
{
|
||||
super('WinningLinesLoopState');
|
||||
this.machine = machine;
|
||||
this.systems = [
|
||||
'resetWinningSymbols',
|
||||
'winLinesIncrementing',
|
||||
'setWinLineSymbols',
|
||||
'setWinLine',
|
||||
'winningSymbolsAnim',
|
||||
'tintWinningSymbolsAnim',
|
||||
'tinting',
|
||||
'interactive',
|
||||
'winLinesAnimations',
|
||||
'displayLineWin',
|
||||
'blurSymbols'
|
||||
];
|
||||
}
|
||||
|
||||
override enter()
|
||||
{
|
||||
this.machine.onEnter();
|
||||
}
|
||||
|
||||
override handleInput(target: BaseMachine)
|
||||
{
|
||||
if (target.inputs.spin === true)
|
||||
{
|
||||
this.machine.setState(STATES.SPINNING);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.systems.every((key) => target.systems[key].state === 'stopped')) return;
|
||||
this.machine.setState(STATES.WINNING_LINES_LOOP);
|
||||
}
|
||||
|
||||
override leave()
|
||||
{
|
||||
this.machine.onLeave();
|
||||
}
|
||||
}
|
||||
70
src/stories/AnimatedExpandedSymbolsSystem.stories.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import ReelsMachine from 'src/slotMachines/ReelsMachine';
|
||||
import { STATES } from 'src/states/BasicStateMachine';
|
||||
import { argTypes, getDefaultArgs } from 'src/stories/utils/argTypes';
|
||||
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
|
||||
import MachineContainer from 'src/stories/utils/MachineContainer';
|
||||
import { centerView } from 'src/stories/utils/resize';
|
||||
import AnimatedExpandedSymbolsSystem from 'src/systems/AnimatedExpandedSymbolsSystem';
|
||||
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import MockSystem from "src/systems/MockSystem";
|
||||
|
||||
const args = {
|
||||
key: 'K',
|
||||
zIndex: 4,
|
||||
};
|
||||
|
||||
const meta: Meta<typeof AnimatedExpandedSymbolsSystem> = {
|
||||
title: 'Systems/ExpandingSpinSystem',
|
||||
tags: ['autodocs'],
|
||||
parameters: {
|
||||
docs: {
|
||||
story: {
|
||||
height: 600
|
||||
},
|
||||
description: {
|
||||
component: 'Expanding symbols mechanics.'
|
||||
},
|
||||
}
|
||||
},
|
||||
argTypes: argTypes(args),
|
||||
args: getDefaultArgs(args),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const ReelsMachineStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context, init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'area69/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new ReelsMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
machine.systems.blockExitState = new MockSystem(machine)
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.systems.expanding = new AnimatedExpandedSymbolsSystem(machine);
|
||||
machine.stateMachine.states[STATES.SPINNING].systems = ['layering', 'spin', 'expanding'];
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = machine.inputs.initMap;
|
||||
machine.systems.expanding.setSettings(_params);
|
||||
machine.inputs.spin = true;
|
||||
|
||||
machine.inputs.blockExitState = true;
|
||||
setTimeout(() => {
|
||||
machine.inputs.blockExitState = false;
|
||||
}, 250);
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
}
|
||||
});
|
||||
97
src/stories/AnticipationSystem.stories.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import { argTypes, getDefaultArgs } from 'src/stories/utils/argTypes';
|
||||
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
|
||||
import MachineContainer from 'src/stories/utils/MachineContainer';
|
||||
import { centerView } from 'src/stories/utils/resize';
|
||||
import ReelsSpinSystem from 'src/systems/ReelsSpinSystem';
|
||||
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import ReelsMachine from '../slotMachines/ReelsMachine';
|
||||
import { STATES } from '../states/ClassicReelsStateMachine';
|
||||
import AnticipationSystem from '../systems/AnticipationSystem';
|
||||
import AnticipationAnimationsSystem from "../systems/AnticipationAnimationsSystem";
|
||||
import AnticipationBack from "../entities/AnticipationBack";
|
||||
import AnticipationFront from "../entities/AnticipationFront";
|
||||
import MockSystem from "src/systems/MockSystem";
|
||||
|
||||
const args = {
|
||||
accelerationTime: 1,
|
||||
backCoef: 1.1,
|
||||
maxSpeed: 40,
|
||||
reelStartDelay: 0.2,
|
||||
reelStopDelay: 0.5,
|
||||
spinningDuration: 2,
|
||||
blur: true,
|
||||
bounce: true
|
||||
};
|
||||
|
||||
const meta: Meta<typeof ReelsSpinSystem> = {
|
||||
title: 'Systems/AnticipationSystem', // Путь для отображения в Storybook
|
||||
tags: ['autodocs'], // Включает автоматическую генерацию документации
|
||||
parameters: {
|
||||
docs: {
|
||||
story: {
|
||||
height: 600
|
||||
},
|
||||
description: {
|
||||
component: 'Anticipation of landing symbols',
|
||||
},
|
||||
}
|
||||
},
|
||||
argTypes: argTypes(args),
|
||||
args: getDefaultArgs(args),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const AnticipationSystemStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context, init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'area69/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new ReelsMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
machine.systems.blockExitState = new MockSystem(machine)
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.systems.spin.setSettings(_params);
|
||||
machine.systems.anticipation1 = new AnticipationSystem(machine);
|
||||
machine.systems.anticipationAnim = new AnticipationAnimationsSystem(machine);
|
||||
machine.systems.anticipationAnim.setSettings({
|
||||
entityClass: AnticipationBack,
|
||||
zIndex: 0
|
||||
});
|
||||
machine.systems.anticipationAnimFront = new AnticipationAnimationsSystem(machine);
|
||||
machine.systems.anticipationAnimFront.setSettings({
|
||||
entityClass: AnticipationFront,
|
||||
zIndex: 10
|
||||
});
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = createScreenInputMap(config)
|
||||
.map((record, index) => ([0, 1].includes(index) ? { ...record, key: 'A' } : record))
|
||||
.map((record, index) => ([5].includes(index) ? { ...record, key: 'B' } : record));
|
||||
|
||||
// machine.systems.anticipation1.setSettings({ anticipatingSymbolKey: 'A', preventReelStopIndex: 0 });
|
||||
// machine.systems.anticipation2 = new AnticipationSystem(machine);
|
||||
// machine.systems.anticipation2.setSettings({ anticipatingSymbolKey: ['B', 'C'], preventReelStopIndex: 1 });
|
||||
// machine.stateMachine.states[STATES.SPINNING].systems = ['anticipation1', 'anticipation2', 'spin',];
|
||||
machine.stateMachine.states[STATES.SPINNING].systems = ['anticipation1', "anticipationAnim", "anticipationAnimFront", 'spin'];
|
||||
machine.inputs.spin = true;
|
||||
machine.inputs.blockExitState = true;
|
||||
setTimeout(() => {
|
||||
machine.inputs.blockExitState = false;
|
||||
}, 250);
|
||||
centerView(view);
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
}
|
||||
});
|
||||
84
src/stories/BlurSymbolsSystem.stories.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import ReelsMachine from 'src/slotMachines/ReelsMachine';
|
||||
import { argTypes, getDefaultArgs } from 'src/stories/utils/argTypes';
|
||||
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
|
||||
import MachineContainer from 'src/stories/utils/MachineContainer';
|
||||
import { centerView } from 'src/stories/utils/resize';
|
||||
import MockSystem from 'src/systems/MockSystem';
|
||||
import BlurSymbolsSystem, { BlurSymbolsSystemSettings } from 'src/systems/BlurSymbolsSystem';
|
||||
import MaskingSystem from 'src/systems/MaskingSystem';
|
||||
import { STATES } from 'src/states/ClassicReelsStateMachine';
|
||||
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
|
||||
|
||||
const args = {
|
||||
velocityX: 0,
|
||||
velocityY: -50,
|
||||
kernelSize: 15,
|
||||
offset: 7,
|
||||
};
|
||||
|
||||
const meta: Meta<typeof BlurSymbolsSystem> = {
|
||||
title: 'Systems/BlurSymbolsSystem',
|
||||
tags: ['autodocs'],
|
||||
parameters: {
|
||||
docs: {
|
||||
story: {
|
||||
height: 600,
|
||||
},
|
||||
description: {
|
||||
component: 'Adjust the blur filter velocity, kernel size, and offset to preview motion blur strength while the reels spin. The story mirrors the standard machine boot flow and calls `setSettings` so designers can iterate on blur intensity in real time.',
|
||||
},
|
||||
},
|
||||
},
|
||||
argTypes: argTypes(args),
|
||||
args: getDefaultArgs(args),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const BlurSymbolsSystemStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context,
|
||||
init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'area69/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new ReelsMachine(config);
|
||||
machine.systems.masking = new MaskingSystem(machine);
|
||||
machine.stateMachine.states[STATES.SPINNING].systems.push('masking');
|
||||
const container = new MachineContainer(machine, false);
|
||||
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.systems.blockExitState = new MockSystem(machine);
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = createScreenInputMap(config);
|
||||
|
||||
const blurSettings: Partial<BlurSymbolsSystemSettings> = {
|
||||
velocity: [_params.velocityX, _params.velocityY],
|
||||
kernelSize: _params.kernelSize,
|
||||
offset: _params.offset,
|
||||
};
|
||||
(machine.systems.blurSymbols as BlurSymbolsSystem).setSettings(blurSettings);
|
||||
|
||||
machine.inputs.spin = true;
|
||||
|
||||
machine.inputs.blockExitState = true;
|
||||
setTimeout(() =>
|
||||
{
|
||||
machine.inputs.blockExitState = false;
|
||||
}, 250);
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
}
|
||||
});
|
||||
64
src/stories/CascadeSpinSystem.stories.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import CascadeMachine from 'src/slotMachines/CascadeMachine';
|
||||
import { argTypes, getDefaultArgs } from 'src/stories/utils/argTypes';
|
||||
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
|
||||
import MachineContainer from 'src/stories/utils/MachineContainer';
|
||||
import { centerView } from 'src/stories/utils/resize';
|
||||
import CascadeSpinSystem from 'src/systems/CascadeSpinSystem';
|
||||
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import MockSystem from "src/systems/MockSystem";
|
||||
|
||||
const args = {
|
||||
gravity: 4000,
|
||||
reelDelay: 0.12,
|
||||
slotDelay: 0.03,
|
||||
refillDelay: 0.5,
|
||||
};
|
||||
|
||||
const meta: Meta<typeof CascadeSpinSystem> = {
|
||||
title: 'Systems/CascadeSpinSystem', // Путь для отображения в Storybook
|
||||
tags: ['autodocs'], // Включает автоматическую генерацию документации
|
||||
parameters: {
|
||||
docs: {
|
||||
story: {
|
||||
height: 600
|
||||
},
|
||||
description: {
|
||||
component: 'Manages the cascade spinning effect in the slot machine. Handles dropping, refilling, and bouncing symbols with gravity effects.'
|
||||
},
|
||||
}
|
||||
},
|
||||
argTypes: argTypes(args),
|
||||
args: getDefaultArgs(args),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const CascadeSpinSystemStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context, init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'bubblex/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new CascadeMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
machine.systems.blockExitState = new MockSystem(machine)
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = createScreenInputMap(config);
|
||||
machine.systems.spin.setSettings(_params);
|
||||
machine.inputs.spin = true;
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
}
|
||||
});
|
||||
70
src/stories/CelebrateSystem.stories.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import { argTypes, getDefaultArgs } from 'src/stories/utils/argTypes';
|
||||
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
|
||||
import MachineContainer from 'src/stories/utils/MachineContainer';
|
||||
import { centerView } from 'src/stories/utils/resize';
|
||||
import ReelsSpinSystem from 'src/systems/ReelsSpinSystem';
|
||||
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import ReelsMachine from '../slotMachines/ReelsMachine';
|
||||
import MockSystem from "src/systems/MockSystem";
|
||||
|
||||
const args = {
|
||||
accelerationTime: 1,
|
||||
backCoef: 1.1,
|
||||
maxSpeed: 40,
|
||||
reelStartDelay: 0.2,
|
||||
reelStopDelay: 0.5,
|
||||
spinningDuration: 2,
|
||||
blur: true,
|
||||
bounce: true
|
||||
};
|
||||
|
||||
const meta: Meta<typeof ReelsSpinSystem> = {
|
||||
title: 'Systems/CelebrateSystem', // Путь для отображения в Storybook
|
||||
tags: ['autodocs'], // Включает автоматическую генерацию документации
|
||||
parameters: {
|
||||
docs: {
|
||||
story: {
|
||||
height: 600
|
||||
},
|
||||
description: {
|
||||
component: 'Celebrate symbols',
|
||||
},
|
||||
}
|
||||
},
|
||||
argTypes: argTypes(args),
|
||||
args: getDefaultArgs(args),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const CelebrateSystemStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context, init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'area69/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new ReelsMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
machine.systems.blockExitState = new MockSystem(machine)
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = createScreenInputMap(config)
|
||||
.map((record, index) => ([0, 1].includes(index) ? { ...record, key: 'A' } : record))
|
||||
.map((record, index) => ([5].includes(index) ? { ...record, key: 'A' } : record));
|
||||
machine.inputs.celebrateSymbolIndexes = [0, 1, 5];
|
||||
machine.inputs.spin = true;
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
}
|
||||
});
|
||||
56
src/stories/GridSystem.stories.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import ReelsMachine from 'src/slotMachines/ReelsMachine';
|
||||
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
|
||||
import MachineContainer from 'src/stories/utils/MachineContainer';
|
||||
import { centerView } from 'src/stories/utils/resize';
|
||||
import GridSystem from 'src/systems/GridSystem';
|
||||
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import MockSystem from "src/systems/MockSystem";
|
||||
|
||||
const args = {
|
||||
|
||||
};
|
||||
|
||||
const meta: Meta<typeof GridSystem> = {
|
||||
title: 'Systems/GridSystem', // Путь для отображения в Storybook
|
||||
tags: ['autodocs'], // Включает автоматическую генерацию документации
|
||||
parameters: {
|
||||
docs: {
|
||||
story: {
|
||||
height: 600
|
||||
},
|
||||
description: {
|
||||
component: 'Manages the positioning of symbols on a grid for a slot machine'
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const GridSystemStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context, init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'area69/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new ReelsMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
machine.systems.blockExitState = new MockSystem(machine)
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.systems.idle.setSettings(_params);
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
}
|
||||
});
|
||||
225
src/stories/HoldAndWin.stories.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import HoldAndWinMachine from 'src/slotMachines/HoldAndWinMachine';
|
||||
import { argTypes, getDefaultArgs } from 'src/stories/utils/argTypes';
|
||||
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
|
||||
import MachineContainer from 'src/stories/utils/MachineContainer';
|
||||
import { centerView } from 'src/stories/utils/resize';
|
||||
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import IdleSystem from '../systems/IdleSystem';
|
||||
import StickySymbolsSystem from 'src/systems/StickySymbolsSystem';
|
||||
import { STATES } from 'src/states/BasicStateMachine';
|
||||
|
||||
const args = {
|
||||
accelerationTime: 1,
|
||||
backCoef: 1.1,
|
||||
maxSpeed: 15,
|
||||
reelStartDelay: 0.2,
|
||||
reelStopDelay: 0.5,
|
||||
slotStopDelay: 0,
|
||||
spinningDuration: 2,
|
||||
blur: false,
|
||||
bounce: true
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates argTypes with fractional range steps for finer Hold & Win tuning.
|
||||
*/
|
||||
const createHoldAndWinArgTypes = (initialArgs: typeof args): ReturnType<typeof argTypes> => {
|
||||
const baseArgTypes = argTypes(initialArgs);
|
||||
|
||||
Object.keys(initialArgs).forEach((key) => {
|
||||
|
||||
const descriptor = baseArgTypes[key];
|
||||
const control = descriptor?.control;
|
||||
|
||||
if (!control || control.type !== 'range') return;
|
||||
|
||||
baseArgTypes[key] = {
|
||||
...descriptor,
|
||||
control: {
|
||||
...control,
|
||||
step: control.step && control.step < 1 ? control.step : 0.1,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return baseArgTypes;
|
||||
};
|
||||
|
||||
const meta: Meta<typeof IdleSystem> = {
|
||||
title: 'features/HoldAndWin', // Путь для отображения в Storybook
|
||||
tags: ['autodocs'], // Включает автоматическую генерацию документации
|
||||
parameters: {
|
||||
docs: {
|
||||
story: {
|
||||
height: 600
|
||||
},
|
||||
description: {
|
||||
component: 'Manages idle animations for random symbols in the slot machine. When the system is idle, it triggers an idle animation on a randomly chosen symbol.'
|
||||
},
|
||||
}
|
||||
},
|
||||
argTypes: createHoldAndWinArgTypes(args),
|
||||
args: getDefaultArgs(args),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const SingleSlotReelsStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context,
|
||||
init: async (view) => {
|
||||
Assets.reset();
|
||||
await Assets.init({ manifest: 'hold-and-win/manifest.json' });
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new HoldAndWinMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
container.scale.set(0.5);
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
// Prepare input maps for screen and initialization
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = createScreenInputMap(config);
|
||||
// Apply story args to spin system and trigger spin
|
||||
machine.systems.spin.setSettings(_params as any);
|
||||
machine.inputs.spin = true;
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () => {
|
||||
const dt = Ticker.shared.deltaMS;
|
||||
if (context.machine) context.machine.update(dt);
|
||||
}
|
||||
});
|
||||
|
||||
export const StickSystemStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context,
|
||||
init: async (view) => {
|
||||
Assets.reset();
|
||||
await Assets.init({ manifest: 'hold-and-win/manifest.json' });
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new HoldAndWinMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
container.scale.set(0.5);
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
|
||||
// Attach StickySymbolsSystem to HoldAndWinMachine
|
||||
machine.systems.stick = new StickySymbolsSystem(machine as any);
|
||||
// Ensure stick system runs during all states where needed
|
||||
machine.stateMachine.states[STATES.SPINNING].systems = ['spin', 'layering'];
|
||||
machine.stateMachine.states.forEach((state) => {
|
||||
if (!state.systems.includes('stick')) state.systems.push('stick');
|
||||
});
|
||||
|
||||
// Prepare input maps for screen and initialization
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = machine.inputs.initMap;
|
||||
|
||||
// Mark which symbols should become sticky; example: stick wilds L/M/N
|
||||
// This ensures only those cells are hidden behind stickies; others remain visible during spin
|
||||
machine.maps.stickySymbols = machine.inputs.screenMap.map((s: any) => ['L', 'M', 'N'].includes(s.key));
|
||||
|
||||
// Configure systems
|
||||
if ((machine.systems as any).stick?.setSettings) (machine.systems as any).stick.setSettings({ zIndex: 5 });
|
||||
machine.systems.spin.setSettings(_params as any);
|
||||
|
||||
// Trigger spin
|
||||
machine.inputs.spin = true;
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () => {
|
||||
const dt = Ticker.shared.deltaMS;
|
||||
if (context.machine) context.machine.update(dt);
|
||||
}
|
||||
});
|
||||
|
||||
export const SingleSlotReelsPreventSpinStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context,
|
||||
init: async (view) => {
|
||||
Assets.reset();
|
||||
await Assets.init({ manifest: 'hold-and-win/manifest.json' });
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new HoldAndWinMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
container.scale.set(0.5);
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
// Prepare input maps for screen and initialization
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.maps.preventReelsSpin = machine.inputs.initMap.map((s: any) => s?.key === 'K');
|
||||
machine.inputs.screenMap = machine.inputs.initMap
|
||||
// Apply story args to spin system and trigger spin
|
||||
machine.systems.spin.setSettings(_params as any);
|
||||
machine.inputs.spin = true;
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () => {
|
||||
const dt = Ticker.shared.deltaMS;
|
||||
if (context.machine) context.machine.update(dt);
|
||||
}
|
||||
});
|
||||
|
||||
export const DoubleHoldAndWinStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context,
|
||||
init: async (view) => {
|
||||
Assets.reset();
|
||||
await Assets.init({ manifest: 'hold-and-win/manifest.json' });
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
|
||||
// Create two machines
|
||||
const machineTop = new HoldAndWinMachine(config);
|
||||
const containerTop = new MachineContainer(machineTop);
|
||||
const machineBottom = new HoldAndWinMachine(config);
|
||||
const containerBottom = new MachineContainer(machineBottom);
|
||||
|
||||
// Scale both machines to half size
|
||||
containerTop.scale.set(0.5);
|
||||
containerBottom.scale.set(0.5);
|
||||
|
||||
// Add to view
|
||||
view.addChild(containerTop);
|
||||
view.addChild(containerBottom);
|
||||
|
||||
// Keep references for update loop
|
||||
(context as any).machines = [machineTop, machineBottom];
|
||||
|
||||
// Prepare input maps (different for each machine)
|
||||
const initMapTop = createScreenInputMap(config);
|
||||
const initMapBottom = [...createScreenInputMap(config)].reverse(); // make it different
|
||||
|
||||
machineTop.inputs.initMap = initMapTop;
|
||||
machineTop.inputs.screenMap = initMapTop;
|
||||
|
||||
machineBottom.inputs.initMap = initMapBottom;
|
||||
machineBottom.inputs.screenMap = initMapBottom;
|
||||
|
||||
// Apply story args to both spin systems and trigger spin
|
||||
machineTop.systems.spin.setSettings(_params as any);
|
||||
machineBottom.systems.spin.setSettings(_params as any);
|
||||
|
||||
machineTop.inputs.spin = true;
|
||||
machineBottom.inputs.spin = true;
|
||||
|
||||
// Layout: one above the other
|
||||
containerBottom.y = containerTop.height + 50; // spacing between machines
|
||||
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () => {
|
||||
const dt = Ticker.shared.deltaMS;
|
||||
const machines = (context as any).machines as any[] | undefined;
|
||||
if (machines) machines.forEach((m: any) => m.update(dt));
|
||||
}
|
||||
});
|
||||
65
src/stories/IdleSystem.stories.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import ReelsMachine from 'src/slotMachines/ReelsMachine';
|
||||
import { argTypes, getDefaultArgs } from 'src/stories/utils/argTypes';
|
||||
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
|
||||
import MachineContainer from 'src/stories/utils/MachineContainer';
|
||||
import { centerView } from 'src/stories/utils/resize';
|
||||
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import IdleSystem from '../systems/IdleSystem';
|
||||
import MockSystem from "src/systems/MockSystem";
|
||||
|
||||
const args = {
|
||||
maxAnimatedSymbols: 5
|
||||
};
|
||||
|
||||
const meta: Meta<typeof IdleSystem> = {
|
||||
title: 'Systems/IdleSystem', // Путь для отображения в Storybook
|
||||
tags: ['autodocs'], // Включает автоматическую генерацию документации
|
||||
parameters: {
|
||||
docs: {
|
||||
story: {
|
||||
height: 600
|
||||
},
|
||||
description: {
|
||||
component: 'Manages idle animations for random symbols in the slot machine. When the system is idle, it triggers an idle animation on a randomly chosen symbol.'
|
||||
},
|
||||
}
|
||||
},
|
||||
argTypes: argTypes(args),
|
||||
args: getDefaultArgs(args),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const IdleSystemStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context, init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'area69/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new ReelsMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.systems.idle.setSettings(_params);
|
||||
machine.systems.blockExitState = new MockSystem(machine)
|
||||
machine.inputs.blockExitState = true;
|
||||
setTimeout(() => {
|
||||
machine.inputs.blockExitState = false;
|
||||
}, 250);
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
context.machine.systems.idle.setSettings(_params);
|
||||
}
|
||||
});
|
||||
57
src/stories/InitSystem.stories.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import ReelsMachine from 'src/slotMachines/ReelsMachine';
|
||||
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
|
||||
import MachineContainer from 'src/stories/utils/MachineContainer';
|
||||
import { centerView } from 'src/stories/utils/resize';
|
||||
import GridSystem from 'src/systems/GridSystem';
|
||||
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import MockSystem from "src/systems/MockSystem";
|
||||
|
||||
const args = {
|
||||
|
||||
};
|
||||
|
||||
const meta: Meta<typeof GridSystem> = {
|
||||
title: 'Systems/InitSystem', // Путь для отображения в Storybook
|
||||
tags: ['autodocs'], // Включает автоматическую генерацию документации
|
||||
parameters: {
|
||||
docs: {
|
||||
story: {
|
||||
height: 600
|
||||
},
|
||||
description: {
|
||||
component: 'A system responsible for initializing symbols in the slot machine. It only runs once, adding symbols based on `initMap` data to the main scene and marking initialization as complete.'
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const GridSystemStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context, init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'area69/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new ReelsMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
machine.systems.blockExitState = new MockSystem(machine)
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.systems.idle.setSettings(_params);
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
context.machine.systems.idle.setSettings(_params);
|
||||
}
|
||||
});
|
||||
61
src/stories/LayeringSystem.stories.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import ReelsMachine from 'src/slotMachines/ReelsMachine';
|
||||
import { argTypes, getDefaultArgs } from 'src/stories/utils/argTypes';
|
||||
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
|
||||
import MachineContainer from 'src/stories/utils/MachineContainer';
|
||||
import { centerView } from 'src/stories/utils/resize';
|
||||
import ReelsSpinSystem from 'src/systems/ReelsSpinSystem';
|
||||
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import MockSystem from "src/systems/MockSystem";
|
||||
|
||||
const args = {
|
||||
|
||||
};
|
||||
|
||||
const meta: Meta<typeof ReelsSpinSystem> = {
|
||||
title: 'Systems/LayeringSystem', // Путь для отображения в Storybook
|
||||
tags: ['autodocs'], // Включает автоматическую генерацию документации
|
||||
parameters: {
|
||||
docs: {
|
||||
story: {
|
||||
height: 600
|
||||
},
|
||||
description: {
|
||||
component: 'Add doc'
|
||||
},
|
||||
}
|
||||
},
|
||||
argTypes: argTypes(args),
|
||||
args: getDefaultArgs(args),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const LayeringSystemStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context, init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'area69/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new ReelsMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
machine.systems.blockExitState = new MockSystem(machine)
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = createScreenInputMap(config);
|
||||
machine.systems.spin.setSettings(_params);
|
||||
machine.inputs.spin = false;
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
}
|
||||
});
|
||||
86
src/stories/MaskingSystem.stories.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import ReelsMachine from 'src/slotMachines/ReelsMachine';
|
||||
import { argTypes, getDefaultArgs } from 'src/stories/utils/argTypes';
|
||||
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
|
||||
import MachineContainer from 'src/stories/utils/MachineContainer';
|
||||
import { centerView } from 'src/stories/utils/resize';
|
||||
import MockSystem from 'src/systems/MockSystem';
|
||||
import MaskingSystem, { MaskingSystemSettings } from 'src/systems/MaskingSystem';
|
||||
import { STATES } from 'src/states/ClassicReelsStateMachine';
|
||||
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
|
||||
|
||||
const args = {
|
||||
enabled: true,
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
};
|
||||
|
||||
const meta: Meta<typeof MaskingSystem> = {
|
||||
title: 'Systems/MaskingSystem',
|
||||
tags: ['autodocs'],
|
||||
parameters: {
|
||||
docs: {
|
||||
story: {
|
||||
height: 600,
|
||||
},
|
||||
description: {
|
||||
component: 'Preview the rectangular reel viewport mask and toggle it with the provided controls. The story mirrors the base reel setup and forwards the knob values into `setSettings` so you can iterate on mask dimensions without leaving Storybook.',
|
||||
},
|
||||
},
|
||||
},
|
||||
argTypes: argTypes(args),
|
||||
args: getDefaultArgs(args),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const MaskingSystemStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context,
|
||||
init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'area69/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new ReelsMachine(config);
|
||||
machine.systems.masking = new MaskingSystem(machine);
|
||||
const spinningState = machine.stateMachine.states[STATES.SPINNING];
|
||||
if (!spinningState.systems.includes('masking'))
|
||||
{
|
||||
spinningState.systems.push('masking');
|
||||
}
|
||||
const container = new MachineContainer(machine, false);
|
||||
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.systems.blockExitState = new MockSystem(machine);
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = createScreenInputMap(config);
|
||||
|
||||
const maskSettings: Partial<MaskingSystemSettings> = {
|
||||
enabled: _params.enabled,
|
||||
width: _params.width,
|
||||
height: _params.height,
|
||||
};
|
||||
(machine.systems.masking as MaskingSystem).setSettings(maskSettings);
|
||||
|
||||
machine.inputs.spin = true;
|
||||
|
||||
machine.inputs.blockExitState = true;
|
||||
setTimeout(() =>
|
||||
{
|
||||
machine.inputs.blockExitState = false;
|
||||
}, 250);
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
}
|
||||
});
|
||||
119
src/stories/ReelsSpinSystem.stories.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import ReelsMachine from 'src/slotMachines/ReelsMachine';
|
||||
import { argTypes, getDefaultArgs } from 'src/stories/utils/argTypes';
|
||||
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
|
||||
import MachineContainer from 'src/stories/utils/MachineContainer';
|
||||
import { centerView } from 'src/stories/utils/resize';
|
||||
import ReelsSpinSystem from 'src/systems/ReelsSpinSystem';
|
||||
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import MockSystem from 'src/systems/MockSystem';
|
||||
import MaskingSystem from 'src/systems/MaskingSystem';
|
||||
import { STATES } from 'src/states/ClassicReelsStateMachine';
|
||||
|
||||
const args = {
|
||||
accelerationTime: 1,
|
||||
backCoef: 1.1,
|
||||
maxSpeed: 40,
|
||||
reelStartDelay: 0.2,
|
||||
reelStopDelay: 0.5,
|
||||
spinningDuration: 2,
|
||||
blur: true,
|
||||
bounce: true
|
||||
};
|
||||
|
||||
const meta: Meta<typeof ReelsSpinSystem> = {
|
||||
title: 'Systems/ReelsSpinSystem', // Путь для отображения в Storybook
|
||||
tags: ['autodocs'], // Включает автоматическую генерацию документации
|
||||
parameters: {
|
||||
docs: {
|
||||
story: {
|
||||
height: 600
|
||||
},
|
||||
description: {
|
||||
component: 'Controls the spinning behavior of slot machine reels, including acceleration, sustained spinning, and deceleration. This class manages reel state transitions, timing for each stage of the spin, and the display of symbols on each reel.'
|
||||
},
|
||||
}
|
||||
},
|
||||
argTypes: argTypes(args),
|
||||
args: getDefaultArgs(args),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const ReelsSpinSystemStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context, init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'area69/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new ReelsMachine(config);
|
||||
machine.systems.masking = new MaskingSystem(machine);
|
||||
machine.stateMachine.states[STATES.SPINNING].systems.push('masking');
|
||||
const container = new MachineContainer(machine, false);
|
||||
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.systems.blockExitState = new MockSystem(machine);
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = createScreenInputMap(config);
|
||||
machine.systems.spin.setSettings(_params);
|
||||
machine.inputs.spin = true;
|
||||
|
||||
machine.inputs.blockExitState = true;
|
||||
setTimeout(() => {
|
||||
machine.inputs.blockExitState = false;
|
||||
}, 250);
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
}
|
||||
});
|
||||
|
||||
export const ReelsSpinSystemPreventedReelsStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context, init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'area69/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new ReelsMachine(config);
|
||||
machine.systems.masking = new MaskingSystem(machine);
|
||||
machine.stateMachine.states[STATES.SPINNING].systems.push('masking');
|
||||
const container = new MachineContainer(machine, false);
|
||||
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.systems.blockExitState = new MockSystem(machine);
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = createScreenInputMap(config);
|
||||
machine.systems.spin.setSettings(_params);
|
||||
|
||||
// Disable first and last reels from spinning
|
||||
const reelsCount = config.reels;
|
||||
machine.maps.preventReelsSpin = Array.from({ length: reelsCount }, (_, i) => i === 0 || i === reelsCount - 1);
|
||||
|
||||
machine.inputs.spin = true;
|
||||
|
||||
machine.inputs.blockExitState = true;
|
||||
setTimeout(() => {
|
||||
machine.inputs.blockExitState = false;
|
||||
}, 250);
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
}
|
||||
});
|
||||
161
src/stories/SlotMultipliersSystem.stories.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import HoldAndWinMachine from 'src/slotMachines/HoldAndWinMachine';
|
||||
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
|
||||
import { centerView } from 'src/stories/utils/resize';
|
||||
import MachineContainer from 'src/stories/utils/MachineContainer';
|
||||
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
|
||||
import SlotMultipliersSystem from 'src/systems/SlotMultipliersSystem';
|
||||
import SlotMultiplierEntity from 'src/entities/SlotMultiplierEntity';
|
||||
import { STATES } from 'src/states/BasicStateMachine';
|
||||
import {argTypes, getDefaultArgs} from "src/stories/utils/argTypes";
|
||||
|
||||
const args = {
|
||||
accelerationTime: 1,
|
||||
backCoef: 1.1,
|
||||
maxSpeed: 15,
|
||||
reelStartDelay: 0.2,
|
||||
reelStopDelay: 0.5,
|
||||
slotStopDelay: 0,
|
||||
spinningDuration: 2,
|
||||
blur: false,
|
||||
bounce: true
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates argTypes with fractional range steps for finer Hold & Win tuning.
|
||||
*/
|
||||
const createHoldAndWinArgTypes = (initialArgs: typeof args): ReturnType<typeof argTypes> => {
|
||||
const baseArgTypes = argTypes(initialArgs);
|
||||
|
||||
Object.keys(initialArgs).forEach((key) => {
|
||||
|
||||
const descriptor = baseArgTypes[key];
|
||||
const control = descriptor?.control;
|
||||
|
||||
if (!control || control.type !== 'range') return;
|
||||
|
||||
baseArgTypes[key] = {
|
||||
...descriptor,
|
||||
control: {
|
||||
...control,
|
||||
step: control.step && control.step < 1 ? control.step : 0.1,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return baseArgTypes;
|
||||
};
|
||||
|
||||
const meta: Meta<typeof SlotMultipliersSystem> = {
|
||||
title: 'systems/SlotMultipliersSystem',
|
||||
tags: ['autodocs'],
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component: 'Displays Spine-based multiplier overlays for every slot based on the slotMultipliers map.',
|
||||
},
|
||||
},
|
||||
},
|
||||
argTypes: createHoldAndWinArgTypes(args),
|
||||
args: getDefaultArgs(args),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
|
||||
/**
|
||||
* Updates the multiplier map with random positive values and zeroed blanks.
|
||||
*/
|
||||
function randomiseMultipliers(machine: HoldAndWinMachine): void
|
||||
{
|
||||
const total = machine.config.reels * machine.config.slots;
|
||||
machine.maps.slotMultipliers = Array.from({ length: total }, () =>
|
||||
(Math.random() > 0.6 ? Math.ceil(Math.random() * 10) : 0));
|
||||
}
|
||||
|
||||
export const HoldAndWinMultipliers: StoryFn = (_params, context) => new PixiStory({
|
||||
context,
|
||||
init: async (view) => {
|
||||
Assets.reset();
|
||||
await Assets.init({ manifest: 'hold-and-win/manifest.json' });
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
await Assets.loadBundle('addons');
|
||||
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new HoldAndWinMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
container.scale.set(1);
|
||||
view.addChild(container);
|
||||
|
||||
context.machine = machine;
|
||||
context.prevSpinState = machine.systems.spin.state;
|
||||
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = machine.inputs.initMap;
|
||||
|
||||
const slotMultipliersSystem = new SlotMultipliersSystem(machine);
|
||||
slotMultipliersSystem.setSettings({ entityClass: SlotMultiplierEntity });
|
||||
machine.systems.slotMultipliers = slotMultipliersSystem;
|
||||
// Apply story args to spin system and trigger spin
|
||||
machine.systems.spin.setSettings(_params as any);
|
||||
|
||||
const idleState = machine.stateMachine.states[STATES.IDLE];
|
||||
if (idleState && !idleState.systems.includes('slotMultipliers'))
|
||||
{
|
||||
idleState.systems.push('slotMultipliers');
|
||||
}
|
||||
|
||||
const spinningState = machine.stateMachine.states[STATES.SPINNING];
|
||||
if (spinningState && !spinningState.systems.includes('slotMultipliers'))
|
||||
{
|
||||
spinningState.systems.push('slotMultipliers');
|
||||
}
|
||||
|
||||
randomiseMultipliers(machine);
|
||||
machine.inputs.spin = true;
|
||||
|
||||
context.spinDelayMs = 0;
|
||||
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () => {
|
||||
const machine: HoldAndWinMachine | undefined = context.machine;
|
||||
if (!machine) return;
|
||||
|
||||
const dt = Ticker.shared.deltaMS;
|
||||
machine.update(dt);
|
||||
|
||||
const spinSystem = machine.systems.spin;
|
||||
const prevState = context.prevSpinState ?? spinSystem.state;
|
||||
|
||||
if (prevState !== spinSystem.state)
|
||||
{
|
||||
if (spinSystem.state === 'starting')
|
||||
{
|
||||
machine.inputs.spin = false;
|
||||
}
|
||||
|
||||
if (spinSystem.state === 'stopped')
|
||||
{
|
||||
context.spinDelayMs = 500;
|
||||
}
|
||||
|
||||
context.prevSpinState = spinSystem.state;
|
||||
}
|
||||
|
||||
if ((context.spinDelayMs ?? 0) > 0)
|
||||
{
|
||||
context.spinDelayMs -= dt;
|
||||
|
||||
if (context.spinDelayMs <= 0)
|
||||
{
|
||||
randomiseMultipliers(machine);
|
||||
machine.inputs.spin = true;
|
||||
context.spinDelayMs = 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
69
src/stories/StaticReelSpinSystem.stories.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import StaticReelsMachine from '../slotMachines/StaticReelsMachine';
|
||||
import { argTypes, getDefaultArgs } from '../stories/utils/argTypes';
|
||||
import { createScreenInputMap } from '../stories/utils/createScreenInputMap';
|
||||
import MachineContainer from '../stories/utils/MachineContainer';
|
||||
import { centerView } from '../stories/utils/resize';
|
||||
import StaticReelsSpinSystem from '../systems/StaticReelsSpinSystem';
|
||||
import { SlotMachineConfig } from '../types/SlotMachineConfig';
|
||||
import MockSystem from "src/systems/MockSystem";
|
||||
|
||||
const args = {
|
||||
reelDelay: 0.1,
|
||||
slotDelay: 0.03,
|
||||
refillDelay: 0.15,
|
||||
};
|
||||
|
||||
const meta: Meta<typeof StaticReelsSpinSystem> = {
|
||||
title: 'Systems/StaticReelsSpinSystem',
|
||||
tags: ['autodocs'],
|
||||
parameters: {
|
||||
docs: {
|
||||
story: {
|
||||
height: 600
|
||||
},
|
||||
description: {
|
||||
component: 'Add doc'
|
||||
},
|
||||
}
|
||||
},
|
||||
argTypes: argTypes(args),
|
||||
args: getDefaultArgs(args),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const StaticReelsSpinSystemStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context, init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'bubblex/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new StaticReelsMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.systems.blockExitState = new MockSystem(machine)
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = createScreenInputMap(config);
|
||||
machine.systems.spin.setSettings(_params);
|
||||
machine.inputs.spin = true;
|
||||
|
||||
machine.inputs.blockExitState = true;
|
||||
setTimeout(() => {
|
||||
machine.inputs.blockExitState = false;
|
||||
}, 250);
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
}
|
||||
});
|
||||
137
src/stories/StickySymbolsSystem.stories.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import CascadeMachine from 'src/slotMachines/CascadeMachine';
|
||||
import ReelsMachine from 'src/slotMachines/ReelsMachine';
|
||||
import StaticReelsMachine from 'src/slotMachines/StaticReelsMachine';
|
||||
import { STATES } from 'src/states/BasicStateMachine';
|
||||
import { argTypes, getDefaultArgs } from 'src/stories/utils/argTypes';
|
||||
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
|
||||
import MachineContainer from 'src/stories/utils/MachineContainer';
|
||||
import { centerView } from 'src/stories/utils/resize';
|
||||
import StickySymbolsSystem from 'src/systems/StickySymbolsSystem';
|
||||
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import MockSystem from "src/systems/MockSystem";
|
||||
|
||||
const args = {
|
||||
zIndex: 5,
|
||||
};
|
||||
|
||||
const meta: Meta<typeof StickySymbolsSystem> = {
|
||||
title: 'Systems/StickySpinSystem',
|
||||
tags: ['autodocs'],
|
||||
parameters: {
|
||||
docs: {
|
||||
story: {
|
||||
height: 600
|
||||
},
|
||||
description: {
|
||||
component: 'Controls the spinning behavior of slot machine reels, including acceleration, sustained spinning, and deceleration. This class manages reel state transitions, timing for each stage of the spin, and the display of symbols on each reel.'
|
||||
},
|
||||
}
|
||||
},
|
||||
argTypes: argTypes(args),
|
||||
args: getDefaultArgs(args),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const ReelsMachineStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context, init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'area69/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new ReelsMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.systems.stick = new StickySymbolsSystem(machine);
|
||||
machine.stateMachine.states[STATES.SPINNING].systems = ['spin', 'layering'];
|
||||
machine.stateMachine.states.forEach((state) => state.systems.push('stick'));
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = machine.inputs.initMap;
|
||||
machine.maps.stickySymbols = machine.inputs.screenMap.map((s: any) => ['L', 'M', 'N'].includes(s.key));
|
||||
machine.systems.stick.setSettings(_params);
|
||||
machine.inputs.spin = true;
|
||||
machine.inputs.blockExitState = true;
|
||||
setTimeout(() => {
|
||||
machine.inputs.blockExitState = false;
|
||||
}, 250);
|
||||
centerView(view);
|
||||
centerView(view);
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
}
|
||||
});
|
||||
|
||||
export const CascadeMachineStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context, init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'bubblex/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new CascadeMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.systems.stick = new StickySymbolsSystem(machine);
|
||||
machine.stateMachine.states[STATES.SPINNING].systems = ['stick', 'spin'];
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = machine.inputs.initMap;
|
||||
machine.maps.stickySymbols = machine.inputs.screenMap.map((s: any) => ["RR"].includes(s.key));
|
||||
machine.systems.stick.setSettings(_params);
|
||||
machine.inputs.spin = true;
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
}
|
||||
});
|
||||
|
||||
export const StaticReelsMachineStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context, init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'bubblex/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new StaticReelsMachine(config);
|
||||
const container = new MachineContainer(machine);
|
||||
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.systems.blockExitState = new MockSystem(machine)
|
||||
machine.systems.stick = new StickySymbolsSystem(machine);
|
||||
machine.stateMachine.states[STATES.SPINNING].systems = ['stick', 'spin'];
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = machine.inputs.initMap;
|
||||
machine.maps.stickySymbols = machine.inputs.screenMap.map((s: any) => ["RR"].includes(s.key));
|
||||
machine.systems.stick.setSettings(_params);
|
||||
machine.inputs.spin = true;
|
||||
centerView(view);
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
}
|
||||
});
|
||||
217
src/stories/WinSymbolSystem.stories.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
import { Assets, Ticker } from 'pixi.js';
|
||||
import { argTypes, getDefaultArgs } from 'src/stories/utils/argTypes';
|
||||
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
|
||||
import MachineContainer from 'src/stories/utils/MachineContainer';
|
||||
import { centerView } from 'src/stories/utils/resize';
|
||||
import ReelsSpinSystem from 'src/systems/ReelsSpinSystem';
|
||||
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
|
||||
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
|
||||
import ReelsMachine from '../slotMachines/ReelsMachine';
|
||||
import MockSystem from "../systems/MockSystem";
|
||||
|
||||
const args = {
|
||||
key: 'A', // skin '10'
|
||||
accelerationTime: 1,
|
||||
backCoef: 1.1,
|
||||
maxSpeed: 40,
|
||||
reelStartDelay: 0.2,
|
||||
reelStopDelay: 0.5,
|
||||
spinningDuration: 2,
|
||||
blur: true,
|
||||
bounce: true
|
||||
};
|
||||
|
||||
const meta: Meta<typeof ReelsSpinSystem> = {
|
||||
title: 'Systems/WinSymbolSystem', // Путь для отображения в Storybook
|
||||
tags: ['autodocs'], // Включает автоматическую генерацию документации
|
||||
parameters: {
|
||||
docs: {
|
||||
story: {
|
||||
height: 600
|
||||
},
|
||||
description: {
|
||||
component: 'Animation of win symbols and winlines, all and individual loop'
|
||||
},
|
||||
}
|
||||
},
|
||||
argTypes: argTypes(args),
|
||||
args: getDefaultArgs(args),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
|
||||
const rawLines = [
|
||||
[0, 1, 0, 1, 0],
|
||||
[0, 1, 1, 1, 0]
|
||||
]
|
||||
const lines = {
|
||||
get: (index: number) => {
|
||||
return rawLines[index]
|
||||
},
|
||||
numberOfLines: () => 2,
|
||||
lines: rawLines
|
||||
}
|
||||
|
||||
export const WinSymbolStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
context, init: async (view) =>
|
||||
{
|
||||
Assets.reset();
|
||||
await Assets.init({
|
||||
manifest: 'area69/manifest.json',
|
||||
});
|
||||
await Assets.loadBundle('configs');
|
||||
await Assets.loadBundle('symbols');
|
||||
const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
const machine = new ReelsMachine({...config, lines: lines.lines});
|
||||
machine.systems.spin.setSettings(_params);
|
||||
const container = new MachineContainer(machine);
|
||||
machine.systems.blockExitState = new MockSystem(machine)
|
||||
view.addChild(container);
|
||||
context.machine = machine;
|
||||
machine.inputs.initMap = createScreenInputMap(config);
|
||||
machine.inputs.screenMap = createScreenInputMap(config);
|
||||
machine.inputs.wins = {
|
||||
totalWin: 200,
|
||||
totalWinString: '2.00 USD',
|
||||
wins: [
|
||||
{
|
||||
type: 'line',
|
||||
amount: 100,
|
||||
amountString: '1.00 USD',
|
||||
lineSlotsIndexMap: [0, 1, 0],
|
||||
lineIndex: 0
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
amount: 100,
|
||||
amountString: '1.00 USD',
|
||||
lineSlotsIndexMap: [0, 1, 1],
|
||||
lineIndex: 1
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
console.log(machine)
|
||||
console.log(machine.inputs)
|
||||
machine.inputs.spin = true;
|
||||
centerView(view);
|
||||
|
||||
},
|
||||
resize: centerView,
|
||||
update: () =>
|
||||
{
|
||||
context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
}
|
||||
});
|
||||
|
||||
// export const WinStickSymbolStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
// context, init: async (view) =>
|
||||
// {
|
||||
// Assets.reset();
|
||||
// await Assets.init({
|
||||
// manifest: 'area69/manifest.json',
|
||||
// });
|
||||
// await Assets.loadBundle('configs');
|
||||
// await Assets.loadBundle('symbols');
|
||||
// const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
// const machine = new ReelsMachine({...config, lines: [
|
||||
// [0, 1, 0, 1, 0],
|
||||
// [0, 1, 1, 1, 0]
|
||||
// ] });
|
||||
// const container = new MachineContainer(machine);
|
||||
//
|
||||
//
|
||||
// view.addChild(container);
|
||||
// context.machine = machine;
|
||||
// machine.inputs.initMap = createScreenInputMap(config);
|
||||
// machine.inputs.screenMap = createScreenInputMap(config);
|
||||
// machine.inputs.wins = [
|
||||
// {
|
||||
// type: 'line',
|
||||
// amount: 100,
|
||||
// amountString: '1.00 USD',
|
||||
// lineSlotsIndexMap: [0, 1, 0],
|
||||
// lineIndex: 0
|
||||
// },
|
||||
// {
|
||||
// type: 'line',
|
||||
// amount: 100,
|
||||
// amountString: '1.00 USD',
|
||||
// lineSlotsIndexMap: [0, 1, 1],
|
||||
// lineIndex: 1
|
||||
// }
|
||||
// ]
|
||||
// machine.stateMachine.states[STATES.SPINNING].systems = ['spin', 'layering', 'stick'];
|
||||
// machine.systems.stick = new StickySymbolsSystem(machine);
|
||||
// machine.systems.stick.setSettings(_params);
|
||||
// machine.systems.spin.setSettings(_params);
|
||||
// // machine.systems.allWinSymbols.setSettings(winSymbolSettings);
|
||||
// // machine.systems.winningLinesLoop.setSettings(winSymbolSettings);
|
||||
//
|
||||
// machine.inputs.blockExitState = true;
|
||||
// setTimeout(() => {
|
||||
// machine.inputs.blockExitState = false;
|
||||
// }, 250);
|
||||
//
|
||||
// centerView(view);
|
||||
// },
|
||||
// resize: centerView,
|
||||
// update: () =>
|
||||
// {
|
||||
// context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// export const WinStickExpandingSymbolStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
|
||||
// context, init: async (view) =>
|
||||
// {
|
||||
// Assets.reset();
|
||||
// await Assets.init({
|
||||
// manifest: 'area69/manifest.json',
|
||||
// });
|
||||
// await Assets.loadBundle('configs');
|
||||
// await Assets.loadBundle('symbols');
|
||||
// const config: SlotMachineConfig = Assets.get('slot_machine_config');
|
||||
// const machine = new ReelsMachine(config);
|
||||
// const container = new MachineContainer(machine);
|
||||
// const winSymbolSettings = {
|
||||
// lines: [[0, 1, 0, 1, 0], [3, 2, 3, 2, 3]],
|
||||
// hideWinline: false,
|
||||
// winlineAnimationClass: WinLine,
|
||||
// basicWinClass: BasicWin,
|
||||
// };
|
||||
// const spin = () =>
|
||||
// {
|
||||
// machine.inputs.spin = true;
|
||||
// setTimeout(() => { machine.inputs.spin = false; }, 1000);
|
||||
// setTimeout(spin, 10000);
|
||||
// };
|
||||
//
|
||||
// view.addChild(container);
|
||||
// context.machine = machine;
|
||||
// machine.inputs.initMap = createScreenInputMap(config);
|
||||
// machine.inputs.screenMap = createScreenInputMap(config);
|
||||
// machine.inputs.wins = [['line', 100, [0, 1, 0], 0], ['line', 100, [3, 2, 3], 1]];
|
||||
// machine.inputs.celebrateSymbolIndexes = [0, 1, 5];
|
||||
// machine.stateMachine.states[STATES.SPINNING].systems = ['spin', 'layering', 'stick', 'expanding'];
|
||||
// machine.systems.expanding = new ExpandedSymbolsSpinSystem(machine);
|
||||
// machine.systems.stick = new StickySymbolsSystem(machine);
|
||||
// machine.systems.expanding.setSettings(_params);
|
||||
// machine.systems.stick.setSettings(_params);
|
||||
// machine.systems.spin.setSettings(_params);
|
||||
// machine.systems.allWinSymbols.setSettings(winSymbolSettings);
|
||||
// machine.systems.winningLinesLoop.setSettings(winSymbolSettings);
|
||||
// machine.inputs.blockExitState = true;
|
||||
// setTimeout(() => {
|
||||
// machine.inputs.blockExitState = false;
|
||||
// }, 250);
|
||||
// centerView(view);
|
||||
// spin();
|
||||
// },
|
||||
// resize: centerView,
|
||||
// update: () =>
|
||||
// {
|
||||
// context.machine && context.machine.update(Ticker.shared.deltaMS);
|
||||
// }
|
||||
// });
|
||||
19
src/stories/assets/area69/anticipation/anticipation.atlas
Normal file
@@ -0,0 +1,19 @@
|
||||
anticipation.webp
|
||||
size:2048,1024
|
||||
filter:Linear,Linear
|
||||
scale:0.75
|
||||
anticip_bg
|
||||
bounds:777,30,220,836
|
||||
offsets:319,27,858,879
|
||||
anticip_circle_whole_source_1.4
|
||||
bounds:999,463,404,403
|
||||
offsets:429,445,858,879
|
||||
anticip_frame
|
||||
bounds:291,27,262,839
|
||||
offsets:297,20,858,879
|
||||
anticip_frame_glow
|
||||
bounds:2,2,287,864
|
||||
offsets:285,7,858,879
|
||||
anticip_glow
|
||||
bounds:555,29,220,837
|
||||
offsets:319,27,858,879
|
||||
BIN
src/stories/assets/area69/anticipation/anticipation.webp
Normal file
|
After Width: | Height: | Size: 209 KiB |
BIN
src/stories/assets/area69/anticipation/anticipation_back.skel
Normal file
BIN
src/stories/assets/area69/anticipation/anticipation_front.skel
Normal file
@@ -0,0 +1,16 @@
|
||||
info face="multiplier_value_font" size=90 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=1,1
|
||||
common lineHeight=104 base=83 scaleW=218 scaleH=218 pages=1 packed=0
|
||||
page id=0 file="multiplier_value_font.png"
|
||||
chars count=12
|
||||
char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=0 xadvance=26 page=0 chnl=15
|
||||
char id=48 x=167 y=0 width=51 height=71 xoffset=-1 yoffset=-1 xadvance=49 page=0 chnl=15
|
||||
char id=49 x=0 y=74 width=42 height=72 xoffset=-1 yoffset=-1 xadvance=41 page=0 chnl=15
|
||||
char id=50 x=0 y=147 width=52 height=71 xoffset=-1 yoffset=-1 xadvance=50 page=0 chnl=15
|
||||
char id=51 x=53 y=147 width=42 height=71 xoffset=0 yoffset=0 xadvance=41 page=0 chnl=15
|
||||
char id=52 x=143 y=147 width=54 height=69 xoffset=-1 yoffset=-1 xadvance=52 page=0 chnl=15
|
||||
char id=53 x=96 y=147 width=46 height=71 xoffset=-1 yoffset=-1 xadvance=44 page=0 chnl=15
|
||||
char id=54 x=0 y=0 width=56 height=73 xoffset=-1 yoffset=-1 xadvance=54 page=0 chnl=15
|
||||
char id=55 x=43 y=74 width=50 height=69 xoffset=-1 yoffset=-1 xadvance=48 page=0 chnl=15
|
||||
char id=56 x=57 y=0 width=52 height=73 xoffset=-1 yoffset=-1 xadvance=50 page=0 chnl=15
|
||||
char id=57 x=110 y=0 width=56 height=73 xoffset=-1 yoffset=-1 xadvance=54 page=0 chnl=15
|
||||
char id=88 x=94 y=74 width=55 height=63 xoffset=-1 yoffset=8 xadvance=54 page=0 chnl=15
|
||||
BIN
src/stories/assets/area69/bitmapFonts/multiplier_value_font.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
@@ -0,0 +1,16 @@
|
||||
info face="wild_multiplier_font" size=90 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=1,1
|
||||
common lineHeight=104 base=83 scaleW=209 scaleH=218 pages=1 packed=0
|
||||
page id=0 file="wild_multiplier_font.png"
|
||||
chars count=12
|
||||
char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=0 xadvance=26 page=0 chnl=15
|
||||
char id=48 x=0 y=0 width=53 height=73 xoffset=-1 yoffset=-1 xadvance=51 page=0 chnl=15
|
||||
char id=49 x=166 y=0 width=43 height=72 xoffset=-1 yoffset=-1 xadvance=41 page=0 chnl=15
|
||||
char id=50 x=150 y=74 width=53 height=71 xoffset=-1 yoffset=-1 xadvance=51 page=0 chnl=15
|
||||
char id=51 x=58 y=74 width=43 height=72 xoffset=-1 yoffset=-1 xadvance=41 page=0 chnl=15
|
||||
char id=52 x=0 y=148 width=55 height=70 xoffset=-1 yoffset=-1 xadvance=53 page=0 chnl=15
|
||||
char id=53 x=102 y=74 width=47 height=72 xoffset=-1 yoffset=-1 xadvance=45 page=0 chnl=15
|
||||
char id=54 x=54 y=0 width=57 height=73 xoffset=-1 yoffset=-1 xadvance=55 page=0 chnl=15
|
||||
char id=55 x=56 y=148 width=51 height=70 xoffset=-1 yoffset=-1 xadvance=49 page=0 chnl=15
|
||||
char id=56 x=112 y=0 width=53 height=73 xoffset=-1 yoffset=-1 xadvance=51 page=0 chnl=15
|
||||
char id=57 x=0 y=74 width=57 height=73 xoffset=-1 yoffset=-1 xadvance=55 page=0 chnl=15
|
||||
char id=88 x=108 y=148 width=56 height=64 xoffset=-1 yoffset=8 xadvance=55 page=0 chnl=15
|
||||
BIN
src/stories/assets/area69/bitmapFonts/wild_multiplier_font.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
111
src/stories/assets/area69/configs/slotMachine.json
Normal file
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"width": 550,
|
||||
"height": 420,
|
||||
"reels": 5,
|
||||
"slots": 4,
|
||||
"paddings": {
|
||||
"left": 16,
|
||||
"top": 0,
|
||||
"right": 18,
|
||||
"bottom": 0
|
||||
},
|
||||
"symbolsKeys": ["L", "J", "I", "H", "G", "F", "E", "D", "C", "B", "A", "K", "M", "N"],
|
||||
"symbols": {
|
||||
"A": {
|
||||
"key": "A",
|
||||
"spine": "lowest_symbols",
|
||||
"skin": "Ten",
|
||||
"zIndex": 1
|
||||
},
|
||||
"B": {
|
||||
"key": "B",
|
||||
"spine": "lowest_symbols",
|
||||
"skin": "J",
|
||||
"zIndex": 1
|
||||
},
|
||||
"C": {
|
||||
"key": "C",
|
||||
"spine": "lowest_symbols",
|
||||
"skin": "Q",
|
||||
"zIndex": 1
|
||||
},
|
||||
"D": {
|
||||
"key": "D",
|
||||
"spine": "low_symbols",
|
||||
"skin": "K",
|
||||
"zIndex": 1
|
||||
},
|
||||
"E": {
|
||||
"key": "E",
|
||||
"spine": "low_symbols",
|
||||
"skin": "A",
|
||||
"zIndex": 1
|
||||
},
|
||||
"F": {
|
||||
"key": "F",
|
||||
"spine": "h5",
|
||||
"skin": "default",
|
||||
"zIndex": 1
|
||||
},
|
||||
"G": {
|
||||
"key": "G",
|
||||
"spine": "h4",
|
||||
"skin": "default",
|
||||
"zIndex": 1
|
||||
},
|
||||
"H": {
|
||||
"key": "H",
|
||||
"spine": "h3",
|
||||
"skin": "default",
|
||||
"zIndex": 1
|
||||
},
|
||||
"I": {
|
||||
"key": "I",
|
||||
"spine": "h2",
|
||||
"skin": "default",
|
||||
"zIndex": 1
|
||||
},
|
||||
"J": {
|
||||
"key": "J",
|
||||
"spine": "h1",
|
||||
"skin": "default",
|
||||
"zIndex": 1
|
||||
},
|
||||
"K": {
|
||||
"key": "K",
|
||||
"spine": "scatter",
|
||||
"skin": "default",
|
||||
"zIndex": 2
|
||||
},
|
||||
"L": {
|
||||
"key": "L",
|
||||
"spine": "wild",
|
||||
"skin": "low",
|
||||
"fontStyle": {
|
||||
"fontSize": 30,
|
||||
"fontName": "wild_multiplier_font"
|
||||
},
|
||||
"zIndex": 3
|
||||
},
|
||||
"M": {
|
||||
"key": "M",
|
||||
"spine": "wild",
|
||||
"skin": "med",
|
||||
"fontStyle": {
|
||||
"fontSize": 30,
|
||||
"fontName": "wild_multiplier_font"
|
||||
},
|
||||
"zIndex": 3
|
||||
},
|
||||
"N": {
|
||||
"key": "N",
|
||||
"spine": "wild",
|
||||
"skin": "high",
|
||||
"fontStyle": {
|
||||
"fontSize": 30,
|
||||
"fontName": "wild_multiplier_font"
|
||||
},
|
||||
"zIndex": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
146
src/stories/assets/area69/manifest.json
Normal file
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"bundles": [
|
||||
{
|
||||
"name": "configs",
|
||||
"assets": [
|
||||
{
|
||||
"name": "slot_machine_config",
|
||||
"srcs": "area69/configs/slotMachine.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"symbols",
|
||||
"assets":[
|
||||
{
|
||||
"name": "wild_multiplier_font",
|
||||
"srcs": "area69/bitmapFonts/wild_multiplier_font.fnt"
|
||||
},
|
||||
{
|
||||
"name": "symbols_atlas",
|
||||
"srcs": "area69/symbols/symbols.atlas"
|
||||
},
|
||||
{
|
||||
"name": "wild",
|
||||
"srcs": "area69/symbols/wild.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.35,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "area69/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "scatter",
|
||||
"srcs": "area69/symbols/scatter.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.5,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "area69/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "lowest_symbols",
|
||||
"srcs": "area69/symbols/L_10_q_j.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.35,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "area69/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "low_symbols",
|
||||
"srcs": "area69/symbols/L_A_K.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.35,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "area69/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "h5",
|
||||
"srcs": "area69/symbols/M3.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.45,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "area69/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "h4",
|
||||
"srcs": "area69/symbols/M2.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.4,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "area69/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "h3",
|
||||
"srcs": "area69/symbols/M1.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.35,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "area69/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "h2",
|
||||
"srcs": "area69/symbols/H2.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.5,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "area69/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "h1",
|
||||
"srcs": "area69/symbols/H1.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.5,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "area69/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "expanding",
|
||||
"srcs": "area69/symbols/expanding_multiplier.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.5,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "area69/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "win_line",
|
||||
"srcs": "area69/winline/win_line.skel",
|
||||
"data": {
|
||||
"spineAtlasAlias": "main_atlas",
|
||||
"spineAtlasFile": "area69/winline/assembly.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "win_frame",
|
||||
"srcs": "area69/textures/win_frame.png"
|
||||
},
|
||||
{
|
||||
"name": "anticipation_back",
|
||||
"srcs": "area69/anticipation/anticipation_back.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.4,
|
||||
"spineAtlasAlias": "anticipation",
|
||||
"spineAtlasFile": "area69/anticipation/anticipation.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "anticipation_front",
|
||||
"srcs": "area69/anticipation/anticipation_front.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.4,
|
||||
"spineAtlasAlias": "anticipation",
|
||||
"spineAtlasFile": "area69/anticipation/anticipation.atlas"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
src/stories/assets/area69/symbols/H1.skel
Normal file
BIN
src/stories/assets/area69/symbols/H2.skel
Normal file
BIN
src/stories/assets/area69/symbols/L_10_q_j.skel
Normal file
BIN
src/stories/assets/area69/symbols/L_A_K.skel
Normal file
BIN
src/stories/assets/area69/symbols/M1.skel
Normal file
BIN
src/stories/assets/area69/symbols/M2.skel
Normal file
BIN
src/stories/assets/area69/symbols/M3.skel
Normal file
BIN
src/stories/assets/area69/symbols/expanding_multiplier.skel
Normal file
BIN
src/stories/assets/area69/symbols/scatter.skel
Normal file
526
src/stories/assets/area69/symbols/symbols.atlas
Normal file
@@ -0,0 +1,526 @@
|
||||
symbols.webp
|
||||
size:3236,2056
|
||||
filter:Linear,Linear
|
||||
103_S
|
||||
bounds:223,1060,258,241
|
||||
rotate:90
|
||||
10_newspaper1_l
|
||||
bounds:2296,893,157,220
|
||||
10_newspaper2_r
|
||||
bounds:1876,1144,149,211
|
||||
rotate:90
|
||||
2(Effect)
|
||||
bounds:2229,307,214,208
|
||||
rotate:90
|
||||
3(Effect)
|
||||
bounds:2202,523,214,209
|
||||
A3_S
|
||||
bounds:2400,1116,295,306
|
||||
A_newspaper1_l
|
||||
bounds:1091,1110,188,284
|
||||
rotate:90
|
||||
A_newspaper2_r
|
||||
bounds:3011,1453,187,280
|
||||
DiscoBall_1_1
|
||||
bounds:241,107,307,309
|
||||
DiscoBall_1_2
|
||||
bounds:550,107,307,309
|
||||
DiscoBall_1_3
|
||||
bounds:867,789,307,309
|
||||
DiscoBall_1_4
|
||||
bounds:1153,1300,307,309
|
||||
rotate:90
|
||||
DiscoBall_1_5
|
||||
bounds:1176,799,307,309
|
||||
DiscoBall_1_6
|
||||
bounds:1464,1300,307,309
|
||||
rotate:90
|
||||
DiscoBall_1_sequins
|
||||
bounds:843,1736,348,318
|
||||
DiscoBall_2_1
|
||||
bounds:1775,1424,307,309
|
||||
DiscoBall_2_2
|
||||
bounds:2084,1424,307,309
|
||||
DiscoBall_2_3
|
||||
bounds:2393,1424,307,309
|
||||
DiscoBall_2_4
|
||||
bounds:2702,1424,307,309
|
||||
DiscoBall_2_5
|
||||
bounds:1485,799,307,309
|
||||
DiscoBall_2_6
|
||||
bounds:2089,1115,307,309
|
||||
rotate:90
|
||||
DiscoBall_2_sequins
|
||||
bounds:1193,1736,348,318
|
||||
DiscoBall_3_1
|
||||
bounds:864,364,296,297
|
||||
rotate:90
|
||||
DiscoBall_3_2
|
||||
bounds:859,66,296,297
|
||||
rotate:90
|
||||
DiscoBall_3_3
|
||||
bounds:1179,501,296,297
|
||||
rotate:90
|
||||
DiscoBall_3_4
|
||||
bounds:1478,501,296,297
|
||||
rotate:90
|
||||
DiscoBall_3_5
|
||||
bounds:1163,203,296,297
|
||||
rotate:90
|
||||
DiscoBall_3_6
|
||||
bounds:1462,203,296,297
|
||||
rotate:90
|
||||
DiscoBall_3_sequins
|
||||
bounds:489,1381,349,319
|
||||
Electricity_m2_00
|
||||
bounds:1543,1735,328,319
|
||||
Electricity_m2_01
|
||||
bounds:1873,1735,328,319
|
||||
Electricity_m2_03
|
||||
bounds:2203,1735,328,319
|
||||
Electricity_m2_04
|
||||
bounds:2533,1735,328,319
|
||||
Electricity_m2_06
|
||||
bounds:2863,1735,328,319
|
||||
Electricity_m2_07
|
||||
bounds:489,1060,328,319
|
||||
Electricity_m2_09
|
||||
bounds:216,739,328,319
|
||||
Electricity_m2_10
|
||||
bounds:216,418,328,319
|
||||
Electricity_m2_12
|
||||
bounds:546,730,328,319
|
||||
rotate:90
|
||||
H1_BG
|
||||
bounds:840,1309,300,311
|
||||
rotate:90
|
||||
H1_button
|
||||
bounds:830,93,12,27
|
||||
rotate:90
|
||||
H1_cigar
|
||||
bounds:1724,1110,53,32
|
||||
H1_cigar _S
|
||||
bounds:2694,356,51,32
|
||||
rotate:90
|
||||
H1_collar_3
|
||||
bounds:3028,342,71,58
|
||||
H1_collar_L_2
|
||||
bounds:2866,284,46,87
|
||||
rotate:90
|
||||
H1_collar_R_1
|
||||
bounds:2775,564,55,117
|
||||
rotate:90
|
||||
H1_eyes
|
||||
bounds:2049,8,109,37
|
||||
H1_finger_1
|
||||
bounds:2993,135,73,38
|
||||
H1_finger_2
|
||||
bounds:45,7,61,29
|
||||
H1_finger_3
|
||||
bounds:3099,422,79,87
|
||||
H1_hand
|
||||
bounds:2855,445,54,117
|
||||
H1_head
|
||||
bounds:2224,2,152,182
|
||||
rotate:90
|
||||
H1_horns
|
||||
bounds:2694,409,153,67
|
||||
rotate:90
|
||||
H1_neck
|
||||
bounds:2442,189,189,141
|
||||
H1_shirt
|
||||
bounds:1750,34,144,100
|
||||
rotate:90
|
||||
H1_sleeve_L_2
|
||||
bounds:3055,2,49,63
|
||||
H1_sleeve_R_1
|
||||
bounds:3100,287,50,72
|
||||
rotate:90
|
||||
H2_BG
|
||||
bounds:2697,1118,294,304
|
||||
H2_brows
|
||||
bounds:241,2,91,51
|
||||
H2_collar _2
|
||||
bounds:1928,2,38,119
|
||||
rotate:90
|
||||
H2_collar_top_1
|
||||
bounds:2894,581,108,120
|
||||
H2_curler
|
||||
bounds:3104,232,67,53
|
||||
H2_ear
|
||||
bounds:2408,2,138,152
|
||||
H2_earring_L_1
|
||||
bounds:2,2,34,41
|
||||
rotate:90
|
||||
H2_earring_R_2
|
||||
bounds:1610,1609,37,41
|
||||
H2_eyelid_L_2
|
||||
bounds:1182,2,36,25
|
||||
H2_eyelid_L_top_1
|
||||
bounds:1906,6,28,20
|
||||
rotate:90
|
||||
H2_eyelid_R_4
|
||||
bounds:3007,479,21,19
|
||||
offsets:2,0,23,19
|
||||
rotate:90
|
||||
H2_eyelid_R_top_3
|
||||
bounds:466,1293,25,21
|
||||
offsets:0,1,25,22
|
||||
rotate:90
|
||||
H2_glasses_L_1
|
||||
bounds:2993,175,78,55
|
||||
H2_glasses_R_3
|
||||
bounds:3180,372,54,57
|
||||
H2_hair_L_1
|
||||
bounds:2633,215,139,137
|
||||
rotate:90
|
||||
H2_hair_L_S_2
|
||||
bounds:2990,72,61,63
|
||||
offsets:2,0,66,64
|
||||
rotate:90
|
||||
H2_hair_R_2
|
||||
bounds:2772,215,84,89
|
||||
rotate:90
|
||||
H2_hand_L_1
|
||||
bounds:3007,502,82,117
|
||||
H2_hand_R_2
|
||||
bounds:2911,467,94,112
|
||||
H2_hat_1
|
||||
bounds:2620,564,153,158
|
||||
H2_hat_S_2
|
||||
bounds:2763,429,133,90
|
||||
rotate:90
|
||||
H2_head_S
|
||||
bounds:3028,402,98,69
|
||||
rotate:90
|
||||
H2_mouth_3
|
||||
bounds:3004,2,29,24
|
||||
H2_mouth_bottom_2
|
||||
bounds:2479,156,31,23
|
||||
rotate:90
|
||||
H2_mouth_top_1
|
||||
bounds:1220,2,35,25
|
||||
H2_neck_1
|
||||
bounds:2675,2,71,112
|
||||
rotate:90
|
||||
H2_nose
|
||||
bounds:2160,5,40,55
|
||||
rotate:90
|
||||
H2_protein_L_2
|
||||
bounds:3091,592,27,23
|
||||
rotate:90
|
||||
H2_protein_R_4
|
||||
bounds:2708,724,24,20
|
||||
H2_pupil_L_1
|
||||
bounds:1163,644,14,16
|
||||
H2_pupil_R_3
|
||||
bounds:2941,885,13,15
|
||||
rotate:90
|
||||
H2_robe
|
||||
bounds:571,5,257,100
|
||||
H2_robe_S
|
||||
bounds:3173,207,76,61
|
||||
rotate:90
|
||||
H2_sleeve_L_1
|
||||
bounds:1473,1609,68,41
|
||||
H2_sleeve_R_2
|
||||
bounds:3101,339,81,60
|
||||
rotate:90
|
||||
J_newspaper1_l
|
||||
bounds:2226,734,157,220
|
||||
rotate:90
|
||||
J_newspaper2_r
|
||||
bounds:1663,1144,149,211
|
||||
rotate:90
|
||||
K_newspaper1_l
|
||||
bounds:1377,1110,188,284
|
||||
rotate:90
|
||||
K_newspaper2_r
|
||||
bounds:1468,14,187,280
|
||||
rotate:90
|
||||
M1_BG
|
||||
bounds:1996,47,219,226
|
||||
rotate:90
|
||||
M1_backpack
|
||||
bounds:2796,88,78,125
|
||||
M1_backpackHandle
|
||||
bounds:2772,301,92,126
|
||||
M1_body
|
||||
bounds:2675,75,138,119
|
||||
rotate:90
|
||||
M1_effect_00604
|
||||
bounds:2993,1191,241,231
|
||||
M1_effect_00605
|
||||
bounds:2993,958,241,231
|
||||
M1_effect_00606
|
||||
bounds:1794,911,241,231
|
||||
M1_effect_00607
|
||||
bounds:2455,883,241,231
|
||||
M1_effect_00608
|
||||
bounds:2698,885,241,231
|
||||
M1_effect_00609
|
||||
bounds:2993,725,241,231
|
||||
M1_effect_00610
|
||||
bounds:1959,511,241,231
|
||||
M1_effect_00611
|
||||
bounds:1996,268,241,231
|
||||
rotate:90
|
||||
M1_hand_L_1
|
||||
bounds:3116,655,68,118
|
||||
rotate:90
|
||||
M1_hand_L_2
|
||||
bounds:3180,431,54,78
|
||||
M1_hand_L_3
|
||||
bounds:475,2,51,65
|
||||
rotate:90
|
||||
M1_hand_L_S
|
||||
bounds:2894,703,69,97
|
||||
rotate:90
|
||||
M1_hand_R_1
|
||||
bounds:3005,232,53,97
|
||||
rotate:90
|
||||
M1_hand_R_2
|
||||
bounds:2876,122,55,101
|
||||
offsets:0,0,55,103
|
||||
M1_hand_R_3
|
||||
bounds:3174,285,85,60
|
||||
rotate:90
|
||||
M1_head
|
||||
bounds:2548,26,125,161
|
||||
offsets:0,0,126,161
|
||||
M1_visor
|
||||
bounds:2944,359,82,106
|
||||
offsets:0,2,82,108
|
||||
M2_detail_1
|
||||
bounds:3155,107,44,48
|
||||
M2_detail_2
|
||||
bounds:2884,774,107,109
|
||||
M2_detail_3
|
||||
bounds:2037,905,208,257
|
||||
rotate:90
|
||||
M3_can
|
||||
bounds:819,1100,207,270
|
||||
offsets:0,0,215,270
|
||||
rotate:90
|
||||
M3_handle
|
||||
bounds:2948,27,50,37
|
||||
rotate:90
|
||||
M3_pict
|
||||
bounds:2775,621,123,117
|
||||
rotate:90
|
||||
M3_pict_rays
|
||||
bounds:2439,332,180,203
|
||||
Q_newspaper1_l
|
||||
bounds:2448,724,157,220
|
||||
rotate:90
|
||||
Q_newspaper2_r
|
||||
bounds:2229,156,149,211
|
||||
rotate:90
|
||||
X2
|
||||
bounds:2866,332,111,76
|
||||
offsets:0,1,111,77
|
||||
rotate:90
|
||||
X200
|
||||
bounds:2621,356,206,71
|
||||
rotate:90
|
||||
bg
|
||||
bounds:2,369,212,838
|
||||
chick_S
|
||||
bounds:334,7,139,46
|
||||
chick_beak
|
||||
bounds:216,380,36,23
|
||||
rotate:90
|
||||
chick_beard
|
||||
bounds:2911,445,20,30
|
||||
rotate:90
|
||||
chick_body
|
||||
bounds:2789,2,93,71
|
||||
chick_brows
|
||||
bounds:3127,1433,44,18
|
||||
chick_eyelids
|
||||
bounds:3163,382,38,15
|
||||
rotate:90
|
||||
chick_eyes
|
||||
bounds:2504,158,37,29
|
||||
chick_hair
|
||||
bounds:3073,177,66,53
|
||||
offsets:0,1,66,54
|
||||
chick_leg_l
|
||||
bounds:3204,517,28,30
|
||||
rotate:90
|
||||
chick_leg_r
|
||||
bounds:2670,724,36,20
|
||||
chick_neck
|
||||
bounds:2933,138,58,85
|
||||
chick_puppil
|
||||
bounds:3141,180,28,12
|
||||
chick_tail
|
||||
bounds:1425,9,39,73
|
||||
offsets:0,0,39,74
|
||||
chick_wing_l
|
||||
bounds:3106,72,47,61
|
||||
chick_wing_r
|
||||
bounds:3106,17,49,53
|
||||
cow_S
|
||||
bounds:1158,29,218,53
|
||||
cow_bell
|
||||
bounds:3141,194,28,36
|
||||
cow_body
|
||||
bounds:2670,746,135,134
|
||||
offsets:0,0,140,134
|
||||
rotate:90
|
||||
cow_collar
|
||||
bounds:1543,1609,65,41
|
||||
cow_ear_l
|
||||
bounds:3204,547,43,30
|
||||
rotate:90
|
||||
cow_ear_r
|
||||
bounds:1852,2,52,32
|
||||
cow_eye
|
||||
bounds:164,2,34,18
|
||||
rotate:90
|
||||
cow_eyelids
|
||||
bounds:1143,2,37,25
|
||||
cow_foot_back_l
|
||||
bounds:2863,225,57,90
|
||||
offsets:0,1,57,91
|
||||
rotate:90
|
||||
cow_foot_back_r
|
||||
bounds:2948,2,23,54
|
||||
rotate:90
|
||||
cow_foot_front_l
|
||||
bounds:2955,253,48,104
|
||||
cow_foot_front_r
|
||||
bounds:2728,357,35,50
|
||||
cow_hair
|
||||
bounds:2037,1115,42,27
|
||||
cow_hay
|
||||
bounds:3011,1428,114,23
|
||||
cow_head
|
||||
bounds:1852,36,74,139
|
||||
cow_mouth
|
||||
bounds:2876,76,62,44
|
||||
cow_mouth_1
|
||||
bounds:2633,189,37,24
|
||||
cow_pupil
|
||||
bounds:3225,179,26,9
|
||||
rotate:90
|
||||
cow_tail
|
||||
bounds:2940,79,57,48
|
||||
rotate:90
|
||||
cow_udder
|
||||
bounds:3055,67,66,49
|
||||
rotate:90
|
||||
effect_shine
|
||||
bounds:489,1702,352,352
|
||||
eyelid_l_top2
|
||||
bounds:1959,488,35,21
|
||||
eyelid_r_top2
|
||||
bounds:819,1353,26,19
|
||||
rotate:90
|
||||
frame
|
||||
bounds:2,1209,219,845
|
||||
glow2
|
||||
bounds:2418,537,185,200
|
||||
rotate:90
|
||||
hand_l_2
|
||||
bounds:2806,756,76,127
|
||||
hand_r_2
|
||||
bounds:1928,42,57,130
|
||||
offsets:0,1,57,131
|
||||
m3_l
|
||||
bounds:1378,2,45,80
|
||||
m3_r
|
||||
bounds:3005,287,53,93
|
||||
rotate:90
|
||||
pig_S
|
||||
bounds:2941,900,216,50
|
||||
rotate:90
|
||||
pig_body
|
||||
bounds:1794,799,175,110
|
||||
pig_brows
|
||||
bounds:2548,6,45,18
|
||||
pig_ear_l
|
||||
bounds:3068,135,49,38
|
||||
pig_ear_r
|
||||
bounds:1663,1110,59,32
|
||||
pig_eyelids
|
||||
bounds:1649,1615,35,17
|
||||
rotate:90
|
||||
pig_eyes
|
||||
bounds:819,1063,45,35
|
||||
pig_head
|
||||
bounds:3004,621,110,102
|
||||
pig_leg_back_l
|
||||
bounds:2442,156,31,35
|
||||
rotate:90
|
||||
pig_leg_back_r
|
||||
bounds:3116,592,61,117
|
||||
rotate:90
|
||||
pig_leg_front_l
|
||||
bounds:2987,28,42,66
|
||||
rotate:90
|
||||
pig_leg_front_r
|
||||
bounds:2884,10,62,64
|
||||
pig_mouth
|
||||
bounds:542,3,50,26
|
||||
rotate:90
|
||||
pig_nose
|
||||
bounds:3157,60,44,45
|
||||
pig_puppil
|
||||
bounds:1750,16,31,16
|
||||
pig_ribbon
|
||||
bounds:3091,511,111,79
|
||||
pig_tail
|
||||
bounds:108,3,33,54
|
||||
rotate:90
|
||||
plate_bg
|
||||
bounds:546,418,310,316
|
||||
offsets:0,0,311,318
|
||||
rotate:90
|
||||
plate_plate
|
||||
bounds:2,38,329,237
|
||||
offsets:0,0,329,243
|
||||
rotate:90
|
||||
plate_txt
|
||||
bounds:1473,1652,198,81
|
||||
ray
|
||||
bounds:223,1320,264,734
|
||||
scroll
|
||||
bounds:241,55,328,50
|
||||
ship
|
||||
bounds:1971,744,253,159
|
||||
smoke
|
||||
bounds:1673,1630,95,103
|
||||
wild_ring_1
|
||||
bounds:867,662,310,125
|
||||
wild_ring_10
|
||||
bounds:1159,1609,312,125
|
||||
wild_ring_11
|
||||
bounds:843,1611,314,123
|
||||
wild_ring_12
|
||||
bounds:1775,1295,312,127
|
||||
wild_ring_2
|
||||
bounds:1777,489,308,97
|
||||
rotate:90
|
||||
wild_ring_3
|
||||
bounds:1864,177,309,76
|
||||
rotate:90
|
||||
wild_ring_4
|
||||
bounds:1942,174,312,52
|
||||
rotate:90
|
||||
wild_ring_5
|
||||
bounds:3193,1741,313,28
|
||||
rotate:90
|
||||
wild_ring_6
|
||||
bounds:830,7,311,57
|
||||
wild_ring_7
|
||||
bounds:1876,488,309,81
|
||||
rotate:90
|
||||
wild_ring_8
|
||||
bounds:1761,180,307,101
|
||||
rotate:90
|
||||
wild_ring_9
|
||||
bounds:1158,84,308,117
|
||||
wild_rope
|
||||
bounds:3171,157,52,48
|
||||
BIN
src/stories/assets/area69/symbols/symbols.webp
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
src/stories/assets/area69/symbols/wild.skel
Normal file
BIN
src/stories/assets/area69/textures/win_frame.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
279
src/stories/assets/area69/winline/assembly.atlas
Normal file
@@ -0,0 +1,279 @@
|
||||
assembly.webp
|
||||
size:2399,2390
|
||||
filter:Linear,Linear
|
||||
Abduction_15
|
||||
bounds:1404,1545,211,191
|
||||
offsets:1,2,214,195
|
||||
Abduction_ABDUCTION_txt
|
||||
bounds:2,543,587,237
|
||||
Abduction_ALIEN_txt
|
||||
bounds:716,1773,301,161
|
||||
Abduction_FREE SPINS_txt
|
||||
bounds:1399,1290,668,199
|
||||
Abduction_bg_Llight
|
||||
bounds:1439,577,247,417
|
||||
rotate:90
|
||||
Abduction_bg_Lplate
|
||||
bounds:281,1645,273,180
|
||||
Abduction_bg_Mlight
|
||||
bounds:1304,327,248,473
|
||||
rotate:90
|
||||
Abduction_bg_Mplate
|
||||
bounds:943,1157,335,203
|
||||
Abduction_bg_Rlight
|
||||
bounds:677,698,239,445
|
||||
rotate:90
|
||||
Abduction_bg_Rplate
|
||||
bounds:2,1677,277,167
|
||||
Abduction_chick
|
||||
bounds:1481,1917,151,205
|
||||
rotate:90
|
||||
Abduction_cow
|
||||
bounds:2,784,238,291
|
||||
rotate:90
|
||||
Abduction_pig
|
||||
bounds:2069,1196,234,230
|
||||
HANGOVER_txt
|
||||
bounds:1280,1078,513,210
|
||||
Hangover_DiscoBall_1_sequins
|
||||
bounds:2,1459,237,216
|
||||
Hangover_DiscoBall_2_sequins
|
||||
bounds:1795,1054,234,236
|
||||
rotate:90
|
||||
Hangover_DiscoBall_3_sequins
|
||||
bounds:2088,1428,239,219
|
||||
Hangover_X2
|
||||
bounds:458,2316,85,60
|
||||
Hangover_X20
|
||||
bounds:847,2300,124,61
|
||||
Hangover_X200
|
||||
bounds:304,2319,152,57
|
||||
Hangover_ball_1
|
||||
bounds:2,2,284,342
|
||||
rotate:90
|
||||
Hangover_ball_2
|
||||
bounds:2070,188,277,323
|
||||
rotate:90
|
||||
PRESS ANYWHERE TO CONTINUE
|
||||
bounds:719,2363,471,25
|
||||
bg_hangar
|
||||
bounds:1162,855,400,221
|
||||
offsets:1,2,402,224
|
||||
bg_paper_angle
|
||||
bounds:952,2084,250,115
|
||||
bg_paper_angle_S
|
||||
bounds:1054,2201,192,88
|
||||
bg_pict_S
|
||||
bounds:2103,1649,179,258
|
||||
rotate:90
|
||||
bg_sattelite
|
||||
bounds:774,939,386,216
|
||||
offsets:1,1,432,218
|
||||
bubble1
|
||||
bounds:1192,2363,25,25
|
||||
bubbles3
|
||||
bounds:1725,2344,44,44
|
||||
buildings_dark
|
||||
bounds:2,1236,939,160
|
||||
offsets:1,3,940,163
|
||||
carS_all
|
||||
bounds:584,2369,133,19
|
||||
car_all
|
||||
bounds:2,2143,297,104
|
||||
car_wheel_front1_all
|
||||
bounds:2,2323,53,53
|
||||
car_wheel_front2_all
|
||||
bounds:2,2323,53,53
|
||||
glow2
|
||||
bounds:241,1417,226,243
|
||||
rotate:90
|
||||
gold_star
|
||||
bounds:2285,2137,112,123
|
||||
hangar_alien_abd
|
||||
bounds:382,1019,390,215
|
||||
offsets:2,1,393,217
|
||||
left_leg_light
|
||||
bounds:1564,826,226,389
|
||||
offsets:22,29,256,421
|
||||
rotate:90
|
||||
logo
|
||||
bounds:437,2121,513,107
|
||||
max_win_MAX_txt_A
|
||||
bounds:1970,2021,133,180
|
||||
rotate:90
|
||||
max_win_MAX_txt_M
|
||||
bounds:1924,1860,159,180
|
||||
rotate:90
|
||||
max_win_MAX_txt_X
|
||||
bounds:2,1999,142,180
|
||||
rotate:90
|
||||
max_win_WIN_txt
|
||||
bounds:1325,2072,264,115
|
||||
max_win_plate_light
|
||||
bounds:1253,1756,180,173
|
||||
monitor_screen_hangover
|
||||
bounds:1877,1668,224,190
|
||||
offsets:4,6,233,199
|
||||
num_bg_all
|
||||
bounds:902,86,778,239
|
||||
particle
|
||||
bounds:871,1936,146,155
|
||||
rotate:90
|
||||
placeholder
|
||||
bounds:1932,2169,100,98
|
||||
offsets:0,2,100,100
|
||||
plate
|
||||
bounds:1858,467,350,247
|
||||
plate_S
|
||||
bounds:1248,2193,166,94
|
||||
progressbar_blue2
|
||||
bounds:973,2300,61,149
|
||||
offsets:1,2,64,152
|
||||
rotate:90
|
||||
progressbar_flask1_L
|
||||
bounds:2093,2339,49,135
|
||||
rotate:90
|
||||
progressbar_flask2_L
|
||||
bounds:2230,2339,49,124
|
||||
rotate:90
|
||||
progressbar_flask3_L
|
||||
bounds:1771,2340,48,135
|
||||
rotate:90
|
||||
progressbar_flasks1
|
||||
bounds:2236,2262,158,75
|
||||
progressbar_flasks2
|
||||
bounds:1666,1707,181,209
|
||||
rotate:90
|
||||
progressbar_green1
|
||||
bounds:696,2301,60,149
|
||||
offsets:2,1,63,152
|
||||
rotate:90
|
||||
progressbar_pink3
|
||||
bounds:545,2308,59,149
|
||||
offsets:1,1,61,152
|
||||
rotate:90
|
||||
progressbar_top
|
||||
bounds:445,2234,203,72
|
||||
progressbar_wall1
|
||||
bounds:1955,716,248,240
|
||||
progressbar_wall2
|
||||
bounds:1491,2272,71,218
|
||||
rotate:90
|
||||
progressbar_wall3
|
||||
bounds:1435,1738,177,229
|
||||
rotate:90
|
||||
progressbar_wire1
|
||||
bounds:184,1998,235,142
|
||||
progressbar_wire1_S
|
||||
bounds:2152,1991,235,144
|
||||
progressbar_wire2
|
||||
bounds:1173,1564,229,190
|
||||
progressbar_wire2_S
|
||||
bounds:1019,1765,232,164
|
||||
progressbar_wire3
|
||||
bounds:1124,2291,70,93
|
||||
rotate:90
|
||||
progressbar_wire4
|
||||
bounds:1393,2289,68,96
|
||||
offsets:1,0,69,96
|
||||
rotate:90
|
||||
rays2_all
|
||||
bounds:591,449,387,247
|
||||
red_star
|
||||
bounds:2034,2167,100,113
|
||||
rotate:90
|
||||
right_leg_light
|
||||
bounds:295,782,235,380
|
||||
offsets:5,13,281,401
|
||||
rotate:90
|
||||
sandL_all
|
||||
bounds:57,2322,245,54
|
||||
sandR_all
|
||||
bounds:1908,2340,183,48
|
||||
sattelite_dark
|
||||
bounds:2,1024,378,210
|
||||
offsets:0,0,419,210
|
||||
ship
|
||||
bounds:980,355,251,322
|
||||
offsets:1,2,254,326
|
||||
rotate:90
|
||||
shipS
|
||||
bounds:1028,1931,451,139
|
||||
offsets:19,35,491,176
|
||||
shipS_alien_abd
|
||||
bounds:421,1983,448,136
|
||||
offsets:16,52,478,190
|
||||
shipSand
|
||||
bounds:556,1595,615,168
|
||||
offsets:2,12,618,184
|
||||
shipSand_alien_abd
|
||||
bounds:1617,1491,469,175
|
||||
offsets:2,7,472,186
|
||||
ship_alien_abd
|
||||
bounds:1124,608,245,313
|
||||
offsets:1,2,248,316
|
||||
rotate:90
|
||||
ship_all
|
||||
bounds:1052,1362,345,200
|
||||
ship_ray_1
|
||||
bounds:1571,2345,152,43
|
||||
offsets:9,3,171,49
|
||||
ship_ray_2
|
||||
bounds:2033,958,282,236
|
||||
offsets:0,1,282,242
|
||||
ship_ray_3
|
||||
bounds:1219,2359,306,29
|
||||
offsets:11,3,321,34
|
||||
sign_danger_dark
|
||||
bounds:2149,2156,104,134
|
||||
rotate:90
|
||||
sign_danger_light
|
||||
bounds:301,2142,105,134
|
||||
rotate:90
|
||||
sign_warning_dark
|
||||
bounds:1688,1890,234,155
|
||||
offsets:0,0,234,161
|
||||
sign_warning_leg_d
|
||||
bounds:2,1846,150,351
|
||||
offsets:57,16,235,367
|
||||
rotate:90
|
||||
sign_warning_leg_l
|
||||
bounds:355,1827,154,359
|
||||
offsets:58,11,240,370
|
||||
rotate:90
|
||||
sign_warning_light
|
||||
bounds:2106,1830,240,159
|
||||
offsets:0,0,240,164
|
||||
sing_triangle_dark
|
||||
bounds:1204,2076,119,115
|
||||
sing_triangle_light
|
||||
bounds:1591,2070,121,117
|
||||
sing_yellow_dark
|
||||
bounds:1714,2068,119,126
|
||||
offsets:0,0,120,126
|
||||
rotate:90
|
||||
sing_yellow_light
|
||||
bounds:1842,2047,120,126
|
||||
rotate:90
|
||||
spark
|
||||
bounds:1527,2346,42,42
|
||||
text1_smw
|
||||
bounds:486,1398,564,195
|
||||
text2_smw
|
||||
bounds:2,288,448,253
|
||||
text_add
|
||||
bounds:1711,2269,523,68
|
||||
text_bw
|
||||
bounds:1779,212,289,253
|
||||
text_fs
|
||||
bounds:1416,2189,514,78
|
||||
text_fs3
|
||||
bounds:2,2249,441,68
|
||||
text_left
|
||||
bounds:1219,2291,172,66
|
||||
text_mw
|
||||
bounds:452,194,448,253
|
||||
text_yw
|
||||
bounds:650,2230,402,68
|
||||
win_line
|
||||
bounds:2,2378,580,10
|
||||
BIN
src/stories/assets/area69/winline/assembly.webp
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
src/stories/assets/area69/winline/win_line.skel
Normal file
73
src/stories/assets/bubblex/configs/slotMachine.json
Normal file
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"width": 317,
|
||||
"height": 320,
|
||||
"reels": 6,
|
||||
"slots": 6,
|
||||
"symbolsKeys": ["S1", "S2", "S3", "S4", "S5", "S6", "RR"],
|
||||
"wilds": [],
|
||||
"scatters": [],
|
||||
"symbols": {
|
||||
"S1": {
|
||||
"key": "S1",
|
||||
"spine": "symbols",
|
||||
"skin": "ORANGE"
|
||||
},
|
||||
"S2": {
|
||||
"key": "S2",
|
||||
"spine": "symbols",
|
||||
"skin": "YELLOW"
|
||||
},
|
||||
"S3": {
|
||||
"key": "S3",
|
||||
"spine": "symbols",
|
||||
"skin": "GREEN"
|
||||
},
|
||||
"S4": {
|
||||
"key": "S4",
|
||||
"spine": "symbols",
|
||||
"skin": "BLUE"
|
||||
},
|
||||
"S5": {
|
||||
"key": "S5",
|
||||
"spine": "symbols",
|
||||
"skin": "VIOLET"
|
||||
},
|
||||
"S6": {
|
||||
"key": "S6",
|
||||
"spine": "symbols",
|
||||
"skin": "ROSE"
|
||||
},
|
||||
"SC1": {
|
||||
"spine": "symbols",
|
||||
"skin": "WHITE_PAPER"
|
||||
},
|
||||
"W0": {
|
||||
"spine": "symbols",
|
||||
"skin": "COLOR_WILD"
|
||||
},
|
||||
"W1": {
|
||||
"spine": "symbols",
|
||||
"skin": "SQUARE_WILD"
|
||||
},
|
||||
"RR": {
|
||||
"key": "RR",
|
||||
"spine": "symbols",
|
||||
"skin": "RAINBOW"
|
||||
},
|
||||
"ADD": {
|
||||
"spine": "symbols",
|
||||
"skin": "X_PLUS"
|
||||
},
|
||||
"BL": {
|
||||
"spine": "symbols",
|
||||
"skin": "COLOR_WILD"
|
||||
},
|
||||
"MUL": {
|
||||
"spine": "symbols",
|
||||
"skin": "X_MULTIPLIER"
|
||||
},
|
||||
"DE": {
|
||||
"spine": "symbols"
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/stories/assets/bubblex/manifest.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"bundles": [
|
||||
{
|
||||
"name": "configs",
|
||||
"assets": [
|
||||
{
|
||||
"name": "slot_machine_config",
|
||||
"srcs": "bubblex/configs/slotMachine.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "symbols",
|
||||
"assets": [
|
||||
{
|
||||
"name": "symbols",
|
||||
"srcs": "bubblex/symbols/symbols.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.31
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
335
src/stories/assets/bubblex/symbols/symbols.atlas
Normal file
@@ -0,0 +1,335 @@
|
||||
symbols.webp
|
||||
size:2017,751
|
||||
filter:Linear,Linear
|
||||
Black_ball_1
|
||||
bounds:1292,147,65,66
|
||||
offsets:3,2,71,70
|
||||
Black_ball_2
|
||||
bounds:1119,27,83,84
|
||||
offsets:4,3,91,90
|
||||
Black_ball_3
|
||||
bounds:108,2,103,104
|
||||
offsets:3,2,109,108
|
||||
Black_ball_4
|
||||
bounds:1238,440,129,131
|
||||
offsets:2,2,135,138
|
||||
Black_base
|
||||
bounds:815,573,176,176
|
||||
offsets:1,1,178,178
|
||||
Blue_ball_1
|
||||
bounds:1359,147,65,66
|
||||
offsets:3,2,71,70
|
||||
Blue_ball_2
|
||||
bounds:859,11,85,85
|
||||
offsets:3,2,91,90
|
||||
Blue_ball_3
|
||||
bounds:213,2,103,104
|
||||
offsets:3,2,109,108
|
||||
Blue_ball_4
|
||||
bounds:695,436,132,135
|
||||
offsets:2,2,135,138
|
||||
Blue_base
|
||||
bounds:636,573,177,176
|
||||
offsets:1,1,179,178
|
||||
Green_ball_1
|
||||
bounds:1426,152,65,66
|
||||
offsets:3,2,71,70
|
||||
Green_ball_2
|
||||
bounds:946,11,85,84
|
||||
offsets:3,3,91,90
|
||||
rotate:90
|
||||
Green_ball_3
|
||||
bounds:439,2,103,104
|
||||
offsets:3,2,109,108
|
||||
Green_ball_4
|
||||
bounds:829,437,133,134
|
||||
offsets:2,2,136,137
|
||||
Green_base
|
||||
bounds:993,573,176,176
|
||||
offsets:1,1,178,178
|
||||
Grey_ball_1
|
||||
bounds:1493,154,65,66
|
||||
offsets:2,2,71,70
|
||||
Grey_ball_2
|
||||
bounds:1838,171,83,84
|
||||
offsets:3,3,91,90
|
||||
Grey_ball_3
|
||||
bounds:544,2,103,104
|
||||
offsets:2,2,109,108
|
||||
Grey_ball_4
|
||||
bounds:1369,440,129,131
|
||||
offsets:2,2,135,138
|
||||
Grey_base
|
||||
bounds:1171,573,176,176
|
||||
offsets:1,1,178,178
|
||||
Orange_ball_1
|
||||
bounds:1560,154,65,66
|
||||
offsets:3,2,71,70
|
||||
Orange_ball_2
|
||||
bounds:1032,27,85,84
|
||||
offsets:3,3,91,90
|
||||
Orange_ball_3
|
||||
bounds:649,2,103,104
|
||||
offsets:3,2,109,108
|
||||
Orange_ball_4
|
||||
bounds:381,237,149,153
|
||||
offsets:1,2,150,155
|
||||
Orange_base
|
||||
bounds:1699,579,169,170
|
||||
offsets:1,1,171,172
|
||||
Rose_ball_1
|
||||
bounds:1292,80,65,66
|
||||
offsets:3,2,71,70
|
||||
rotate:90
|
||||
Rose_ball_2
|
||||
bounds:1923,181,83,84
|
||||
offsets:4,3,91,90
|
||||
Rose_ball_3
|
||||
bounds:1762,473,103,104
|
||||
offsets:3,2,109,108
|
||||
Rose_ball_4
|
||||
bounds:1103,440,131,133
|
||||
offsets:2,2,134,136
|
||||
rotate:90
|
||||
Rose_base
|
||||
bounds:431,392,169,169
|
||||
offsets:1,1,171,172
|
||||
Violet_ball_1
|
||||
bounds:1360,79,65,66
|
||||
offsets:3,2,71,70
|
||||
Violet_ball_2
|
||||
bounds:1751,86,83,84
|
||||
offsets:4,3,91,90
|
||||
rotate:90
|
||||
Violet_ball_3
|
||||
bounds:1762,367,103,104
|
||||
offsets:3,2,109,108
|
||||
Violet_ball_4
|
||||
bounds:1500,440,129,131
|
||||
offsets:2,2,131,134
|
||||
Violet_base
|
||||
bounds:1527,579,170,170
|
||||
offsets:1,1,172,172
|
||||
White_ball_1
|
||||
bounds:1427,84,65,66
|
||||
offsets:3,2,71,70
|
||||
White_ball_2
|
||||
bounds:1837,86,83,84
|
||||
offsets:4,3,91,90
|
||||
rotate:90
|
||||
White_ball_3
|
||||
bounds:754,2,103,104
|
||||
offsets:3,2,109,108
|
||||
White_ball_4
|
||||
bounds:1631,446,129,131
|
||||
offsets:1,2,131,133
|
||||
White_base
|
||||
bounds:1349,573,176,176
|
||||
offsets:1,1,178,178
|
||||
Yellow_ball_1
|
||||
bounds:1494,86,65,66
|
||||
offsets:3,2,71,70
|
||||
Yellow_ball_2
|
||||
bounds:1923,95,83,84
|
||||
offsets:4,3,91,90
|
||||
Yellow_ball_3
|
||||
bounds:1050,211,103,104
|
||||
offsets:3,2,109,108
|
||||
Yellow_ball_4
|
||||
bounds:1430,322,115,116
|
||||
Yellow_base
|
||||
bounds:259,392,169,170
|
||||
offsets:1,1,171,172
|
||||
rotate:90
|
||||
blank_circle
|
||||
bounds:532,242,148,148
|
||||
offsets:1,1,150,150
|
||||
blank_middle
|
||||
bounds:951,317,118,118
|
||||
offsets:1,1,120,120
|
||||
effect/effect_00001
|
||||
bounds:1813,36,48,48
|
||||
offsets:51,52,150,150
|
||||
effect/effect_00002
|
||||
bounds:1148,139,70,70
|
||||
offsets:40,41,150,150
|
||||
effect/effect_00003
|
||||
bounds:930,98,86,86
|
||||
offsets:32,33,150,150
|
||||
effect/effect_00004
|
||||
bounds:1467,222,98,98
|
||||
offsets:26,27,150,150
|
||||
effect/effect_00005
|
||||
bounds:1365,220,100,100
|
||||
offsets:25,26,150,150
|
||||
effect/effect_00006
|
||||
bounds:1050,113,96,96
|
||||
offsets:27,28,150,150
|
||||
effect/effect_00007
|
||||
bounds:2,2,104,104
|
||||
offsets:23,24,150,150
|
||||
effect/effect_00008
|
||||
bounds:1757,257,108,108
|
||||
offsets:21,22,150,150
|
||||
effect/effect_00009
|
||||
bounds:1547,324,114,114
|
||||
offsets:18,19,150,150
|
||||
effect/effect_00010
|
||||
bounds:1071,320,118,118
|
||||
offsets:16,17,150,150
|
||||
effect/effect_00011
|
||||
bounds:829,315,120,120
|
||||
offsets:15,16,150,150
|
||||
effect/effect_00012
|
||||
bounds:685,189,124,124
|
||||
offsets:13,14,150,150
|
||||
effect/effect_00013
|
||||
bounds:410,109,129,126
|
||||
offsets:12,13,150,150
|
||||
effect/effect_00014
|
||||
bounds:1867,389,132,128
|
||||
offsets:11,12,150,150
|
||||
effect/effect_00015
|
||||
bounds:964,441,137,130
|
||||
offsets:10,11,150,150
|
||||
effect/effect_00016
|
||||
bounds:541,108,142,132
|
||||
offsets:8,10,150,150
|
||||
effect/effect_00017
|
||||
bounds:1870,519,144,132
|
||||
offsets:6,10,150,150
|
||||
effect/effect_00018
|
||||
bounds:1870,653,145,96
|
||||
offsets:5,27,150,150
|
||||
effect/effect_00019
|
||||
bounds:602,415,146,91
|
||||
offsets:4,30,150,150
|
||||
rotate:90
|
||||
rainbow_1
|
||||
bounds:1427,17,65,65
|
||||
offsets:1,1,67,67
|
||||
rainbow_10
|
||||
bounds:869,127,59,58
|
||||
offsets:1,1,61,60
|
||||
rainbow_2
|
||||
bounds:1220,78,62,62
|
||||
offsets:1,1,64,64
|
||||
rainbow_3
|
||||
bounds:1561,86,65,66
|
||||
offsets:1,1,67,68
|
||||
rainbow_4
|
||||
bounds:1268,18,58,58
|
||||
offsets:1,1,60,60
|
||||
rainbow_5
|
||||
bounds:1628,83,65,66
|
||||
offsets:1,1,67,68
|
||||
rotate:90
|
||||
rainbow_6
|
||||
bounds:1628,17,64,64
|
||||
offsets:1,1,66,66
|
||||
rainbow_7
|
||||
bounds:1220,142,70,70
|
||||
offsets:1,1,72,72
|
||||
rainbow_8
|
||||
bounds:1696,26,58,58
|
||||
offsets:1,1,60,60
|
||||
rainbow_9
|
||||
bounds:1328,18,58,59
|
||||
offsets:1,1,60,61
|
||||
rainbow_S
|
||||
bounds:2,108,183,189
|
||||
offsets:1,1,185,191
|
||||
rainbow_ballS
|
||||
bounds:1567,224,98,95
|
||||
offsets:4,2,105,99
|
||||
rotate:90
|
||||
symbol_glow_color
|
||||
bounds:2,494,255,255
|
||||
symbol_glow_white
|
||||
bounds:210,221,169,169
|
||||
symbol_shine/eff_00000
|
||||
bounds:210,467,20,25
|
||||
offsets:29,130,203,188
|
||||
symbol_shine/eff_00001
|
||||
bounds:1696,86,53,62
|
||||
offsets:21,106,203,188
|
||||
symbol_shine/eff_00002
|
||||
bounds:785,115,70,82
|
||||
offsets:20,90,203,188
|
||||
rotate:90
|
||||
symbol_shine/eff_00003
|
||||
bounds:1664,236,85,98
|
||||
offsets:20,76,203,188
|
||||
symbol_shine/eff_00004
|
||||
bounds:310,109,98,110
|
||||
offsets:20,65,203,188
|
||||
symbol_shine/eff_00005
|
||||
bounds:187,108,111,121
|
||||
offsets:20,54,203,188
|
||||
rotate:90
|
||||
symbol_shine/eff_00006
|
||||
bounds:811,187,119,126
|
||||
offsets:25,44,203,188
|
||||
symbol_shine/eff_00007
|
||||
bounds:1867,267,120,128
|
||||
offsets:37,34,203,188
|
||||
rotate:90
|
||||
symbol_shine/eff_00008
|
||||
bounds:695,315,119,128
|
||||
offsets:49,25,203,188
|
||||
rotate:90
|
||||
symbol_shine/eff_00009
|
||||
bounds:932,186,116,127
|
||||
offsets:62,17,203,188
|
||||
symbol_shine/eff_00010
|
||||
bounds:318,2,105,119
|
||||
offsets:75,15,203,188
|
||||
rotate:90
|
||||
symbol_shine/eff_00011
|
||||
bounds:1663,336,92,108
|
||||
offsets:88,15,203,188
|
||||
symbol_shine/eff_00012
|
||||
bounds:685,108,79,98
|
||||
offsets:101,15,203,188
|
||||
rotate:90
|
||||
symbol_shine/eff_00013
|
||||
bounds:1923,28,65,83
|
||||
offsets:115,17,203,188
|
||||
rotate:90
|
||||
symbol_shine/eff_00014
|
||||
bounds:1204,27,49,62
|
||||
offsets:130,22,203,188
|
||||
rotate:90
|
||||
symbol_shine_spot
|
||||
bounds:1756,29,55,55
|
||||
offsets:0,0,55,56
|
||||
wildColor_ball_1
|
||||
bounds:1494,18,65,66
|
||||
offsets:3,2,71,70
|
||||
wildColor_ball_2
|
||||
bounds:1751,171,85,84
|
||||
offsets:3,3,91,90
|
||||
wildColor_ball_3
|
||||
bounds:1155,214,103,104
|
||||
offsets:3,2,109,108
|
||||
wildColor_ball_4
|
||||
bounds:1191,321,118,117
|
||||
wildSquare_ball_1
|
||||
bounds:1561,18,65,66
|
||||
offsets:3,2,71,70
|
||||
wildSquare_ball_2
|
||||
bounds:1664,150,85,84
|
||||
offsets:3,3,91,90
|
||||
wildSquare_ball_3
|
||||
bounds:1260,215,103,104
|
||||
offsets:3,2,109,108
|
||||
wildSquare_ball_4
|
||||
bounds:1311,322,117,116
|
||||
wild_base
|
||||
bounds:448,563,186,186
|
||||
offsets:1,1,188,188
|
||||
wild_base_Square
|
||||
bounds:259,563,187,186
|
||||
wild_ray
|
||||
bounds:2,299,206,193
|
||||
offsets:4,7,217,206
|
||||
BIN
src/stories/assets/bubblex/symbols/symbols.skel
Normal file
BIN
src/stories/assets/bubblex/symbols/symbols.webp
Normal file
|
After Width: | Height: | Size: 405 KiB |
@@ -0,0 +1,19 @@
|
||||
anticipation.webp
|
||||
size:2048,1024
|
||||
filter:Linear,Linear
|
||||
scale:0.75
|
||||
anticip_bg
|
||||
bounds:777,30,220,836
|
||||
offsets:319,27,858,879
|
||||
anticip_circle_whole_source_1.4
|
||||
bounds:999,463,404,403
|
||||
offsets:429,445,858,879
|
||||
anticip_frame
|
||||
bounds:291,27,262,839
|
||||
offsets:297,20,858,879
|
||||
anticip_frame_glow
|
||||
bounds:2,2,287,864
|
||||
offsets:285,7,858,879
|
||||
anticip_glow
|
||||
bounds:555,29,220,837
|
||||
offsets:319,27,858,879
|
||||
BIN
src/stories/assets/hold-and-win/anticipation/anticipation.webp
Normal file
|
After Width: | Height: | Size: 209 KiB |
@@ -0,0 +1,16 @@
|
||||
info face="multiplier_value_font" size=90 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=1,1
|
||||
common lineHeight=104 base=83 scaleW=218 scaleH=218 pages=1 packed=0
|
||||
page id=0 file="multiplier_value_font.png"
|
||||
chars count=12
|
||||
char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=0 xadvance=26 page=0 chnl=15
|
||||
char id=48 x=167 y=0 width=51 height=71 xoffset=-1 yoffset=-1 xadvance=49 page=0 chnl=15
|
||||
char id=49 x=0 y=74 width=42 height=72 xoffset=-1 yoffset=-1 xadvance=41 page=0 chnl=15
|
||||
char id=50 x=0 y=147 width=52 height=71 xoffset=-1 yoffset=-1 xadvance=50 page=0 chnl=15
|
||||
char id=51 x=53 y=147 width=42 height=71 xoffset=0 yoffset=0 xadvance=41 page=0 chnl=15
|
||||
char id=52 x=143 y=147 width=54 height=69 xoffset=-1 yoffset=-1 xadvance=52 page=0 chnl=15
|
||||
char id=53 x=96 y=147 width=46 height=71 xoffset=-1 yoffset=-1 xadvance=44 page=0 chnl=15
|
||||
char id=54 x=0 y=0 width=56 height=73 xoffset=-1 yoffset=-1 xadvance=54 page=0 chnl=15
|
||||
char id=55 x=43 y=74 width=50 height=69 xoffset=-1 yoffset=-1 xadvance=48 page=0 chnl=15
|
||||
char id=56 x=57 y=0 width=52 height=73 xoffset=-1 yoffset=-1 xadvance=50 page=0 chnl=15
|
||||
char id=57 x=110 y=0 width=56 height=73 xoffset=-1 yoffset=-1 xadvance=54 page=0 chnl=15
|
||||
char id=88 x=94 y=74 width=55 height=63 xoffset=-1 yoffset=8 xadvance=54 page=0 chnl=15
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
@@ -0,0 +1,16 @@
|
||||
info face="wild_multiplier_font" size=90 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=1,1
|
||||
common lineHeight=104 base=83 scaleW=209 scaleH=218 pages=1 packed=0
|
||||
page id=0 file="wild_multiplier_font.png"
|
||||
chars count=12
|
||||
char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=0 xadvance=26 page=0 chnl=15
|
||||
char id=48 x=0 y=0 width=53 height=73 xoffset=-1 yoffset=-1 xadvance=51 page=0 chnl=15
|
||||
char id=49 x=166 y=0 width=43 height=72 xoffset=-1 yoffset=-1 xadvance=41 page=0 chnl=15
|
||||
char id=50 x=150 y=74 width=53 height=71 xoffset=-1 yoffset=-1 xadvance=51 page=0 chnl=15
|
||||
char id=51 x=58 y=74 width=43 height=72 xoffset=-1 yoffset=-1 xadvance=41 page=0 chnl=15
|
||||
char id=52 x=0 y=148 width=55 height=70 xoffset=-1 yoffset=-1 xadvance=53 page=0 chnl=15
|
||||
char id=53 x=102 y=74 width=47 height=72 xoffset=-1 yoffset=-1 xadvance=45 page=0 chnl=15
|
||||
char id=54 x=54 y=0 width=57 height=73 xoffset=-1 yoffset=-1 xadvance=55 page=0 chnl=15
|
||||
char id=55 x=56 y=148 width=51 height=70 xoffset=-1 yoffset=-1 xadvance=49 page=0 chnl=15
|
||||
char id=56 x=112 y=0 width=53 height=73 xoffset=-1 yoffset=-1 xadvance=51 page=0 chnl=15
|
||||
char id=57 x=0 y=74 width=57 height=73 xoffset=-1 yoffset=-1 xadvance=55 page=0 chnl=15
|
||||
char id=88 x=108 y=148 width=56 height=64 xoffset=-1 yoffset=8 xadvance=55 page=0 chnl=15
|
||||
|
After Width: | Height: | Size: 11 KiB |
126
src/stories/assets/hold-and-win/configs/slotMachine.json
Normal file
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"width": 550,
|
||||
"height": 420,
|
||||
"reels": 5,
|
||||
"slots": 4,
|
||||
"paddings": {
|
||||
"left": 16,
|
||||
"top": 0,
|
||||
"right": 18,
|
||||
"bottom": 0
|
||||
},
|
||||
"symbolsKeys": ["L", "J", "I", "H", "G", "F", "E", "D", "C", "B", "A", "K", "M", "N"],
|
||||
"symbolsWeights": {
|
||||
"L": 0,
|
||||
"J": 0,
|
||||
"I": 0,
|
||||
"H": 0,
|
||||
"G": 0,
|
||||
"F": 0,
|
||||
"E": 0,
|
||||
"D": 0,
|
||||
"C": 0,
|
||||
"B": 0,
|
||||
"K": 0.5,
|
||||
"M": 0,
|
||||
"N": 0
|
||||
},
|
||||
"symbols": {
|
||||
"A": {
|
||||
"key": "A",
|
||||
"spine": "lowest_symbols",
|
||||
"skin": "Ten",
|
||||
"zIndex": 1
|
||||
},
|
||||
"B": {
|
||||
"key": "B",
|
||||
"spine": "lowest_symbols",
|
||||
"skin": "J",
|
||||
"zIndex": 1
|
||||
},
|
||||
"C": {
|
||||
"key": "C",
|
||||
"spine": "lowest_symbols",
|
||||
"skin": "Q",
|
||||
"zIndex": 1
|
||||
},
|
||||
"D": {
|
||||
"key": "D",
|
||||
"spine": "low_symbols",
|
||||
"skin": "K",
|
||||
"zIndex": 1
|
||||
},
|
||||
"E": {
|
||||
"key": "E",
|
||||
"spine": "low_symbols",
|
||||
"skin": "A",
|
||||
"zIndex": 1
|
||||
},
|
||||
"F": {
|
||||
"key": "F",
|
||||
"spine": "h5",
|
||||
"skin": "default",
|
||||
"zIndex": 1
|
||||
},
|
||||
"G": {
|
||||
"key": "G",
|
||||
"spine": "h4",
|
||||
"skin": "default",
|
||||
"zIndex": 1
|
||||
},
|
||||
"H": {
|
||||
"key": "H",
|
||||
"spine": "h3",
|
||||
"skin": "default",
|
||||
"zIndex": 1
|
||||
},
|
||||
"I": {
|
||||
"key": "I",
|
||||
"spine": "h2",
|
||||
"skin": "default",
|
||||
"zIndex": 1
|
||||
},
|
||||
"J": {
|
||||
"key": "J",
|
||||
"spine": "h1",
|
||||
"skin": "default",
|
||||
"zIndex": 1
|
||||
},
|
||||
"K": {
|
||||
"key": "K",
|
||||
"spine": "scatter",
|
||||
"skin": "default",
|
||||
"zIndex": 2
|
||||
},
|
||||
"L": {
|
||||
"key": "L",
|
||||
"spine": "wild",
|
||||
"skin": "low",
|
||||
"fontStyle": {
|
||||
"fontSize": 30,
|
||||
"fontName": "wild_multiplier_font"
|
||||
},
|
||||
"zIndex": 3
|
||||
},
|
||||
"M": {
|
||||
"key": "M",
|
||||
"spine": "wild",
|
||||
"skin": "med",
|
||||
"fontStyle": {
|
||||
"fontSize": 30,
|
||||
"fontName": "wild_multiplier_font"
|
||||
},
|
||||
"zIndex": 3
|
||||
},
|
||||
"N": {
|
||||
"key": "N",
|
||||
"spine": "wild",
|
||||
"skin": "high",
|
||||
"fontStyle": {
|
||||
"fontSize": 30,
|
||||
"fontName": "wild_multiplier_font"
|
||||
},
|
||||
"zIndex": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
158
src/stories/assets/hold-and-win/manifest.json
Normal file
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"bundles": [
|
||||
{
|
||||
"name": "configs",
|
||||
"assets": [
|
||||
{
|
||||
"name": "slot_machine_config",
|
||||
"srcs": "hold-and-win/configs/slotMachine.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"symbols",
|
||||
"assets":[
|
||||
{
|
||||
"name": "wild_multiplier_font",
|
||||
"srcs": "hold-and-win/bitmapFonts/wild_multiplier_font.fnt"
|
||||
},
|
||||
{
|
||||
"name": "symbols_atlas",
|
||||
"srcs": "hold-and-win/symbols/symbols.atlas"
|
||||
},
|
||||
{
|
||||
"name": "wild",
|
||||
"srcs": "hold-and-win/symbols/wild.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.35,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "hold-and-win/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "scatter",
|
||||
"srcs": "hold-and-win/symbols/scatter.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.5,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "hold-and-win/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "lowest_symbols",
|
||||
"srcs": "hold-and-win/symbols/L_10_q_j.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.35,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "hold-and-win/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "low_symbols",
|
||||
"srcs": "hold-and-win/symbols/L_A_K.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.35,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "hold-and-win/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "h5",
|
||||
"srcs": "hold-and-win/symbols/M3.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.45,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "hold-and-win/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "h4",
|
||||
"srcs": "hold-and-win/symbols/M2.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.4,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "hold-and-win/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "h3",
|
||||
"srcs": "hold-and-win/symbols/M1.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.35,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "hold-and-win/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "h2",
|
||||
"srcs": "hold-and-win/symbols/H2.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.5,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "hold-and-win/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "h1",
|
||||
"srcs": "hold-and-win/symbols/H1.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.5,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "hold-and-win/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "expanding",
|
||||
"srcs": "hold-and-win/symbols/expanding_multiplier.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.5,
|
||||
"spineAtlasAlias": "symbols_atlas",
|
||||
"spineAtlasFile": "hold-and-win/symbols/symbols.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "win_line",
|
||||
"srcs": "hold-and-win/winline/win_line.skel",
|
||||
"data": {
|
||||
"spineAtlasAlias": "main_atlas",
|
||||
"spineAtlasFile": "hold-and-win/winline/assembly.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "win_frame",
|
||||
"srcs": "hold-and-win/textures/win_frame.png"
|
||||
},
|
||||
{
|
||||
"name": "anticipation_back",
|
||||
"srcs": "hold-and-win/anticipation/anticipation_back.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.4,
|
||||
"spineAtlasAlias": "anticipation",
|
||||
"spineAtlasFile": "hold-and-win/anticipation/anticipation.atlas"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "anticipation_front",
|
||||
"srcs": "hold-and-win/anticipation/anticipation_front.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.4,
|
||||
"spineAtlasAlias": "anticipation",
|
||||
"spineAtlasFile": "hold-and-win/anticipation/anticipation.atlas"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "addons",
|
||||
"assets": [
|
||||
{
|
||||
"name": "slot_multiplier",
|
||||
"srcs": "hold-and-win/slot-multiplier/slot_multiplier.skel",
|
||||
"data": {
|
||||
"spineSkeletonScale": 0.4
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
slot_multiplier.webp
|
||||
size:392,280
|
||||
filter:Linear,Linear
|
||||
imgs/frame
|
||||
bounds:2,2,284,276
|
||||
imgs/placeholder
|
||||
bounds:288,178,100,100
|
||||
offsets:1,1,102,102
|
||||
|
After Width: | Height: | Size: 19 KiB |