All files / web/src/arcade-games/know-your-world mapColors.ts

0% Statements 0/133
0% Branches 0/1
0% Functions 0/1
0% Lines 0/133

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134                                                                                                                                                                                                                                                                           
/**
 * Map coloring utilities for Know Your World game
 * Provides distinct colors for map regions to improve visual clarity
 */

// Color palette: 8 distinct, visually appealing colors
// These colors are chosen to be distinguishable and work well at different opacities
export const REGION_COLOR_PALETTE = [
  { name: 'blue', base: '#3b82f6', light: '#93c5fd', dark: '#1e40af' },
  { name: 'green', base: '#10b981', light: '#6ee7b7', dark: '#047857' },
  { name: 'purple', base: '#8b5cf6', light: '#c4b5fd', dark: '#6d28d9' },
  { name: 'orange', base: '#f97316', light: '#fdba74', dark: '#c2410c' },
  { name: 'pink', base: '#ec4899', light: '#f9a8d4', dark: '#be185d' },
  { name: 'yellow', base: '#eab308', light: '#fde047', dark: '#a16207' },
  { name: 'teal', base: '#14b8a6', light: '#5eead4', dark: '#0f766e' },
  { name: 'red', base: '#ef4444', light: '#fca5a5', dark: '#b91c1c' },
] as const

/**
 * Hash function to deterministically assign a color to a region based on its ID
 * This ensures the same region always gets the same color across sessions
 */
function hashString(str: string): number {
  let hash = 0
  for (let i = 0; i < str.length; i++) {
    const char = str.charCodeAt(i)
    hash = (hash << 5) - hash + char
    hash = hash & hash // Convert to 32bit integer
  }
  return Math.abs(hash)
}

/**
 * Get color index for a region ID
 */
export function getRegionColorIndex(regionId: string): number {
  return hashString(regionId) % REGION_COLOR_PALETTE.length
}

/**
 * Get color for a region based on its state
 */
export function getRegionColor(
  regionId: string,
  isFound: boolean,
  isHovered: boolean,
  isDark: boolean
): string {
  const colorIndex = getRegionColorIndex(regionId)
  const color = REGION_COLOR_PALETTE[colorIndex]

  if (isFound) {
    // Found: use base color with full opacity
    return color.base
  } else if (isHovered) {
    // Hovered: use light color with good opacity for clear feedback
    return isDark
      ? `${color.light}99` // 60% opacity in dark mode
      : `${color.base}77` // 47% opacity in light mode
  } else {
    // Not found: use earth-tone colors for land masses
    // Higher opacity and warmer colors distinguish land from sea
    return isDark
      ? '#4a5568' // Warm gray in dark mode - clearly land
      : '#d4c4a8' // Sandy/earthy beige in light mode - clearly land
  }
}

/**
 * Get stroke (border) color for a region
 */
export function getRegionStroke(isFound: boolean, isDark: boolean): string {
  if (isFound) {
    return isDark ? '#ffffff' : '#000000' // High contrast for found regions
  }
  // More visible borders for unfound regions - darker to contrast with land colors
  return isDark ? '#2d3748' : '#8b7355' // Dark gray / brown border
}

/**
 * Get stroke width for a region
 */
export function getRegionStrokeWidth(isHovered: boolean, isFound: boolean): number {
  if (isHovered) return 3
  if (isFound) return 2
  return 0.5
}

/**
 * Get text color for label based on background
 * Uses high contrast to ensure readability
 */
export function getLabelTextColor(isDark: boolean, isFound: boolean): string {
  if (isFound) {
    // For found regions with colored backgrounds, use white text
    return '#ffffff'
  }
  // For unfound regions, use standard text color
  return isDark ? '#e5e7eb' : '#1f2937'
}

/**
 * Get text shadow for label to ensure visibility
 * Creates a strong outline effect
 */
export function getLabelTextShadow(isDark: boolean, isFound: boolean): string {
  if (isFound) {
    // Strong shadow for found regions (white text on colored background)
    return `
      0 0 3px rgba(0,0,0,0.9),
      0 0 6px rgba(0,0,0,0.7),
      1px 1px 0 rgba(0,0,0,0.8),
      -1px -1px 0 rgba(0,0,0,0.8),
      1px -1px 0 rgba(0,0,0,0.8),
      -1px 1px 0 rgba(0,0,0,0.8)
    `
  }

  // Subtle shadow for unfound regions
  return isDark
    ? `
      0 0 3px rgba(0,0,0,0.8),
      0 0 6px rgba(0,0,0,0.5),
      1px 1px 0 rgba(0,0,0,0.6),
      -1px -1px 0 rgba(0,0,0,0.6)
    `
    : `
      0 0 3px rgba(255,255,255,0.9),
      0 0 6px rgba(255,255,255,0.7),
      1px 1px 0 rgba(255,255,255,0.8),
      -1px -1px 0 rgba(255,255,255,0.8)
    `
}