[claude] Add sovereignty Easter egg animation (#126) (#185)
Some checks failed
Deploy Nexus / deploy (push) Has been cancelled
Some checks failed
Deploy Nexus / deploy (push) Has been cancelled
This commit was merged in pull request #185.
This commit is contained in:
99
app.js
99
app.js
@@ -297,6 +297,105 @@ window.addEventListener('player-left', (/** @type {CustomEvent} */ event) => {
|
|||||||
|
|
||||||
window.addEventListener('chat-message', (/** @type {CustomEvent} */ event) => {
|
window.addEventListener('chat-message', (/** @type {CustomEvent} */ event) => {
|
||||||
console.log('Chat message:', event.detail);
|
console.log('Chat message:', event.detail);
|
||||||
|
if (typeof event.detail?.text === 'string' && event.detail.text.toLowerCase().includes('sovereignty')) {
|
||||||
|
triggerSovereigntyEasterEgg();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// === SOVEREIGNTY EASTER EGG ===
|
||||||
|
const SOVEREIGNTY_WORD = 'sovereignty';
|
||||||
|
let sovereigntyBuffer = '';
|
||||||
|
let sovereigntyBufferTimer = /** @type {ReturnType<typeof setTimeout>|null} */ (null);
|
||||||
|
|
||||||
|
const sovereigntyMsg = document.getElementById('sovereignty-msg');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers the sovereignty Easter egg: stars pulse gold, message flashes.
|
||||||
|
*/
|
||||||
|
function triggerSovereigntyEasterEgg() {
|
||||||
|
// Flash constellation lines gold
|
||||||
|
const originalLineColor = constellationLines.material.color.getHex();
|
||||||
|
constellationLines.material.color.setHex(0xffd700);
|
||||||
|
constellationLines.material.opacity = 0.9;
|
||||||
|
|
||||||
|
// Stars burst gold
|
||||||
|
const originalStarColor = starMaterial.color.getHex();
|
||||||
|
const originalStarOpacity = starMaterial.opacity;
|
||||||
|
starMaterial.color.setHex(0xffd700);
|
||||||
|
starMaterial.opacity = 1.0;
|
||||||
|
|
||||||
|
// Show overlay message
|
||||||
|
if (sovereigntyMsg) {
|
||||||
|
sovereigntyMsg.classList.remove('visible');
|
||||||
|
// Force reflow so animation restarts
|
||||||
|
void sovereigntyMsg.offsetWidth;
|
||||||
|
sovereigntyMsg.classList.add('visible');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animate gold fade-out over 2.5s
|
||||||
|
const startTime = performance.now();
|
||||||
|
const DURATION = 2500;
|
||||||
|
|
||||||
|
function fadeBack() {
|
||||||
|
const t = Math.min((performance.now() - startTime) / DURATION, 1);
|
||||||
|
const eased = t * t; // ease in: slow start, fast end
|
||||||
|
|
||||||
|
// Interpolate star color back
|
||||||
|
const goldR = 1.0, goldG = 0.843, goldB = 0;
|
||||||
|
const origColor = new THREE.Color(originalStarColor);
|
||||||
|
starMaterial.color.setRGB(
|
||||||
|
goldR + (origColor.r - goldR) * eased,
|
||||||
|
goldG + (origColor.g - goldG) * eased,
|
||||||
|
goldB + (origColor.b - goldB) * eased
|
||||||
|
);
|
||||||
|
starMaterial.opacity = 1.0 + (originalStarOpacity - 1.0) * eased;
|
||||||
|
|
||||||
|
// Interpolate line color back
|
||||||
|
const origLineColor = new THREE.Color(originalLineColor);
|
||||||
|
constellationLines.material.color.setRGB(
|
||||||
|
1.0 + (origLineColor.r - 1.0) * eased,
|
||||||
|
0.843 + (origLineColor.g - 0.843) * eased,
|
||||||
|
0 + origLineColor.b * eased
|
||||||
|
);
|
||||||
|
|
||||||
|
if (t < 1) {
|
||||||
|
requestAnimationFrame(fadeBack);
|
||||||
|
} else {
|
||||||
|
// Restore originals exactly
|
||||||
|
starMaterial.color.setHex(originalStarColor);
|
||||||
|
starMaterial.opacity = originalStarOpacity;
|
||||||
|
constellationLines.material.color.setHex(originalLineColor);
|
||||||
|
if (sovereigntyMsg) sovereigntyMsg.classList.remove('visible');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(fadeBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect 'sovereignty' typed anywhere on the page (cheat-code style)
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
||||||
|
if (e.key.length !== 1) {
|
||||||
|
// Non-printable key resets buffer
|
||||||
|
sovereigntyBuffer = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sovereigntyBuffer += e.key.toLowerCase();
|
||||||
|
|
||||||
|
// Keep only the last N chars needed
|
||||||
|
if (sovereigntyBuffer.length > SOVEREIGNTY_WORD.length) {
|
||||||
|
sovereigntyBuffer = sovereigntyBuffer.slice(-SOVEREIGNTY_WORD.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sovereigntyBuffer === SOVEREIGNTY_WORD) {
|
||||||
|
sovereigntyBuffer = '';
|
||||||
|
triggerSovereigntyEasterEgg();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset buffer after 3s of inactivity
|
||||||
|
if (sovereigntyBufferTimer) clearTimeout(sovereigntyBufferTimer);
|
||||||
|
sovereigntyBufferTimer = setTimeout(() => { sovereigntyBuffer = ''; }, 3000);
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('beforeunload', () => {
|
window.addEventListener('beforeunload', () => {
|
||||||
|
|||||||
@@ -46,6 +46,8 @@
|
|||||||
<span class="photo-hint">[P] exit | [[] focus- []] focus+ focus: <span id="photo-focus">5.0</span></span>
|
<span class="photo-hint">[P] exit | [[] focus- []] focus+ focus: <span id="photo-focus">5.0</span></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="sovereignty-msg">⚡ SOVEREIGNTY ⚡</div>
|
||||||
|
|
||||||
<script type="module" src="app.js"></script>
|
<script type="module" src="app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
33
style.css
33
style.css
@@ -150,3 +150,36 @@ body.photo-mode #overview-indicator {
|
|||||||
#photo-focus {
|
#photo-focus {
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* === SOVEREIGNTY EASTER EGG === */
|
||||||
|
#sovereignty-msg {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
color: #ffd700;
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: 13px;
|
||||||
|
letter-spacing: 0.3em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 30;
|
||||||
|
border: 1px solid #ffd700;
|
||||||
|
padding: 8px 20px;
|
||||||
|
background: rgba(0, 0, 8, 0.7);
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sovereignty-msg.visible {
|
||||||
|
display: block;
|
||||||
|
animation: sovereignty-flash 2.5s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sovereignty-flash {
|
||||||
|
0% { opacity: 0; transform: translate(-50%, -50%) scale(0.85); }
|
||||||
|
15% { opacity: 1; transform: translate(-50%, -50%) scale(1.05); }
|
||||||
|
40% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
|
||||||
|
100% { opacity: 0; transform: translate(-50%, -50%) scale(1); }
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user