diff --git a/diplomacy/animation/simple-test.html b/diplomacy/animation/simple-test.html
index db91f59..c186a2d 100644
--- a/diplomacy/animation/simple-test.html
+++ b/diplomacy/animation/simple-test.html
@@ -3,14 +3,12 @@
- Diplomacy Map Test
-
+ Diplomacy Map (Fallback-Only)
@@ -37,6 +35,7 @@
.map-view {
flex-grow: 1;
background-color: #87CEEB;
+ position: relative;
}
button {
padding: 8px 16px;
@@ -52,6 +51,7 @@
}
#phase-display {
font-weight: bold;
+ margin-left: 10px;
}
#file-input {
display: none;
@@ -90,777 +90,128 @@
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
- // Basic variables
+ // --- CORE VARIABLES ---
let scene, camera, renderer, controls;
let gameData = null;
let currentPhaseIndex = 0;
- let coordinateData = null; // Will hold map coordinates
- let unitMeshes = []; // Track unit meshes for cleanup
- let mapGeometry; // Shared geometry for map components
-
- // DOM elements
- const loadBtn = document.getElementById('load-btn');
- const fileInput = document.getElementById('file-input');
- const prevBtn = document.getElementById('prev-btn');
- const nextBtn = document.getElementById('next-btn');
- const phaseDisplay = document.getElementById('phase-display');
- const infoPanel = document.getElementById('info-panel');
- const mapView = document.getElementById('map-view');
-
- // Initialize Three.js scene
+ let coordinateData = null;
+ let unitMeshes = []; // To store references for units + supply center 3D objects
+ let mapPlane = null; // The fallback map plane
+
+ // --- DOM ELEMENTS ---
+ const loadBtn = document.getElementById('load-btn');
+ const fileInput = document.getElementById('file-input');
+ const prevBtn = document.getElementById('prev-btn');
+ const nextBtn = document.getElementById('next-btn');
+ const phaseDisplay = document.getElementById('phase-display');
+ const infoPanel = document.getElementById('info-panel');
+ const mapView = document.getElementById('map-view');
+
+ // --- INITIALIZE SCENE ---
function initScene() {
- // Create scene
scene = new THREE.Scene();
- scene.background = new THREE.Color(0x87CEEB); // Sky blue background
+ scene.background = new THREE.Color(0x87CEEB);
- // Setup camera
+ // Camera
camera = new THREE.PerspectiveCamera(
60,
mapView.clientWidth / mapView.clientHeight,
1,
- 5000
+ 3000
);
- camera.position.set(0, 500, 500);
+ camera.position.set(0, 800, 800);
camera.lookAt(0, 0, 0);
- // Setup renderer with improved quality
- renderer = new THREE.WebGLRenderer({
- antialias: true,
- alpha: true
- });
+ // Renderer
+ renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(mapView.clientWidth, mapView.clientHeight);
renderer.setPixelRatio(window.devicePixelRatio);
- renderer.shadowMap.enabled = true;
- renderer.shadowMap.type = THREE.PCFSoftShadowMap;
mapView.appendChild(renderer.domElement);
- // Add controls with better defaults
+ // Controls
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.screenSpacePanning = true;
controls.minDistance = 100;
- controls.maxDistance = 1000;
- controls.maxPolarAngle = Math.PI / 2.1; // Limit rotation to prevent seeing under the map
-
- // Add camera preset buttons
- createCameraPresetButtons();
-
- // Enhanced lighting setup
- setupLighting();
-
- // Add an environment (skybox, fog)
- setupEnvironment();
-
- // Add a subtle grid for reference that fades with distance
- const gridHelper = new THREE.GridHelper(1000, 50, 0x444444, 0x222222);
- gridHelper.material.opacity = 0.2;
- gridHelper.material.transparent = true;
- scene.add(gridHelper);
-
- // Load the coordinate data
- loadCoordinateData().then(() => {
- // Create enhanced map with texture once coordinates are loaded
- createMapWithTexture();
-
- // Disable territory disks - we'll draw territories directly on the canvas map
- // createTerritoryBoundaries();
-
- // Add territory labels
- if (coordinateData && coordinateData.coordinates) {
- console.log('Adding territory labels...');
- for (const [province, position] of Object.entries(coordinateData.coordinates)) {
- // Don't add labels for coast variants (they have underscores)
- if (!province.includes('_')) {
- addLabel(province, position.x, position.z);
- }
- }
- }
-
- // Add supply centers
- displaySupplyCenters();
-
- // Add territory interactivity
- addTerritoryInteractivity();
-
- // Optimize label visibility
- optimizeLabels();
- });
-
- // Start animation loop
+ controls.maxDistance = 1500;
+ controls.maxPolarAngle = Math.PI / 2; // Limit so you don't flip under the map
+
+ // Lighting (keep it simple)
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
+ scene.add(ambientLight);
+
+ const dirLight = new THREE.DirectionalLight(0xffffff, 0.6);
+ dirLight.position.set(300, 400, 300);
+ scene.add(dirLight);
+
+ // Load coordinate data, then build the fallback map
+ loadCoordinateData()
+ .then(() => {
+ createFallbackMap(); // Create the map plane from the start
+ })
+ .catch(err => {
+ console.error("Error loading coordinates:", err);
+ infoPanel.textContent = `Error loading coords: ${err.message}`;
+ });
+
+ // Kick off animation loop
animate();
- // Add window resize handler
+ // Handle resizing
window.addEventListener('resize', onWindowResize);
}
- // Enhanced lighting setup
- function setupLighting() {
- // Clear existing lights if any
- scene.children.forEach(child => {
- if (child.isLight) scene.remove(child);
- });
+ // --- ANIMATION LOOP ---
+ function animate() {
+ requestAnimationFrame(animate);
- // Add ambient light for base illumination
- const ambientLight = new THREE.AmbientLight(0xCCDDFF, 0.4);
- scene.add(ambientLight);
-
- // Main directional light (sun-like)
- const mainLight = new THREE.DirectionalLight(0xFFFFCC, 0.8);
- mainLight.position.set(300, 400, 300);
- mainLight.castShadow = true;
-
- // Better shadow quality
- mainLight.shadow.mapSize.width = 2048;
- mainLight.shadow.mapSize.height = 2048;
- mainLight.shadow.camera.near = 0.5;
- mainLight.shadow.camera.far = 1000;
- mainLight.shadow.camera.left = -500;
- mainLight.shadow.camera.right = 500;
- mainLight.shadow.camera.top = 500;
- mainLight.shadow.camera.bottom = -500;
- scene.add(mainLight);
-
- // Helper backlight
- const backLight = new THREE.DirectionalLight(0x9999FF, 0.3);
- backLight.position.set(-200, 200, -300);
- scene.add(backLight);
-
- // Add some point lights for atmosphere
- const colors = [0xFF6666, 0x66AAFF, 0x66FFAA, 0xFFFF66];
-
- for (let i = 0; i < 4; i++) {
- const pointLight = new THREE.PointLight(colors[i % colors.length], 0.4, 300);
- const angle = (i / 4) * Math.PI * 2;
- pointLight.position.set(
- Math.cos(angle) * 400,
- 100,
- Math.sin(angle) * 400
- );
- scene.add(pointLight);
- }
- }
-
- // Create an environmental atmosphere
- function setupEnvironment() {
- // Add subtle fog for depth
- scene.fog = new THREE.FogExp2(0xCCDDFF, 0.0005);
-
- // Store initial background color for day/night transitions
- scene.userData.dayColor = new THREE.Color(0x87CEEB);
- scene.userData.nightColor = new THREE.Color(0x001133);
- scene.userData.currentTimeOfDay = 'day';
- }
-
- // Create camera preset buttons
- function createCameraPresetButtons() {
- const presetContainer = document.createElement('div');
- presetContainer.style.position = 'absolute';
- presetContainer.style.bottom = '20px';
- presetContainer.style.left = '20px';
- presetContainer.style.zIndex = '100';
-
- const presets = [
- { name: 'Top View', position: [0, 600, 0], target: [0, 0, 0] },
- { name: 'Europe', position: [0, 400, 400], target: [0, 0, 0] },
- { name: 'West', position: [-500, 300, 0], target: [0, 0, 0] },
- { name: 'East', position: [500, 300, 0], target: [0, 0, 0] },
- { name: 'North', position: [0, 300, -500], target: [0, 0, 0] },
- { name: 'South', position: [0, 300, 500], target: [0, 0, 0] },
- { name: 'Toggle Day/Night', action: toggleDayNight }
- ];
-
- presets.forEach(preset => {
- const btn = document.createElement('button');
- btn.innerText = preset.name;
- btn.style.margin = '5px';
- btn.style.padding = '8px 12px';
- btn.style.backgroundColor = '#444';
- btn.style.color = 'white';
- btn.style.border = 'none';
- btn.style.borderRadius = '4px';
- btn.style.cursor = 'pointer';
-
- btn.addEventListener('click', () => {
- if (preset.action) {
- preset.action();
- } else {
- moveCamera(preset.position, preset.target);
+ // Update any pulsing or wave animations on supply centers or units
+ if (scene.userData.animatedObjects) {
+ scene.userData.animatedObjects.forEach(obj => {
+ if (obj.userData.pulseAnimation) {
+ const anim = obj.userData.pulseAnimation;
+ anim.time += anim.speed;
+ if (obj.userData.glowMesh) {
+ const pulseValue = Math.sin(anim.time) * anim.intensity + 0.5;
+ obj.userData.glowMesh.material.opacity = 0.2 + (pulseValue * 0.3);
+ obj.userData.glowMesh.scale.set(
+ 1 + (pulseValue * 0.1),
+ 1 + (pulseValue * 0.1),
+ 1 + (pulseValue * 0.1)
+ );
+ }
+ // Subtle bobbing up/down
+ obj.position.y = 2 + Math.sin(anim.time) * 0.5;
}
});
-
- presetContainer.appendChild(btn);
- });
-
- document.body.appendChild(presetContainer);
- }
-
- // Smoothly move camera to new position and target
- function moveCamera(position, target) {
- const startPosition = camera.position.clone();
- const startTarget = controls.target.clone();
- const endPosition = new THREE.Vector3(...position);
- const endTarget = new THREE.Vector3(...target);
-
- const duration = 1000; // ms
- const startTime = Date.now();
-
- function updateCamera() {
- const elapsed = Date.now() - startTime;
- const progress = Math.min(elapsed / duration, 1);
-
- // Use easing function for smoother motion
- const easeProgress = 1 - Math.pow(1 - progress, 3);
-
- // Interpolate position and target
- camera.position.lerpVectors(startPosition, endPosition, easeProgress);
- controls.target.lerpVectors(startTarget, endTarget, easeProgress);
- controls.update();
-
- if (progress < 1) {
- requestAnimationFrame(updateCamera);
- }
}
- updateCamera();
+ controls.update();
+ renderer.render(scene, camera);
}
- // Toggle between day and night mode
- function toggleDayNight() {
- const isDay = scene.userData.currentTimeOfDay === 'day';
- const targetColor = isDay ? scene.userData.nightColor : scene.userData.dayColor;
- const currentColor = scene.background.clone();
-
- // Get all directional lights
- const lights = scene.children.filter(child => child.isDirectionalLight);
- const currentIntensities = lights.map(light => light.intensity);
- const targetIntensities = isDay ? lights.map(light => light.intensity * 0.3) : lights.map(light => light.intensity / 0.3);
-
- // Get the ambient light
- const ambientLight = scene.children.find(child => child.isAmbientLight);
- const currentAmbientIntensity = ambientLight ? ambientLight.intensity : 0.4;
- const targetAmbientIntensity = isDay ? currentAmbientIntensity * 0.5 : currentAmbientIntensity / 0.5;
-
- // Update fog
- const currentFogColor = scene.fog.color.clone();
- const targetFogColor = isDay ? new THREE.Color(0x001133) : new THREE.Color(0xCCDDFF);
-
- const duration = 1000; // ms
- const startTime = Date.now();
-
- function updateLighting() {
- const elapsed = Date.now() - startTime;
- const progress = Math.min(elapsed / duration, 1);
-
- // Use easing function for smoother transition
- const easeProgress = 1 - Math.pow(1 - progress, 3);
-
- // Interpolate background color
- scene.background.lerpColors(currentColor, targetColor, easeProgress);
-
- // Interpolate light intensities
- lights.forEach((light, index) => {
- light.intensity = currentIntensities[index] + (targetIntensities[index] - currentIntensities[index]) * easeProgress;
- });
-
- // Interpolate ambient light intensity
- if (ambientLight) {
- ambientLight.intensity = currentAmbientIntensity + (targetAmbientIntensity - currentAmbientIntensity) * easeProgress;
- }
-
- // Interpolate fog color
- scene.fog.color.lerpColors(currentFogColor, targetFogColor, easeProgress);
-
- if (progress < 1) {
- requestAnimationFrame(updateLighting);
- } else {
- // Update current time of day
- scene.userData.currentTimeOfDay = isDay ? 'night' : 'day';
- }
- }
-
- updateLighting();
+ // --- RESIZE HANDLER ---
+ function onWindowResize() {
+ camera.aspect = mapView.clientWidth / mapView.clientHeight;
+ camera.updateProjectionMatrix();
+ renderer.setSize(mapView.clientWidth, mapView.clientHeight);
}
- // Create territory boundaries based on coordinate data
- function createTerritoryBoundaries() {
- if (!coordinateData || !coordinateData.coordinates) {
- console.warn('No coordinate data available for territory boundaries');
- return;
- }
-
- // Group provinces by country
- const countries = {
- 'AUSTRIA': ['VIE', 'BUD', 'TRI', 'GAL', 'BOH', 'TYR'],
- 'ENGLAND': ['LON', 'EDI', 'LVP', 'WAL', 'YOR', 'CLY'],
- 'FRANCE': ['PAR', 'BRE', 'MAR', 'PIC', 'GAS', 'BUR'],
- 'GERMANY': ['BER', 'MUN', 'KIE', 'RUH', 'PRU', 'SIL'],
- 'ITALY': ['ROM', 'VEN', 'NAP', 'TUS', 'PIE', 'APU'],
- 'RUSSIA': ['STP', 'MOS', 'WAR', 'SEV', 'UKR', 'LVN'],
- 'TURKEY': ['CON', 'ANK', 'SMY', 'SYR', 'ARM']
- };
-
- // Sea provinces for special styling
- const seaProvinces = [
- 'NAO', 'NWG', 'BAR', 'NTH', 'SKA', 'HEL', 'BAL', 'BOT', 'ENG', 'IRI', 'MAO',
- 'WES', 'LYO', 'TYS', 'ADR', 'ION', 'AEG', 'EAS', 'BLA'
- ];
-
- // Country colors with better contrast and theme
- const countryColors = {
- 'AUSTRIA': 0xd82b2b,
- 'ENGLAND': 0x223a87,
- 'FRANCE': 0x3699d4,
- 'GERMANY': 0x232323,
- 'ITALY': 0x35a335,
- 'RUSSIA': 0xcccccc,
- 'TURKEY': 0xe0c846
- };
-
- // Create a group for all territory boundaries
- const territoriesGroup = new THREE.Group();
- territoriesGroup.name = 'territories';
-
- // Main group for land territories
- const landGroup = new THREE.Group();
- landGroup.name = 'land_territories';
-
- // Main group for sea territories
- const seaGroup = new THREE.Group();
- seaGroup.name = 'sea_territories';
-
- // Process each province
- for (const [province, position] of Object.entries(coordinateData.coordinates)) {
- if (!province.includes('_')) { // Skip coast variants
- // Determine if this is a sea province
- const isSea = seaProvinces.includes(province);
-
- // Determine country by checking each country's provinces
- let country = null;
- for (const [countryName, provinces] of Object.entries(countries)) {
- if (provinces.includes(province)) {
- country = countryName;
- break;
- }
- }
-
- // Larger radius for territories
- const radius = isSea ? 35 : 33;
-
- // Create appearance based on territory type
- if (isSea) {
- // Sea territory - blue with wave effect
- const circleGeometry = new THREE.CircleGeometry(radius, 32);
- const circleMaterial = new THREE.MeshStandardMaterial({
- color: 0x1a3c6e,
- transparent: true,
- opacity: 0.8,
- side: THREE.DoubleSide,
- metalness: 0.3,
- roughness: 0.7
- });
-
- const circleMesh = new THREE.Mesh(circleGeometry, circleMaterial);
- circleMesh.position.set(position.x, 0.2, position.z); // Slightly lower than land
- circleMesh.rotation.x = -Math.PI / 2; // Make it horizontal
-
- // Add a wave effect overlay
- const waveGeometry = new THREE.CircleGeometry(radius - 2, 32);
- const waveTexture = createWaveTexture();
- const waveMaterial = new THREE.MeshStandardMaterial({
- color: 0x66aadd,
- transparent: true,
- opacity: 0.7,
- alphaMap: waveTexture,
- side: THREE.DoubleSide
- });
-
- const waveMesh = new THREE.Mesh(waveGeometry, waveMaterial);
- waveMesh.position.set(position.x, 0.3, position.z);
- waveMesh.rotation.x = -Math.PI / 2;
-
- // Store province data
- circleMesh.userData = {
- province,
- country,
- isSea: true
- };
-
- // Add territory border (thicker for seas)
- const borderGeometry = new THREE.RingGeometry(radius - 0.5, radius + 5, 32);
- const borderMaterial = new THREE.MeshStandardMaterial({
- color: 0x0a1a3c,
- transparent: true,
- opacity: 0.8,
- side: THREE.DoubleSide
- });
-
- const borderMesh = new THREE.Mesh(borderGeometry, borderMaterial);
- borderMesh.position.set(position.x, 0.25, position.z);
- borderMesh.rotation.x = -Math.PI / 2; // Make it horizontal
-
- // Add to sea group
- seaGroup.add(circleMesh);
- seaGroup.add(waveMesh);
- seaGroup.add(borderMesh);
-
- // Add animation for waves
- if (!scene.userData.animatedObjects) {
- scene.userData.animatedObjects = [];
- }
-
- waveMesh.userData.waveAnimation = {
- speed: 0.0015 + Math.random() * 0.001,
- time: Math.random() * Math.PI * 2
- };
-
- scene.userData.animatedObjects.push(waveMesh);
-
- } else {
- // Land territory - color based on country
- const color = country ? countryColors[country] : 0xb19b69; // Default to tan for neutral provinces
-
- // Create a more natural territory shape
- const circleGeometry = new THREE.CircleGeometry(radius, 32);
- const circleMaterial = new THREE.MeshStandardMaterial({
- color,
- transparent: false,
- side: THREE.DoubleSide,
- metalness: 0.1,
- roughness: 0.9
- });
-
- const circleMesh = new THREE.Mesh(circleGeometry, circleMaterial);
- circleMesh.position.set(position.x, 0.5, position.z); // Higher than sea
- circleMesh.rotation.x = -Math.PI / 2; // Make it horizontal
-
- // Add a raised border around the territory (thinner for land)
- const borderGeometry = new THREE.RingGeometry(radius - 0.5, radius + 3.5, 32);
-
- // Use contrasting border color based on territory color brightness
- const brightness = getBrightness(color);
- const borderColor = brightness > 0.6 ? 0x333333 : 0xdddddd;
-
- const borderMaterial = new THREE.MeshStandardMaterial({
- color: borderColor,
- transparent: true,
- opacity: 0.9,
- side: THREE.DoubleSide
- });
-
- const borderMesh = new THREE.Mesh(borderGeometry, borderMaterial);
- borderMesh.position.set(position.x, 0.55, position.z); // Slightly above territory
- borderMesh.rotation.x = -Math.PI / 2; // Make it horizontal
-
- // Add subtle terrain texture
- const terrainGeometry = new THREE.CircleGeometry(radius - 2, 32);
- const terrainTexture = createTerrainTexture(country);
- const terrainMaterial = new THREE.MeshStandardMaterial({
- color,
- transparent: true,
- opacity: 0.5,
- alphaMap: terrainTexture,
- side: THREE.DoubleSide
- });
-
- const terrainMesh = new THREE.Mesh(terrainGeometry, terrainMaterial);
- terrainMesh.position.set(position.x, 0.6, position.z);
- terrainMesh.rotation.x = -Math.PI / 2;
-
- // Store province data
- circleMesh.userData = {
- province,
- country,
- isSea: false
- };
-
- // Add to land group
- landGroup.add(circleMesh);
- landGroup.add(borderMesh);
- landGroup.add(terrainMesh);
- }
- }
- }
-
- // Add land and sea groups to the main territories group
- territoriesGroup.add(seaGroup); // Sea goes first (below land)
- territoriesGroup.add(landGroup); // Land on top
-
- // Add to scene
- scene.add(territoriesGroup);
- }
-
- // Create wave texture for sea territories
- function createWaveTexture() {
- const canvas = document.createElement('canvas');
- canvas.width = 128;
- canvas.height = 128;
- const ctx = canvas.getContext('2d');
-
- // Create gradient background
- const gradient = ctx.createRadialGradient(64, 64, 0, 64, 64, 64);
- gradient.addColorStop(0, 'rgba(255, 255, 255, 0.1)');
- gradient.addColorStop(0.7, 'rgba(255, 255, 255, 0.05)');
- gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
-
- ctx.fillStyle = gradient;
- ctx.fillRect(0, 0, 128, 128);
-
- // Add wave patterns
- ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
- ctx.lineWidth = 2;
-
- // Draw wavy lines
- for (let i = 0; i < 8; i++) {
- const y = i * 16 + 8;
-
- ctx.beginPath();
- ctx.moveTo(0, y);
-
- for (let x = 0; x < 128; x += 16) {
- const amplitude = 4;
- const yOffset = Math.sin(x / 16 + i) * amplitude;
-
- ctx.quadraticCurveTo(
- x + 8, y + yOffset,
- x + 16, y
- );
- }
-
- ctx.stroke();
- }
-
- const texture = new THREE.CanvasTexture(canvas);
- texture.wrapS = THREE.RepeatWrapping;
- texture.wrapT = THREE.RepeatWrapping;
- return texture;
- }
-
- // Create terrain texture for land territories
- function createTerrainTexture(country) {
- const canvas = document.createElement('canvas');
- canvas.width = 128;
- canvas.height = 128;
- const ctx = canvas.getContext('2d');
-
- // Different patterns based on country
- if (country) {
- // Color based on country
- switch(country) {
- case 'AUSTRIA':
- drawMountainTexture(ctx);
- break;
- case 'ITALY':
- drawMediterraneanTexture(ctx);
- break;
- case 'RUSSIA':
- drawTundraTexture(ctx);
- break;
- case 'GERMANY':
- drawForestTexture(ctx);
- break;
- case 'FRANCE':
- drawPlainsTexture(ctx);
- break;
- case 'ENGLAND':
- drawIslandTexture(ctx);
- break;
- case 'TURKEY':
- drawAridTexture(ctx);
- break;
- default:
- drawGenericLandTexture(ctx);
- }
- } else {
- // Generic neutral territory
- drawGenericLandTexture(ctx);
- }
-
- const texture = new THREE.CanvasTexture(canvas);
- return texture;
- }
-
- // Utility to get brightness of a color
- function getBrightness(hexColor) {
- const r = (hexColor >> 16) & 255;
- const g = (hexColor >> 8) & 255;
- const b = hexColor & 255;
-
- // Convert to relative luminance
- return (0.299 * r + 0.587 * g + 0.114 * b) / 255;
- }
-
- // Draw various terrain textures
- function drawMountainTexture(ctx) {
- // Draw small triangle patterns for mountains
- ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
-
- for (let i = 0; i < 15; i++) {
- const x = Math.random() * 128;
- const y = Math.random() * 128;
- const size = 5 + Math.random() * 10;
-
- ctx.beginPath();
- ctx.moveTo(x, y + size);
- ctx.lineTo(x - size / 2, y - size / 2);
- ctx.lineTo(x + size / 2, y - size / 2);
- ctx.fill();
- }
- }
-
- function drawMediterraneanTexture(ctx) {
- // Olive/vineyard-like pattern
- ctx.fillStyle = 'rgba(255, 255, 255, 0.15)';
-
- for (let i = 0; i < 30; i++) {
- const x = Math.random() * 128;
- const y = Math.random() * 128;
- const radius = 2 + Math.random() * 4;
-
- ctx.beginPath();
- ctx.arc(x, y, radius, 0, Math.PI * 2);
- ctx.fill();
- }
- }
-
- function drawTundraTexture(ctx) {
- // Sparse dots for tundra
- ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
-
- for (let i = 0; i < 80; i++) {
- const x = Math.random() * 128;
- const y = Math.random() * 128;
- const radius = 1 + Math.random() * 2;
-
- ctx.beginPath();
- ctx.arc(x, y, radius, 0, Math.PI * 2);
- ctx.fill();
- }
- }
-
- function drawForestTexture(ctx) {
- // Small tree-like shapes
- ctx.fillStyle = 'rgba(255, 255, 255, 0.15)';
-
- for (let i = 0; i < 20; i++) {
- const x = Math.random() * 128;
- const y = Math.random() * 128;
- const size = 3 + Math.random() * 6;
-
- // Tree top
- ctx.beginPath();
- ctx.arc(x, y - size/2, size, 0, Math.PI * 2);
- ctx.fill();
-
- // Trunk
- ctx.fillRect(x - 1, y, 2, size);
- }
- }
-
- function drawPlainsTexture(ctx) {
- // Horizontal line patterns for fields
- ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
- ctx.lineWidth = 1;
-
- for (let i = 0; i < 12; i++) {
- const y = i * 10 + Math.random() * 5;
-
- ctx.beginPath();
- ctx.moveTo(0, y);
- ctx.lineTo(128, y);
- ctx.stroke();
- }
- }
-
- function drawIslandTexture(ctx) {
- // Small dots and lines for coastal features
- ctx.fillStyle = 'rgba(255, 255, 255, 0.15)';
- ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
- ctx.lineWidth = 1;
-
- // Dots
- for (let i = 0; i < 50; i++) {
- const x = Math.random() * 128;
- const y = Math.random() * 128;
- const radius = 1 + Math.random();
-
- ctx.beginPath();
- ctx.arc(x, y, radius, 0, Math.PI * 2);
- ctx.fill();
- }
-
- // Curved lines for terrain contours
- for (let i = 0; i < 5; i++) {
- const x1 = Math.random() * 128;
- const y1 = Math.random() * 128;
- const x2 = x1 + (Math.random() * 40 - 20);
- const y2 = y1 + (Math.random() * 40 - 20);
-
- ctx.beginPath();
- ctx.moveTo(x1, y1);
- ctx.quadraticCurveTo(
- (x1 + x2) / 2 + (Math.random() * 20 - 10),
- (y1 + y2) / 2 + (Math.random() * 20 - 10),
- x2, y2
- );
- ctx.stroke();
- }
- }
-
- function drawAridTexture(ctx) {
- // Sand dune-like patterns
- ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
- ctx.lineWidth = 1;
-
- for (let i = 0; i < 10; i++) {
- const yBase = i * 12 + Math.random() * 5;
-
- ctx.beginPath();
- ctx.moveTo(0, yBase);
-
- for (let x = 0; x < 128; x += 16) {
- const amplitude = 2 + Math.random() * 2;
- const yOffset = Math.sin(x / 16) * amplitude;
-
- ctx.quadraticCurveTo(
- x + 8, yBase + yOffset,
- x + 16, yBase
- );
- }
-
- ctx.stroke();
- }
- }
-
- function drawGenericLandTexture(ctx) {
- // Simple noise pattern
- ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
-
- for (let i = 0; i < 60; i++) {
- const x = Math.random() * 128;
- const y = Math.random() * 128;
- const radius = 1 + Math.random() * 3;
-
- ctx.beginPath();
- ctx.arc(x, y, radius, 0, Math.PI * 2);
- ctx.fill();
- }
- }
-
- // Load coordinate data
+ // --- LOAD COORDINATE DATA ---
function loadCoordinateData() {
return new Promise((resolve, reject) => {
fetch('./assets/maps/standard_coords.json')
.then(response => {
if (!response.ok) {
- // Try alternate path
+ // Try an alternate path if desired
return fetch('../assets/maps/standard_coords.json');
}
return response;
})
.then(response => {
if (!response.ok) {
- // Try another alternate path
+ // Another fallback path
return fetch('/diplomacy/animation/assets/maps/standard_coords.json');
}
return response;
@@ -873,2013 +224,465 @@
})
.then(data => {
coordinateData = data;
- infoPanel.textContent = 'Coordinate data loaded successfully!';
- console.log('Loaded coordinates for', Object.keys(coordinateData.coordinates).length, 'provinces');
+ infoPanel.textContent = 'Coordinate data loaded!';
resolve(coordinateData);
})
.catch(error => {
- console.error('Error loading coordinates:', error);
- infoPanel.textContent = `Error loading coordinates: ${error.message}. Using placeholder positions.`;
- // Resolve anyway so we can continue with placeholder positions
- resolve(null);
+ console.error(error);
+ reject(error);
});
});
}
-
- // Animation loop
- function animate() {
- requestAnimationFrame(animate);
-
- // Update pulsing animations for supply centers
- if (scene.userData.animatedObjects) {
- scene.userData.animatedObjects.forEach(obj => {
- // Handle supply center pulsing
- if (obj.userData.pulseAnimation) {
- const anim = obj.userData.pulseAnimation;
- anim.time += anim.speed;
-
- // Pulsing effect on the glow
- if (obj.userData.glowMesh) {
- const pulseValue = Math.sin(anim.time) * anim.intensity + 0.5;
- obj.userData.glowMesh.material.opacity = 0.2 + (pulseValue * 0.3);
- obj.userData.glowMesh.scale.set(
- 1 + (pulseValue * 0.1),
- 1 + (pulseValue * 0.1),
- 1 + (pulseValue * 0.1)
- );
- }
-
- // Subtle height movement
- obj.position.y = 2 + (Math.sin(anim.time) * 0.5);
- }
-
- // Handle wave animation for sea territories
- if (obj.userData.waveAnimation) {
- const anim = obj.userData.waveAnimation;
- anim.time += anim.speed;
-
- // Rotate texture coordinates for wave effect
- if (obj.material && obj.material.map) {
- obj.material.map.offset.x = Math.sin(anim.time) * 0.05;
- obj.material.map.offset.y = Math.cos(anim.time) * 0.05;
- obj.material.map.needsUpdate = true;
- }
- }
- });
+
+ // --- CREATE THE FALLBACK MAP AS A PLANE ---
+ function createFallbackMap() {
+ // If there's already a plane from a previous phase re-draw, remove it
+ if (mapPlane) {
+ scene.remove(mapPlane);
+ mapPlane.geometry.dispose();
+ mapPlane.material.dispose();
+ mapPlane = null;
}
-
- controls.update();
- renderer.render(scene, camera);
+
+ const mapGeometry = new THREE.PlaneGeometry(1000, 1000);
+
+ // We'll create a texture by drawing on a