Files
idle_clicker/README.md
T

5.5 KiB
Raw Blame History

Idle Clicker

A themeable idle game inspired by Universal Paperclips. Make units by hand, sell them, automate production, buy upgrades. Runs in any browser (phone, tablet, desktop); progress is saved in cookies and can be exported/imported as a JSON or TXT file. There is one game: themes (paperclips, witch potions, alien minerals, leaf-piling insects) are only skins, so switching theme keeps every amount.

Zero runtime dependencies: a small Node server (http module only) plus a vanilla JS frontend (ES modules).

Run it

docker compose up -d --build     # http://localhost:8080  (GAME_PORT=9000 to change the port)

The container is a single image running as the unprivileged node user, with a read-only filesystem, no capabilities and no-new-privileges.

Without Docker: npm start (Node 22+). Tests: npm test.

How the project is organised

server/                  static file server + /api/themes and /api/locales (auto-discovery)
public/
  index.html             page skeleton (texts come from i18n via data-i18n attributes)
  css/                   base (tokens) · layout (responsive grid) · components
  data/                  GAME CONTENT, theme-independent: rules, generators, upgrades (ids + numbers)
  locales/               core UI texts, one file per language  (en, fr)
  themes/<id>/           ONE FOLDER PER THEME
    theme.json             order + (later) image paths
    theme.css              colours/fonts, as CSS variables
    locales/<lang>.json    the theme's vocabulary: names of resources, generators, upgrades
  js/
    engine/              game rules, pure JS, no DOM (also used by the tests)
    services/            cookies · storage · save import/export · i18n · formatting · data loading
    ui/                  DOM helpers · main view · settings dialog · toasts
    main.js              wires everything together
tests/                   node:test suites (engine, save import/export, server, content completeness)

The key idea: mechanics are shared, themes are skins. public/data/ defines generators and upgrades by id (gen1, click_1, …) with their numbers; a theme only gives those ids a name, a colour palette and, later, images. Rebalancing the game is a change in public/data/, not in each theme.

Add a theme

  1. Copy public/themes/paperclip/ to public/themes/<your-id>/ (lowercase letters, digits, dashes).
  2. Edit theme.css (palette, font, radius), theme.json (order), and the texts in locales/*.json.
  3. Reload. The server discovers the folder; no code change and no rebuild list. (With Docker, rebuild the image or mount the folder.)

npm test fails if a theme misses a name for any resource, generator or upgrade, or lacks a language the game supports.

Add a language

  1. Copy public/locales/en.json to public/locales/<code>.json and translate it (set meta.name to the language's own name).
  2. Add public/themes/<id>/locales/<code>.json to every theme.

Missing keys fall back to English, then to the raw key. Strings accept {param} and {@other.key} (embeds another entry, which is how core texts use the theme's vocabulary, e.g. {@theme.res.material}).

Images

Every icon is currently a grey square. To use real images, put files in the theme folder and list them in its theme.json:

{ "order": 1, "assets": { "gen.gen1": "img/autoclipper.svg", "res.units": "img/clip.svg", "upg.click_1": "img/bender.svg" } }

Keys: res.units|funds|material, gen.<id>, upg.<id>. Anything not listed keeps the placeholder.

Saves

  • One shared game for all themes, stored in cookies (ic_game_*, chunked and base64-encoded because a cookie holds ~4 KB), plus one preferences cookie (theme + language). Saves from the first version (one per theme) are merged automatically: the most advanced one is kept.
  • Save & settings exports the save as .json or .txt (download or copy/paste) and imports either (file picker or pasted text). The file does not depend on the theme. Imports are validated and sanitised: unknown ids are dropped, numbers clamped.
  • Progress continues while the game is closed: up to 8 h, at 50 % efficiency (public/data/rules.json).
  • Cookies are sent with every request to the server. A save is under 2 KB, far below header limits, but it is the reason to keep saves compact.

Tuning the game

Everything numeric is in public/data/: rules.json (market, raw material, marketing, offline, autosave), generators.json, upgrades.json.

  • Selling: demand is a smooth curve, baseDemand × demandMultiplier × (referencePrice / price) ^ elasticity units/s: lower the price and more people buy, with no ceiling. The best price is where demand just matches your output (selling for less gains no volume, selling for more leaves stock unsold). That point moves with every generator, upgrade and marketing level, and the game does not display it: the market hint only says whether you are selling too cheap, too dear, or about right.
  • Raw material (wire, herbs…): the price per unit starts high (startUnitPrice, about a third of the sale price) and falls towards floorUnitPrice as total production grows (economies of scale). Lots grow with the operation, in doublings. The market is erratic: the price is re-drawn every 10 s (updateEverySeconds) with wide random swings plus frequent shortages and gluts (spikeChance, spikeSize), and is shown to the cent, with ▲/▼ for its last move. The auto-buy upgrade is patient: it waits for a fair price unless stock is nearly gone.