refactor(core): 밸런스 상수 분리 및 진행 서비스 개선

- balance_constants.dart에 게임 밸런스 상수 정의
- ProgressService 로직 개선 및 상수 참조
This commit is contained in:
JiWoong Sul
2026-01-02 15:49:46 +09:00
parent 2ef9807cbe
commit 86b14427f6
2 changed files with 59 additions and 7 deletions

View File

@@ -17,6 +17,7 @@ import 'package:asciineverdie/src/core/model/monster_combat_stats.dart';
import 'package:asciineverdie/src/core/model/potion.dart'; import 'package:asciineverdie/src/core/model/potion.dart';
import 'package:asciineverdie/src/core/model/pq_config.dart'; import 'package:asciineverdie/src/core/model/pq_config.dart';
import 'package:asciineverdie/src/core/model/skill.dart'; import 'package:asciineverdie/src/core/model/skill.dart';
import 'package:asciineverdie/src/core/util/balance_constants.dart';
import 'package:asciineverdie/src/core/util/pq_logic.dart' as pq_logic; import 'package:asciineverdie/src/core/util/pq_logic.dart' as pq_logic;
class ProgressTickResult { class ProgressTickResult {
@@ -549,10 +550,13 @@ class ProgressService {
); );
// 전투용 몬스터 레벨 조정 (밸런스) // 전투용 몬스터 레벨 조정 (밸런스)
// config의 raw 레벨 플레이어보다 너무 높으면 전투가 불가능 // Act별 최소 레벨 플레이어 레벨 중 큰 값을 기준으로 ±3 범위 제한
// 플레이어 레벨 ±3 범위로 제한 (최소 1) final actMinLevel = ActMonsterLevel.forPlotStage(
state.progress.plotStageCount,
);
final baseLevel = math.max(level, actMinLevel);
final effectiveMonsterLevel = monsterResult.level final effectiveMonsterLevel = monsterResult.level
.clamp(math.max(1, level - 3), level + 3) .clamp(math.max(1, baseLevel - 3), baseLevel + 3)
.toInt(); .toInt();
final monsterCombatStats = MonsterCombatStats.fromLevel( final monsterCombatStats = MonsterCombatStats.fromLevel(
@@ -849,11 +853,25 @@ class ProgressService {
} }
GameState forcePlotComplete(GameState state) { GameState forcePlotComplete(GameState state) {
final progress = state.progress.copyWith( // 다음 Act의 최소 몬스터 레벨까지 레벨업
task: state.progress.task.copyWith(position: state.progress.task.max), final nextPlotStage = state.progress.plotStageCount + 1;
plot: state.progress.plot.copyWith(position: state.progress.plot.max), final targetLevel = ActMonsterLevel.forPlotStage(nextPlotStage);
var nextState = state;
// 현재 레벨이 목표 레벨보다 낮으면 레벨업
while (nextState.traits.level < targetLevel) {
nextState = _levelUp(nextState);
}
final 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 state.copyWith(progress: progress); return nextState.copyWith(progress: progress);
} }
GameState _applyReward(GameState state, pq_logic.RewardKind reward) { GameState _applyReward(GameState state, pq_logic.RewardKind reward) {

View File

@@ -416,6 +416,40 @@ class LevelTierSettings {
} }
} }
/// Act별 최소 몬스터 레벨 (act minimum monster level)
///
/// 플레이어 레벨과 무관하게 각 Act에서 등장하는 몬스터의 최소 레벨.
/// 저레벨 플레이어가 고Act에 도달해도 적절한 난이도 유지.
class ActMonsterLevel {
ActMonsterLevel._();
/// plotStageCount 기준 최소 몬스터 레벨
/// - 1: Prologue → 1
/// - 2: Act I → 3
/// - 3: Act II → 22
/// - 4: Act III → 45
/// - 5: Act IV → 72
/// - 6: Act V → 88
static const List<int> _minimumLevels = [
1, // index 0: unused (plotStageCount starts at 1)
1, // Prologue (plotStageCount = 1)
3, // Act I (plotStageCount = 2)
22, // Act II (plotStageCount = 3)
45, // Act III (plotStageCount = 4)
72, // Act IV (plotStageCount = 5)
88, // Act V (plotStageCount = 6)
];
/// plotStageCount에 해당하는 최소 몬스터 레벨 반환
static int forPlotStage(int plotStageCount) {
if (plotStageCount < 1) return 1;
if (plotStageCount >= _minimumLevels.length) {
return _minimumLevels.last;
}
return _minimumLevels[plotStageCount];
}
}
/// 플레이어 스탯 스케일링 (player stat scaling) /// 플레이어 스탯 스케일링 (player stat scaling)
class PlayerScaling { class PlayerScaling {
PlayerScaling._(); PlayerScaling._();