import type L from 'leaflet' import { HnHMaxZoom } from '~/lib/LeafletCustomTypes' export type LeafletApi = typeof import('leaflet') /** SVG data URL for character marker icon (teal pin, bottom-center anchor). */ const CHARACTER_ICON_URL = 'data:image/svg+xml,' + encodeURIComponent( '' + '' + '' + '' ) function createCharacterIcon(L: LeafletApi): L.Icon { return new L.Icon({ iconUrl: CHARACTER_ICON_URL, iconSize: [24, 32], iconAnchor: [12, 32], popupAnchor: [0, -32], }) } export interface CharacterData { name: string position: { x: number; y: number } type: string id: number map: number } export interface CharacterMapViewRef { map: L.Map mapid: number markerLayer?: L.LayerGroup } export interface MapCharacter { id: number name: string position: { x: number; y: number } type: string map: number text: string value: number leafletMarker: L.Marker | null remove: (mapview: CharacterMapViewRef) => void add: (mapview: CharacterMapViewRef) => void update: (mapview: CharacterMapViewRef, updated: CharacterData | MapCharacter) => void setClickCallback: (callback: (e: L.LeafletMouseEvent) => void) => void } export function createCharacter(data: CharacterData, L: LeafletApi): MapCharacter { let leafletMarker: L.Marker | null = null let onClick: ((e: L.LeafletMouseEvent) => void) | null = null const characterIcon = createCharacterIcon(L) const character: MapCharacter = { id: data.id, name: data.name, position: { ...data.position }, type: data.type, map: data.map, text: data.name, value: data.id, get leafletMarker() { return leafletMarker }, remove(mapview: CharacterMapViewRef): void { if (leafletMarker) { const layer = mapview.markerLayer ?? mapview.map layer.removeLayer(leafletMarker) leafletMarker = null } }, add(mapview: CharacterMapViewRef): void { if (character.map === mapview.mapid) { const position = mapview.map.unproject([character.position.x, character.position.y], HnHMaxZoom) leafletMarker = L.marker(position, { icon: characterIcon, title: character.name }) leafletMarker.on('click', (e: L.LeafletMouseEvent) => { if (onClick) onClick(e) }) const targetLayer = mapview.markerLayer ?? mapview.map leafletMarker.addTo(targetLayer) } }, update(mapview: CharacterMapViewRef, updated: CharacterData | MapCharacter): void { if (character.map !== updated.map) { character.remove(mapview) } character.map = updated.map character.position = { ...updated.position } if (!leafletMarker && character.map === mapview.mapid) { character.add(mapview) } if (leafletMarker) { const position = mapview.map.unproject([updated.position.x, updated.position.y], HnHMaxZoom) leafletMarker.setLatLng(position) } }, setClickCallback(callback: (e: L.LeafletMouseEvent) => void): void { onClick = callback }, } return character }