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:
JiWoong Sul
2025-12-17 17:05:48 +09:00
parent 6a696ecd57
commit 517bf54a56
5 changed files with 1041 additions and 22 deletions

View File

@@ -0,0 +1,267 @@
/// 스킬 타입
enum SkillType {
/// 공격 스킬
attack,
/// 회복 스킬
heal,
/// 버프 스킬
buff,
/// 디버프 스킬
debuff,
}
/// 버프 효과
class BuffEffect {
const BuffEffect({
required this.id,
required this.name,
required this.durationMs,
this.atkModifier = 0.0,
this.defModifier = 0.0,
this.criRateModifier = 0.0,
this.evasionModifier = 0.0,
});
/// 버프 ID
final String id;
/// 버프 이름
final String name;
/// 지속 시간 (밀리초)
final int durationMs;
/// 공격력 배율 보정 (0.0 = 변화 없음, 0.5 = +50%)
final double atkModifier;
/// 방어력 배율 보정
final double defModifier;
/// 크리티컬 확률 보정
final double criRateModifier;
/// 회피율 보정
final double evasionModifier;
}
/// 스킬 정의
class Skill {
const Skill({
required this.id,
required this.name,
required this.type,
required this.mpCost,
required this.cooldownMs,
required this.power,
this.damageMultiplier = 1.0,
this.healAmount = 0,
this.healPercent = 0.0,
this.buff,
this.selfDamagePercent = 0.0,
this.targetDefReduction = 0.0,
});
/// 스킬 ID
final String id;
/// 스킬 이름
final String name;
/// 스킬 타입
final SkillType type;
/// MP 소모량
final int mpCost;
/// 쿨타임 (밀리초)
final int cooldownMs;
/// 스킬 위력 (기본 값)
final int power;
/// 데미지 배율 (공격 스킬용)
final double damageMultiplier;
/// 고정 회복량 (회복 스킬용)
final int healAmount;
/// HP% 회복 (회복 스킬용, 0.0 ~ 1.0)
final double healPercent;
/// 버프 효과 (버프/디버프 스킬용)
final BuffEffect? buff;
/// 자해 데미지 % (일부 강력한 스킬)
final double selfDamagePercent;
/// 적 방어력 감소 % (일부 공격 스킬)
final double targetDefReduction;
/// 공격 스킬 여부
bool get isAttack => type == SkillType.attack;
/// 회복 스킬 여부
bool get isHeal => type == SkillType.heal;
/// 버프 스킬 여부
bool get isBuff => type == SkillType.buff;
/// 디버프 스킬 여부
bool get isDebuff => type == SkillType.debuff;
/// MP 효율 (데미지 당 MP 비용)
double get mpEfficiency {
if (type != SkillType.attack || damageMultiplier <= 0) return 0;
return damageMultiplier / mpCost;
}
}
/// 스킬 사용 상태 (쿨타임 추적)
class SkillState {
const SkillState({
required this.skillId,
required this.lastUsedMs,
required this.rank,
});
/// 스킬 ID
final String skillId;
/// 마지막 사용 시간 (게임 내 경과 시간, 밀리초)
final int lastUsedMs;
/// 스킬 랭크 (레벨)
final int rank;
/// 쿨타임 완료 여부
bool isReady(int currentMs, int cooldownMs) {
return currentMs - lastUsedMs >= cooldownMs;
}
/// 남은 쿨타임 (밀리초)
int remainingCooldown(int currentMs, int cooldownMs) {
final elapsed = currentMs - lastUsedMs;
if (elapsed >= cooldownMs) return 0;
return cooldownMs - elapsed;
}
SkillState copyWith({
String? skillId,
int? lastUsedMs,
int? rank,
}) {
return SkillState(
skillId: skillId ?? this.skillId,
lastUsedMs: lastUsedMs ?? this.lastUsedMs,
rank: rank ?? this.rank,
);
}
/// 새 스킬 상태 생성 (쿨타임 0)
factory SkillState.fresh(String skillId, {int rank = 1}) {
return SkillState(
skillId: skillId,
lastUsedMs: -999999, // 즉시 사용 가능하도록 먼 과거
rank: rank,
);
}
}
/// 활성 버프 상태
class ActiveBuff {
const ActiveBuff({
required this.effect,
required this.startedMs,
required this.sourceSkillId,
});
/// 버프 효과
final BuffEffect effect;
/// 버프 시작 시간 (게임 내 경과 시간)
final int startedMs;
/// 버프를 발동한 스킬 ID
final String sourceSkillId;
/// 버프 만료 여부
bool isExpired(int currentMs) {
return currentMs - startedMs >= effect.durationMs;
}
/// 남은 지속 시간 (밀리초)
int remainingDuration(int currentMs) {
final elapsed = currentMs - startedMs;
if (elapsed >= effect.durationMs) return 0;
return effect.durationMs - elapsed;
}
ActiveBuff copyWith({
BuffEffect? effect,
int? startedMs,
String? sourceSkillId,
}) {
return ActiveBuff(
effect: effect ?? this.effect,
startedMs: startedMs ?? this.startedMs,
sourceSkillId: sourceSkillId ?? this.sourceSkillId,
);
}
}
/// 스킬 사용 결과
class SkillUseResult {
const SkillUseResult({
required this.skill,
required this.success,
this.damage = 0,
this.healedAmount = 0,
this.appliedBuff,
this.failReason,
});
/// 사용한 스킬
final Skill skill;
/// 성공 여부
final bool success;
/// 데미지 (공격 스킬)
final int damage;
/// 회복량 (회복 스킬)
final int healedAmount;
/// 적용된 버프 (버프 스킬)
final ActiveBuff? appliedBuff;
/// 실패 사유
final SkillFailReason? failReason;
/// 실패 결과 생성
factory SkillUseResult.failed(Skill skill, SkillFailReason reason) {
return SkillUseResult(
skill: skill,
success: false,
failReason: reason,
);
}
}
/// 스킬 실패 사유
enum SkillFailReason {
/// MP 부족
notEnoughMp,
/// 쿨타임 중
onCooldown,
/// 스킬 없음
skillNotFound,
/// 사용 불가 상태
invalidState,
}