feat(game): 포션 시스템 및 UI 패널 추가

- 포션 시스템 구현 (PotionService, Potion 모델)
- 포션 인벤토리 패널 위젯
- 활성 버프 패널 위젯
- 장비 스탯 패널 위젯
- 스킬 시스템 확장
- 일본어 번역 추가
- 전투 이벤트/상태 모델 개선
This commit is contained in:
JiWoong Sul
2025-12-21 23:53:27 +09:00
parent eb71d2a199
commit 7cd8be88df
25 changed files with 5174 additions and 261 deletions

View 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,
),
),
);
}
}