la-li-lu-le-lo/README.md
rain fbb5f02f49 Add AGENTS.md and move the verification tools into the repo
The checks that were being run by hand lived in /tmp and would have been
lost, and the colour-contrast pass was ad-hoc enough that it missed
--dim2 entirely.

- tools/verify.js: headless smoke test. Boots the page against a stubbed
  DOM/canvas and checks all 9 tabs render, all 10 maps draw with no
  non-finite geometry and no squashed labels, dog-tag sub-checkboxes cover
  every tag in every list, no photo appears in two steps, every theme has a
  CSS block, and no personal data shipped.
- tools/theme-contrast.py: WCAG check across all themes, now also covering
  --on-acc on --acc.

Running the stricter check found real problems: --dim2 (small captions) was
2.3-3.0:1 in four themes and --acc-d was 2.7-2.8:1 in two. Lightened those.
Worst theme is now 3.2:1, all on-accent pairs are 4.8:1 or better.

- AGENTS.md documents the architecture, build stages, assertions, data
  provenance, deployment and the traps already hit, so a future session does
  not rediscover them the hard way.
2026-09-15 17:41:48 -04:00

169 lines
12 KiB
Markdown

# la-li-lu-le-lo
> *The Patriots have restricted access to this information.*
A single-file, offline-friendly companion page for 100%-ing **Metal Gear Solid 2: Sons of Liberty — Master Collection Version** (51 achievements).
## Files
| File | What it is |
|---|---|
| `index.html` | The whole guide. Self-contained — data, styles, logic and your progress all live in this one file. |
| `steam-sync.py` | Optional local helper. Serves this folder **and** proxies Steam achievement lookups so the page can auto-tick what you already own. |
| `steam-proxy-worker.js` | Optional Cloudflare Worker. Same job as the Python helper, but for when the page is **hosted** somewhere instead of run locally. |
| `build/` | The sources. `index.html` is generated from these, so edit here rather than in the built file. |
| `deploy.sh` | Rebuilds and pushes to `pages` in one step. |
| `tools/` | `verify.js` (headless smoke test) and `theme-contrast.py` (WCAG check). Both run before deploying. |
| `AGENTS.md` | Orientation for anyone picking this up cold: architecture, conventions, and the traps already hit. |
## Hosting it (GitHub Pages, Netlify, Cloudflare Pages, Neocities…)
**This copy is deployed at <https://rain.pages.melonbread.xyz/la-li-lu-le-lo/>**, served from the `pages` branch of <https://git.melonbread.xyz/rain/la-li-lu-le-lo>.
### Updating the live site
```bash
node tools/verify.js # smoke test
python3 tools/theme-contrast.py # after touching any colour
./deploy.sh "what you changed"
```
That rebuilds `index.html` from `build/`, commits, and pushes to `pages`. The Pages site redeploys itself in about 15 seconds — no separate publish step.
**Edit the sources in `build/`, not the built `index.html`.** The built file is generated: `build/template.html` holds the page, `build/*_src.py` the written content, and `build/*.json` the extracted data (achievements, dog tags, maps, photo pins). `build/build.py` stitches them together.
### Steam sync on the deployed site
A hosted page cannot read Steam directly — the achievement page sends no CORS headers. So the deployed page talks to **`steam-sync.py` running on your own machine**:
```bash
nohup python3 ~/Desktop/la-li-lu-le-lo/steam-sync.py > /tmp/steam-sync.log 2>&1 &
```
Then sync just works on the Pages site. `steam-sync.py` answers the Private Network Access preflight that Chrome requires when a public page calls into `127.0.0.1`; without that header the request is blocked before it is sent. If the helper is not running you get a message saying so, and everything except sync still works.
If you would rather not keep the helper running, deploy `steam-proxy-worker.js` to Cloudflare and put its URL in `STEAM_PROXY` at the top of the page's script — `ALLOW_ORIGIN` in the Worker is already pinned to the Pages origin.
**The page itself hosts fine as-is.** It is one static HTML file with all the guide data, the 10 area maps and the colour coding baked in, and it stores progress in the browser's `localStorage`. Drop it in a repo, enable Pages, done. No build step, no dependencies.
Two things behave differently on a static host:
**1. Live Steam sync needs a server.** Steam's achievement page sends no CORS headers, so a browser cannot read it from another origin — that is the whole reason `steam-sync.py` exists. Hosted with no proxy, the page says so instead of failing silently, and everything except sync still works. To get sync back you have two options:
- **Run it locally** (`python3 steam-sync.py`) — simplest, and what the sync feature was built for.
- **Deploy the Worker** — paste `steam-proxy-worker.js` into a free Cloudflare Worker, then set at the top of `index.html`:
```js
const STEAM_PROXY = 'https://your-worker-name.yourname.workers.dev';
```
Any serverless function works the same way — it just has to forward `/api/steam?profile=…` to Steam and return JSON with a permissive `Access-Control-Allow-Origin`.
**2. Your ticks do not follow you between addresses.** `localStorage` is scoped per origin, so `http://127.0.0.1:8765/` and `https://you.github.io/mgs2/` are separate checklists. Use **Export** on one and **Import** on the other — that is exactly what those buttons are for.
There is also a deliberate default in the page: `DEFAULT_PROFILE` is pre-filled so it syncs on load. Set it to `''` before publishing if you would rather it started blank, and change `ALLOW_ORIGIN` in the Worker from `*` to your site so nobody else can point their own page at your function.
### A note on the images
The 62 location screenshots are **hot-linked** from the Steam Community CDN and Dayngl's Guides, with `referrerpolicy="no-referrer"` so they load from any domain. That keeps the page small and credits the original authors, but it does mean the page depends on two external hosts. If you plan to publish it widely rather than keep it for yourself, download the images into an `img/` folder and rewrite the URLs — that also removes any question about borrowing someone else's bandwidth.
## Run it locally (recommended)
```bash
cd ~/Desktop/la-li-lu-le-lo
python3 steam-sync.py
```
Then open **http://127.0.0.1:8765/** and leave it on your second screen.
`python3 steam-sync.py --open` launches the browser for you. `PORT=9000 python3 steam-sync.py` uses a different port.
No dependencies, no Steam API key, no install. Requires Python 3 (already on macOS/Linux; on Windows use `py steam-sync.py`).
**Keep it running in the background** (so it survives closing the terminal):
```bash
nohup python3 steam-sync.py > /tmp/steam-sync.log 2>&1 &
```
To stop it later: `pkill -f steam-sync.py`
If the port is already in use, either it is already running or you can pick another: `PORT=9000 python3 steam-sync.py`.
## Just want the checklist, no sync?
Double-click `index.html`. Everything works except live Steam sync. Note that opening it as a `file://` page makes browser storage less reliable — use **Export** to keep a backup of your ticks.
## Steam sync
- Enter your **vanity name** (e.g. `gaben`) or **SteamID64** on the **🔄 Steam Sync** tab and hit **Sync now**.
- It re-syncs automatically every time the page loads.
- Achievements you already own get a `STEAM ✓` badge and a blue edge, and are counted as done.
- Use the **⬇ Missing on Steam** filter on the Achievements tab to see only what is left.
- No profile is pre-filled in the public repo — type yours on the Steam Sync tab once and it is remembered in this browser (per origin, so the Pages site and a local copy keep separate memories).
**If it fails,** the page tells you why. The usual cause is a private profile:
Steam → your profile → **Edit Profile → Privacy Settings → Game details → Public**.
## What's inside
- **🗺️ Roadmap** — the 5-pass plan, plus a 48-step ordered walkthrough of the Tanker and Plant with every collectible, box, weapon and one-shot achievement flagged in the order you hit them.
- Every step has **sub-checkboxes** underneath it listing exactly what to get there — the dog tags by name and area, the achievements, and the items.
- **The dog-tag boxes are the same checklist as the Dog Tags tab.** Tick a tag in the walkthrough and it ticks in the Dog Tags tab, and vice versa — same key, same saved state. Achievements and items sync to their own tabs the same way.
- A **difficulty selector** at the top picks which tag list the steps show (defaults to Very Easy, for run 1). Coverage is complete: all **67/70/82/87/88** tags for Very Easy/Easy/Normal/Hard/Extreme appear exactly once.
- Text is **colour coded** so you can scan a step at a glance: <span>teal = guard whose dog tag it is</span>, violet = weapon/box, blue = achievement, **amber = missable achievement**, red = boss.
- **🏆 Achievements** — all 51 with official Steam icons, missable/cumulative flags, category filters, search, and a full how-to for each.
- **🏷️ Dog Tags** — all **394** tags across 10 difficulty lists, with guard names, areas, and 81 conditional/missable spawn notes.
- **🗺️ Area Maps** — the guard-position maps from the tracking spreadsheet, **rebuilt cell-by-cell** as real images (no internet needed). Colour-coded: orange = guard with a tag, magenta = tough guard, red = boss with a tag, green = conditional guard. Zoomable, with the spreadsheet's own Keys and numbered guard legend rendered in place, plus a **label-size control**. Reachable from the Dog Tags tab via **Open … area map**.
- **📷 Location photos** — **48 in-game screenshots** of the exact spots (locker poster, fire extinguisher, Hold No. 2 screens, the Book, the orange boxes, and more), drawn from **two** guides: the Steam Community guide and Dayngl's Guides. Each one is pinned to the **specific step where that thing is** — the seven Moving Day boxes are spread across the six steps that actually contain them, and the two Johnny on the Spot microphone moments go to their own steps. Hot-linked at thumbnail size.
- Duplicates are removed: Dayngl's guide reuses the same screenshots as the Steam guide, so 22 pairs were detected by perceptual hash (dHash, ≤14/240 bits apart) and de-duplicated — you will not see the same picture twice.
- **Click any photo to zoom it** in a full-screen viewer. Then: **click the image** (or press Space) to toggle fit ↔ full resolution, **drag** to pan while zoomed, **← / →** to move between the photos in that step, **Esc** or a backdrop click to close, and **open original ↗** to jump to the source site.
- **🔄 Steam Sync** — see above.
- **💀 Big Boss Run** — every codename requirement, the heal points, time checkpoints, save points, and a strategy for each of the 8 bosses.
- **⚠️ Missables** — everything that can be permanently missed, in story order.
- **🕹️ VR / Extra Modes** — Snake Tales, Boss Survival, VR missions, Casting Theater, plus the special-item unlock table.
- **📚 Sources & Notes** — credits, links, and Master Collection version differences.
## Colour themes
The **theme dropdown in the header** switches the whole page. It is remembered in `localStorage` and applied before first paint, so there is no flash on reload. All of them are dark.
| | |
|---|---|
| `Codec` | the default teal/blue |
| `Gruvbox Dark` · `Monokai` · `Dracula` · `Nord` | |
| `Solarized Dark` | |
| `Tokyo Night` · `Catppuccin Mocha` · `One Dark` · `Rosé Pine` | |
Everything is themed through CSS custom properties — panels, borders, the walkthrough colour code, pills, callouts, the lightbox and the progress bars. The **area maps keep their white background** on purpose: they are reproductions of a spreadsheet, and inverting them would make the colour legend lie.
Every foreground/background pair in all 10 themes was checked against WCAG contrast; the worst case is **3.1:1** (Nord's red), which is above the 3:1 threshold for UI text and components.
## Progress & backups
Ticks save automatically to this browser's `localStorage`, under the key `mgs2mc-guide-v1`.
They are per-browser **and** per-origin — if you switch browser or open the file from a different path you'll get a fresh checklist.
Use **Export** to download `mgs2-progress.json` and **Import** to restore it. Export before each playthrough.
## Credits
Compiled from community work — full links are on the **Sources & Notes** tab:
- Steam Community guide by **Cole ヴ Viper** (achievement write-ups, boss strategies, Big Boss criteria)
- **Video Chums** dog tag checklist (all 394 tags)
- **u/Spikeyroxas** dog tag spreadsheet (conditional spawn notes)
- **Dayngl's Guides** (cross-check)
- **TrueSteamAchievements** (authoritative 51-achievement list)
Unofficial fan-made companion, for personal use. MGS2 © Konami.
## Licence
The **code** here (`index.html`, `steam-sync.py`, `steam-proxy-worker.js`) is **MIT** — see [LICENSE](LICENSE).
Two carve-outs worth knowing before you fork it:
- **The guide content is compiled from other people's work.** The achievement write-ups, the 394 dog tags, the guard-position maps and the Big Boss strategies all come from the community guides credited above. This repo reorganises and presents them; it does not claim authorship. If you reuse the text, credit the original authors.
- **The screenshots are not in this repo, and are not mine.** All 48 are hot-linked from the Steam Community CDN and Dayngl's Guides and remain the property of their authors. If you fork this and republish it widely, download them into an `img/` folder or drop them — see the note under *Hosting it*.
Metal Gear Solid 2: Sons of Liberty is © Konami. This is an unofficial, non-commercial fan project.