diff --git a/ai_animation/src/domElements/chatWindows.ts b/ai_animation/src/domElements/chatWindows.ts index ade89f3..0f4a0a6 100644 --- a/ai_animation/src/domElements/chatWindows.ts +++ b/ai_animation/src/domElements/chatWindows.ts @@ -178,65 +178,6 @@ export function updateChatWindows(phase: any, stepMessages = false) { } }); gameState.messagesPlaying = false; - - // If instant chat is enabled during stepwise mode, immediately proceed to next phase logic - if (stepMessages && config.isInstantMode) { - // Trigger the same logic as the end of stepwise message display - if (gameState.isPlaying && !gameState.isSpeaking) { - if (gameState.gameData && gameState.gameData.phases) { - const currentPhase = gameState.gameData.phases[gameState.phaseIndex]; - - if (config.isDebugMode) { - console.log(`Instant chat enabled - processing end of phase ${currentPhase.name}`); - } - - // Show summary first if available - if (currentPhase.summary?.trim()) { - addToNewsBanner(`(${currentPhase.name}) ${currentPhase.summary}`); - } - - // Get previous phase for animations - const prevIndex = gameState.phaseIndex > 0 ? gameState.phaseIndex - 1 : null; - const previousPhase = prevIndex !== null ? gameState.gameData.phases[prevIndex] : null; - - // Only schedule next phase if not already scheduled - if (!gameState.nextPhaseScheduled) { - gameState.nextPhaseScheduled = true; - - // Show animations for current phase's orders - if (previousPhase) { - if (config.isDebugMode) { - console.log(`Animating orders from ${previousPhase.name} to ${currentPhase.name}`); - } - - // After animations complete, advance to next phase with longer delay - gameState.playbackTimer = setTimeout(() => { - if (gameState.isPlaying) { - if (config.isDebugMode) { - console.log(`Animations complete, advancing from ${currentPhase.name}`); - } - advanceToNextPhase(); - } - }, config.playbackSpeed + config.animationDuration); // Wait for both summary and animations - } else { - // For first phase, use shorter delay since there are no animations - if (config.isDebugMode) { - console.log(`First phase ${currentPhase.name} - no animations to wait for`); - } - - gameState.playbackTimer = setTimeout(() => { - if (gameState.isPlaying) { - if (config.isDebugMode) { - console.log(`Advancing from first phase ${currentPhase.name}`); - } - advanceToNextPhase(); - } - }, config.playbackSpeed); // Only wait for summary, no animation delay - } - } - } - } - } } else { // Stepwise mode: show one message at a time, animating word-by-word gameState.messagesPlaying = true; @@ -259,64 +200,7 @@ export function updateChatWindows(phase: any, stepMessages = false) { if (config.isDebugMode) { console.log(`All messages displayed in ${Date.now() - messageStartTime}ms`); } - gameState.messagesPlaying = false; - - // Only proceed if we're in playback mode and not speaking - if (gameState.isPlaying && !gameState.isSpeaking) { - if (gameState.gameData && gameState.gameData.phases) { - const currentPhase = gameState.gameData.phases[gameState.phaseIndex]; - - if (config.isDebugMode) { - console.log(`Processing end of phase ${currentPhase.name}`); - } - - // Show summary first if available - if (currentPhase.summary?.trim()) { - addToNewsBanner(`(${currentPhase.name}) ${currentPhase.summary}`); - } - - // Get previous phase for animations - const prevIndex = gameState.phaseIndex > 0 ? gameState.phaseIndex - 1 : null; - const previousPhase = prevIndex !== null ? gameState.gameData.phases[prevIndex] : null; - - // Only schedule next phase if not already scheduled - if (!gameState.nextPhaseScheduled) { - gameState.nextPhaseScheduled = true; - - // Show animations for current phase's orders - if (previousPhase) { - if (config.isDebugMode) { - console.log(`Animating orders from ${previousPhase.name} to ${currentPhase.name}`); - } - - // After animations complete, advance to next phase with longer delay - gameState.playbackTimer = setTimeout(() => { - if (gameState.isPlaying) { - if (config.isDebugMode) { - console.log(`Animations complete, advancing from ${currentPhase.name}`); - } - advanceToNextPhase(); - } - }, config.effectivePlaybackSpeed + config.effectiveAnimationDuration); // Wait for both summary and animations - } else { - // For first phase, use shorter delay since there are no animations - if (config.isDebugMode) { - console.log(`First phase ${currentPhase.name} - no animations to wait for`); - } - - gameState.playbackTimer = setTimeout(() => { - if (gameState.isPlaying) { - if (config.isDebugMode) { - console.log(`Advancing from first phase ${currentPhase.name}`); - } - advanceToNextPhase(); - } - }, config.effectivePlaybackSpeed); // Only wait for summary, no animation delay - } - } - } - } return; } diff --git a/ai_animation/src/domElements/relationshipPopup.ts b/ai_animation/src/domElements/relationshipPopup.ts deleted file mode 100644 index b23f977..0000000 --- a/ai_animation/src/domElements/relationshipPopup.ts +++ /dev/null @@ -1,244 +0,0 @@ -import { relationshipsBtn } from '../domElements'; -import { gameState } from '../gameState'; -import { PowerENUM } from '../types/map'; -import { GameSchemaType } from '../types/gameState'; -import { renderRelationshipHistoryChartView, DisplayType } from '../components/rotatingDisplay'; -import { getPowerDisplayName } from '../utils/powerNames'; - -// DOM element references -let relationshipPopupContainer: HTMLElement | null = null; -let relationshipContent: HTMLElement | null = null; -let closeButton: HTMLElement | null = null; - -/** - * Initialize the relationship popup by creating DOM elements and attaching event handlers - */ -export function initRelationshipPopup(): void { - // Create the container if it doesn't exist - if (!document.getElementById('relationship-popup-container')) { - createRelationshipPopupElements(); - } - - // Get references to the created elements - relationshipPopupContainer = document.getElementById('relationship-popup-container'); - relationshipContent = document.getElementById('relationship-content'); - closeButton = document.getElementById('relationship-close-btn'); - - // Add event listeners - if (closeButton) { - closeButton.addEventListener('click', hideRelationshipPopup); - } - - // Add click handler for the relationships button - if (relationshipsBtn) { - relationshipsBtn.addEventListener('click', toggleRelationshipPopup); - } -} - -/** - * Create all DOM elements needed for the relationship popup - */ -function createRelationshipPopupElements(): void { - const container = document.createElement('div'); - container.id = 'relationship-popup-container'; - container.className = 'relationship-popup-container'; - - // Create header - const header = document.createElement('div'); - header.className = 'relationship-header'; - - const title = document.createElement('h2'); - title.textContent = 'Diplomatic Relations'; - header.appendChild(title); - - const closeBtn = document.createElement('button'); - closeBtn.id = 'relationship-close-btn'; - closeBtn.textContent = '×'; - closeBtn.title = 'Close Relationships Chart'; - header.appendChild(closeBtn); - - container.appendChild(header); - - // Create content container - const content = document.createElement('div'); - content.id = 'relationship-content'; - content.className = 'relationship-content'; - container.appendChild(content); - - // Add to document - document.body.appendChild(container); -} - -/** - * Toggle the visibility of the relationship popup - */ -export function toggleRelationshipPopup(): void { - if (relationshipPopupContainer) { - if (relationshipPopupContainer.classList.contains('visible')) { - hideRelationshipPopup(); - } else { - showRelationshipPopup(); - } - } -} - -/** - * Show the relationship popup - */ -export function showRelationshipPopup(): void { - if (relationshipPopupContainer && relationshipContent) { - relationshipPopupContainer.classList.add('visible'); - - // Only render if we have game data - if (gameState.gameData) { - renderRelationshipChart(); - } else { - relationshipContent.innerHTML = '
No game data loaded. Please load a game to view relationships.
'; - } - } -} - -/** - * Hide the relationship popup - */ -export function hideRelationshipPopup(): void { - if (relationshipPopupContainer) { - relationshipPopupContainer.classList.remove('visible'); - } -} - -/** - * Render the relationship chart in the popup - */ -function renderRelationshipChart(): void { - if (!relationshipContent || !gameState.gameData) return; - - // Clear current content - relationshipContent.innerHTML = ''; - - // Get a list of powers that have relationship data - const powersWithRelationships = new Set(); - - // Check all phases for relationships - if (gameState.gameData && gameState.gameData.phases) { - // Debug what relationship data we have - - let hasRelationshipData = false; - for (const phase of gameState.gameData.phases) { - if (phase.agent_relationships) { - hasRelationshipData = true; - // Add powers that have relationship data defined - Object.keys(phase.agent_relationships).forEach(power => { - powersWithRelationships.add(power); - }); - } - } - - if (!hasRelationshipData) { - console.log("No relationship data found in any phase"); - } - } - - // Create a container for each power's relationship chart - for (const power of Object.values(PowerENUM)) { - // Skip any non-string values - if (typeof power !== 'string') continue; - - // Check if this power has relationship data - if (powersWithRelationships.has(power)) { - const powerContainer = document.createElement('div'); - powerContainer.className = `power-relationship-container power-${power.toLowerCase()}`; - - const powerHeader = document.createElement('h3'); - powerHeader.className = `power-${power.toLowerCase()}`; - powerHeader.textContent = getPowerDisplayName(power as PowerENUM); - powerContainer.appendChild(powerHeader); - - const chartContainer = document.createElement('div'); - chartContainer.className = 'relationship-chart-container'; - - // Use the existing chart rendering function - renderRelationshipHistoryChartView( - chartContainer, - gameState.gameData, - gameState.phaseIndex, - power as PowerENUM - ); - - powerContainer.appendChild(chartContainer); - relationshipContent.appendChild(powerContainer); - } - } - - // If no powers have relationship data, create message to say so - if (powersWithRelationships.size === 0) { - console.log("No relationship data found in game"); - - // Create sample relationship data for all powers in the game - const allPowers = new Set(); - - // Find all powers from units and centers - if (gameState.gameData && gameState.gameData.phases && gameState.gameData.phases.length > 0) { - const currentPhase = gameState.gameData.phases[gameState.phaseIndex]; - - if (currentPhase.state?.units) { - Object.keys(currentPhase.state.units).forEach(power => allPowers.add(power)); - } - - if (currentPhase.state?.centers) { - Object.keys(currentPhase.state.centers).forEach(power => allPowers.add(power)); - } - - // Only proceed if we found some powers - if (allPowers.size > 0) { - console.log(`Found ${allPowers.size} powers in game, creating sample relationships`); - - // For each power, create a container and chart - for (const power of allPowers) { - const powerContainer = document.createElement('div'); - powerContainer.className = `power-relationship-container power-${power.toLowerCase()}`; - - const powerHeader = document.createElement('h3'); - powerHeader.className = `power-${power.toLowerCase()}`; - powerHeader.textContent = getPowerDisplayName(power as PowerENUM); - powerContainer.appendChild(powerHeader); - - const chartContainer = document.createElement('div'); - chartContainer.className = 'relationship-chart-container'; - - // Create a message about sample data - const sampleMessage = document.createElement('div'); - sampleMessage.className = 'sample-data-message'; - sampleMessage.innerHTML = `Note: No relationship data found for ${power}. - This chart will display when relationship data is available.`; - - chartContainer.appendChild(sampleMessage); - powerContainer.appendChild(chartContainer); - relationshipContent.appendChild(powerContainer); - } - } else { - // If we couldn't find any powers, show the no data message - const noDataMsg = document.createElement('div'); - noDataMsg.className = 'no-data-message'; - noDataMsg.textContent = 'No relationship data available in this game file.'; - relationshipContent.appendChild(noDataMsg); - } - } else { - // If no phases, show the no data message - const noDataMsg = document.createElement('div'); - noDataMsg.className = 'no-data-message'; - noDataMsg.textContent = 'No relationship data available in this game file.'; - relationshipContent.appendChild(noDataMsg); - } - } -} - -/** - * Update the relationship popup when game data changes - */ -export function updateRelationshipPopup(): void { - if (relationshipPopupContainer && - relationshipPopupContainer.classList.contains('visible')) { - renderRelationshipChart(); - } -} diff --git a/ai_animation/src/main.ts b/ai_animation/src/main.ts index e17cecf..2a2936c 100644 --- a/ai_animation/src/main.ts +++ b/ai_animation/src/main.ts @@ -141,18 +141,17 @@ function animate() { // Call update on each active animation gameState.unitAnimations.forEach((anim) => anim.update()) - // If all animations are complete and we're in playback mode - if (gameState.unitAnimations.length === 0 && gameState.isPlaying && !gameState.messagesPlaying && !gameState.isSpeaking && !gameState.nextPhaseScheduled) { - // Schedule next phase after a pause delay - console.log(`Scheduling next phase in ${config.effectivePlaybackSpeed}ms`); - gameState.nextPhaseScheduled = true; - gameState.playbackTimer = setTimeout(() => { - gameState.nextPhaseScheduled = false; - advanceToNextPhase(); - }, config.effectivePlaybackSpeed); - } } + // If all animations are complete and we're in playback mode + if (gameState.unitAnimations.length === 0 && gameState.isPlaying && !gameState.messagesPlaying && !gameState.isSpeaking && !gameState.nextPhaseScheduled) { + // Schedule next phase after a pause delay + console.log(`Scheduling next phase in ${config.effectivePlaybackSpeed}ms`); + gameState.nextPhaseScheduled = true; + gameState.playbackTimer = setTimeout(() => { + advanceToNextPhase(); + }, config.effectivePlaybackSpeed); + } // Update any pulsing or wave animations on supply centers or units if (gameState.scene.userData.animatedObjects) { gameState.scene.userData.animatedObjects.forEach(obj => { diff --git a/ai_animation/src/phase.ts b/ai_animation/src/phase.ts index efb2f0c..318d146 100644 --- a/ai_animation/src/phase.ts +++ b/ai_animation/src/phase.ts @@ -61,6 +61,7 @@ export function _setPhase(phaseIndex: number) { } else { displayPhase() } + gameState.nextPhaseScheduled = false; } // Finally, update the gameState with the current phaseIndex @@ -261,8 +262,6 @@ export function advanceToNextPhase() { logger.log("Cannot advance phase: invalid game state"); return; } - // Reset the nextPhaseScheduled flag to allow scheduling the next phase - gameState.nextPhaseScheduled = false; // Get current phase const currentPhase = gameState.gameData.phases[gameState.phaseIndex]; @@ -296,6 +295,8 @@ export function advanceToNextPhase() { if (gameState.isPlaying) { nextPhase(); } + }).finally(() => { + }); } else { console.error("Attempted to start speaking when already speaking...") @@ -305,6 +306,9 @@ export function advanceToNextPhase() { // No summary to speak, advance immediately nextPhase(); } + + // Reset the nextPhaseScheduled flag to allow scheduling the next phase + gameState.nextPhaseScheduled = false; } function displayFinalPhase() {