feat(race-class): Phase 5 종족/클래스 특화 시스템 구현

- RaceTraits: 7종족 (Byte Human, Null Elf, Buffer Dwarf 등)
- ClassTraits: 6클래스 (Bug Hunter, Compiler Mage 등)
- StatCalculator: 종족/클래스 보정 계산
- CombatStats.fromStats: 종족/클래스 패시브 효과 통합
- 종족별 스탯 보정 및 패시브 (경험치, HP/MP, 크리티컬 등)
- 클래스별 스탯 보정 및 패시브 (공격력, 방어력, 회피 등)
This commit is contained in:
JiWoong Sul
2025-12-17 17:22:28 +09:00
parent 21bf057cfc
commit d158c11249
6 changed files with 909 additions and 16 deletions

View File

@@ -0,0 +1,200 @@
import 'package:askiineverdie/src/core/model/class_traits.dart';
import 'package:askiineverdie/src/core/model/combat_stats.dart';
import 'package:askiineverdie/src/core/model/game_state.dart';
import 'package:askiineverdie/src/core/model/race_traits.dart';
/// 스탯 계산기 (stat calculator)
///
/// 기본 스탯에 종족/클래스 보정을 적용하여 최종 스탯 계산
class StatCalculator {
const StatCalculator();
/// 기본 스탯에 종족/클래스 보정 적용
///
/// [baseStats] 기본 캐릭터 스탯
/// [race] 종족 특성
/// [klass] 클래스 특성
Stats applyModifiers({
required Stats baseStats,
required RaceTraits race,
required ClassTraits klass,
}) {
// 종족 보정 적용
var str = baseStats.str + race.getModifier(StatType.str);
var con = baseStats.con + race.getModifier(StatType.con);
var dex = baseStats.dex + race.getModifier(StatType.dex);
var intel = baseStats.intelligence + race.getModifier(StatType.intelligence);
var wis = baseStats.wis + race.getModifier(StatType.wis);
var cha = baseStats.cha + race.getModifier(StatType.cha);
// 클래스 보정 적용
str += klass.getModifier(StatType.str);
con += klass.getModifier(StatType.con);
dex += klass.getModifier(StatType.dex);
intel += klass.getModifier(StatType.intelligence);
wis += klass.getModifier(StatType.wis);
cha += klass.getModifier(StatType.cha);
// HP/MP에 종족 패시브 적용
var hpMax = baseStats.hpMax;
var mpMax = baseStats.mpMax;
// 종족 HP 보너스 (Heap Troll: +20%)
final raceHpBonus = race.getPassiveValue(PassiveType.hpBonus);
if (raceHpBonus > 0) {
hpMax = (hpMax * (1 + raceHpBonus)).round();
}
// 종족 MP 보너스 (Pointer Fairy: +20%)
final raceMpBonus = race.getPassiveValue(PassiveType.mpBonus);
if (raceMpBonus > 0) {
mpMax = (mpMax * (1 + raceMpBonus)).round();
}
// 클래스 HP 보너스 (Garbage Collector: +30%)
final classHpBonus = klass.getPassiveValue(ClassPassiveType.hpBonus);
if (classHpBonus > 0) {
hpMax = (hpMax * (1 + classHpBonus)).round();
}
return baseStats.copyWith(
str: str,
con: con,
dex: dex,
intelligence: intel,
wis: wis,
cha: cha,
hpMax: hpMax,
mpMax: mpMax,
);
}
/// CombatStats에 종족/클래스 패시브 효과 적용
///
/// [combatStats] 기본 전투 스탯
/// [race] 종족 특성
/// [klass] 클래스 특성
CombatStats applyPassives({
required CombatStats combatStats,
required RaceTraits race,
required ClassTraits klass,
}) {
var atk = combatStats.atk;
var def = combatStats.def;
var magAtk = combatStats.magAtk;
var criRate = combatStats.criRate;
var evasion = combatStats.evasion;
// 종족 패시브 적용
// 마법 데미지 보너스 (Null Elf: +15%)
final raceMagicBonus = race.getPassiveValue(PassiveType.magicDamageBonus);
if (raceMagicBonus > 0) {
magAtk = (magAtk * (1 + raceMagicBonus)).round();
}
// 방어력 보너스 (Buffer Dwarf: +10%)
final raceDefenseBonus = race.getPassiveValue(PassiveType.defenseBonus);
if (raceDefenseBonus > 0) {
def = (def * (1 + raceDefenseBonus)).round();
}
// 크리티컬 보너스 (Stack Goblin: +5%)
final raceCritBonus = race.getPassiveValue(PassiveType.criticalBonus);
if (raceCritBonus > 0) {
criRate = (criRate + raceCritBonus).clamp(0.0, 0.8);
}
// 클래스 패시브 적용
// 물리 공격력 보너스 (Bug Hunter: +20%)
final classPhysicalBonus = klass.getPassiveValue(ClassPassiveType.physicalDamageBonus);
if (classPhysicalBonus > 0) {
atk = (atk * (1 + classPhysicalBonus)).round();
}
// 방어력 보너스 (Debugger Paladin: +15%)
final classDefenseBonus = klass.getPassiveValue(ClassPassiveType.defenseBonus);
if (classDefenseBonus > 0) {
def = (def * (1 + classDefenseBonus)).round();
}
// 마법 데미지 보너스 (Compiler Mage: +25%)
final classMagicBonus = klass.getPassiveValue(ClassPassiveType.magicDamageBonus);
if (classMagicBonus > 0) {
magAtk = (magAtk * (1 + classMagicBonus)).round();
}
// 회피율 보너스 (Refactor Monk: +15%)
final classEvasionBonus = klass.getPassiveValue(ClassPassiveType.evasionBonus);
if (classEvasionBonus > 0) {
evasion = (evasion + classEvasionBonus).clamp(0.0, 0.6);
}
// 크리티컬 보너스 (Pointer Assassin: +20%)
final classCritBonus = klass.getPassiveValue(ClassPassiveType.criticalBonus);
if (classCritBonus > 0) {
criRate = (criRate + classCritBonus).clamp(0.0, 0.8);
}
return combatStats.copyWith(
atk: atk,
def: def,
magAtk: magAtk,
criRate: criRate,
evasion: evasion,
);
}
/// 경험치 배율 계산
///
/// 종족 경험치 배율 반환 (Byte Human: 1.10)
double calculateExpMultiplier(RaceTraits race) {
return race.expMultiplier;
}
/// 전투 후 HP 회복량 계산 (Garbage Collector 패시브)
///
/// [klass] 클래스 특성
/// [maxHp] 최대 HP
/// Returns: 회복할 HP 양
int calculatePostCombatHeal({
required ClassTraits klass,
required int maxHp,
}) {
final healRate = klass.getPassiveValue(ClassPassiveType.postCombatHeal);
if (healRate <= 0) return 0;
return (maxHp * healRate).round();
}
/// 첫 공격 배율 계산 (Pointer Assassin 패시브)
///
/// [klass] 클래스 특성
/// Returns: 첫 공격 배율 (기본 1.0)
double calculateFirstStrikeMultiplier(ClassTraits klass) {
final bonus = klass.getPassiveValue(ClassPassiveType.firstStrikeBonus);
return bonus > 0 ? bonus : 1.0;
}
/// 사망 시 보존할 장비 개수 (Coredump Undead 패시브)
///
/// [race] 종족 특성
/// Returns: 보존 장비 개수
int calculateDeathEquipmentPreserve(RaceTraits race) {
if (race.hasPassive(PassiveType.deathEquipmentPreserve)) {
return race.getPassiveValue(PassiveType.deathEquipmentPreserve).round();
}
return 0;
}
/// 연속 공격 가능 여부 (Refactor Monk 패시브)
bool hasMultiAttack(ClassTraits klass) {
return klass.hasPassive(ClassPassiveType.multiAttack);
}
/// 회복력 배율 계산 (Debugger Paladin 패시브)
double calculateHealingMultiplier(ClassTraits klass) {
final bonus = klass.getPassiveValue(ClassPassiveType.healingBonus);
return 1.0 + bonus;
}
}