diff --git a/fog.js b/fog.js index c833708..c8ac9d7 100644 --- a/fog.js +++ b/fog.js @@ -3,38 +3,57 @@ document.addEventListener("DOMContentLoaded", () => { + // ============================= + // 設定 + // ============================= const START_POS = [38.891, 139.824]; - const REVEAL_RADIUS = 40; // m + const REVEAL_RADIUS = 40; // m + const MAX_EXPLORE_POINTS = 300; // 制圧率100% + // ============================= + // DOM + // ============================= const titleEl = document.getElementById("title"); const startBtn = document.getElementById("start"); - const stopBtn = document.getElementById("stop"); + const stopBtn = document.getElementById("stop"); + const controls = document.getElementById("controls"); + // 制圧率表示(HTMLに無いので作る) + const rateEl = document.createElement("span"); + rateEl.textContent = " 制圧率: 0%"; + rateEl.style.marginLeft = "12px"; + rateEl.style.fontWeight = "bold"; + controls.appendChild(rateEl); + + // ============================= + // 状態 + // ============================= let map; let playerMarker; let watchId = null; - // === 解除済みポイント(世界座標)=== + // 解除済みポイント const revealedPoints = []; - // === Canvas霧レイヤ === + // ============================= + // Fog Layer + // ============================= const FogLayer = L.GridLayer.extend({ createTile: function (coords) { - const tile = document.createElement('canvas'); + const tile = document.createElement("canvas"); const size = this.getTileSize(); tile.width = size.x; tile.height = size.y; - const ctx = tile.getContext('2d'); + const ctx = tile.getContext("2d"); - // 霧を塗る - ctx.fillStyle = 'rgba(0,0,0,0.85)'; + // 霧 + ctx.fillStyle = "rgba(0,0,0,0.85)"; ctx.fillRect(0, 0, size.x, size.y); - // タイル境界の緯度経度 const bounds = this._tileCoordsToBounds(coords); + ctx.globalCompositeOperation = "destination-out"; - // 解除ポイントを削る revealedPoints.forEach(latlng => { if (!bounds.contains(latlng)) return; @@ -44,9 +63,14 @@ const x = point.x - tilePoint.x; const y = point.y - tilePoint.y; - ctx.globalCompositeOperation = 'destination-out'; ctx.beginPath(); - ctx.arc(x, y, metersToPixels(REVEAL_RADIUS, latlng.lat), 0, Math.PI * 2); + ctx.arc( + x, + y, + metersToPixels(REVEAL_RADIUS, latlng.lat), + 0, + Math.PI * 2 + ); ctx.fill(); }); @@ -56,15 +80,45 @@ let fogLayer; + // ============================= + // Utility + // ============================= function metersToPixels(m, lat) { - return m / 40075017 * 256 * Math.pow(2, map.getZoom()) / Math.cos(lat * Math.PI / 180); + return ( + m / 40075017 * + 256 * + Math.pow(2, map.getZoom()) / + Math.cos(lat * Math.PI / 180) + ); } + function isNearExisting(latlng, threshold = 10) { + return revealedPoints.some(p => p.distanceTo(latlng) < threshold); + } + + // ============================= + // 制圧率 + // ============================= + function updateExploreRate() { + const rate = Math.min( + 100, + Math.floor((revealedPoints.length / MAX_EXPLORE_POINTS) * 100) + ); + rateEl.textContent = ` 制圧率: ${rate}%`; + + if (rate === 100) { + titleEl.textContent = "完全制圧!"; + } + } + + // ============================= + // Map初期化 + // ============================= function initMap() { map = L.map("locationmap").setView(START_POS, 16); - L.tileLayer("https://{s}.tile.osm.org/{z}/{x}/{y}.png", { - attribution: '© OpenStreetMap contributors' + L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { + attribution: "© OpenStreetMap contributors" }).addTo(map); fogLayer = new FogLayer(); @@ -73,24 +127,38 @@ playerMarker = L.marker(map.getCenter()).addTo(map); playerMarker.bindPopup("STARTで探索開始").openPopup(); + // デバッグ用クリック探索 map.on("click", e => updateLocation(e.latlng)); } + // ============================= + // 霧解除 + // ============================= function reveal(latlng) { + // 同じ場所での重複解除防止 + if (isNearExisting(latlng)) return; + revealedPoints.push(latlng); - fogLayer.redraw(); // ← ここが重要 + fogLayer.redraw(); + updateExploreRate(); } + // ============================= + // 位置更新 + // ============================= function updateLocation(latlng) { - map.panTo(latlng); + map.panTo(latlng, { animate: false }); playerMarker.setLatLng(latlng); reveal(latlng); - - playerMarker.setPopupContent("探索中…").openPopup(); } + // ============================= + // GPS + // ============================= function onSuccess(pos) { - updateLocation(L.latLng(pos.coords.latitude, pos.coords.longitude)); + updateLocation( + L.latLng(pos.coords.latitude, pos.coords.longitude) + ); } function startWatch() { @@ -104,16 +172,22 @@ } function stopWatch() { - if (watchId) { + if (watchId !== null) { navigator.geolocation.clearWatch(watchId); watchId = null; - titleEl.textContent = "STOP"; } + titleEl.textContent = "霧解除探索ゲーム"; } + // ============================= + // Event + // ============================= startBtn.addEventListener("click", startWatch); stopBtn.addEventListener("click", stopWatch); + // ============================= + // Start + // ============================= initMap(); }); })();