feat(skill): Phase 3 MP 기반 스킬 시스템 구현
- Skill, SkillType, BuffEffect, SkillState, SkillUseResult 클래스 정의 (skill.dart) - SkillSystemState를 GameState에 통합 (activeBuffs, skillStates, elapsedMs) - 프로그래밍 테마 스킬 데이터 정의 (skill_data.dart) - 공격: Debug Strike, Memory Leak, Core Dump, Kernel Panic 등 - 회복: Hot Reload, Garbage Collection, Quick Fix - 버프: Safe Mode, Overclock, Firewall - SkillService 구현 (skill_service.dart) - 스킬 사용 가능 여부 확인 (MP, 쿨타임) - 공격/회복/버프 스킬 사용 로직 - 자동 스킬 선택 (HP < 30% → 회복, 보스전 → 강력한 공격, 일반 → MP 효율) - MP 자연 회복 (비전투: 50ms당 1, 전투: WIS 기반) - progress_service.dart에 스킬 시스템 통합 - tick()에서 스킬 시간 업데이트 및 버프 만료 처리 - _processCombatTickWithSkills()로 전투 중 자동 스킬 사용
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:askiineverdie/data/game_text_l10n.dart' as l10n;
|
||||
import 'package:askiineverdie/data/skill_data.dart';
|
||||
import 'package:askiineverdie/src/core/engine/combat_calculator.dart';
|
||||
import 'package:askiineverdie/src/core/engine/game_mutations.dart';
|
||||
import 'package:askiineverdie/src/core/engine/reward_service.dart';
|
||||
import 'package:askiineverdie/src/core/engine/skill_service.dart';
|
||||
import 'package:askiineverdie/src/core/model/combat_state.dart';
|
||||
import 'package:askiineverdie/src/core/model/combat_stats.dart';
|
||||
import 'package:askiineverdie/src/core/model/game_state.dart';
|
||||
@@ -141,6 +143,34 @@ class ProgressService {
|
||||
var questDone = false;
|
||||
var actDone = false;
|
||||
|
||||
// 스킬 시스템 시간 업데이트 (Phase 3)
|
||||
final skillService = SkillService(rng: state.rng);
|
||||
var skillSystem = skillService.updateElapsedTime(state.skillSystem, clamped);
|
||||
|
||||
// 만료된 버프 정리
|
||||
skillSystem = skillService.cleanupExpiredBuffs(skillSystem);
|
||||
|
||||
// 비전투 시 MP 회복
|
||||
final isInCombat = progress.currentTask.type == TaskType.kill &&
|
||||
progress.currentCombat != null &&
|
||||
progress.currentCombat!.isActive;
|
||||
|
||||
if (!isInCombat && nextState.stats.mp < nextState.stats.mpMax) {
|
||||
final mpRegen = skillService.calculateMpRegen(
|
||||
elapsedMs: clamped,
|
||||
isInCombat: false,
|
||||
wis: nextState.stats.wis,
|
||||
);
|
||||
if (mpRegen > 0) {
|
||||
final newMp = (nextState.stats.mp + mpRegen).clamp(0, nextState.stats.mpMax);
|
||||
nextState = nextState.copyWith(
|
||||
stats: nextState.stats.copyWith(mpCurrent: newMp),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
nextState = nextState.copyWith(skillSystem: skillSystem);
|
||||
|
||||
// Advance task bar if still running.
|
||||
if (progress.task.position < progress.task.max) {
|
||||
final uncapped = progress.task.position + clamped;
|
||||
@@ -148,10 +178,18 @@ class ProgressService {
|
||||
? progress.task.max
|
||||
: uncapped;
|
||||
|
||||
// 킬 태스크 중 전투 진행
|
||||
// 킬 태스크 중 전투 진행 (스킬 자동 사용 포함)
|
||||
var updatedCombat = progress.currentCombat;
|
||||
var updatedSkillSystem = nextState.skillSystem;
|
||||
if (progress.currentTask.type == TaskType.kill && updatedCombat != null && updatedCombat.isActive) {
|
||||
updatedCombat = _processCombatTick(nextState, updatedCombat, clamped);
|
||||
final combatResult = _processCombatTickWithSkills(
|
||||
nextState,
|
||||
updatedCombat,
|
||||
updatedSkillSystem,
|
||||
clamped,
|
||||
);
|
||||
updatedCombat = combatResult.combat;
|
||||
updatedSkillSystem = combatResult.skillSystem;
|
||||
}
|
||||
|
||||
progress = progress.copyWith(
|
||||
@@ -159,7 +197,7 @@ class ProgressService {
|
||||
currentCombat: updatedCombat,
|
||||
);
|
||||
nextState = _recalculateEncumbrance(
|
||||
nextState.copyWith(progress: progress),
|
||||
nextState.copyWith(progress: progress, skillSystem: updatedSkillSystem),
|
||||
);
|
||||
return ProgressTickResult(state: nextState);
|
||||
}
|
||||
@@ -791,22 +829,25 @@ class ProgressService {
|
||||
);
|
||||
}
|
||||
|
||||
/// 전투 틱 처리
|
||||
/// 전투 틱 처리 (스킬 자동 사용 포함)
|
||||
///
|
||||
/// [state] 현재 게임 상태
|
||||
/// [combat] 현재 전투 상태
|
||||
/// [skillSystem] 스킬 시스템 상태
|
||||
/// [elapsedMs] 경과 시간 (밀리초)
|
||||
/// Returns: 업데이트된 전투 상태
|
||||
CombatState _processCombatTick(
|
||||
/// Returns: 업데이트된 전투 상태 및 스킬 시스템 상태
|
||||
({CombatState combat, SkillSystemState skillSystem}) _processCombatTickWithSkills(
|
||||
GameState state,
|
||||
CombatState combat,
|
||||
SkillSystemState skillSystem,
|
||||
int elapsedMs,
|
||||
) {
|
||||
if (!combat.isActive || combat.isCombatOver) {
|
||||
return combat;
|
||||
return (combat: combat, skillSystem: skillSystem);
|
||||
}
|
||||
|
||||
final calculator = CombatCalculator(rng: state.rng);
|
||||
final skillService = SkillService(rng: state.rng);
|
||||
var playerStats = combat.playerStats;
|
||||
var monsterStats = combat.monsterStats;
|
||||
var playerAccumulator = combat.playerAttackAccumulatorMs + elapsedMs;
|
||||
@@ -814,15 +855,66 @@ class ProgressService {
|
||||
var totalDamageDealt = combat.totalDamageDealt;
|
||||
var totalDamageTaken = combat.totalDamageTaken;
|
||||
var turnsElapsed = combat.turnsElapsed;
|
||||
var updatedSkillSystem = skillSystem;
|
||||
|
||||
// 플레이어 공격 체크
|
||||
if (playerAccumulator >= playerStats.attackDelayMs) {
|
||||
final attackResult = calculator.playerAttackMonster(
|
||||
attacker: playerStats,
|
||||
defender: monsterStats,
|
||||
// 스킬 자동 선택
|
||||
final availableSkillIds = updatedSkillSystem.skillStates
|
||||
.map((s) => s.skillId)
|
||||
.toList();
|
||||
// 기본 스킬이 없으면 기본 스킬 추가
|
||||
if (availableSkillIds.isEmpty) {
|
||||
availableSkillIds.addAll(SkillData.defaultSkillIds);
|
||||
}
|
||||
|
||||
final selectedSkill = skillService.selectAutoSkill(
|
||||
player: playerStats,
|
||||
monster: monsterStats,
|
||||
skillSystem: updatedSkillSystem,
|
||||
availableSkillIds: availableSkillIds,
|
||||
);
|
||||
monsterStats = attackResult.updatedDefender;
|
||||
totalDamageDealt += attackResult.result.damage;
|
||||
|
||||
if (selectedSkill != null && selectedSkill.isAttack) {
|
||||
// 공격 스킬 사용
|
||||
final skillResult = skillService.useAttackSkill(
|
||||
skill: selectedSkill,
|
||||
player: playerStats,
|
||||
monster: monsterStats,
|
||||
skillSystem: updatedSkillSystem,
|
||||
);
|
||||
playerStats = skillResult.updatedPlayer;
|
||||
monsterStats = skillResult.updatedMonster;
|
||||
totalDamageDealt += skillResult.result.damage;
|
||||
updatedSkillSystem = skillResult.updatedSkillSystem;
|
||||
} else if (selectedSkill != null && selectedSkill.isHeal) {
|
||||
// 회복 스킬 사용
|
||||
final skillResult = skillService.useHealSkill(
|
||||
skill: selectedSkill,
|
||||
player: playerStats,
|
||||
skillSystem: updatedSkillSystem,
|
||||
);
|
||||
playerStats = skillResult.updatedPlayer;
|
||||
updatedSkillSystem = skillResult.updatedSkillSystem;
|
||||
} else if (selectedSkill != null && selectedSkill.isBuff) {
|
||||
// 버프 스킬 사용
|
||||
final skillResult = skillService.useBuffSkill(
|
||||
skill: selectedSkill,
|
||||
player: playerStats,
|
||||
skillSystem: updatedSkillSystem,
|
||||
);
|
||||
playerStats = skillResult.updatedPlayer;
|
||||
updatedSkillSystem = skillResult.updatedSkillSystem;
|
||||
} else {
|
||||
// 일반 공격
|
||||
final attackResult = calculator.playerAttackMonster(
|
||||
attacker: playerStats,
|
||||
defender: monsterStats,
|
||||
);
|
||||
monsterStats = attackResult.updatedDefender;
|
||||
totalDamageDealt += attackResult.result.damage;
|
||||
}
|
||||
|
||||
playerAccumulator -= playerStats.attackDelayMs;
|
||||
turnsElapsed++;
|
||||
}
|
||||
@@ -841,15 +933,18 @@ class ProgressService {
|
||||
// 전투 종료 체크
|
||||
final isActive = playerStats.isAlive && monsterStats.isAlive;
|
||||
|
||||
return combat.copyWith(
|
||||
playerStats: playerStats,
|
||||
monsterStats: monsterStats,
|
||||
playerAttackAccumulatorMs: playerAccumulator,
|
||||
monsterAttackAccumulatorMs: monsterAccumulator,
|
||||
totalDamageDealt: totalDamageDealt,
|
||||
totalDamageTaken: totalDamageTaken,
|
||||
turnsElapsed: turnsElapsed,
|
||||
isActive: isActive,
|
||||
return (
|
||||
combat: combat.copyWith(
|
||||
playerStats: playerStats,
|
||||
monsterStats: monsterStats,
|
||||
playerAttackAccumulatorMs: playerAccumulator,
|
||||
monsterAttackAccumulatorMs: monsterAccumulator,
|
||||
totalDamageDealt: totalDamageDealt,
|
||||
totalDamageTaken: totalDamageTaken,
|
||||
turnsElapsed: turnsElapsed,
|
||||
isActive: isActive,
|
||||
),
|
||||
skillSystem: updatedSkillSystem,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user