[lane B+C] Akkermansia: spend coat to summon allies sooner
The cleverest node in the reputation loop — a commensal that eats your mucus. Brushing it COSTS coat but fills BIOME STANDING fast (gain 12 vs flora's 5): the "spend coat for a quicker phage ally" trade against flora's safe heal-and-slow. - enemies.js: flora's branch generalised to a shared COMMENSAL branch, sign-flipped by data (cfg.coat gifts, cfg.coatCost nibbles); flora:tended now carries `gain`. New `mucus_grazer` archetype — cyan (still flora, readability law), but a dodecahedron amoeba-blob so the coat cost reads as a different bug, not a betrayal by the healing rod. - combat/index.js: standing gain comes from the event's `gain`. - levels: ARCHETYPES += mucus_grazer; catalogue `akkermansia`; L2 teaches it at s210, right after the flora reef so the same-colour / opposite-trade contrast lands. Verified: brushing it dropped coat 100->88 while standing hit 36% in 3 tends (4x flora's rate); distinct blob vs the flora capsule. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
4bcb942312
commit
aae1ebdcfd
@ -55,9 +55,17 @@ export const BALANCE = {
|
|||||||
// no combo (enemies.js kill()): it sits in the enemy pool so it is shootable, but shooting
|
// no combo (enemies.js kill()): it sits in the enemy pool so it is shootable, but shooting
|
||||||
// it is the reputation trap, never a reward. `coat` kept small + no parking in a flow tube =
|
// it is the reputation trap, never a reward. `coat` kept small + no parking in a flow tube =
|
||||||
// a trickle, not a fountain. hp low: a stray pellet shouldn't wipe a colony you meant to spare.
|
// a trickle, not a fountain. hp low: a stray pellet shouldn't wipe a colony you meant to spare.
|
||||||
|
// No `gain` => flora:tended defaults to the small +5 standing in combat/index.js.
|
||||||
flora: { hp: 18, radius: 2.0, score: 0, drift: 1.2, bob: 0.4,
|
flora: { hp: 18, radius: 2.0, score: 0, drift: 1.2, bob: 0.4,
|
||||||
auraRadius: 3.6, coat: 6, auraTick: 0.3, pool: 24 },
|
auraRadius: 3.6, coat: 6, auraTick: 0.3, pool: 24 },
|
||||||
|
|
||||||
|
// mucus_grazer (Akkermansia) — a commensal that eats your mucin. Brushing it COSTS `coatCost`
|
||||||
|
// per tick but signals gut health hardest (`gain` 12 vs flora's 5): the "spend coat for
|
||||||
|
// faster allies" trade. Shares flora's drift+aura branch, sign-flipped by data. score 0,
|
||||||
|
// shootable (harming it is still flora:harmed). The clever risk/reward of the reputation loop.
|
||||||
|
mucus_grazer: { hp: 20, radius: 2.2, score: 0, drift: 1.0, bob: 0.35,
|
||||||
|
auraRadius: 4.0, coatCost: 4, gain: 12, auraTick: 0.3, pool: 16 },
|
||||||
|
|
||||||
// spore_pod — a foe budding-yeast/Candida cluster. Mechanically the floater (drift + corrosive
|
// spore_pod — a foe budding-yeast/Candida cluster. Mechanically the floater (drift + corrosive
|
||||||
// aura) with its OWN pool so its blobby mesh reads distinct from tumbling food debris. The
|
// aura) with its OWN pool so its blobby mesh reads distinct from tumbling food debris. The
|
||||||
// first entity authored to be skinned by a Trellis mesh (assets/models/spore_pod.glb).
|
// first entity authored to be skinned by a Trellis mesh (assets/models/spore_pod.glb).
|
||||||
|
|||||||
@ -63,6 +63,15 @@ const ARCHETYPES = {
|
|||||||
color: EMISSIVE.ally,
|
color: EMISSIVE.ally,
|
||||||
foe: false,
|
foe: false,
|
||||||
},
|
},
|
||||||
|
// mucus_grazer (Akkermansia) — a commensal that NIBBLES your coat (it eats mucin) but signals
|
||||||
|
// gut health hardest. Cyan (still flora, readability law), but a distinct amoeba-blob silhouette
|
||||||
|
// so the coat cost reads as "a different bug", not a betrayal by the healing rod.
|
||||||
|
mucus_grazer: {
|
||||||
|
cfg: B.enemies.mucus_grazer,
|
||||||
|
geo: () => new THREE.DodecahedronGeometry(B.enemies.mucus_grazer.radius, 0),
|
||||||
|
color: EMISSIVE.neutral,
|
||||||
|
foe: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createEnemies({ scene, world, bus, rng, assets = null }) {
|
export function createEnemies({ scene, world, bus, rng, assets = null }) {
|
||||||
@ -230,10 +239,12 @@ export function createEnemies({ scene, world, bus, rng, assets = null }) {
|
|||||||
e.auraT = cfg.auraTick;
|
e.auraT = cfg.auraTick;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (e.type === 'flora') {
|
} else if (e.type === 'flora' || e.type === 'mucus_grazer') {
|
||||||
// The FRIEND. Drifts like a floater but its aura HEALS: brush past and it sheds a
|
// COMMENSALS. Drift like a floater; brushing the aura raises BIOME STANDING (flora:tended)
|
||||||
// little mucus coat (the "mobile mucin lane"). Never damages you. It's still in the
|
// — that is the reputation you bank. The coat effect is data-driven and sign-flipped:
|
||||||
// enemy pool, so you CAN shoot it — and that is the reputation trap, handled in kill().
|
// flora sheds `coat` (a mobile mucin lane) + small standing (default +5).
|
||||||
|
// mucus_grazer nibbles `coatCost` (Akkermansia eats mucin) + big standing (`gain`).
|
||||||
|
// Neither ever "attacks" you; both stay shootable, which is the reputation trap (kill()).
|
||||||
e.phase += dt * cfg.bob;
|
e.phase += dt * cfg.bob;
|
||||||
e.s += Math.sin(e.phase) * cfg.drift * dt;
|
e.s += Math.sin(e.phase) * cfg.drift * dt;
|
||||||
e.x += Math.cos(e.phase * 0.7) * cfg.drift * dt;
|
e.x += Math.cos(e.phase * 0.7) * cfg.drift * dt;
|
||||||
@ -243,9 +254,10 @@ export function createEnemies({ scene, world, bus, rng, assets = null }) {
|
|||||||
const d = Math.hypot(e.s - ps.s, e.x - ps.x, e.y - ps.y);
|
const d = Math.hypot(e.s - ps.s, e.x - ps.x, e.y - ps.y);
|
||||||
e.auraT -= dt;
|
e.auraT -= dt;
|
||||||
if (d < cfg.auraRadius + player.radius && e.auraT <= 0) {
|
if (d < cfg.auraRadius + player.radius && e.auraT <= 0) {
|
||||||
player.refill({ coat: cfg.coat }); // friendly: gives coat instead of taking it
|
if (cfg.coat) player.refill({ coat: cfg.coat });
|
||||||
|
if (cfg.coatCost) player.damage(cfg.coatCost, 'graze');
|
||||||
e.auraT = cfg.auraTick;
|
e.auraT = cfg.auraTick;
|
||||||
bus.emit('flora:tended', { s: e.s }); // for the (future) BIOME STANDING meter
|
bus.emit('flora:tended', { s: e.s, gain: cfg.gain }); // gain undefined for flora => default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (e.type === 'phage') {
|
} else if (e.type === 'phage') {
|
||||||
|
|||||||
@ -81,7 +81,7 @@ export function createCombat({ scene, world, bus, rng, flags = {}, assets = null
|
|||||||
// first pass — C/playtest tunes the feel.
|
// first pass — C/playtest tunes the feel.
|
||||||
let standing = 0, phageBudCd = 0;
|
let standing = 0, phageBudCd = 0;
|
||||||
const setStanding = (v) => { standing = Math.max(0, Math.min(100, v)); bus.emit('standing', { value: standing }); };
|
const setStanding = (v) => { standing = Math.max(0, Math.min(100, v)); bus.emit('standing', { value: standing }); };
|
||||||
const offTended = bus.on('flora:tended', () => { setStanding(standing + 5); budPhage(); });
|
const offTended = bus.on('flora:tended', (e) => { setStanding(standing + (e?.gain ?? 5)); budPhage(); });
|
||||||
const offHarmed = bus.on('flora:harmed', () => setStanding(standing - 35));
|
const offHarmed = bus.on('flora:harmed', () => setStanding(standing - 35));
|
||||||
function budPhage() {
|
function budPhage() {
|
||||||
if (standing < 100 || phageBudCd > 0) return; // a FULL bar buds an ally
|
if (standing < 100 || phageBudCd > 0) return; // a FULL bar buds an ally
|
||||||
|
|||||||
@ -86,6 +86,7 @@
|
|||||||
{ "s": 20, "type": "checkpoint", "name": "Upper Esophageal Sphincter" },
|
{ "s": 20, "type": "checkpoint", "name": "Upper Esophageal Sphincter" },
|
||||||
{ "s": 120, "type": "pickup", "kind": "nutrient_orb", "count": 3, "spread": 40, "note": "Orb lines teach the racing line before they score anything." },
|
{ "s": 120, "type": "pickup", "kind": "nutrient_orb", "count": 3, "spread": 40, "note": "Orb lines teach the racing line before they score anything." },
|
||||||
{ "s": 180, "type": "spawn", "enemy": "lacto_drifter", "count": 4, "spread": 90, "rho": 0.7, "note": "MICROBE PASS (round 2) - the first FRIEND, taught first, in the calmest stretch. A Lactobacillus reef drifting off the racing line (cyan = friendly, ART_BIBLE). Brush it and it sheds mucus coat; SHOOT it and you have harmed the biome (flora:harmed) - the seed of the BIOME STANDING loop. Teaches 'don't shoot cyan' before any level charges for it." },
|
{ "s": 180, "type": "spawn", "enemy": "lacto_drifter", "count": 4, "spread": 90, "rho": 0.7, "note": "MICROBE PASS (round 2) - the first FRIEND, taught first, in the calmest stretch. A Lactobacillus reef drifting off the racing line (cyan = friendly, ART_BIBLE). Brush it and it sheds mucus coat; SHOOT it and you have harmed the biome (flora:harmed) - the seed of the BIOME STANDING loop. Teaches 'don't shoot cyan' before any level charges for it." },
|
||||||
|
{ "s": 210, "type": "spawn", "enemy": "akkermansia", "count": 2, "spread": 30, "rho": 0.5, "note": "MICROBE PASS (round 2) - the CHOICE node, taught right after flora so the contrast lands: same cyan family, opposite trade. Brushing Akkermansia nibbles a little coat but fills BIOME STANDING fast (gain 12) - spend coat to summon a phage ally sooner. Near the racing line so the choice is unavoidable." },
|
||||||
{ "s": 240, "type": "spawn", "enemy": "bolus_chunk", "count": 1, "note": "TEACH bolus: one chunk, slow, unmissable, harmless if you simply steer." },
|
{ "s": 240, "type": "spawn", "enemy": "bolus_chunk", "count": 1, "note": "TEACH bolus: one chunk, slow, unmissable, harmless if you simply steer." },
|
||||||
{ "s": 300, "type": "spawn", "enemy": "yeast_pod", "count": 1, "rho": 0.6, "note": "MICROBE PASS (round 2) - the spore_pod foe's teach beat: one drifting Candida cluster, off-line, harmless if you steer. A distinct blobby silhouette (Trellis-skinned), not the food-debris floater." },
|
{ "s": 300, "type": "spawn", "enemy": "yeast_pod", "count": 1, "rho": 0.6, "note": "MICROBE PASS (round 2) - the spore_pod foe's teach beat: one drifting Candida cluster, off-line, harmless if you steer. A distinct blobby silhouette (Trellis-skinned), not the food-debris floater." },
|
||||||
{ "s": 340, "type": "pickup", "kind": "nutrient_orb", "count": 3, "spread": 40 },
|
{ "s": 340, "type": "pickup", "kind": "nutrient_orb", "count": 3, "spread": 40 },
|
||||||
|
|||||||
@ -21,8 +21,9 @@
|
|||||||
|
|
||||||
/** The archetypes Lane B implements. Selfcheck enforces membership.
|
/** The archetypes Lane B implements. Selfcheck enforces membership.
|
||||||
* Round-2 microbe pass added `flora` (the first friend), `spore_pod` (a distinct-mesh foe),
|
* Round-2 microbe pass added `flora` (the first friend), `spore_pod` (a distinct-mesh foe),
|
||||||
* and `phage` (the first ally — a friendly seeker that hunts foes). */
|
* `phage` (the first ally — a friendly seeker that hunts foes), and `mucus_grazer` (a commensal
|
||||||
export const ARCHETYPES = ['floater', 'seeker', 'turret', 'flora', 'spore_pod', 'phage'];
|
* that trades your coat for faster standing). */
|
||||||
|
export const ARCHETYPES = ['floater', 'seeker', 'turret', 'flora', 'spore_pod', 'phage', 'mucus_grazer'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fields:
|
* Fields:
|
||||||
@ -100,6 +101,14 @@ export const ENEMIES = Object.freeze({
|
|||||||
'(teal, not the cyan "don\'t shoot" flora). Budded by high BIOME STANDING — the payoff ' +
|
'(teal, not the cyan "don\'t shoot" flora). Budded by high BIOME STANDING — the payoff ' +
|
||||||
'for tending flora instead of spraying it. Placeable by C, but mainly biome-spawned.',
|
'for tending flora instead of spraying it. Placeable by C, but mainly biome-spawned.',
|
||||||
},
|
},
|
||||||
|
akkermansia: {
|
||||||
|
archetype: 'mucus_grazer', biome: '*', tint: 0x39e6ff, wall: false,
|
||||||
|
note: 'Akkermansia muciniphila — lives in your mucus layer and EATS it, yet is a marker of ' +
|
||||||
|
'a lean healthy gut. The cleverest node in the reputation loop: brushing it COSTS a ' +
|
||||||
|
'little coat but fills BIOME STANDING fast (gain 12 vs flora\'s 5) — spend coat to earn ' +
|
||||||
|
'allies quicker. Cyan (a commensal), amoeba-blob shape so the coat cost reads as a ' +
|
||||||
|
'different bug, not a betrayal by the healing rod.',
|
||||||
|
},
|
||||||
|
|
||||||
// ---- passthrough: bare archetype names --------------------------------------------
|
// ---- passthrough: bare archetype names --------------------------------------------
|
||||||
// Compatibility so the round-0 stub level's `enemy: 'floater'` style still resolves and
|
// Compatibility so the round-0 stub level's `enemy: 'floater'` style still resolves and
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user