Skip to content

Commit 7ab932a

Browse files
authored
Force surge effects (#316)
1 parent 488b3ca commit 7ab932a

File tree

3 files changed

+130
-25
lines changed

3 files changed

+130
-25
lines changed

src/_includes/system-override.njk

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
<button onclick="triggerSecretUnlock('matrix')" class="override-btn py-1.5 bg-green-500/5 hover:bg-green-500/20 text-green-500 text-[8px] border border-green-500/20 rounded transition-all">
1818
MATRIX
1919
</button>
20-
<button onclick="triggerSecretUnlock('footer_surge')"
21-
class="override-btn py-1.5 px-4 bg-cyan-500/10 hover:bg-cyan-400/30 text-cyan-300 text-[8px] font-black tracking-widest border border-cyan-400/40 rounded transition-all duration-300 shadow-[0_0_10px_rgba(6,182,212,0.1)] hover:shadow-[0_0_20px_rgba(6,182,212,0.4)] animate-pulse-slow uppercase">
22-
⚡ SURGE
20+
<button onclick="triggerForceSurge()"
21+
class="relative overflow-hidden py-2 px-6 bg-blue-600/20 text-blue-300 border-2 border-blue-400 rounded-lg font-bold tracking-[0.2em] transition-all hover:bg-blue-500/40 hover:shadow-[0_0_30px_rgba(59,130,246,0.6)] animate-pulse uppercase">
22+
<span class="relative z-10">Force Surge</span>
23+
<div class="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full animate-[shimmer_2s_infinite]"></div>
2324
</button>
2425
</div>
2526

src/assets/css/style.css

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -534,22 +534,6 @@ body[data-level="6"]::after {
534534
}
535535
}
536536

537-
/* The Active Class */
538-
.force-glow {
539-
--glow-color: #38bdf8; /* Default Jedi Blue */
540-
position: relative;
541-
z-index: 10;
542-
transition: all 0.5s ease;
543-
animation: force-pulse 10s infinite ease-in-out;
544-
}
545-
546-
/* Sith Variant (Red Glow) */
547-
[data-level^="13"],
548-
[data-level^="14"],
549-
[data-level^="15"] .force-glow {
550-
--glow-color: #ef4444; /* Sith Red */
551-
}
552-
553537
/* Ensure the parent container has a background so you can see the 'empty' part */
554538
#game-stats .bg-black\/10 {
555539
background-color: rgba(0, 0, 0, 0.1) !important;
@@ -873,3 +857,58 @@ a:hover {
873857
#matrix-console-container.is-dragging {
874858
user-select: none;
875859
}
860+
861+
/* Full-screen Force Field */
862+
.force-field-overlay {
863+
position: fixed !important;
864+
inset: 0 !important;
865+
z-index: 999999 !important;
866+
background: radial-gradient(
867+
circle,
868+
rgba(0, 150, 255, 0.1) 0%,
869+
rgba(0, 0, 50, 0.6) 100%
870+
);
871+
pointer-events: none;
872+
box-shadow: inset 0 0 150px rgba(0, 190, 255, 0.5);
873+
backdrop-filter: blur(2px) contrast(1.2);
874+
animation: force-throb 4s ease-in-out infinite;
875+
}
876+
877+
@keyframes force-throb {
878+
0%,
879+
100% {
880+
opacity: 0.7;
881+
}
882+
50% {
883+
opacity: 1;
884+
}
885+
}
886+
887+
/* Bright Lightning Arcs */
888+
.lightning-flash {
889+
position: fixed;
890+
inset: 0;
891+
z-index: 1000000;
892+
pointer-events: none;
893+
background: white;
894+
opacity: 0;
895+
}
896+
897+
@keyframes bolt {
898+
0% {
899+
opacity: 0;
900+
}
901+
10% {
902+
opacity: 1;
903+
background: #99f6ff;
904+
}
905+
20% {
906+
opacity: 0;
907+
}
908+
25% {
909+
opacity: 0.8;
910+
}
911+
30% {
912+
opacity: 0;
913+
}
914+
}

