Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions projects/sunwake-a6f3/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

# Local browser review output
qa/artifacts/
playwright-report/
test-results/
44 changes: 44 additions & 0 deletions projects/sunwake-a6f3/JOURNAL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Sunwake — journal

## Design direction

An original, one-button distance game about a solar skiff surfing an endless dune sea. Its signature is a live, layered landscape: an enormous low sun, painterly dunes, ancient arches, and a tiny turquoise sail leaving a ribbon of light. Dive downhill, release to soar, thread sun rings, and turn graceful landings into a solar burst.

Palette: ink plum #342c40, chalk #faf7f2, sand #f3ba7b, apricot #ef986f, rose #b86783, turquoise #75d8ce. Typography: locally hosted Bricolage Grotesque for the expressive display; DM Sans for controls and reading; monospace for live flight instruments. Layout: restrained warm page chrome, a dominant panoramic playable world, a compact mission strip, and an illustrated journey map. One strong visual moment instead of a dashboard of interchangeable cards.

## Ideas / backlog

- [x] Responsive one-button dune-surfing physics with touch and keyboard controls.
- [x] Illustrated procedural world with four distance-based regions.
- [x] Sparks, rings, clean landings, near misses, and solar bursts.
- [x] Voyage, seeded daily run, and endless practice modes.
- [x] Local records, missions, and earnable cosmetic skiffs.
- [x] Synthesized reactive audio, pause, reduced motion, and fullscreen.
- [x] Browser playtest on desktop and mobile; exact repository CI gate.
- [x] Original static 16:10 catalog thumbnail.
- [x] Prepare and verify a single-project release for the automatic PR publishing workflow.
- [ ] Future: optional shareable ghost replays and more skiff silhouettes.

## Session log

- 2026-09-24 (codex / gpt-6): Started Sunwake from the enforced Vite + React + TypeScript template. Planned an original momentum-driven sky-surfing game and a warm panoramic art direction.

## Shipped behavior and decisions

- A fixed 120 Hz simulation keeps the same seed and input consistent on 30/60/144 Hz displays. Hold accelerates down slopes and dives in air; release launches the skiff, and an open sail floats off dune crests.
- Sunlight depletes during Voyage/Daily and refills from pickups and clean landings. Rocks and electric storms take sunlight with a grace period. A 65-charge solar burst adds speed and protection for 2.8 seconds.
- Skilled timing extends runs: sampled simulations averaged roughly 2 km for idle/held play, 5 km for rhythmic dives, 7.5 km with bursts, and 10 km with slope timing. Three seconds of airtime is attainable by bursting near an apex.
- Region palettes change every 1,000 m and cycle endlessly. Portrait gameplay renders at least 900 logical units of course width so hazards have useful lead time. A pennant marks the player's previous personal horizon.
- Daily seeds and records use UTC dates captured at run start. Free flight tracks lifetime journeys only, preserving the meaning of earned records and unlocks. The first three incomplete missions are eligible per run; settlement runs exactly once.
- Progress survives reloads with validated local saves, and blocked storage falls back to the session with honest UI feedback. Skiffs are cosmetic. Fonts are local with OFL licenses; no runtime services or downloaded game assets are required.
- Sound is gesture-unlocked, speed-reactive, and muted by default. Web Audio resources are bounded and recreated after StrictMode cleanup. Focus loss pauses the run; reduced motion removes shake and speed streaks.

## Validation

- Exact repository gate passes: scope, conformance, frozen pnpm install, ESLint, TypeScript/Vite build, relative deployment assets, and static 16:10 thumbnail.
- Engine checks pass: input transitions, stable frame rates, pause, seeded layouts, pickup/damage/burst collisions, sunlight depletion, advanced airtime, and bounded four-minute practice simulation.
- 12 progress checks pass: corrupt saves, invalid numbers, unavailable storage, ownership validation, mission eligibility, once-only mission rewards, daily dates/seeds, and practice isolation.
- Seven production browser scenarios pass: keyboard/pointer run lifecycle, saved settlement, deterministic daily course, practice isolation, hangar purchases, 320/390 px real touch play, and sound/pause/reload controls. No console errors or horizontal overflow.
- Visually reviewed desktop/mobile welcome, flight, results, and region palettes; loaded the built game at a nested static-server subpath without failed asset requests. Browser screenshots stay ignored in `qa/artifacts/`.

- 2026-09-24 (codex / gpt-6): Built and balanced the complete Sunwake game, refined mobile lookahead and dark-region contrast, verified persistence and reward isolation, and passed the exact publication gate. Release is ready for the automatic pull-request workflow.
67 changes: 67 additions & 0 deletions projects/sunwake-a6f3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Sunwake

