npm package: @jingx/part-six36
Description: React package for the 3D lottery machine runtime.
Latest version: 0.1.4

# 3D Lottery Machine

Browser-based 3D lottery draw built with CreateJS, Cannon.js, and Three.js. The app loads a 3D lottery drum, simulates 36 numbered balls, and extracts 6 preset numbers after a short countdown.

## Overview

This project is a canvas-based browser game with a lightweight static front end:

- `index.html` loads the static assets and module entrypoint.
- `js/bootstrap.js` builds the runtime config passed into `CMain` and starts the app.
- `js/appConfig.js` contains the live draw and startup configuration.
- `js/CMain.js` handles preload, asset registration, and game startup.
- `js/CGame.js` controls countdown, shuffling, extraction, and reset.
- `js/CScenario.js` builds the physics world and loads the machine mesh from `models/sphere_machine.txt`.
- `js/CBall.js` renders and animates each numbered ball sprite.
- `js/settings.js` stores canvas, camera, physics, and gameplay constants.

## Tech Stack

- CreateJS / EaselJS for stage management, sprites, and tweens
- Cannon.js for physics simulation
- Three.js plus `FBXLoader` for loading the lottery machine geometry
- Howler.js for audio playback
- jQuery and helper utilities from `js/lib/`

## Current Game Flow

The current implementation is not a generic draw engine yet. It runs a fixed sequence defined in `js/appConfig.js` and passed through `CMain` into `CGame`:

- `TOTAL_NUMBERS = 36`
- `NUMBERS_TO_PICK = 6`
- `COUNTDOWN` is set to `5`
- `RESULT_NUMBERS` is set to `[12, 13, 14, 15, 16, 17]`

After assets load, the game starts automatically, counts down, shuffles the balls, extracts those preset numbers in order, then resets and starts the loop again.

## Running Locally

Use an HTTP server. Do not rely on opening `index.html` directly from the filesystem.

### Option 1: Simple Static Server

```bash
cd /path/to/3d-game-v1
python3 -m http.server 8080
```

Then open `http://localhost:8080`.

### Option 2: Vite

`package.json` includes Vite scripts:

```bash
npm install
npm run dev
npm run build
npm run build:react-package
npm run build:all
npm run preview
```

The current local Node version in this workspace is `v18.18.0`, so the repo is pinned below Vite 7. That is deliberate: Vite 5 and 6 support Node 18 / 20+, while Vite 7 requires Node 20.19+ or 22.12+.

If you only want the dev server:

```bash
npm start
```

React package compatibility:

- `@jingx/part-six36` supports React `16.8+`, `17`, `18`, and `19`

## React Integration

This repo now includes a React package around the game runtime.

Current architecture:

- the game still supports the original standalone page runtime
- the React package now exports both a native component and an iframe wrapper
- communication is handled through props and callbacks

The native component lives in [src/react/LotteryMachine.js](/Users/broschign/Documents/createjs/3d-game-v1/src/react/LotteryMachine.js), the iframe wrapper lives in [src/react/LotteryMachineFrame.js](/Users/broschign/Documents/createjs/3d-game-v1/src/react/LotteryMachineFrame.js), and the package build uses [vite.react-package.config.js](/Users/broschign/Documents/createjs/3d-game-v1/vite.react-package.config.js).

### Package Build

Build the standalone game and the React wrapper:

```bash
npm run build:all
```

Or build only the wrapper package:

```bash
npm run build:react-package
```

Run the local Vite consumer example:

```bash
npm run example:install
npm run example
```

Build the local Vite consumer example:

```bash
npm run example:build
```

Wrapper output:

- `dist-react/lottery-machine-react.js`
- `dist-react/lottery-machine-react.cjs`
- emitted runtime assets in `dist-react/` for sprites, sounds, fonts, and the model

From `@jingx/part-six36@0.1.4` onward, the React package is shipped as a self-contained runtime bundle. Sprites, sounds, fonts, and the model are inlined into the package build, so consuming apps do not need to copy `dist-react/` into their own `public/` folder and older webpack-based React apps do not need to parse `import.meta.url` from the package.

Package export:

```js
import {LotteryMachine, LotteryMachineFrame} from "@jingx/part-six36";
```

### Install In Your React App

Local install from this repo:

```bash
npm install /absolute/path/to/3d-game-v1
```

Or in your React app `package.json`:

```json
{
  "dependencies": {
    "@jingx/part-six36": "file:../3d-game-v1"
  }
}
```

Exact import shape in the React app:

```js
import {LotteryMachine} from "@jingx/part-six36";
```

### Native React Component

Use `LotteryMachine` if you want the lottery canvas to render directly inside your React tree without an `iframe`.

Example:

```jsx
import {LotteryMachine} from "@jingx/part-six36";

export function LotteryMachinePanel() {
  return (
    <LotteryMachine
      countdown={10}
      resultNumbers={[12, 13, 14, 15, 16, 17]}
      onReady={(payload) => console.log("ready", payload)}
      onRoundComplete={(payload) => console.log("done", payload)}
      style={{width: 640, height: 640}}
    />
  );
}
```

A full consumer example is included in [LotteryMachineConsumerExample.jsx](/Users/broschign/Documents/createjs/3d-game-v1/examples/react/LotteryMachineConsumerExample.jsx).

