feat: implement Timmy's mood lighting for Nexus atmosphere
This commit is contained in:
53
app.js
53
app.js
@@ -14,7 +14,14 @@ const NEXUS = {
|
||||
constellationLine: 0x334488,
|
||||
constellationFade: 0x112244,
|
||||
accent: 0x4488ff,
|
||||
}
|
||||
},
|
||||
moods: {
|
||||
content: { bg: 0x002828, ambient: 1.4, particleIntensity: 0.9, particleSpeed: 0.01 },
|
||||
busy: { bg: 0x003838, ambient: 1.8, particleIntensity: 1.2, particleSpeed: 0.02 },
|
||||
contemplative: { bg: 0x000818, ambient: 0.8, particleIntensity: 0.6, particleSpeed: 0.005 },
|
||||
error: { bg: 0x280808, ambient: 1.0, particleIntensity: 0.8, particleSpeed: 0.01 }
|
||||
},
|
||||
currentMood: 'content'
|
||||
};
|
||||
|
||||
// === ASSET LOADER ===
|
||||
@@ -343,6 +350,37 @@ window.addEventListener('resize', () => {
|
||||
// === ANIMATION LOOP ===
|
||||
const clock = new THREE.Clock();
|
||||
|
||||
// Mood derivation variables
|
||||
let chatActivity = 0;
|
||||
let prMergeRate = 0;
|
||||
let errorCount = 0;
|
||||
let lastMoodUpdate = 0;
|
||||
|
||||
/**
|
||||
* Derives Timmy's mood based on current metrics.
|
||||
*/
|
||||
function deriveMood() {
|
||||
const now = clock.getElapsedTime();
|
||||
if (now - lastMoodUpdate < 5) return; // Update mood every 5 seconds
|
||||
lastMoodUpdate = now;
|
||||
|
||||
if (errorCount > 3) {
|
||||
NEXUS.currentMood = 'error';
|
||||
} else if (chatActivity > 5 || prMergeRate > 1) {
|
||||
NEXUS.currentMood = 'busy';
|
||||
} else if (chatActivity < 2 && prMergeRate === 0) {
|
||||
NEXUS.currentMood = 'contemplative';
|
||||
} else {
|
||||
NEXUS.currentMood = 'content';
|
||||
}
|
||||
|
||||
// Update scene atmosphere based on mood
|
||||
const mood = NEXUS.moods[NEXUS.currentMood];
|
||||
scene.background = new THREE.Color(mood.bg);
|
||||
ambientLight.intensity = mood.ambient;
|
||||
starMaterial.opacity = mood.particleIntensity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main animation loop — called each frame via requestAnimationFrame.
|
||||
* @returns {void}
|
||||
@@ -351,6 +389,9 @@ function animate() {
|
||||
// Only start animation after assets are loaded
|
||||
requestAnimationFrame(animate);
|
||||
const elapsed = clock.getElapsedTime();
|
||||
|
||||
// Update mood based on metrics
|
||||
deriveMood();
|
||||
|
||||
// Smooth camera transition for overview mode
|
||||
const targetT = overviewMode ? 1 : 0;
|
||||
@@ -440,6 +481,10 @@ import { wsClient } from './ws-client.js';
|
||||
|
||||
wsClient.connect();
|
||||
|
||||
// Simulate error count for mood derivation (replace with real error tracking)
|
||||
setInterval(() => {
|
||||
if (Math.random() < 0.1) errorCount++;
|
||||
}, 10000);
|
||||
window.addEventListener('player-joined', (/** @type {CustomEvent} */ event) => {
|
||||
console.log('Player joined:', event.detail);
|
||||
});
|
||||
@@ -450,6 +495,7 @@ window.addEventListener('player-left', (/** @type {CustomEvent} */ event) => {
|
||||
|
||||
window.addEventListener('chat-message', (/** @type {CustomEvent} */ event) => {
|
||||
console.log('Chat message:', event.detail);
|
||||
chatActivity++;
|
||||
if (typeof event.detail?.text === 'string' && event.detail.text.toLowerCase().includes('sovereignty')) {
|
||||
triggerSovereigntyEasterEgg();
|
||||
}
|
||||
@@ -615,7 +661,10 @@ async function initCommitBanners() {
|
||||
];
|
||||
|
||||
// Load commit banners after assets are ready
|
||||
initCommitBanners();
|
||||
initCommitBanners();
|
||||
|
||||
// Update PR merge rate for mood derivation
|
||||
prMergeRate++;
|
||||
}
|
||||
|
||||
const spreadX = [-7, -3.5, 0, 3.5, 7];
|
||||
|
||||
Reference in New Issue
Block a user