import 'package:flutter/material.dart'; import 'package:asciineverdie/src/core/model/arena_match.dart'; import 'package:asciineverdie/src/core/model/combat_event.dart'; import 'package:asciineverdie/src/core/model/game_state.dart'; import 'package:asciineverdie/src/core/model/hall_of_fame.dart'; import 'package:asciineverdie/src/shared/animation/race_character_frames.dart'; import 'package:asciineverdie/src/shared/widgets/ascii_disintegrate_widget.dart'; import 'package:asciineverdie/src/features/game/widgets/ascii_animation_card.dart'; import 'package:asciineverdie/src/shared/retro_colors.dart'; /// 아레나 전투 영역 위젯 (Arena Battle Area) /// /// 활성 전투 중 ASCII 애니메이션 표시, 종료 시 승자/패자 분기 처리 class ArenaBattleArea extends StatelessWidget { const ArenaBattleArea({ super.key, required this.match, required this.isFinished, this.result, this.latestCombatEvent, }); final ArenaMatch match; final bool isFinished; final ArenaMatchResult? result; final CombatEvent? latestCombatEvent; @override Widget build(BuildContext context) { if (isFinished && result != null) { return _buildFinishedArea(context); } return _buildActiveArea(context); } /// 방패 장착 여부 확인 bool _hasShield(HallOfFameEntry entry) { final equipment = entry.finalEquipment; if (equipment == null) return false; return equipment.any((item) => item.slot.name == 'shield'); } /// 활성 전투 영역 (기존 AsciiAnimationCard) Widget _buildActiveArea(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: SizedBox( height: 120, child: AsciiAnimationCard( taskType: TaskType.kill, raceId: match.challenger.raceId, shieldName: _hasShield(match.challenger) ? 'shield' : null, opponentRaceId: match.opponent.raceId, opponentHasShield: _hasShield(match.opponent), latestCombatEvent: latestCombatEvent, ), ), ); } /// 종료된 전투 영역 (승자 유지 + 패자 분해) Widget _buildFinishedArea(BuildContext context) { final isVictory = result!.isVictory; final winnerRaceId = isVictory ? match.challenger.raceId : match.opponent.raceId; final loserRaceId = isVictory ? match.opponent.raceId : match.challenger.raceId; // 패자 캐릭터 프레임 (idle 첫 프레임) final loserFrameData = RaceCharacterFrames.get(loserRaceId) ?? RaceCharacterFrames.defaultFrames; final loserLines = loserFrameData.idle.first.lines; // 승자 캐릭터 프레임 (idle 첫 프레임) final winnerFrameData = RaceCharacterFrames.get(winnerRaceId) ?? RaceCharacterFrames.defaultFrames; final winnerLines = winnerFrameData.idle.first.lines; return Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: SizedBox( height: 120, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ // 좌측: 도전자 (승자면 유지, 패자면 분해) Expanded( child: Center( child: isVictory ? _buildStaticCharacter(context, winnerLines) : AsciiDisintegrateWidget( characterLines: _mirrorLines(loserLines), ), ), ), // 중앙 VS Text( 'VS', style: TextStyle( fontFamily: 'PressStart2P', fontSize: 14, color: RetroColors.goldOf(context).withValues(alpha: 0.5), ), ), // 우측: 상대 (승자면 유지, 패자면 분해) Expanded( child: Center( child: isVictory ? AsciiDisintegrateWidget(characterLines: loserLines) : _buildStaticCharacter(context, _mirrorLines(winnerLines)), ), ), ], ), ), ); } /// 정적 ASCII 캐릭터 표시 Widget _buildStaticCharacter(BuildContext context, List lines) { final textColor = RetroColors.textPrimaryOf(context); return Column( mainAxisSize: MainAxisSize.min, children: lines .map( (line) => Text( line, style: TextStyle( fontFamily: 'JetBrainsMono', fontSize: 15, color: textColor, height: 1.2, ), ), ) .toList(), ); } /// ASCII 문자열 미러링 (좌우 대칭) static List _mirrorLines(List lines) { return lines.map((line) { final chars = line.split(''); return chars.reversed.map(_mirrorChar).join(); }).toList(); } /// 개별 문자 미러링 static String _mirrorChar(String char) { return switch (char) { '/' => r'\', r'\' => '/', '(' => ')', ')' => '(', '[' => ']', ']' => '[', '{' => '}', '}' => '{', '<' => '>', '>' => '<', 'd' => 'b', 'b' => 'd', 'q' => 'p', 'p' => 'q', _ => char, }; } }