import { useState, useRef, useCallback } from 'react' import { useLayoutEffect, useEffect } from 'react' import { useNext, useGotoSummary, type Log } from '@remake/hooks' import { useJudge } from '@/hooks/judge' import { achievements, events, talents } from '@remake/data' import { properties } from '@/display' import { AutoInterval } from '@/config' import { format } from '@remake/vitex' import { toastAchvs } from '@/toast/Achv' import './Play.css' function LogTalent({ id }: { id: number }) { const { name, description, grade } = talents.get(id)! return (
  • [天赋] {name} {description}
  • ) } function LogTalents({ items }: { items: number[] }) { if (items.length === 0) return null const els = items.map(id => ) return } const year = new Date().getFullYear() interface LogEventProps { id: number post: boolean index: number } function LogEvent({ id, post, index }: LogEventProps) { let { event, postEvent, grade, format: f } = events.get(id)! if (f) { const g = (key: string) => ({ CurrentYear: year + index })[key] event = format(event, g) if (post && postEvent) postEvent = format(postEvent, g) } return ( <>
  • {event}
  • {post && postEvent && (
  • {postEvent}
  • )} ) } function LogEvents({ items, index }: { items: number[]; index: number }) { const last = items.length - 1 const els = items.map((id, i) => ( )) return } function LogAchievement({ id }: { id: number }) { const { name, description, grade } = achievements.get(id)! return (
  • [成就] {name} {description}
  • ) } function LogAchievements({ items }: { items: number[] }) { if (items.length === 0) return null const els = items.map(id => ) return
      {els}
    } function Log({ log, index }: { log: Log; index: number }) { return (
  • {log.age}岁
  • ) } interface PropProps { prop: keyof typeof properties value: number grade: number } function Prop({ prop, value, grade }: PropProps) { const prevRef = useRef(value) const [trend, setTrend] = useState<'up' | 'down' | 'normal'>('normal') const [flip, setFlip] = useState(0) const [displayValue, setDisplayValue] = useState(value) useEffect(() => { const prev = prevRef.current if (value === prev) return const startValue = prevRef.current prevRef.current = value setTrend(value > prev ? 'up' : 'down') setFlip(f => (f + 1) % 2) let startTimestamp: number | null = null const duration = 400 let end = false const step = (timestamp: number) => { if (!startTimestamp) startTimestamp = timestamp const progress = timestamp - startTimestamp const progressRatio = Math.min(progress / duration, 1) const easeOutQuad = progressRatio * (2 - progressRatio) const currentDec = startValue + (value - startValue) * easeOutQuad setDisplayValue(Math.round(currentDec)) if (!end && progress < duration) requestAnimationFrame(step) } requestAnimationFrame(step) const timer = setTimeout(() => setTrend('normal'), 2000) return () => { clearTimeout(timer) setDisplayValue(value) end = true } }, [value]) return (
  • {properties[prop]} {displayValue}
  • ) } function Properties() { const judges = useJudge() return (
      {judges.map(([key, { value, grade }]) => ( ))}
    ) } export function Play() { const [{ logs, ended }, next] = useNext() const [auto, setAuto] = useState(false) const logRef = useRef(null) const autoRef = useRef(0) const gotoSummary = useGotoSummary() const handleNext = useCallback(() => { if (ended) return const achievements = next() toastAchvs(achievements) }, [ended, next]) const handleGotoSummary = useCallback(() => { if (!ended) return const achievements = gotoSummary() toastAchvs(achievements) }, [ended, gotoSummary]) useLayoutEffect(() => { requestAnimationFrame(() => { if (!logRef.current) return logRef.current.scrollTop = logRef.current.scrollHeight }) }, [logs]) useEffect(() => { if (!auto) window.clearInterval(autoRef.current) else autoRef.current = window.setInterval(handleNext, AutoInterval) return () => window.clearInterval(autoRef.current) }, [auto, handleNext]) return (
      {logs.map((log, index) => ( ))}
    {!ended && ( )} {ended && ( )}
    ) } export default Play