From 69e7695cb7707861a5745471e37e7065185e8e61 Mon Sep 17 00:00:00 2001 From: JiWoong Sul Date: Tue, 31 Mar 2026 00:12:52 +0900 Subject: [PATCH] =?UTF-8?q?feat(balance):=20CHA(=EB=A7=A4=EB=A0=A5)=20?= =?UTF-8?q?=EC=8A=A4=ED=83=AF=20=ED=99=9C=EC=84=B1=ED=99=94=20=E2=80=94=20?= =?UTF-8?q?=EC=83=81=EC=A0=90=20=ED=95=A0=EC=9D=B8=20+=20=EB=93=9C?= =?UTF-8?q?=EB=A1=AD=EB=A5=A0=20=EB=B3=B4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - shop_service: CHA 기반 구매 할인 (CHA-10)×1%, 최대 15% - item_service: CHA 기반 희귀 아이템 드롭률 보정 (CHA-10)×0.5% - 호출부(game_mutations, market_service, resurrection_service)에 CHA 전달 - 기존 데드 스탯이었던 CHA가 게임 로직에 반영됨 --- lib/src/core/engine/game_mutations.dart | 1 + lib/src/core/engine/item_service.dart | 32 +++++++++++++------ lib/src/core/engine/market_service.dart | 2 +- lib/src/core/engine/resurrection_service.dart | 1 + lib/src/core/engine/shop_service.dart | 23 ++++++++++--- 5 files changed, 43 insertions(+), 16 deletions(-) diff --git a/lib/src/core/engine/game_mutations.dart b/lib/src/core/engine/game_mutations.dart index e7a7756..4951aeb 100644 --- a/lib/src/core/engine/game_mutations.dart +++ b/lib/src/core/engine/game_mutations.dart @@ -26,6 +26,7 @@ class GameMutations { name: name, slot: slot, level: level, + cha: state.stats.cha, ); final updatedEquip = state.equipment diff --git a/lib/src/core/engine/item_service.dart b/lib/src/core/engine/item_service.dart index 5eb2444..7f4585b 100644 --- a/lib/src/core/engine/item_service.dart +++ b/lib/src/core/engine/item_service.dart @@ -60,23 +60,34 @@ class ItemService { // 희귀도 결정 // ============================================================================ - /// 희귀도 결정 (고정 확률) + /// 희귀도 결정 (고정 확률 + CHA 보정) /// - /// 확률 분포: + /// 기본 확률 분포: /// - Common: 34% /// - Uncommon: 40% /// - Rare: 20% /// - Epic: 5% /// - Legendary: 1% - ItemRarity determineRarity(int level) { + /// + /// CHA 보정(charisma bonus): (CHA - 10) * 0.5% 추가 희귀 확률. + /// 보정값만큼 Common 확률이 줄고, Rare 이상 확률이 증가. + ItemRarity determineRarity(int level, {int cha = 0}) { final roll = rng.nextInt(100); - // Legendary: 0-0 (1%) - if (roll < 1) return ItemRarity.legendary; - // Epic: 1-5 (5%) - if (roll < 6) return ItemRarity.epic; - // Rare: 6-25 (20%) - if (roll < 26) return ItemRarity.rare; + // CHA 보정: (CHA - 10) * 0.5, 0~10 범위 + final chaBonus = ((cha - 10) * 0.5).clamp(0.0, 10.0); + + // 보정된 임계값 (chaBonus만큼 희귀 쪽으로 이동) + final legendaryThreshold = 1.0 + chaBonus * 0.1; // 최대 2% + final epicThreshold = 6.0 + chaBonus * 0.3; // 최대 9% + final rareThreshold = 26.0 + chaBonus * 0.6; // 최대 32% + + // Legendary + if (roll < legendaryThreshold) return ItemRarity.legendary; + // Epic + if (roll < epicThreshold) return ItemRarity.epic; + // Rare + if (roll < rareThreshold) return ItemRarity.rare; // Uncommon: 26-65 (40%) if (roll < 66) return ItemRarity.uncommon; // Common: 66-99 (34%) @@ -331,8 +342,9 @@ class ItemService { required EquipmentSlot slot, required int level, ItemRarity? rarity, + int cha = 0, }) { - final itemRarity = rarity ?? determineRarity(level); + final itemRarity = rarity ?? determineRarity(level, cha: cha); final stats = generateItemStats( level: level, rarity: itemRarity, diff --git a/lib/src/core/engine/market_service.dart b/lib/src/core/engine/market_service.dart index 323b4ed..3fd56f4 100644 --- a/lib/src/core/engine/market_service.dart +++ b/lib/src/core/engine/market_service.dart @@ -56,7 +56,7 @@ class MarketService { slot: slot, targetRarity: ItemRarity.common, ); - final price = shopService.calculateBuyPrice(item); + final price = shopService.calculateBuyPrice(item, cha: state.stats.cha); if (nextState.inventory.gold >= price) { nextState = nextState.copyWith( diff --git a/lib/src/core/engine/resurrection_service.dart b/lib/src/core/engine/resurrection_service.dart index 290b88e..887b347 100644 --- a/lib/src/core/engine/resurrection_service.dart +++ b/lib/src/core/engine/resurrection_service.dart @@ -105,6 +105,7 @@ class ResurrectionService { playerLevel: state.traits.level, currentGold: state.inventory.gold, currentEquipment: state.equipment, + cha: state.stats.cha, ); // 장비 적용 diff --git a/lib/src/core/engine/shop_service.dart b/lib/src/core/engine/shop_service.dart index 7aa9ed5..b134bfb 100644 --- a/lib/src/core/engine/shop_service.dart +++ b/lib/src/core/engine/shop_service.dart @@ -18,10 +18,21 @@ class ShopService { /// 장비 구매 가격 계산 /// - /// 가격 = 아이템 레벨 * 50 * 희귀도 배율 - int calculateBuyPrice(EquipmentItem item) { + /// 가격 = 아이템 레벨 * 50 * 희귀도 배율 * (1 - CHA 할인율) + /// CHA 할인율(charisma discount): (CHA - 10) * 1%, 최대 15%, 최소 0% + int calculateBuyPrice(EquipmentItem item, {int cha = 0}) { if (item.isEmpty) return 0; - return (item.level * 50 * _getRarityPriceMultiplier(item.rarity)).round(); + final basePrice = (item.level * 50 * _getRarityPriceMultiplier(item.rarity)) + .round(); + final discount = chaDiscount(cha); + return (basePrice * (1.0 - discount)).round(); + } + + /// CHA 기반 할인율 계산 + /// + /// (CHA - 10) * 0.01, 0~0.15 범위로 클램프 + static double chaDiscount(int cha) { + return ((cha - 10) * 0.01).clamp(0.0, 0.15); } /// 장비 판매 가격 계산 @@ -211,6 +222,7 @@ class ShopService { required int playerLevel, required int currentGold, required Equipment currentEquipment, + int cha = 0, }) { var remainingGold = currentGold; final purchasedItems = []; @@ -230,7 +242,7 @@ class ShopService { targetRarity: ItemRarity.common, // 부활 시 Common만 구매 ); - final price = calculateBuyPrice(shopItem); + final price = calculateBuyPrice(shopItem, cha: cha); if (price <= remainingGold) { remainingGold -= price; purchasedItems.add(shopItem); @@ -254,6 +266,7 @@ class ShopService { required int currentGold, required EquipmentSlot slot, ItemRarity? preferredRarity, + int cha = 0, }) { final item = generateShopItem( playerLevel: playerLevel, @@ -261,7 +274,7 @@ class ShopService { targetRarity: preferredRarity, ); - final price = calculateBuyPrice(item); + final price = calculateBuyPrice(item, cha: cha); if (price > currentGold) return null; return PurchaseResult(