feat(ui): 몬스터 등급 UI 및 SFX 연동
- GamePlayScreen 회피/방어/패리 SFX 추가 - TaskProgressPanel 몬스터 등급 표시 - EnhancedAnimationPanel/AsciiAnimationCard 개선 - MobileCarouselLayout 몬스터 등급 전달
This commit is contained in:
@@ -302,16 +302,21 @@ class _GamePlayScreenState extends State<GamePlayScreen>
|
|||||||
case CombatEventType.monsterAttack:
|
case CombatEventType.monsterAttack:
|
||||||
audio.playMonsterSfx('hit');
|
audio.playMonsterSfx('hit');
|
||||||
|
|
||||||
|
// 회피/방어 SFX (Phase 11)
|
||||||
|
case CombatEventType.playerEvade:
|
||||||
|
audio.playPlayerSfx('evade');
|
||||||
|
case CombatEventType.monsterEvade:
|
||||||
|
// 몬스터 회피 = 플레이어 공격 빗나감 (evade SFX)
|
||||||
|
audio.playPlayerSfx('evade');
|
||||||
|
case CombatEventType.playerBlock:
|
||||||
|
audio.playPlayerSfx('block');
|
||||||
|
case CombatEventType.playerParry:
|
||||||
|
audio.playPlayerSfx('parry');
|
||||||
|
|
||||||
// SFX 없음
|
// SFX 없음
|
||||||
case CombatEventType.dotTick:
|
case CombatEventType.dotTick:
|
||||||
// DOT 틱은 SFX 없음 (너무 자주 발생)
|
// DOT 틱은 SFX 없음 (너무 자주 발생)
|
||||||
break;
|
break;
|
||||||
case CombatEventType.playerEvade:
|
|
||||||
case CombatEventType.monsterEvade:
|
|
||||||
case CombatEventType.playerBlock:
|
|
||||||
case CombatEventType.playerParry:
|
|
||||||
// 회피/방어는 별도 SFX 없음
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -925,6 +930,7 @@ class _GamePlayScreenState extends State<GamePlayScreen>
|
|||||||
shieldName: state.equipment.shield,
|
shieldName: state.equipment.shield,
|
||||||
characterLevel: state.traits.level,
|
characterLevel: state.traits.level,
|
||||||
monsterLevel: state.progress.currentTask.monsterLevel,
|
monsterLevel: state.progress.currentTask.monsterLevel,
|
||||||
|
monsterGrade: state.progress.currentTask.monsterGrade,
|
||||||
latestCombatEvent:
|
latestCombatEvent:
|
||||||
state.progress.currentCombat?.recentEvents.lastOrNull,
|
state.progress.currentCombat?.recentEvents.lastOrNull,
|
||||||
raceId: state.traits.raceId,
|
raceId: state.traits.raceId,
|
||||||
|
|||||||
@@ -663,9 +663,11 @@ class _MobileCarouselLayoutState extends State<MobileCarouselLayout> {
|
|||||||
shieldName: state.equipment.shield,
|
shieldName: state.equipment.shield,
|
||||||
characterLevel: state.traits.level,
|
characterLevel: state.traits.level,
|
||||||
monsterLevel: state.progress.currentTask.monsterLevel,
|
monsterLevel: state.progress.currentTask.monsterLevel,
|
||||||
|
monsterGrade: state.progress.currentTask.monsterGrade,
|
||||||
latestCombatEvent:
|
latestCombatEvent:
|
||||||
state.progress.currentCombat?.recentEvents.lastOrNull,
|
state.progress.currentCombat?.recentEvents.lastOrNull,
|
||||||
raceId: state.traits.raceId,
|
raceId: state.traits.raceId,
|
||||||
|
weaponRarity: state.equipment.weaponItem.rarity,
|
||||||
),
|
),
|
||||||
|
|
||||||
// 중앙: 캐로셀 (PageView)
|
// 중앙: 캐로셀 (PageView)
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import 'package:asciineverdie/src/core/animation/weapon_category.dart';
|
|||||||
import 'package:asciineverdie/src/core/constants/ascii_colors.dart';
|
import 'package:asciineverdie/src/core/constants/ascii_colors.dart';
|
||||||
import 'package:asciineverdie/src/core/model/combat_event.dart';
|
import 'package:asciineverdie/src/core/model/combat_event.dart';
|
||||||
import 'package:asciineverdie/src/core/model/game_state.dart';
|
import 'package:asciineverdie/src/core/model/game_state.dart';
|
||||||
|
import 'package:asciineverdie/src/core/model/item_stats.dart';
|
||||||
|
import 'package:asciineverdie/src/core/model/monster_grade.dart';
|
||||||
|
|
||||||
/// 애니메이션 모드
|
/// 애니메이션 모드
|
||||||
enum AnimationMode {
|
enum AnimationMode {
|
||||||
@@ -43,9 +45,11 @@ class AsciiAnimationCard extends StatefulWidget {
|
|||||||
this.shieldName,
|
this.shieldName,
|
||||||
this.characterLevel,
|
this.characterLevel,
|
||||||
this.monsterLevel,
|
this.monsterLevel,
|
||||||
|
this.monsterGrade,
|
||||||
this.isPaused = false,
|
this.isPaused = false,
|
||||||
this.latestCombatEvent,
|
this.latestCombatEvent,
|
||||||
this.raceId,
|
this.raceId,
|
||||||
|
this.weaponRarity,
|
||||||
});
|
});
|
||||||
|
|
||||||
final TaskType taskType;
|
final TaskType taskType;
|
||||||
@@ -73,12 +77,18 @@ class AsciiAnimationCard extends StatefulWidget {
|
|||||||
/// 몬스터 레벨 (몬스터 크기 결정용)
|
/// 몬스터 레벨 (몬스터 크기 결정용)
|
||||||
final int? monsterLevel;
|
final int? monsterLevel;
|
||||||
|
|
||||||
|
/// 몬스터 등급 (Normal/Elite/Boss) - 색상/접두사 표시용
|
||||||
|
final MonsterGrade? monsterGrade;
|
||||||
|
|
||||||
/// 최근 전투 이벤트 (애니메이션 동기화용)
|
/// 최근 전투 이벤트 (애니메이션 동기화용)
|
||||||
final CombatEvent? latestCombatEvent;
|
final CombatEvent? latestCombatEvent;
|
||||||
|
|
||||||
/// 종족 ID (Phase 4: 종족별 캐릭터 애니메이션)
|
/// 종족 ID (Phase 4: 종족별 캐릭터 애니메이션)
|
||||||
final String? raceId;
|
final String? raceId;
|
||||||
|
|
||||||
|
/// 무기 희귀도 (Phase 9: 무기 등급별 이펙트 색상)
|
||||||
|
final ItemRarity? weaponRarity;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AsciiAnimationCard> createState() => _AsciiAnimationCardState();
|
State<AsciiAnimationCard> createState() => _AsciiAnimationCardState();
|
||||||
}
|
}
|
||||||
@@ -128,6 +138,12 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
|||||||
bool _showParryEffect = false;
|
bool _showParryEffect = false;
|
||||||
bool _showSkillEffect = false;
|
bool _showSkillEffect = false;
|
||||||
|
|
||||||
|
// 추가 전투 이펙트 (Phase 11)
|
||||||
|
bool _showEvadeEffect = false;
|
||||||
|
bool _showMissEffect = false;
|
||||||
|
bool _showDebuffEffect = false;
|
||||||
|
bool _showDotEffect = false;
|
||||||
|
|
||||||
// 공격 속도 기반 동적 페이즈 프레임 수 (Phase 6)
|
// 공격 속도 기반 동적 페이즈 프레임 수 (Phase 6)
|
||||||
int _eventDrivenPhaseFrames = 0;
|
int _eventDrivenPhaseFrames = 0;
|
||||||
bool _isEventDrivenPhase = false;
|
bool _isEventDrivenPhase = false;
|
||||||
@@ -191,12 +207,13 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
|||||||
oldWidget.weaponName != widget.weaponName ||
|
oldWidget.weaponName != widget.weaponName ||
|
||||||
oldWidget.shieldName != widget.shieldName ||
|
oldWidget.shieldName != widget.shieldName ||
|
||||||
oldWidget.monsterLevel != widget.monsterLevel ||
|
oldWidget.monsterLevel != widget.monsterLevel ||
|
||||||
oldWidget.raceId != widget.raceId) {
|
oldWidget.raceId != widget.raceId ||
|
||||||
|
oldWidget.weaponRarity != widget.weaponRarity) {
|
||||||
_updateAnimation();
|
_updateAnimation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 전투 이벤트에 따라 애니메이션 페이즈 강제 전환 (Phase 5)
|
/// 전투 이벤트에 따라 애니메이션 페이즈 강제 전환 (Phase 5, 11)
|
||||||
void _handleCombatEvent(CombatEvent event) {
|
void _handleCombatEvent(CombatEvent event) {
|
||||||
_lastEventTimestamp = event.timestamp;
|
_lastEventTimestamp = event.timestamp;
|
||||||
|
|
||||||
@@ -204,121 +221,90 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
|||||||
if (_animationMode != AnimationMode.battle) return;
|
if (_animationMode != AnimationMode.battle) return;
|
||||||
|
|
||||||
// 이벤트 타입에 따라 페이즈 및 효과 결정
|
// 이벤트 타입에 따라 페이즈 및 효과 결정
|
||||||
|
// (targetPhase, isCritical, isBlock, isParry, isSkill, isEvade, isMiss, isDebuff, isDot)
|
||||||
final (
|
final (
|
||||||
targetPhase,
|
targetPhase,
|
||||||
isCritical,
|
isCritical,
|
||||||
isBlock,
|
isBlock,
|
||||||
isParry,
|
isParry,
|
||||||
isSkill,
|
isSkill,
|
||||||
|
isEvade,
|
||||||
|
isMiss,
|
||||||
|
isDebuff,
|
||||||
|
isDot,
|
||||||
) = switch (event.type) {
|
) = switch (event.type) {
|
||||||
// 플레이어 공격 → prepare 페이즈부터 시작 (준비 동작 표시)
|
// 플레이어 공격 → prepare 페이즈부터 시작 (준비 동작 표시)
|
||||||
CombatEventType.playerAttack => (
|
CombatEventType.playerAttack => (
|
||||||
BattlePhase.prepare,
|
BattlePhase.prepare,
|
||||||
event.isCritical,
|
event.isCritical,
|
||||||
false,
|
false, false, false, false, false, false, false,
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
),
|
||||||
// 스킬 사용 → prepare 페이즈부터 시작 + 스킬 이펙트
|
// 스킬 사용 → prepare 페이즈부터 시작 + 스킬 이펙트
|
||||||
CombatEventType.playerSkill => (
|
CombatEventType.playerSkill => (
|
||||||
BattlePhase.prepare,
|
BattlePhase.prepare,
|
||||||
event.isCritical,
|
event.isCritical,
|
||||||
false,
|
false, false, true, false, false, false, false,
|
||||||
false,
|
|
||||||
true,
|
|
||||||
),
|
),
|
||||||
|
|
||||||
// 몬스터 공격 → prepare 페이즈부터 시작
|
// 몬스터 공격 → prepare 페이즈부터 시작
|
||||||
CombatEventType.monsterAttack => (
|
CombatEventType.monsterAttack => (
|
||||||
BattlePhase.prepare,
|
BattlePhase.prepare,
|
||||||
false,
|
false, false, false, false, false, false, false, false,
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
),
|
||||||
// 블록 → hit 페이즈 + 블록 이펙트
|
// 블록 → hit 페이즈 + 블록 이펙트 + 텍스트
|
||||||
CombatEventType.playerBlock => (
|
CombatEventType.playerBlock => (
|
||||||
BattlePhase.hit,
|
BattlePhase.hit,
|
||||||
false,
|
false, true, false, false, false, false, false, false,
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
),
|
||||||
// 패리 → hit 페이즈 + 패리 이펙트
|
// 패리 → hit 페이즈 + 패리 이펙트 + 텍스트
|
||||||
CombatEventType.playerParry => (
|
CombatEventType.playerParry => (
|
||||||
BattlePhase.hit,
|
BattlePhase.hit,
|
||||||
false,
|
false, false, true, false, false, false, false, false,
|
||||||
false,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
),
|
),
|
||||||
|
|
||||||
// 회피 → recover 페이즈 (빠른 회피 동작)
|
// 플레이어 회피 → recover 페이즈 + 회피 텍스트
|
||||||
CombatEventType.playerEvade => (
|
CombatEventType.playerEvade => (
|
||||||
BattlePhase.recover,
|
BattlePhase.recover,
|
||||||
false,
|
false, false, false, false, true, false, false, false,
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
),
|
||||||
|
// 몬스터 회피 → idle 페이즈 + 미스 텍스트
|
||||||
CombatEventType.monsterEvade => (
|
CombatEventType.monsterEvade => (
|
||||||
BattlePhase.idle,
|
BattlePhase.idle,
|
||||||
false,
|
false, false, false, false, false, true, false, false,
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
),
|
||||||
|
|
||||||
// 회복/버프 → idle 페이즈 유지
|
// 회복/버프 → idle 페이즈 유지
|
||||||
CombatEventType.playerHeal => (
|
CombatEventType.playerHeal => (
|
||||||
BattlePhase.idle,
|
BattlePhase.idle,
|
||||||
false,
|
false, false, false, false, false, false, false, false,
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
),
|
||||||
CombatEventType.playerBuff => (
|
CombatEventType.playerBuff => (
|
||||||
BattlePhase.idle,
|
BattlePhase.idle,
|
||||||
false,
|
false, false, false, false, false, false, false, false,
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
),
|
||||||
|
|
||||||
// 디버프 적용 → idle 페이즈 유지
|
// 디버프 적용 → idle 페이즈 + 디버프 텍스트
|
||||||
CombatEventType.playerDebuff => (
|
CombatEventType.playerDebuff => (
|
||||||
BattlePhase.idle,
|
BattlePhase.idle,
|
||||||
false,
|
false, false, false, false, false, false, true, false,
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
),
|
||||||
|
|
||||||
// DOT 틱 → attack 페이즈 (지속 피해)
|
// DOT 틱 → attack 페이즈 + DOT 텍스트
|
||||||
CombatEventType.dotTick => (
|
CombatEventType.dotTick => (
|
||||||
BattlePhase.attack,
|
BattlePhase.attack,
|
||||||
false,
|
false, false, false, false, false, false, false, true,
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
),
|
||||||
|
|
||||||
// 물약 사용 → idle 페이즈 유지
|
// 물약 사용 → idle 페이즈 유지
|
||||||
CombatEventType.playerPotion => (
|
CombatEventType.playerPotion => (
|
||||||
BattlePhase.idle,
|
BattlePhase.idle,
|
||||||
false,
|
false, false, false, false, false, false, false, false,
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
),
|
||||||
|
|
||||||
// 물약 드랍 → idle 페이즈 유지
|
// 물약 드랍 → idle 페이즈 유지
|
||||||
CombatEventType.potionDrop => (
|
CombatEventType.potionDrop => (
|
||||||
BattlePhase.idle,
|
BattlePhase.idle,
|
||||||
false,
|
false, false, false, false, false, false, false, false,
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -330,6 +316,10 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
|||||||
_showBlockEffect = isBlock;
|
_showBlockEffect = isBlock;
|
||||||
_showParryEffect = isParry;
|
_showParryEffect = isParry;
|
||||||
_showSkillEffect = isSkill;
|
_showSkillEffect = isSkill;
|
||||||
|
_showEvadeEffect = isEvade;
|
||||||
|
_showMissEffect = isMiss;
|
||||||
|
_showDebuffEffect = isDebuff;
|
||||||
|
_showDotEffect = isDot;
|
||||||
|
|
||||||
// 페이즈 인덱스 동기화
|
// 페이즈 인덱스 동기화
|
||||||
_phaseIndex = _battlePhaseSequence.indexWhere((p) => p.$1 == targetPhase);
|
_phaseIndex = _battlePhaseSequence.indexWhere((p) => p.$1 == targetPhase);
|
||||||
@@ -454,6 +444,7 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
|||||||
monsterCategory: monsterCategory,
|
monsterCategory: monsterCategory,
|
||||||
monsterSize: monsterSize,
|
monsterSize: monsterSize,
|
||||||
raceId: widget.raceId,
|
raceId: widget.raceId,
|
||||||
|
weaponRarity: widget.weaponRarity,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 환경 타입 추론
|
// 환경 타입 추론
|
||||||
@@ -483,6 +474,10 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
|||||||
_showBlockEffect = false;
|
_showBlockEffect = false;
|
||||||
_showParryEffect = false;
|
_showParryEffect = false;
|
||||||
_showSkillEffect = false;
|
_showSkillEffect = false;
|
||||||
|
_showEvadeEffect = false;
|
||||||
|
_showMissEffect = false;
|
||||||
|
_showDebuffEffect = false;
|
||||||
|
_showDotEffect = false;
|
||||||
// 공격자 타입 및 이벤트 기반 페이즈 리셋 (idle 페이즈 진입 시에만)
|
// 공격자 타입 및 이벤트 기반 페이즈 리셋 (idle 페이즈 진입 시에만)
|
||||||
// 공격 사이클(prepare→attack→hit→recover) 동안 유지 (Bug fix)
|
// 공격 사이클(prepare→attack→hit→recover) 동안 유지 (Bug fix)
|
||||||
if (_battlePhaseSequence[_phaseIndex].$1 == BattlePhase.idle) {
|
if (_battlePhaseSequence[_phaseIndex].$1 == BattlePhase.idle) {
|
||||||
@@ -513,6 +508,13 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
|
|||||||
_environment,
|
_environment,
|
||||||
_globalTick,
|
_globalTick,
|
||||||
attacker: _currentAttacker,
|
attacker: _currentAttacker,
|
||||||
|
isCritical: _showCriticalEffect,
|
||||||
|
isEvade: _showEvadeEffect,
|
||||||
|
isMiss: _showMissEffect,
|
||||||
|
isDebuff: _showDebuffEffect,
|
||||||
|
isDot: _showDotEffect,
|
||||||
|
isBlock: _showBlockEffect,
|
||||||
|
isParry: _showParryEffect,
|
||||||
) ??
|
) ??
|
||||||
[AsciiLayer.empty()],
|
[AsciiLayer.empty()],
|
||||||
AnimationMode.walking =>
|
AnimationMode.walking =>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import 'package:asciineverdie/src/core/animation/ascii_animation_type.dart';
|
|||||||
import 'package:asciineverdie/src/core/model/combat_event.dart';
|
import 'package:asciineverdie/src/core/model/combat_event.dart';
|
||||||
import 'package:asciineverdie/src/core/model/combat_state.dart';
|
import 'package:asciineverdie/src/core/model/combat_state.dart';
|
||||||
import 'package:asciineverdie/src/core/model/game_state.dart';
|
import 'package:asciineverdie/src/core/model/game_state.dart';
|
||||||
|
import 'package:asciineverdie/src/core/model/item_stats.dart';
|
||||||
|
import 'package:asciineverdie/src/core/model/monster_grade.dart';
|
||||||
import 'package:asciineverdie/src/features/game/widgets/ascii_animation_card.dart';
|
import 'package:asciineverdie/src/features/game/widgets/ascii_animation_card.dart';
|
||||||
|
|
||||||
/// 모바일용 확장 애니메이션 패널
|
/// 모바일용 확장 애니메이션 패널
|
||||||
@@ -29,8 +31,10 @@ class EnhancedAnimationPanel extends StatefulWidget {
|
|||||||
this.shieldName,
|
this.shieldName,
|
||||||
this.characterLevel,
|
this.characterLevel,
|
||||||
this.monsterLevel,
|
this.monsterLevel,
|
||||||
|
this.monsterGrade,
|
||||||
this.latestCombatEvent,
|
this.latestCombatEvent,
|
||||||
this.raceId,
|
this.raceId,
|
||||||
|
this.weaponRarity,
|
||||||
});
|
});
|
||||||
|
|
||||||
final ProgressState progress;
|
final ProgressState progress;
|
||||||
@@ -45,11 +49,17 @@ class EnhancedAnimationPanel extends StatefulWidget {
|
|||||||
final String? shieldName;
|
final String? shieldName;
|
||||||
final int? characterLevel;
|
final int? characterLevel;
|
||||||
final int? monsterLevel;
|
final int? monsterLevel;
|
||||||
|
|
||||||
|
/// 몬스터 등급 (Normal/Elite/Boss) - UI 색상/접두사 표시용
|
||||||
|
final MonsterGrade? monsterGrade;
|
||||||
final CombatEvent? latestCombatEvent;
|
final CombatEvent? latestCombatEvent;
|
||||||
|
|
||||||
/// 종족 ID (Phase 4: 종족별 캐릭터 애니메이션)
|
/// 종족 ID (Phase 4: 종족별 캐릭터 애니메이션)
|
||||||
final String? raceId;
|
final String? raceId;
|
||||||
|
|
||||||
|
/// 무기 희귀도 (Phase 9: 무기 등급별 이펙트 색상)
|
||||||
|
final ItemRarity? weaponRarity;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<EnhancedAnimationPanel> createState() => _EnhancedAnimationPanelState();
|
State<EnhancedAnimationPanel> createState() => _EnhancedAnimationPanelState();
|
||||||
}
|
}
|
||||||
@@ -185,9 +195,11 @@ class _EnhancedAnimationPanelState extends State<EnhancedAnimationPanel>
|
|||||||
shieldName: widget.shieldName,
|
shieldName: widget.shieldName,
|
||||||
characterLevel: widget.characterLevel,
|
characterLevel: widget.characterLevel,
|
||||||
monsterLevel: widget.monsterLevel,
|
monsterLevel: widget.monsterLevel,
|
||||||
|
monsterGrade: widget.monsterGrade,
|
||||||
isPaused: widget.isPaused,
|
isPaused: widget.isPaused,
|
||||||
latestCombatEvent: widget.latestCombatEvent,
|
latestCombatEvent: widget.latestCombatEvent,
|
||||||
raceId: widget.raceId,
|
raceId: widget.raceId,
|
||||||
|
weaponRarity: widget.weaponRarity,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -630,11 +642,34 @@ class _EnhancedAnimationPanelState extends State<EnhancedAnimationPanel>
|
|||||||
? (task.position / task.max).clamp(0.0, 1.0)
|
? (task.position / task.max).clamp(0.0, 1.0)
|
||||||
: 0.0;
|
: 0.0;
|
||||||
|
|
||||||
|
// 몬스터 등급에 따른 접두사와 색상
|
||||||
|
final grade = widget.monsterGrade;
|
||||||
|
final isKillTask = widget.progress.currentTask.type == TaskType.kill;
|
||||||
|
final gradePrefix =
|
||||||
|
(isKillTask && grade != null) ? grade.displayPrefix : '';
|
||||||
|
final gradeColor =
|
||||||
|
(isKillTask && grade != null) ? grade.displayColor : null;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
// 캡션
|
// 캡션 (등급에 따른 접두사 및 색상)
|
||||||
Text(
|
Text.rich(
|
||||||
_getStatusMessage(),
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
if (gradePrefix.isNotEmpty)
|
||||||
|
TextSpan(
|
||||||
|
text: gradePrefix,
|
||||||
|
style: TextStyle(
|
||||||
|
color: gradeColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: _getStatusMessage(),
|
||||||
|
style: gradeColor != null ? TextStyle(color: gradeColor) : null,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:asciineverdie/l10n/app_localizations.dart';
|
|||||||
import 'package:asciineverdie/src/core/animation/ascii_animation_type.dart';
|
import 'package:asciineverdie/src/core/animation/ascii_animation_type.dart';
|
||||||
import 'package:asciineverdie/src/core/model/combat_event.dart';
|
import 'package:asciineverdie/src/core/model/combat_event.dart';
|
||||||
import 'package:asciineverdie/src/core/model/game_state.dart';
|
import 'package:asciineverdie/src/core/model/game_state.dart';
|
||||||
|
import 'package:asciineverdie/src/core/model/monster_grade.dart';
|
||||||
import 'package:asciineverdie/src/features/game/widgets/ascii_animation_card.dart';
|
import 'package:asciineverdie/src/features/game/widgets/ascii_animation_card.dart';
|
||||||
|
|
||||||
/// 상단 패널: ASCII 애니메이션 + Task Progress 바
|
/// 상단 패널: ASCII 애니메이션 + Task Progress 바
|
||||||
@@ -23,6 +24,7 @@ class TaskProgressPanel extends StatelessWidget {
|
|||||||
this.shieldName,
|
this.shieldName,
|
||||||
this.characterLevel,
|
this.characterLevel,
|
||||||
this.monsterLevel,
|
this.monsterLevel,
|
||||||
|
this.monsterGrade,
|
||||||
this.latestCombatEvent,
|
this.latestCombatEvent,
|
||||||
this.raceId,
|
this.raceId,
|
||||||
});
|
});
|
||||||
@@ -44,6 +46,9 @@ class TaskProgressPanel extends StatelessWidget {
|
|||||||
final int? characterLevel;
|
final int? characterLevel;
|
||||||
final int? monsterLevel;
|
final int? monsterLevel;
|
||||||
|
|
||||||
|
/// 몬스터 등급 (Normal/Elite/Boss) - UI 색상/접두사 표시용
|
||||||
|
final MonsterGrade? monsterGrade;
|
||||||
|
|
||||||
/// 최근 전투 이벤트 (애니메이션 동기화용, Phase 5)
|
/// 최근 전투 이벤트 (애니메이션 동기화용, Phase 5)
|
||||||
final CombatEvent? latestCombatEvent;
|
final CombatEvent? latestCombatEvent;
|
||||||
|
|
||||||
@@ -74,6 +79,7 @@ class TaskProgressPanel extends StatelessWidget {
|
|||||||
shieldName: shieldName,
|
shieldName: shieldName,
|
||||||
characterLevel: characterLevel,
|
characterLevel: characterLevel,
|
||||||
monsterLevel: monsterLevel,
|
monsterLevel: monsterLevel,
|
||||||
|
monsterGrade: monsterGrade,
|
||||||
isPaused: isPaused,
|
isPaused: isPaused,
|
||||||
latestCombatEvent: latestCombatEvent,
|
latestCombatEvent: latestCombatEvent,
|
||||||
raceId: raceId,
|
raceId: raceId,
|
||||||
@@ -87,11 +93,7 @@ class TaskProgressPanel extends StatelessWidget {
|
|||||||
_buildPauseButton(context),
|
_buildPauseButton(context),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: _buildStatusMessage(context),
|
||||||
_getStatusMessage(context),
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
_buildSpeedButton(context),
|
_buildSpeedButton(context),
|
||||||
@@ -153,6 +155,42 @@ class TaskProgressPanel extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 상태 메시지 위젯 (등급에 따른 접두사 및 색상 적용)
|
||||||
|
Widget _buildStatusMessage(BuildContext context) {
|
||||||
|
final message = _getStatusMessage(context);
|
||||||
|
|
||||||
|
// 몬스터 등급에 따른 접두사와 색상
|
||||||
|
final grade = monsterGrade;
|
||||||
|
final isKillTask = progress.currentTask.type == TaskType.kill;
|
||||||
|
final gradePrefix =
|
||||||
|
(isKillTask && grade != null) ? grade.displayPrefix : '';
|
||||||
|
final gradeColor =
|
||||||
|
(isKillTask && grade != null) ? grade.displayColor : null;
|
||||||
|
|
||||||
|
return Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
if (gradePrefix.isNotEmpty)
|
||||||
|
TextSpan(
|
||||||
|
text: gradePrefix,
|
||||||
|
style: TextStyle(
|
||||||
|
color: gradeColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: message,
|
||||||
|
style: gradeColor != null ? TextStyle(color: gradeColor) : null,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// 현재 상태에 맞는 메시지 반환
|
/// 현재 상태에 맞는 메시지 반환
|
||||||
///
|
///
|
||||||
/// 특수 애니메이션(부활 등) 중에는 해당 메시지 표시
|
/// 특수 애니메이션(부활 등) 중에는 해당 메시지 표시
|
||||||
|
|||||||
Reference in New Issue
Block a user