73 lines
3.7 KiB
Markdown
73 lines
3.7 KiB
Markdown
# Newbury Nights
|
|
|
|
A fan-made AR ghost-hunting web app — a tribute to the AR mechanics of the LEGO® Hidden Side™ app. The focus is the **Hunter Mode** AR loop: raise your phone as a ghost detector, scan the colour wheel to uncover gloom, lock on, and blast ghosts down before your battery dies.
|
|
|
|
> Fan-made tribute. Not affiliated with, sponsored by, or endorsed by the LEGO Group. LEGO® and Hidden Side™ are trademarks of the LEGO Group.
|
|
|
|
## What's inside
|
|
|
|
- **Backend** — Node + Express 5, SQLite via `better-sqlite3`, bcrypt password hashing, JWT auth (8h), multer image uploads.
|
|
- **Frontend** — Three.js (ES module import maps via CDN), `getUserMedia` camera passthrough with `DeviceOrientation` gyro look (iOS Safari friendly), `BarcodeDetector` QR scanning with a manual code fallback.
|
|
- **Ghost rendering** — animated-GIF billboards (texture pumped via `texture.needsUpdate` each frame) when a ghost has an uploaded image; procedural Three.js wisp meshes otherwise.
|
|
- **Spawning** — rarity-weighted: ★ common down to ★★★★ legendary.
|
|
- **Admin panel** (`/admin`) — JWT-gated. Upload / enable / disable / delete ghost images, create sets with scan codes linked to ghost rosters (many-to-many via `set_ghosts`).
|
|
- **Public endpoint** — `GET /api/scan/:code` requires no auth and returns a set's ghost roster.
|
|
|
|
## Data
|
|
|
|
The ghost roster, stats, abilities, and boss→set references are seeded from `data/*.json`, which are generated from the source spreadsheet by `scripts/extract_ghosts.py`. Three ghost types (red / yellow / blue) and four rarity tiers drive the colour-wheel and damage mechanics.
|
|
|
|
To regenerate the JSON from a spreadsheet:
|
|
|
|
```bash
|
|
python3 scripts/extract_ghosts.py path/to/Ghost_Data.xlsx data
|
|
```
|
|
|
|
## Setup
|
|
|
|
Requires Node 18+ and `build-essential` on Ubuntu (so `better-sqlite3` can compile its native module):
|
|
|
|
```bash
|
|
sudo apt-get install -y build-essential
|
|
npm install
|
|
cp .env.example .env # then edit JWT_SECRET and admin creds
|
|
npm run seed # creates the DB, seeds ghosts/sets, bootstraps admin
|
|
npm start
|
|
```
|
|
|
|
The app listens on `PORT` (default 3000) over plain HTTP — **nginx terminates HTTPS in front of it**. `app.set('trust proxy', 1)` is set so secure cookies work behind the proxy.
|
|
|
|
Re-running `npm run seed` is safe: it only seeds ghost/set data when the `ghosts` table is empty, so it won't clobber admin edits. It always ensures an admin user exists.
|
|
|
|
## nginx
|
|
|
|
See `deploy/nginx.conf.example` for a reverse-proxy block. Camera and gyro APIs require a secure context, so the site must be served over HTTPS (which nginx already handles for you).
|
|
|
|
## API quick reference
|
|
|
|
Public:
|
|
- `GET /api/scan/:code` — set roster + boss for a scan code
|
|
- `GET /api/freehunt?n=&type=` — rarity-weighted random spawns
|
|
- `GET /api/ghosts?type=&rarity=&boss=` — public ghost index (enabled only)
|
|
- `GET /api/abilities` — ability reference
|
|
|
|
Auth:
|
|
- `POST /auth/login` · `POST /auth/logout` · `GET /auth/me` · `POST /auth/change-password`
|
|
|
|
Admin (JWT required):
|
|
- `GET/POST /api/admin/ghosts`, `PATCH/DELETE /api/admin/ghosts/:id`, `POST /api/admin/ghosts/:id/image`
|
|
- `GET/POST /api/admin/sets`, `PATCH/DELETE /api/admin/sets/:id`, `PUT /api/admin/sets/:id/roster`
|
|
|
|
## Project layout
|
|
|
|
```
|
|
server.js Express app + static hosting
|
|
db/index.js SQLite schema + connection
|
|
routes/ auth, public api, admin api, auth middleware
|
|
scripts/extract_ghosts.py xlsx -> data/*.json
|
|
scripts/seed.js seed DB from data/*.json + bootstrap admin
|
|
data/ ghosts.json, abilities.json, sets.json
|
|
public/ index.html (game), admin.html, css/, js/
|
|
uploads/ uploaded ghost billboards (gitignored)
|
|
```
|