What it does
You describe a catalogue: products with a kind, a price and optional ownership prerequisites; areas with a price,
prerequisite areas and products the player must own; and slots, each in an area and accepting one kind of product.
createRenovationProgress checks it, refusing duplicate ids, negative or fractional prices, and prerequisites that are
missing or circular, then keeps credits, stored items, installed products and unlocked areas.
The player acts through baskets of lines: buy, place, store and unlock. quote works out a whole basket without
changing anything, and transact commits every line in order or rejects the whole basket with a reason such as
insufficient-credits, area-locked or product-does-not-fit-slot. Placing a variant in a filled slot returns the
old one to storage, buying and placing never duplicates inventory, and storage capacity is checked on the final
basket, so buying a bench and installing it straight away is one transaction.
Rewards come from whatever game surrounds it. grant takes credits and items with a receipt id, and a repeated id
changes nothing, even after a reload, so a level replayed or a callback fired twice cannot pay twice. There is no
reward policy, score model or random drop built in: your game decides what to award.
Saves hold the state and the catalogue's id, not the catalogue itself. restore fits a save to the catalogue the game
has now: removed products and areas are dropped, an installed product whose slot moved or changed kind goes back to
storage, new starting areas and the starting stock of new products are added, and a report says what changed. save
and load work through localStorage or any adapter with synchronous getItem and setItem.
Features
- A catalogue as data — products, areas and slots with prerequisites, plus optional labels, variant names and levels for your own display.
- Atomic baskets —
quoteandtransactover 1 to 100 lines; a failed line spends nothing. - Four actions — buy, place, store and unlock, with
applyfor a single line. - Clean variant swaps — replacing an installed product returns the old one to storage.
- Rewards that pay once —
grantwith a stable receipt id, across reloads, for up to 100,000 receipts. - Storage capacity — 1,000 stored items by default, checked on the final basket.
- Saves that survive catalogue edits — a versioned save restores into an edited catalogue and reports what was dropped, moved back to storage and added.
- Clear refusals — a save from another game or a malformed one throws a named error, and a refused restore leaves the live state as it was.
- Any storage —
localStorageor your own adapter; storage errors reach your code, so you can tell the player that saving failed.
Good for
- Renovation and decorating meta-games between puzzle levels, where earned credits furnish rooms one slot at a time.
- Garden, home or shop makeovers where areas unlock in order and each slot takes one kind of item.
- Level-based games with a shop: earn credits in levels, spend them on items stored for later.
- Live games that change their catalogue between releases and must not strand a player's progress.
- Games drawn in 2D, in 3D or as plain text from the same state, since installed choices are plain ids.
Make it yours
Everything is the catalogue: what can be bought, what it costs and needs, which areas exist and what opens them, which
slots take which kinds, and the starting credits, starting areas, starting stock and storage capacity. The optional
version records which release a save came from, so a host can show a "what changed" notice; change the id only when
a release should start every player afresh.
Two different uses: a home makeover where each puzzle level grants credits, the living room is open from the start, the garden needs the living room unlocked and a lamp owned, and each sofa slot takes any sofa variant; and a shop builder where rewards give stock rather than credits, shelves are slots of their own kind, and storage is capped low so the player must place stock before taking more.
A quick look
A catalogue loaded from storage, a reward granted once, and a basket bought and installed together:
const garden = createRenovationProgress({ id: 'garden', version: 2, products, areas, slots });
const report = garden.load(localStorage); // false on the first run
garden.grant({ id: 'level-12', credits: 50 }); // granting 'level-12' again changes nothing
const basket = [{ type: 'buy', product: 'bench' }, { type: 'place', product: 'bench', slot: 'patio-1' }];
if (garden.quote(basket).valid) garden.transact(basket);
garden.save(localStorage);
Works with
save— a simple key-value store for scores and settings beside it; this pack writes its own saves through any storage adapter.ui— for the shop, basket and room screens around it.inventory— item stacks, weighted containers and equipment, when the game needs more than stored and installed products.
Good to know
- It is local game state, not an authoritative server economy, and there are no real-money purchases or network saves.
- Saves are checked but not encrypted or tamper-proof.
- A restore fits a save to an edited catalogue by fixed rules; anything beyond them, such as refunding a removed product, is your game's to do from the report.
- It holds ids, not scenes: the geometry and the art are your game's.






