feat(game): 포션 시스템 및 UI 패널 추가
- 포션 시스템 구현 (PotionService, Potion 모델) - 포션 인벤토리 패널 위젯 - 활성 버프 패널 위젯 - 장비 스탯 패널 위젯 - 스킬 시스템 확장 - 일본어 번역 추가 - 전투 이벤트/상태 모델 개선
This commit is contained in:
206
lib/src/features/game/widgets/active_buff_panel.dart
Normal file
206
lib/src/features/game/widgets/active_buff_panel.dart
Normal file
@@ -0,0 +1,206 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:askiineverdie/src/core/model/skill.dart';
|
||||
|
||||
/// 활성 버프 패널 위젯
|
||||
///
|
||||
/// 현재 적용 중인 버프 목록과 남은 시간을 표시.
|
||||
class ActiveBuffPanel extends StatelessWidget {
|
||||
const ActiveBuffPanel({
|
||||
super.key,
|
||||
required this.activeBuffs,
|
||||
required this.currentMs,
|
||||
});
|
||||
|
||||
final List<ActiveBuff> activeBuffs;
|
||||
final int currentMs;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (activeBuffs.isEmpty) {
|
||||
return const Center(
|
||||
child: Text(
|
||||
'No active buffs',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Colors.grey,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: activeBuffs.length,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
itemBuilder: (context, index) {
|
||||
final buff = activeBuffs[index];
|
||||
return _BuffRow(buff: buff, currentMs: currentMs);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 개별 버프 행 위젯
|
||||
class _BuffRow extends StatelessWidget {
|
||||
const _BuffRow({
|
||||
required this.buff,
|
||||
required this.currentMs,
|
||||
});
|
||||
|
||||
final ActiveBuff buff;
|
||||
final int currentMs;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final remainingMs = buff.remainingDuration(currentMs);
|
||||
final remainingSec = (remainingMs / 1000).toStringAsFixed(1);
|
||||
final progress = remainingMs / buff.effect.durationMs;
|
||||
final modifiers = _buildModifierList();
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
// 버프 아이콘
|
||||
const Icon(
|
||||
Icons.trending_up,
|
||||
size: 14,
|
||||
color: Colors.lightBlue,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
|
||||
// 버프 이름
|
||||
Expanded(
|
||||
child: Text(
|
||||
buff.effect.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.lightBlue,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
|
||||
// 남은 시간
|
||||
Text(
|
||||
'${remainingSec}s',
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: remainingMs < 3000 ? Colors.orange : Colors.grey,
|
||||
fontWeight:
|
||||
remainingMs < 3000 ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 2),
|
||||
|
||||
// 남은 시간 프로그레스 바
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
child: LinearProgressIndicator(
|
||||
value: progress.clamp(0.0, 1.0),
|
||||
minHeight: 3,
|
||||
backgroundColor: Colors.grey.shade800,
|
||||
valueColor: AlwaysStoppedAnimation(
|
||||
progress > 0.3 ? Colors.lightBlue : Colors.orange,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 효과 목록
|
||||
if (modifiers.isNotEmpty) ...[
|
||||
const SizedBox(height: 2),
|
||||
Wrap(
|
||||
spacing: 6,
|
||||
runSpacing: 2,
|
||||
children: modifiers,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 버프 효과 목록 생성
|
||||
List<Widget> _buildModifierList() {
|
||||
final modifiers = <Widget>[];
|
||||
final effect = buff.effect;
|
||||
|
||||
if (effect.atkModifier != 0) {
|
||||
modifiers.add(_ModifierChip(
|
||||
label: 'ATK',
|
||||
value: effect.atkModifier,
|
||||
isPositive: effect.atkModifier > 0,
|
||||
));
|
||||
}
|
||||
|
||||
if (effect.defModifier != 0) {
|
||||
modifiers.add(_ModifierChip(
|
||||
label: 'DEF',
|
||||
value: effect.defModifier,
|
||||
isPositive: effect.defModifier > 0,
|
||||
));
|
||||
}
|
||||
|
||||
if (effect.criRateModifier != 0) {
|
||||
modifiers.add(_ModifierChip(
|
||||
label: 'CRI',
|
||||
value: effect.criRateModifier,
|
||||
isPositive: effect.criRateModifier > 0,
|
||||
));
|
||||
}
|
||||
|
||||
if (effect.evasionModifier != 0) {
|
||||
modifiers.add(_ModifierChip(
|
||||
label: 'EVA',
|
||||
value: effect.evasionModifier,
|
||||
isPositive: effect.evasionModifier > 0,
|
||||
));
|
||||
}
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
}
|
||||
|
||||
/// 효과 칩 위젯
|
||||
class _ModifierChip extends StatelessWidget {
|
||||
const _ModifierChip({
|
||||
required this.label,
|
||||
required this.value,
|
||||
required this.isPositive,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final double value;
|
||||
final bool isPositive;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final color = isPositive ? Colors.green : Colors.red;
|
||||
final sign = isPositive ? '+' : '';
|
||||
final percent = (value * 100).round();
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withValues(alpha: 0.2),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
child: Text(
|
||||
'$label: $sign$percent%',
|
||||
style: TextStyle(
|
||||
fontSize: 8,
|
||||
color: color,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user