refactor(core): 진행 서비스 및 모델 개선
- ProgressService 로직 개선 - GameState 상태 관리 확장 - MonsterCombatStats 속성 추가 - game_text_l10n 번역 추가
This commit is contained in:
@@ -103,6 +103,12 @@ String taskDebugging(String monsterName) {
|
|||||||
return 'Debugging $monsterName';
|
return 'Debugging $monsterName';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String taskFinalBoss(String bossName) {
|
||||||
|
if (isKoreanLocale) return '최종 보스와 대결: $bossName';
|
||||||
|
if (isJapaneseLocale) return '最終ボスと対決: $bossName';
|
||||||
|
return 'Final Battle: $bossName';
|
||||||
|
}
|
||||||
|
|
||||||
String taskSelling(String itemDescription) {
|
String taskSelling(String itemDescription) {
|
||||||
if (isKoreanLocale) return '$itemDescription 판매 중';
|
if (isKoreanLocale) return '$itemDescription 판매 중';
|
||||||
if (isJapaneseLocale) return '$itemDescription を販売中';
|
if (isJapaneseLocale) return '$itemDescription を販売中';
|
||||||
|
|||||||
@@ -302,6 +302,27 @@ class ProgressService {
|
|||||||
progress: progress,
|
progress: progress,
|
||||||
potionInventory: resetPotionInventory,
|
potionInventory: resetPotionInventory,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 최종 보스 처치 체크
|
||||||
|
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,
|
||||||
|
completedAct: true,
|
||||||
|
gameComplete: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 시장/판매/구매 태스크 완료 시 처리 (원본 Main.pas:631-649)
|
// 시장/판매/구매 태스크 완료 시 처리 (원본 Main.pas:631-649)
|
||||||
@@ -494,7 +515,13 @@ class ProgressService {
|
|||||||
return (progress: progress, queue: queue);
|
return (progress: progress, queue: queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. MonsterTask 실행 (원본 678-684줄)
|
// 3. 최종 보스 전투 체크
|
||||||
|
// finalBossState == fighting이면 Glitch God 스폰
|
||||||
|
if (state.progress.finalBossState == FinalBossState.fighting) {
|
||||||
|
return _startFinalBossFight(state, progress, queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. MonsterTask 실행 (원본 678-684줄)
|
||||||
final level = state.traits.level;
|
final level = state.traits.level;
|
||||||
|
|
||||||
// 원본 Main.pas:548-551: 25% 확률로 Quest Monster 사용
|
// 원본 Main.pas:548-551: 25% 확률로 Quest Monster 사용
|
||||||
@@ -567,6 +594,61 @@ class ProgressService {
|
|||||||
return (progress: progress, queue: queue);
|
return (progress: progress, queue: queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 최종 보스(Glitch God) 전투 시작
|
||||||
|
///
|
||||||
|
/// Act V 플롯 완료 후 호출되며, 글리치 갓과의 전투를 설정합니다.
|
||||||
|
({ProgressState progress, QueueState queue}) _startFinalBossFight(
|
||||||
|
GameState state,
|
||||||
|
ProgressState progress,
|
||||||
|
QueueState queue,
|
||||||
|
) {
|
||||||
|
final level = state.traits.level;
|
||||||
|
|
||||||
|
// 플레이어 전투 스탯 생성
|
||||||
|
final playerCombatStats = CombatStats.fromStats(
|
||||||
|
stats: state.stats,
|
||||||
|
equipment: state.equipment,
|
||||||
|
level: level,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Glitch God 생성 (레벨 100 최종 보스)
|
||||||
|
final glitchGod = MonsterCombatStats.glitchGod();
|
||||||
|
|
||||||
|
// 전투 상태 초기화
|
||||||
|
final combatState = CombatState.start(
|
||||||
|
playerStats: playerCombatStats,
|
||||||
|
monsterStats: glitchGod,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 전투 시간 추정 (보스 전투는 더 길게)
|
||||||
|
final combatCalculator = CombatCalculator(rng: state.rng);
|
||||||
|
final baseDuration = combatCalculator.estimateCombatDurationMs(
|
||||||
|
player: playerCombatStats,
|
||||||
|
monster: glitchGod,
|
||||||
|
);
|
||||||
|
// 최종 보스는 최소 10초, 최대 60초
|
||||||
|
final durationMillis = baseDuration.clamp(10000, 60000);
|
||||||
|
|
||||||
|
final taskResult = pq_logic.startTask(
|
||||||
|
progress,
|
||||||
|
l10n.taskFinalBoss(glitchGod.name),
|
||||||
|
durationMillis,
|
||||||
|
);
|
||||||
|
|
||||||
|
final updatedProgress = taskResult.progress.copyWith(
|
||||||
|
currentTask: TaskInfo(
|
||||||
|
caption: taskResult.caption,
|
||||||
|
type: TaskType.kill,
|
||||||
|
monsterBaseName: 'Glitch God',
|
||||||
|
monsterPart: '*', // 특수 전리품
|
||||||
|
monsterLevel: glitchGod.level,
|
||||||
|
),
|
||||||
|
currentCombat: combatState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (progress: updatedProgress, queue: queue);
|
||||||
|
}
|
||||||
|
|
||||||
/// Advances quest completion, applies reward, and enqueues next quest task.
|
/// Advances quest completion, applies reward, and enqueues next quest task.
|
||||||
GameState completeQuest(GameState state) {
|
GameState completeQuest(GameState state) {
|
||||||
final result = pq_logic.completeQuest(
|
final result = pq_logic.completeQuest(
|
||||||
@@ -625,28 +707,45 @@ class ProgressService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Advances plot to next act and applies any act-level rewards.
|
/// Advances plot to next act and applies any act-level rewards.
|
||||||
/// Returns gameComplete=true if Act V was completed (game ends).
|
/// Returns gameComplete=true if Final Boss was defeated (game ends).
|
||||||
({GameState state, bool gameComplete}) completeAct(GameState state) {
|
({GameState state, bool gameComplete}) completeAct(GameState state) {
|
||||||
// Act V 완료 시 (plotStageCount == 6) 게임 클리어
|
// Act V 완료 시 (plotStageCount == 6) → 최종 보스 전투 시작
|
||||||
// plotStageCount: 1=Prologue, 2=Act I, 3=Act II, 4=Act III, 5=Act IV, 6=Act V
|
// plotStageCount: 1=Prologue, 2=Act I, 3=Act II, 4=Act III, 5=Act IV, 6=Act V
|
||||||
if (state.progress.plotStageCount >= 6) {
|
if (state.progress.plotStageCount >= 6) {
|
||||||
// Act V 완료 - 게임 클리어!
|
// 이미 최종 보스가 처치되었으면 게임 클리어
|
||||||
// 히스토리만 업데이트하고 새 Act는 생성하지 않음
|
if (state.progress.finalBossState == FinalBossState.defeated) {
|
||||||
final updatedPlotHistory = [
|
final updatedPlotHistory = [
|
||||||
...state.progress.plotHistory.map(
|
...state.progress.plotHistory.map(
|
||||||
(e) => e.isComplete ? e : e.copyWith(isComplete: true),
|
(e) => e.isComplete ? e : e.copyWith(isComplete: true),
|
||||||
),
|
),
|
||||||
const HistoryEntry(caption: '*** THE END ***', isComplete: true),
|
const HistoryEntry(caption: '*** THE END ***', isComplete: true),
|
||||||
];
|
];
|
||||||
|
|
||||||
final updatedProgress = state.progress.copyWith(
|
final updatedProgress = state.progress.copyWith(
|
||||||
plotHistory: updatedPlotHistory,
|
plotHistory: updatedPlotHistory,
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
state: state.copyWith(progress: updatedProgress),
|
state: state.copyWith(progress: updatedProgress),
|
||||||
gameComplete: true,
|
gameComplete: true,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 최종 보스가 아직 등장하지 않았으면 보스 전투 시작
|
||||||
|
if (state.progress.finalBossState == FinalBossState.notSpawned) {
|
||||||
|
final updatedProgress = state.progress.copyWith(
|
||||||
|
finalBossState: FinalBossState.fighting,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 게임은 아직 끝나지 않음 - 보스 전투 진행
|
||||||
|
return (
|
||||||
|
state: state.copyWith(progress: updatedProgress),
|
||||||
|
gameComplete: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 보스 전투 중이면 계속 진행 (게임 종료 안 함)
|
||||||
|
return (state: state, gameComplete: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
final actResult = pq_logic.completeAct(state.progress.plotStageCount);
|
final actResult = pq_logic.completeAct(state.progress.plotStageCount);
|
||||||
|
|||||||
@@ -727,6 +727,18 @@ class QuestMonsterInfo {
|
|||||||
static const empty = QuestMonsterInfo(monsterData: '', monsterIndex: -1);
|
static const empty = QuestMonsterInfo(monsterData: '', monsterIndex: -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 최종 보스 상태 (Final Boss State)
|
||||||
|
enum FinalBossState {
|
||||||
|
/// 최종 보스 등장 전
|
||||||
|
notSpawned,
|
||||||
|
|
||||||
|
/// 최종 보스 전투 중
|
||||||
|
fighting,
|
||||||
|
|
||||||
|
/// 최종 보스 처치 완료
|
||||||
|
defeated,
|
||||||
|
}
|
||||||
|
|
||||||
class ProgressState {
|
class ProgressState {
|
||||||
const ProgressState({
|
const ProgressState({
|
||||||
required this.task,
|
required this.task,
|
||||||
@@ -743,6 +755,7 @@ class ProgressState {
|
|||||||
this.currentCombat,
|
this.currentCombat,
|
||||||
this.monstersKilled = 0,
|
this.monstersKilled = 0,
|
||||||
this.deathCount = 0,
|
this.deathCount = 0,
|
||||||
|
this.finalBossState = FinalBossState.notSpawned,
|
||||||
});
|
});
|
||||||
|
|
||||||
final ProgressBarState task;
|
final ProgressBarState task;
|
||||||
@@ -772,6 +785,9 @@ class ProgressState {
|
|||||||
/// 사망 횟수
|
/// 사망 횟수
|
||||||
final int deathCount;
|
final int deathCount;
|
||||||
|
|
||||||
|
/// 최종 보스 상태 (Act V)
|
||||||
|
final FinalBossState finalBossState;
|
||||||
|
|
||||||
factory ProgressState.empty() => ProgressState(
|
factory ProgressState.empty() => ProgressState(
|
||||||
task: ProgressBarState.empty(),
|
task: ProgressBarState.empty(),
|
||||||
quest: ProgressBarState.empty(),
|
quest: ProgressBarState.empty(),
|
||||||
@@ -802,6 +818,7 @@ class ProgressState {
|
|||||||
CombatState? currentCombat,
|
CombatState? currentCombat,
|
||||||
int? monstersKilled,
|
int? monstersKilled,
|
||||||
int? deathCount,
|
int? deathCount,
|
||||||
|
FinalBossState? finalBossState,
|
||||||
}) {
|
}) {
|
||||||
return ProgressState(
|
return ProgressState(
|
||||||
task: task ?? this.task,
|
task: task ?? this.task,
|
||||||
@@ -818,6 +835,7 @@ class ProgressState {
|
|||||||
currentCombat: currentCombat ?? this.currentCombat,
|
currentCombat: currentCombat ?? this.currentCombat,
|
||||||
monstersKilled: monstersKilled ?? this.monstersKilled,
|
monstersKilled: monstersKilled ?? this.monstersKilled,
|
||||||
deathCount: deathCount ?? this.deathCount,
|
deathCount: deathCount ?? this.deathCount,
|
||||||
|
finalBossState: finalBossState ?? this.finalBossState,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,6 +174,30 @@ class MonsterCombatStats {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 최종 보스 (Glitch God) 생성
|
||||||
|
///
|
||||||
|
/// Act V 완료 시 등장하는 최종 보스.
|
||||||
|
/// balance_constants.dart의 BossStats.glitchGod 기반.
|
||||||
|
factory MonsterCombatStats.glitchGod() {
|
||||||
|
const bossLevel = 100;
|
||||||
|
final bossStats = BossStats.glitchGod(bossLevel);
|
||||||
|
|
||||||
|
return MonsterCombatStats(
|
||||||
|
name: 'The Primordial Glitch',
|
||||||
|
level: bossLevel,
|
||||||
|
atk: bossStats.atk,
|
||||||
|
def: bossStats.def,
|
||||||
|
hpMax: bossStats.hp,
|
||||||
|
hpCurrent: bossStats.hp,
|
||||||
|
criRate: 0.25, // 보스 크리티컬 확률 25%
|
||||||
|
criDamage: 2.0, // 보스 크리티컬 데미지 200%
|
||||||
|
evasion: 0.15, // 보스 회피율 15%
|
||||||
|
accuracy: 0.95, // 보스 명중률 95%
|
||||||
|
attackDelayMs: 800, // 보스 공격 속도 (빠름)
|
||||||
|
expReward: bossStats.exp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// 몬스터 이름에서 속도 타입 추론
|
/// 몬스터 이름에서 속도 타입 추론
|
||||||
///
|
///
|
||||||
/// 특정 키워드 기반으로 속도 결정 (향후 확장 가능)
|
/// 특정 키워드 기반으로 속도 결정 (향후 확장 가능)
|
||||||
|
|||||||
Reference in New Issue
Block a user