**Ride the last light.** An original, momentum-driven distance game about a solar skiff surfing an endless sea of dunes.

Hold to dive into a downhill slope. Release to launch. Thread sun rings, collect light, and turn clean landings into a solar burst. Four illustrated regions cycle across an endless, seeded landscape.

## Play

| Action | Keyboard | Touch / mouse |
| --- | --- | --- |
| Dive | Hold Space or ↓ | Hold the canvas or dive button |
| Soar | Release | Release |
| Solar burst | Shift or ↑ | Burst button when charged |
| Pause / resume | P or Escape | Pause / resume button |
| Start / retry | Space; R after a run | Let’s fly / One more horizon |

- **Voyage:** a fresh course each run, sunlight management, personal records and challenge rewards.
- **Daily flight:** the same course for the same UTC date, with a personal daily record. No online leaderboard or account.
- **Free flight:** unlimited sunlight for practice. Counts toward lifetime flight count and distance, but does not award light, challenges, or competitive records.
- **The hangar:** spend earned light on Manta and Comet. All three skiffs share the same physics.

Progress is stored on this device with `localStorage`. If browser storage is blocked, the game still works for the current session. Audio starts only after a gesture and can be muted. The game pauses on focus loss and follows the system’s reduced-motion preference.

## Develop

```sh
pnpm install
pnpm dev
```

The production build uses relative asset URLs and needs no external runtime services, font requests, images, or audio downloads. Fonts and their OFL licenses are bundled in `public/fonts`. Artwork is drawn with canvas and static SVG; audio is synthesized with Web Audio.

## Verify

From this folder:

```sh
pnpm lint
pnpm build
node qa/engine-check.mjs
node --test tests/progress.test.mjs
```

Engine checks require Node 24+ native TypeScript support. They cover stable results at 30/60/144 Hz, seeded courses, input transitions, collision grace, charge use, attainable airtime, and bounded effects during long runs. Progress checks cover malformed saves, unavailable storage, reward eligibility, daily records, and practice isolation.

For browser checks, install a Playwright Chromium browser (`pnpm exec playwright install chromium`) and serve the built app with `pnpm preview --host 127.0.0.1 --port 4179`. Then:

```sh
SUNWAKE_URL=http://127.0.0.1:4179 node qa/browser-check.mjs
```

Set `CHROMIUM_PATH` if using an existing browser executable. The suite exercises the real canvas loop and React UI, including desktop and touch controls, pause/resume, once-per-run rewards, saved purchases, repeatable daily flights, dialogs, and narrow-screen layouts. Local screenshots are ignored under `qa/artifacts/`.

The exact repository publishing gate, from the repository root:

```sh
node scripts/verify-project.mjs sunwake-a6f3
```

## Code map

- `src/game/engine.ts`: fixed-timestep physics, terrain, seeded pickups and hazards, collision handling, and scoring.
- `src/game/renderer.ts`: layered landscapes, skiffs, effects, region transitions, and the personal-best flag.
- `src/game/progress.ts`: validated saves, challenges, daily seeds, and cosmetic unlocks.
- `src/game/audio.ts`: gesture-unlocked wind, ambient tones, and event sounds.
- `src/App.tsx`: menus, input, HUD, results, accessibility, and persistence integration.
- `JOURNAL.md`: design decisions, shipped work, and future ideas.
22 changes: 22 additions & 0 deletions projects/sunwake-a6f3/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'

export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
globals: globals.browser,
},
},
])
15 changes: 15 additions & 0 deletions projects/sunwake-a6f3/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#faf7f2" />
<meta name="description" content="Ride the last light. An endless dune-surfing odyssey: dive, soar, and chase a new horizon in Sunwake." />
<link rel="icon" type="image/svg+xml" href="./favicon.svg" />
<title>Sunwake — Ride the last light</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
33 changes: 33 additions & 0 deletions projects/sunwake-a6f3/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "sunwake",
"private": true,
"version": "0.0.0",
"type": "module",
"packageManager": "pnpm@11.5.2",
"scripts": {
"preinstall": "npx only-allow pnpm",
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.2.6",
"react-dom": "^19.2.6"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@playwright/test": "^1.63.0",
"@types/node": "^24.12.3",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^10.3.0",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.6.0",
"typescript": "~6.0.2",
"typescript-eslint": "^8.59.2",
"vite": "^8.0.12"
}
}
Loading
Loading