AI_Diplomacy/diplomacy/animation/test.html
2025-03-04 11:35:02 -08:00

376 lines
No EOL
15 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Diplomacy Animation Test</title>
<!-- Import map for Three.js and dependencies -->
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
"three/examples/jsm/controls/OrbitControls.js": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/controls/OrbitControls.js",
"three/examples/jsm/loaders/GLTFLoader.js": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/loaders/GLTFLoader.js",
"three/examples/jsm/loaders/TextureLoader.js": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/loaders/TextureLoader.js",
"three/examples/jsm/loaders/OBJLoader.js": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/loaders/OBJLoader.js"
}
}
</script>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.animation-container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
}
.loading-overlay {
text-align: center;
padding: 40px;
}
h1 {
color: #333;
text-align: center;
}
h2 {
color: #444;
border-bottom: 1px solid #ddd;
padding-bottom: 8px;
margin-top: 30px;
}
button {
padding: 10px 15px;
background-color: #4a5568;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.2s;
margin-right: 8px;
margin-bottom: 8px;
}
button:hover {
background-color: #2d3748;
}
button:disabled {
background-color: #a0aec0;
cursor: not-allowed;
}
.test-controls {
margin-top: 20px;
padding: 15px;
background-color: #f0f0f0;
border-radius: 5px;
}
.test-section {
margin-bottom: 20px;
}
.test-section h3 {
margin-top: 0;
border-bottom: 1px solid #ddd;
padding-bottom: 5px;
}
.scenario-description {
background-color: #e6f7ff;
border-left: 4px solid #1890ff;
padding: 10px 15px;
margin-bottom: 15px;
border-radius: 0 4px 4px 0;
}
.test-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 15px;
margin-top: 15px;
}
.test-card {
background-color: #fff;
border: 1px solid #ddd;
border-radius: 5px;
padding: 15px;
}
.test-card h4 {
margin-top: 0;
color: #333;
}
#debug-info {
margin-top: 20px;
padding: 10px;
background-color: #ffe6e6;
border: 1px solid #ffcccc;
border-radius: 4px;
display: none;
}
</style>
</head>
<body>
<div class="animation-container" id="animation-container">
<div class="loading-overlay" id="loading-overlay">
<h1>Diplomacy Animation Test</h1>
<p>This page tests the Three.js animation system for Diplomacy with enhanced order visualization and unit movement.</p>
<button id="load-game-btn">Load Game File</button>
<div id="debug-info"></div>
<div class="test-controls" id="test-controls" style="display: none;">
<h2>Phase 3 Test Scenarios</h2>
<p>The following test scenarios demonstrate the capabilities implemented in Phase 3 of the animation system.</p>
<div class="test-section">
<h3>Basic Movement Tests</h3>
<div class="scenario-description">
Tests basic unit movement between provinces with full animation.
</div>
<button id="test-movement">Test Basic Movement</button>
</div>
<div class="test-section">
<h3>Order Visualization Tests</h3>
<div class="scenario-description">
Tests various order types and their visual representation, including moves, holds, supports, and convoys.
</div>
<div class="test-grid">
<div class="test-card">
<h4>Basic Orders</h4>
<button id="test-basic-orders">Test Basic Orders</button>
<p>Move, Hold, and Support orders</p>
</div>
<div class="test-card">
<h4>Complex Orders</h4>
<button id="test-complex-orders">Test Complex Orders</button>
<p>Support moves, Convoys, and multi-unit moves</p>
</div>
<div class="test-card">
<h4>Failed Orders</h4>
<button id="test-failed-orders">Test Failed Orders</button>
<p>Bounces, cuts, and dislodges</p>
</div>
</div>
</div>
<div class="test-section">
<h3>Retreat Tests</h3>
<div class="scenario-description">
Tests retreat scenarios where units are dislodged and must retreat or be disbanded.
</div>
<button id="test-retreat">Test Retreat Phase</button>
</div>
<div class="test-section">
<h3>Build Tests</h3>
<div class="scenario-description">
Tests build/adjustment phase with unit creation and removal.
</div>
<button id="test-build">Test Build Phase</button>
</div>
</div>
</div>
</div>
<script type="module">
console.log('Script starting...');
const debugInfo = document.getElementById('debug-info');
debugInfo.style.display = 'block';
debugInfo.textContent = 'Loading script...';
try {
// Check if modules are available
debugInfo.textContent += '\nChecking modules...';
// Add cache busting for JavaScript files
const cacheBust = `?v=${Date.now()}`;
import(`./index.js${cacheBust}`)
.then(module => {
debugInfo.textContent += '\nindex.js loaded successfully.';
// Store all exported functions from index.js in the global scope
window.initializeAnimation = module.initializeAnimation;
window.loadGameForAnimation = module.loadGameForAnimation;
console.log('Available exports from index.js:', Object.keys(module));
debugInfo.textContent += `\nAvailable exports from index.js: ${Object.keys(module).join(', ')}`;
return import(`./utils/GameStateManager.js${cacheBust}`);
})
.then(module => {
debugInfo.textContent += '\nGameStateManager.js loaded successfully.';
const { GameStateManager } = module;
// DOM elements
const loadingOverlay = document.getElementById('loading-overlay');
const animationContainer = document.getElementById('animation-container');
const loadGameBtn = document.getElementById('load-game-btn');
const testControls = document.getElementById('test-controls');
debugInfo.textContent += '\nDOM elements retrieved.';
// Initialize animation system
const gameStateManager = new GameStateManager();
// Add event listeners for test buttons
document.getElementById('load-game-btn').addEventListener('click', () => {
// Use the existing loadGameFromDisk method instead of trying to fetch a file
gameStateManager.loadGameFromDisk()
.then(gameData => {
console.log('Game data loaded successfully');
return gameStateManager;
})
.then(() => {
console.log('Game loaded successfully');
document.getElementById('loading-overlay').style.display = 'none';
// Create an animation player container if it doesn't exist
let animationPlayer = document.getElementById('animation-player');
if (!animationPlayer) {
animationPlayer = document.createElement('div');
animationPlayer.id = 'animation-player';
document.getElementById('animation-container').appendChild(animationPlayer);
}
animationPlayer.style.display = 'block';
// Initialize the animation player
initializeAnimationPlayer(gameStateManager);
})
.catch(error => {
console.error('Error loading game:', error);
document.getElementById('debug-info').style.display = 'block';
document.getElementById('debug-info').innerHTML = `<strong>Error:</strong> ${error.message}`;
});
});
// Manually trigger a click to ensure it's working
debugInfo.textContent += '\nEvent listener set up! Try clicking the button.';
// Define the initializeAnimationPlayer function
function initializeAnimationPlayer(gameStateManager) {
console.log('Initializing animation player with game state manager');
// Create a map container if it doesn't exist
let mapContainer = document.getElementById('map-container');
if (!mapContainer) {
mapContainer = document.createElement('div');
mapContainer.id = 'map-container';
mapContainer.style.width = '100%';
mapContainer.style.height = '600px';
mapContainer.style.position = 'relative';
mapContainer.style.backgroundColor = '#87CEEB';
mapContainer.style.marginTop = '20px';
document.getElementById('animation-player').appendChild(mapContainer);
}
// Create animation controls if they don't exist
let animationControls = document.getElementById('animation-controls');
if (!animationControls) {
animationControls = document.createElement('div');
animationControls.id = 'animation-controls';
animationControls.style.marginTop = '20px';
// Add play controls
animationControls.innerHTML = `
<div class="control-group">
<button id="prev-phase">◀ Prev</button>
<button id="play-button">▶ Play</button>
<button id="next-phase">Next ▶</button>
<label>Speed: <select id="speed-control">
<option value="0.5">0.5x</option>
<option value="1" selected>1x</option>
<option value="2">2x</option>
<option value="4">4x</option>
</select></label>
<span id="phase-display">Phase: 1 of ${gameStateManager.getPhaseCount()}</span>
</div>
<div class="control-group">
<label><input type="checkbox" id="show-order-viz" checked> Show Order Visualizations</label>
<label><input type="checkbox" id="animate-units" checked> Animate Unit Movements</label>
<label><input type="checkbox" id="use-easing" checked> Use Animation Easing</label>
<label><input type="checkbox" id="auto-advance" checked> Auto-Advance to Next Phase</label>
</div>
`;
document.getElementById('animation-player').prepend(animationControls);
}
// Now use the initializeAnimation function from the index.js module
if (typeof window.initializeAnimation === 'function') {
const animationPlayer = window.initializeAnimation({
containerId: 'map-container',
gameStateManager: gameStateManager,
mapVariant: 'standard',
detailLevel: 'medium'
});
// Set up control event listeners
document.getElementById('prev-phase')?.addEventListener('click', () => {
animationPlayer.previousPhase();
});
document.getElementById('play-button')?.addEventListener('click', () => {
if (animationPlayer.isPlaying) {
animationPlayer.pause();
document.getElementById('play-button').textContent = '▶ Play';
} else {
animationPlayer.play();
document.getElementById('play-button').textContent = '⏸ Pause';
}
});
document.getElementById('next-phase')?.addEventListener('click', () => {
animationPlayer.nextPhase();
});
document.getElementById('speed-control')?.addEventListener('change', (e) => {
animationPlayer.setPlaybackSpeed(parseFloat(e.target.value));
});
document.getElementById('show-order-viz')?.addEventListener('change', (e) => {
animationPlayer.showOrderVisualizations = e.target.checked;
});
document.getElementById('animate-units')?.addEventListener('change', (e) => {
animationPlayer.animateUnitMovements = e.target.checked;
});
document.getElementById('use-easing')?.addEventListener('change', (e) => {
animationPlayer.setEasing(e.target.checked);
});
document.getElementById('auto-advance')?.addEventListener('change', (e) => {
animationPlayer.autoAdvance = e.target.checked;
});
console.log('Animation player initialized successfully');
} else {
console.error('initializeAnimation function not found. Available exports:',
Object.keys(window).filter(k => k.includes('initialize')).join(', '));
document.getElementById('debug-info').style.display = 'block';
document.getElementById('debug-info').innerHTML =
'<strong>Error:</strong> initializeAnimation function not available. Ensure index.js is loaded correctly.';
}
}
})
.catch(error => {
console.error('Error loading modules:', error);
debugInfo.textContent += `\nModule import error: ${error.message}`;
if (error.stack) {
debugInfo.textContent += `\nStack: ${error.stack}`;
}
});
} catch (error) {
console.error('Error in script:', error);
debugInfo.textContent += `\nScript error: ${error.message}`;
if (error.stack) {
debugInfo.textContent += `\nStack: ${error.stack}`;
}
}
</script>
</body>
</html>