diff --git a/lib/src/features/game/widgets/victory_overlay.dart b/lib/src/features/game/widgets/victory_overlay.dart index 6b2e057..75686b3 100644 --- a/lib/src/features/game/widgets/victory_overlay.dart +++ b/lib/src/features/game/widgets/victory_overlay.dart @@ -8,6 +8,8 @@ import 'package:asciineverdie/src/shared/retro_colors.dart'; /// 게임 클리어 엔딩 오버레이 (Act V 완료 시) /// /// 영화 엔딩 크레딧 스타일로 텍스트가 아래에서 위로 스크롤됨 +/// - 탭/클릭 시 스크롤 최하단으로 즉시 이동 +/// - 최하단에 명예의 전당 버튼 표시 class VictoryOverlay extends StatefulWidget { const VictoryOverlay({ super.key, @@ -35,6 +37,9 @@ class _VictoryOverlayState extends State late AnimationController _scrollController; late Animation _scrollAnimation; + // 스크롤이 완료(최하단 도달) 되었는지 여부 + bool _isScrollComplete = false; + // 스크롤 지속 시간 (밀리초) static const _scrollDurationMs = 25000; // 25초 @@ -51,10 +56,12 @@ class _VictoryOverlayState extends State CurvedAnimation(parent: _scrollController, curve: Curves.linear), ); - // 스크롤 완료 시 자동 종료 + // 스크롤 완료 시 버튼 표시 (자동 종료하지 않음) _scrollController.addStatusListener((status) { if (status == AnimationStatus.completed) { - widget.onComplete(); + setState(() { + _isScrollComplete = true; + }); } }); @@ -67,12 +74,21 @@ class _VictoryOverlayState extends State super.dispose(); } + /// 탭 시 스크롤 최하단으로 즉시 이동 + void _skipToEnd() { + _scrollController.stop(); + _scrollController.value = 1.0; + setState(() { + _isScrollComplete = true; + }); + } + @override Widget build(BuildContext context) { final gold = RetroColors.goldOf(context); return GestureDetector( - onTap: widget.onComplete, // 탭으로 스킵 + onTap: _isScrollComplete ? null : _skipToEnd, // 스크롤 중에만 탭으로 스킵 child: Material( color: Colors.black, child: SafeArea( @@ -86,38 +102,40 @@ class _VictoryOverlayState extends State }, ), - // 스킵 버튼 - Positioned( - top: 16, - right: 16, - child: TextButton( - onPressed: widget.onComplete, - child: Text( - L10n.of(context).endingSkip, - style: TextStyle( - fontFamily: 'PressStart2P', - fontSize: 10, - color: gold.withValues(alpha: 0.5), + // 스킵 버튼 (스크롤 중에만 표시) + if (!_isScrollComplete) + Positioned( + top: 16, + right: 16, + child: TextButton( + onPressed: _skipToEnd, + child: Text( + L10n.of(context).endingSkip, + style: TextStyle( + fontFamily: 'PressStart2P', + fontSize: 10, + color: gold.withValues(alpha: 0.5), + ), ), ), ), - ), - // 하단 탭 힌트 - Positioned( - bottom: 16, - left: 0, - right: 0, - child: Text( - L10n.of(context).endingTapToSkip, - style: TextStyle( - fontFamily: 'PressStart2P', - fontSize: 8, - color: Colors.white.withValues(alpha: 0.2), + // 하단 탭 힌트 (스크롤 중에만 표시) + if (!_isScrollComplete) + Positioned( + bottom: 16, + left: 0, + right: 0, + child: Text( + L10n.of(context).endingTapToSkip, + style: TextStyle( + fontFamily: 'PressStart2P', + fontSize: 8, + color: Colors.white.withValues(alpha: 0.2), + ), + textAlign: TextAlign.center, ), - textAlign: TextAlign.center, ), - ), ], ), ), @@ -145,7 +163,8 @@ class _VictoryOverlayState extends State double _estimateContentHeight() { // 대략적인 콘텐츠 높이 추정 (스크롤 계산용) - return 1500.0; + // 명예의 전당 버튼 추가로 인해 높이 증가 + return 1600.0; } Widget _buildCreditContent(BuildContext context) { @@ -231,7 +250,13 @@ class _VictoryOverlayState extends State // THE END // ═══════════════════════════════════ _buildTheEnd(context, gold), - const SizedBox(height: 200), // 여백 (스크롤 끝) + const SizedBox(height: 60), + + // ═══════════════════════════════════ + // HALL OF FAME BUTTON + // ═══════════════════════════════════ + _buildHallOfFameButton(context, gold), + const SizedBox(height: 100), // 여백 (스크롤 끝) ], ), ), @@ -518,7 +543,6 @@ class _VictoryOverlayState extends State } Widget _buildTheEnd(BuildContext context, Color gold) { - final l10n = L10n.of(context); const theEnd = ''' ████████╗██╗ ██╗███████╗ ███████╗███╗ ██╗██████╗ ╚══██╔══╝██║ ██║██╔════╝ ██╔════╝████╗ ██║██╔══██╗ @@ -539,7 +563,17 @@ class _VictoryOverlayState extends State ), textAlign: TextAlign.center, ), - const SizedBox(height: 24), + ], + ); + } + + /// 명예의 전당 버튼 (최하단) + Widget _buildHallOfFameButton(BuildContext context, Color gold) { + final l10n = L10n.of(context); + + return Column( + children: [ + // 안내 텍스트 Text( l10n.endingHallOfFameLine1, style: TextStyle( @@ -557,6 +591,38 @@ class _VictoryOverlayState extends State color: gold.withValues(alpha: 0.7), ), ), + const SizedBox(height: 24), + + // 명예의 전당 버튼 + SizedBox( + width: 280, + height: 56, + child: ElevatedButton( + onPressed: widget.onComplete, + style: ElevatedButton.styleFrom( + backgroundColor: gold, + foregroundColor: Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + elevation: 8, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.emoji_events, size: 24), + const SizedBox(width: 12), + Text( + l10n.endingHallOfFameButton, + style: const TextStyle( + fontFamily: 'PressStart2P', + fontSize: 10, + ), + ), + ], + ), + ), + ), ], ); }