import React, { useState, useEffect, useRef } from 'react'; import { Clock, AlertTriangle, Zap, GitBranch, GitMerge, Play, Pause, RotateCcw, ChevronRight } from 'lucide-react'; export default function TimelineWeaver() { // Game state const [gameState, setGameState] = useState({ level: 1, playing: false, timelines: [], events: [], paradoxes: [], timeSpeed: 1, tutorial: true, tutorialStep: 0, completed: false, selectedTimeline: null, selectedEvent: null, showParallelUniverses: false, }); const canvasRef = useRef(null); const animationRef = useRef(null); // Initialize game useEffect(() => { initializeLevel(gameState.level); return () => { if (animationRef.current) { cancelAnimationFrame(animationRef.current); } }; }, [gameState.level]); // Initialize a level with timelines, events, and paradoxes const initializeLevel = (level) => { let timelines = []; let events = []; let paradoxes = []; // Level 1: Simple introduction with two timelines if (level === 1) { timelines = [ { id: 1, name: "Alpha", color: "#ffd700", points: generateTimelinePoints(0), active: true }, { id: 2, name: "Beta", color: "#9333ea", points: generateTimelinePoints(1), active: true }, ]; events = [ { id: 1, timelineId: 1, position: 0.2, name: "Invention", resolved: false, connectedTo: null }, { id: 2, timelineId: 2, position: 0.5, name: "Discovery", resolved: false, connectedTo: null }, { id: 3, timelineId: 1, position: 0.7, name: "Paradox Event", resolved: false, connectedTo: null }, ]; paradoxes = [ { id: 1, description: "Invention occurs without prior discovery", eventIds: [1, 2], resolved: false } ]; } else if (level === 2) { // More complex level with three timelines timelines = [ { id: 1, name: "Alpha", color: "#ffd700", points: generateTimelinePoints(0), active: true }, { id: 2, name: "Beta", color: "#9333ea", points: generateTimelinePoints(1), active: true }, { id: 3, name: "Gamma", color: "#0d9488", points: generateTimelinePoints(2), active: true }, ]; events = [ { id: 1, timelineId: 1, position: 0.2, name: "Origin Event", resolved: false, connectedTo: null }, { id: 2, timelineId: 2, position: 0.3, name: "Consequence A", resolved: false, connectedTo: null }, { id: 3, timelineId: 3, position: 0.4, name: "Consequence B", resolved: false, connectedTo: null }, { id: 4, timelineId: 1, position: 0.6, name: "Future Event", resolved: false, connectedTo: null }, { id: 5, timelineId: 2, position: 0.7, name: "Paradox Event", resolved: false, connectedTo: null }, ]; paradoxes = [ { id: 1, description: "Future event occurs before its cause", eventIds: [2, 4], resolved: false }, { id: 2, description: "Consequence B lacks causal origin", eventIds: [1, 3], resolved: false } ]; } setGameState(prev => ({ ...prev, timelines, events, paradoxes, playing: false, completed: false, selectedTimeline: null, selectedEvent: null, })); }; // Generate points for timeline visualization const generateTimelinePoints = (index) => { const baseY = 100 + index * 120; const points = []; for (let i = 0; i <= 100; i++) { const x = i * 10; // Add some gentle waves to make timelines look more interesting const y = baseY + Math.sin(i * 0.05) * 10; points.push({ x, y }); } return points; }; // Handle timeline selection const selectTimeline = (id) => { setGameState(prev => ({ ...prev, selectedTimeline: prev.selectedTimeline === id ? null : id, selectedEvent: null })); }; // Handle event selection const selectEvent = (id) => { setGameState(prev => ({ ...prev, selectedEvent: prev.selectedEvent === id ? null : id })); }; // Connect events to resolve paradoxes const connectEvents = (sourceId, targetId) => { if (sourceId === targetId) return; setGameState(prev => { const updatedEvents = prev.events.map(event => { if (event.id === sourceId) { return { ...event, connectedTo: targetId }; } return event; }); // Check if this connection resolves any paradoxes const updatedParadoxes = prev.paradoxes.map(paradox => { const { eventIds } = paradox; if (eventIds.includes(sourceId) && eventIds.includes(targetId)) { return { ...paradox, resolved: true }; } return paradox; }); // Check if all paradoxes are resolved const allResolved = updatedParadoxes.every(p => p.resolved); return { ...prev, events: updatedEvents, paradoxes: updatedParadoxes, completed: allResolved, selectedEvent: null }; }); }; // Handle connecting selected event to another const handleEventConnection = (targetId) => { if (gameState.selectedEvent) { connectEvents(gameState.selectedEvent, targetId); } }; // Toggle play/pause const togglePlay = () => { setGameState(prev => ({ ...prev, playing: !prev.playing })); }; // Reset level const resetLevel = () => { initializeLevel(gameState.level); }; // Advance to next level const nextLevel = () => { setGameState(prev => ({ ...prev, level: prev.level + 1, tutorial: prev.level === 1, // Show tutorial only for level 1 tutorialStep: 0 })); }; // Toggle parallel universe view const toggleParallelUniverses = () => { setGameState(prev => ({ ...prev, showParallelUniverses: !prev.showParallelUniverses })); }; // Advance tutorial const advanceTutorial = () => { setGameState(prev => ({ ...prev, tutorialStep: prev.tutorialStep + 1, tutorial: prev.tutorialStep < 3 // End tutorial after step 3 })); }; // Tutorial content const getTutorialContent = () => { switch (gameState.tutorialStep) { case 0: return "Welcome to Timeline Weaver! Your goal is to resolve temporal paradoxes by connecting events across timelines."; case 1: return "Click on an event to select it, then click on another event to create a causal connection between them."; case 2: return "Paradoxes are shown in the right panel. Connect the related events to resolve them."; case 3: return "Complete all paradox resolutions to finish the level and advance to the next challenge!"; default: return ""; } }; return (
{/* Header */}

Timeline Weaver

Level {gameState.level}
{/* Main game area */}
{/* Timeline view */}
{gameState.timelines.map((timeline) => (
selectTimeline(timeline.id)} style={{ transform: 'translateY(-50%)' }} > {timeline.name} Timeline
{/* Timeline visualization */}
`${p.x},${p.y}`).join(' L ')}`} fill="none" stroke={timeline.color} strokeWidth="3" strokeLinecap="round" className="drop-shadow-[0_0_8px_rgba(255,215,0,0.5)]" /> {/* Events on this timeline */} {gameState.events .filter(event => event.timelineId === timeline.id) .map(event => { const position = timeline.points[Math.floor(event.position * timeline.points.length)]; return (
{ if (gameState.selectedEvent && gameState.selectedEvent !== event.id) { handleEventConnection(event.id); } else { selectEvent(event.id); } }} >
{event.id}
); })}
))} {/* Connection lines between events */} {gameState.events .filter(event => event.connectedTo) .map(event => { const sourceEvent = event; const targetEvent = gameState.events.find(e => e.id === event.connectedTo); if (!targetEvent) return null; const sourceTimeline = gameState.timelines.find(t => t.id === sourceEvent.timelineId); const targetTimeline = gameState.timelines.find(t => t.id === targetEvent.timelineId); const sourcePosition = sourceTimeline.points[Math.floor(sourceEvent.position * sourceTimeline.points.length)]; const targetPosition = targetTimeline.points[Math.floor(targetEvent.position * targetTimeline.points.length)]; return ( ); })}
{/* Right sidebar */}
{/* Event details */}

Events

{gameState.events.map(event => (
selectEvent(event.id)} >
{event.name}
Timeline: {gameState.timelines.find(t => t.id === event.timelineId)?.name}
{event.connectedTo && (
Connected to Event {event.connectedTo}
)}
))}
{/* Paradoxes */}

Paradoxes

{gameState.paradoxes.map(paradox => (
{paradox.resolved ? '✓ Resolved' : '⚠ Active Paradox'}
{paradox.description}
Events: {paradox.eventIds.join(', ')}
))}
{/* Tools */}

Tools

{/* Bottom controls */}
{gameState.playing ? "Time is flowing..." : "Time is paused"}
{gameState.selectedEvent && (
Event {gameState.selectedEvent} selected - Click another event to connect
)} {gameState.completed && ( )}
{/* Tutorial overlay */} {gameState.tutorial && (

Tutorial

{getTutorialContent()}

)}
); }