diff --git a/hokaku.js b/hokaku.js new file mode 100644 index 0000000..bdb7818 --- /dev/null +++ b/hokaku.js @@ -0,0 +1,190 @@ +(() => { + 'use strict'; + + document.addEventListener("DOMContentLoaded", () => { + + // ========================= + // 定数 + // ========================= + const START_POS = [38.891, 139.824]; + const CAPTURE_DISTANCE = 30; // m + const NEAR_DISTANCE = 100; // m + const CAPTURE_RATE = 0.7; // 捕獲成功率 + + // ========================= + // DOM + // ========================= + const titleEl = document.getElementById("title"); + const startBtn = document.getElementById("start"); + const stopBtn = document.getElementById("stop"); + const captureBtn = document.getElementById("capture"); + + // ========================= + // 状態 + // ========================= + let map; + let playerMarker; + let monsterMarker = null; + let monsterLatLng = null; + + let watchId = null; + let nTrial = 100; + let canCapture = false; + + // ========================= + // Map 初期化 + // ========================= + function initMap() { + map = L.map("locationmap", { tap: true }) + .setView(START_POS, 16); + + L.tileLayer("https://{s}.tile.osm.org/{z}/{x}/{y}.png", { + attribution: + '© OpenStreetMap contributors' + }).addTo(map); + + playerMarker = L.marker(map.getCenter()).addTo(map); + playerMarker.bindPopup("STARTを押して探索開始").openPopup(); + } + + // ========================= + // モンスター生成 + // ========================= + function spawnMonster(center) { + const offsetLat = (Math.random() - 0.5) * 0.002; + const offsetLng = (Math.random() - 0.5) * 0.002; + + monsterLatLng = L.latLng( + center.lat + offsetLat, + center.lng + offsetLng + ); + + if (monsterMarker) { + map.removeLayer(monsterMarker); + } + + monsterMarker = L.marker(monsterLatLng, { + title: "なにかの気配" + }).addTo(map); + } + + // ========================= + // 位置更新 + // ========================= + function updateLocation(latlng) { + map.panTo(latlng); + playerMarker.setLatLng(latlng); + + if (!monsterLatLng) { + spawnMonster(latlng); + } + + const distance = map.distance(latlng, monsterLatLng); + let message = "探索中…"; + + if (distance < CAPTURE_DISTANCE) { + message = "🎯 モンスターが近い!捕獲できる!"; + canCapture = true; + captureBtn.disabled = false; + } else if (distance < NEAR_DISTANCE) { + message = "👀 気配が近い…"; + canCapture = false; + captureBtn.disabled = true; + } else { + canCapture = false; + captureBtn.disabled = true; + } + + playerMarker.setPopupContent(message).openPopup(); + } + + // ========================= + // 捕獲処理(ガチャ) + // ========================= + function captureMonster() { + if (!canCapture || !monsterLatLng) return; + + const success = Math.random() < CAPTURE_RATE; + + if (success) { + playerMarker.setPopupContent("🎉 捕獲成功!").openPopup(); + } else { + playerMarker.setPopupContent("💨 逃げられた…").openPopup(); + } + + if (monsterMarker) { + map.removeLayer(monsterMarker); + } + + monsterMarker = null; + monsterLatLng = null; + canCapture = false; + captureBtn.disabled = true; + } + + // ========================= + // GPS 成功 + // ========================= + function onSuccess(pos) { + const latlng = L.latLng( + pos.coords.latitude, + pos.coords.longitude + ); + updateLocation(latlng); + } + + // ========================= + // GPS エラー + // ========================= + function onError() { + nTrial--; + playerMarker + .setPopupContent(`捕捉失敗… 残り ${nTrial} 回`) + .openPopup(); + + if (nTrial <= 0 && watchId !== null) { + navigator.geolocation.clearWatch(watchId); + watchId = null; + titleEl.textContent = "ERROR"; + } + } + + // ========================= + // 追跡制御 + // ========================= + function startWatch() { + stopWatch(); + watchId = navigator.geolocation.watchPosition( + onSuccess, + onError, + { maximumAge: 0, timeout: 3000, enableHighAccuracy: true } + ); + titleEl.textContent = "探索中"; + } + + function stopWatch() { + if (watchId !== null) { + navigator.geolocation.clearWatch(watchId); + watchId = null; + titleEl.textContent = "STOP"; + playerMarker.setPopupContent("探索を停止しました").openPopup(); + } + } + + // ========================= + // イベント + // ========================= + startBtn.addEventListener("click", startWatch); + stopBtn.addEventListener("click", stopWatch); + captureBtn.addEventListener("click", captureMonster); + + // マップクリック(デバッグ用) + map?.on?.("click", () => {}); + + // ========================= + // 起動 + // ========================= + initMap(); + + }); +})();