All files / web/src/app/arcade/complement-race/hooks useTrainTransforms.ts

98.29% Statements 115/117
95.83% Branches 23/24
100% Functions 1/1
98.29% Lines 115/117

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 1181x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 38x 38x 38x 38x 38x 38x 38x 38x 38x 38x 38x 38x 38x 38x 19x 18x 18x 18x 38x 38x 38x 38x 19x 1x 5x 5x 5x 5x 5x 1x 1x 18x 18x 81x 81x 81x 81x 81x 81x 81x 81x 81x 81x 81x 81x 81x 24x 81x 1x 1x 56x 56x 1x 56x     81x 81x 81x 81x 81x 81x 18x 38x 38x 38x 38x 19x 19x 19x 19x 19x 19x 19x 1x 19x 2x 2x 16x 16x 2x 16x 1x 1x 13x 13x 38x 38x 38x 38x 38x 38x 38x 38x  
import { useEffect, useMemo, useState } from 'react'
import type { RailroadTrackGenerator } from '../lib/RailroadTrackGenerator'
 
interface TrainTransform {
  x: number
  y: number
  rotation: number
}
 
interface TrainCarTransform extends TrainTransform {
  position: number
  opacity: number
}
 
interface UseTrainTransformsParams {
  trainPosition: number
  trackGenerator: RailroadTrackGenerator
  pathRef: React.RefObject<SVGPathElement>
  maxCars: number
  carSpacing: number
}
 
export function useTrainTransforms({
  trainPosition,
  trackGenerator,
  pathRef,
  maxCars,
  carSpacing,
}: UseTrainTransformsParams) {
  const [trainTransform, setTrainTransform] = useState<TrainTransform>({
    x: 50,
    y: 300,
    rotation: 0,
  })
 
  // Update train position and rotation
  useEffect(() => {
    if (pathRef.current) {
      const transform = trackGenerator.getTrainTransform(pathRef.current, trainPosition)
      setTrainTransform(transform)
    }
  }, [trainPosition, trackGenerator, pathRef])
 
  // Calculate train car transforms (each car follows behind the locomotive)
  const trainCars = useMemo((): TrainCarTransform[] => {
    if (!pathRef.current) {
      return Array.from({ length: maxCars }, () => ({
        x: 0,
        y: 0,
        rotation: 0,
        position: 0,
        opacity: 0,
      }))
    }
 
    return Array.from({ length: maxCars }).map((_, carIndex) => {
      // Calculate position for this car (behind the locomotive)
      const carPosition = Math.max(0, trainPosition - (carIndex + 1) * carSpacing)
 
      // Calculate opacity: fade in at left tunnel (3-8%), fade out at right tunnel (92-97%)
      const fadeInStart = 3
      const fadeInEnd = 8
      const fadeOutStart = 92
      const fadeOutEnd = 97
 
      let opacity = 1 // Default to fully visible
 
      // Fade in from left tunnel
      if (carPosition <= fadeInStart) {
        opacity = 0
      } else if (carPosition < fadeInEnd) {
        opacity = (carPosition - fadeInStart) / (fadeInEnd - fadeInStart)
      }
      // Fade out into right tunnel
      else if (carPosition >= fadeOutEnd) {
        opacity = 0
      } else if (carPosition > fadeOutStart) {
        opacity = 1 - (carPosition - fadeOutStart) / (fadeOutEnd - fadeOutStart)
      }
 
      return {
        ...trackGenerator.getTrainTransform(pathRef.current!, carPosition),
        position: carPosition,
        opacity,
      }
    })
  }, [trainPosition, trackGenerator, pathRef, maxCars, carSpacing])
 
  // Calculate locomotive opacity (fade in/out through tunnels)
  const locomotiveOpacity = useMemo(() => {
    const fadeInStart = 3
    const fadeInEnd = 8
    const fadeOutStart = 92
    const fadeOutEnd = 97
 
    // Fade in from left tunnel
    if (trainPosition <= fadeInStart) {
      return 0
    } else if (trainPosition < fadeInEnd) {
      return (trainPosition - fadeInStart) / (fadeInEnd - fadeInStart)
    }
    // Fade out into right tunnel
    else if (trainPosition >= fadeOutEnd) {
      return 0
    } else if (trainPosition > fadeOutStart) {
      return 1 - (trainPosition - fadeOutStart) / (fadeOutEnd - fadeOutStart)
    }
 
    return 1 // Default to fully visible
  }, [trainPosition])
 
  return {
    trainTransform,
    trainCars,
    locomotiveOpacity,
  }
}