diff --git a/index.html b/index.html index 954b00a..d66a208 100644 --- a/index.html +++ b/index.html @@ -1,252 +1,536 @@ - - -Nerdhalla — Valheim World Map - - - + + + Nerdhalla — Valheim Server + + + -
- - - -
-

🌍 Nerdhalla

-
SeedyzZ5fr2tGa
-
World24000 x 24000
-
POIs11309
-
Items24407
-
Biomes9
-
-
- Click a marker for details
- Search by name or category + +
+
+
⚔️
+
+

Nerdhalla

+
Valheim Community Server
+
+
+
+ + Checking server... +
-
-
- - - -
+ + -
+ +
+ +
+
+

🏰 Welcome to Nerdhalla

+

+ A vanilla Valheim community server — no mods, no BepInEx, just pure Viking survival. + Explore a hand-mapped world, find the best base locations, and conquer the tenth world together. +

+
- + + + + + composite.addTo(map); + map.fitBounds(bounds); + + L.control.layers(baseMaps, null, { collapsed: false }).addTo(map); + + // Coordinate display + const coordDisplay = L.control({ position: 'bottomleft' }); + coordDisplay.onAdd = function() { + const div = L.DomUtil.create('div', 'coord-display'); + div.style.cssText = 'background:rgba(20,20,31,0.9); color:#d4d4dc; padding:4px 10px; border-radius:4px; font-size:12px; border:1px solid #2a2a3e;'; + div.innerHTML = 'Move cursor for coordinates'; + return div; + }; + coordDisplay.addTo(map); + + map.on('mousemove', function(e) { + const x = Math.round(e.latlng.lng); + const y = Math.round(e.latlng.lat); + document.querySelector('.coord-display').innerHTML = `📍 (${x}, ${y})`; + }); + + // POI markers from database + fetch('/api/pois?limit=500') + .then(r => r.json()) + .then(pois => { + const poiIcons = { + 'point_of_interest': L.divIcon({ html: '📍', className: 'poi-icon', iconSize: [20, 20] }), + 'boss': L.divIcon({ html: '👑', className: 'poi-icon', iconSize: [20, 20] }), + 'dungeon': L.divIcon({ html: '🏚️', className: 'poi-icon', iconSize: [20, 20] }), + 'resource': L.divIcon({ html: '⛏️', className: 'poi-icon', iconSize: [20, 20] }), + 'structure': L.divIcon({ html: '🏠', className: 'poi-icon', iconSize: [20, 20] }), + 'spawner': L.divIcon({ html: '👾', className: 'poi-icon', iconSize: [20, 20] }), + 'altar': L.divIcon({ html: '🪦', className: 'poi-icon', iconSize: [20, 20] }), + 'vendor': L.divIcon({ html: '🏪', className: 'poi-icon', iconSize: [20, 20] }), + 'other': L.divIcon({ html: '❓', className: 'poi-icon', iconSize: [20, 20] }), + 'default': L.divIcon({ html: '📍', className: 'poi-icon', iconSize: [20, 20] }) + }; + const markerLayer = L.layerGroup(); + pois.forEach(p => { + const icon = poiIcons[p.category] || poiIcons['default']; + const m = L.marker([p.y, p.x], { icon }).bindPopup( + `${p.prefab || 'Unknown'}
📍 (${Math.round(p.x)}, ${Math.round(p.y)})
📂 ${p.category || 'Unknown'}` + ); + markerLayer.addLayer(m); + }); + markerLayer.addTo(map); + L.control.layers(null, { "POIs (11k+)": markerLayer }, { collapsed: true }).addTo(map); + }); + } + + // Init map when page loads or switches to map + document.addEventListener('DOMContentLoaded', () => { + // Defer map init until tab is clicked + }); + document.querySelector('.nav a[data-page="map"]').addEventListener('click', () => { + setTimeout(initMap, 200); + }); + + // ====== POI Browser ====== + let allPOIs = []; + async function loadPOIs() { + const results = document.getElementById('poiResults'); + if (allPOIs.length > 0) return renderPOIs(); + try { + const resp = await fetch('/api/pois?limit=11309'); + allPOIs = await resp.json(); + // Populate categories + const cats = new Set(allPOIs.map(p => p.category).filter(Boolean)); + const sel = document.getElementById('poiCategory'); + cats.forEach(c => { + const opt = document.createElement('option'); + opt.value = c; + opt.textContent = c.replace(/_/g, ' '); + sel.appendChild(opt); + }); + renderPOIs(); + } catch { + results.innerHTML = '

Failed to load POIs

'; + } + } + + function renderPOIs() { + const search = document.getElementById('poiSearch').value.toLowerCase(); + const cat = document.getElementById('poiCategory').value; + const filtered = allPOIs.filter(p => { + const name = (p.prefab || '').toLowerCase(); + const catVal = (p.category || ''); + return name.includes(search) && (!cat || catVal === cat); + }); + const results = document.getElementById('poiResults'); + if (filtered.length === 0) { + results.innerHTML = '

No matching POIs

'; + return; + } + results.innerHTML = filtered.slice(0, 200).map(p => ` +
+ ${p.prefab || 'Unknown'} (${Math.round(p.x)}, ${Math.round(p.y)}) + ${(p.category || '').replace(/_/g, ' ')} +
+ `).join(''); + if (filtered.length > 200) { + results.innerHTML += `

Showing 200 of ${filtered.length} results

`; + } + } + + document.getElementById('poiSearch').addEventListener('input', renderPOIs); + document.getElementById('poiCategory').addEventListener('change', renderPOIs); + - \ No newline at end of file +