mirror of
https://github.com/GoodStartLabs/AI_Diplomacy.git
synced 2026-04-19 12:58:09 +00:00
animate by message and fewer sound effects
This commit is contained in:
parent
4b6da987ea
commit
8884e13c14
1 changed files with 125 additions and 25 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue