feat(animation): ASCII 애니메이션 시스템 구현
- TaskType별 애니메이션 (전투, 마을, 걷기) - 몬스터 카테고리별 전투 애니메이션 (7종) - 특수 애니메이션 (레벨업, 퀘스트 완료, Act 완료) - 색상 테마 옵션 (green, amber, white, system) - 테마 설정 SharedPreferences 저장 - 프로그레스 바를 상단으로 이동
This commit is contained in:
157
lib/src/features/game/widgets/task_progress_panel.dart
Normal file
157
lib/src/features/game/widgets/task_progress_panel.dart
Normal file
@@ -0,0 +1,157 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:askiineverdie/src/core/animation/ascii_animation_data.dart';
|
||||
import 'package:askiineverdie/src/core/animation/ascii_animation_type.dart';
|
||||
import 'package:askiineverdie/src/core/model/game_state.dart';
|
||||
import 'package:askiineverdie/src/features/game/widgets/ascii_animation_card.dart';
|
||||
|
||||
/// 상단 패널: ASCII 애니메이션 + Task Progress 바
|
||||
class TaskProgressPanel extends StatelessWidget {
|
||||
const TaskProgressPanel({
|
||||
super.key,
|
||||
required this.progress,
|
||||
required this.speedMultiplier,
|
||||
required this.onSpeedCycle,
|
||||
required this.colorTheme,
|
||||
required this.onThemeCycle,
|
||||
this.specialAnimation,
|
||||
});
|
||||
|
||||
final ProgressState progress;
|
||||
final int speedMultiplier;
|
||||
final VoidCallback onSpeedCycle;
|
||||
final AsciiColorTheme colorTheme;
|
||||
final VoidCallback onThemeCycle;
|
||||
|
||||
/// 특수 애니메이션 (레벨업, 퀘스트 완료 등)
|
||||
final AsciiAnimationType? specialAnimation;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
border: Border(
|
||||
bottom: BorderSide(color: Theme.of(context).dividerColor),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// ASCII 애니메이션 카드
|
||||
SizedBox(
|
||||
height: 120,
|
||||
child: AsciiAnimationCard(
|
||||
taskType: progress.currentTask.type,
|
||||
monsterBaseName: progress.currentTask.monsterBaseName,
|
||||
colorTheme: colorTheme,
|
||||
specialAnimation: specialAnimation,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// 상태 메시지 + 버튼들
|
||||
Row(
|
||||
children: [
|
||||
_buildThemeButton(context),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
progress.currentTask.caption.isNotEmpty
|
||||
? progress.currentTask.caption
|
||||
: 'Welcome to Progress Quest!',
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_buildSpeedButton(context),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
|
||||
// Task Progress 바
|
||||
_buildProgressBar(context),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildThemeButton(BuildContext context) {
|
||||
final themeLabel = switch (colorTheme) {
|
||||
AsciiColorTheme.green => 'G',
|
||||
AsciiColorTheme.amber => 'A',
|
||||
AsciiColorTheme.white => 'W',
|
||||
AsciiColorTheme.system => 'S',
|
||||
};
|
||||
|
||||
final themeColor = switch (colorTheme) {
|
||||
AsciiColorTheme.green => const Color(0xFF00FF00),
|
||||
AsciiColorTheme.amber => const Color(0xFFFFB000),
|
||||
AsciiColorTheme.white => Colors.white,
|
||||
AsciiColorTheme.system => Theme.of(context).colorScheme.primary,
|
||||
};
|
||||
|
||||
return SizedBox(
|
||||
height: 28,
|
||||
child: OutlinedButton(
|
||||
onPressed: onThemeCycle,
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
visualDensity: VisualDensity.compact,
|
||||
side: BorderSide(color: themeColor.withValues(alpha: 0.5)),
|
||||
),
|
||||
child: Text(
|
||||
themeLabel,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: themeColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSpeedButton(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 28,
|
||||
child: OutlinedButton(
|
||||
onPressed: onSpeedCycle,
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
child: Text(
|
||||
'${speedMultiplier}x',
|
||||
style: TextStyle(
|
||||
fontWeight:
|
||||
speedMultiplier > 1 ? FontWeight.bold : FontWeight.normal,
|
||||
color: speedMultiplier > 1
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProgressBar(BuildContext context) {
|
||||
final progressValue = progress.task.max > 0
|
||||
? (progress.task.position / progress.task.max).clamp(0.0, 1.0)
|
||||
: 0.0;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
child: LinearProgressIndicator(
|
||||
value: progressValue,
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.primary.withValues(alpha: 0.2),
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
minHeight: 12,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user