@adiakys/feeling-lucky 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +211 -2
- package/dist/config/resolve.d.ts +4 -0
- package/dist/config/types.d.ts +56 -0
- package/dist/de-Cp-ILcpM.js +452 -0
- package/dist/developer/panel.d.ts +60 -0
- package/dist/developer/rig.d.ts +2 -0
- package/dist/developer/settings.d.ts +29 -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/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-DHZIstf4.js +452 -0
- package/dist/feeling-lucky.iife.js +1478 -0
- package/dist/feeling-lucky.js +63 -0
- package/dist/fr-BPFoerSF.js +452 -0
- package/dist/games/blackjack/blackjack.d.ts +47 -0
- package/dist/games/cards/card.d.ts +5 -0
- package/dist/games/cards/table.d.ts +8 -0
- package/dist/games/holdem/holdem.d.ts +27 -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-CihzHMU5.js +484 -0
- package/dist/index.d.ts +4 -0
- package/dist/it-B95CFDto.js +452 -0
- package/dist/locales/de/cards.d.ts +68 -0
- package/dist/locales/de/developer.d.ts +67 -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 +84 -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 +67 -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 +84 -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 +67 -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 +84 -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 +67 -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 +84 -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 +67 -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 +84 -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-BxDaFww2.js +7034 -0
- package/dist/panel-D1hKmQL7.js +530 -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 +44 -0
- package/dist/room/mount.d.ts +6 -0
- package/dist/room/room.d.ts +70 -0
- package/dist/room/stage.d.ts +17 -0
- package/dist/room/theme.d.ts +15 -0
- package/dist/runtime/context.d.ts +33 -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,212 @@
|
|
|
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
|
+
| `player` | `{ name, avatar? }`, shown at the card tables | anonymous seat |
|
|
68
|
+
| `onWin` | `(win: Win) => void` | none |
|
|
69
|
+
| `onClaim` | `(win: Win) => Promise<void>`; shows **Claim** on the certificate | no claim button |
|
|
70
|
+
| `developer` | `true` or `{ passwordHash? }`: enables the developer menu (Ctrl+Shift+L), behind a password when the hash is given | disabled |
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
interface Prize {
|
|
74
|
+
id: string;
|
|
75
|
+
name: string;
|
|
76
|
+
icon: string; // image URL
|
|
77
|
+
color?: string; // chip colour
|
|
78
|
+
variants?: Variant[] | ((tier: PrizeTier) => Variant | Promise<Variant>);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
type PrizeTier = 'small' | 'big' | 'jackpot';
|
|
82
|
+
|
|
83
|
+
interface Variant {
|
|
84
|
+
tier: PrizeTier;
|
|
85
|
+
label: string; // certificate: "7 days"
|
|
86
|
+
short?: string; // game boards: "7D"
|
|
87
|
+
details?: { label: string; value: string }[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface Win {
|
|
91
|
+
prize: Prize;
|
|
92
|
+
variant: Variant | null; // null: the full prize
|
|
93
|
+
tier: PrizeTier;
|
|
94
|
+
game: GameId;
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Prizes and variants
|
|
99
|
+
|
|
100
|
+
- **Every play draws a tier first** (`none`, `small`, `big` or `jackpot`), then the game stages it.
|
|
101
|
+
- **Variants are optional.** A prize without variants is won in full, whatever the tier.
|
|
102
|
+
- **Board labels:** game boards (wheel segments, pachinko pockets, cash-out steps, scratch tickets) show the variants' `short` labels.
|
|
103
|
+
- **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.
|
|
104
|
+
- **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.
|
|
105
|
+
- **Your own fields:** variants can carry fields of yours. The win hands back the very object you gave, typed as yours:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
interface Plan extends Variant {
|
|
109
|
+
months: number;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const lucky = createFeelingLucky<Plan>({
|
|
113
|
+
prizes: [{ id: 'pro', name: 'Pro', icon: '/pro.svg', variants: [{ tier: 'big', label: '1 year', months: 12 }] }],
|
|
114
|
+
onClaim: async (win) => {
|
|
115
|
+
lucky.close();
|
|
116
|
+
router.push(`/redeem/${win.prize.id}?months=${win.variant?.months ?? 'all'}`);
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Developer menu
|
|
122
|
+
|
|
123
|
+
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):
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
printf '%s' 'my password' | sha256sum
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Languages
|
|
130
|
+
|
|
131
|
+
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.
|
|
132
|
+
|
|
133
|
+
- **Default locale:** without `locale`, the room follows `<html lang>` and switches when the host app changes it, falling back to `navigator.language`.
|
|
134
|
+
- **Matching:** `pt-BR` uses `pt-BR`, then `pt`, then `en`.
|
|
135
|
+
- **New languages:** add them through `messages`. Missing keys fall back to `en`.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import { createFeelingLucky, en } from '@adiakys/feeling-lucky';
|
|
139
|
+
|
|
140
|
+
createFeelingLucky({
|
|
141
|
+
prizes,
|
|
142
|
+
messages: {
|
|
143
|
+
pt: { ...en, room: 'Sala de jogos' /* … */ },
|
|
144
|
+
it: { games: { wheel: { name: 'Ruota d’oro' } } },
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Games
|
|
150
|
+
|
|
151
|
+
| Room | Games |
|
|
152
|
+
|---|---|
|
|
153
|
+
| Main hall | Grand Wheel (Prize Hunt, Heads or Tails, Pachinko bonus), Roulette, Slot, Pachinko, Scratch card |
|
|
154
|
+
| Salon Privé | Blackjack, Texas Hold'em, single player against the house |
|
|
155
|
+
|
|
156
|
+
- **Bonus of the day:** one main-hall game per day, the same for everyone, gets its own variant and boosted odds:
|
|
157
|
+
- Storm Roulette
|
|
158
|
+
- Booster on the Grand Wheel
|
|
159
|
+
- Takeoff or Breakaway in place of the slot
|
|
160
|
+
- Double Luck or Forever in place of the scratch card
|
|
161
|
+
- colour Pachinko
|
|
162
|
+
- **Room themes:** Christmas, Halloween, Easter and August, taken from the calendar.
|
|
163
|
+
|
|
164
|
+
## Structure
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
src/
|
|
168
|
+
index.ts public API
|
|
169
|
+
config/ config types, defaults
|
|
170
|
+
engine/ outcomes, no DOM
|
|
171
|
+
runtime/ i18n, icons, sound, fx
|
|
172
|
+
room/ room, lobby, certificate
|
|
173
|
+
games/ one folder per game:
|
|
174
|
+
wheel, roulette, slot, pachinko,
|
|
175
|
+
scratch, blackjack, holdem
|
|
176
|
+
developer/ developer menu, lazy
|
|
177
|
+
styles/ CSS per area
|
|
178
|
+
locales/ en, it, es, fr, de
|
|
179
|
+
playground/ demo with sample prizes
|
|
180
|
+
tests/ unit (engine), e2e
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Rules
|
|
184
|
+
|
|
185
|
+
- **`engine/` decides every outcome** and never touches the DOM. Games only stage what it decides.
|
|
186
|
+
- **Rig seam:** the developer menu reaches the outcomes only through `engine/rig` (`decide(key, context, draw)`). Games don't know it exists.
|
|
187
|
+
- **Lit elements render into the room's shadow root, not into their own.** One stylesheet styles the whole room.
|
|
188
|
+
- **Every text lives in `locales/`.** Icons are inline SVG.
|
|
189
|
+
|
|
190
|
+
## Development
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
npm install
|
|
194
|
+
npm run dev # playground
|
|
195
|
+
npm test # unit tests
|
|
196
|
+
npm run test:e2e # one smoke test per game
|
|
197
|
+
npm run typecheck
|
|
198
|
+
npm run build # dist/: ESM + IIFE
|
|
199
|
+
npm run build:demo # site/: the playground, as on GitHub Pages
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Release
|
|
203
|
+
|
|
204
|
+
Bump `version` in `package.json`, then push a `v<version>` tag. The release workflow:
|
|
205
|
+
- runs the checks;
|
|
206
|
+
- publishes the package to npm;
|
|
207
|
+
- attaches the packed `.tgz` to a GitHub release;
|
|
208
|
+
- deploys the playground to GitHub Pages.
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
[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,56 @@
|
|
|
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 Player {
|
|
28
|
+
name: string;
|
|
29
|
+
avatar?: string;
|
|
30
|
+
}
|
|
31
|
+
export type Messages = {
|
|
32
|
+
[key: string]: string | Messages;
|
|
33
|
+
};
|
|
34
|
+
export interface FeelingLuckyConfig<V extends Variant = Variant> {
|
|
35
|
+
prizes: Resolvable<Prize<V>[]>;
|
|
36
|
+
locale?: Resolvable<string>;
|
|
37
|
+
messages?: Resolvable<Record<string, Messages>>;
|
|
38
|
+
player?: Resolvable<Player>;
|
|
39
|
+
logo?: Resolvable<string>;
|
|
40
|
+
onWin?: (win: Win<V>) => void;
|
|
41
|
+
onClaim?: (win: Win<V>) => Promise<void>;
|
|
42
|
+
developer?: Resolvable<boolean | {
|
|
43
|
+
passwordHash?: string;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
export interface FeelingLucky<V extends Variant = Variant> {
|
|
47
|
+
open(options?: {
|
|
48
|
+
from?: Element | {
|
|
49
|
+
x: number;
|
|
50
|
+
y: number;
|
|
51
|
+
};
|
|
52
|
+
}): void;
|
|
53
|
+
close(): void;
|
|
54
|
+
update(config: Partial<FeelingLuckyConfig<V>>): void;
|
|
55
|
+
destroy(): void;
|
|
56
|
+
}
|