const canvas = document.getElementById("mapCanvas"); const fogCanvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); const fogCtx = fogCanvas.getContext("2d"); const iframe = document.getElementById("worldmap-iframe"); let image = null; let offsetX = 0, offsetY = 0; let scale = 1; let isDragging = false; let startX, startY; let fogEnabled = false; let fogMode = "erase"; let brushSize = 50; let mouseX = 0, mouseY = 0; const channel = new BroadcastChannel("mapsync"); let MapX = 0; let MapY = 0; let MapZoom = 2; function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; draw(); if (fogEnabled) drawBrushPreview(mouseX, mouseY); } window.addEventListener("resize", resizeCanvas); resizeCanvas(); // === Fog Controls === document.getElementById("toggleFog").addEventListener("change", (e) => { fogEnabled = e.target.checked; channel.postMessage({ type: "fogEnabled", enabled: fogEnabled }); draw(); if (fogEnabled) drawBrushPreview(mouseX, mouseY); }); document.getElementById("fogMode").addEventListener("click", () => { fogMode = fogMode === "erase" ? "paint" : "erase"; document.getElementById("fogMode").innerText = fogMode === "erase" ? "Modus: Radierer" : "Modus: Nebel"; }); document.getElementById("fogClear").addEventListener("click", () => { const btn = document.getElementById("fogClear"); btn.disabled = true; setTimeout(() => btn.disabled = false, 1000); fogCtx.clearRect(0, 0, fogCanvas.width, fogCanvas.height); syncFog(); draw(); if (fogEnabled) drawBrushPreview(mouseX, mouseY); }); document.getElementById("fogFull").addEventListener("click", () => { const btn = document.getElementById("fogFull"); btn.disabled = true; setTimeout(() => btn.disabled = false, 1000); fogCtx.fillStyle = "rgba(50, 50, 50, 1)"; fogCtx.fillRect(0, 0, fogCanvas.width, fogCanvas.height); syncFog(); draw(); if (fogEnabled) drawBrushPreview(mouseX, mouseY); }); document.getElementById("brushSize").addEventListener("input", (e) => { brushSize = 50 * parseInt(e.target.value); }); // === Mouse Interaktion === canvas.addEventListener("dragover", e => e.preventDefault()); canvas.addEventListener("drop", e => { e.preventDefault(); const file = e.dataTransfer.files[0]; const reader = new FileReader(); reader.onload = evt => { const img = new Image(); img.onload = () => { image = img; //Fog auf Größe des Geladenen Bildes anpassen. fogCanvas.width = img.width; fogCanvas.height = img.height; fogCtx.clearRect(0, 0, fogCanvas.width, fogCanvas.height); draw(); if (fogEnabled) drawBrushPreview(mouseX, mouseY); channel.postMessage({ type: "mapData", data: evt.target.result }); channel.postMessage({ type: "fogEnabled", enabled: fogEnabled }); syncFog(); }; img.src = evt.target.result; }; reader.readAsDataURL(file); }); function LoadImageMap(i) { const img = new Image(); img.onload = () => { image = img; fogCanvas.width = img.width; fogCanvas.height = img.height; fogCtx.clearRect(0, 0, fogCanvas.width, fogCanvas.height); draw(); if (fogEnabled) drawBrushPreview(mouseX, mouseY); channel.postMessage({ type: "mapData", data: i }); channel.postMessage({ type: "fogEnabled", enabled: fogEnabled }); syncFog(); }; img.src = i; } window.LoadImageMap = LoadImageMap; canvas.addEventListener("mousedown", e => { if (e.button === 1) { isDragging = true; startX = e.clientX - offsetX; startY = e.clientY - offsetY; } }); canvas.addEventListener("mouseup", () => { isDragging = false; }); canvas.addEventListener("mouseout", () => { isDragging = false; }); canvas.addEventListener("mousemove", e => { mouseX = e.clientX; mouseY = e.clientY; if (fogEnabled) draw(); if (isDragging) { offsetX = e.clientX - startX; offsetY = e.clientY - startY; syncView(); draw(); if (fogEnabled) drawBrushPreview(mouseX, mouseY); } else if (fogEnabled && e.buttons === 1) { const x = (mouseX - offsetX) / scale; const y = (mouseY - offsetY) / scale; fogCtx.globalCompositeOperation = fogMode === "erase" ? "destination-out" : "source-over"; fogCtx.fillStyle = "rgba(50, 50, 50, 0.9)"; fogCtx.beginPath(); fogCtx.arc(x, y, brushSize, 0, 2 * Math.PI); fogCtx.fill(); syncFog(); draw(); drawBrushPreview(mouseX, mouseY); } }); canvas.addEventListener("wheel", e => { e.preventDefault(); const worldX = (e.clientX - offsetX) / scale; const worldY = (e.clientY - offsetY) / scale; const zoomFactor = e.deltaY < 0 ? 1.1 : 0.9; scale *= zoomFactor; offsetX = e.clientX - worldX * scale; offsetY = e.clientY - worldY * scale; syncView(); draw(); if (fogEnabled) drawBrushPreview(mouseX, mouseY); }); function syncView() { channel.postMessage({ type: "viewData", offsetX, offsetY, scale }); } function syncFog() { const fogURL = fogCanvas.toDataURL("image/png"); channel.postMessage({ type: "fogData", data: fogURL }); } function draw() { ctx.setTransform(scale, 0, 0, scale, offsetX, offsetY); ctx.clearRect(-offsetX / scale, -offsetY / scale, canvas.width / scale, canvas.height / scale); if (image) ctx.drawImage(image, 0, 0); if (fogEnabled && fogCanvas.width && fogCanvas.height) { ctx.globalAlpha = 0.4; ctx.drawImage(fogCanvas, 0, 0); ctx.globalAlpha = 1; } if (fogEnabled) drawBrushPreview(mouseX, mouseY); } function drawBrushPreview(mouseX, mouseY) { const worldX = (mouseX - offsetX) / scale; const worldY = (mouseY - offsetY) / scale; ctx.save(); ctx.setTransform(scale, 0, 0, scale, offsetX, offsetY); ctx.strokeStyle = "rgba(255,255,255,0.8)"; ctx.lineWidth = 2 / scale; ctx.beginPath(); ctx.arc(worldX, worldY, brushSize, 0, 2 * Math.PI); ctx.stroke(); ctx.restore(); } window.addEventListener("keydown", (e) => { if (e.key.toLowerCase() === "f") { fogEnabled = !fogEnabled; document.getElementById("toggleFog").checked = fogEnabled; channel.postMessage({ type: "fogEnabled", enabled: fogEnabled }); draw(); } }); document.getElementById("toggleHexgrid").addEventListener("change", (e) => { channel.postMessage({ type: "hexgrid", enabled: e.target.checked }); }); document.getElementById("gridScaleSlider").addEventListener("input", (e) => { const scale = parseFloat(e.target.value); channel.postMessage({ type: "hexgridScale", scale }); }); // === Weltkarte Funktionen === let worldMapMode = false; let previousImageSrc = null; // === Leaflet View Update Intervall === setInterval(() => { const iframe = document.getElementById("worldmap-iframe"); if (iframe?.contentWindow && worldMapMode) { iframe.contentWindow.postMessage("getView", "*"); } }, 500); window.addEventListener("message", (event) => { const data = event.data; if (data?.type === "viewData") { MapX = data.center.lng; MapY = data.center.lat; MapZoom = data.zoom; channel.postMessage({ type: "MapCoord", MapX, MapY, MapZoom}); //Dem Slave die Karten Werte schicken. } }); const mapButton = document.getElementById("mapToggleButton"); if (mapButton) { mapButton.addEventListener("click", () => { if (!worldMapMode) { previousImageSrc = image?.src || "pause.png"; loadMap(); channel.postMessage({ type: "mapMode", enabled: true }); mapButton.textContent = "↩️"; worldMapMode = true; syncWorldMapView(); } else { restoreImage(previousImageSrc); channel.postMessage({ type: "mapMode", enabled: false }); mapButton.textContent = "🗺"; worldMapMode = false; } }); } //Laden der iframe WorldMap function loadMap() { const iframe = document.getElementById("worldmap-iframe"); if (!iframe) return; iframe.style.display = "block"; if (iframe.contentWindow) { iframe.contentWindow.postMessage({ action: "syncRequest" }, "*"); } else { iframe.onload = () => { iframe.contentWindow.postMessage({ action: "syncRequest" }, "*"); }; } } //Nach dem schließen der iframe WorldMap, wieder die letzte Map herstellen. function restoreImage(src) { const iframe = document.getElementById("worldmap-iframe"); const canvas = document.getElementById("mapCanvas"); if (iframe) iframe.style.display = "none"; if (canvas) canvas.style.display = "block"; if (!src) return; const img = new Image(); img.onload = () => { image = img; fogCanvas.width = img.width; fogCanvas.height = img.height; fogCtx.clearRect(0, 0, fogCanvas.width, fogCanvas.height); draw(); }; img.src = src; } function syncWorldMapView() { const iframe = document.getElementById("worldmap-iframe"); if (worldMapMode && iframe?.contentWindow) { iframe.contentWindow.postMessage("getView", "*"); } }