refactor(engine): tick() 메서드 분할 (350→80 LOC)

- 8개 헬퍼 메서드로 책임 분리
- _generateNextTask() 35 LOC로 감소
This commit is contained in:
JiWoong Sul
2026-01-21 17:34:31 +09:00
parent 742b0d1773
commit 7f44e95163

View File

@@ -161,36 +161,107 @@ class ProgressService {
/// Tick the timer loop (equivalent to Timer1Timer in the original code).
ProgressTickResult tick(GameState state, int elapsedMillis) {
// 10000ms 제한: 100x 배속 (50ms * 100 = 5000ms) + 여유 공간
// 디버그 터보 모드(100x) 지원을 위해 확장
final int clamped = elapsedMillis.clamp(0, 10000).toInt();
var progress = state.progress;
var queue = state.queue;
var nextState = state;
// 1. 스킬 시스템 업데이트 (시간, 버프, MP 회복)
var nextState = _updateSkillSystem(state, clamped);
var progress = nextState.progress;
var queue = nextState.queue;
// 2. 태스크 바 진행 중이면 전투 틱 처리
if (progress.task.position < progress.task.max) {
return _processTaskInProgress(nextState, clamped);
}
// 3. 태스크 완료 처리
final gain = progress.currentTask.type == TaskType.kill;
final incrementSeconds = progress.task.max ~/ 1000;
final int monsterExpReward =
progress.currentCombat?.monsterStats.expReward ?? 0;
var leveledUp = false;
var questDone = false;
var actDone = false;
var gameComplete = false;
// 스킬 시스템 시간 업데이트 (Phase 3)
// 4. 킬 태스크 완료 처리
if (gain) {
final killResult = _handleKillTaskCompletion(nextState, progress, queue);
if (killResult.earlyReturn != null) return killResult.earlyReturn!;
nextState = killResult.state;
progress = killResult.progress;
queue = killResult.queue;
}
// 5. 시장/판매/구매 태스크 완료 처리
final marketResult = _handleMarketTaskCompletion(nextState, progress, queue);
if (marketResult.earlyReturn != null) return marketResult.earlyReturn!;
nextState = marketResult.state;
progress = marketResult.progress;
queue = marketResult.queue;
// 6. 경험치/레벨업 처리
if (gain && nextState.traits.level < 100 && monsterExpReward > 0) {
final expResult = _handleExpGain(nextState, progress, monsterExpReward);
nextState = expResult.state;
progress = expResult.progress;
leveledUp = expResult.leveledUp;
}
// 7. 퀘스트 진행 처리
final questResult = _handleQuestProgress(
nextState, progress, queue, gain, incrementSeconds,
);
nextState = questResult.state;
progress = questResult.progress;
queue = questResult.queue;
questDone = questResult.completed;
// 8. 플롯 진행 및 Act Boss 소환 처리
progress = _handlePlotProgress(
nextState, progress, gain, incrementSeconds,
);
// 9. 다음 태스크 디큐/생성
final dequeueResult = _handleTaskDequeue(nextState, progress, queue);
nextState = dequeueResult.state;
progress = dequeueResult.progress;
queue = dequeueResult.queue;
actDone = dequeueResult.actDone;
gameComplete = dequeueResult.gameComplete;
nextState = _recalculateEncumbrance(
nextState.copyWith(progress: progress, queue: queue),
);
return ProgressTickResult(
state: nextState,
leveledUp: leveledUp,
completedQuest: questDone,
completedAct: actDone,
gameComplete: gameComplete,
);
}
/// 스킬 시스템 업데이트 (시간, 버프 정리, MP 회복)
GameState _updateSkillSystem(GameState state, int elapsedMs) {
final skillService = SkillService(rng: state.rng);
var skillSystem = skillService.updateElapsedTime(
state.skillSystem,
clamped,
elapsedMs,
);
// 만료된 버프 정리
skillSystem = skillService.cleanupExpiredBuffs(skillSystem);
var nextState = state.copyWith(skillSystem: skillSystem);
// 비전투 시 MP 회복
final isInCombat =
progress.currentTask.type == TaskType.kill &&
progress.currentCombat != null &&
progress.currentCombat!.isActive;
state.progress.currentTask.type == TaskType.kill &&
state.progress.currentCombat != null &&
state.progress.currentCombat!.isActive;
if (!isInCombat && nextState.stats.mp < nextState.stats.mpMax) {
final mpRegen = skillService.calculateMpRegen(
elapsedMs: clamped,
elapsedMs: elapsedMs,
isInCombat: false,
wis: nextState.stats.wis,
);
@@ -205,28 +276,32 @@ class ProgressService {
}
}
nextState = nextState.copyWith(skillSystem: skillSystem);
return nextState;
}
// Advance task bar if still running.
if (progress.task.position < progress.task.max) {
final uncapped = progress.task.position + clamped;
/// 태스크 진행 중 처리 (전투 틱 포함)
ProgressTickResult _processTaskInProgress(GameState state, int elapsedMs) {
var progress = state.progress;
final uncapped = progress.task.position + elapsedMs;
final int newTaskPos = uncapped > progress.task.max
? progress.task.max
: uncapped;
// 킬 태스크 중 전투 진행 (CombatTickService 사용)
var updatedCombat = progress.currentCombat;
var updatedSkillSystem = nextState.skillSystem;
var updatedPotionInventory = nextState.potionInventory;
var updatedSkillSystem = state.skillSystem;
var updatedPotionInventory = state.potionInventory;
var nextState = state;
// 킬 태스크 중 전투 진행
if (progress.currentTask.type == TaskType.kill &&
updatedCombat != null &&
updatedCombat.isActive) {
final combatTickService = CombatTickService(rng: nextState.rng);
final combatTickService = CombatTickService(rng: state.rng);
final combatResult = combatTickService.processTick(
state: nextState,
state: state,
combat: updatedCombat,
skillSystem: updatedSkillSystem,
elapsedMs: clamped,
elapsedMs: elapsedMs,
);
updatedCombat = combatResult.combat;
updatedSkillSystem = combatResult.skillSystem;
@@ -234,11 +309,11 @@ class ProgressService {
updatedPotionInventory = combatResult.potionInventory!;
}
// Phase 4: 플레이어 사망 체크
// 플레이어 사망 체크
if (!updatedCombat.playerStats.isAlive) {
final monsterName = updatedCombat.monsterStats.name;
nextState = _processPlayerDeath(
nextState,
state,
killerName: monsterName,
cause: DeathCause.monster,
);
@@ -251,7 +326,7 @@ class ProgressService {
currentCombat: updatedCombat,
);
nextState = _recalculateEncumbrance(
nextState.copyWith(
state.copyWith(
progress: progress,
skillSystem: updatedSkillSystem,
potionInventory: updatedPotionInventory,
@@ -260,28 +335,27 @@ class ProgressService {
return ProgressTickResult(state: nextState);
}
final gain = progress.currentTask.type == TaskType.kill;
final incrementSeconds = progress.task.max ~/ 1000;
/// 킬 태스크 완료 처리 (HP 회복, 전리품, 보스 처리)
({
GameState state,
ProgressState progress,
QueueState queue,
ProgressTickResult? earlyReturn,
}) _handleKillTaskCompletion(
GameState state,
ProgressState progress,
QueueState queue,
) {
var nextState = state;
// 몬스터 경험치 미리 저장 (currentCombat이 null되기 전)
final int monsterExpReward =
progress.currentCombat?.monsterStats.expReward ?? 0;
// 킬 태스크 완료 시 전투 결과 반영 및 전리품 획득
if (gain) {
// 전투 결과에 따라 플레이어 HP 업데이트 + 전투 후 회복
// 전투 후 HP 회복
final combat = progress.currentCombat;
if (combat != null && combat.isActive) {
// 전투 중 남은 HP
final remainingHp = combat.playerStats.hpCurrent;
final maxHp = combat.playerStats.hpMax;
// 전투 승리 시 HP 회복 (50% + CON/2 + 클래스 패시브)
// 아이들 게임 특성상 전투 사이 HP가 회복되어야 지속 플레이 가능
final conBonus = nextState.stats.con ~/ 2;
var healAmount = (maxHp * 0.5).round() + conBonus;
// 클래스 패시브: 전투 후 HP 회복 (예: Garbage Collector +5%)
final klass = ClassData.findById(nextState.traits.classId);
if (klass != null) {
final postCombatHealRate =
@@ -292,17 +366,16 @@ class ProgressService {
}
final newHp = (remainingHp + healAmount).clamp(0, maxHp);
nextState = nextState.copyWith(
stats: nextState.stats.copyWith(hpCurrent: newHp),
);
}
// 전리품 획득 (원본 Main.pas:625-630)
// 전리품 획득
final lootResult = _winLoot(nextState);
nextState = lootResult.state;
// 물약 드랍 시 전투 로그에 이벤트 추가
// 물약 드랍 로그 추가
var combatForReset = progress.currentCombat;
if (lootResult.droppedPotion != null && combatForReset != null) {
final potionDropEvent = CombatEvent.potionDrop(
@@ -319,9 +392,8 @@ class ProgressService {
progress = progress.copyWith(currentCombat: combatForReset);
}
// Boss 승리 처리: 시네마틱 트리거
// Boss 승리 처리
if (progress.pendingActCompletion) {
// Act Boss를 처치했으므로 시네마틱 재생
final cinematicEntries = pq_logic.interplotCinematic(
config,
nextState.rng,
@@ -332,87 +404,107 @@ class ProgressService {
progress = progress.copyWith(
currentCombat: null,
monstersKilled: progress.monstersKilled + 1,
pendingActCompletion: false, // Boss 처치 완료
pendingActCompletion: false,
);
} else {
// 일반 전투 종료
progress = progress.copyWith(
currentCombat: null,
monstersKilled: progress.monstersKilled + 1,
);
}
nextState = nextState.copyWith(
progress: progress,
queue: queue,
);
nextState = nextState.copyWith(progress: progress, queue: queue);
// 최종 보스 처치 체크
if (progress.finalBossState == FinalBossState.fighting) {
// 글리치 갓 처치 완료 - 게임 클리어
progress = progress.copyWith(finalBossState: FinalBossState.defeated);
nextState = nextState.copyWith(progress: progress);
// completeAct를 호출하여 게임 완료 처리
final actResult = completeAct(nextState);
nextState = actResult.state;
return ProgressTickResult(
state: nextState,
leveledUp: false,
completedQuest: false,
return (
state: actResult.state,
progress: actResult.state.progress,
queue: actResult.state.queue,
earlyReturn: ProgressTickResult(
state: actResult.state,
completedAct: true,
gameComplete: true,
),
);
}
return (
state: nextState,
progress: progress,
queue: queue,
earlyReturn: null,
);
}
// 시장/판매/구매 태스크 완료 시 처리 (MarketService 사용)
final marketService = MarketService(rng: nextState.rng);
/// 시장/판매/구매 태스크 완료 처리
({
GameState state,
ProgressState progress,
QueueState queue,
ProgressTickResult? earlyReturn,
}) _handleMarketTaskCompletion(
GameState state,
ProgressState progress,
QueueState queue,
) {
var nextState = state;
final marketService = MarketService(rng: state.rng);
final taskType = progress.currentTask.type;
if (taskType == TaskType.buying) {
// 장비 구매 완료 (원본 631-634)
nextState = marketService.completeBuying(nextState);
progress = nextState.progress;
} else if (taskType == TaskType.market || taskType == TaskType.sell) {
// 시장 도착 또는 판매 완료 (원본 635-649)
final sellResult = marketService.processSell(nextState);
nextState = sellResult.state;
progress = nextState.progress;
queue = nextState.queue;
// 판매 중이면 다른 로직 건너뛰기
if (sellResult.continuesSelling) {
nextState = _recalculateEncumbrance(
nextState.copyWith(progress: progress, queue: queue),
);
return ProgressTickResult(
return (
state: nextState,
leveledUp: false,
completedQuest: false,
completedAct: false,
progress: progress,
queue: queue,
earlyReturn: ProgressTickResult(state: nextState),
);
}
}
// Gain XP / level up (몬스터 경험치 기반)
// 최대 레벨(100) 제한: 100레벨에서는 더 이상 레벨업하지 않음
if (gain && nextState.traits.level < 100 && monsterExpReward > 0) {
// 종족 경험치 배율 적용 (예: Byte Human +5%, Callback Seraph +3%)
return (
state: nextState,
progress: progress,
queue: queue,
earlyReturn: null,
);
}
/// 경험치 획득 및 레벨업 처리
({GameState state, ProgressState progress, bool leveledUp}) _handleExpGain(
GameState state,
ProgressState progress,
int monsterExpReward,
) {
var nextState = state;
var leveledUp = false;
final race = RaceData.findById(nextState.traits.raceId);
final expMultiplier = race?.expMultiplier ?? 1.0;
final adjustedExp = (monsterExpReward * expMultiplier).round();
final newExpPos = progress.exp.position + adjustedExp;
// 레벨업 체크 (경험치가 필요량 이상일 때)
if (newExpPos >= progress.exp.max) {
// 초과 경험치 계산
final overflowExp = newExpPos - progress.exp.max;
nextState = _levelUp(nextState);
leveledUp = true;
progress = nextState.progress;
// 초과 경험치를 다음 레벨에 적용
if (overflowExp > 0 && nextState.traits.level < 100) {
progress = progress.copyWith(
exp: progress.exp.copyWith(position: overflowExp),
@@ -423,14 +515,32 @@ class ProgressService {
exp: progress.exp.copyWith(position: newExpPos),
);
}
return (state: nextState, progress: progress, leveledUp: leveledUp);
}
// Advance quest bar after Act I.
/// 퀘스트 진행 처리
({
GameState state,
ProgressState progress,
QueueState queue,
bool completed,
}) _handleQuestProgress(
GameState state,
ProgressState progress,
QueueState queue,
bool gain,
int incrementSeconds,
) {
var nextState = state;
var questDone = false;
final canQuestProgress =
gain &&
progress.plotStageCount > 1 &&
progress.questCount > 0 &&
progress.quest.max > 0;
if (canQuestProgress) {
if (progress.quest.position + incrementSeconds >= progress.quest.max) {
nextState = completeQuest(nextState);
@@ -446,19 +556,31 @@ class ProgressService {
}
}
// 플롯(plot) 바가 완료되면 Act Boss 소환
// (개선: Boss 처치 → 시네마틱 → Act 전환 순서)
return (
state: nextState,
progress: progress,
queue: queue,
completed: questDone,
);
}
/// 플롯 진행 및 Act Boss 소환 처리
ProgressState _handlePlotProgress(
GameState state,
ProgressState progress,
bool gain,
int incrementSeconds,
) {
if (gain &&
progress.plot.max > 0 &&
progress.plot.position >= progress.plot.max &&
!progress.pendingActCompletion) {
// Act Boss 소환 및 플래그 설정
final actProgressionService = ActProgressionService(config: config);
final actBoss = actProgressionService.createActBoss(nextState);
progress = progress.copyWith(
plot: progress.plot.copyWith(position: 0), // Plot bar 리셋
final actBoss = actProgressionService.createActBoss(state);
return progress.copyWith(
plot: progress.plot.copyWith(position: 0),
currentCombat: actBoss,
pendingActCompletion: true, // Boss 처치 대기 플래그
pendingActCompletion: true,
);
} else if (progress.currentTask.type != TaskType.load &&
progress.plot.max > 0 &&
@@ -467,12 +589,29 @@ class ProgressService {
final int newPlotPos = uncappedPlot > progress.plot.max
? progress.plot.max
: uncappedPlot;
progress = progress.copyWith(
return progress.copyWith(
plot: progress.plot.copyWith(position: newPlotPos),
);
}
return progress;
}
/// 태스크 디큐 및 생성 처리
({
GameState state,
ProgressState progress,
QueueState queue,
bool actDone,
bool gameComplete,
}) _handleTaskDequeue(
GameState state,
ProgressState progress,
QueueState queue,
) {
var nextState = state;
var actDone = false;
var gameComplete = false;
// Dequeue next scripted task if available.
final dq = pq_logic.dequeue(progress, queue);
if (dq != null) {
progress = dq.progress.copyWith(
@@ -480,7 +619,6 @@ class ProgressService {
);
queue = dq.queue;
// plot 타입이 dequeue 되면 completeAct 실행 (원본 Main.pas 로직)
if (dq.kind == QueueKind.plot) {
nextState = nextState.copyWith(progress: progress, queue: queue);
final actResult = completeAct(nextState);
@@ -491,22 +629,17 @@ class ProgressService {
queue = nextState.queue;
}
} else {
// 큐가 비어있으면 새 태스크 생성 (원본 Dequeue 667-684줄)
nextState = nextState.copyWith(progress: progress, queue: queue);
final newTaskResult = _generateNextTask(nextState);
progress = newTaskResult.progress;
queue = newTaskResult.queue;
}
nextState = _recalculateEncumbrance(
nextState.copyWith(progress: progress, queue: queue),
);
return ProgressTickResult(
return (
state: nextState,
leveledUp: leveledUp,
completedQuest: questDone,
completedAct: actDone,
progress: progress,
queue: queue,
actDone: actDone,
gameComplete: gameComplete,
);
}
@@ -519,68 +652,116 @@ class ProgressService {
final queue = state.queue;
final oldTaskType = progress.currentTask.type;
// 1. Encumbrance가 가득 찼으면 시장으로 이동 (원본 667-669줄)
if (progress.encumbrance.position >= progress.encumbrance.max &&
progress.encumbrance.max > 0) {
// 1. Encumbrance 초과 시 시장 이동
if (_shouldGoToMarket(progress)) {
return _createMarketTask(progress, queue);
}
// 2. 전환 태스크 (buying/heading)
if (_needsTransitionTask(oldTaskType)) {
return _createTransitionTask(state, progress, queue);
}
// 3. Act Boss 리트라이
if (state.progress.pendingActCompletion) {
return _createActBossRetryTask(state, progress, queue);
}
// 4. 최종 보스 전투
if (state.progress.finalBossState == FinalBossState.fighting &&
!state.progress.isInBossLevelingMode) {
if (state.progress.bossLevelingEndTime != null) {
progress = progress.copyWith(clearBossLevelingEndTime: true);
}
final actProgressionService = ActProgressionService(config: config);
return actProgressionService.startFinalBossFight(state, progress, queue);
}
// 5. 일반 몬스터 전투
return _createMonsterTask(state, progress, queue);
}
/// 시장 이동 조건 확인
bool _shouldGoToMarket(ProgressState progress) {
return progress.encumbrance.position >= progress.encumbrance.max &&
progress.encumbrance.max > 0;
}
/// 전환 태스크 필요 여부 확인
bool _needsTransitionTask(TaskType oldTaskType) {
return oldTaskType != TaskType.kill &&
oldTaskType != TaskType.neutral &&
oldTaskType != TaskType.buying;
}
/// 시장 이동 태스크 생성
({ProgressState progress, QueueState queue}) _createMarketTask(
ProgressState progress,
QueueState queue,
) {
final taskResult = pq_logic.startTask(
progress,
l10n.taskHeadingToMarket(),
4 * 1000,
);
progress = taskResult.progress.copyWith(
final updatedProgress = taskResult.progress.copyWith(
currentTask: TaskInfo(
caption: taskResult.caption,
type: TaskType.market,
),
currentCombat: null, // 비전투 태스크이므로 전투 상태 초기화
currentCombat: null,
);
return (progress: progress, queue: queue);
return (progress: updatedProgress, queue: queue);
}
// 2. kill/heading/buying 태스크가 아니었으면 heading 또는 buying 태스크 실행
// (원본 670-677줄) - buying 완료 후 무한 루프 방지
if (oldTaskType != TaskType.kill &&
oldTaskType != TaskType.neutral &&
oldTaskType != TaskType.buying) {
// Gold가 충분하면 장비 구매 (Common 장비 가격 기준)
// 실제 구매 가격과 동일한 공식 사용: level * 50
/// 전환 태스크 생성 (buying 또는 heading)
({ProgressState progress, QueueState queue}) _createTransitionTask(
GameState state,
ProgressState progress,
QueueState queue,
) {
final gold = state.inventory.gold;
final equipPrice = state.traits.level * 50; // Common 장비 1개 가격
final equipPrice = state.traits.level * 50;
// Gold 충분 시 장비 구매
if (gold > equipPrice) {
final taskResult = pq_logic.startTask(
progress,
l10n.taskUpgradingHardware(),
5 * 1000,
);
progress = taskResult.progress.copyWith(
final updatedProgress = taskResult.progress.copyWith(
currentTask: TaskInfo(
caption: taskResult.caption,
type: TaskType.buying,
),
currentCombat: null, // 비전투 태스크이므로 전투 상태 초기화
currentCombat: null,
);
return (progress: progress, queue: queue);
return (progress: updatedProgress, queue: queue);
}
// Gold 부족하면 전장으로 이동 (원본 674-676줄)
// Gold 부족 전장 이동
final taskResult = pq_logic.startTask(
progress,
l10n.taskEnteringDebugZone(),
4 * 1000,
);
progress = taskResult.progress.copyWith(
final updatedProgress = taskResult.progress.copyWith(
currentTask: TaskInfo(
caption: taskResult.caption,
type: TaskType.neutral,
),
currentCombat: null, // 비전투 태스크이므로 전투 상태 초기화
currentCombat: null,
);
return (progress: progress, queue: queue);
return (progress: updatedProgress, queue: queue);
}
// 3. Act Boss 리트라이 체크
// pendingActCompletion이 true면 Act Boss 재소환
if (state.progress.pendingActCompletion) {
/// Act Boss 재도전 태스크 생성
({ProgressState progress, QueueState queue}) _createActBossRetryTask(
GameState state,
ProgressState progress,
QueueState queue,
) {
final actProgressionService = ActProgressionService(config: config);
final actBoss = actProgressionService.createActBoss(state);
final combatCalculator = CombatCalculator(rng: state.rng);
@@ -595,12 +776,12 @@ class ProgressService {
durationMillis,
);
progress = taskResult.progress.copyWith(
final updatedProgress = taskResult.progress.copyWith(
currentTask: TaskInfo(
caption: taskResult.caption,
type: TaskType.kill,
monsterBaseName: actBoss.monsterStats.name,
monsterPart: '*', // Boss는 WinItem 드랍
monsterPart: '*',
monsterLevel: actBoss.monsterStats.level,
monsterGrade: MonsterGrade.boss,
monsterSize: getBossSizeForAct(state.progress.plotStageCount),
@@ -608,31 +789,18 @@ class ProgressService {
currentCombat: actBoss,
);
return (progress: progress, queue: queue);
return (progress: updatedProgress, queue: queue);
}
// 4. 최종 보스 전투 체크
// finalBossState == fighting이면 Glitch God 스폰
// 단, 레벨링 모드 중이면 일반 몬스터로 레벨업 후 재도전
if (state.progress.finalBossState == FinalBossState.fighting) {
if (state.progress.isInBossLevelingMode) {
// 레벨링 모드: 일반 몬스터 전투로 대체 (아래 MonsterTask로 진행)
} else {
// 레벨링 모드 종료 또는 첫 도전: 보스전 시작
// 레벨링 모드가 끝났으면 타이머 초기화
if (state.progress.bossLevelingEndTime != null) {
progress = progress.copyWith(clearBossLevelingEndTime: true);
}
final actProgressionService = ActProgressionService(config: config);
return actProgressionService.startFinalBossFight(state, progress, queue);
}
}
// 5. MonsterTask 실행 (원본 678-684줄)
/// 일반 몬스터 전투 태스크 생성
({ProgressState progress, QueueState queue}) _createMonsterTask(
GameState state,
ProgressState progress,
QueueState queue,
) {
final level = state.traits.level;
// 원본 Main.pas:548-551: 25% 확률로 Quest Monster 사용
// fQuest.Caption이 비어있지 않으면 해당 몬스터 데이터 전달
// 퀘스트 몬스터 데이터 확인
final questMonster = state.progress.currentQuestMonster;
final questMonsterData = questMonster?.monsterData;
final questLevel = questMonsterData != null
@@ -640,6 +808,7 @@ class ProgressService {
0
: null;
// 몬스터 생성
final monsterResult = pq_logic.monsterTask(
config,
state.rng,
@@ -648,8 +817,7 @@ class ProgressService {
questLevel,
);
// 전투용 몬스터 레벨 조정 (밸런스)
// Act별 최소 레벨과 플레이어 레벨 중 큰 값을 기준으로 ±3 범위 제한
// 몬스터 레벨 조정 (밸런스)
final actMinLevel = ActMonsterLevel.forPlotStage(
state.progress.plotStageCount,
);
@@ -658,7 +826,7 @@ class ProgressService {
.clamp(math.max(1, baseLevel - 3), baseLevel + 3)
.toInt();
// 전투 스탯 생성 (Phase 12: 몬스터 레벨 기반 페널티 적용)
// 전투 스탯 생성
final playerCombatStats = CombatStats.fromStats(
stats: state.stats,
equipment: state.equipment,
@@ -673,13 +841,12 @@ class ProgressService {
plotStageCount: state.progress.plotStageCount,
);
// 전투 상태 초기화
// 전투 상태 및 지속시간
final combatState = CombatState.start(
playerStats: playerCombatStats,
monsterStats: monsterCombatStats,
);
// 태스크 지속시간 계산 (CombatCalculator 기반)
final combatCalculator = CombatCalculator(rng: state.rng);
final durationMillis = combatCalculator.estimateCombatDurationMs(
player: playerCombatStats,
@@ -692,14 +859,14 @@ class ProgressService {
durationMillis,
);
// 몬스터 사이즈 결정 (Act 기반, Phase 13)
// 몬스터 사이즈 결정
final monsterSize = getMonsterSizeForAct(
plotStageCount: state.progress.plotStageCount,
grade: monsterResult.grade,
rng: state.rng,
);
progress = taskResult.progress.copyWith(
final updatedProgress = taskResult.progress.copyWith(
currentTask: TaskInfo(
caption: taskResult.caption,
type: TaskType.kill,
@@ -712,7 +879,7 @@ class ProgressService {
currentCombat: combatState,
);
return (progress: progress, queue: queue);
return (progress: updatedProgress, queue: queue);
}
/// Advances quest completion, applies reward, and enqueues next quest task.