Pendekar Book Game v0.35 · tools designed in three parts
Chapters and search

Design: tools

Build plan

Planned · Tools I–III

How to build the tools, version by version, against the game as it is now: what to add, which functions change, how old saves come across, and the tests that prove it.

Part 1: Tools of the trade

Ship this alone and the game is whole: every gathering verb needs a tool and there is always a way to get one. In site/play/index.html:

  1. Data. Add the tools to ITEMS with k:'tool' (below) and their icons to ICON (in book/design/tools.mjs). Give POOLG each a tool, and the tree g in both zone builders tool:'kapak',hard:1. Adding a property draws no random numbers, so the seeded island doesn't move.
  2. State. S.belt=new Array(4).fill(null); save it, and check it on load like inv.
  3. Choosing a tool. A bestTool(kind) that scans belt, weapon and pack and returns the highest tier the player can use. A gatherTicks(o,tool) used by both interact() and actFrac(), so the action ring stays honest.
  4. Gathering. In interact(), before the level check: no tool → stop with the message for this zone; then add tool.pow to the chance and take tool.fast off the ticks.
  5. Teachers. TALK.mat and TALK.esah get a start() that gives the starter tool (belt first), and their after branch replaces a missing one. Esah's reward() adds a candat.
  6. Guru and the journal. Guru's first lines send you to Esah for a net; his boat lines send you to Pak Mat for a hatchet. objective() and questList() gain the two “get a tool” steps, done as soon as you hold one.
  7. Kak Yah. New stock in SHOPS.yah. In sell(), tools without v get her line about Pak Mat's hatchet.
  8. Panels. The belt row in the Pack tab; clicking moves a tool between belt and pack. useSlot, effect, itemInfo and useHint learn k:'tool'.
  9. Hover and examine. labelOf() adds the red “needs a …” line; the tree's examine text names the hatchet.
  10. Hands. The chop clip's prop becomes the hatchet you are using: one prop geometry per hatchet, the head coloured by tier. The net and line clips show the net and the line.
  11. Old saves. Saves from before tools get both starter tools on the belt and one line in the chat. Bump the save format's v.
/* tools: t is the kind of work, tier ranks tools of a kind, lv is the level to use it, pow adds to every attempt's chance */
  kapakl:{n:'Old hatchet',en:'Kapak lama',k:'tool',t:'kapak',tier:1,lv:1,pow:0},
  kapakb:{n:'Iron hatchet',en:'Kapak besi',k:'tool',t:'kapak',tier:2,lv:5,pow:.08,v:16},
  jalal:{n:'Old cast net',en:'Jala lama',k:'tool',t:'jala',tier:1,lv:1,pow:0,net:true},
  candat:{n:'Squid jig',en:'Candat',k:'tool',t:'candat',tier:1,lv:5,pow:0,v:3},
  bubu:{n:'Prawn trap',en:'Bubu',k:'tool',t:'bubu',tier:1,lv:8,pow:0,v:6},
  pancing:{n:'Hand-line',en:'Tali pancing',k:'tool',t:'pancing',tier:1,lv:12,pow:0,line:true,v:8}
/* the best tool of a kind you carry and can use: belt, hand, then pack */
const TOOLSK={kapak:'kayu',parang:'kayu',jala:'nelayan',candat:'nelayan',bubu:'nelayan',pancing:'nelayan'};
function bestTool(t){
  let b=null;
  for(const id of [...S.belt,S.eq.weapon,...S.inv]){
    const x=id&&ITEMS[id];
    if(x&&x.k==='tool'&&x.t===t&&L(TOOLSK[t])>=x.lv&&(!b||x.tier>ITEMS[b].tier))b=id;
  }
  return b;
}
const gatherTicks=(o,tool)=>Math.max(2,o.g.ticks-(learned(o.type==='pool'?'esah':'mat')?1:0)-(tool&&ITEMS[tool].fast||0));
/* in interact(): */
const tool=o.g.tool?bestTool(o.g.tool):null;
if(o.g.tool&&!tool){stopAct(NEED[o.g.tool][Z.id],'w');return}
if(o.g.hard&&ITEMS[tool].tier<o.g.hard){stopAct(TOO_SOFT(o,tool),'w');return}
...
if(Math.random()<Math.min(.92,.35+(L(g.skill)-g.req)*.03+(ITEMS[tool]?.pow||0))){ ... }

Part 1 tests

A new headless test, tests/tools.js, in the style of the others (Playwright with the __bfTest hook):

  • A tree with no hatchet stops with the Pak Mat message on Perhentian and the Kak Yah one in Kuala Besut; with the old hatchet it gives timber.
  • Each spot refuses without its tool and works with it; the wrong tool (a net at a squid spot) is refused.
  • With an old and an iron hatchet both carried, the iron one is used once Woodwork is 5, and the old one before.
  • Talking to Pak Mat and Mak Long Esah gives the starters to the belt; with a full belt they go to the pack; with the tool gone, the next talk replaces it.
  • A save from before tools loads with both starters on the belt, once.
  • Kak Yah refuses the starters and buys the rest at v.
  • Castaway's Training can still be finished from a new game, start to boat, without buying anything.
  • The action ring's ticks equal the gathering ticks for every tool.

Part 2: Hardwood and the forge

  1. Items: the steel hatchet, the parang, meranti, cengal, bamboo, rattan, the meranti handle.
  2. Nodes: meranti, cengal, buluh, rotan as object types with their own g, examine text, verbs (“Chop down”, “Cut”) and models. Hardness check in interact().
  3. Placement: after the seeded layout, as on the hatchets page. Bamboo and rattan join BLOCK and the stump logic.
  4. Pak Leh: an NPC, a forge building on the main road, a shop in SHOPS, and a “forge” list for the steel hatchet (cost in items plus coins).
  5. Recipes: RECIPES entries get at and skill; the Craft tab shows the station you stand at.
  6. Parang as weapon: useSlot equips a tool with a slot; wBonus() already reads bonus.
  7. Tests: hardness refusals, the forge job (handle and coins in, steel hatchet out), parang wielded still cuts bamboo.

Part 3: Traps and lines

  1. A saved clock: S.clock, ticks played since the new game, saved and loaded. Traps stamp it, so they keep time across zones and sessions.
  2. Traps: S.traps=[{zone,x,y,set}]; a set trap is drawn at its spot and the spot is “Haul” when full. Max traps by Fishing level. Hauling rolls the extra prawns and re-sets.
  3. Bait: the tuna spot's g.bait; check before the first attempt, consume on a catch.
  4. Wear: torn and snapped variants as items; the roll after a catch; mending by clicking rope or a hook (a short action at no station).
  5. Net racks: a new object beside Esah and by the fish market; the net, big net and hand-line recipes.
  6. Tests: a trap fills after 60 ticks and survives a save and reload; never more traps than the level allows; tuna without mackerel refuses; a torn net is slower and mends with one rope.

How this book is made

The book lives in book/ in the Pendekar repository and deploys as its own Cloudflare Worker to book.pendekar.themasterofnone.xyz.

  • book/game.mjs reads the committed game (git show HEAD:site/play/index.html), cuts out the data and the zone builders, and runs them in a sandbox. Every live table in this book is that data, so the book can't drift from the game.
  • book/design/tools.mjs is the tools design as data, in the game's own shapes, ready to lift into the game.
  • book/pages.mjs is the words; book/build.mjs lays them out into book/site/, with a search index.
node book/build.mjs            # or --working to read the uncommitted game
cd book && set -a && . ~/.kracked/deploy.env && set +a && npx wrangler@4 deploy

Rebuild after every game version so the live chapters follow it.