#!/usr/bin/env python3 """ Build a Leaflet.js web viewer for the Nerdhalla Valheim world. """ import sqlite3, json, os DB_PATH = "/tmp/valheim_poi.db" OUTPUT_DIR = "/tmp/valheim_map_output" VIEWER_PATH = os.path.join(OUTPUT_DIR, "index.html") WORLD_SIZE = 24000 PIXELS_TOTAL = 4096 def valheim_to_pixel(vx, vz): px = (vx + WORLD_SIZE/2) / WORLD_SIZE * PIXELS_TOTAL py = (vz + WORLD_SIZE/2) / WORLD_SIZE * PIXELS_TOTAL return px, py def query_pois(): conn = sqlite3.connect(DB_PATH) c = conn.cursor() c.execute(""" SELECT id, prefab_name, category, pos_x, pos_z, has_dungeon, has_important FROM locations WHERE pos_x IS NOT NULL AND pos_z IS NOT NULL ORDER BY category, prefab_name """) pois = [] for row in c.fetchall(): loc_id, name, cat, x, z, has_dungeon, has_important = row px, py = valheim_to_pixel(x, z) pois.append({ 'id': loc_id, 'name': name, 'category': cat, 'x': round(x, 1), 'z': round(z, 1), 'px': round(px, 1), 'py': round(py, 1), 'has_dungeon': bool(has_dungeon), 'has_important': bool(has_important), }) c.execute(""" SELECT l.id, l.prefab_name, l.pos_x, l.pos_z, ic.friendly_name, ic.count FROM locations l JOIN important_contents ic ON l.id = ic.location_id WHERE l.pos_x IS NOT NULL AND l.pos_z IS NOT NULL ORDER BY ic.count DESC """) contents = [] for row in c.fetchall(): loc_id, name, x, z, fname, count = row px, py = valheim_to_pixel(x, z) contents.append({ 'loc_id': loc_id, 'loc_name': name, 'item': fname, 'count': count, 'x': round(x, 1), 'z': round(z, 1), 'px': round(px, 1), 'py': round(py, 1), }) conn.close() return pois, contents def build_viewer(pois, contents): pois_json = json.dumps(pois) contents_json = json.dumps(contents) cat_colors = { 'boss': '#ff4444', 'dungeon': '#ff8800', 'vegvisir': '#44aaff', 'altar': '#aa88ff', 'vendor': '#ffdd00', 'structure': '#88cc88', 'resource': '#ff66aa', 'spawner': '#ff44ff', 'point_of_interest': '#44ffaa', 'other': '#888888', } cat_colors_json = json.dumps(cat_colors) cat_labels = { 'boss': 'Boss Altars', 'dungeon': 'Dungeons', 'vegvisir': 'Vegvisirs', 'vendor': 'Vendors', 'altar': 'Altars / Stones', 'structure': 'Structures', 'resource': 'Resources', 'spawner': 'Spawners', 'point_of_interest': 'POIs', 'other': 'Other', } cat_labels_json = json.dumps(cat_labels) # Build category counts cat_counts = {} for p in pois: cat_counts[p['category']] = cat_counts.get(p['category'], 0) + 1 cat_counts_json = json.dumps(cat_counts) html = """ Nerdhalla — Valheim World Map

🌍 Nerdhalla

SeedyzZ5fr2tGa
World24000 x 24000
POIs""" + str(len(pois)) + """
Items""" + str(len(contents)) + """
Biomes9

Click a marker for details
Search by name or category
""" with open(VIEWER_PATH, 'w') as f: f.write(html) print(f"Viewer written to {VIEWER_PATH}") if __name__ == '__main__': print("Building Valheim map viewer...") pois, contents = query_pois() print(f"Loaded {len(pois)} POIs and {len(contents)} important contents") build_viewer(pois, contents) print("Done!")