src/assets/js/script.js

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -369,17 +369,82 @@ function updateThemeIcon(theme) {
369369
/**
370370
* 5. EASTER EGG LOGIC & TRIGGERS
371371
*/
372-
function triggerForceSurge() {
373-
if (isSurging) return; // Prevent overlapping surges
372+
function playForceSoundtrack(duration = 8000) {
373+
initAudio();
374+
if (!audioCtx) return;
375+
376+
// 1. Create a Master Compressor to prevent distortion
377+
const compressor = audioCtx.createDynamicsCompressor();
378+
compressor.threshold.setValueAtTime(-24, audioCtx.currentTime);
379+
compressor.knee.setValueAtTime(40, audioCtx.currentTime);
380+
compressor.ratio.setValueAtTime(12, audioCtx.currentTime);
381+
compressor.attack.setValueAtTime(0, audioCtx.currentTime);
382+
compressor.release.setValueAtTime(0.25, audioCtx.currentTime);
383+
compressor.connect(audioCtx.destination);
384+
385+
const now = audioCtx.currentTime;
386+
const masterGain = audioCtx.createGain();
387+
masterGain.connect(compressor);
388+
389+
// Smooth volume swell
390+
masterGain.gain.setValueAtTime(0, now);
391+
masterGain.gain.linearRampToValueAtTime(0.6, now + 1.5);
392+
masterGain.gain.setValueAtTime(0.6, now + duration / 1000 - 2);
393+
masterGain.gain.exponentialRampToValueAtTime(0.001, now + duration / 1000);
394+
395+
// Create the "Harmonic Stack" (Multiple notes for a rich sound)
396+
[55, 110, 164.81].forEach((freq, i) => {
397+
const osc = audioCtx.createOscillator();
398+
osc.type = i === 0 ? "sawtooth" : "triangle"; // Mix textures
399+
osc.frequency.setValueAtTime(freq, now);
400+
401+
// Add a slight "wobble" (detune) for realism
402+
osc.detune.setValueAtTime(i * 5, now);
374403

404+
osc.connect(masterGain);
405+
osc.start(now);
406+
osc.stop(now + duration / 1000);
407+
});
408+
}
409+
410+
function triggerForceSurge() {
411+
if (isSurging) return;
375412
isSurging = true;
376-
initAudio();
377-
addExperience(1000);
378413

379-
// Reset after the animation duration (e.g., 1 second)
414+
// 1. Audio
415+
playForceSoundtrack(8000);
416+
417+
// 2. Add Overlay
418+
const overlay = document.createElement("div");
419+
overlay.className = "force-field-overlay";
420+
document.body.appendChild(overlay);
421+
422+
// 3. Lightning Bolts (Flash every 1.5 seconds)
423+
const flash = document.createElement("div");
424+
flash.className = "lightning-flash";
425+
document.body.appendChild(flash);
426+
427+
const boltInterval = setInterval(() => {
428+
flash.style.animation = "none";
429+
void flash.offsetWidth; // Reset animation
430+
flash.style.animation = "bolt 0.4s ease-out";
431+
}, 1800);
432+
433+
// 4. XP Logic
434+
let currentXPAdded = 0;
435+
const xpInt = setInterval(() => {
436+
addExperience(10);
437+
currentXPAdded += 10;
438+
if (currentXPAdded >= 1000) clearInterval(xpInt);
439+
}, 50);
440+
441+
// 5. Cleanup
380442
setTimeout(() => {
443+
clearInterval(boltInterval);
444+
overlay.remove();
445+
flash.remove();
381446
isSurging = false;
382-
}, 10000);
447+
}, 8000);
383448
}
384449

385450
function triggerMagicXP() {

0 commit comments

Comments
 (0)