diff --git a/src/_includes/footer.njk b/src/_includes/footer.njk index 7c0b85f..cee498f 100644 --- a/src/_includes/footer.njk +++ b/src/_includes/footer.njk @@ -124,3 +124,26 @@ [ESC] EXIT_SIMULATION + +
+
+ +
+
+ System_Log +
+ +
+ + + +
+
+ +
+

// Terminal link established...

+
+
+
diff --git a/src/assets/css/style.css b/src/assets/css/style.css index ab9df84..ca8a66f 100644 --- a/src/assets/css/style.css +++ b/src/assets/css/style.css @@ -583,3 +583,150 @@ a:hover { filter: brightness(1.2); text-decoration: underline; } + +@keyframes badgePop { + 0% { transform: scale(1); filter: brightness(1); } + 50% { transform: scale(1.4); filter: brightness(1.5); } + 100% { transform: scale(1); filter: brightness(1); } +} + +.animate-badge-pop { + animation: badgePop 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards; + z-index: 101; /* Ensure it pops above the header bar */ +} + +/* Optional: Add a glow to the level number too */ +#level-number { + transition: all 0.3s ease; +} +.animate-badge-pop #level-number { + color: white; + text-shadow: 0 0 10px rgba(255,255,255,1); +} + + + +.level-up-toast { + position: fixed; + top: 20px; + left: 50%; + transform: translateX(-50%); + background: rgba(15, 23, 42, 0.9); /* Dark background for contrast */ + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.1); + padding: 16px 24px; + border-radius: 12px; + box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5), + 0 0 15px var(--accent); /* Glow matches current level */ + z-index: 1000; + animation: slideDownIn 0.5s cubic-bezier(0.18, 0.89, 0.32, 1.28) forwards; +} + +.toast-content { + display: flex; + align-items: center; + gap: 16px; +} + +.toast-emoji { + font-size: 2rem; +} + +.toast-title { + color: #94a3b8; + font-size: 0.7rem; + font-weight: 800; + letter-spacing: 0.1rem; + margin: 0; +} + +.toast-rank { + font-size: 1.25rem; + font-weight: 900; + margin: 0; + text-transform: uppercase; +} + +@keyframes slideDownIn { + from { transform: translate(-50%, -100px); opacity: 0; } + to { transform: translate(-50%, 0); opacity: 1; } +} + +.fade-out { + opacity: 0; + transform: translate(-50%, -20px); + transition: all 0.5s ease; +} +.matrix-alert { + position: fixed; + bottom: 20%; + right: 20px; + background: rgba(0, 0, 0, 0.9); + border-left: 4px solid #10b981; + color: #10b981; + padding: 15px; + font-family: 'Courier New', monospace; + z-index: 2000; + box-shadow: 0 0 20px rgba(16, 185, 129, 0.2); + animation: matrixSlideIn 0.3s ease-out; +} + +@keyframes matrixSlideIn { + from { transform: translateX(100%); opacity: 0; } + to { transform: translateX(0); opacity: 1; } +} + +#matrix-console-output { + /* Subtle green glow on text */ + text-shadow: 0 0 5px rgba(16, 185, 129, 0.3); + scroll-behavior: smooth; + display: flex; + flex-direction: column; +} + +/* Custom scrollbar for the terminal */ +#matrix-console-output::-webkit-scrollbar { + width: 3px; +} +#matrix-console-output::-webkit-scrollbar-thumb { + background: rgba(16, 185, 129, 0.3); +} + +/* The Matrix Line Animation */ +.matrix-line { + border-left: 2px solid #10b981; + padding-left: 8px; + margin-bottom: 4px; + animation: matrixLineFade 0.3s ease-out; +} + +@keyframes matrixLineFade { + from { opacity: 0; transform: translateX(-10px); } + to { opacity: 1; transform: translateX(0); } +} +/* Maximize State */ +.console-maximized { + width: 90vw !important; + height: auto !important; + bottom: 5vh !important; + right: 5vw !important; + left: 5vw !important; + z-index: 2000 !important; +} + +#matrix-console-output { + scrollbar-width: thin; + scrollbar-color: #10b981 transparent; +} + +/* Add a scanline effect for that retro terminal feel */ +#matrix-console-output::before { + content: " "; + position: absolute; + top: 0; left: 0; bottom: 0; right: 0; + background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), + linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06)); + z-index: 10; + background-size: 100% 2px, 3px 100%; + pointer-events: none; +} diff --git a/src/assets/js/script.js b/src/assets/js/script.js index 0b9b8b2..5a30244 100644 --- a/src/assets/js/script.js +++ b/src/assets/js/script.js @@ -354,6 +354,67 @@ function getRank(lvl) { return rank; } + +const consoleContainer = document.getElementById('matrix-console-container'); +const consoleOutput = document.getElementById('matrix-console-output'); + +function minimizeConsole() { + // Toggles the height of the output area + if (consoleOutput.style.display === 'none') { + consoleOutput.style.display = 'block'; + consoleContainer.style.width = '20rem'; // w-80 + } else { + consoleOutput.style.display = 'none'; + consoleContainer.style.width = '150px'; // Compact view + } +} + +function maximizeConsole() { + // Toggles a full-screen-ish mode + consoleContainer.classList.toggle('console-maximized'); + + // Adjust height when maximized + if (consoleContainer.classList.contains('console-maximized')) { + consoleOutput.style.height = '70vh'; + consoleOutput.style.display = 'block'; + } else { + consoleOutput.style.height = '12rem'; // h-48 + } +} + +function closeConsole() { + const container = document.getElementById('matrix-console-container'); + const reopenBtn = document.getElementById('reopen-console-btn'); + + // Hide the console + container.style.opacity = '0'; + container.style.transform = 'translateY(20px)'; + + setTimeout(() => { + container.classList.add('hidden'); + // Show the small reopen button + if (reopenBtn) reopenBtn.classList.remove('hidden'); + }, 300); +} + +function reopenConsole() { + const container = document.getElementById('matrix-console-container'); + const reopenBtn = document.getElementById('reopen-console-btn'); + + // Show the console + container.classList.remove('hidden'); + + // Trigger reflow for animation + void container.offsetWidth; + + container.style.opacity = '1'; + container.style.transform = 'translateY(0)'; + + // Hide the reopen button + if (reopenBtn) reopenBtn.classList.add('hidden'); +} + + let isProcessingXP = false; // Ensure this is in the GLOBAL scope (not hidden inside another function) @@ -884,6 +945,69 @@ function renderXP(value) { // console.log(`XP: ${currentXPNum}, Percent: ${percentage}%`); } +function showLevelUpToast(rank) { + // 1. Create the container + const toast = document.createElement('div'); + toast.className = 'level-up-toast'; + + // 2. Build the inner content + // We use the rank color for the name and emoji to make it feel custom + toast.innerHTML = ` +
+ ${rank.emoji} +
+

