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,136 @@
/// 물약 종류
enum PotionType {
/// HP 회복 물약
hp,
/// MP 회복 물약
mp,
}
/// 물약 아이템
///
/// 전투 중 사용 가능한 소모품.
/// 전투당 종류별 1회만 사용 가능.
class Potion {
const Potion({
required this.id,
required this.name,
required this.type,
required this.tier,
this.healAmount = 0,
this.healPercent = 0.0,
this.price = 0,
});
/// 물약 ID
final String id;
/// 물약 이름
final String name;
/// 물약 종류 (hp / mp)
final PotionType type;
/// 물약 티어 (1~5, 높을수록 강력)
final int tier;
/// 고정 회복량
final int healAmount;
/// 비율 회복량 (0.0 ~ 1.0)
final double healPercent;
/// 구매 가격 (골드)
final int price;
/// HP 물약 여부
bool get isHpPotion => type == PotionType.hp;
/// MP 물약 여부
bool get isMpPotion => type == PotionType.mp;
/// 실제 회복량 계산
///
/// [maxValue] 최대 HP 또는 MP
int calculateHeal(int maxValue) {
final percentHeal = (maxValue * healPercent).round();
return healAmount + percentHeal;
}
}
/// 물약 인벤토리 상태
///
/// 보유 물약 수량 및 전투 중 사용 기록 관리
class PotionInventory {
const PotionInventory({
this.potions = const {},
this.usedInBattle = const {},
});
/// 보유 물약 (물약 ID → 수량)
final Map<String, int> potions;
/// 현재 전투에서 사용한 물약 종류
final Set<PotionType> usedInBattle;
/// 물약 보유 여부
bool hasPotion(String potionId) => (potions[potionId] ?? 0) > 0;
/// 물약 수량 조회
int getQuantity(String potionId) => potions[potionId] ?? 0;
/// 특정 종류 물약 사용 가능 여부
///
/// 전투당 종류별 1회 제한 체크
bool canUseType(PotionType type) => !usedInBattle.contains(type);
/// 물약 추가
PotionInventory addPotion(String potionId, [int count = 1]) {
final newPotions = Map<String, int>.from(potions);
newPotions[potionId] = (newPotions[potionId] ?? 0) + count;
return PotionInventory(
potions: newPotions,
usedInBattle: usedInBattle,
);
}
/// 물약 사용 (수량 감소)
PotionInventory usePotion(String potionId, PotionType type) {
final currentQty = potions[potionId] ?? 0;
if (currentQty <= 0) return this;
final newPotions = Map<String, int>.from(potions);
newPotions[potionId] = currentQty - 1;
if (newPotions[potionId] == 0) {
newPotions.remove(potionId);
}
final newUsed = Set<PotionType>.from(usedInBattle)..add(type);
return PotionInventory(
potions: newPotions,
usedInBattle: newUsed,
);
}
/// 전투 종료 시 사용 기록 초기화
PotionInventory resetBattleUsage() {
return PotionInventory(
potions: potions,
usedInBattle: const {},
);
}
/// 빈 인벤토리
static const empty = PotionInventory();
PotionInventory copyWith({
Map<String, int>? potions,
Set<PotionType>? usedInBattle,
}) {
return PotionInventory(
potions: potions ?? this.potions,
usedInBattle: usedInBattle ?? this.usedInBattle,
);
}
}