@adiakys/feeling-lucky 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +227 -2
- package/dist/config/resolve.d.ts +4 -0
- package/dist/config/types.d.ts +62 -0
- package/dist/de-B56NsnDF.js +458 -0
- package/dist/developer/panel.d.ts +61 -0
- package/dist/developer/rig.d.ts +2 -0
- package/dist/developer/settings.d.ts +30 -0
- package/dist/engine/awards.d.ts +12 -0
- package/dist/engine/cards.d.ts +44 -0
- package/dist/engine/cashout.d.ts +32 -0
- package/dist/engine/dealer.d.ts +21 -0
- package/dist/engine/outcome.d.ts +13 -0
- package/dist/engine/pachinko.d.ts +58 -0
- package/dist/engine/random.d.ts +6 -0
- package/dist/engine/rig.d.ts +28 -0
- package/dist/engine/roulette.d.ts +53 -0
- package/dist/engine/scratch.d.ts +40 -0
- package/dist/engine/slot.d.ts +19 -0
- package/dist/engine/themes.d.ts +30 -0
- package/dist/engine/tiers.d.ts +64 -0
- package/dist/engine/wheel.d.ts +47 -0
- package/dist/es-9iWwCe8s.js +458 -0
- package/dist/feeling-lucky.iife.js +1494 -0
- package/dist/feeling-lucky.js +65 -0
- package/dist/fr-Y2BcCS3U.js +458 -0
- package/dist/games/blackjack/blackjack.d.ts +48 -0
- package/dist/games/cards/card.d.ts +5 -0
- package/dist/games/cards/dealer.d.ts +9 -0
- package/dist/games/cards/table.d.ts +12 -0
- package/dist/games/holdem/holdem.d.ts +29 -0
- package/dist/games/index.d.ts +9 -0
- package/dist/games/pachinko/pachinko.d.ts +53 -0
- package/dist/games/roulette/roulette.d.ts +63 -0
- package/dist/games/roulette/table.d.ts +19 -0
- package/dist/games/roulette/track.d.ts +18 -0
- package/dist/games/roulette/wheel.d.ts +9 -0
- package/dist/games/scratch/covers.d.ts +3 -0
- package/dist/games/scratch/double.d.ts +3 -0
- package/dist/games/scratch/forever.d.ts +3 -0
- package/dist/games/scratch/scratch.d.ts +39 -0
- package/dist/games/slot/breakaway.d.ts +32 -0
- package/dist/games/slot/machine.d.ts +18 -0
- package/dist/games/slot/slot.d.ts +32 -0
- package/dist/games/slot/takeoff.d.ts +28 -0
- package/dist/games/wheel/coin.d.ts +19 -0
- package/dist/games/wheel/face.d.ts +18 -0
- package/dist/games/wheel/hunt.d.ts +15 -0
- package/dist/games/wheel/wheel.d.ts +64 -0
- package/dist/i18n-it2FlmVZ.js +490 -0
- package/dist/index.d.ts +4 -0
- package/dist/it-CNSOLgaT.js +458 -0
- package/dist/locales/de/cards.d.ts +68 -0
- package/dist/locales/de/developer.d.ts +71 -0
- package/dist/locales/de/index.d.ts +2 -0
- package/dist/locales/de/pachinko.d.ts +26 -0
- package/dist/locales/de/room.d.ts +86 -0
- package/dist/locales/de/roulette.d.ts +77 -0
- package/dist/locales/de/scratch.d.ts +56 -0
- package/dist/locales/de/slot.d.ts +61 -0
- package/dist/locales/de/wheel.d.ts +45 -0
- package/dist/locales/en/cards.d.ts +68 -0
- package/dist/locales/en/developer.d.ts +71 -0
- package/dist/locales/en/index.d.ts +2 -0
- package/dist/locales/en/pachinko.d.ts +26 -0
- package/dist/locales/en/room.d.ts +86 -0
- package/dist/locales/en/roulette.d.ts +77 -0
- package/dist/locales/en/scratch.d.ts +56 -0
- package/dist/locales/en/slot.d.ts +61 -0
- package/dist/locales/en/wheel.d.ts +45 -0
- package/dist/locales/es/cards.d.ts +68 -0
- package/dist/locales/es/developer.d.ts +71 -0
- package/dist/locales/es/index.d.ts +2 -0
- package/dist/locales/es/pachinko.d.ts +26 -0
- package/dist/locales/es/room.d.ts +86 -0
- package/dist/locales/es/roulette.d.ts +77 -0
- package/dist/locales/es/scratch.d.ts +56 -0
- package/dist/locales/es/slot.d.ts +61 -0
- package/dist/locales/es/wheel.d.ts +45 -0
- package/dist/locales/fr/cards.d.ts +68 -0
- package/dist/locales/fr/developer.d.ts +71 -0
- package/dist/locales/fr/index.d.ts +2 -0
- package/dist/locales/fr/pachinko.d.ts +26 -0
- package/dist/locales/fr/room.d.ts +86 -0
- package/dist/locales/fr/roulette.d.ts +77 -0
- package/dist/locales/fr/scratch.d.ts +56 -0
- package/dist/locales/fr/slot.d.ts +61 -0
- package/dist/locales/fr/wheel.d.ts +45 -0
- package/dist/locales/index.d.ts +7 -0
- package/dist/locales/it/cards.d.ts +68 -0
- package/dist/locales/it/developer.d.ts +71 -0
- package/dist/locales/it/index.d.ts +2 -0
- package/dist/locales/it/pachinko.d.ts +26 -0
- package/dist/locales/it/room.d.ts +86 -0
- package/dist/locales/it/roulette.d.ts +77 -0
- package/dist/locales/it/scratch.d.ts +56 -0
- package/dist/locales/it/slot.d.ts +61 -0
- package/dist/locales/it/wheel.d.ts +45 -0
- package/dist/locales/merge.d.ts +2 -0
- package/dist/merge-iGSNfZkz.js +10 -0
- package/dist/mount-CBUkAcVV.js +7109 -0
- package/dist/panel-tWJMt9kl.js +545 -0
- package/dist/room/boost.d.ts +2 -0
- package/dist/room/certificate.d.ts +22 -0
- package/dist/room/chips.d.ts +21 -0
- package/dist/room/lobby.d.ts +45 -0
- package/dist/room/mount.d.ts +6 -0
- package/dist/room/room.d.ts +74 -0
- package/dist/room/stage.d.ts +18 -0
- package/dist/room/theme.d.ts +15 -0
- package/dist/runtime/context.d.ts +34 -0
- package/dist/runtime/element.d.ts +15 -0
- package/dist/runtime/fx.d.ts +43 -0
- package/dist/runtime/i18n.d.ts +8 -0
- package/dist/runtime/icons.d.ts +2 -0
- package/dist/runtime/motion.d.ts +2 -0
- package/dist/runtime/signal.d.ts +6 -0
- package/dist/runtime/sound.d.ts +11 -0
- package/dist/signal-CAveEGrG.js +19 -0
- package/dist/styles/index.d.ts +1 -0
- package/package.json +56 -4
package/README.md
CHANGED
|
@@ -1,3 +1,228 @@
|
|
|
1
|
-
#
|
|
1
|
+
# feeling-lucky
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A casino room for any web app: players try their luck on classic casino games and win the prizes you configure.
|
|
4
|
+
|
|
5
|
+
**[Live demo](https://adiakys.github.io/feeling-lucky/)**: Ctrl+Shift+L in the room opens the developer menu.
|
|
6
|
+
|
|
7
|
+
- **Framework-free:** a single entry point that works with React, Angular, Vue or plain HTML.
|
|
8
|
+
- **No runtime dependencies:** built with [Lit](https://lit.dev) and [Lucide](https://lucide.dev) icons, both bundled.
|
|
9
|
+
- **Isolated:** the room lives in a shadow root mounted on `body`, so the host's CSS never reaches it and its CSS never leaks out.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @adiakys/feeling-lucky
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Without a bundler, the IIFE build exposes a global:
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<script src="https://cdn.jsdelivr.net/npm/@adiakys/feeling-lucky/dist/feeling-lucky.iife.js"></script>
|
|
21
|
+
<script>
|
|
22
|
+
const lucky = FeelingLucky.createFeelingLucky({ prizes: [/* … */] });
|
|
23
|
+
</script>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { createFeelingLucky } from '@adiakys/feeling-lucky';
|
|
30
|
+
|
|
31
|
+
const lucky = createFeelingLucky({
|
|
32
|
+
prizes: [
|
|
33
|
+
{
|
|
34
|
+
id: 'pro',
|
|
35
|
+
name: 'Pro',
|
|
36
|
+
icon: '/icons/pro.svg',
|
|
37
|
+
variants: [
|
|
38
|
+
{ tier: 'small', label: '7 days', short: '7D' },
|
|
39
|
+
{ tier: 'big', label: '1 year', short: '1Y' },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
{ id: 'basic', name: 'Basic', icon: '/icons/basic.svg' },
|
|
43
|
+
],
|
|
44
|
+
onWin: (win) => console.log(win),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
button.addEventListener('click', (event) => lucky.open({ from: event.currentTarget }));
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
| Method | |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `open({ from? })` | Opens the room, growing out of `from` (an element or `{ x, y }`) |
|
|
53
|
+
| `close()` | Closes the room |
|
|
54
|
+
| `update(config)` | Merges a partial config |
|
|
55
|
+
| `destroy()` | Removes the room and its listeners |
|
|
56
|
+
|
|
57
|
+
## Config
|
|
58
|
+
|
|
59
|
+
Every field takes a value, a function, or a function returning a Promise.
|
|
60
|
+
|
|
61
|
+
| Field | Type | Default |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| `prizes` | `Prize[]`, 1 to n | required |
|
|
64
|
+
| `locale` | language code, e.g. `'it'` or `'pt-BR'` | follows the host page, see [Languages](#languages) |
|
|
65
|
+
| `messages` | `{ [locale]: Messages }`: new languages, or overrides of the built-in ones | none |
|
|
66
|
+
| `logo` | image URL of your brand mark, on the certificate, the wheel's hub and the scratch card | a clover |
|
|
67
|
+
| `dealers` | `Dealer[]`: the dealers who take the Salon Privé's shifts, see [Dealers](#dealers) | the library's own dealer |
|
|
68
|
+
| `player` | `{ name, avatar? }`, shown at the card tables | anonymous seat |
|
|
69
|
+
| `onWin` | `(win: Win) => void` | none |
|
|
70
|
+
| `onClaim` | `(win: Win) => Promise<void>`; shows **Claim** on the certificate | no claim button |
|
|
71
|
+
| `developer` | `true` or `{ passwordHash? }`: enables the developer menu (Ctrl+Shift+L), behind a password when the hash is given | disabled |
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
interface Prize {
|
|
75
|
+
id: string;
|
|
76
|
+
name: string;
|
|
77
|
+
icon: string; // image URL
|
|
78
|
+
color?: string; // chip colour
|
|
79
|
+
variants?: Variant[] | ((tier: PrizeTier) => Variant | Promise<Variant>);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
type PrizeTier = 'small' | 'big' | 'jackpot';
|
|
83
|
+
|
|
84
|
+
interface Variant {
|
|
85
|
+
tier: PrizeTier;
|
|
86
|
+
label: string; // certificate: "7 days"
|
|
87
|
+
short?: string; // game boards: "7D"
|
|
88
|
+
details?: { label: string; value: string }[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface Win {
|
|
92
|
+
prize: Prize;
|
|
93
|
+
variant: Variant | null; // null: the full prize
|
|
94
|
+
tier: PrizeTier;
|
|
95
|
+
game: GameId;
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Prizes and variants
|
|
100
|
+
|
|
101
|
+
- **Every play draws a tier first** (`none`, `small`, `big` or `jackpot`), then the game stages it.
|
|
102
|
+
- **Variants are optional.** A prize without variants is won in full, whatever the tier.
|
|
103
|
+
- **Board labels:** game boards (wheel segments, pachinko pockets, cash-out steps, scratch tickets) show the variants' `short` labels.
|
|
104
|
+
- **Tier symbol fallback:** a prize without variants, or with a variants function, shows the tier symbol on the board instead. Its variant is revealed on the certificate.
|
|
105
|
+
- **No signing or issuing.** `onClaim` hands the win to the host, which does whatever claiming means for it, e.g. closing the room and opening its own page, prefilled from the variant.
|
|
106
|
+
- **Your own fields:** variants can carry fields of yours. The win hands back the very object you gave, typed as yours:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
interface Plan extends Variant {
|
|
110
|
+
months: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const lucky = createFeelingLucky<Plan>({
|
|
114
|
+
prizes: [{ id: 'pro', name: 'Pro', icon: '/pro.svg', variants: [{ tier: 'big', label: '1 year', months: 12 }] }],
|
|
115
|
+
onClaim: async (win) => {
|
|
116
|
+
lucky.close();
|
|
117
|
+
router.push(`/redeem/${win.prize.id}?months=${win.variant?.months ?? 'all'}`);
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Developer menu
|
|
123
|
+
|
|
124
|
+
Ctrl+Shift+L in the room opens a password-gated panel that forces outcomes, themes and bonuses, for demos and testing. Without `passwordHash` it opens without a password. With it, only the password's SHA-256 hex goes in the config; checking it needs a secure context (https or localhost):
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
printf '%s' 'my password' | sha256sum
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Languages
|
|
131
|
+
|
|
132
|
+
Every text, game names included, comes from the locale. Built in: `en`, `it`, `es`, `fr`, `de`. English ships with the library; the others load on demand, only when the room speaks them.
|
|
133
|
+
|
|
134
|
+
- **Default locale:** without `locale`, the room follows `<html lang>` and switches when the host app changes it, falling back to `navigator.language`.
|
|
135
|
+
- **Matching:** `pt-BR` uses `pt-BR`, then `pt`, then `en`.
|
|
136
|
+
- **New languages:** add them through `messages`. Missing keys fall back to `en`.
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { createFeelingLucky, en } from '@adiakys/feeling-lucky';
|
|
140
|
+
|
|
141
|
+
createFeelingLucky({
|
|
142
|
+
prizes,
|
|
143
|
+
messages: {
|
|
144
|
+
pt: { ...en, room: 'Sala de jogos' /* … */ },
|
|
145
|
+
it: { games: { wheel: { name: 'Ruota d’oro' } } },
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Games
|
|
151
|
+
|
|
152
|
+
| Room | Games |
|
|
153
|
+
|---|---|
|
|
154
|
+
| Main hall | Grand Wheel (Prize Hunt, Heads or Tails, Pachinko bonus), Roulette, Slot, Pachinko, Scratch card |
|
|
155
|
+
| Salon Privé | Blackjack, Texas Hold'em, single player against the house |
|
|
156
|
+
|
|
157
|
+
- **Bonus of the day:** one main-hall game per day, the same for everyone, gets its own variant and boosted odds:
|
|
158
|
+
- Storm Roulette
|
|
159
|
+
- Booster on the Grand Wheel
|
|
160
|
+
- Takeoff or Breakaway in place of the slot
|
|
161
|
+
- Double Luck or Forever in place of the scratch card
|
|
162
|
+
- colour Pachinko
|
|
163
|
+
- **Dealers:** three shifts a day, the same for everyone, from the date's hash, see [Dealers](#dealers).
|
|
164
|
+
- **Room themes:** Christmas, Halloween, Easter and August, taken from the calendar.
|
|
165
|
+
|
|
166
|
+
## Dealers
|
|
167
|
+
|
|
168
|
+
A dealer stands at the Salon Privé's tables during three shifts a day of 30 to 90 minutes between 10:00 and 23:00, local time. The shifts come from the date's hash, and the dealers in the list take them in turn. While a dealer is on shift, Blackjack and Texas Hold'em win twice as often. A dealer arriving or leaving mid-game walks in or out of the table, and the odds change from the next hand.
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
interface Dealer {
|
|
172
|
+
image: string; // waist-up portrait, transparent background, forearms resting forward at the bottom
|
|
173
|
+
name?: string;
|
|
174
|
+
hands?: string; // CSS clip-path of the forearms and hands, drawn over the table's rim
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Without `dealers`, the library's own illustrated dealer takes every shift; `dealers: []` leaves the tables without one.
|
|
179
|
+
|
|
180
|
+
## Structure
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
src/
|
|
184
|
+
index.ts public API
|
|
185
|
+
config/ config types, defaults
|
|
186
|
+
engine/ outcomes, no DOM
|
|
187
|
+
runtime/ i18n, icons, sound, fx
|
|
188
|
+
room/ room, lobby, certificate
|
|
189
|
+
games/ one folder per game:
|
|
190
|
+
wheel, roulette, slot, pachinko,
|
|
191
|
+
scratch, blackjack, holdem
|
|
192
|
+
developer/ developer menu, lazy
|
|
193
|
+
styles/ CSS per area
|
|
194
|
+
locales/ en, it, es, fr, de
|
|
195
|
+
playground/ demo with sample prizes
|
|
196
|
+
tests/ unit (engine), e2e
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Rules
|
|
200
|
+
|
|
201
|
+
- **`engine/` decides every outcome** and never touches the DOM. Games only stage what it decides.
|
|
202
|
+
- **Rig seam:** the developer menu reaches the outcomes only through `engine/rig` (`decide(key, context, draw)`). Games don't know it exists.
|
|
203
|
+
- **Lit elements render into the room's shadow root, not into their own.** One stylesheet styles the whole room.
|
|
204
|
+
- **Every text lives in `locales/`.** Icons are inline SVG.
|
|
205
|
+
|
|
206
|
+
## Development
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
npm install
|
|
210
|
+
npm run dev # playground
|
|
211
|
+
npm test # unit tests
|
|
212
|
+
npm run test:e2e # one smoke test per game
|
|
213
|
+
npm run typecheck
|
|
214
|
+
npm run build # dist/: ESM + IIFE
|
|
215
|
+
npm run build:demo # site/: the playground, as on GitHub Pages
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Release
|
|
219
|
+
|
|
220
|
+
Publish a GitHub release or pre-release on a `v<version>` tag, e.g. `v1.2.0` or `v1.3.0-beta.1`: the version comes from the tag, `package.json` only holds a placeholder. The release workflow:
|
|
221
|
+
- runs the checks;
|
|
222
|
+
- publishes the package to npm: `latest` for a release, `next` for a pre-release;
|
|
223
|
+
- attaches the packed `.tgz` to the release;
|
|
224
|
+
- deploys the playground to GitHub Pages, for releases only.
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
[GPL-3.0-or-later](LICENSE)
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FeelingLuckyConfig, Resolvable } from './types';
|
|
2
|
+
import type { RoomConfig } from '../room/room';
|
|
3
|
+
export declare const resolve: <T>(value: Resolvable<T>) => Promise<T>;
|
|
4
|
+
export declare function resolveConfig(config: FeelingLuckyConfig): Promise<RoomConfig>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export type Tier = 'none' | 'small' | 'big' | 'jackpot';
|
|
2
|
+
export type PrizeTier = Exclude<Tier, 'none'>;
|
|
3
|
+
export type GameId = 'wheel' | 'slot' | 'roulette' | 'pachinko' | 'scratch' | 'blackjack' | 'holdem';
|
|
4
|
+
export type Resolvable<T> = T | (() => T | Promise<T>);
|
|
5
|
+
export interface Variant {
|
|
6
|
+
tier: PrizeTier;
|
|
7
|
+
label: string;
|
|
8
|
+
short?: string;
|
|
9
|
+
details?: {
|
|
10
|
+
label: string;
|
|
11
|
+
value: string;
|
|
12
|
+
}[];
|
|
13
|
+
}
|
|
14
|
+
export interface Prize<V extends Variant = Variant> {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
icon: string;
|
|
18
|
+
color?: string;
|
|
19
|
+
variants?: V[] | ((tier: PrizeTier) => V | Promise<V>);
|
|
20
|
+
}
|
|
21
|
+
export interface Win<V extends Variant = Variant> {
|
|
22
|
+
prize: Prize<V>;
|
|
23
|
+
variant: V | null;
|
|
24
|
+
tier: PrizeTier;
|
|
25
|
+
game: GameId;
|
|
26
|
+
}
|
|
27
|
+
export interface Dealer {
|
|
28
|
+
image: string;
|
|
29
|
+
name?: string;
|
|
30
|
+
hands?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface Player {
|
|
33
|
+
name: string;
|
|
34
|
+
avatar?: string;
|
|
35
|
+
}
|
|
36
|
+
export type Messages = {
|
|
37
|
+
[key: string]: string | Messages;
|
|
38
|
+
};
|
|
39
|
+
export interface FeelingLuckyConfig<V extends Variant = Variant> {
|
|
40
|
+
prizes: Resolvable<Prize<V>[]>;
|
|
41
|
+
locale?: Resolvable<string>;
|
|
42
|
+
messages?: Resolvable<Record<string, Messages>>;
|
|
43
|
+
player?: Resolvable<Player>;
|
|
44
|
+
logo?: Resolvable<string>;
|
|
45
|
+
dealers?: Resolvable<Dealer[]>;
|
|
46
|
+
onWin?: (win: Win<V>) => void;
|
|
47
|
+
onClaim?: (win: Win<V>) => Promise<void>;
|
|
48
|
+
developer?: Resolvable<boolean | {
|
|
49
|
+
passwordHash?: string;
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
52
|
+
export interface FeelingLucky<V extends Variant = Variant> {
|
|
53
|
+
open(options?: {
|
|
54
|
+
from?: Element | {
|
|
55
|
+
x: number;
|
|
56
|
+
y: number;
|
|
57
|
+
};
|
|
58
|
+
}): void;
|
|
59
|
+
close(): void;
|
|
60
|
+
update(config: Partial<FeelingLuckyConfig<V>>): void;
|
|
61
|
+
destroy(): void;
|
|
62
|
+
}
|