LEVEL UP!

+

${rank.name}

+
+
+ `; + + document.body.appendChild(toast); + + // 3. Auto-remove after animation + setTimeout(() => { + toast.classList.add('fade-out'); + setTimeout(() => toast.remove(), 500); + }, 2500); +} + + +function matrixConsoleLog(level) { + const rank = getRank(level); + + // This looks awesome in the F12 Dev Console + console.log( + `%c [SYSTEM] %c LEVEL UP: %c ${rank.name.toUpperCase()} %c [LVL ${level}] `, + "color: #10b981; font-weight: bold; background: #064e3b; padding: 2px;", + "color: #ffffff; background: #1e293b; padding: 2px;", + `color: ${rank.color}; font-weight: 900; background: #1e293b; padding: 2px;`, + "color: #94a3b8; background: #1e293b; padding: 2px;" + ); + + // 3. If you have an on-screen Matrix Console element, push there too: + const matrixConsole = document.getElementById('matrix-console-output'); + if (matrixConsole) { + const line = document.createElement('p'); + line.className = 'matrix-line text-xs font-mono mb-1'; + line.innerHTML = `>> Rank Updated: ${rank.name}`; + matrixConsole.appendChild(line); + // Auto-scroll to bottom + matrixConsole.scrollTop = matrixConsole.scrollHeight; + } +} + +document.addEventListener('keydown', (e) => { + // Check if user pressed 'L' (for Log) and isn't typing in an input field + if (e.key.toLowerCase() === 'l' && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') { + const container = document.getElementById('matrix-console-container'); + if (container.classList.contains('hidden')) { + reopenConsole(); + } else { + closeConsole(); + } + } +}); + async function addExperience(amount) { // 1. Force strict numeric types to prevent "1" + "1" = "11" let xpToAdd = Number(amount) || 0; @@ -898,14 +1022,29 @@ async function addExperience(amount) { // Using a while loop ensures that if you gain 100 XP, // it processes Level 1, then Level 2, with the remainder left over. while (currentXP >= XP_THRESHOLD && currentLevel < 200) { - currentXP -= XP_THRESHOLD; // Subtract exactly the cost of one level + currentXP -= XP_THRESHOLD; currentLevel++; + // 1. Trigger the Visual Toast (Top of screen) + if (typeof showLevelUpToast === 'function') { + showLevelUpToast(getRank(currentLevel)); + } - // Safety: Ensure we don't end up with negative XP from rounding - currentXP = Math.max(0, currentXP); + // 2. Trigger the "Matrix" Console Log + matrixConsoleLog(currentLevel); + + // --- THE POPUP TRIGGER --- + const badge = document.getElementById('level-badge'); + if (badge) { + // Remove the class if it exists (to reset animation) + badge.classList.remove('animate-badge-pop'); + // Trigger a "reflow" (magic trick to allow re-animation) + void badge.offsetWidth; + // Re-add the class + badge.classList.add('animate-badge-pop'); + } + // -------------------------- - // Optional: Trigger level-up specific effects here - console.log(`Leveled Up! Now Level: ${currentLevel}`); + console.log(`Leveled Up to ${currentLevel}!`); } // 4. Persistence: Save clean numbers diff --git a/src/index.njk b/src/index.njk index 3ed9456..2fe8fab 100644 --- a/src/index.njk +++ b/src/index.njk @@ -64,13 +64,13 @@ layout: false - {% include "footer.njk" %} - + {% include "footer.njk" %} +