feat(ui): HP/MP 바 개선 및 전투 시스템 UI 업데이트
- HP/MP 변화 시 플래시 효과 및 변화량 표시 추가 - 전투 중 몬스터 HP 바 표시 기능 추가 - 몬스터 HP 바 Row 오버플로우 버그 수정 (Flexible 적용) - 전투 상태 및 이벤트 모델 개선 - 캐릭터 애니메이션 및 전투 컴포저 업데이트
This commit is contained in:
@@ -10,6 +10,7 @@ import 'package:askiineverdie/src/core/animation/character_frames.dart';
|
||||
import 'package:askiineverdie/src/core/animation/monster_size.dart';
|
||||
import 'package:askiineverdie/src/core/animation/weapon_category.dart';
|
||||
import 'package:askiineverdie/src/core/constants/ascii_colors.dart';
|
||||
import 'package:askiineverdie/src/core/model/combat_event.dart';
|
||||
import 'package:askiineverdie/src/core/model/game_state.dart';
|
||||
|
||||
/// ASCII 애니메이션 카드 위젯
|
||||
@@ -30,6 +31,7 @@ class AsciiAnimationCard extends StatefulWidget {
|
||||
this.characterLevel,
|
||||
this.monsterLevel,
|
||||
this.isPaused = false,
|
||||
this.latestCombatEvent,
|
||||
});
|
||||
|
||||
final TaskType taskType;
|
||||
@@ -57,6 +59,9 @@ class AsciiAnimationCard extends StatefulWidget {
|
||||
/// 몬스터 레벨 (몬스터 크기 결정용)
|
||||
final int? monsterLevel;
|
||||
|
||||
/// 최근 전투 이벤트 (애니메이션 동기화용)
|
||||
final CombatEvent? latestCombatEvent;
|
||||
|
||||
@override
|
||||
State<AsciiAnimationCard> createState() => _AsciiAnimationCardState();
|
||||
}
|
||||
@@ -90,6 +95,10 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
||||
int _phaseIndex = 0;
|
||||
int _phaseFrameCount = 0;
|
||||
|
||||
// 전투 이벤트 동기화용 (Phase 5)
|
||||
int? _lastEventTimestamp;
|
||||
bool _showCriticalEffect = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -124,6 +133,12 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
||||
return;
|
||||
}
|
||||
|
||||
// 전투 이벤트 동기화 (Phase 5)
|
||||
if (widget.latestCombatEvent != null &&
|
||||
widget.latestCombatEvent!.timestamp != _lastEventTimestamp) {
|
||||
_handleCombatEvent(widget.latestCombatEvent!);
|
||||
}
|
||||
|
||||
if (oldWidget.taskType != widget.taskType ||
|
||||
oldWidget.monsterBaseName != widget.monsterBaseName ||
|
||||
oldWidget.weaponName != widget.weaponName ||
|
||||
@@ -133,6 +148,45 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 전투 이벤트에 따라 애니메이션 페이즈 강제 전환 (Phase 5)
|
||||
void _handleCombatEvent(CombatEvent event) {
|
||||
_lastEventTimestamp = event.timestamp;
|
||||
|
||||
// 전투 모드가 아니면 무시
|
||||
if (!_isBattleMode) return;
|
||||
|
||||
// 이벤트 타입에 따라 페이즈 강제 전환
|
||||
final (targetPhase, isCritical) = switch (event.type) {
|
||||
// 플레이어 공격 → attack 페이즈
|
||||
CombatEventType.playerAttack => (BattlePhase.attack, event.isCritical),
|
||||
CombatEventType.playerSkill => (BattlePhase.attack, event.isCritical),
|
||||
|
||||
// 몬스터 공격/플레이어 피격 → hit 페이즈
|
||||
CombatEventType.monsterAttack => (BattlePhase.hit, false),
|
||||
CombatEventType.playerBlock => (BattlePhase.hit, false),
|
||||
CombatEventType.playerParry => (BattlePhase.hit, false),
|
||||
|
||||
// 회피 → recover 페이즈 (빠른 회피 동작)
|
||||
CombatEventType.playerEvade => (BattlePhase.recover, false),
|
||||
CombatEventType.monsterEvade => (BattlePhase.idle, false),
|
||||
|
||||
// 회복/버프 → idle 페이즈 유지
|
||||
CombatEventType.playerHeal => (BattlePhase.idle, false),
|
||||
CombatEventType.playerBuff => (BattlePhase.idle, false),
|
||||
};
|
||||
|
||||
setState(() {
|
||||
_battlePhase = targetPhase;
|
||||
_battleSubFrame = 0;
|
||||
_phaseFrameCount = 0;
|
||||
_showCriticalEffect = isCritical;
|
||||
|
||||
// 페이즈 인덱스 동기화
|
||||
_phaseIndex = _battlePhaseSequence.indexWhere((p) => p.$1 == targetPhase);
|
||||
if (_phaseIndex < 0) _phaseIndex = 0;
|
||||
});
|
||||
}
|
||||
|
||||
/// 현재 상태를 유지하면서 타이머만 재시작
|
||||
void _restartTimer() {
|
||||
_timer?.cancel();
|
||||
@@ -286,6 +340,8 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
||||
_phaseIndex = (_phaseIndex + 1) % _battlePhaseSequence.length;
|
||||
_phaseFrameCount = 0;
|
||||
_battleSubFrame = 0;
|
||||
// 크리티컬 이펙트 리셋 (페이즈 전환 시)
|
||||
_showCriticalEffect = false;
|
||||
} else {
|
||||
_battleSubFrame++;
|
||||
}
|
||||
@@ -376,17 +432,23 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
||||
frameText = _animationData.frames[frameIndex];
|
||||
}
|
||||
|
||||
// 특수 애니메이션 중이면 테두리 표시
|
||||
// 테두리 효과 결정 (특수 애니메이션 또는 크리티컬 히트)
|
||||
final isSpecial = _currentSpecialAnimation != null;
|
||||
Border? borderEffect;
|
||||
if (_showCriticalEffect) {
|
||||
// 크리티컬 히트: 노란색 테두리 (Phase 5)
|
||||
borderEffect = Border.all(color: Colors.yellow.withValues(alpha: 0.8), width: 2);
|
||||
} else if (isSpecial) {
|
||||
// 특수 애니메이션: 시안 테두리
|
||||
borderEffect = Border.all(color: AsciiColors.positive.withValues(alpha: 0.5));
|
||||
}
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
color: bgColor,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
border: isSpecial
|
||||
? Border.all(color: AsciiColors.positive.withValues(alpha: 0.5))
|
||||
: null,
|
||||
border: borderEffect,
|
||||
),
|
||||
child: _isBattleMode
|
||||
? LayoutBuilder(
|
||||
|
||||
Reference in New Issue
Block a user