feat(skill): DamageType 및 magAtk/magDef 스킬 시스템 추가

- DamageType enum 추가 (physical/magical)
- 스킬별 데미지 타입 지정 기능 구현
- 마법 스킬 데미지에 magAtk/magDef 적용
- 장비 아이템에서 magAtk/magDef 스탯 추출
- 관련 테스트 업데이트
This commit is contained in:
JiWoong Sul
2026-01-15 23:22:36 +09:00
parent 525e231c06
commit b0913a24ff
8 changed files with 254 additions and 39 deletions

View File

@@ -24,6 +24,7 @@ void main() {
level: 1,
atk: 10,
def: 50, // DEF 50 → 50 * 0.4 = 20 감소
magDef: 50,
hpMax: 100,
hpCurrent: 100,
criRate: 0.05,
@@ -63,6 +64,7 @@ void main() {
level: 1,
atk: 10,
def: 0, // DEF 0
magDef: 0,
hpMax: 500,
hpCurrent: 500,
criRate: 0.05,
@@ -100,6 +102,7 @@ void main() {
level: 1,
atk: 10,
def: 0,
magDef: 0,
hpMax: 100,
hpCurrent: 100,
criRate: 0.05,
@@ -135,6 +138,7 @@ void main() {
level: 1,
atk: 100,
def: 0,
magDef: 0,
hpMax: 100,
hpCurrent: 100,
criRate: 0.0, // 크리티컬 없음
@@ -174,6 +178,7 @@ void main() {
level: 1,
atk: 100,
def: 0,
magDef: 0,
hpMax: 100,
hpCurrent: 100,
criRate: 0.0,
@@ -221,6 +226,7 @@ void main() {
level: 1,
atk: 10,
def: 10,
magDef: 10,
hpMax: 100,
hpCurrent: 100,
criRate: 0.05,
@@ -255,6 +261,7 @@ void main() {
level: 1,
atk: 10,
def: 5,
magDef: 5,
hpMax: 50,
hpCurrent: 50,
criRate: 0.05,
@@ -270,6 +277,7 @@ void main() {
level: 10,
atk: 50,
def: 20,
magDef: 20,
hpMax: 500,
hpCurrent: 500,
criRate: 0.05,
@@ -314,6 +322,7 @@ void main() {
level: 1,
atk: 30,
def: 10,
magDef: 10,
hpMax: 80,
hpCurrent: 80,
criRate: 0.05,

View File

@@ -103,6 +103,7 @@ void main() {
level: 1,
atk: 10,
def: 50, // DEF 50 → 50 * 0.3 = 15 감소
magDef: 50,
hpMax: 500,
hpCurrent: 500,
criRate: 0.05,
@@ -124,7 +125,7 @@ void main() {
// ATK 100 * 2.0 - DEF 50 * 0.3 = 200 - 15 = 185
expect(result.result.success, isTrue);
expect(result.result.damage, equals(185));
expect(result.updatedPlayer.mpCurrent, equals(40)); // 50 - 10
expect(result.updatedPlayer.mpCurrent, equals(20)); // 50 - 30 (mpCost 30)
expect(result.updatedMonster.hpCurrent, equals(315)); // 500 - 185
});
@@ -143,6 +144,7 @@ void main() {
level: 1,
atk: 10,
def: 0,
magDef: 0,
hpMax: 500,
hpCurrent: 500,
criRate: 0.05,
@@ -196,6 +198,7 @@ void main() {
level: 1,
atk: 10,
def: 0,
magDef: 0,
hpMax: 500,
hpCurrent: 500,
criRate: 0.05,
@@ -217,7 +220,7 @@ void main() {
// 랭크 1: 1.0x → ATK 100 * 2.0 * 1.0 = 200
expect(result.result.damage, equals(200));
expect(result.updatedPlayer.mpCurrent, equals(40)); // MP 10 소모
expect(result.updatedPlayer.mpCurrent, equals(20)); // MP 30 소모
});
test('랭크 5 데미지 스케일링', () {
@@ -235,6 +238,7 @@ void main() {
level: 1,
atk: 10,
def: 0,
magDef: 0,
hpMax: 500,
hpCurrent: 500,
criRate: 0.05,
@@ -254,12 +258,12 @@ void main() {
rank: 5,
);
// 랭크 5: 1.6x (1.0 + 4 * 0.15)
// ATK 100 * 2.0 * 1.6 = 320
expect(result.result.damage, equals(320));
// 랭크 5: 1.32x (1.0 + 4 * 0.08) - 랭크 배율 하향
// ATK 100 * 2.0 * 1.32 = 264
expect(result.result.damage, equals(264));
// MP 비용: 10 * (1.0 - 4 * 0.03) = 10 * 0.88 = 9 (반올림)
expect(result.updatedPlayer.mpCurrent, equals(41)); // 50 - 9
// MP 비용: 30 * (1.0 - 4 * 0.03) = 30 * 0.88 = 26 (반올림)
expect(result.updatedPlayer.mpCurrent, equals(24)); // 50 - 26
});
});
@@ -268,12 +272,12 @@ void main() {
final rng = DeterministicRandom(42);
final service = SkillService(rng: rng);
const skill = SkillData.hotReload; // healAmount: 30
const skill = SkillData.hotReload; // healAmount: 30, mpCost: 80
final player = CombatStats.empty().copyWith(
hpMax: 200,
hpCurrent: 100,
mpMax: 100,
mpCurrent: 50,
mpMax: 200,
mpCurrent: 150,
);
final skillSystem = SkillSystemState.empty().copyWith(elapsedMs: 5000);
@@ -286,7 +290,7 @@ void main() {
expect(result.result.success, isTrue);
expect(result.result.healedAmount, equals(30));
expect(result.updatedPlayer.hpCurrent, equals(130)); // 100 + 30
expect(result.updatedPlayer.mpCurrent, equals(35)); // 50 - 15
expect(result.updatedPlayer.mpCurrent, equals(70)); // 150 - 80
});
test('퍼센트 회복량', () {
@@ -483,6 +487,7 @@ void main() {
level: 1,
atk: 10,
def: 10,
magDef: 10,
hpMax: 100,
hpCurrent: 100,
criRate: 0.05,
@@ -512,14 +517,15 @@ void main() {
final player = CombatStats.empty().copyWith(
hpMax: 100,
hpCurrent: 20, // 20% HP
mpMax: 100,
mpCurrent: 80,
mpMax: 200,
mpCurrent: 150, // 힐 스킬 사용 가능한 MP (garbageCollection: 130)
);
final monster = MonsterCombatStats(
name: 'Test Monster',
level: 1,
atk: 10,
def: 10,
magDef: 10,
hpMax: 100,
hpCurrent: 100,
criRate: 0.05,
@@ -548,10 +554,10 @@ void main() {
final rng = DeterministicRandom(42);
final service = SkillService(rng: rng);
const skill = SkillData.debugMode; // ATK +25% 버프
const skill = SkillData.debugMode; // ATK +25% 버프, mpCost: 100
final player = CombatStats.empty().copyWith(
mpMax: 100,
mpCurrent: 50,
mpMax: 200,
mpCurrent: 150,
);
final skillSystem = SkillSystemState.empty().copyWith(elapsedMs: 5000);
@@ -568,7 +574,7 @@ void main() {
equals(0.25),
);
expect(result.updatedSkillSystem.activeBuffs.length, equals(1));
expect(result.updatedPlayer.mpCurrent, equals(30)); // 50 - 20
expect(result.updatedPlayer.mpCurrent, equals(50)); // 150 - 100
});
test('중복 버프 제거 후 새 버프 적용', () {
@@ -648,11 +654,12 @@ void main() {
group('getRankMultiplier', () {
test('랭크별 배율 계산', () {
// 랭크 배율 하향: 0.15 → 0.08 per rank
expect(getRankMultiplier(1), equals(1.0));
expect(getRankMultiplier(2), closeTo(1.15, 0.001));
expect(getRankMultiplier(3), closeTo(1.30, 0.001));
expect(getRankMultiplier(5), closeTo(1.60, 0.001));
expect(getRankMultiplier(10), closeTo(2.35, 0.001));
expect(getRankMultiplier(2), closeTo(1.08, 0.001));
expect(getRankMultiplier(3), closeTo(1.16, 0.001));
expect(getRankMultiplier(5), closeTo(1.32, 0.001));
expect(getRankMultiplier(10), closeTo(1.72, 0.001));
});
});