diff --git a/apps/web/src/app/games/complement-race/components/RaceTrack/RailroadTrackPath.tsx b/apps/web/src/app/games/complement-race/components/RaceTrack/RailroadTrackPath.tsx new file mode 100644 index 00000000..1dcef3df --- /dev/null +++ b/apps/web/src/app/games/complement-race/components/RaceTrack/RailroadTrackPath.tsx @@ -0,0 +1,197 @@ +'use client' + +import { memo } from 'react' +import type { Station, Passenger } from '../../lib/gameTypes' +import type { Landmark } from '../../lib/landmarks' + +interface RailroadTrackPathProps { + tiesAndRails: { + ties: Array<{ x1: number; y1: number; x2: number; y2: number }> + leftRailPoints: string[] + rightRailPoints: string[] + } | null + referencePath: string + pathRef: React.RefObject + landmarkPositions: Array<{ x: number; y: number }> + landmarks: Landmark[] + stationPositions: Array<{ x: number; y: number }> + stations: Station[] + passengers: Passenger[] + boardingAnimations: Map + disembarkingAnimations: Map +} + +export const RailroadTrackPath = memo(({ + tiesAndRails, + referencePath, + pathRef, + landmarkPositions, + landmarks, + stationPositions, + stations, + passengers, + boardingAnimations, + disembarkingAnimations +}: RailroadTrackPathProps) => { + return ( + <> + {/* Railroad ties */} + {tiesAndRails?.ties.map((tie, index) => ( + + ))} + + {/* Left rail */} + {tiesAndRails && tiesAndRails.leftRailPoints.length > 1 && ( + + )} + + {/* Right rail */} + {tiesAndRails && tiesAndRails.rightRailPoints.length > 1 && ( + + )} + + {/* Reference path (invisible, used for positioning) */} + + + {/* Landmarks - background scenery */} + {landmarkPositions.map((pos, index) => ( + + {landmarks[index]?.emoji} + + ))} + + {/* Station markers */} + {stationPositions.map((pos, index) => { + const station = stations[index] + // Find passengers waiting at this station (exclude currently boarding) + const waitingPassengers = passengers.filter(p => + p.originStationId === station?.id && !p.isBoarded && !p.isDelivered && !boardingAnimations.has(p.id) + ) + // Find passengers delivered at this station (exclude currently disembarking) + const deliveredPassengers = passengers.filter(p => + p.destinationStationId === station?.id && p.isDelivered && !disembarkingAnimations.has(p.id) + ) + + return ( + + {/* Station platform */} + + {/* Station icon */} + + {station?.icon} + + {/* Station name */} + + {station?.name} + + + {/* Waiting passengers at this station */} + {waitingPassengers.map((passenger, pIndex) => ( + + {passenger.avatar} + + ))} + + {/* Delivered passengers at this station (celebrating) */} + {deliveredPassengers.map((passenger, pIndex) => ( + + {passenger.avatar} + + ))} + + ) + })} + + ) +}) + +RailroadTrackPath.displayName = 'RailroadTrackPath' diff --git a/apps/web/src/app/games/complement-race/components/RaceTrack/SteamTrainJourney.tsx b/apps/web/src/app/games/complement-race/components/RaceTrack/SteamTrainJourney.tsx index 2abd7553..7188f79c 100644 --- a/apps/web/src/app/games/complement-race/components/RaceTrack/SteamTrainJourney.tsx +++ b/apps/web/src/app/games/complement-race/components/RaceTrack/SteamTrainJourney.tsx @@ -14,6 +14,7 @@ import { PressureGauge } from '../PressureGauge' import { useGameMode } from '@/contexts/GameModeContext' import { useUserProfile } from '@/contexts/UserProfileContext' import { TrainTerrainBackground } from './TrainTerrainBackground' +import { RailroadTrackPath } from './RailroadTrackPath' const BoardingPassengerAnimation = memo(({ animation }: { animation: BoardingAnimation }) => { const spring = useSpring({ @@ -248,162 +249,20 @@ export function SteamTrainJourney({ momentum, trainPosition, pressure, elapsedTi groundTextureCircles={groundTextureCircles} /> - {/* Railroad ties */} - {tiesAndRails?.ties.map((tie, index) => ( - - ))} - - {/* Left rail */} - {tiesAndRails && tiesAndRails.leftRailPoints.length > 1 && ( - - )} - - {/* Right rail */} - {tiesAndRails && tiesAndRails.rightRailPoints.length > 1 && ( - - )} - - {/* Reference path (invisible, used for positioning) */} - - {/* Landmarks - background scenery */} - {landmarkPositions.map((pos, index) => ( - - {landmarks[index]?.emoji} - - ))} - - {/* Station markers */} - {stationPositions.map((pos, index) => { - const station = state.stations[index] - // Find passengers waiting at this station (exclude currently boarding) - const waitingPassengers = state.passengers.filter(p => - p.originStationId === station?.id && !p.isBoarded && !p.isDelivered && !boardingAnimations.has(p.id) - ) - // Find passengers delivered at this station (exclude currently disembarking) - const deliveredPassengers = state.passengers.filter(p => - p.destinationStationId === station?.id && p.isDelivered && !disembarkingAnimations.has(p.id) - ) - - return ( - - {/* Station platform */} - - {/* Station icon */} - - {station?.icon} - - {/* Station name */} - - {station?.name} - - - {/* Waiting passengers at this station */} - {waitingPassengers.map((passenger, pIndex) => ( - - {passenger.avatar} - - ))} - - {/* Delivered passengers at this station (celebrating) */} - {deliveredPassengers.map((passenger, pIndex) => ( - - {passenger.avatar} - - ))} - - ) - })} - {/* Boarding animations - passengers moving from station to train car */} {Array.from(boardingAnimations.values()).map(animation => (