// ============================================================ // ALL FEEL LIVES HERE. Tune the game by editing this file only. // // The three knobs that matter most, in order: // 1. JITTER_STRENGTH — the life of the world // 2. the bond barrier — BARRIER_*, BOND_LOCK_DIST — the wrestle // 3. snap juice — SNAP_TRAUMA, HITSTOP, SNAP_SHOCKWAVE // ============================================================ export const CFG = { // ---- world ---- W: 1280, H: 720, DT: 1 / 120, // fixed sim step MAX_FRAME: 0.25, // clamp huge tab-switch deltas // ---- molly ---- MOLLY_RADIUS: 14, MOLLY_ACCEL: 3000, MOLLY_MAX_SPEED: 260, MOLLY_DAMP: 0.004, // fraction of velocity left after 1s MOLLY_JITTER: 26, // she never sits perfectly still // ---- particles / the storm ---- P_RADIUS: 11, JITTER_STRENGTH: 90, // px/s per sqrt(s) — Brownian, framerate-independent P_DAMP: 0.02, // fraction of velocity left after 1s COLLIDE_PUSH: 0.5, // overlap resolution strength // ---- charge interaction ---- CHARGE_RANGE: 190, K_CHARGE: 1.2e7, // F = K*q1*q2/d^2 D_MIN: 26, // clamp denominator MAX_CHARGE_FORCE: 3000, DISPLAY_LERP: 9, // charge color/shape morph speed (per second) // ---- hands / grab ---- GRAB_RANGE: 130, AIM_DIST: 92, // reticle offset from molly (gamepad) HAND_SPRING: 500, HAND_DAMP: 30, CHARGE_COOLDOWN: 0.16, // ---- THE BOND WRESTLE (the heart of the game) ---- SQUEEZE_RATE: 42, // px/s the hands close while you HOLD. the verb. // Radical pairs recombine barrierlessly, so the SQUEEZE has to be fast too — // dropping the barrier alone doesn't help when the hands are the bottleneck. // 1.8s is a wonderful puzzle payoff and a fatal combat move. RAD_SQUEEZE_MUL: 3.4, BOND_ATTEMPT_RANGE: 62, // where mutual attraction kicks in while held BOND_PULL: 900, // attraction while wrestling BARRIER_OUTER: 46, // barrier band starts here... // ...and peaks halfway to BOND_LOCK_DIST. // MUST stay > MAX_CHARGE_FORCE, or opposites bond themselves and the // wrestle — the whole game — gets skipped. The barrier is why Molly matters. BARRIER_FORCE: 3400, // Bond distances SCALE WITH ATOM SIZE. A flat lock distance is a trap: // two carbons (r13) can never get closer than 26px because collision // stops them, so a flat 22px lock means carbon can never bond at all. // Big atoms bond further out — which is also just true. LOCK_MUL: 0.92, // lock distance = (ra + rb) * this BOND_LEN_MUL: 0.95, // resting bond length = (ra + rb) * this BARRIER_BAND: 24, // barrier runs from lock -> lock + this BOND_LOCK_DIST: 22, // fallback only BOND_LENGTH: 25, // fallback only BOND_SPRING: 560, BOND_DAMP: 16, BOND_BREAK_DIST: 74, // ---- snap payoff ---- SNAP_TRAUMA: 0.62, HITSTOP: 0.05, SNAP_KNOCKBACK: 340, // shove on molly SNAP_SHOCKWAVE: 620, // radial impulse on nearby particles (this is a TOOL) SNAP_SHOCK_RADIUS: 170, SNAP_SPARKS: 26, // ---- zones ---- ZONE_FLIP_TIME: 1.2, // seconds of sour air before a neutral thing goes + ZONE_GRACE: 0.35, // timer reset after player acts on a particle // ---- membrane ---- MEM_THICK: 24, MEM_FORCE: 6000, MEM_BOUNCE_TRAUMA: 0.14, // ---- LASERHANDS ---- // Tap = bolt. Travel time, never hitscan — travel is what reads as "pyew". BOLT_SPEED: 940, BOLT_R: 4, BOLT_LIFE: 1.8, BOLT_COOLDOWN: 0.16, BOLT_HITSTOP: 45, // ms, requested through the juice budget BOLT_TRAUMA: 0.15, BOLT_RECOIL: 40, // molly shoved backward by give BOLT_PULL: 30, // molly tugged forward by take BOLT_CURVE: 0.30, // bolts are charged bodies -> they BEND round atoms BOLT_SPARKS: 8, HAND_KICK: 6, // px the hand sprite kicks back MUZZLE_LIFE: 0.09, // give SPENDS, take EARNS. you physically cannot spam one hand. AMMO_MAX: 8, AMMO_RING_R: 18, AMMO_SPIN: 1.2, // ---- OVERCHARGE / DETONATION ---- // Charge clamps at +-1 for handling, but you can OVERLOAD past it. // Cram 3 into one atom and it goes critical: a fuse, then a blast that // charges everything nearby — which can push THOSE past 3 too. Cascade. OVERLOAD_MAX: 3, FUSE_TIME: 0.9, BLAST_R: 130, BLAST_IMPULSE: 700, BLAST_HITSTOP: 160, BLAST_TRAUMA: 0.62, CHAIN_FUSE_MUL: 0.5, // a fuse lit by a blast burns faster // ---- THE RADICAL ---- RAD_TICK: 0.32, // deterministic hop interval. NEVER per-frame random. RAD_REACH: 70, // how far it can grab // 2nd victim this close -> it takes BOTH, and branch factor crosses 1. // MUST exceed the field's natural packing distance. The barrier repels every // bondable pair out to (lock + BARRIER_BAND) ~= 48px, so atoms self-space // around there; a split radius below that can never fire and the outbreak // would only ever crawl instead of blooming. RAD_SPLIT_R: 58, RAD_TELEGRAPH: 0.2, RAD_CAP: 120, RAD_KILL_HITSTOP: 130, RAD_KILL_TRAUMA: 0.7, RAD_QUENCH_R: 150, // annihilation puts out everything in this radius RAD_BARRIER_MUL: 0.15, // radical-radical is barrierless -> fast wrestle // ---- molly's life ---- MOLLY_ELECTRONS: 3, MOLLY_IFRAME: 1.1, MOLLY_DEATH: 0.4, // ---- juice budget ---- JUICE_WINDOW: 0.5, // seconds JUICE_HITSTOP_CAP: 200, // ms of hitstop allowed per window // ---- openers ---- HEAT_RADIUS: 155, HEAT_IMPULSE: 540, HEAT_JITTER_SPIKE: 2.4, HEAT_TRAUMA: 0.34, HEAT_COOLDOWN: 0.5, LIGHT_COOLDOWN: 0.35, // ---- camera / juice ---- CAM_LERP: 7, MAX_SHAKE: 20, MAX_ROT: 2 * Math.PI / 180, TRAUMA_DECAY: 1.5, // ---- palette ---- COL_POS: '#FF2D9B', COL_NEG: '#2DB8FF', COL_NEU: '#C8D0D8', // near-black reads as a VOID. ink-indigo reads as a MEDIUM. COL_BG: '#0B0716', COL_GRID: '#1A1030', GRID_STEP: 32, COL_MOLLY: '#FFE9A3', COL_MEM: '#7FE3C4', // zone washes (subtle — never hurt readability) TINT_SOUR: 'rgba(190, 210, 40, 0.055)', TINT_SOAPY: 'rgba(130, 110, 255, 0.055)', // ---- audio ---- MASTER_GAIN: 0.35, }; // charge state -> visual identity. Color AND shape, always redundant. // +1 positive : hot pink, SPIKY outline // 0 neutral : dim gray-white, flat & quiet // -1 negative : cyan, smooth with a soft pulsing halo export const CHARGE_COL = { '-1': CFG.COL_NEG, '0': CFG.COL_NEU, '1': CFG.COL_POS };