# Virtue Slots

A browser-based slot machine game with a Christian religious theme, built with Phaser 3 and vanilla JavaScript. May Fortune Favor the Faithful.

![Game preview: three spinning reels with religious symbols, gold UI, and animated win effects](assets/symbol_sprites.png)

---

## Features

- **10 holy symbols** — from Dove to Holy Grail, each with unique payout values scaled by holiness
- **Animated reels** — smooth spin, decelerate, and snap-to-symbol with sprite overlays
- **Win sequence** — "MATCH: 3×" banner punches in with elastic animation, sparkles, and shaking; followed by arcing coin animations splitting 60% to the player and 40% to The Lord
- **Loss sequence** — a devil carries the spent money up to the Sin box with a wobble animation
- **Split labels** — on a win, animated callouts show "YOUR WINNINGS 60%" and "THE LORD'S TITHE 40%" so the player always understands where the money goes
- **The Reckoning** — two filling vials on the right track cumulative funds for "The Lord" and "Sin"; whichever reaches $2,000 first wins; vials shake progressively harder as they approach the limit
- **Living background** — stars drift, twinkle, and pulse independently for an ambient celestial feel

---

## Tech Stack

| Thing | Choice |
|---|---|
| Game engine | [Phaser 3](https://phaser.io/) via CDN |
| Language | Vanilla JavaScript — ES6 modules |
| Bundler | **None** — files are served directly |
| Canvas | 1600 × 900, FIT + CENTER scale mode |

---

## Running Locally

Browsers block ES6 module imports from `file://`, so serve over HTTP:

```bash
# Python (built-in)
python3 -m http.server 8080

# Node (npx)
npx serve .
```

Then open **http://localhost:8080** in any modern browser.

---

## Controls

| Action | Input |
|---|---|
| Spin | Click the **SPIN** button |
| Spin | Press **Space Bar** |

---

## Game Economy

| Setting | Value |
|---|---|
| Starting funds | $1,000 |
| Cost per spin | $50 |
| Win rate | ~1 in 15 spins |
| Win split | 60% to player · 40% to The Lord |
| Loss result | $50 added to the Sin total |

---

## Symbols & Payouts

Symbols are ordered by holiness. A three-of-a-kind match pays the symbol's full payout value.

| Symbol | Holiness | Payout |
|---|---|---|
| Dove | 1 | $200 |
| Joel Osteen | 2 | $300 |
| Holy Bible | 3 | $400 |
| Lamb of God | 4 | $500 |
| Crown of Thorns | 5 | $600 |
| Halo | 6 | $700 |
| The Cross | 7 | $900 |
| Jesus on Cross | 8 | $1,100 |
| Baby Jesus | 9 | $1,300 |
| Holy Grail | 10 | $1,500 |

---

## Project Structure

```
index.html              Loads Phaser 3 (CDN) and main.js as type="module"
main.js                 Phaser.Game config — registers all three scenes

state/
  GameState.js          Singleton: playerFunds, lordFunds, sinTotal, spinning flag

utils/
  RNG.js                shouldWin() (~1/15), pickResults() for reel targets

objects/
  Symbol.js             SYMBOLS array — id, label, holiness, payout, color
  Reel.js               Single reel: virtual infinite strip, pooled cells, sprite overlay
  SlotMachine.js        Orchestrates 3 reels; manages spin timing and result callback
  WinAnimation.js       Match banner → gold flash → arcing coin animation → split labels
  LossAnimation.js      Devil graphic rises to the Sin box
  MatchBanner.js        Full-screen "MATCH: 3× {label}" overlay with particle burst
  VialDisplay.js        Filling vial with shake escalation and winner detection

scenes/
  BootScene.js          Preloads assets (symbol spritesheet), then starts game
  GameScene.js          Background, stars, slot machine, vials, input handling
  UIScene.js            Overlay HUD: fund boxes (top), message + SPIN button (bottom)

assets/
  symbol_sprites.png    200×100 px spritesheet — 10 frames, one per symbol in order
```

---

## Scene Communication

Scenes communicate exclusively through the global Phaser event bus (`this.game.events`):

| Emitter | Event | Payload |
|---|---|---|
| UIScene | `spin` | — |
| GameScene | `win` | `{ playerGain, lordGain, symbol }` |
| GameScene | `loss` | `{ sinAdded }` |
| GameScene | `funds-updated` | — |
| GameScene | `spin-complete` | — |
| GameScene | `insufficient-funds` | — |
| VialDisplay | `vial-winner` | `{ winner }` |

---

## Sprite Sheet

`assets/symbol_sprites.png` must be a horizontal sprite sheet:

- **Frame size:** 200 × 100 px
- **Frame count:** 10 (one per symbol)
- **Frame order:** must match the `SYMBOLS` array order in `objects/Symbol.js` (Dove at index 0, Holy Grail at index 9)

---

## License

Do whatever you want with it. Probably don't build a real gambling site.
