[lane B+C+F] The biofilm gate: the reputation cash-out, "it helps you in level 5" made real

The deeper payoff of the flora loop. A new `biofilm` level event: cross one and combat checks
BIOME STANDING — high enough (>= need), the native flora PART the biofilm, budding a wave of phage
allies + refilling coat, then spending the goodwill; too low, it stays sealed and you run the
gauntlet raw. Emits `biofilm {opened}` so E can voice both.

- combat/index.js: biofilmGate(ev) on the level:event pump (fields need/allies/coat).
- levels/index.js: the selfcheck knows the `biofilm` type now (validates need 0..100).
- L2: one demoed at s2500 — the opening flora/akkermansia/beacon let you BANK standing, and the
  gate cashes it into a phage escort right before the hiatus + reflux finale.

The mechanism is complete; L5 also gets the fast-route/Appendix branch when that level + world
support land. Verified in-engine: high standing -> opened, 3 phages, coat 50->90; low -> sealed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-20 00:16:12 +10:00
parent fce26abfab
commit 3c059f20db
4 changed files with 26 additions and 0 deletions

View File

@ -184,6 +184,9 @@ Ratified round 2 (round-1 requests):
- `standing {value}` — BIOME STANDING 0..100 (combat/index.js owns it). Tending flora raises it; - `standing {value}` — BIOME STANDING 0..100 (combat/index.js owns it). Tending flora raises it;
harming flora drops it; crossing the threshold buds phage allies. E can meter it. ("Don't harming flora drops it; crossing the threshold buds phage allies. E can meter it. ("Don't
shoot the biome, it helps you" — V2_IDEAS, now partly real.) shoot the biome, it helps you" — V2_IDEAS, now partly real.)
- `biofilm {opened, s}` — a `biofilm` level event was crossed (the reputation cash-out). `opened`
= standing was high enough, so the biome parted (allies budded + coat refilled, standing spent);
false = it stayed sealed. E can voice both. (New event type `biofilm`, fields `need/allies/coat`.)
## Asset manifest contract (D owns `assets.js` + `manifest.json`) ## Asset manifest contract (D owns `assets.js` + `manifest.json`)

View File

@ -56,6 +56,8 @@ export function createCombat({ scene, world, bus, rng, flags = {}, assets = null
} else if (ev.type === 'pickup') { } else if (ev.type === 'pickup') {
for (let i = 0; i < (ev.count ?? 1); i++) for (let i = 0; i < (ev.count ?? 1); i++)
pickups.spawn(ev.kind, { s: fan(ev, i), theta: thetaFor(ev, i), rho: ev.rho ?? null }); pickups.spawn(ev.kind, { s: fan(ev, i), theta: thetaFor(ev, i), rho: ev.rho ?? null });
} else if (ev.type === 'biofilm') {
biofilmGate(ev);
} }
}); });
@ -93,6 +95,22 @@ export function createCombat({ scene, world, bus, rng, flags = {}, assets = null
} }
} }
// biofilm gate — the reputation CASH-OUT (V2_IDEAS: "it helps you in level 5"). Cross one with
// high BIOME STANDING and the native flora part the biofilm: a wave of phage allies buds and
// your coat is refilled, then the goodwill is spent. Low standing => it stays sealed and you run
// the gauntlet raw. Placed as a `biofilm` level event (`need`/`allies`/`coat` optional). Emits
// `biofilm {opened}` so E can voice it. The L5 payoff, built as a mechanism usable anywhere.
function biofilmGate(ev) {
const opened = standing >= (ev.need ?? 60);
if (opened) {
for (let i = 0; i < (ev.allies ?? 3); i++) enemies.spawn('phage', { s: player.state.s + 6 + i * 4 });
player.refill({ coat: ev.coat ?? 40 });
setStanding(20); // spent the goodwill
}
bus.emit('biofilm', { opened, s: ev.s });
bus.emit('audio:cue', { name: opened ? 'gate_pass' : 'gate_hit' });
}
function update(dt) { function update(dt) {
if (phageBudCd > 0) phageBudCd -= dt; if (phageBudCd > 0) phageBudCd -= dt;
enemies.update(dt, player); enemies.update(dt, player);

View File

@ -126,6 +126,7 @@
{ "s": 2300, "type": "pickup", "kind": "antacid_ammo", "count": 2, "spread": 20, "note": "Arms the player 700 units before the reflux finale. Antacid neutralises an acid zone (GDD) - fired BACKWARD it stalls the surge. The level hands you the tool, never explains it, and rewards you for noticing." }, { "s": 2300, "type": "pickup", "kind": "antacid_ammo", "count": 2, "spread": 20, "note": "Arms the player 700 units before the reflux finale. Antacid neutralises an acid zone (GDD) - fired BACKWARD it stalls the surge. The level hands you the tool, never explains it, and rewards you for noticing." },
{ "s": 2380, "type": "spawn", "enemy": "bolus_chunk", "count": 6, "spread": 140 }, { "s": 2380, "type": "spawn", "enemy": "bolus_chunk", "count": 6, "spread": 140 },
{ "s": 2420, "type": "spawn", "enemy": "candida_bloom", "count": 2, "theta": [1.8, 5.1], "note": "Blooms + debris simultaneously: the level's densest sustained stretch." }, { "s": 2420, "type": "spawn", "enemy": "candida_bloom", "count": 2, "theta": [1.8, 5.1], "note": "Blooms + debris simultaneously: the level's densest sustained stretch." },
{ "s": 2500, "type": "biofilm", "need": 60, "allies": 3, "coat": 40, "note": "MICROBE PASS (round 2) - the reputation CASH-OUT (the L5 payoff, demoed here). If you tended the opening flora/akkermansia/beacon and kept BIOME STANDING high, the biofilm parts: 3 phage allies bud and your coat refills, right before the hiatus + reflux finale. Ignore the biome and it stays sealed - you run the finale raw. 'Don't shoot the biome, it helps you.'" },
{ "s": 2540, "type": "pickup", "kind": "mucin_glob", "count": 1 }, { "s": 2540, "type": "pickup", "kind": "mucin_glob", "count": 1 },
{ "s": 2580, "type": "checkpoint", "name": "Diaphragmatic Hiatus" }, { "s": 2580, "type": "checkpoint", "name": "Diaphragmatic Hiatus" },

View File

@ -526,6 +526,10 @@ export async function validate(level, { expectId = null } = {}) {
if (e.warn != null && e.warn < 2 && e.lethal !== false) W(`${at}: warn ${e.warn}s is under the 2s telegraph law for lethal hazards`); if (e.warn != null && e.warn < 2 && e.lethal !== false) W(`${at}: warn ${e.warn}s is under the 2s telegraph law for lethal hazards`);
break; break;
} }
case 'biofilm':
// reputation cash-out (combat/index.js biofilmGate). Optional need (0..100)/allies/coat.
if (e.need != null && (e.need < 0 || e.need > 100)) E(`${at}: biofilm need ${e.need} out of 0..100`);
break;
case 'checkpoint': case 'boss': case 'gate': break; case 'checkpoint': case 'boss': case 'gate': break;
default: W(`${at}: unknown event type "${e.type}" — B/E will ignore it`); default: W(`${at}: unknown event type "${e.type}" — B/E will ignore it`);
} }