feat(game): 게임 클리어 시 VictoryOverlay 추가

- VictoryOverlay 위젯 신규 생성
- GameSessionController에 isComplete 상태 추가
- 레벨 100 도달 시 승리 오버레이 표시
- 승리 후 명예의 전당 화면으로 이동
This commit is contained in:
JiWoong Sul
2025-12-31 18:52:18 +09:00
parent 9b668d80a4
commit a6d3c1e42f
4 changed files with 635 additions and 65 deletions

View File

@@ -12,18 +12,16 @@ import 'package:asciineverdie/src/core/animation/ascii_animation_type.dart';
import 'package:asciineverdie/src/core/engine/story_service.dart';
import 'package:asciineverdie/src/core/model/combat_event.dart';
import 'package:asciineverdie/src/core/l10n/game_data_l10n.dart';
import 'package:asciineverdie/src/core/model/combat_stats.dart';
import 'package:asciineverdie/src/core/model/game_state.dart';
import 'package:asciineverdie/src/core/model/hall_of_fame.dart';
import 'package:asciineverdie/src/core/model/skill.dart';
import 'package:asciineverdie/src/core/notification/notification_service.dart';
import 'package:asciineverdie/src/core/storage/hall_of_fame_storage.dart';
import 'package:asciineverdie/src/core/util/pq_logic.dart' as pq_logic;
import 'package:asciineverdie/src/features/game/game_session_controller.dart';
import 'package:asciineverdie/src/features/hall_of_fame/hall_of_fame_screen.dart';
import 'package:asciineverdie/src/features/game/widgets/cinematic_view.dart';
import 'package:asciineverdie/src/features/game/widgets/combat_log.dart';
import 'package:asciineverdie/src/features/game/widgets/death_overlay.dart';
import 'package:asciineverdie/src/features/game/widgets/victory_overlay.dart';
import 'package:asciineverdie/src/features/game/widgets/hp_mp_bar.dart';
import 'package:asciineverdie/src/features/game/widgets/notification_overlay.dart';
import 'package:asciineverdie/src/features/game/widgets/stats_panel.dart';
@@ -138,14 +136,9 @@ class _GamePlayScreenState extends State<GamePlayScreen>
if (newAct != _lastAct && !_showingCinematic) {
_lastAct = newAct;
// Phase 10: 엔딩 도달 시 클리어 처리 (시네마틱 대신 클리어 다이얼로그)
// 다음 프레임에서 실행 (리스너 콜백 중 showDialog 문제 방지)
if (newAct == StoryAct.ending && state.traits.level >= 100) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) _handleGameClear(state);
});
} else {
// 일반 Act 전환 시 시네마틱 표시
// 엔딩은 controller.isComplete 상태에서 VictoryOverlay로 처리
// 일반 Act 전환 시에만 시네마틱 표시
if (newAct != StoryAct.ending) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) _showCinematicForAct(newAct);
});
@@ -419,49 +412,13 @@ class _GamePlayScreenState extends State<GamePlayScreen>
_showingCinematic = false;
}
/// Phase 10: 게임 클리어 처리 (Handle Game Clear)
Future<void> _handleGameClear(GameState state) async {
// 게임 일시 정지
await widget.controller.pause(saveOnStop: true);
// 최종 전투 스탯 계산
final combatStats = CombatStats.fromStats(
stats: state.stats,
equipment: state.equipment,
level: state.traits.level,
/// VictoryOverlay 완료 후 명예의 전당 화면으로 이동
void _handleVictoryComplete() {
Navigator.of(context).pushReplacement(
MaterialPageRoute<void>(
builder: (context) => const HallOfFameScreen(),
),
);
// 명예의 전당 엔트리 생성
final entry = HallOfFameEntry.fromGameState(
state: state,
totalDeaths: state.progress.deathCount,
monstersKilled: state.progress.monstersKilled,
combatStats: combatStats,
);
// 명예의 전당에 저장
final storage = HallOfFameStorage();
await storage.addEntry(entry);
// 클리어 다이얼로그 표시
if (mounted) {
await showGameClearDialog(
context,
entry: entry,
onNewGame: () {
// 프론트 화면으로 돌아가기
Navigator.of(context).popUntil((route) => route.isFirst);
},
onViewHallOfFame: () {
// 명예의 전당 화면으로 이동
Navigator.of(context).pushReplacement(
MaterialPageRoute<void>(
builder: (context) => const HallOfFameScreen(),
),
);
},
);
}
}
void _resetSpecialAnimationAfterFrame() {
@@ -857,6 +814,15 @@ class _GamePlayScreenState extends State<GamePlayScreen>
traits: state.traits,
onResurrect: _handleResurrect,
),
// 승리 오버레이 (게임 클리어)
if (widget.controller.isComplete)
VictoryOverlay(
traits: state.traits,
stats: state.stats,
progress: state.progress,
elapsedMs: state.skillSystem.elapsedMs,
onComplete: _handleVictoryComplete,
),
],
),
),
@@ -990,6 +956,15 @@ class _GamePlayScreenState extends State<GamePlayScreen>
traits: state.traits,
onResurrect: _handleResurrect,
),
// 승리 오버레이 (게임 클리어)
if (widget.controller.isComplete)
VictoryOverlay(
traits: state.traits,
stats: state.stats,
progress: state.progress,
elapsedMs: state.skillSystem.elapsedMs,
onComplete: _handleVictoryComplete,
),
],
),
),