There is also a runnable Vite app in [examples/react-app/package.json](/Users/broschign/Documents/createjs/3d-game-v1/examples/react-app/package.json). It consumes `@jingx/part-six36` through a local `file:../..` dependency, uses React `16.14.0`, and forces a fresh Vite dev cache on startup so package rebuilds are picked up cleanly.

Because that example uses a local `file:` dependency outside the app root, its Vite config also allows the repo root in `server.fs.allow`. That setting is needed for local linked development; it is not the normal requirement for a published npm install.

Notes:

- the container must have a real width and height
- the component observes container resizes and rescales the canvas to fit that box
- the native component currently supports only one mounted instance at a time because the legacy engine still uses shared globals
- if you need multiple simultaneous machines, use `LotteryMachineFrame`

### iframe Wrapper

Use `LotteryMachineFrame` if you want stronger isolation or need multiple embeds.

Example:

```jsx
import {LotteryMachineFrame} from "@jingx/part-six36";

export function LotteryMachineScreen() {
  return (
    <LotteryMachineFrame
      gameUrl="/lottery-machine/index.html"
      countdown={10}
      resultNumbers={[12, 13, 14, 15, 16, 17]}
      onReady={(payload) => console.log("ready", payload)}
      onRoundComplete={(payload) => console.log("done", payload)}
      style={{width: "100%", height: "100%", border: 0}}
    />
  );
}
```

### Host The Standalone Game Assets

`LotteryMachineFrame` needs a URL for the actual game page.

Typical flow:

1. Run `npm run build`
2. Host this repo's `dist/` output inside your React app's public assets, for example at `/lottery-machine/`
3. Point the wrapper's `gameUrl` prop to that hosted page

Example hosted URL:

```text
/lottery-machine/index.html
```

### Why iframe Still Exists

`LotteryMachineFrame` is still useful because:

- this project still uses a legacy global runtime internally
- `iframe` keeps those globals isolated from your React app
- it allows multiple simultaneous embeds
- it works well when the standalone page is already hosted elsewhere

### Initial Values Through URL

The runtime supports query parameters:

- `countdown`
- `results`

Example:

```text
/lottery-machine/index.html?countdown=10&results=12,13,14,15,16,17
```

### Live Updates From React

The embedded page listens for:

```js
{
  type: "LOTTERY_MACHINE_UPDATE_DRAW",
  payload: {
    countdown: 10,
    resultNumbers: [12, 13, 14, 15, 16, 17]
  }
}
```

`resultNumbers` should contain exactly 6 integers in the `1-36` range.

The embedded page also exposes `window.LotteryMachine` with:

- `updateDraw({ countdown, resultNumbers })`
- `getDrawConfig()`

### Result Complete Event To React

When the last ball fully reaches the end of the tube and stops, the embedded page sends this message to the parent:

```js
{
  type: "LOTTERY_MACHINE_ROUND_COMPLETE",
  payload: {
    resultNumbers: [12, 13, 14, 15, 16, 17],
    releasedSum: 87,
    countdown: 10
  }
}
```

This is posted with `window.parent.postMessage(...)`.

### Behavior Notes

- if React sends a new countdown before extraction starts, the game resets its countdown to the new value
- if React sends new values while balls are already being extracted, the new config is applied to the next round
- `resultNumbers` must contain exactly 6 integers in the `1-36` range

## Configuration

The main places to change behavior are:

- `index.html`
  - Static page shell and script loading order
- `js/appConfig.js`
  - Active `COUNTDOWN`
  - Active `RESULT_NUMBERS`
  - `CMain` startup flags such as `fullscreen` and `audio_enable_on_startup`
- `js/settings.js`
  - Canvas size
  - Camera settings
  - Physics constants
  - Shuffle timing
  - `TOTAL_NUMBERS` and `NUMBERS_TO_PICK`
- `js/CLang.js`
  - UI text such as the start label

## Project Structure

```text
.
├── index.html
├── package.json
├── css/
├── js/
│   ├── appConfig.js
│   ├── bootstrap.js
│   ├── CMain.js
│   ├── CGame.js
│   ├── CScenario.js
│   ├── CBall.js
│   ├── CPreloader.js
│   ├── CInterface.js
│   ├── CLang.js
│   ├── settings.js
│   └── lib/
├── src/
│   └── react/
├── models/
│   └── sphere_machine.txt
├── sounds/
└── sprites/
```

## Notes for Maintainers

- The countdown and draw results in `js/settings.js` are legacy defaults only. Live values now come either from `js/bootstrap.js` / `js/appConfig.js` in standalone mode or from React props in the package runtime.
- `vite.config.js` copies the legacy runtime directories into `dist/` during build because the current game still loads many assets by string path at runtime instead of module import.
- `src/react/legacyRuntime.js` packages the legacy scripts with raw imports and emits a manifest for sprites, sounds, models, and fonts so the native React component can resolve assets without hardcoded public paths.
- `js/CInterface.js` exists, but the current game flow does not instantiate it.
- `js/CGame.js` creates a start-extraction toggle and immediately unloads it, so the present behavior is effectively auto-start only.
- `LotteryMachine` is a direct React mount, but it still wraps a legacy singleton engine under the hood. Only one native instance is supported at a time.
- There is no automated test suite configured in `package.json`.

## Assets

- `sprites/` contains UI, background, and ball sprite sheets
- `sounds/` contains panel, shuffle, extraction, and soundtrack audio
- `models/sphere_machine.txt` is an FBX text export of the lottery machine mesh