From f13783a35b6e2cfb8c2b70e31224045068bac0d4 Mon Sep 17 00:00:00 2001 From: JiWoong Sul Date: Mon, 5 Jan 2026 19:41:52 +0900 Subject: [PATCH] =?UTF-8?q?feat(combat):=20=EB=A0=88=EB=B2=A8=20=ED=8E=98?= =?UTF-8?q?=EB=84=90=ED=8B=B0=20=EB=B0=8F=20=ED=99=95=EB=A5=A0=20=EC=BA=A1?= =?UTF-8?q?=20=EC=83=81=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 레벨 차이에 따른 확률 감소 배율 함수 추가 - 확률 캡 상수 정의 (크리티컬 50%, 회피 40%, 방어 50%, 패리 35%) - Phase 12 밸런스 조정 --- lib/src/core/model/combat_stats.dart | 71 ++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/lib/src/core/model/combat_stats.dart b/lib/src/core/model/combat_stats.dart index 0f4ce4e..b416e27 100644 --- a/lib/src/core/model/combat_stats.dart +++ b/lib/src/core/model/combat_stats.dart @@ -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),