import 'package:flutter/material.dart'; import 'package:asciineverdie/data/game_text_l10n.dart' as l10n; import 'package:asciineverdie/src/core/engine/item_service.dart'; import 'package:asciineverdie/src/core/model/arena_match.dart'; import 'package:asciineverdie/src/core/model/equipment_item.dart'; import 'package:asciineverdie/src/core/model/equipment_slot.dart'; import 'package:asciineverdie/src/core/model/item_stats.dart'; import 'package:asciineverdie/src/shared/retro_colors.dart'; // 아레나 관련 임시 문자열 (추후 l10n으로 이동) const _arenaVictory = 'VICTORY!'; const _arenaDefeat = 'DEFEAT...'; const _arenaExchange = 'EQUIPMENT EXCHANGE'; /// 아레나 결과 다이얼로그 /// /// 전투 승패 및 장비 교환 결과 표시 class ArenaResultDialog extends StatelessWidget { const ArenaResultDialog({ super.key, required this.result, required this.onClose, }); /// 대전 결과 final ArenaMatchResult result; /// 닫기 콜백 final VoidCallback onClose; @override Widget build(BuildContext context) { final isVictory = result.isVictory; final resultColor = isVictory ? Colors.amber : Colors.red; final slot = result.match.bettingSlot; return AlertDialog( backgroundColor: RetroColors.panelBgOf(context), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide(color: resultColor, width: 2), ), title: _buildTitle(context, isVictory, resultColor), content: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 350), child: Column( mainAxisSize: MainAxisSize.min, children: [ // 전투 정보 _buildBattleInfo(context), const SizedBox(height: 16), const Divider(), const SizedBox(height: 16), // 장비 교환 결과 _buildExchangeResult(context, slot), ], ), ), actions: [ FilledButton( onPressed: onClose, style: FilledButton.styleFrom( backgroundColor: resultColor, ), child: Text( l10n.buttonConfirm, style: const TextStyle( fontFamily: 'PressStart2P', fontSize: 8, ), ), ), ], ); } Widget _buildTitle(BuildContext context, bool isVictory, Color color) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( isVictory ? Icons.emoji_events : Icons.sentiment_very_dissatisfied, color: color, size: 28, ), const SizedBox(width: 8), Text( isVictory ? _arenaVictory : _arenaDefeat, style: TextStyle( fontFamily: 'PressStart2P', fontSize: 12, color: color, ), ), const SizedBox(width: 8), Icon( isVictory ? Icons.emoji_events : Icons.sentiment_very_dissatisfied, color: color, size: 28, ), ], ); } Widget _buildBattleInfo(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ // 도전자 정보 _buildFighterInfo( context, result.match.challenger.characterName, result.isVictory, ), // VS Text( 'VS', style: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, color: RetroColors.textMutedOf(context), ), ), // 상대 정보 _buildFighterInfo( context, result.match.opponent.characterName, !result.isVictory, ), ], ); } Widget _buildFighterInfo(BuildContext context, String name, bool isWinner) { return Column( children: [ Icon( isWinner ? Icons.emoji_events : Icons.close, color: isWinner ? Colors.amber : Colors.grey, size: 24, ), const SizedBox(height: 4), Text( name, style: TextStyle( fontFamily: 'PressStart2P', fontSize: 7, color: isWinner ? RetroColors.goldOf(context) : RetroColors.textMutedOf(context), ), maxLines: 1, overflow: TextOverflow.ellipsis, ), Text( isWinner ? 'WINNER' : 'LOSER', style: TextStyle( fontFamily: 'PressStart2P', fontSize: 6, color: isWinner ? Colors.amber : Colors.grey, ), ), ], ); } Widget _buildExchangeResult(BuildContext context, EquipmentSlot slot) { // 교환된 장비 찾기 final challengerOldItem = _findItem( result.match.challenger.finalEquipment, slot, ); final opponentOldItem = _findItem( result.match.opponent.finalEquipment, slot, ); final challengerNewItem = _findItem( result.updatedChallenger.finalEquipment, slot, ); final opponentNewItem = _findItem( result.updatedOpponent.finalEquipment, slot, ); return Column( children: [ // 교환 타이틀 Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.swap_horiz, color: RetroColors.goldOf(context), size: 20, ), const SizedBox(width: 8), Text( _arenaExchange, style: TextStyle( fontFamily: 'PressStart2P', fontSize: 8, color: RetroColors.goldOf(context), ), ), ], ), const SizedBox(height: 12), // 슬롯 정보 Text( _getSlotLabel(slot), style: TextStyle( fontFamily: 'PressStart2P', fontSize: 7, color: RetroColors.textSecondaryOf(context), ), ), const SizedBox(height: 12), // 내 캐릭터 장비 변경 _buildExchangeRow( context, result.match.challenger.characterName, challengerOldItem, challengerNewItem, result.isVictory, ), const SizedBox(height: 8), // 상대 장비 변경 _buildExchangeRow( context, result.match.opponent.characterName, opponentOldItem, opponentNewItem, !result.isVictory, ), ], ); } Widget _buildExchangeRow( BuildContext context, String name, EquipmentItem? oldItem, EquipmentItem? newItem, bool isWinner, ) { final oldScore = oldItem != null ? ItemService.calculateEquipmentScore(oldItem) : 0; final newScore = newItem != null ? ItemService.calculateEquipmentScore(newItem) : 0; final scoreDiff = newScore - oldScore; final isGain = scoreDiff > 0; return Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: isWinner ? Colors.green.withValues(alpha: 0.1) : Colors.red.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), border: Border.all( color: isWinner ? Colors.green.withValues(alpha: 0.3) : Colors.red.withValues(alpha: 0.3), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 이름 Text( name, style: TextStyle( fontFamily: 'PressStart2P', fontSize: 7, color: RetroColors.textPrimaryOf(context), ), ), const SizedBox(height: 4), // 장비 변경 Row( children: [ // 이전 장비 Expanded( child: _buildItemChip( context, oldItem, oldScore, isOld: true, ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: Icon( Icons.arrow_forward, size: 14, color: RetroColors.textMutedOf(context), ), ), // 새 장비 Expanded( child: _buildItemChip( context, newItem, newScore, isOld: false, ), ), ], ), const SizedBox(height: 4), // 점수 변화 Align( alignment: Alignment.centerRight, child: Text( '${isGain ? '+' : ''}$scoreDiff pt', style: TextStyle( fontFamily: 'PressStart2P', fontSize: 7, color: isGain ? Colors.green : Colors.red, ), ), ), ], ), ); } Widget _buildItemChip( BuildContext context, EquipmentItem? item, int score, { required bool isOld, }) { if (item == null || item.isEmpty) { return Text( '(empty)', style: TextStyle( fontFamily: 'PressStart2P', fontSize: 6, color: RetroColors.textMutedOf(context), ), ); } final rarityColor = _getRarityColor(item.rarity); return Container( padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), decoration: BoxDecoration( color: rarityColor.withValues(alpha: isOld ? 0.1 : 0.2), borderRadius: BorderRadius.circular(4), border: Border.all( color: rarityColor.withValues(alpha: 0.5), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.name, style: TextStyle( fontFamily: 'PressStart2P', fontSize: 5, color: rarityColor, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), Text( '$score pt', style: TextStyle( fontFamily: 'PressStart2P', fontSize: 5, color: RetroColors.textMutedOf(context), ), ), ], ), ); } EquipmentItem? _findItem(List? equipment, EquipmentSlot slot) { if (equipment == null) return null; for (final item in equipment) { if (item.slot == slot) return item; } return null; } String _getSlotLabel(EquipmentSlot slot) { return switch (slot) { EquipmentSlot.weapon => l10n.slotWeapon, EquipmentSlot.shield => l10n.slotShield, EquipmentSlot.helm => l10n.slotHelm, EquipmentSlot.hauberk => l10n.slotHauberk, EquipmentSlot.brassairts => l10n.slotBrassairts, EquipmentSlot.vambraces => l10n.slotVambraces, EquipmentSlot.gauntlets => l10n.slotGauntlets, EquipmentSlot.gambeson => l10n.slotGambeson, EquipmentSlot.cuisses => l10n.slotCuisses, EquipmentSlot.greaves => l10n.slotGreaves, EquipmentSlot.sollerets => l10n.slotSollerets, }; } Color _getRarityColor(ItemRarity rarity) { return switch (rarity) { ItemRarity.common => Colors.grey.shade600, ItemRarity.uncommon => Colors.green.shade600, ItemRarity.rare => Colors.blue.shade600, ItemRarity.epic => Colors.purple.shade600, ItemRarity.legendary => Colors.orange.shade700, }; } } /// 아레나 결과 다이얼로그 표시 Future showArenaResultDialog( BuildContext context, { required ArenaMatchResult result, required VoidCallback onClose, }) { return showDialog( context: context, barrierDismissible: false, builder: (context) => ArenaResultDialog( result: result, onClose: () { Navigator.of(context).pop(); onClose(); }, ), ); }