# SKILL.md — well_game.sh v2 Game Development Guide

> Every game is ONE standalone HTML page (`games/<id>.html`) on the v2 shell.
> Quality bar: vector art, animations everywhere, RU/EN, zero emoji, 500+ lines.

## 1. File structure (mandatory, exact order)

```html
<!DOCTYPE html>
<html lang="ru" data-theme="terminal">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>RU-NAME — well_game.sh</title>
<meta name="description" content="1-2 real sentences, no lorem"/>
<meta name="author" content="Wellbou"/>
<meta name="well_game:slug" content="<id>"/>
<meta name="well_game:title" content="RU-NAME"/>
<meta name="well_game:category" content="arcade|puzzle|action|strategy|nokia|casual"/>
<meta name="well_game:engine" content="<engine-name>2"/>
<meta name="well_game:version" content="2.0.0"/>
<meta name="well_game:difficulty" content="1-5"/>
<link rel="icon" href="data:image/svg+xml,..."/>  <!-- UNIQUE vector glyph per game -->
<link rel="stylesheet" href="wg.css"/>
</head>
<body>
<div id="wgStagePh"></div>
<canvas id="cv"></canvas>   <!-- fixed logical size; CSS scales it. DOM games: also <div id="ui"> -->
<script>
window.WG_META = {
  slug: '<id>', category: '<cat>',
  title: { ru: '..', en: '..' },
  desc: { ru: '1-2 real sentences', en: '1-2 real sentences' },
  controls: { ru: '..', en: '..' },
  author: 'Wellbou'
};
window.WG_STR = { key: { ru: '..', en: '..' } };  <!-- custom strings -->
</script>
<script src="wg.js"></script>
<script> /* game, see §3-7 */ </script>
</body>
</html>
```

Rules: NO `game-chrome.js`. NO emoji anywhere (canvas = vector only;
allowed text glyphs: `▲▼◀▶★☆→←↑↓·—«»`). Filename must equal registry `file`.
`slug` must equal filename without `.html`.

## 2. Shell API (`window.WG`) — provided by `wg.js`, free for every game

| API | Purpose |
|---|---|
| `WG.t(k)` / `WG.tt(k)` / `WG.L(v)` | shell strings / game strings (`WG.setStrings({...})` first) / localize `{ru,en}` |
| `WG.report(score)` | header score (auto bump anim) + postMessage to portal |
| `WG.saveBest(s)` → bool | persists `wg:best:<slug>`, returns true on new record |
| `WG.over(title,msg,score)` / `WG.hideOver()` | animated game-over card / hide |
| `WG.toast(msg)` | slide-up toast |
| `WG.dpad(true)` | touch gamepad (**touch devices only** — shell hides it on desktop automatically) |
| `WG.togglePause` / `WG.autoPause` / `WG.restart` | **you assign these**; shell buttons + P/R call them |
| `WG.noShellKeys=true` | set when your game types letters (F must not fullscreen) |
| `WG.tw.add({dur,ease,update(k),done})` | tween manager — call `WG.tw.update(dt)` every frame |
| `WG.Easing` | linear/outCubic/inOut/outBack/outElastic |
| `WG.shake(a)` / `WG.shakeUpdate(dt)` / `WG.shakeX/Y()` | screen shake — wrap world draw in `translate(shakeX(),shakeY())` |
| `new WG.Particles()` | `.burst(x,y,{n,colors,speed,decay,size,gravity})`, `.trail(x,y,c)`, `.update(dt)`, `.draw(ctx)` |
| `new WG.Backdrop('grid'/'stars')` | `.update(dt,w,h)`, `.draw(ctx,w,h,accent)` animated background |
| `WG.passParams(href)` | link builder preserving `?theme=&lang=` |

Shell gives you free: header (title/badge/score/best/pause/restart/**fullscreen=game-only**/new-tab/copy-link/hide-UI/home),
scrollable info section (desc/author/controls from YOUR meta), star rating,
sidebar "other games" (from registry, same category first), footer, F/Esc keys,
focus-on-load, `?theme=` (10 themes) / `?lang=ru|en` / `?embed=1`.

## 3. Mandatory per-game feature list (all must exist)

1. **Welcome screen**: vector LOGO (emblem + name on canvas) + best + controls hint
   + achievements count + `Space/tap` to start. Game must NEVER act before start.
