diff --git a/app.js b/app.js index 9ac2064..24a2180 100644 --- a/app.js +++ b/app.js @@ -1034,6 +1034,25 @@ function animate() { oathSpot.intensity = 3.8 + Math.sin(elapsed * 0.9) * 0.4; } + // Animate shockwave ripple rings + for (let i = shockwaveRings.length - 1; i >= 0; i--) { + const ring = shockwaveRings[i]; + const age = elapsed - ring.startTime - ring.delay; + if (age < 0) continue; + const t = Math.min(age / SHOCKWAVE_DURATION, 1); + if (t >= 1) { + scene.remove(ring.mesh); + ring.mesh.geometry.dispose(); + ring.mat.dispose(); + shockwaveRings.splice(i, 1); + continue; + } + // Ease out: fast at start, decelerates toward edge + const eased = 1 - Math.pow(1 - t, 2); + ring.mesh.scale.setScalar(eased * SHOCKWAVE_MAX_RADIUS + 0.1); + ring.mat.opacity = (1 - t) * 0.9; + } + // Animate rune ring — orbit and vertical float for (const rune of runeSprites) { const angle = rune.baseAngle + elapsed * RUNE_ORBIT_SPEED; @@ -1463,10 +1482,46 @@ function triggerSovereigntyEasterEgg() { requestAnimationFrame(fadeBack); } +// === SHOCKWAVE RIPPLE === +// Expanding ring waves that emanate from the platform center on PR merge. + +const SHOCKWAVE_RING_COUNT = 3; +const SHOCKWAVE_MAX_RADIUS = 14; +const SHOCKWAVE_DURATION = 2.5; // seconds per ring + +/** @type {Array<{mesh: THREE.Mesh, mat: THREE.MeshBasicMaterial, startTime: number, delay: number}>} */ +const shockwaveRings = []; + +/** + * Spawns a set of expanding concentric ripple rings from the scene centre. + * Called on PR merge alongside triggerMergeFlash(). + */ +function triggerShockwave() { + const now = clock.getElapsedTime(); + for (let i = 0; i < SHOCKWAVE_RING_COUNT; i++) { + const mat = new THREE.MeshBasicMaterial({ + color: 0x00ffff, + transparent: true, + opacity: 0, + side: THREE.DoubleSide, + depthWrite: false, + blending: THREE.AdditiveBlending, + }); + // Thin ring: inner=0.9, outer=1.0 — scaled uniformly to expand outward + const geo = new THREE.RingGeometry(0.9, 1.0, 64); + const mesh = new THREE.Mesh(geo, mat); + mesh.rotation.x = -Math.PI / 2; + mesh.position.y = 0.02; + scene.add(mesh); + shockwaveRings.push({ mesh, mat, startTime: now, delay: i * 0.35 }); + } +} + /** * Triggers a visual flash effect for merge events: stars pulse bright, lines glow. */ function triggerMergeFlash() { + triggerShockwave(); // Flash constellation lines bright blue-green const originalLineColor = constellationLines.material.color.getHex(); constellationLines.material.color.setHex(0x00ffff);