From 9e5472728f6c6f5c878d717636486f42f8d27233 Mon Sep 17 00:00:00 2001 From: JiWoong Sul Date: Fri, 16 Jan 2026 00:15:38 +0900 Subject: [PATCH] =?UTF-8?q?refactor(potion):=20=EB=AC=BC=EC=95=BD=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=20=EC=82=AC=EC=9A=A9=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 임계치 기반 → 소모량 기반 조건 전환 - HP/MP 소모량 >= 물약 회복량일 때 사용 - emergencyHpThreshold, emergencyMpThreshold 상수 제거 - 우선순위 HP > MP 유지 --- lib/src/core/engine/combat_tick_service.dart | 114 +++++++++---------- lib/src/core/engine/potion_service.dart | 40 ++++--- 2 files changed, 76 insertions(+), 78 deletions(-) diff --git a/lib/src/core/engine/combat_tick_service.dart b/lib/src/core/engine/combat_tick_service.dart index 156a007..afbb5c3 100644 --- a/lib/src/core/engine/combat_tick_service.dart +++ b/lib/src/core/engine/combat_tick_service.dart @@ -265,79 +265,73 @@ class CombatTickService { return null; } - // 우선순위 1: HP 물약 (HP <= 30%) - final hpRatio = playerStats.hpCurrent / playerStats.hpMax; - if (hpRatio <= PotionService.emergencyHpThreshold) { - final hpPotion = potionService.selectEmergencyHpPotion( + // 우선순위 1: HP 물약 (소모된 HP >= 물약 회복량) + final hpPotion = potionService.selectEmergencyHpPotion( + currentHp: playerStats.hpCurrent, + maxHp: playerStats.hpMax, + inventory: potionInventory, + playerLevel: playerLevel, + ); + + if (hpPotion != null) { + final result = potionService.usePotion( + potionId: hpPotion.id, + inventory: potionInventory, currentHp: playerStats.hpCurrent, maxHp: playerStats.hpMax, - inventory: potionInventory, - playerLevel: playerLevel, + currentMp: playerStats.mpCurrent, + maxMp: playerStats.mpMax, ); - if (hpPotion != null) { - final result = potionService.usePotion( - potionId: hpPotion.id, - inventory: potionInventory, - currentHp: playerStats.hpCurrent, - maxHp: playerStats.hpMax, - currentMp: playerStats.mpCurrent, - maxMp: playerStats.mpMax, + if (result.success) { + return ( + playerStats: playerStats.copyWith(hpCurrent: result.newHp), + lastPotionUsedMs: timestamp, + potionInventory: result.newInventory!, + events: [ + CombatEvent.playerPotion( + timestamp: timestamp, + potionName: hpPotion.name, + healAmount: result.healedAmount, + isHp: true, + ), + ], ); - - if (result.success) { - return ( - playerStats: playerStats.copyWith(hpCurrent: result.newHp), - lastPotionUsedMs: timestamp, - potionInventory: result.newInventory!, - events: [ - CombatEvent.playerPotion( - timestamp: timestamp, - potionName: hpPotion.name, - healAmount: result.healedAmount, - isHp: true, - ), - ], - ); - } } } - // 우선순위 2: MP 물약 (MP <= 50%) - final mpRatio = playerStats.mpCurrent / playerStats.mpMax; - if (mpRatio <= PotionService.emergencyMpThreshold) { - final mpPotion = potionService.selectEmergencyMpPotion( + // 우선순위 2: MP 물약 (소모된 MP >= 물약 회복량) + final mpPotion = potionService.selectEmergencyMpPotion( + currentMp: playerStats.mpCurrent, + maxMp: playerStats.mpMax, + inventory: potionInventory, + playerLevel: playerLevel, + ); + + if (mpPotion != null) { + final result = potionService.usePotion( + potionId: mpPotion.id, + inventory: potionInventory, + currentHp: playerStats.hpCurrent, + maxHp: playerStats.hpMax, currentMp: playerStats.mpCurrent, maxMp: playerStats.mpMax, - inventory: potionInventory, - playerLevel: playerLevel, ); - if (mpPotion != null) { - final result = potionService.usePotion( - potionId: mpPotion.id, - inventory: potionInventory, - currentHp: playerStats.hpCurrent, - maxHp: playerStats.hpMax, - currentMp: playerStats.mpCurrent, - maxMp: playerStats.mpMax, + if (result.success) { + return ( + playerStats: playerStats.copyWith(mpCurrent: result.newMp), + lastPotionUsedMs: timestamp, + potionInventory: result.newInventory!, + events: [ + CombatEvent.playerPotion( + timestamp: timestamp, + potionName: mpPotion.name, + healAmount: result.healedAmount, + isHp: false, + ), + ], ); - - if (result.success) { - return ( - playerStats: playerStats.copyWith(mpCurrent: result.newMp), - lastPotionUsedMs: timestamp, - potionInventory: result.newInventory!, - events: [ - CombatEvent.playerPotion( - timestamp: timestamp, - potionName: mpPotion.name, - healAmount: result.healedAmount, - isHp: false, - ), - ], - ); - } } } diff --git a/lib/src/core/engine/potion_service.dart b/lib/src/core/engine/potion_service.dart index 119beb2..3069fb9 100644 --- a/lib/src/core/engine/potion_service.dart +++ b/lib/src/core/engine/potion_service.dart @@ -13,12 +13,6 @@ class PotionService { /// 글로벌 물약 쿨타임 (1배속 기준 3초) static const int globalPotionCooldownMs = 3000; - /// 긴급 물약 사용 HP 임계치 (30%) - static const double emergencyHpThreshold = 0.30; - - /// 긴급 물약 사용 MP 임계치 (50%) - static const double emergencyMpThreshold = 0.50; - // ============================================================================ // 물약 사용 가능 여부 // ============================================================================ @@ -104,7 +98,7 @@ class PotionService { /// 긴급 HP 물약 선택 /// - /// HP가 임계치 이하일 때 사용할 최적의 물약 선택 + /// 소모된 HP >= 물약 회복량이면 사용할 최적의 물약 선택 /// [currentHp] 현재 HP /// [maxHp] 최대 HP /// [inventory] 물약 인벤토리 @@ -115,9 +109,8 @@ class PotionService { required PotionInventory inventory, required int playerLevel, }) { - // 임계치 체크 - final hpRatio = currentHp / maxHp; - if (hpRatio > emergencyHpThreshold) return null; + final hpLost = maxHp - currentHp; + if (hpLost <= 0) return null; // 적정 티어 계산 final targetTier = PotionData.tierForLevel(playerLevel); @@ -126,7 +119,10 @@ class PotionService { for (var tier = targetTier; tier >= 1; tier--) { final potion = PotionData.getHpPotionByTier(tier); if (potion != null && inventory.hasPotion(potion.id)) { - return potion; + final healAmount = potion.calculateHeal(maxHp); + if (hpLost >= healAmount) { + return potion; + } } } @@ -134,7 +130,10 @@ class PotionService { for (var tier = targetTier + 1; tier <= 5; tier++) { final potion = PotionData.getHpPotionByTier(tier); if (potion != null && inventory.hasPotion(potion.id)) { - return potion; + final healAmount = potion.calculateHeal(maxHp); + if (hpLost >= healAmount) { + return potion; + } } } @@ -143,16 +142,15 @@ class PotionService { /// 긴급 MP 물약 선택 /// - /// MP가 임계치 이하일 때 사용할 최적의 물약 선택 + /// 소모된 MP >= 물약 회복량이면 사용할 최적의 물약 선택 Potion? selectEmergencyMpPotion({ required int currentMp, required int maxMp, required PotionInventory inventory, required int playerLevel, }) { - // 임계치 체크 - final mpRatio = currentMp / maxMp; - if (mpRatio > emergencyMpThreshold) return null; + final mpLost = maxMp - currentMp; + if (mpLost <= 0) return null; // 적정 티어 계산 final targetTier = PotionData.tierForLevel(playerLevel); @@ -161,7 +159,10 @@ class PotionService { for (var tier = targetTier; tier >= 1; tier--) { final potion = PotionData.getMpPotionByTier(tier); if (potion != null && inventory.hasPotion(potion.id)) { - return potion; + final healAmount = potion.calculateHeal(maxMp); + if (mpLost >= healAmount) { + return potion; + } } } @@ -169,7 +170,10 @@ class PotionService { for (var tier = targetTier + 1; tier <= 5; tier++) { final potion = PotionData.getMpPotionByTier(tier); if (potion != null && inventory.hasPotion(potion.id)) { - return potion; + final healAmount = potion.calculateHeal(maxMp); + if (mpLost >= healAmount) { + return potion; + } } }