- arena_battle_controller.dart: 전투 로직 (스트림, 턴, 로그) - arena_battle_area.dart: 전투 영역 위젯 - arena_turn_indicator.dart: 경과 시간 위젯 - arena_combat_event_icons.dart: 전투 이벤트 아이콘 위젯
174 lines
5.2 KiB
Dart
174 lines
5.2 KiB
Dart
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,
|
|
};
|
|
}
|
|
}
|