feat(combat): 레벨 페널티 및 확률 캡 상수 추가
- 레벨 차이에 따른 확률 감소 배율 함수 추가 - 확률 캡 상수 정의 (크리티컬 50%, 회피 40%, 방어 50%, 패리 35%) - Phase 12 밸런스 조정
This commit is contained in:
@@ -7,6 +7,51 @@ import 'package:asciineverdie/src/core/model/race_traits.dart';
|
||||
/// 기본 Stats와 Equipment를 기반으로 계산되는 전투 관련 수치.
|
||||
/// 불변(immutable) 객체로 설계되어 상태 변경 시 새 인스턴스 생성.
|
||||
class CombatStats {
|
||||
// ============================================================================
|
||||
// 레벨 페널티 상수 (Phase 12)
|
||||
// ============================================================================
|
||||
|
||||
/// 1레벨당 확률 감소율 (8%)
|
||||
static const double _levelPenaltyPerLevel = 0.08;
|
||||
|
||||
/// 최저 페널티 배율 (20%)
|
||||
static const double _minLevelMultiplier = 0.2;
|
||||
|
||||
// ============================================================================
|
||||
// 확률 캡 상수 (Phase 12)
|
||||
// ============================================================================
|
||||
|
||||
/// 크리티컬 확률 최대 (50%)
|
||||
static const double _maxCriRate = 0.5;
|
||||
|
||||
/// 회피율 최대 (40%)
|
||||
static const double _maxEvasion = 0.4;
|
||||
|
||||
/// 방패 방어율 최대 (50%)
|
||||
static const double _maxBlockRate = 0.5;
|
||||
|
||||
/// 무기 쳐내기 최대 (35%)
|
||||
static const double _maxParryRate = 0.35;
|
||||
|
||||
// ============================================================================
|
||||
// 레벨 페널티 함수 (Phase 12)
|
||||
// ============================================================================
|
||||
|
||||
/// 레벨 차이에 따른 확률 감소 배율 (플레이어 전용)
|
||||
///
|
||||
/// - levelDiff = monsterLevel - playerLevel (몬스터가 높으면 양수)
|
||||
/// - 0레벨 차이: 1.0 (100% 유지)
|
||||
/// - 10레벨 이상 차이: 0.2 (20% = 최저)
|
||||
/// - 상승 없음 (플레이어가 높아도 보너스 없음)
|
||||
static double _getLevelPenalty(int playerLevel, int monsterLevel) {
|
||||
final levelDiff = monsterLevel - playerLevel;
|
||||
if (levelDiff <= 0) return 1.0; // 플레이어가 높거나 같으면 페널티 없음
|
||||
|
||||
// 1레벨당 8%씩 감소 (100% → 92% → 84% → ... → 20%)
|
||||
final penalty = 1.0 - (levelDiff * _levelPenaltyPerLevel);
|
||||
return penalty.clamp(_minLevelMultiplier, 1.0);
|
||||
}
|
||||
|
||||
const CombatStats({
|
||||
// 기본 스탯 (Stats에서 복사)
|
||||
required this.str,
|
||||
@@ -204,13 +249,17 @@ class CombatStats {
|
||||
/// [level] 캐릭터 레벨 (스케일링용)
|
||||
/// [race] 종족 특성 (선택사항, Phase 5)
|
||||
/// [klass] 클래스 특성 (선택사항, Phase 5)
|
||||
/// [monsterLevel] 상대 몬스터 레벨 (레벨 페널티 계산용, Phase 12)
|
||||
factory CombatStats.fromStats({
|
||||
required Stats stats,
|
||||
required Equipment equipment,
|
||||
required int level,
|
||||
RaceTraits? race,
|
||||
ClassTraits? klass,
|
||||
int? monsterLevel,
|
||||
}) {
|
||||
// 레벨 페널티 계산 (Phase 12)
|
||||
final levelPenalty = _getLevelPenalty(level, monsterLevel ?? level);
|
||||
// 장비 총 스탯 가져오기
|
||||
final equipStats = equipment.totalStats;
|
||||
|
||||
@@ -362,9 +411,21 @@ class CombatStats {
|
||||
klass?.getPassiveValue(ClassPassiveType.criticalBonus) ?? 0.0;
|
||||
criRate += classCritBonus;
|
||||
|
||||
// 최종 클램핑
|
||||
criRate = criRate.clamp(0.05, 0.8);
|
||||
evasion = evasion.clamp(0.0, 0.6);
|
||||
// ========================================================================
|
||||
// 레벨 페널티 및 최종 클램핑 (Phase 12)
|
||||
// ========================================================================
|
||||
|
||||
// 레벨 페널티 적용 (크리/회피/블록/패리)
|
||||
criRate *= levelPenalty;
|
||||
evasion *= levelPenalty;
|
||||
var finalBlockRate = blockRate * levelPenalty;
|
||||
var finalParryRate = parryRate * levelPenalty;
|
||||
|
||||
// 최종 클램핑 (새 캡 적용)
|
||||
criRate = criRate.clamp(0.05, _maxCriRate);
|
||||
evasion = evasion.clamp(0.0, _maxEvasion);
|
||||
finalBlockRate = finalBlockRate.clamp(0.0, _maxBlockRate);
|
||||
finalParryRate = finalParryRate.clamp(0.0, _maxParryRate);
|
||||
|
||||
return CombatStats(
|
||||
str: effectiveStr,
|
||||
@@ -381,8 +442,8 @@ class CombatStats {
|
||||
criDamage: criDamage,
|
||||
evasion: evasion,
|
||||
accuracy: accuracy,
|
||||
blockRate: blockRate,
|
||||
parryRate: parryRate,
|
||||
blockRate: finalBlockRate,
|
||||
parryRate: finalParryRate,
|
||||
attackDelayMs: attackDelayMs,
|
||||
hpMax: totalHpMax,
|
||||
hpCurrent: stats.hp.clamp(0, totalHpMax),
|
||||
|
||||
Reference in New Issue
Block a user