77 lines
9.6 KiB
Markdown
77 lines
9.6 KiB
Markdown
# Crystal
|
|
|
|
A native web port of Pokémon Crystal, translated from [PRET's Game Boy disassembly](https://github.com/pret/pokecrystal). The game runs in TypeScript and reads graphics, maps, sprites and dialogue from the user's own ROM. No original game assets are included.
|
|
|
|
**Current milestone: from the opening to Route 30.** Play in a minimal Crystal-style interface with Game, Savegames, Map, Pokédex, Link and Friends tabs. The native canvas fills the play area, and the live local map follows your position. Save, export and import work now. Pokédex, Link and Friends show their planned scope without placeholder data.
|
|
|
|
Walk around your home, the town and its buildings at the original walking speed. Mom gives you the Pokégear and asks the day of the week. The teacher stops you from leaving town. Your rival shoves you away from the lab window. Professor Elm introduces his errand, and you choose Chikorita, Cyndaquil or Totodile. The Pokémon's picture and stats come from your ROM, and you can nickname it. Elm's aide then gives you a Potion. These scenes run the game's own event scripts and movement data, with native choices and naming.
|
|
|
|
With a Pokémon, head west along Route 29 to Cherrygrove City. Wild Pokémon appear in the grass and battle you with their original moves. The route has ledges, signs, an item ball and a Berry tree that regrows daily. Townspeople wander and look around as in the original. In Cherrygrove, the Guide Gent gives a tour, the Poké Mart buys and sells items, the Pokémon Center nurse heals your party, and a whiteout returns you to the last Pokémon Center you entered.
|
|
|
|
North of Cherrygrove, Route 30's trainers battle you. Bug Catcher Don spots you from a distance and walks over; Youngster Mikey battles when you talk to him. Each sends out two Pokémon, and beaten trainers remember you. The Berry house gives a Berry, and a Potion is hidden on the route.
|
|
|
|
Mr. Pokémon gives you the Mystery Egg, and Professor Oak gives you the Pokédex. Your rival challenges you in Cherrygrove on the way back, and Elm's aide gives you Poké Balls. Back on Route 29, the Dude shows you how to catch Pokémon. From then on, open PACK in a wild battle to throw a Poké Ball. Caught Pokémon join your party, and you can give them a nickname. In battle, PKMN switches Pokémon or opens a Pokémon's stats, and when one faints you choose the next. PACK's Items pocket uses healing items, revives, Ethers, X items and Poké Dolls.
|
|
|
|
Start opens the start menu. POKéMON shows your party: open a Pokémon's stats, change the order, rearrange its moves, or give and take held items. PACK shows your pockets: use Potions, status healers, revives, Ethers, PP Ups, vitamins, Rare Candy and evolution stones, give or toss items, and ride the Bicycle. Pokémon evolve when they level up after a won battle (press B during the flashes to stop it), and learn new moves outside battle too. Field moves work from the party menu too: Cut, Surf, Strength, Flash, Whirlpool, Waterfall, Sweet Scent, and Softboiled or Milk Drink to share HP. Fly opens the Fly map to choose a town you have visited, Teleport returns to the town of the last Pokémon Center you entered, and Dig or an Escape Rope leads out of a cave through the entrance you last used. In PACK, SEL registers a key item such as the Bicycle, and Select then uses it. POKéGEAR shows the clock, the Town Map card with a cursor over every landmark, and the phone: call Mom or Professor Elm. Call Mom after the catching lesson and she offers to save some of your trainer prize money. Once the Mystery Egg is delivered, talk to her at home to save or take money, or to switch Daylight Saving Time; with enough saved, she buys items and dolls for you and calls to say so. Your name in the start menu opens the trainer card: your ID, money, Pokédex count and play time, then the Johto leaders with the badges you have earned. OPTION sets the battle style (SET skips the offer to switch when a trainer sends out the next Pokémon) and the menu account (the start menu's descriptions); text speed, battle scene, sound, print and frame are kept for later. Bill's PC can move Pokémon between the party and any box. The poster opens the Town Map.
|
|
|
|
The Pokémon Center PC opens Bill's PC, your own PC and, once you have the Pokédex, Professor Oak's rating. Deposit, withdraw and release Pokémon, and switch or name boxes. Store, withdraw or toss items. With six Pokémon in your party, a catch goes to the current box. When that box fills up, Bill calls you. The bedroom PC also lets you arrange owned decorations.
|
|
|
|
Route 31 and beyond, trade evolutions, the Pokégear's radio, the Pokédex screen, Headbutt and Rock Smash, MAIL, Kurt's special balls and audio are still being ported. Unported neighboring areas appear at the edges but cannot be entered yet.
|
|
|
|
## Run
|
|
|
|
Requires Node.js 22.12 or newer.
|
|
|
|
```sh
|
|
npm ci
|
|
npm run dev
|
|
```
|
|
|
|
Open the printed local URL and select your uncompressed **English US/Europe revision 1.1** ROM (`.gbc`, 2 MiB). Its checksum is verified locally. ROMs are never uploaded or stored in browser storage; select yours again after reloading to resume. A new game starts with Professor Oak's introduction, where you choose your character, set the clock and pick a name.
|
|
|
|
Use onscreen controls, or focus the game screen and use arrows to move, **X** to interact and **X / Z** to continue dialogue. In choices, **Up / Down** change the answer, **X** confirms and **Z** answers No. Sessions save every 10 seconds and when paused. Switching apps pauses play. Download a backup before clearing browser data.
|
|
|
|
Native saves are separate from emulator snapshots and cartridge `.sav` files. Saving during a scene keeps the progress from before it, so the scene replays after loading. The time you give Oak is kept as an offset from the device clock (the steppers start at the device's time), and the weekday you give Mom as an offset from that clock's weekday. The tile counter measures in-game movement, not real-world steps.
|
|
|
|
## Reference emulator
|
|
|
|
During development, open **`/reference.html`** to run the original game for comparison. It has separate saves and does not run inside the native player. Reference tools and the emulator are excluded from the default build.
|
|
|
|
```sh
|
|
npm run build # Native application in dist/
|
|
npm run check:release # Check the native distribution
|
|
npm run build:reference # Optional reference in dist-reference/
|
|
```
|
|
|
|
Serve `dist/` through an HTTPS static host, or use `npm run preview` locally. Subdirectory hosting is supported with `npm run build -- --base=/Crystal/`. Use HTTPS from another device; ROM verification and save locking require a secure context.
|
|
|
|
## Development
|
|
|
|
```sh
|
|
npm test
|
|
npx playwright install chromium webkit
|
|
npm run test:e2e
|
|
npm run check:vendor
|
|
```
|
|
|
|
Local-ROM tests use `Pokemon - Crystal Version (UE) (V1.1) [C][!].gbc` at the root, or `CRYSTAL_ROM_PATH`. They skip when absent; no test downloads a ROM. Game files, private reference builds and gameplay screenshots must remain ignored.
|
|
|
|
[CI checks](https://git.azuze.fr/kawa/Crystal/actions?workflow=checks.yml) run on pushes and pull requests, or manually from Actions. The [workflow](.gitea/workflows/checks.yml) uses the shared `docker-build` runner with Node 22 in a Debian container. It installs from the lockfile, runs synthetic unit and Chromium/WebKit browser tests, builds both applications, and checks vendor integrity and native release isolation. `CRYSTAL_ROM_PATH` points to a verified absent file, so ROM-dependent tests skip; `check:reference` runs separately with the private ROM and reference build.
|
|
|
|
[Private ROM checks](.gitea/workflows/private-checks.yml) also run on pushes to `main`, or manually with `main` selected. Set the repository Actions secret `CRYSTAL_ROM_URL` to a direct HTTPS download URL accessible to the runner. The job rejects redirects, verifies the 2 MiB Crystal 1.1 SHA-1, builds the pinned PRET reference with RGBDS 1.0.3, runs `check:reference`, then runs unit/playthrough and Chromium/WebKit gameplay/reference tests. The URL is exposed only to the download step. No ROM, reference build, save or gameplay artifact is uploaded or cached; private files and browser output are removed even after test failure. Pull requests retain the synthetic workflow. A missing or inaccessible secret fails the private job instead of silently skipping its tests. Manual-device and visual acceptance cards still require their recorded checks.
|
|
|
|
To reproduce the CI test selection locally:
|
|
|
|
```sh
|
|
CRYSTAL_ROM_PATH=/nonexistent/crystal-ci.gbc npm test -- --maxWorkers=2
|
|
CRYSTAL_ROM_PATH=/nonexistent/crystal-ci.gbc npm run test:e2e -- --workers=2
|
|
```
|
|
|
|
`git config core.hooksPath .githooks` keeps the forge's project board in step with commits: `Closes #N` in a message moves card N to Fait and closes it, any other `#N` moves it to En cours, and a commit on `main` without a reference adds a closed card to Fait. `[no-card]` skips a commit. See [project-board.mjs](scripts/project-board.mjs).
|
|
|
|
The [full port plan](docs/PLAN.md) covers remaining game systems, save slots, the world map, Pokédex, internet Link, friends and optional mobile step support for egg hatching. See the [architecture](docs/ARCHITECTURE.md) and [routine map and verification](docs/NATIVE_PORT.md) for module boundaries and reference checks.
|
|
|
|
References: PRET [documentation](https://pret.github.io/pokecrystal/), [wiki](https://github.com/pret/pokecrystal/wiki) and [source](https://github.com/pret/pokecrystal). The development reference uses [binjgb](https://github.com/binji/binjgb) under its [MIT license](reference/public/vendor/binjgb/LICENSE); see [provenance](reference/public/vendor/binjgb/PROVENANCE.md).
|
|
|
|
Independent project. Not affiliated with Nintendo or Game Freak.
|