rigging: the fabric bet is reachable — F cycles cloth/membrane in prep

FABRIC and setFabric() shipped in SPRINT6 and the sim honoured porosity
all along, but no key, click or HUD line ever reached them — every rig
ever played was DEFAULT_FABRIC. F toggles the two entries, the panel
shows the fabric, the splash teaches it, the blurb announces the switch.
Session-level assert pins the cycle, the $0 price, and the refusal of
unknown ids.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
m3ultra 2026-07-18 19:03:36 +10:00
parent b7a069069d
commit b74dfd80db
3 changed files with 28 additions and 3 deletions

View File

@ -516,6 +516,7 @@ export function createHud(d) {
['click', 'pick an anchor · again to unpick'],
['[ ]', 'tension the rig — tighter holds shape, and breaks harder'],
['S', 'buy a spare shackle'],
['F', 'choose the fabric — cloth breathes, membrane stops the rain'],
['E', 'repair a blown corner (ladder if it is up high)'],
['C', 'brace against the wind'],
['Enter', 'commit the rig and start the night'],

View File

@ -514,6 +514,15 @@ export async function createRiggingUI({
if (ev.key === '[') session.setTension(session.tension - 0.05);
else if (ev.key === ']') session.setTension(session.tension + 0.05);
else if (ev.key.toLowerCase() === 's') say(session.setSpares(session.spares ? 0 : 1));
else if (ev.key.toLowerCase() === 'f') {
// The forecast bet, finally reachable. FABRIC and setFabric() shipped in
// SPRINT6 and the sim honoured porosity all along — but no key, click or
// HUD line ever reached them, so every rig ever played was DEFAULT_FABRIC.
// Two entries, so F is a toggle; announce the blurb because the panel
// line alone doesn't say what the bet IS.
const next = FABRIC[(FABRIC.indexOf(session.fabric) + 1) % FABRIC.length];
if (say(session.setFabric(next.id)).ok) onMessage(`${next.name}${next.blurb}`);
}
else return;
ev.preventDefault();
refresh();
@ -585,14 +594,14 @@ export async function createRiggingUI({
: null;
el.textContent = [
`PREP — rig four corners $${s.budget} left`,
`tension ${s.tension.toFixed(2)} spare x${s.spares}${area ? ` sail ${area.toFixed(0)} m2` : ''}`,
`tension ${s.tension.toFixed(2)} spare x${s.spares} ${s.fabric.name}${area ? ` sail ${area.toFixed(0)} m2` : ''}`,
'',
...rows,
...(standingLine ? ['', standingLine] : []),
'',
s.canStart ? 'ENTER to start the storm' : `pick ${MAX_CORNERS - session.picks.length} more corner(s)`,
'click anchor: rig / cycle hw shift-click: remove',
'[ ] tension S spare RMB orbit',
'[ ] tension S spare F fabric RMB orbit',
].join('\n');
}

View File

@ -6,7 +6,7 @@
* js/tests/b.test.js) and node.
*/
import { RiggingSession } from './rigging.js';
import { RiggingSession, FABRIC } from './rigging.js';
import { SailRig, TENSION_MIN, TENSION_MAX } from './sail.js';
import { HARDWARE, START_BUDGET, SPARE_COST } from './contracts.js';
@ -344,6 +344,21 @@ test('reset() returns a used session to a fresh prep phase', () => {
return 'budget, picks, tension, spares all fresh; rig-able again';
});
test('fabric is a bet the F key can actually place (SPRINT15 — the dead mechanic)', () => {
const s = session();
assert(s.fabric.id === 'cloth', `default fabric should be cloth, got ${s.fabric.id}`);
// the exact call the F key makes: cycle to the other entry by id
const next = FABRIC[(FABRIC.indexOf(s.fabric) + 1) % FABRIC.length];
assert(s.setFabric(next.id).ok, 'cycling to membrane refused');
assert(s.fabric.id === 'membrane' && s.fabric.porosity === 0, 'membrane not applied');
assert(s.budget === START_BUDGET, 'fabric is a bet, not a purchase — budget must not move');
assert(s.summary.fabric.id === 'membrane', 'the panel reads fabric off summary and must see the change');
// wrap-around brings it home, and garbage is refused loudly
assert(s.setFabric(FABRIC[(FABRIC.indexOf(s.fabric) + 1) % FABRIC.length].id).ok && s.fabric.id === 'cloth', 'cycle should wrap back to cloth');
assert(s.setFabric('hessian').ok === false, 'unknown fabric must refuse, not default');
return 'cloth -> membrane -> cloth, $0 both ways, hessian refused';
});
export const RIGGING_TESTS = TESTS;
export function runRiggingSelftest() {