From d92db7ad44de5f7850d0b61517e6f55223b8c1d1 Mon Sep 17 00:00:00 2001 From: ChrisBeWithYou Date: Thu, 25 Jun 2026 07:26:59 -0500 Subject: [PATCH] =?UTF-8?q?feat(highway=5F3d):=20wave=202=20=E2=80=94=20ge?= =?UTF-8?q?m=20scale-punch,=20streak=20heat,=20colorblind=20verdict=20mark?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #3 (completion) gem scale-punch: the hit gem briefly grows (1 + 0.22·hitFx·alpha), biggest at the strike and easing with the verdict — the per-gem impulse. - #7 streak heat: a renderer-side consecutive-hit counter eases a 0..1 "heat" (plateau at 16) that grows the spark burst + warms the strike-line idle glow; a miss eases it back down. Behind the Streak-feedback toggle. - #6 colorblind verdict marks: a redundant ✓ (hit) / ✗ (miss) glyph on the verdict via the existing 2D label overlay, so the green/red pair isn't the only signal — notably also covers the provider path (where the timing labels don't show). - settings.html: Streak-feedback + Accessible-marks toggles. Deferred: #4 bloom+ACES (needs the Three.js postprocessing addons vendored into core static/vendor/three/ — not present; warrants its own infra change), and #5's timing tint (the early/late ±ms labels already render on the event path; surfacing them on the provider path needs a notedetect verdict field — a cross-plugin item). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_011Q9BpGYqUaga9ZJyS3dDPq --- plugins/highway_3d/screen.js | 14 ++++++++++++-- plugins/highway_3d/settings.html | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/plugins/highway_3d/screen.js b/plugins/highway_3d/screen.js index 3eb8e04..0e35e2f 100644 --- a/plugins/highway_3d/screen.js +++ b/plugins/highway_3d/screen.js @@ -3569,6 +3569,7 @@ const _SPARK_N = 256; const _sparkSeen = new Map(); // note-key -> expiry; one burst per hit let _juiceLastT = 0; // frame-dt clock for the juice layer + let _streakHits = 0, _streakHeat = 0; // #7 consecutive-hit escalation let fpsVisible = BG_DEFAULTS.fpsVisible; let fretDividersVisible = BG_DEFAULTS.fretDividersVisible; let chordDiagramVisible = BG_DEFAULTS.chordDiagramVisible; @@ -12671,6 +12672,7 @@ // blocks, so _showHit can be a const and _ndGood is available for the // sustain trail (which renders even when skipBody=true for slide targets). let _ndGood = false; // true when provider confirms hit/active + let _hitPunch = 1; // #3 per-gem scale-punch on a fresh hit let _ndState = null; // 'hit'|'active'|'miss'|null; null → fall back to proximity heuristic let _ndCs = null; // raw provider response — truthy when provider returned a verdict let _ndCsIsObj = false; // typeof _ndCs === 'object' @@ -12851,16 +12853,22 @@ _ndOutline = mMissOutline; _ndFaceMat = mMissEdgeArrays; if (_vAlpha > _ndMissFlash) _ndMissFlash = _vAlpha; + _streakHits = 0; // #7 break the streak (heat eases down) + if (_verdictMarks) _ndLabels.push({ x, y: y + NH * 1.7, z: noteZ + 0.02, labels: [{ text: '✗', color: '#ff5a7a' }] }); // #6 } else if (_ndGood) { _ndOutline = mHitBright[s] ?? mGlow[s]; _ndFaceMat = mHitBrightArrays[s] ?? null; if (_vAlpha > _ndHitFlash) _ndHitFlash = _vAlpha; + _hitPunch = 1 + 0.22 * _hitFx * _vAlpha; // #3 scale-punch (biggest at strike, eases) + if (_verdictMarks) _ndLabels.push({ x, y: y + NH * 1.7, z: noteZ + 0.02, labels: [{ text: '✓', color: '#22ff88' }] }); // #6 if (_hitFx > 0 && _vAlpha > 0.5) { const _spk = s + '|' + n.f + '|' + n.t.toFixed(2); if (!(_sparkSeen.get(_spk) > now)) { _sparkSeen.set(_spk, now + 1.0); if (_sparkSeen.size > 600) _sparkSeen.clear(); - _sparkBurst(x, y, noteZ, 0x22ff88, Math.round(7 + 13 * _hitFx)); + _streakHits++; + const _heatMul = _streakFx ? (1 + 0.85 * _streakHeat) : 1; // #7 escalate + _sparkBurst(x, y, noteZ, 0x22ff88, Math.round((7 + 13 * _hitFx) * _heatMul)); } } } @@ -12975,6 +12983,7 @@ } else { core.scale.set(rimXY, rimXY, 2.5 * rimZ); } + if (_hitPunch !== 1) core.scale.multiplyScalar(_hitPunch); // #3 hit scale-punch // Fret digits on fretted (n.f > 0) flying notes deliberately // omitted: the showFretOnNote setting and its UI helper text // promise digits on the fretboard ghost only, never on the @@ -14440,11 +14449,12 @@ const _jdt = _juiceLastT === 0 ? 1 / 60 : Math.min(0.05, (_jNow - _juiceLastT) / 1000); _juiceLastT = _jNow; _sparkUpdate(_jdt); + _streakHeat += (Math.min(1, _streakHits / 16) - _streakHeat) * 0.08; // #7 ease heat if (_strikeLine) { const hf = _ndHitFlash * _hitFx, mf = _ndMissFlash * _hitFx; const lit = Math.max(hf, mf); const m = _strikeLine.material; - m.opacity = 0.10 + 0.85 * Math.min(1, lit); + m.opacity = 0.10 + (_streakFx ? 0.12 * _streakHeat : 0) + 0.85 * Math.min(1, lit); if (lit > 0.002) m.color.setHex(hf >= mf ? 0x22ff88 : 0xff0066); else m.color.setHex(0x2a3a52); _ndHitFlash *= 0.82; _ndMissFlash *= 0.82; diff --git a/plugins/highway_3d/settings.html b/plugins/highway_3d/settings.html index 598f69f..86bb370 100644 --- a/plugins/highway_3d/settings.html +++ b/plugins/highway_3d/settings.html @@ -601,6 +601,26 @@ onchange="window.h3dBgSetCinematic && window.h3dBgSetCinematic(this.checked)" class="accent-accent mt-0.5"> + +
+ + +
+ +
+ + +