refactor(arena): arena_battle_screen 759줄 → 155줄 분할

- arena_battle_controller.dart: 전투 로직 (스트림, 턴, 로그)
- arena_battle_area.dart: 전투 영역 위젯
- arena_turn_indicator.dart: 경과 시간 위젯
- arena_combat_event_icons.dart: 전투 이벤트 아이콘 위젯
This commit is contained in:
JiWoong Sul
2026-03-30 20:40:55 +09:00
parent 5d38bac79e
commit 9be0dd3e4f
5 changed files with 787 additions and 643 deletions

View File

@@ -0,0 +1,173 @@
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<String> 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<String> _mirrorLines(List<String> 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,
};
}
}