diff --git a/lib/src/core/engine/progress_service.dart b/lib/src/core/engine/progress_service.dart index 52b9383..4b1e4a6 100644 --- a/lib/src/core/engine/progress_service.dart +++ b/lib/src/core/engine/progress_service.dart @@ -1118,6 +1118,7 @@ class ProgressService { skillName: selectedSkill.name, damage: skillResult.result.damage, targetName: monsterStats.name, + attackDelayMs: playerStats.attackDelayMs, ), ); } else if (selectedSkill != null && selectedSkill.isDot) { @@ -1144,6 +1145,7 @@ class ProgressService { skillName: selectedSkill.name, damage: skillResult.result.damage, targetName: monsterStats.name, + attackDelayMs: playerStats.attackDelayMs, ), ); } else if (selectedSkill != null && selectedSkill.isHeal) { @@ -1206,6 +1208,7 @@ class ProgressService { damage: result.damage, targetName: monsterStats.name, isCritical: result.isCritical, + attackDelayMs: playerStats.attackDelayMs, ), ); } @@ -1257,6 +1260,7 @@ class ProgressService { timestamp: timestamp, damage: result.damage, attackerName: monsterStats.name, + attackDelayMs: monsterStats.attackDelayMs, ), ); } diff --git a/lib/src/core/model/combat_event.dart b/lib/src/core/model/combat_event.dart index d36ffd8..ce5c456 100644 --- a/lib/src/core/model/combat_event.dart +++ b/lib/src/core/model/combat_event.dart @@ -49,6 +49,7 @@ class CombatEvent { this.isCritical = false, this.skillName, this.targetName, + this.attackDelayMs, }); /// 이벤트 타입 @@ -72,12 +73,17 @@ class CombatEvent { /// 대상 이름 (몬스터 또는 플레이어) final String? targetName; + /// 공격자의 공격 속도 (ms) + /// 애니메이션 페이즈 지속 시간 계산에 사용 + final int? attackDelayMs; + /// 플레이어 공격 이벤트 생성 factory CombatEvent.playerAttack({ required int timestamp, required int damage, required String targetName, bool isCritical = false, + int? attackDelayMs, }) { return CombatEvent( type: CombatEventType.playerAttack, @@ -85,6 +91,7 @@ class CombatEvent { damage: damage, targetName: targetName, isCritical: isCritical, + attackDelayMs: attackDelayMs, ); } @@ -93,12 +100,14 @@ class CombatEvent { required int timestamp, required int damage, required String attackerName, + int? attackDelayMs, }) { return CombatEvent( type: CombatEventType.monsterAttack, timestamp: timestamp, damage: damage, targetName: attackerName, + attackDelayMs: attackDelayMs, ); } @@ -161,6 +170,7 @@ class CombatEvent { required int damage, required String targetName, bool isCritical = false, + int? attackDelayMs, }) { return CombatEvent( type: CombatEventType.playerSkill, @@ -169,6 +179,7 @@ class CombatEvent { damage: damage, targetName: targetName, isCritical: isCritical, + attackDelayMs: attackDelayMs, ); } diff --git a/lib/src/features/game/widgets/ascii_animation_card.dart b/lib/src/features/game/widgets/ascii_animation_card.dart index 89e5bc9..2c74f85 100644 --- a/lib/src/features/game/widgets/ascii_animation_card.dart +++ b/lib/src/features/game/widgets/ascii_animation_card.dart @@ -128,6 +128,10 @@ class _AsciiAnimationCardState extends State { bool _showParryEffect = false; bool _showSkillEffect = false; + // 공격 속도 기반 동적 페이즈 프레임 수 (Phase 6) + int _eventDrivenPhaseFrames = 0; + bool _isEventDrivenPhase = false; + // 특수 애니메이션 프레임 수는 ascii_animation_type.dart의 // specialAnimationFrameCounts 상수 사용 @@ -318,6 +322,15 @@ class _AsciiAnimationCardState extends State { // 페이즈 인덱스 동기화 _phaseIndex = _battlePhaseSequence.indexWhere((p) => p.$1 == targetPhase); if (_phaseIndex < 0) _phaseIndex = 0; + + // 공격 속도에 따른 동적 페이즈 프레임 수 계산 (Phase 6) + // 200ms tick 기준으로 프레임 수 계산 (최소 2, 최대 10) + if (event.attackDelayMs != null && event.attackDelayMs! > 0) { + _eventDrivenPhaseFrames = (event.attackDelayMs! ~/ 200).clamp(2, 10); + _isEventDrivenPhase = true; + } else { + _isEventDrivenPhase = false; + } }); } @@ -434,8 +447,14 @@ class _AsciiAnimationCardState extends State { _phaseFrameCount++; final currentPhase = _battlePhaseSequence[_phaseIndex]; + // 현재 페이즈의 프레임 수 결정 (Phase 6) + // 이벤트 기반 페이즈일 경우 공격 속도에 따른 동적 프레임 수 사용 + final targetFrames = _isEventDrivenPhase + ? _eventDrivenPhaseFrames + : currentPhase.$2; + // 현재 페이즈의 프레임 수를 초과하면 다음 페이즈로 - if (_phaseFrameCount >= currentPhase.$2) { + if (_phaseFrameCount >= targetFrames) { _phaseIndex = (_phaseIndex + 1) % _battlePhaseSequence.length; _phaseFrameCount = 0; _battleSubFrame = 0; @@ -444,6 +463,8 @@ class _AsciiAnimationCardState extends State { _showBlockEffect = false; _showParryEffect = false; _showSkillEffect = false; + // 이벤트 기반 페이즈 종료 + _isEventDrivenPhase = false; } else { _battleSubFrame++; }