@@ -51,6 +51,9 @@ export type GameState = {
5151 donationLog : DonationLogEntry [ ] ;
5252 /** После ×2 с деда на 400 — скрыть таблицу на 3-й доске; сброс при входе на похороны (сервер). */
5353 hideDonationsTableOnBoard3 ?: boolean ;
54+ /** Титры после игры (только квиз-доска 3). */
55+ creditsRollActive ?: boolean ;
56+ creditsRollStartedAt ?: number ;
5457} ;
5558
5659const DEFAULT_PLAYERS : Player [ ] = Array . from ( { length : 5 } , ( _ , i ) => ( {
@@ -86,6 +89,8 @@ const DEFAULT_CORE: Omit<GameState, "themes" | "questions"> = {
8689 boardRoom : undefined ,
8790 donationLog : [ ...DEFAULT_DONATION_LOG ] ,
8891 hideDonationsTableOnBoard3 : false ,
92+ creditsRollActive : false ,
93+ creditsRollStartedAt : undefined ,
8994} ;
9095
9196const PLAYERS_KEY = "adepts-shared-players" ;
@@ -384,6 +389,9 @@ function loadInitialState(boardId: AdeptsBoardId): GameState {
384389 typeof parsed . hideDonationsTableOnBoard3 === "boolean"
385390 ? parsed . hideDonationsTableOnBoard3
386391 : false ,
392+ /** Титры не восстанавливаем из localStorage — только по кнопке «Титры». */
393+ creditsRollActive : false ,
394+ creditsRollStartedAt : undefined ,
387395 } ) ;
388396 }
389397 } catch ( err ) {
@@ -541,6 +549,30 @@ export function useGameState(boardId: AdeptsBoardId) {
541549 const nextHideDonationsTableOnBoard3 =
542550 typeof rawHide === "boolean" ? rawHide : ( prev . hideDonationsTableOnBoard3 ?? false ) ;
543551
552+ const nextCredits =
553+ boardId !== 3
554+ ? { creditsRollActive : false , creditsRollStartedAt : undefined as number | undefined }
555+ : boardMismatch
556+ ? {
557+ /** Relay с другой доски (1/2): титры не переносим; сервер уже сбросил credits. */
558+ creditsRollActive : false ,
559+ creditsRollStartedAt : undefined ,
560+ }
561+ : ( ( ) => {
562+ const active = recToUse [ "creditsRollActive" ] === true ;
563+ const rawAt = recToUse [ "creditsRollStartedAt" ] ;
564+ const at =
565+ typeof rawAt === "number" && Number . isFinite ( rawAt )
566+ ? Math . floor ( rawAt )
567+ : active
568+ ? prev . creditsRollStartedAt
569+ : undefined ;
570+ return {
571+ creditsRollActive : active ,
572+ creditsRollStartedAt : active ? at : undefined ,
573+ } ;
574+ } ) ( ) ;
575+
544576 let nextState = withClosedActiveQuizIfCellUsed (
545577 migrateCatalog ( boardId , {
546578 ...prev ,
@@ -557,6 +589,8 @@ export function useGameState(boardId: AdeptsBoardId) {
557589 dataVersion :
558590 typeof recToUse [ "dataVersion" ] === "number" ? recToUse [ "dataVersion" ] : prev . dataVersion ,
559591 hideDonationsTableOnBoard3 : nextHideDonationsTableOnBoard3 ,
592+ creditsRollActive : nextCredits . creditsRollActive ,
593+ creditsRollStartedAt : nextCredits . creditsRollStartedAt ,
560594 } )
561595 ) ;
562596
@@ -608,7 +642,11 @@ export function useGameState(boardId: AdeptsBoardId) {
608642 } , [ boardId , adeptsSocketKey ] ) ;
609643
610644 useEffect ( ( ) => {
611- localStorage . setItem ( rt . storageKey , JSON . stringify ( state ) ) ;
645+ const persisted : GameState =
646+ boardId === 3
647+ ? { ...state , creditsRollActive : false , creditsRollStartedAt : undefined }
648+ : state ;
649+ localStorage . setItem ( rt . storageKey , JSON . stringify ( persisted ) ) ;
612650 localStorage . setItem ( PLAYERS_KEY , JSON . stringify ( state . players ) ) ;
613651 localStorage . setItem ( DONATION_LOG_KEY , JSON . stringify ( state . donationLog ) ) ;
614652
@@ -626,15 +664,20 @@ export function useGameState(boardId: AdeptsBoardId) {
626664 type : "hostQuizRelay" ,
627665 payload : buildAdeptsQuizRelayPayload ( state , getAdeptsSessionId ( ) , includeCatalog , boardId ) ,
628666 } ) ;
629- } , [ state , catalogReady , rt . storageKey ] ) ;
667+ } , [ state , catalogReady , rt . storageKey , boardId ] ) ;
630668
631669 useEffect ( ( ) => {
632670 const storageKey = rt . storageKey ;
633671 const handler = ( e : StorageEvent ) => {
634672 if ( e . key === storageKey && e . newValue ) {
635673 try {
636674 skipEmitRef . current = true ;
637- setState ( JSON . parse ( e . newValue ) ) ;
675+ const parsed = JSON . parse ( e . newValue ) as GameState ;
676+ if ( boardId === 3 ) {
677+ parsed . creditsRollActive = false ;
678+ parsed . creditsRollStartedAt = undefined ;
679+ }
680+ setState ( parsed ) ;
638681 } catch { }
639682 }
640683 if ( e . key === PLAYERS_KEY && e . newValue ) {
@@ -830,6 +873,18 @@ export function useGameState(boardId: AdeptsBoardId) {
830873 getAdeptsCommandSocket ( ) . emit ( "command" , { type : "playerDonation" , amount } ) ;
831874 } , [ ] ) ;
832875
876+ const setBoard3CreditsRoll = useCallback (
877+ ( active : boolean ) => {
878+ if ( boardId !== 3 || ! isHostRole ( ) ) return ;
879+ setState ( ( prev ) => ( {
880+ ...prev ,
881+ creditsRollActive : active ,
882+ creditsRollStartedAt : active ? Date . now ( ) : undefined ,
883+ } ) ) ;
884+ } ,
885+ [ boardId ] ,
886+ ) ;
887+
833888 const emitPickCell = useCallback (
834889 ( themeIndex : number , questionIndex : number , opts ?: { turnSeat ?: number } ) => {
835890 /**
@@ -869,5 +924,6 @@ export function useGameState(boardId: AdeptsBoardId) {
869924 resetGame,
870925 emitPickCell,
871926 submitPlayerDonation,
927+ setBoard3CreditsRoll,
872928 } ;
873929}
0 commit comments