animate by message and fewer sound effects

This commit is contained in:
AlxAI 2025-03-12 12:36:23 -07:00
parent 4b6da987ea
commit 8884e13c14

View file

@ -114,6 +114,9 @@ let faceIconCache = {}; // Cache for generated face icons
// NEW: Add a lock for text-to-speech // NEW: Add a lock for text-to-speech
let isSpeaking = false; // Lock to pause game flow while TTS is active let isSpeaking = false; // Lock to pause game flow while TTS is active
// Add a message counter to track sound effect frequency
let messageCounter = 0;
// --- DOM ELEMENTS --- // --- DOM ELEMENTS ---
const loadBtn = document.getElementById('load-btn'); const loadBtn = document.getElementById('load-btn');
const fileInput = document.getElementById('file-input'); const fileInput = document.getElementById('file-input');
@ -1287,7 +1290,9 @@ function updateChatWindows(phase, stepMessages = false) {
relevantMessages.forEach(msg => { relevantMessages.forEach(msg => {
const isNew = addMessageToChat(msg, phase.name); const isNew = addMessageToChat(msg, phase.name);
if (isNew) { if (isNew) {
animateHeadNod(msg); // Increment message counter and play sound on every third message
messageCounter++;
animateHeadNod(msg, (messageCounter % 3 === 0));
} }
}); });
messagesPlaying = false; messagesPlaying = false;
@ -1296,6 +1301,7 @@ function updateChatWindows(phase, stepMessages = false) {
messagesPlaying = true; messagesPlaying = true;
let index = 0; let index = 0;
// Define the showNext function that will be called after each message animation completes
const showNext = () => { const showNext = () => {
if (index >= relevantMessages.length) { if (index >= relevantMessages.length) {
messagesPlaying = false; messagesPlaying = false;
@ -1305,16 +1311,21 @@ function updateChatWindows(phase, stepMessages = false) {
} }
return; return;
} }
const msg = relevantMessages[index]; const msg = relevantMessages[index];
const isNew = addMessageToChat(msg, phase.name); index++; // Increment index before adding message so word animation knows the correct next message
const isNew = addMessageToChat(msg, phase.name, true, showNext); // Pass showNext as callback
if (isNew && !isDebugMode) { if (isNew && !isDebugMode) {
animateHeadNod(msg); // Increment message counter
} messageCounter++;
index++;
// Increase the delay between messages - 3x the playback speed gives more spacing // Only animate head and play sound for every third message
// Remove the delay if we're developing animateHeadNod(msg, (messageCounter % 3 === 0));
if (isDebugMode) { } else if (isDebugMode) {
showNext() // In debug mode, immediately call showNext to skip waiting for animation
showNext();
} else { } else {
setTimeout(showNext, playbackSpeed * 3); setTimeout(showNext, playbackSpeed * 3);
} }
@ -1325,8 +1336,8 @@ function updateChatWindows(phase, stepMessages = false) {
} }
} }
// Modified to return whether this was a new message // Modified to support word-by-word animation and callback
function addMessageToChat(msg, phaseName) { function addMessageToChat(msg, phaseName, animateWords = false, onComplete = null) {
// Determine which chat window to use // Determine which chat window to use
let targetPower; let targetPower;
if (msg.recipient === 'GLOBAL') { if (msg.recipient === 'GLOBAL') {
@ -1334,7 +1345,7 @@ function addMessageToChat(msg, phaseName) {
} else { } else {
targetPower = msg.sender === currentPower ? msg.recipient : msg.sender; targetPower = msg.sender === currentPower ? msg.recipient : msg.sender;
} }
if (!chatWindows[targetPower]) return; if (!chatWindows[targetPower]) return false;
// Create a unique ID for this message to avoid duplication // Create a unique ID for this message to avoid duplication
const msgId = `${msg.sender}-${msg.recipient}-${msg.time_sent}-${msg.message}`; const msgId = `${msg.sender}-${msg.recipient}-${msg.time_sent}-${msg.message}`;
@ -1355,19 +1366,39 @@ function addMessageToChat(msg, phaseName) {
// Global chat shows sender info // Global chat shows sender info
const senderColor = msg.sender.toLowerCase(); const senderColor = msg.sender.toLowerCase();
messageElement.className = 'chat-message message-incoming'; messageElement.className = 'chat-message message-incoming';
messageElement.innerHTML = `
<span style="font-weight: bold;" class="power-${senderColor}">${msg.sender}:</span> // Add the header with the sender name immediately
${msg.message} const headerSpan = document.createElement('span');
<div class="message-time">${phaseName}</div> headerSpan.style.fontWeight = 'bold';
`; headerSpan.className = `power-${senderColor}`;
headerSpan.textContent = `${msg.sender}: `;
messageElement.appendChild(headerSpan);
// Create a span for the message content that will be filled word by word
const contentSpan = document.createElement('span');
contentSpan.id = `msg-content-${msgId.replace(/[^a-zA-Z0-9]/g, '-')}`;
messageElement.appendChild(contentSpan);
// Add timestamp
const timeDiv = document.createElement('div');
timeDiv.className = 'message-time';
timeDiv.textContent = phaseName;
messageElement.appendChild(timeDiv);
} else { } else {
// Private chat - outgoing or incoming style // Private chat - outgoing or incoming style
const isOutgoing = msg.sender === currentPower; const isOutgoing = msg.sender === currentPower;
messageElement.className = `chat-message ${isOutgoing ? 'message-outgoing' : 'message-incoming'}`; messageElement.className = `chat-message ${isOutgoing ? 'message-outgoing' : 'message-incoming'}`;
messageElement.innerHTML = `
${msg.message} // Create content span
<div class="message-time">${phaseName}</div> const contentSpan = document.createElement('span');
`; contentSpan.id = `msg-content-${msgId.replace(/[^a-zA-Z0-9]/g, '-')}`;
messageElement.appendChild(contentSpan);
// Add timestamp
const timeDiv = document.createElement('div');
timeDiv.className = 'message-time';
timeDiv.textContent = phaseName;
messageElement.appendChild(timeDiv);
} }
// Add to container // Add to container
@ -1375,12 +1406,79 @@ function addMessageToChat(msg, phaseName) {
// Scroll to bottom // Scroll to bottom
messagesContainer.scrollTop = messagesContainer.scrollHeight; messagesContainer.scrollTop = messagesContainer.scrollHeight;
if (animateWords) {
// Start word-by-word animation
const contentSpanId = `msg-content-${msgId.replace(/[^a-zA-Z0-9]/g, '-')}`;
animateMessageWords(msg.message, contentSpanId, targetPower, messagesContainer, onComplete);
} else {
// Show entire message at once
const contentSpan = messageElement.querySelector(`#msg-content-${msgId.replace(/[^a-zA-Z0-9]/g, '-')}`);
if (contentSpan) {
contentSpan.textContent = msg.message;
}
// If there's a completion callback, call it immediately for non-animated messages
if (onComplete) {
onComplete();
}
}
return true; // This was a new message return true; // This was a new message
} }
// Animate a head nod when a message appears // New function to animate message words one at a time
function animateHeadNod(msg) { function animateMessageWords(message, contentSpanId, targetPower, messagesContainer, onComplete) {
const words = message.split(/\s+/);
const contentSpan = document.getElementById(contentSpanId);
if (!contentSpan) {
// If span not found, still call onComplete to avoid breaking the game flow
if (onComplete) onComplete();
return;
}
// Clear any existing content
contentSpan.textContent = '';
let wordIndex = 0;
// Function to add the next word
const addNextWord = () => {
if (wordIndex >= words.length) {
// All words added - keep messagesPlaying true until next message starts
// Add a slight delay after the last word for readability
setTimeout(() => {
if (onComplete) {
onComplete(); // Call the completion callback
}
}, Math.min(playbackSpeed / 3, 150));
return;
}
// Add space if not the first word
if (wordIndex > 0) {
contentSpan.textContent += ' ';
}
// Add the next word
contentSpan.textContent += words[wordIndex];
wordIndex++;
// Schedule the next word with a delay based on word length and playback speed
const delay = Math.max(30, Math.min(120, playbackSpeed / 10 * (words[wordIndex-1].length / 4)));
setTimeout(addNextWord, delay);
// Scroll to ensure newest content is visible
messagesContainer.scrollTop = messagesContainer.scrollHeight;
};
// Start animation
addNextWord();
}
// Modified to support conditional sound effects
function animateHeadNod(msg, playSoundEffect = true) {
// Determine which chat window's head to animate // Determine which chat window's head to animate
let targetPower; let targetPower;
if (msg.recipient === 'GLOBAL') { if (msg.recipient === 'GLOBAL') {
@ -1459,8 +1557,10 @@ function animateHeadNod(msg) {
img.dataset.animating = 'false'; img.dataset.animating = 'false';
}; };
// Trigger random snippet // Trigger random snippet only if playSoundEffect is true
playRandomSoundEffect(); if (playSoundEffect) {
playRandomSoundEffect();
}
} }
// Generate a 3D face icon for chat windows with higher contrast // Generate a 3D face icon for chat windows with higher contrast