- arena: 전투/결과/랭크 화면 폰트 조정 - front: 메인 화면 폰트 조정 - game: 게임플레이 위젯 전반 폰트 조정 (스킬, 장비, 인벤토리 등) - hall_of_fame: 명예의 전당 폰트 조정 - new_character: 캐릭터 생성 화면 폰트 조정 - settings: 설정 화면 폰트 조정 - 전반적인 가독성 향상
708 lines
22 KiB
Dart
708 lines
22 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:path_provider/path_provider.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/hall_of_fame.dart';
|
|
import 'package:asciineverdie/src/core/model/item_stats.dart';
|
|
import 'package:asciineverdie/src/features/game/widgets/combat_log.dart';
|
|
import 'package:asciineverdie/src/shared/retro_colors.dart';
|
|
|
|
// 임시 문자열
|
|
const _victory = 'VICTORY!';
|
|
const _defeat = 'DEFEAT...';
|
|
const _exchange = 'EQUIPMENT EXCHANGE';
|
|
const _turns = 'TURNS';
|
|
|
|
/// 아레나 결과 패널 (인라인)
|
|
///
|
|
/// 전투 로그 하단에 표시되는 플로팅 결과 패널
|
|
class ArenaResultPanel extends StatefulWidget {
|
|
const ArenaResultPanel({
|
|
super.key,
|
|
required this.result,
|
|
required this.turnCount,
|
|
required this.onContinue,
|
|
this.battleLog,
|
|
});
|
|
|
|
/// 대전 결과
|
|
final ArenaMatchResult result;
|
|
|
|
/// 총 턴 수
|
|
final int turnCount;
|
|
|
|
/// Continue 콜백
|
|
final VoidCallback onContinue;
|
|
|
|
/// 배틀 로그 (디버그 모드 저장용)
|
|
final List<CombatLogEntry>? battleLog;
|
|
|
|
@override
|
|
State<ArenaResultPanel> createState() => _ArenaResultPanelState();
|
|
}
|
|
|
|
class _ArenaResultPanelState extends State<ArenaResultPanel>
|
|
with SingleTickerProviderStateMixin {
|
|
late AnimationController _slideController;
|
|
late Animation<Offset> _slideAnimation;
|
|
late Animation<double> _fadeAnimation;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_slideController = AnimationController(
|
|
duration: const Duration(milliseconds: 500),
|
|
vsync: this,
|
|
);
|
|
|
|
_slideAnimation =
|
|
Tween<Offset>(
|
|
begin: const Offset(0, 1), // 아래에서 위로
|
|
end: Offset.zero,
|
|
).animate(
|
|
CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic),
|
|
);
|
|
|
|
_fadeAnimation = Tween<double>(
|
|
begin: 0.0,
|
|
end: 1.0,
|
|
).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOut));
|
|
|
|
// 약간 지연 후 애니메이션 시작 (분해 애니메이션과 동기화)
|
|
Future.delayed(const Duration(milliseconds: 800), () {
|
|
if (mounted) {
|
|
_slideController.forward();
|
|
}
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_slideController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
/// 배틀 로그 JSON 저장 (macOS 디버그 모드 전용)
|
|
Future<void> _saveBattleLog() async {
|
|
if (widget.battleLog == null || widget.battleLog!.isEmpty) return;
|
|
|
|
try {
|
|
// macOS: Downloads 폴더에 저장 (사용자가 쉽게 찾을 수 있도록)
|
|
final directory =
|
|
await getDownloadsDirectory() ??
|
|
await getApplicationDocumentsDirectory();
|
|
final timestamp = DateTime.now().toIso8601String().replaceAll(':', '-');
|
|
final challenger = widget.result.match.challenger.characterName;
|
|
final opponent = widget.result.match.opponent.characterName;
|
|
final fileName = 'arena_${challenger}_vs_${opponent}_$timestamp.json';
|
|
final file = File('${directory.path}/$fileName');
|
|
|
|
// 전투 통계 계산
|
|
final stats = _calculateBattleStats();
|
|
|
|
final jsonData = {
|
|
'match': {
|
|
'challenger': challenger,
|
|
'opponent': opponent,
|
|
'isVictory': widget.result.isVictory,
|
|
'turnCount': widget.turnCount,
|
|
'timestamp': DateTime.now().toIso8601String(),
|
|
},
|
|
'characters': {
|
|
'challenger': _characterToJson(widget.result.match.challenger),
|
|
'opponent': _characterToJson(widget.result.match.opponent),
|
|
},
|
|
'stats': stats,
|
|
'battleLog': widget.battleLog!.map((e) => e.toJson()).toList(),
|
|
};
|
|
|
|
await file.writeAsString(
|
|
const JsonEncoder.withIndent(' ').convert(jsonData),
|
|
);
|
|
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(
|
|
'${l10n.uiSaved}: $fileName',
|
|
style: const TextStyle(fontFamily: 'PressStart2P', fontSize: 11),
|
|
),
|
|
backgroundColor: RetroColors.mpOf(context),
|
|
duration: const Duration(seconds: 3),
|
|
),
|
|
);
|
|
}
|
|
} catch (e) {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(
|
|
'${l10n.uiError}: $e',
|
|
style: const TextStyle(fontFamily: 'PressStart2P', fontSize: 11),
|
|
),
|
|
backgroundColor: RetroColors.hpOf(context),
|
|
duration: const Duration(seconds: 3),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 캐릭터 정보를 JSON으로 변환
|
|
Map<String, dynamic> _characterToJson(HallOfFameEntry entry) {
|
|
return {
|
|
'name': entry.characterName,
|
|
'level': entry.level,
|
|
'race': entry.race,
|
|
'class': entry.klass,
|
|
'combatStats': entry.finalStats?.toJson(),
|
|
'equipment': entry.finalEquipment
|
|
?.map(
|
|
(EquipmentItem e) => {
|
|
'slot': e.slot.name,
|
|
'name': e.name,
|
|
'level': e.level,
|
|
'rarity': e.rarity.name,
|
|
'stats': e.stats.toJson(),
|
|
},
|
|
)
|
|
.toList(),
|
|
'skills': entry.finalSkills,
|
|
};
|
|
}
|
|
|
|
/// 배틀 로그에서 전투 통계 계산
|
|
Map<String, dynamic> _calculateBattleStats() {
|
|
if (widget.battleLog == null || widget.battleLog!.isEmpty) {
|
|
return {};
|
|
}
|
|
|
|
int challengerTotalDamage = 0;
|
|
int opponentTotalDamage = 0;
|
|
int challengerTotalHeal = 0;
|
|
int opponentTotalHeal = 0;
|
|
int challengerCriticals = 0;
|
|
int opponentCriticals = 0;
|
|
int challengerBlocks = 0;
|
|
int opponentBlocks = 0;
|
|
int challengerEvades = 0;
|
|
int opponentEvades = 0;
|
|
int challengerSkillsUsed = 0;
|
|
int opponentSkillsUsed = 0;
|
|
|
|
final challenger = widget.result.match.challenger.characterName;
|
|
|
|
for (final entry in widget.battleLog!) {
|
|
final msg = entry.message;
|
|
final isChallenger = msg.startsWith(challenger);
|
|
|
|
switch (entry.type) {
|
|
case CombatLogType.damage:
|
|
final dmg = _extractNumber(msg);
|
|
if (isChallenger) {
|
|
challengerTotalDamage += dmg;
|
|
}
|
|
case CombatLogType.monsterAttack:
|
|
final dmg = _extractNumber(msg);
|
|
opponentTotalDamage += dmg;
|
|
case CombatLogType.critical:
|
|
final dmg = _extractNumber(msg);
|
|
if (isChallenger) {
|
|
challengerTotalDamage += dmg;
|
|
challengerCriticals++;
|
|
} else {
|
|
opponentTotalDamage += dmg;
|
|
opponentCriticals++;
|
|
}
|
|
case CombatLogType.heal:
|
|
final heal = _extractNumber(msg);
|
|
if (isChallenger) {
|
|
challengerTotalHeal += heal;
|
|
} else {
|
|
opponentTotalHeal += heal;
|
|
}
|
|
case CombatLogType.block:
|
|
if (isChallenger) {
|
|
challengerBlocks++;
|
|
} else {
|
|
opponentBlocks++;
|
|
}
|
|
case CombatLogType.evade:
|
|
if (isChallenger) {
|
|
challengerEvades++;
|
|
} else {
|
|
opponentEvades++;
|
|
}
|
|
case CombatLogType.skill:
|
|
if (isChallenger) {
|
|
challengerSkillsUsed++;
|
|
} else {
|
|
opponentSkillsUsed++;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return {
|
|
'challenger': {
|
|
'totalDamage': challengerTotalDamage,
|
|
'totalHeal': challengerTotalHeal,
|
|
'criticals': challengerCriticals,
|
|
'blocks': challengerBlocks,
|
|
'evades': challengerEvades,
|
|
'skillsUsed': challengerSkillsUsed,
|
|
},
|
|
'opponent': {
|
|
'totalDamage': opponentTotalDamage,
|
|
'totalHeal': opponentTotalHeal,
|
|
'criticals': opponentCriticals,
|
|
'blocks': opponentBlocks,
|
|
'evades': opponentEvades,
|
|
'skillsUsed': opponentSkillsUsed,
|
|
},
|
|
};
|
|
}
|
|
|
|
/// 메시지에서 숫자 추출
|
|
int _extractNumber(String msg) {
|
|
final match = RegExp(r'(\d+)').firstMatch(msg);
|
|
return match != null ? int.tryParse(match.group(1)!) ?? 0 : 0;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final isVictory = widget.result.isVictory;
|
|
final resultColor = isVictory ? Colors.amber : Colors.red.shade400;
|
|
final panelColor = isVictory
|
|
? RetroColors.goldOf(context).withValues(alpha: 0.15)
|
|
: Colors.red.withValues(alpha: 0.1);
|
|
final borderColor = isVictory
|
|
? RetroColors.goldOf(context)
|
|
: Colors.red.shade400;
|
|
|
|
return SlideTransition(
|
|
position: _slideAnimation,
|
|
child: FadeTransition(
|
|
opacity: _fadeAnimation,
|
|
child: Container(
|
|
margin: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: RetroColors.panelBgOf(context),
|
|
borderRadius: BorderRadius.circular(8),
|
|
border: Border.all(color: borderColor, width: 2),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: borderColor.withValues(alpha: 0.3),
|
|
blurRadius: 8,
|
|
spreadRadius: 1,
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
// 타이틀 배너
|
|
Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
decoration: BoxDecoration(
|
|
color: panelColor,
|
|
borderRadius: const BorderRadius.vertical(
|
|
top: Radius.circular(6),
|
|
),
|
|
),
|
|
child: _buildTitle(context, isVictory, resultColor),
|
|
),
|
|
// 내용
|
|
Padding(
|
|
padding: const EdgeInsets.all(12),
|
|
child: Column(
|
|
children: [
|
|
// 전투 요약 (턴 수)
|
|
_buildBattleSummary(context),
|
|
const SizedBox(height: 12),
|
|
// 장비 교환
|
|
_buildExchangeSection(context),
|
|
const SizedBox(height: 12),
|
|
// 배틀로그 저장 버튼 (macOS 디버그 모드 전용)
|
|
if (kDebugMode &&
|
|
Platform.isMacOS &&
|
|
widget.battleLog != null) ...[
|
|
_buildSaveLogButton(context),
|
|
const SizedBox(height: 8),
|
|
],
|
|
// Continue 버튼
|
|
_buildContinueButton(context, resultColor),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
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: 20,
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
isVictory ? _victory : _defeat,
|
|
style: TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 14,
|
|
color: color,
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Icon(
|
|
isVictory ? Icons.emoji_events : Icons.sentiment_very_dissatisfied,
|
|
color: color,
|
|
size: 20,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildBattleSummary(BuildContext context) {
|
|
final winner = widget.result.isVictory
|
|
? widget.result.match.challenger.characterName
|
|
: widget.result.match.opponent.characterName;
|
|
final loser = widget.result.isVictory
|
|
? widget.result.match.opponent.characterName
|
|
: widget.result.match.challenger.characterName;
|
|
|
|
return Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
// 승자
|
|
Text(
|
|
winner,
|
|
style: TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 11,
|
|
color: RetroColors.goldOf(context),
|
|
),
|
|
),
|
|
Text(
|
|
' defeated ',
|
|
style: TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 11,
|
|
color: RetroColors.textMutedOf(context),
|
|
),
|
|
),
|
|
// 패자
|
|
Text(
|
|
loser,
|
|
style: TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 12,
|
|
color: RetroColors.textSecondaryOf(context),
|
|
),
|
|
),
|
|
Text(
|
|
' in ',
|
|
style: TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 11,
|
|
color: RetroColors.textMutedOf(context),
|
|
),
|
|
),
|
|
// 턴 수
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
|
decoration: BoxDecoration(
|
|
color: RetroColors.goldOf(context).withValues(alpha: 0.2),
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
child: Text(
|
|
'${widget.turnCount} $_turns',
|
|
style: TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 12,
|
|
color: RetroColors.goldOf(context),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildExchangeSection(BuildContext context) {
|
|
final isVictory = widget.result.isVictory;
|
|
|
|
// 승패에 따라 교환 슬롯 결정
|
|
// 승리: 도전자가 선택한 슬롯(상대에게서 약탈)
|
|
// 패배: 상대가 선택한 슬롯(도전자에게서 약탈당함)
|
|
final slot = isVictory
|
|
? widget.result.match.challengerBettingSlot
|
|
: widget.result.match.opponentBettingSlot;
|
|
|
|
// 도전자의 교환 결과
|
|
final oldItem = _findItem(
|
|
widget.result.match.challenger.finalEquipment,
|
|
slot,
|
|
);
|
|
final newItem = _findItem(
|
|
widget.result.updatedChallenger.finalEquipment,
|
|
slot,
|
|
);
|
|
|
|
final oldScore = oldItem != null
|
|
? ItemService.calculateEquipmentScore(oldItem)
|
|
: 0;
|
|
final newScore = newItem != null
|
|
? ItemService.calculateEquipmentScore(newItem)
|
|
: 0;
|
|
final scoreDiff = newScore - oldScore;
|
|
|
|
return Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: isVictory
|
|
? Colors.green.withValues(alpha: 0.1)
|
|
: Colors.red.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(6),
|
|
border: Border.all(
|
|
color: isVictory
|
|
? Colors.green.withValues(alpha: 0.3)
|
|
: Colors.red.withValues(alpha: 0.3),
|
|
),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
// 교환 타이틀
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(
|
|
Icons.swap_horiz,
|
|
color: RetroColors.goldOf(context),
|
|
size: 14,
|
|
),
|
|
const SizedBox(width: 4),
|
|
Text(
|
|
_exchange,
|
|
style: TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 12,
|
|
color: RetroColors.goldOf(context),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
// 슬롯
|
|
Text(
|
|
_getSlotLabel(slot),
|
|
style: TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 12,
|
|
color: RetroColors.textMutedOf(context),
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
// 교환 내용
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
// 이전 아이템
|
|
_buildItemBadge(context, oldItem, oldScore),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
|
child: Icon(
|
|
Icons.arrow_forward,
|
|
size: 14,
|
|
color: RetroColors.textMutedOf(context),
|
|
),
|
|
),
|
|
// 새 아이템
|
|
_buildItemBadge(context, newItem, newScore),
|
|
const SizedBox(width: 8),
|
|
// 점수 변화
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
|
decoration: BoxDecoration(
|
|
color: scoreDiff >= 0
|
|
? Colors.green.withValues(alpha: 0.2)
|
|
: Colors.red.withValues(alpha: 0.2),
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(
|
|
scoreDiff >= 0
|
|
? Icons.arrow_upward
|
|
: Icons.arrow_downward,
|
|
size: 10,
|
|
color: scoreDiff >= 0 ? Colors.green : Colors.red,
|
|
),
|
|
Text(
|
|
'${scoreDiff >= 0 ? '+' : ''}$scoreDiff',
|
|
style: TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 11,
|
|
color: scoreDiff >= 0 ? Colors.green : Colors.red,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildItemBadge(BuildContext context, EquipmentItem? item, int score) {
|
|
if (item == null || item.isEmpty) {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: Colors.grey.withValues(alpha: 0.2),
|
|
borderRadius: BorderRadius.circular(4),
|
|
border: Border.all(color: Colors.grey.withValues(alpha: 0.3)),
|
|
),
|
|
child: Text(
|
|
'(empty)',
|
|
style: TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 11,
|
|
color: RetroColors.textMutedOf(context),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
final rarityColor = _getRarityColor(item.rarity);
|
|
|
|
return Container(
|
|
constraints: const BoxConstraints(maxWidth: 80),
|
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: rarityColor.withValues(alpha: 0.15),
|
|
borderRadius: BorderRadius.circular(4),
|
|
border: Border.all(color: rarityColor.withValues(alpha: 0.5)),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
Text(
|
|
item.name,
|
|
style: TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 12,
|
|
color: rarityColor,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
Text(
|
|
'$score pt',
|
|
style: TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 12,
|
|
color: RetroColors.textMutedOf(context),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildContinueButton(BuildContext context, Color color) {
|
|
return SizedBox(
|
|
width: double.infinity,
|
|
child: FilledButton(
|
|
onPressed: widget.onContinue,
|
|
style: FilledButton.styleFrom(
|
|
backgroundColor: color,
|
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),
|
|
),
|
|
child: Text(
|
|
l10n.buttonConfirm,
|
|
style: const TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 13,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 배틀로그 저장 버튼 (macOS 디버그 모드 전용)
|
|
Widget _buildSaveLogButton(BuildContext context) {
|
|
return SizedBox(
|
|
width: double.infinity,
|
|
child: OutlinedButton.icon(
|
|
onPressed: _saveBattleLog,
|
|
icon: const Icon(Icons.save_alt, size: 14),
|
|
label: Text(
|
|
l10n.uiSaveBattleLog,
|
|
style: const TextStyle(fontFamily: 'PressStart2P', fontSize: 11),
|
|
),
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor: RetroColors.mpOf(context),
|
|
side: BorderSide(color: RetroColors.mpOf(context)),
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
EquipmentItem? _findItem(List<EquipmentItem>? 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,
|
|
};
|
|
}
|
|
}
|