hole-content
Add content to Hole Island - a new edible item (catalog row + procedural builder + tests + lineup check) or a whole new map (bounds, zones, point budget, registry entry, per-map scores version). Use f
SKILL.md.claude/skills/hole-content/SKILL.md
GitHubHole Island content workflow
Read first: src/games/hole/DETAILS.md (rules, level / tier tables, catalog, art style) and, for the toy store,
src/games/hole/TOY-STORE.md, for Animal Island src/games/hole/ANIMAL-ISLAND.md, for Construction Site src/games/hole/CONSTRUCTION-SITE.md. Points, tier and unlock level are derived from the item size
(size = max(w, d, h x 0.25), see sim/progression.ts): never hand-pick them. Changing an item size changes its points,
so bump that map's scoring version (below).
Add an item
- Catalog row.
- City Island:
items/catalog.ts(def(...), keep the list ordered by size). - Toy Emporium:
items/catalog-toy.ts(generated from the roster inTOY-STORE.md4.1; keep the department inwhereas"<Department> - <note>", the generator homes items by that department; give it a priority). paints= per-instance colours, only if the builder haspaint: trueparts (the test checks both ways).
- City Island:
- Builder. City:
build-small.ts/build-vehicles.ts/build-buildings.ts. Toy:build-toys.ts(bricks, figures, toys, games, dolls, store),build-toyveh.ts(vehicles, rides, robots, rockets, outdoor, trucks),build-plush.ts(plush rig: add a family toPLUSHand an idplush_<family>_<size>),build-landmarks.ts.- Toy recipes use
F(m, w, d, h)fromkit-toy.tsand fractions of the catalog size, so the built size equals the catalog size. Vehicles run along X, plush / shelves / people face +Z, pivot at the ground centre. - Toy builders run with
Mesher.decoplanar(faces that share a plane are pulled apart by 5.8 mm). Cylinders and tapered boxes are not handled: keep their tops / sides off other parts' planes by hand. - Proud details (eyes, doors, decals) stick out 1-3 cm (use at least
0.012 / das a fraction on small items). - Register in the builder map of that file (
TOY_BUILDERS,TOYVEH_BUILDERS,LANDMARK_BUILDERS, ...);builders.tsmerges them. Plush ids are resolved from the family name automatically.
- Toy recipes use
- Tests.
npx rstest run tests/hole- City:items,clip; Toy:toy-items(size within 15 % footprint / 10 % height, on the ground, paint list, triangle budget per group),toy-clip(z-fighting, mostly-buried parts; the failure names the builderfile:line),toy-map. Every size tier 1-25 and hole level 1-15 needs >= 2 types per catalog: protect the thin tiers (toy: 17, 20, 21, 23). - Look at it.
item-viewer.html?map=toy&mode=lineup&family=plush_panda&gamecam=1(andmode=comparefor the tier boundary). It has to read at game-camera distance at the item's first hole level, not just in the viewer. - Place it. City: add its
SPAWNrow inmap/spawn.ts(zones its centre may be in; only vehicles may listroad, see DETAILS.md "Map generation rules") and a placement inmap/generate.tsthroughtryPut/spotFor. Toy: it is picked up automatically by the zone of its department (homeOfinmap/toy/generate.ts); anything with size > 14.5 m homes in the atrium. Checkmap-viewer.html?map=toy. - Docs + scores. Update the DETAILS.md /
TOY-STORE.mdroster row (one row, no prose). Bump the scoring version of the map (below).
Add an animal / scenery item (Animal Island)
- Row in
items/catalog-animal.ts(def(id, name, group, w, d, h, note, priority, biomes, motion, { paints, variants })): biomes = where it may be placed, motion =wander | herd | hop | crawl | skitter | flutter | swim | trail | patrol | roam | -. Check tier / level coverage withnpx rstest run tests/hole/animal-items. - Builder: a family row in
build-animals.ts(QUADS,HOPS,BIRDS,SIMPLE;<family>_big/<family>_giantreuse the rig with a glowing collar), or a recipe inbuild-nature.ts/build-zoo.ts. Recipes run inMesher.fitted, so design by proportion; useball()for round parts. Parts closer than 4 mm to a neighbour's face fail the clip test. - The generator places it by itself from its biomes and priority (
planCounts); giants and compound items are listed inGIANTSandbuildCompoundofmap/animal/generate.ts. BumpMAP_SCORING_VERSIONS.animalonce the map is released. - Look:
item-viewer.html?map=animal&mode=lineup&family=<id part>; pace:tests/hole/animal-map.test.tsbands.
Add a map
- A generator
map/<id>/generate.tsreturningMapData(map/types.ts):placements,rects(flat floor patches),start, and eithercoast(island) orbounds(hx,hz,inset,door) for a closed rectangle. Deterministic (src/shared/rng.ts), DOM-free. - Exact point total per map (City Island 30 000, Toy Emporium 22 000): fill, then trim small items / top up with 1-point fillers
(
balancePointsinmap/generate.tsandmap/toy/generate.ts). Every tier 1-25 and level 1-15 has >= 2 types placed, every type up to tier 20 is placed, the start has > 15 tier 1-3 items within 25 m, total >= 2 xcumulativeXp(15). - Pace it with the bot (
sim/bot.ts,balance.html?map=<id>): good bot, Hard 120 s reaches level >= 10, Medium 240 s level 15, Easy 480 s clears > 95 %. Density (points per m2) decides the pace: the toy store needed a 600 x 400 m floor. Tune floor size and clustering, not the XP curve. - Ground + mood. A ground builder in
render/(use the stencil-cutcreateGroundMaterial()for everything the hole cuts) and wire it inrender/map-ground.ts; walls usegetItemMaterials().propso the building fade dithers them. - Register a
MapDefinmap/registry.ts(name, blurb, noun for the HUD, points, mood colours, menu camera) and add the id toMAP_SCORING_VERSIONSingame/scores.ts(start at 1). A new map goes intoMAPS_LISTwithhideInProd: trueinsrc/games/hole/release.tsfirst (dev server andbuild:testonly); remove the flag when Deni says ship. Add a menu card picturescreenshots/menu-<id>.jpg(640 x 360) ingame/menu.ts. - Tests like
tests/hole/toy-map.test.ts(determinism, exact points, coverage, items inside bounds, reachability from the clamped hole centre, balance bands). Viewers:map-viewer.html?map=<id>,balance.html?map=<id>.
Testing in the browser
Open the game with mute=1 (/games/hole/?map=toy&mute=1): no sound while creating / editing (AGENTS.md "Sound while testing"). Drop it only when the task is a sound fix in game/audio.ts.
Scoring version rule
MAP_SCORING_VERSIONS in game/scores.ts is per map. Bump the version of every map whose item dimensions / points,
point total, clear bonus, difficulty times or map content changed (shared rules in sim/ bump all). The history lives in
the comment above it.