feat(ui): Phase 8 실시간 피드백 시스템 구현

- StatsPanel: 스탯 변화 애니메이션 (증감 표시)
- CombatLog: 전투 이벤트 로그 위젯
- NotificationService: 큐 기반 알림 관리
- NotificationOverlay: 레벨업/퀘스트 완료 팝업 알림
- GamePlayScreen: 새 위젯 통합
This commit is contained in:
JiWoong Sul
2025-12-17 18:33:21 +09:00
parent bfcec44ac7
commit 8cbef3475b
5 changed files with 760 additions and 48 deletions

View File

@@ -0,0 +1,149 @@
import 'dart:async';
/// 알림 타입 (Notification Type)
enum NotificationType {
levelUp, // 레벨업
questComplete, // 퀘스트 완료
actComplete, // 막(Act) 완료
newSpell, // 새 주문 습득
newEquipment, // 새 장비 획득
bossDefeat, // 보스 처치
}
/// 게임 알림 데이터 (Game Notification)
class GameNotification {
const GameNotification({
required this.type,
required this.title,
this.subtitle,
this.data,
this.duration = const Duration(seconds: 3),
});
final NotificationType type;
final String title;
final String? subtitle;
final Map<String, dynamic>? data;
final Duration duration;
}
/// 알림 서비스 (Phase 8: 이벤트 기반 알림 관리)
///
/// 게임 이벤트(레벨업, 퀘스트 완료 등)를 큐에 추가하고
/// 순차적으로 UI에 표시
class NotificationService {
NotificationService();
final _notificationController =
StreamController<GameNotification>.broadcast();
final _dismissController = StreamController<void>.broadcast();
/// 알림 스트림 (Notification Stream)
Stream<GameNotification> get notifications => _notificationController.stream;
/// 알림 닫기 스트림
Stream<void> get dismissals => _dismissController.stream;
/// 알림 큐 (대기 중인 알림)
final List<GameNotification> _queue = [];
bool _isShowing = false;
/// 알림 추가 (Add Notification)
void show(GameNotification notification) {
_queue.add(notification);
_processQueue();
}
/// 레벨업 알림 (Level Up Notification)
void showLevelUp(int newLevel) {
show(GameNotification(
type: NotificationType.levelUp,
title: 'LEVEL UP!',
subtitle: 'Level $newLevel',
data: {'level': newLevel},
duration: const Duration(seconds: 2),
));
}
/// 퀘스트 완료 알림
void showQuestComplete(String questName) {
show(GameNotification(
type: NotificationType.questComplete,
title: 'QUEST COMPLETE!',
subtitle: questName,
data: {'quest': questName},
duration: const Duration(seconds: 2),
));
}
/// 막 완료 알림 (Act Complete)
void showActComplete(int actNumber) {
show(GameNotification(
type: NotificationType.actComplete,
title: 'ACT $actNumber COMPLETE!',
duration: const Duration(seconds: 3),
));
}
/// 새 주문 알림
void showNewSpell(String spellName) {
show(GameNotification(
type: NotificationType.newSpell,
title: 'NEW SPELL!',
subtitle: spellName,
data: {'spell': spellName},
duration: const Duration(seconds: 2),
));
}
/// 새 장비 알림
void showNewEquipment(String equipmentName, String slot) {
show(GameNotification(
type: NotificationType.newEquipment,
title: 'NEW EQUIPMENT!',
subtitle: equipmentName,
data: {'equipment': equipmentName, 'slot': slot},
duration: const Duration(seconds: 2),
));
}
/// 보스 처치 알림
void showBossDefeat(String bossName) {
show(GameNotification(
type: NotificationType.bossDefeat,
title: 'BOSS DEFEATED!',
subtitle: bossName,
data: {'boss': bossName},
duration: const Duration(seconds: 3),
));
}
/// 큐 처리 (Process Queue)
void _processQueue() {
if (_isShowing || _queue.isEmpty) return;
_isShowing = true;
final notification = _queue.removeAt(0);
_notificationController.add(notification);
// 지정된 시간 후 자동 닫기
Future.delayed(notification.duration, () {
_dismissController.add(null);
_isShowing = false;
_processQueue(); // 다음 알림 처리
});
}
/// 현재 알림 즉시 닫기
void dismiss() {
_dismissController.add(null);
_isShowing = false;
_processQueue();
}
/// 서비스 정리 (Dispose)
void dispose() {
_notificationController.close();
_dismissController.close();
}
}