feat: preserve track and passengers during route transitions
- Store pending track data and only apply when train resets to beginning - Preserve passenger list display until entire train exits - Prevent visual jumps by keeping old route data visible during fade-out - Track transitions now seamless: old track/passengers persist until trainPosition < 0 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e704a28524
commit
f2e71657dc
|
|
@ -142,11 +142,65 @@ export function SteamTrainJourney({ momentum, trainPosition, pressure, elapsedTi
|
||||||
// Get current route theme
|
// Get current route theme
|
||||||
const routeTheme = getRouteTheme(state.currentRoute)
|
const routeTheme = getRouteTheme(state.currentRoute)
|
||||||
|
|
||||||
|
// Track previous route data to maintain visuals during transition
|
||||||
|
const previousRouteRef = useRef(state.currentRoute)
|
||||||
|
const [pendingTrackData, setPendingTrackData] = useState<ReturnType<typeof trackGenerator.generateTrack> | null>(null)
|
||||||
|
|
||||||
|
// Preserve passengers during route transition
|
||||||
|
const [displayPassengers, setDisplayPassengers] = useState(state.passengers)
|
||||||
|
const previousPassengersRef = useRef(state.passengers)
|
||||||
|
|
||||||
// Generate track on mount and when route changes
|
// Generate track on mount and when route changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const track = trackGenerator.generateTrack(state.currentRoute)
|
const track = trackGenerator.generateTrack(state.currentRoute)
|
||||||
setTrackData(track)
|
|
||||||
}, [trackGenerator, state.currentRoute])
|
// If we're in the middle of a route (position > 0), store as pending
|
||||||
|
// Only apply new track when position resets to beginning (< 0)
|
||||||
|
if (state.trainPosition > 0 && previousRouteRef.current !== state.currentRoute) {
|
||||||
|
setPendingTrackData(track)
|
||||||
|
} else {
|
||||||
|
setTrackData(track)
|
||||||
|
previousRouteRef.current = state.currentRoute
|
||||||
|
setPendingTrackData(null)
|
||||||
|
}
|
||||||
|
}, [trackGenerator, state.currentRoute, state.trainPosition])
|
||||||
|
|
||||||
|
// Apply pending track when train resets to beginning
|
||||||
|
useEffect(() => {
|
||||||
|
if (pendingTrackData && state.trainPosition < 0) {
|
||||||
|
setTrackData(pendingTrackData)
|
||||||
|
previousRouteRef.current = state.currentRoute
|
||||||
|
setPendingTrackData(null)
|
||||||
|
}
|
||||||
|
}, [pendingTrackData, state.trainPosition, state.currentRoute])
|
||||||
|
|
||||||
|
// Manage passenger display during route transitions
|
||||||
|
useEffect(() => {
|
||||||
|
// If we're starting a new route (position < 0) or passengers haven't changed, update immediately
|
||||||
|
if (state.trainPosition < 0 || state.passengers === previousPassengersRef.current) {
|
||||||
|
setDisplayPassengers(state.passengers)
|
||||||
|
previousPassengersRef.current = state.passengers
|
||||||
|
}
|
||||||
|
// Otherwise, if we're mid-route and passengers changed, keep showing old passengers
|
||||||
|
else if (state.trainPosition > 0 && state.passengers !== previousPassengersRef.current) {
|
||||||
|
// Keep displaying old passengers until train exits
|
||||||
|
// Don't update displayPassengers yet
|
||||||
|
}
|
||||||
|
|
||||||
|
// When train resets to beginning, switch to new passengers
|
||||||
|
if (state.trainPosition < 0 && state.passengers !== previousPassengersRef.current) {
|
||||||
|
setDisplayPassengers(state.passengers)
|
||||||
|
previousPassengersRef.current = state.passengers
|
||||||
|
}
|
||||||
|
}, [state.passengers, state.trainPosition])
|
||||||
|
|
||||||
|
// Update display passengers during gameplay (same route)
|
||||||
|
useEffect(() => {
|
||||||
|
// Only update if we're in the same route (not transitioning)
|
||||||
|
if (previousRouteRef.current === state.currentRoute && state.trainPosition >= 0 && state.trainPosition < 100) {
|
||||||
|
setDisplayPassengers(state.passengers)
|
||||||
|
}
|
||||||
|
}, [state.passengers, state.currentRoute, state.trainPosition])
|
||||||
|
|
||||||
// Generate ties and rails when path is ready
|
// Generate ties and rails when path is ready
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -372,13 +426,13 @@ export function SteamTrainJourney({ momentum, trainPosition, pressure, elapsedTi
|
||||||
|
|
||||||
// Memoize filtered passenger lists to avoid recalculating on every render
|
// Memoize filtered passenger lists to avoid recalculating on every render
|
||||||
const boardedPassengers = useMemo(() =>
|
const boardedPassengers = useMemo(() =>
|
||||||
state.passengers.filter(p => p.isBoarded && !p.isDelivered),
|
displayPassengers.filter(p => p.isBoarded && !p.isDelivered),
|
||||||
[state.passengers]
|
[displayPassengers]
|
||||||
)
|
)
|
||||||
|
|
||||||
const nonDeliveredPassengers = useMemo(() =>
|
const nonDeliveredPassengers = useMemo(() =>
|
||||||
state.passengers.filter(p => !p.isDelivered),
|
displayPassengers.filter(p => !p.isDelivered),
|
||||||
[state.passengers]
|
[displayPassengers]
|
||||||
)
|
)
|
||||||
|
|
||||||
// Memoize ground texture circles to avoid recreating on every render
|
// Memoize ground texture circles to avoid recreating on every render
|
||||||
|
|
|
||||||
|
|
@ -374,11 +374,11 @@ function gameReducer(state: GameState, action: GameAction): GameState {
|
||||||
...state,
|
...state,
|
||||||
currentRoute: action.routeNumber,
|
currentRoute: action.routeNumber,
|
||||||
stations: action.stations,
|
stations: action.stations,
|
||||||
trainPosition: 0,
|
trainPosition: -5, // Start off-screen to the left for smooth fade-in
|
||||||
deliveredPassengers: 0,
|
deliveredPassengers: 0,
|
||||||
showRouteCelebration: false,
|
showRouteCelebration: false,
|
||||||
momentum: 0,
|
momentum: 50, // Give some starting momentum for the new route
|
||||||
pressure: 0
|
pressure: 50
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'COMPLETE_ROUTE':
|
case 'COMPLETE_ROUTE':
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue