import 'package:flutter/material.dart'; import 'package:asciineverdie/data/game_text_l10n.dart' as l10n; import 'package:asciineverdie/src/core/l10n/game_data_l10n.dart'; import 'package:asciineverdie/src/core/model/equipment_slot.dart'; import 'package:asciineverdie/src/core/model/game_state.dart'; import 'package:asciineverdie/src/core/model/hall_of_fame.dart'; import 'package:asciineverdie/src/core/model/item_stats.dart'; import 'package:asciineverdie/src/features/game/widgets/ascii_animation_card.dart'; import 'package:asciineverdie/src/shared/retro_colors.dart'; import 'package:asciineverdie/src/shared/widgets/retro_dialog.dart'; /// 명예의 전당 상세 정보 다이얼로그 class HeroDetailDialog extends StatelessWidget { const HeroDetailDialog({super.key, required this.entry}); final HallOfFameEntry entry; @override Widget build(BuildContext context) { return RetroDialog( title: entry.characterName, titleIcon: '👑', maxWidth: 420, maxHeight: 600, child: Column( mainAxisSize: MainAxisSize.min, children: [ // 서브 타이틀 Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Text( '${GameDataL10n.getRaceName(context, entry.race)} ' '${GameDataL10n.getKlassName(context, entry.klass)} - ' 'Lv.${entry.level}', style: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, color: RetroColors.textSecondaryOf(context), ), ), ), // 스크롤 가능한 컨텐츠 Flexible( child: SingleChildScrollView( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // 캐릭터 애니메이션 섹션 _buildSection( context, icon: Icons.movie, title: l10n.hofCharacterPreview, child: _buildAnimationPreview(context), ), const SizedBox(height: 12), // 통계 섹션 _buildSection( context, icon: Icons.analytics, title: l10n.hofStats, child: _buildStatsGrid(context), ), const SizedBox(height: 12), // 전투 스탯 섹션 if (entry.finalStats != null) ...[ _buildSection( context, icon: Icons.sports_mma, title: l10n.hofCombatStats, child: _buildCombatStatsGrid(context), ), const SizedBox(height: 12), ], // 장비 섹션 if (entry.finalEquipment != null) ...[ _buildSection( context, icon: Icons.shield, title: l10n.navEquipment, child: _buildEquipmentList(context), ), const SizedBox(height: 12), ], // 스킬 섹션 (스킬이 없어도 표시) _buildSection( context, icon: Icons.auto_fix_high, title: l10n.hofSkills, child: _buildSkillList(context), ), ], ), ), ), ], ), ); } Widget _buildSection( BuildContext context, { required IconData icon, required String title, required Widget child, }) { final goldColor = RetroColors.goldOf(context); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(icon, size: 14, color: goldColor), const SizedBox(width: 6), Text( title.toUpperCase(), style: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, color: goldColor, ), ), const SizedBox(width: 8), Expanded( child: Container( height: 1, decoration: BoxDecoration( gradient: LinearGradient( colors: [ goldColor, goldColor.withValues(alpha: 0.3), Colors.transparent, ], ), ), ), ), ], ), const SizedBox(height: 8), child, ], ); } /// 캐릭터 애니메이션 미리보기 위젯 Widget _buildAnimationPreview(BuildContext context) { // 장비에서 무기와 방패 이름 추출 String? weaponName; String? shieldName; if (entry.finalEquipment != null) { for (final item in entry.finalEquipment!) { if (item.slot == EquipmentSlot.weapon && item.isNotEmpty) { weaponName = item.name; } else if (item.slot == EquipmentSlot.shield && item.isNotEmpty) { shieldName = item.name; } } } final borderColor = RetroColors.borderOf(context); return Center( child: Container( decoration: BoxDecoration( border: Border.all(color: borderColor, width: 1), ), child: SizedBox( width: 360, height: 80, child: AsciiAnimationCard( taskType: TaskType.kill, raceId: entry.race, weaponName: weaponName, shieldName: shieldName, characterLevel: entry.level, monsterLevel: entry.level, monsterBaseName: 'Glitch God', ), ), ), ); } Widget _buildStatsGrid(BuildContext context) { return Wrap( spacing: 8, runSpacing: 6, children: [ _buildStatItem( context, Icons.timer, l10n.hofTime, entry.formattedPlayTime, ), _buildStatItem( context, Icons.pest_control, l10n.hofMonsters, '${entry.monstersKilled}', ), _buildStatItem( context, Icons.heart_broken, l10n.hofDeaths, '${entry.totalDeaths}', ), _buildStatItem( context, Icons.check_circle, l10n.hofQuests, '${entry.questsCompleted}', ), _buildStatItem( context, Icons.calendar_today, l10n.hofCleared, entry.formattedClearedDate, ), ], ); } Widget _buildCombatStatsGrid(BuildContext context) { final stats = entry.finalStats!; final borderColor = RetroColors.borderOf(context); return Column( children: [ // 기본 스탯 행 Wrap( spacing: 6, runSpacing: 4, children: [ _buildCombatStatChip(l10n.statStr, '${stats.str}', Colors.red), _buildCombatStatChip(l10n.statCon, '${stats.con}', Colors.orange), _buildCombatStatChip(l10n.statDex, '${stats.dex}', Colors.green), _buildCombatStatChip( l10n.statInt, '${stats.intelligence}', Colors.blue, ), _buildCombatStatChip(l10n.statWis, '${stats.wis}', Colors.purple), _buildCombatStatChip(l10n.statCha, '${stats.cha}', Colors.pink), ], ), const SizedBox(height: 6), Container(height: 1, color: borderColor), const SizedBox(height: 6), // 공격 스탯 행 Wrap( spacing: 6, runSpacing: 4, children: [ _buildCombatStatChip( l10n.statAtk, '${stats.atk}', Colors.red.shade700, ), _buildCombatStatChip( l10n.statMAtk, '${stats.magAtk}', Colors.blue.shade700, ), _buildCombatStatChip( l10n.statCri, '${(stats.criRate * 100).toStringAsFixed(1)}%', Colors.amber.shade700, ), ], ), const SizedBox(height: 6), // 방어 스탯 행 Wrap( spacing: 6, runSpacing: 4, children: [ _buildCombatStatChip(l10n.statDef, '${stats.def}', Colors.brown), _buildCombatStatChip( l10n.statMDef, '${stats.magDef}', Colors.indigo, ), _buildCombatStatChip( l10n.statEva, '${(stats.evasion * 100).toStringAsFixed(1)}%', Colors.teal, ), _buildCombatStatChip( l10n.statBlock, '${(stats.blockRate * 100).toStringAsFixed(1)}%', Colors.blueGrey, ), ], ), const SizedBox(height: 6), // HP/MP 행 Wrap( spacing: 6, runSpacing: 4, children: [ _buildCombatStatChip( l10n.statHp, '${stats.hpMax}', Colors.red.shade400, ), _buildCombatStatChip( l10n.statMp, '${stats.mpMax}', Colors.blue.shade400, ), ], ), ], ); } Widget _buildCombatStatChip(String label, String value, Color color) { return Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 3), decoration: BoxDecoration( color: color.withValues(alpha: 0.1), border: Border.all(color: color.withValues(alpha: 0.3), width: 1), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( '$label ', style: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, color: color, ), ), Text( value, style: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, color: color, ), ), ], ), ); } Widget _buildStatItem( BuildContext context, IconData icon, String label, String value, ) { final goldColor = RetroColors.goldOf(context); return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: goldColor.withValues(alpha: 0.1), border: Border.all(color: goldColor.withValues(alpha: 0.3), width: 1), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 12, color: goldColor), const SizedBox(width: 4), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( value, style: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, color: goldColor, ), ), Text( label, style: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, color: RetroColors.textMutedOf(context), ), ), ], ), ], ), ); } Widget _buildEquipmentList(BuildContext context) { final equipment = entry.finalEquipment!; final mutedColor = RetroColors.textMutedOf(context); return Column( children: equipment.map((item) { if (item.isEmpty) return const SizedBox.shrink(); final slotLabel = _getSlotLabel(item.slot); final slotIcon = _getSlotIcon(item.slot); final slotIndex = _getSlotIndex(item.slot); final rarityColor = _getRarityColor(item.rarity); // 장비 이름 번역 final translatedName = GameDataL10n.translateEquipString( context, item.name, slotIndex, ); // 주요 스탯 요약 final statSummary = _buildStatSummary(item.stats, item.slot); return Padding( padding: const EdgeInsets.symmetric(vertical: 3), child: Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( color: rarityColor.withValues(alpha: 0.1), border: Border.all( color: rarityColor.withValues(alpha: 0.3), width: 1, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 슬롯 + 희귀도 표시 Row( children: [ Icon(slotIcon, size: 12, color: rarityColor), const SizedBox(width: 4), Text( slotLabel, style: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, color: mutedColor, ), ), const Spacer(), Container( padding: const EdgeInsets.symmetric( horizontal: 4, vertical: 1, ), decoration: BoxDecoration( color: rarityColor.withValues(alpha: 0.2), border: Border.all( color: rarityColor.withValues(alpha: 0.4), width: 1, ), ), child: Text( _getRarityLabel(item.rarity), style: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, color: rarityColor, ), ), ), ], ), const SizedBox(height: 4), // 장비 이름 Text( translatedName, style: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, color: rarityColor, ), ), // 스탯 요약 if (statSummary.isNotEmpty) ...[ const SizedBox(height: 4), Wrap(spacing: 6, runSpacing: 2, children: statSummary), ], ], ), ), ); }).toList(), ); } 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, }; } IconData _getSlotIcon(EquipmentSlot slot) { return switch (slot) { EquipmentSlot.weapon => Icons.gavel, EquipmentSlot.shield => Icons.shield, EquipmentSlot.helm => Icons.sports_mma, EquipmentSlot.hauberk => Icons.checkroom, EquipmentSlot.brassairts => Icons.front_hand, EquipmentSlot.vambraces => Icons.back_hand, EquipmentSlot.gauntlets => Icons.sports_handball, EquipmentSlot.gambeson => Icons.dry_cleaning, EquipmentSlot.cuisses => Icons.airline_seat_legroom_normal, EquipmentSlot.greaves => Icons.snowshoeing, EquipmentSlot.sollerets => Icons.do_not_step, }; } int _getSlotIndex(EquipmentSlot slot) { return switch (slot) { EquipmentSlot.weapon => 0, EquipmentSlot.shield => 1, _ => 2, }; } 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, }; } String _getRarityLabel(ItemRarity rarity) { return switch (rarity) { ItemRarity.common => l10n.rarityCommon, ItemRarity.uncommon => l10n.rarityUncommon, ItemRarity.rare => l10n.rarityRare, ItemRarity.epic => l10n.rarityEpic, ItemRarity.legendary => l10n.rarityLegendary, }; } List _buildStatSummary(ItemStats stats, EquipmentSlot slot) { final widgets = []; void addStat(String label, String value, Color color) { widgets.add( Text( '$label $value', style: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, color: color, ), ), ); } // 공격 스탯 if (stats.atk > 0) addStat(l10n.statAtk, '+${stats.atk}', Colors.red); if (stats.magAtk > 0) { addStat(l10n.statMAtk, '+${stats.magAtk}', Colors.blue); } // 방어 스탯 if (stats.def > 0) addStat(l10n.statDef, '+${stats.def}', Colors.brown); if (stats.magDef > 0) { addStat(l10n.statMDef, '+${stats.magDef}', Colors.indigo); } // 확률 스탯 if (stats.criRate > 0) { addStat( l10n.statCri, '+${(stats.criRate * 100).toStringAsFixed(0)}%', Colors.amber, ); } if (stats.blockRate > 0) { addStat( l10n.statBlock, '+${(stats.blockRate * 100).toStringAsFixed(0)}%', Colors.blueGrey, ); } if (stats.evasion > 0) { addStat( l10n.statEva, '+${(stats.evasion * 100).toStringAsFixed(0)}%', Colors.teal, ); } if (stats.parryRate > 0) { addStat( l10n.statParry, '+${(stats.parryRate * 100).toStringAsFixed(0)}%', Colors.cyan, ); } // 보너스 스탯 if (stats.hpBonus > 0) { addStat(l10n.statHp, '+${stats.hpBonus}', Colors.red.shade400); } if (stats.mpBonus > 0) { addStat(l10n.statMp, '+${stats.mpBonus}', Colors.blue.shade400); } // 능력치 보너스 if (stats.strBonus > 0) { addStat(l10n.statStr, '+${stats.strBonus}', Colors.red.shade700); } if (stats.conBonus > 0) { addStat(l10n.statCon, '+${stats.conBonus}', Colors.orange.shade700); } if (stats.dexBonus > 0) { addStat(l10n.statDex, '+${stats.dexBonus}', Colors.green.shade700); } if (stats.intBonus > 0) { addStat(l10n.statInt, '+${stats.intBonus}', Colors.blue.shade700); } return widgets; } Widget _buildSkillList(BuildContext context) { final skills = entry.finalSkills; const skillColor = Colors.purple; // 스킬이 없는 경우 빈 상태 표시 if (skills == null || skills.isEmpty) { return Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: RetroColors.backgroundOf(context), border: Border.all(color: RetroColors.borderOf(context), width: 1), ), child: Text( l10n.hofNoSkills.toUpperCase(), style: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, color: RetroColors.textMutedOf(context), ), ), ); } return Wrap( spacing: 6, runSpacing: 4, children: skills.map((skill) { final name = skill['name'] ?? ''; final rank = skill['rank'] ?? ''; // 스킬 이름 번역 적용 final translatedName = GameDataL10n.getSpellName(context, name); return Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 3), decoration: BoxDecoration( color: skillColor.withValues(alpha: 0.1), border: Border.all( color: skillColor.withValues(alpha: 0.3), width: 1, ), ), child: Text( '$translatedName $rank', style: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, color: skillColor.shade400, ), ), ); }).toList(), ); } }