diff --git a/lib/src/core/engine/progress_service.dart b/lib/src/core/engine/progress_service.dart index ed918e4..41354c2 100644 --- a/lib/src/core/engine/progress_service.dart +++ b/lib/src/core/engine/progress_service.dart @@ -104,7 +104,8 @@ class ProgressService { ); // ExpBar 초기화 (원본 743-746줄) - final expBar = ProgressBarState(position: 0, max: pq_logic.levelUpTime(1)); + final expBar = + ProgressBarState(position: 0, max: ExpConstants.requiredExp(1)); // PlotBar 초기화 - Prologue 5분 (300초) final plotBar = const ProgressBarState(position: 0, max: 300); @@ -253,6 +254,10 @@ class ProgressService { final gain = progress.currentTask.type == TaskType.kill; final incrementSeconds = progress.task.max ~/ 1000; + // 몬스터 경험치 미리 저장 (currentCombat이 null되기 전) + final int monsterExpReward = + progress.currentCombat?.monsterStats.expReward ?? 0; + // 킬 태스크 완료 시 전투 결과 반영 및 전리품 획득 if (gain) { // 전투 결과에 따라 플레이어 HP 업데이트 + 전투 후 회복 @@ -354,19 +359,26 @@ class ProgressService { } } - // Gain XP / level up. + // Gain XP / level up (몬스터 경험치 기반) // 최대 레벨(100) 제한: 100레벨에서는 더 이상 레벨업하지 않음 - if (gain) { - if (progress.exp.position >= progress.exp.max && - nextState.traits.level < 100) { + if (gain && nextState.traits.level < 100 && monsterExpReward > 0) { + final newExpPos = progress.exp.position + monsterExpReward; + + // 레벨업 체크 (경험치가 필요량 이상일 때) + if (newExpPos >= progress.exp.max) { + // 초과 경험치 계산 + final overflowExp = newExpPos - progress.exp.max; nextState = _levelUp(nextState); leveledUp = true; progress = nextState.progress; - } else if (nextState.traits.level < 100) { - final uncappedExp = progress.exp.position + incrementSeconds; - final int newExpPos = uncappedExp > progress.exp.max - ? progress.exp.max - : uncappedExp; + + // 초과 경험치를 다음 레벨에 적용 + if (overflowExp > 0 && nextState.traits.level < 100) { + progress = progress.copyWith( + exp: progress.exp.copyWith(position: overflowExp), + ); + } + } else { progress = progress.copyWith( exp: progress.exp.copyWith(position: newExpPos), ); @@ -870,15 +882,18 @@ class ProgressService { nextState = _levelUp(nextState); } - final progress = nextState.progress.copyWith( + // 태스크 바 완료 처리 + var progress = nextState.progress.copyWith( task: nextState.progress.task.copyWith( position: nextState.progress.task.max, ), - plot: nextState.progress.plot.copyWith( - position: nextState.progress.plot.max, - ), ); - return nextState.copyWith(progress: progress); + nextState = nextState.copyWith(progress: progress); + + // 디버그 모드에서는 completeAct 직접 호출하여 plotStageCount 즉시 업데이트 + // 시네마틱은 생략하고 바로 다음 Act로 진입 + final actResult = completeAct(nextState); + return actResult.state; } GameState _applyReward(GameState state, pq_logic.RewardKind reward) { @@ -912,7 +927,7 @@ class ProgressService { final expBar = ProgressBarState( position: 0, - max: pq_logic.levelUpTime(nextLevel), + max: ExpConstants.requiredExp(nextLevel), ); final progress = nextState.progress.copyWith(exp: expBar); nextState = nextState.copyWith(progress: progress); diff --git a/lib/src/core/util/balance_constants.dart b/lib/src/core/util/balance_constants.dart index b91a07a..365f0e8 100644 --- a/lib/src/core/util/balance_constants.dart +++ b/lib/src/core/util/balance_constants.dart @@ -7,36 +7,27 @@ library; class ExpConstants { ExpConstants._(); - /// 기본 경험치 값 - static const int baseExp = 100; - - /// 레벨 구간별 경험치 증가율 (tiered growth rate) - /// - 1-30: 1.10 (초반 빠른 진행) - /// - 31-60: 1.12 (중반 적정 속도) - /// - 61-100: 1.14 (후반 도전) - static double _getGrowthRate(int level) { - if (level <= 30) return 1.10; - if (level <= 60) return 1.12; - return 1.14; - } - - /// 레벨업에 필요한 경험치 계산 (구간별 차등 적용) + /// 레벨업에 필요한 경험치 계산 (몬스터 기반) /// - /// 조정 후 예상: - /// 레벨 10: ~259 exp - /// 레벨 30: ~1,744 exp - /// 레벨 50: ~9,705 exp - /// 레벨 80: ~133,860 exp - /// 레벨 100: ~636,840 exp + /// 공식: (10 + level * 5) * (25 + level / 3) + /// - 몬스터 경험치와 동기화 (MonsterBaseStats.exp = 10 + level * 5) + /// - 레벨당 약 25~58마리 처치 필요 + /// + /// 예상: + /// 레벨 1: 15 * 25 = 375 exp (~25마리) + /// 레벨 10: 60 * 28 = 1,680 exp (~28마리) + /// 레벨 30: 160 * 35 = 5,600 exp (~35마리) + /// 레벨 50: 260 * 42 = 10,920 exp (~42마리) + /// 레벨 80: 410 * 52 = 21,320 exp (~52마리) + /// 레벨 100: 510 * 58 = 29,580 exp (~58마리) static int requiredExp(int level) { - if (level <= 0) return baseExp; + if (level <= 0) return 375; - // 구간별 복합 성장 계산 - double result = baseExp.toDouble(); - for (int i = 1; i <= level; i++) { - result *= _getGrowthRate(i); - } - return result.round(); + // 해당 레벨 몬스터 경험치 = 10 + level * 5 + final monsterExp = 10 + level * 5; + // 필요 킬 수 = 25 + level / 3 (레벨이 올라갈수록 약간 더 많이) + final killsRequired = 25 + level ~/ 3; + return monsterExp * killsRequired; } /// 총 누적 경험치 계산 (특정 레벨까지) @@ -159,14 +150,16 @@ class MonsterBaseStats { /// 레벨 기반 기본 스탯 생성 /// /// HP: 50 + level * 20 + (level^2 / 5) - /// ATK: 5 + level * 4 (플레이어 DEF 스케일링에 맞춰 상향) + /// ATK: 10 + level * 12 (장비 DEF 스케일링 대응) + /// - 장비 DEF ≈ level * 16 (9개 방어구 합산) + /// - 데미지 공식: ATK - DEF * 0.5 → 의미있는 피해를 위해 상향 /// DEF: 2 + level * 2 /// EXP: 10 + level * 5 /// GOLD: 5 + level * 3 factory MonsterBaseStats.forLevel(int level) { return MonsterBaseStats( hp: 50 + level * 20 + (level * level ~/ 5), - atk: 5 + level * 4, + atk: 10 + level * 12, def: 2 + level * 2, exp: 10 + level * 5, gold: 5 + level * 3,