feat(game): 게임 시스템 전면 개편 및 다국어 지원 확장
## 스킬 시스템 개선 - skill_data.dart: 스킬 데이터 구조 전면 개편 (+1176 라인) - skill_service.dart: 스킬 발동 로직 확장 및 버프 시스템 연동 - skill.dart: 스킬 모델 개선, 쿨다운/효과 타입 추가 ## Canvas 애니메이션 리팩토링 - battle_composer.dart 삭제 (레거시 위젯 기반 렌더러) - monster_colors.dart 삭제 (AsciiCell 색상 시스템으로 통합) - canvas_battle_composer.dart: z-index 정렬 (몬스터 z=1, 캐릭터 z=2, 이펙트 z=3) - ascii_cell.dart, ascii_layer.dart: 코드 정리 ## UI/UX 개선 - hp_mp_bar.dart: l10n 적용, 몬스터 HP 바 컴팩트화 - death_overlay.dart: 사망 화면 개선 - equipment_stats_panel.dart: 장비 스탯 표시 확장 - active_buff_panel.dart: 버프 패널 개선 - notification_overlay.dart: 알림 시스템 개선 ## 다국어 지원 확장 - game_text_l10n.dart: 게임 텍스트 통합 (+758 라인) - 한국어/일본어/영어/중국어 번역 업데이트 - ARB 파일 동기화 ## 게임 로직 개선 - progress_service.dart: 진행 로직 리팩토링 - combat_calculator.dart: 전투 계산 로직 개선 - stat_calculator.dart: 스탯 계산 시스템 개선 - story_service.dart: 스토리 진행 로직 개선 ## 기타 - theme_preferences.dart 삭제 (미사용) - 테스트 파일 업데이트 - class_data.dart: 클래스 데이터 정리
This commit is contained in:
@@ -117,6 +117,9 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
||||
// 전투 이벤트 동기화용 (Phase 5)
|
||||
int? _lastEventTimestamp;
|
||||
bool _showCriticalEffect = false;
|
||||
bool _showBlockEffect = false;
|
||||
bool _showParryEffect = false;
|
||||
bool _showSkillEffect = false;
|
||||
|
||||
// 특수 애니메이션 프레임 수는 ascii_animation_type.dart의
|
||||
// specialAnimationFrameCounts 상수 사용
|
||||
@@ -177,33 +180,114 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
||||
// 전투 모드가 아니면 무시
|
||||
if (_animationMode != AnimationMode.battle) return;
|
||||
|
||||
// 이벤트 타입에 따라 페이즈 강제 전환
|
||||
final (targetPhase, isCritical) = switch (event.type) {
|
||||
// 이벤트 타입에 따라 페이즈 및 효과 결정
|
||||
final (
|
||||
targetPhase,
|
||||
isCritical,
|
||||
isBlock,
|
||||
isParry,
|
||||
isSkill,
|
||||
) = switch (event.type) {
|
||||
// 플레이어 공격 → attack 페이즈
|
||||
CombatEventType.playerAttack => (BattlePhase.attack, event.isCritical),
|
||||
CombatEventType.playerSkill => (BattlePhase.attack, event.isCritical),
|
||||
CombatEventType.playerAttack => (
|
||||
BattlePhase.attack,
|
||||
event.isCritical,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
// 스킬 사용 → attack 페이즈 + 스킬 이펙트
|
||||
CombatEventType.playerSkill => (
|
||||
BattlePhase.attack,
|
||||
event.isCritical,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
|
||||
// 몬스터 공격/플레이어 피격 → hit 페이즈
|
||||
CombatEventType.monsterAttack => (BattlePhase.hit, false),
|
||||
CombatEventType.playerBlock => (BattlePhase.hit, false),
|
||||
CombatEventType.playerParry => (BattlePhase.hit, false),
|
||||
// 몬스터 공격 → hit 페이즈
|
||||
CombatEventType.monsterAttack => (
|
||||
BattlePhase.hit,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
// 블록 → hit 페이즈 + 블록 이펙트
|
||||
CombatEventType.playerBlock => (
|
||||
BattlePhase.hit,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
// 패리 → hit 페이즈 + 패리 이펙트
|
||||
CombatEventType.playerParry => (
|
||||
BattlePhase.hit,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
|
||||
// 회피 → recover 페이즈 (빠른 회피 동작)
|
||||
CombatEventType.playerEvade => (BattlePhase.recover, false),
|
||||
CombatEventType.monsterEvade => (BattlePhase.idle, false),
|
||||
CombatEventType.playerEvade => (
|
||||
BattlePhase.recover,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
CombatEventType.monsterEvade => (
|
||||
BattlePhase.idle,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
|
||||
// 회복/버프 → idle 페이즈 유지
|
||||
CombatEventType.playerHeal => (BattlePhase.idle, false),
|
||||
CombatEventType.playerBuff => (BattlePhase.idle, false),
|
||||
CombatEventType.playerHeal => (
|
||||
BattlePhase.idle,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
CombatEventType.playerBuff => (
|
||||
BattlePhase.idle,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
|
||||
// DOT 틱 → attack 페이즈 (지속 피해)
|
||||
CombatEventType.dotTick => (BattlePhase.attack, false),
|
||||
CombatEventType.dotTick => (
|
||||
BattlePhase.attack,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
|
||||
// 물약 사용 → idle 페이즈 유지
|
||||
CombatEventType.playerPotion => (BattlePhase.idle, false),
|
||||
CombatEventType.playerPotion => (
|
||||
BattlePhase.idle,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
|
||||
// 물약 드랍 → idle 페이즈 유지
|
||||
CombatEventType.potionDrop => (BattlePhase.idle, false),
|
||||
CombatEventType.potionDrop => (
|
||||
BattlePhase.idle,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
};
|
||||
|
||||
setState(() {
|
||||
@@ -211,6 +295,9 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
||||
_battleSubFrame = 0;
|
||||
_phaseFrameCount = 0;
|
||||
_showCriticalEffect = isCritical;
|
||||
_showBlockEffect = isBlock;
|
||||
_showParryEffect = isParry;
|
||||
_showSkillEffect = isSkill;
|
||||
|
||||
// 페이즈 인덱스 동기화
|
||||
_phaseIndex = _battlePhaseSequence.indexWhere((p) => p.$1 == targetPhase);
|
||||
@@ -322,8 +409,11 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
||||
_phaseIndex = (_phaseIndex + 1) % _battlePhaseSequence.length;
|
||||
_phaseFrameCount = 0;
|
||||
_battleSubFrame = 0;
|
||||
// 크리티컬 이펙트 리셋 (페이즈 전환 시)
|
||||
// 이펙트 리셋 (페이즈 전환 시)
|
||||
_showCriticalEffect = false;
|
||||
_showBlockEffect = false;
|
||||
_showParryEffect = false;
|
||||
_showSkillEffect = false;
|
||||
} else {
|
||||
_battleSubFrame++;
|
||||
}
|
||||
@@ -340,21 +430,22 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
||||
/// 현재 애니메이션 레이어 생성
|
||||
List<AsciiLayer> _composeLayers() {
|
||||
return switch (_animationMode) {
|
||||
AnimationMode.battle => _battleComposer?.composeLayers(
|
||||
_battlePhase,
|
||||
_battleSubFrame,
|
||||
widget.monsterBaseName,
|
||||
_environment,
|
||||
_globalTick,
|
||||
) ??
|
||||
[AsciiLayer.empty()],
|
||||
AnimationMode.battle =>
|
||||
_battleComposer?.composeLayers(
|
||||
_battlePhase,
|
||||
_battleSubFrame,
|
||||
widget.monsterBaseName,
|
||||
_environment,
|
||||
_globalTick,
|
||||
) ??
|
||||
[AsciiLayer.empty()],
|
||||
AnimationMode.walking => _walkingComposer.composeLayers(_globalTick),
|
||||
AnimationMode.town => _townComposer.composeLayers(_globalTick),
|
||||
AnimationMode.special => _specialComposer.composeLayers(
|
||||
_currentSpecialAnimation ?? AsciiAnimationType.levelUp,
|
||||
_currentFrame,
|
||||
_globalTick,
|
||||
),
|
||||
_currentSpecialAnimation ?? AsciiAnimationType.levelUp,
|
||||
_currentFrame,
|
||||
_globalTick,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -363,17 +454,38 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
||||
// Phase 7: 고정 4색 팔레트 사용 (colorTheme 무시)
|
||||
const bgColor = AsciiColors.background;
|
||||
|
||||
// 테두리 효과 결정 (특수 애니메이션 또는 크리티컬 히트)
|
||||
// 테두리 효과 결정 (전투 이벤트 또는 특수 애니메이션)
|
||||
final isSpecial = _currentSpecialAnimation != null;
|
||||
Border? borderEffect;
|
||||
if (_showCriticalEffect) {
|
||||
// 크리티컬 히트: 노란색 테두리 (Phase 5)
|
||||
borderEffect =
|
||||
Border.all(color: Colors.yellow.withValues(alpha: 0.8), width: 2);
|
||||
// 크리티컬 히트: 노란색 테두리
|
||||
borderEffect = Border.all(
|
||||
color: Colors.yellow.withValues(alpha: 0.8),
|
||||
width: 2,
|
||||
);
|
||||
} else if (_showBlockEffect) {
|
||||
// 블록 (방패 방어): 파란색 테두리
|
||||
borderEffect = Border.all(
|
||||
color: Colors.blue.withValues(alpha: 0.8),
|
||||
width: 2,
|
||||
);
|
||||
} else if (_showParryEffect) {
|
||||
// 패리 (무기 쳐내기): 주황색 테두리
|
||||
borderEffect = Border.all(
|
||||
color: Colors.orange.withValues(alpha: 0.8),
|
||||
width: 2,
|
||||
);
|
||||
} else if (_showSkillEffect) {
|
||||
// 스킬 사용: 마젠타 테두리
|
||||
borderEffect = Border.all(
|
||||
color: Colors.purple.withValues(alpha: 0.8),
|
||||
width: 2,
|
||||
);
|
||||
} else if (isSpecial) {
|
||||
// 특수 애니메이션: 시안 테두리
|
||||
borderEffect =
|
||||
Border.all(color: AsciiColors.positive.withValues(alpha: 0.5));
|
||||
borderEffect = Border.all(
|
||||
color: AsciiColors.positive.withValues(alpha: 0.5),
|
||||
);
|
||||
}
|
||||
|
||||
return Container(
|
||||
|
||||
Reference in New Issue
Block a user