import { Router } from 'express'; import { getDb, saveDb } from '../db.js'; const router = Router(); // Get all hexes for a map (bulk load) router.get('/:mapId/hexes', (req, res) => { const db = getDb(); const mapId = req.params.mapId; const hexRows = db.exec( 'SELECT id, q, r, base_terrain FROM hexes WHERE map_id = ?', [mapId], ); if (hexRows.length === 0) { res.json([]); return; } const hexIds = hexRows[0].values.map(row => row[0] as number); const hexes: any[] = []; // Fetch features for all hexes const featureMap = new Map(); if (hexIds.length > 0) { // Batch query — SQLite doesn't have great IN with params for large sets, // so we query all features for this map's hexes const featureRows = db.exec( `SELECT hf.hex_id, hf.terrain_id, hf.edge_mask FROM hex_features hf JOIN hexes h ON hf.hex_id = h.id WHERE h.map_id = ?`, [mapId], ); if (featureRows.length > 0) { for (const row of featureRows[0].values) { const hexId = row[0] as number; if (!featureMap.has(hexId)) featureMap.set(hexId, []); featureMap.get(hexId)!.push({ terrainId: row[1], edgeMask: row[2], }); } } } for (const row of hexRows[0].values) { const hexId = row[0] as number; hexes.push({ q: row[1], r: row[2], base: row[3], features: featureMap.get(hexId) ?? [], }); } res.json(hexes); }); // Bulk upsert hexes router.put('/:mapId/hexes', (req, res) => { const db = getDb(); const mapId = req.params.mapId; const hexes: Array<{ q: number; r: number; base: string; features: Array<{ terrainId: string; edgeMask: number }>; }> = req.body; if (!Array.isArray(hexes)) { res.status(400).json({ error: 'Expected array of hex updates' }); return; } db.run('BEGIN TRANSACTION'); try { for (const hex of hexes) { // Upsert the hex db.run( `INSERT INTO hexes (map_id, q, r, base_terrain, updated_at) VALUES (?, ?, ?, ?, datetime('now')) ON CONFLICT(map_id, q, r) DO UPDATE SET base_terrain = excluded.base_terrain, updated_at = datetime('now')`, [mapId, hex.q, hex.r, hex.base], ); // Get the hex id const idRows = db.exec( 'SELECT id FROM hexes WHERE map_id = ? AND q = ? AND r = ?', [mapId, hex.q, hex.r], ); const hexId = idRows[0].values[0][0] as number; // Replace features db.run('DELETE FROM hex_features WHERE hex_id = ?', [hexId]); for (const feature of hex.features) { if (feature.edgeMask === 0) continue; db.run( 'INSERT INTO hex_features (hex_id, terrain_id, edge_mask) VALUES (?, ?, ?)', [hexId, feature.terrainId, feature.edgeMask], ); } } // Update map timestamp db.run("UPDATE hex_maps SET updated_at = datetime('now') WHERE id = ?", [mapId]); db.run('COMMIT'); saveDb(); res.json({ ok: true, count: hexes.length }); } catch (err) { db.run('ROLLBACK'); res.status(500).json({ error: String(err) }); } }); // Delete a hex (reset to default) router.delete('/:mapId/hexes/:q/:r', (req, res) => { const db = getDb(); const { mapId, q, r } = req.params; const idRows = db.exec( 'SELECT id FROM hexes WHERE map_id = ? AND q = ? AND r = ?', [mapId, q, r], ); if (idRows.length > 0 && idRows[0].values.length > 0) { const hexId = idRows[0].values[0][0]; db.run('DELETE FROM hex_features WHERE hex_id = ?', [hexId]); db.run('DELETE FROM hexes WHERE id = ?', [hexId]); saveDb(); } res.json({ ok: true }); }); export default router;