2. **Animations everywhere**: particles on every event, shake on hits, tweens for
   banners/popups/spawns, animated backdrop, idle motion on ALL sprites
   (pulse/sine/bob), interpolated movement (never teleport), spawn scale-in,
   death explosion, floating score text, animated state transitions.
   A static frame must be impossible.
3. **Internal levels + level GUI**: progression inside the file (waves/tiers/zones/stages)
   + visible level indicator + progress bar. Discrete stages → picker with stars
   (`wg:stars:<slug>:<n>`, locked until previous beaten) + digit shortcuts.
4. **RU/EN everything** via `WG_STR` + `tt()/t()`.
5. **Achievements**: exactly 3 per game (`wg:ach:<slug>:<id>`), toast on unlock,
   count on welcome. Pattern:
   `function ach(id){var k='wg:ach:'+WG.slug+':'+id;try{if(localStorage.getItem(k))return;localStorage.setItem(k,'1');var a=...;WG.toast((WG.lang==='ru'?'Достижение: ':'Achievement: ')+WG.L(a));}catch(e){}}`
6. **Pause**: assign `WG.togglePause` + `WG.autoPause` (P/button/tab-hide), `WG.restart` (R).
7. **Input**: touch-first (tap/drag/swipe, `touch-action:none`), keyboard full
   (`arrows`/`WASD`, `Space`/`Enter` start+act, digits for menus, `R`/`P`/`M`),
   board games need an arrow-key cursor + Enter. **Zero-click keyboard play**:
   focus, Space, arrows, Enter must reach gameplay without a mouse.
8. **Sound**: tiny WebAudio synth, `M` mute. Nokia games: square-wave beeps.
9. **Size**: minimum 500 lines (`wc -l`) of REAL content (art, animation, levels,
   sounds, GUI). No filler.
10. **Correctness**: solvable/fair/winnable, spawn safety, forgiving hitboxes,
    restart always works, no `alert/confirm/prompt`, no external assets.

## 4. Art direction — vector only, zero emoji

- Shapes: rounded rects, arcs, gradients (2-stop linear), glow (`shadowBlur`
  sparingly — perf!), grid/star backdrops, dotted aim lines, rings, trails.
- Sprites need faces/eyes/details drawn with arcs — never emoji.
- Nokia games: `#9bbc0f/#8bac0f/#306230/#0f380f`, low-res canvas,
  `image-rendering:pixelated`, `.nokia-screen` class pattern.
- Text: monospace only. HUD minimal (shell shows score/best).

## 5. Adding the game to the portal

1. Write `games/<id>.html` per this spec.
2. Append to `games/registry.json`:
   `{"id","name","name_ru","icon"(*-free: use "" or category glyph — portal uses screenshots*),"category","description","description_ru","controls","controls_ru","tags","author":"Wellbou","file":"games/<id>.html"}`.
   (`icon` is legacy; cards render `assets/shots/<file>.png`.)
3. Verify (§6), screenshot (§7), open a PR.

## 6. Verification (every file, every time)

```bash
# syntax of all inline scripts
python3 -c "import re;s=open('games/X.html').read();m=re.findall(r'<script(?![^>]*src=)[^>]*>(.*?)</script>',s,re.S);open('/tmp/c.js','w').write(chr(10).join(m))" && node --check /tmp/c.js
wc -l games/X.html   # >= 500
grep -c game-chrome.js games/X.html   # must be 0
# runtime: /tmp/opencode/pwenv/bin/python tools/shot.py <id> --wait 2 --actions  # must print OK, no JS-ERR
```

## 7. Screenshot QA (`tools/shot.py`)

`/tmp/opencode/pwenv/bin/python tools/shot.py [ids...] [--wait S] [--actions]`
saves `assets/shots/<id>.png` (also = portal card thumbnails).
Every game is screenshotted: welcome state must show logo, gameplay must show
motion/score, game-over must show the shell card. Blank/black/broken shots =
rework the game. Review EVERY shot before submitting.

## 8. Music (portal)

`music/playlist.json` for bundled tracks; players add local files via `+`
(IndexedDB, persists). Detached player: `music/player.html` (heartbeat
`wg_music_popup`, state `wg_music_state`) — music survives navigation.

## 9. License

MIT. Author: Wellbou. Portal: static, GitHub Pages, no servers.
