import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:askiineverdie/data/race_data.dart'; import 'package:askiineverdie/src/core/animation/front_screen_animation.dart'; /// 프론트 화면용 Hero vs Glitch God ASCII 애니메이션 위젯 /// /// 작은 용사가 거대한 Glitch God에 맞서는 루프 애니메이션 /// RichText로 문자별 색상 적용, 랜덤 종족 변경 지원 class HeroVsBossAnimation extends StatefulWidget { const HeroVsBossAnimation({super.key}); @override State createState() => _HeroVsBossAnimationState(); } class _HeroVsBossAnimationState extends State { int _currentFrame = 0; Timer? _animationTimer; Timer? _raceChangeTimer; final Random _random = Random(); // 현재 종족 (랜덤 변경) String _currentRaceId = 'byte_human'; int _raceIndex = 0; @override void initState() { super.initState(); _startAnimation(); _startRaceChangeTimer(); } @override void dispose() { _animationTimer?.cancel(); _raceChangeTimer?.cancel(); super.dispose(); } void _startAnimation() { _animationTimer = Timer.periodic( const Duration(milliseconds: frontScreenAnimationIntervalMs), (_) { if (!mounted) return; // 프레임 빌드 중이면 다음 프레임까지 대기 if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) { SchedulerBinding.instance.addPostFrameCallback((_) { if (mounted) { setState(() { _currentFrame = (_currentFrame + 1) % frontScreenAnimationFrameCount; }); } }); } else { setState(() { _currentFrame = (_currentFrame + 1) % frontScreenAnimationFrameCount; }); } }, ); } /// 8초마다 랜덤 종족 변경 void _startRaceChangeTimer() { _raceChangeTimer = Timer.periodic( const Duration(seconds: 8), (_) => _changeRace(), ); } void _changeRace() { if (!mounted) return; final allRaces = RaceData.all; if (allRaces.isEmpty) return; // 프레임 빌드 중이면 다음 프레임까지 대기 if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) { SchedulerBinding.instance.addPostFrameCallback((_) { if (mounted) { setState(() { _raceIndex = (_raceIndex + 1) % allRaces.length; _currentRaceId = allRaces[_raceIndex].raceId; }); } }); } else { setState(() { _raceIndex = (_raceIndex + 1) % allRaces.length; _currentRaceId = allRaces[_raceIndex].raceId; }); } } /// 글리치 효과: 랜덤 문자 대체 String _applyGlitchEffect(String frame) { // 10% 확률로 글리치 효과 적용 if (_random.nextDouble() > 0.1) return frame; const glitchChars = '@#\$%&*!?~'; final chars = frame.split(''); final glitchCount = _random.nextInt(5) + 1; for (var i = 0; i < glitchCount; i++) { final pos = _random.nextInt(chars.length); if (chars[pos] != ' ' && chars[pos] != '\n') { chars[pos] = glitchChars[_random.nextInt(glitchChars.length)]; } } return chars.join(); } /// 문자별 색상 결정 Color _getCharColor(String char) { // 공격/이펙트 (시안) if ('><=!+'.contains(char)) return Colors.cyan; // 데미지/글리치 (마젠타) if ('*~@#\$%&'.contains(char)) return const Color(0xFFFF00FF); // 보스 눈 (빨강) if ('◈◉X'.contains(char)) return Colors.red; // 보스 타이틀 텍스트 (시안) if ('GLITCH'.contains(char) || 'GOD'.contains(char)) return Colors.cyan; // 기본 (흰색) return Colors.white; } /// 문자열을 색상별 TextSpan으로 변환 TextSpan _buildColoredTextSpan(String text) { final spans = []; for (final char in text.split('')) { final color = _getCharColor(char); spans.add( TextSpan( text: char, style: TextStyle( fontFamily: 'JetBrainsMono', fontSize: 10, height: 1.1, color: color, letterSpacing: 0, ), ), ); } return TextSpan(children: spans); } @override Widget build(BuildContext context) { final frame = _applyGlitchEffect( frontScreenAnimationFrames[_currentFrame], ); // 현재 종족 이름 (UI 표시용) final raceName = RaceData.findById(_currentRaceId)?.name ?? 'Hero'; return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16), decoration: BoxDecoration( // 항상 검은 배경 color: Colors.black, borderRadius: BorderRadius.circular(12), border: Border.all( color: Colors.white24, width: 1, ), // 은은한 글로우 효과 boxShadow: [ BoxShadow( color: Colors.cyan.withValues(alpha: 0.15), blurRadius: 20, spreadRadius: 2, ), ], ), child: Column( children: [ // ASCII 애니메이션 (컬러 적용) FittedBox( fit: BoxFit.scaleDown, child: RichText( text: _buildColoredTextSpan(frame), ), ), const SizedBox(height: 8), // 하단 효과 바 (컬러) _buildEffectBar(), const SizedBox(height: 4), // 현재 종족 표시 Text( '♦ $raceName', style: TextStyle( fontFamily: 'JetBrainsMono', fontSize: 9, color: Colors.cyan.withValues(alpha: 0.7), ), ), ], ), ); } /// 하단 효과 바: 글리치/전투 효과 시각화 Widget _buildEffectBar() { // 프레임에 따라 색상 변화 final colors = [ Colors.cyan, Colors.purple, Colors.red, Colors.cyan, Colors.yellow, Colors.purple, ]; final currentColor = colors[_currentFrame % colors.length]; return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // 왼쪽 검 아이콘 (용사) Icon( Icons.flash_on, size: 14, color: Colors.yellow.withValues(alpha: 0.8), ), const SizedBox(width: 8), // 중앙 효과 바 Expanded( child: Container( height: 3, decoration: BoxDecoration( gradient: LinearGradient( colors: [ Colors.transparent, currentColor.withValues(alpha: 0.7), Colors.white.withValues(alpha: 0.9), currentColor.withValues(alpha: 0.7), Colors.transparent, ], ), borderRadius: BorderRadius.circular(2), ), ), ), const SizedBox(width: 8), // 오른쪽 보스 아이콘 Icon( Icons.whatshot, size: 14, color: Colors.red.withValues(alpha: 0.8), ), ], ); } }