feat(balance): CHA(매력) 스탯 활성화 — 상점 할인 + 드롭률 보정
- shop_service: CHA 기반 구매 할인 (CHA-10)×1%, 최대 15% - item_service: CHA 기반 희귀 아이템 드롭률 보정 (CHA-10)×0.5% - 호출부(game_mutations, market_service, resurrection_service)에 CHA 전달 - 기존 데드 스탯이었던 CHA가 게임 로직에 반영됨
This commit is contained in:
@@ -26,6 +26,7 @@ class GameMutations {
|
|||||||
name: name,
|
name: name,
|
||||||
slot: slot,
|
slot: slot,
|
||||||
level: level,
|
level: level,
|
||||||
|
cha: state.stats.cha,
|
||||||
);
|
);
|
||||||
|
|
||||||
final updatedEquip = state.equipment
|
final updatedEquip = state.equipment
|
||||||
|
|||||||
@@ -60,23 +60,34 @@ class ItemService {
|
|||||||
// 희귀도 결정
|
// 희귀도 결정
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
/// 희귀도 결정 (고정 확률)
|
/// 희귀도 결정 (고정 확률 + CHA 보정)
|
||||||
///
|
///
|
||||||
/// 확률 분포:
|
/// 기본 확률 분포:
|
||||||
/// - Common: 34%
|
/// - Common: 34%
|
||||||
/// - Uncommon: 40%
|
/// - Uncommon: 40%
|
||||||
/// - Rare: 20%
|
/// - Rare: 20%
|
||||||
/// - Epic: 5%
|
/// - Epic: 5%
|
||||||
/// - Legendary: 1%
|
/// - 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);
|
final roll = rng.nextInt(100);
|
||||||
|
|
||||||
// Legendary: 0-0 (1%)
|
// CHA 보정: (CHA - 10) * 0.5, 0~10 범위
|
||||||
if (roll < 1) return ItemRarity.legendary;
|
final chaBonus = ((cha - 10) * 0.5).clamp(0.0, 10.0);
|
||||||
// Epic: 1-5 (5%)
|
|
||||||
if (roll < 6) return ItemRarity.epic;
|
// 보정된 임계값 (chaBonus만큼 희귀 쪽으로 이동)
|
||||||
// Rare: 6-25 (20%)
|
final legendaryThreshold = 1.0 + chaBonus * 0.1; // 최대 2%
|
||||||
if (roll < 26) return ItemRarity.rare;
|
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%)
|
// Uncommon: 26-65 (40%)
|
||||||
if (roll < 66) return ItemRarity.uncommon;
|
if (roll < 66) return ItemRarity.uncommon;
|
||||||
// Common: 66-99 (34%)
|
// Common: 66-99 (34%)
|
||||||
@@ -331,8 +342,9 @@ class ItemService {
|
|||||||
required EquipmentSlot slot,
|
required EquipmentSlot slot,
|
||||||
required int level,
|
required int level,
|
||||||
ItemRarity? rarity,
|
ItemRarity? rarity,
|
||||||
|
int cha = 0,
|
||||||
}) {
|
}) {
|
||||||
final itemRarity = rarity ?? determineRarity(level);
|
final itemRarity = rarity ?? determineRarity(level, cha: cha);
|
||||||
final stats = generateItemStats(
|
final stats = generateItemStats(
|
||||||
level: level,
|
level: level,
|
||||||
rarity: itemRarity,
|
rarity: itemRarity,
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class MarketService {
|
|||||||
slot: slot,
|
slot: slot,
|
||||||
targetRarity: ItemRarity.common,
|
targetRarity: ItemRarity.common,
|
||||||
);
|
);
|
||||||
final price = shopService.calculateBuyPrice(item);
|
final price = shopService.calculateBuyPrice(item, cha: state.stats.cha);
|
||||||
|
|
||||||
if (nextState.inventory.gold >= price) {
|
if (nextState.inventory.gold >= price) {
|
||||||
nextState = nextState.copyWith(
|
nextState = nextState.copyWith(
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ class ResurrectionService {
|
|||||||
playerLevel: state.traits.level,
|
playerLevel: state.traits.level,
|
||||||
currentGold: state.inventory.gold,
|
currentGold: state.inventory.gold,
|
||||||
currentEquipment: state.equipment,
|
currentEquipment: state.equipment,
|
||||||
|
cha: state.stats.cha,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 장비 적용
|
// 장비 적용
|
||||||
|
|||||||
@@ -18,10 +18,21 @@ class ShopService {
|
|||||||
|
|
||||||
/// 장비 구매 가격 계산
|
/// 장비 구매 가격 계산
|
||||||
///
|
///
|
||||||
/// 가격 = 아이템 레벨 * 50 * 희귀도 배율
|
/// 가격 = 아이템 레벨 * 50 * 희귀도 배율 * (1 - CHA 할인율)
|
||||||
int calculateBuyPrice(EquipmentItem item) {
|
/// CHA 할인율(charisma discount): (CHA - 10) * 1%, 최대 15%, 최소 0%
|
||||||
|
int calculateBuyPrice(EquipmentItem item, {int cha = 0}) {
|
||||||
if (item.isEmpty) return 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 playerLevel,
|
||||||
required int currentGold,
|
required int currentGold,
|
||||||
required Equipment currentEquipment,
|
required Equipment currentEquipment,
|
||||||
|
int cha = 0,
|
||||||
}) {
|
}) {
|
||||||
var remainingGold = currentGold;
|
var remainingGold = currentGold;
|
||||||
final purchasedItems = <EquipmentItem>[];
|
final purchasedItems = <EquipmentItem>[];
|
||||||
@@ -230,7 +242,7 @@ class ShopService {
|
|||||||
targetRarity: ItemRarity.common, // 부활 시 Common만 구매
|
targetRarity: ItemRarity.common, // 부활 시 Common만 구매
|
||||||
);
|
);
|
||||||
|
|
||||||
final price = calculateBuyPrice(shopItem);
|
final price = calculateBuyPrice(shopItem, cha: cha);
|
||||||
if (price <= remainingGold) {
|
if (price <= remainingGold) {
|
||||||
remainingGold -= price;
|
remainingGold -= price;
|
||||||
purchasedItems.add(shopItem);
|
purchasedItems.add(shopItem);
|
||||||
@@ -254,6 +266,7 @@ class ShopService {
|
|||||||
required int currentGold,
|
required int currentGold,
|
||||||
required EquipmentSlot slot,
|
required EquipmentSlot slot,
|
||||||
ItemRarity? preferredRarity,
|
ItemRarity? preferredRarity,
|
||||||
|
int cha = 0,
|
||||||
}) {
|
}) {
|
||||||
final item = generateShopItem(
|
final item = generateShopItem(
|
||||||
playerLevel: playerLevel,
|
playerLevel: playerLevel,
|
||||||
@@ -261,7 +274,7 @@ class ShopService {
|
|||||||
targetRarity: preferredRarity,
|
targetRarity: preferredRarity,
|
||||||
);
|
);
|
||||||
|
|
||||||
final price = calculateBuyPrice(item);
|
final price = calculateBuyPrice(item, cha: cha);
|
||||||
if (price > currentGold) return null;
|
if (price > currentGold) return null;
|
||||||
|
|
||||||
return PurchaseResult(
|
return PurchaseResult(
|
||||||
|
|||||||
Reference in New Issue
Block a user