/** * Palette of distinguishable colors for character markers (readable on typical map tiles). * Same character id always maps to the same color via id % palette.length. */ const CHARACTER_PALETTE_FILL: string[] = [ '#0d9488', // teal '#dc2626', // red '#2563eb', // blue '#16a34a', // green '#ea580c', // orange '#7c3aed', // violet '#db2777', // pink '#ca8a04', // yellow/amber '#0891b2', // cyan '#65a30d', // lime '#4f46e5', // indigo '#b45309', // brown/amber-dark ] /** Darken a hex color by a factor (0–1). Used for stroke. */ function darkenHex(hex: string, factor: number): string { const n = hex.slice(1) const r = Math.max(0, parseInt(n.slice(0, 2), 16) * (1 - factor)) const g = Math.max(0, parseInt(n.slice(2, 4), 16) * (1 - factor)) const b = Math.max(0, parseInt(n.slice(4, 6), 16) * (1 - factor)) return '#' + [r, g, b].map((x) => Math.round(x).toString(16).padStart(2, '0')).join('') } export interface CharacterColors { fill: string stroke: string } /** * Returns stable fill and stroke colors for a character by id. * Optionally use a distinct style for "owned by me" (e.g. brighter stroke). */ export function getColorForCharacterId( id: number, options?: { ownedByMe?: boolean } ): CharacterColors { const fill = CHARACTER_PALETTE_FILL[Math.abs(id) % CHARACTER_PALETTE_FILL.length] const stroke = darkenHex(fill, 0.25) if (options?.ownedByMe) { return { fill, stroke: '#1e293b' } } return { fill, stroke } }