[claude] Add sovereignty Easter egg animation (#126) #185
99
app.js
99
app.js
@@ -297,6 +297,105 @@ window.addEventListener('player-left', (/** @type {CustomEvent} */ event) => {
|
||||
|
||||
window.addEventListener('chat-message', (/** @type {CustomEvent} */ event) => {
|
||||
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', () => {
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
<span class="photo-hint">[P] exit | [[] focus- []] focus+ focus: <span id="photo-focus">5.0</span></span>
|
||||
</div>
|
||||
|
||||
<div id="sovereignty-msg">⚡ SOVEREIGNTY ⚡</div>
|
||||
|
||||
<script type="module" src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
33
style.css
33
style.css
@@ -150,3 +150,36 @@ body.photo-mode #overview-indicator {
|
||||
#photo-focus {
|
||||
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