Something went wrong. Try again.
An atproto Tilemap editor
Something went wrong. Try again.
3.2 kB · 88 lines
TypeScript
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889// ---------------------------------------------------------------------------// Map minimap renderer — SEAM MODULE.//// The World editor composites a small preview of each placed map's tiles. A// real minimap renderer (bake a map's painted canvases + its tileset image into// a bitmap, cached, suitable for world-composition tools) is assumed to exist// and is built separately. This module declares the contract the World editor// consumes and ships a labelled placeholder so the editor renders and is// testable before that feature lands.//// Replace `renderPlaceholder` with the real render (read the MapDoc via// `getMapDoc`, load its tileset, blit baked tiles to an offscreen canvas) and// the World editor picks it up unchanged.// ---------------------------------------------------------------------------
import { getMapDoc } from '@/store/mapPersistence'
/** A drawable preview of a map plus its cell extent (for sizing + grid math). */export interface MapMinimap { bitmap: ImageBitmap | HTMLCanvasElement cols: number rows: number}
const cache = new Map<string, MapMinimap>()
/** Drop one (or all) cached minimaps — call when a map's tiles change. */export function invalidateMinimap(mapId?: string) { if (mapId) cache.delete(mapId) else cache.clear()}
/** Stable 0–360 hue from a string, so each placeholder reads as a distinct map. */function hueFromId(id: string): number { let h = 0 for (let i = 0; i < id.length; i++) h = (h * 31 + id.charCodeAt(i)) % 360 return h}
/** Default placeholder extent, in cells, until a real map size is known. */const PLACEHOLDER_COLS = 16const PLACEHOLDER_ROWS = 12/** Offscreen pixels per cell when baking the placeholder bitmap. */const BAKE_PX = 8
function renderPlaceholder(mapId: string, cols: number, rows: number): HTMLCanvasElement { const canvas = document.createElement('canvas') canvas.width = cols * BAKE_PX canvas.height = rows * BAKE_PX const ctx = canvas.getContext('2d') if (ctx) { const hue = hueFromId(mapId) ctx.fillStyle = `hsl(${hue} 35% 22%)` ctx.fillRect(0, 0, canvas.width, canvas.height) ctx.strokeStyle = `hsl(${hue} 45% 45%)` ctx.lineWidth = 2 ctx.strokeRect(1, 1, canvas.width - 2, canvas.height - 2) // a faint diagonal so a placeholder is visibly "not a real map yet" ctx.strokeStyle = `hsl(${hue} 40% 35%)` ctx.lineWidth = 1 ctx.beginPath() ctx.moveTo(0, 0) ctx.lineTo(canvas.width, canvas.height) ctx.stroke() } return canvas}
/** Get a (cached) minimap for a map. Returns null if the id can't be resolved. * Placeholder render until the real minimap feature ships. */export async function getMapMinimap(mapId: string): Promise<MapMinimap | null> { const hit = cache.get(mapId) if (hit) return hit
// TODO(minimap): when a real renderer exists, bake the map's tiles here. const doc = await getMapDoc(mapId) // undefined until map persistence ships const cols = PLACEHOLDER_COLS const rows = PLACEHOLDER_ROWS void doc
const result: MapMinimap = { bitmap: renderPlaceholder(mapId, cols, rows), cols, rows, } cache.set(mapId, result) return result}