feat(ui): 화면 및 컨트롤러 수익화 연동

- 앱 초기화에 광고/IAP 서비스 추가
- 게임 세션 컨트롤러 수익화 상태 관리
- 캐릭터 생성 화면 굴리기 제한 UI
- 설정 화면 광고 제거 구매 UI
- 애니메이션 패널 개선
This commit is contained in:
JiWoong Sul
2026-01-16 20:10:43 +09:00
parent c95e4de5a4
commit 748160d543
8 changed files with 1288 additions and 373 deletions

View File

@@ -6,6 +6,8 @@ import 'package:flutter/services.dart' show KeyDownEvent, LogicalKeyboardKey;
import 'package:asciineverdie/data/game_text_l10n.dart' as game_l10n;
import 'package:asciineverdie/data/skill_data.dart';
import 'package:asciineverdie/src/core/engine/iap_service.dart';
import 'package:asciineverdie/src/core/engine/return_rewards_service.dart';
import 'package:asciineverdie/data/story_data.dart';
import 'package:asciineverdie/l10n/app_localizations.dart';
import 'package:asciineverdie/src/core/animation/ascii_animation_type.dart';
@@ -28,6 +30,7 @@ import 'package:asciineverdie/src/features/game/widgets/equipment_stats_panel.da
import 'package:asciineverdie/src/features/game/widgets/potion_inventory_panel.dart';
import 'package:asciineverdie/src/features/game/widgets/task_progress_panel.dart';
import 'package:asciineverdie/src/features/game/widgets/active_buff_panel.dart';
import 'package:asciineverdie/src/features/game/widgets/return_rewards_dialog.dart';
import 'package:asciineverdie/src/features/game/layouts/mobile_carousel_layout.dart';
import 'package:asciineverdie/src/features/settings/settings_screen.dart';
import 'package:asciineverdie/src/features/game/widgets/statistics_dialog.dart';
@@ -246,6 +249,9 @@ class _GamePlayScreenState extends State<GamePlayScreen>
// 오디오 볼륨 초기화
_audioController.initVolumes();
// Phase 7: 복귀 보상 콜백 설정
widget.controller.onReturnRewardAvailable = _showReturnRewardsDialog;
}
@override
@@ -262,6 +268,7 @@ class _GamePlayScreenState extends State<GamePlayScreen>
_storyService.dispose();
WidgetsBinding.instance.removeObserver(this);
widget.controller.removeListener(_onControllerChanged);
widget.controller.onReturnRewardAvailable = null; // Phase 7: 콜백 정리
super.dispose();
}
@@ -399,6 +406,21 @@ class _GamePlayScreenState extends State<GamePlayScreen>
return platform == TargetPlatform.iOS || platform == TargetPlatform.android;
}
/// 복귀 보상 다이얼로그 표시 (Phase 7)
void _showReturnRewardsDialog(ReturnReward reward) {
// 잠시 후 다이얼로그 표시 (게임 시작 후)
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
ReturnRewardsDialog.show(
context,
reward: reward,
onClaim: (totalGold) {
widget.controller.applyReturnReward(totalGold);
},
);
});
}
/// 통계 다이얼로그 표시
void _showStatisticsDialog(BuildContext context) {
StatisticsDialog.show(
@@ -486,6 +508,47 @@ class _GamePlayScreenState extends State<GamePlayScreen>
});
}
/// 광고 부활 핸들러 (HP 100% + 아이템 복구 + 10분 자동부활)
Future<void> _handleAdRevive() async {
// 1. 부활 애니메이션 먼저 설정 (DeathOverlay 사라지기 전에)
setState(() {
_specialAnimation = AsciiAnimationType.resurrection;
});
// 2. 광고 부활 처리 (HP 100%, 아이템 복구, 10분 자동부활 버프)
await widget.controller.adRevive();
// 3. 애니메이션 종료 후 게임 재개
final duration = getSpecialAnimationDuration(
AsciiAnimationType.resurrection,
);
Future.delayed(Duration(milliseconds: duration), () async {
if (mounted) {
await widget.controller.resumeAfterResurrection();
if (mounted) {
setState(() {
_specialAnimation = null;
});
}
}
});
}
/// 속도 부스트 활성화 핸들러 (Phase 6)
Future<void> _handleSpeedBoost() async {
final activated = await widget.controller.activateSpeedBoost();
if (activated && mounted) {
_notificationService.show(
GameNotification(
type: NotificationType.info,
title: game_l10n.speedBoostActive,
duration: const Duration(seconds: 2),
),
);
setState(() {});
}
}
@override
Widget build(BuildContext context) {
final state = widget.controller.state;
@@ -603,6 +666,11 @@ class _GamePlayScreenState extends State<GamePlayScreen>
navigator.popUntil((route) => route.isFirst);
}
},
// 수익화 버프 (자동부활, 5배속)
autoReviveEndMs: widget.controller.monetization.autoReviveEndMs,
speedBoostEndMs: widget.controller.monetization.speedBoostEndMs,
isPaidUser: widget.controller.monetization.isPaidUser,
onSpeedBoostActivate: _handleSpeedBoost,
),
// 사망 오버레이
if (state.isDead && state.deathInfo != null)
@@ -610,12 +678,8 @@ class _GamePlayScreenState extends State<GamePlayScreen>
deathInfo: state.deathInfo!,
traits: state.traits,
onResurrect: _handleResurrect,
isAutoResurrectEnabled: widget.controller.autoResurrect,
onToggleAutoResurrect: () {
widget.controller.setAutoResurrect(
!widget.controller.autoResurrect,
);
},
onAdRevive: _handleAdRevive,
isPaidUser: IAPService.instance.isAdRemovalPurchased,
),
// 승리 오버레이 (게임 클리어)
if (widget.controller.isComplete)
@@ -759,18 +823,14 @@ class _GamePlayScreenState extends State<GamePlayScreen>
],
),
// Phase 4: 사망 오버레이 (Death Overlay)
// 사망 오버레이
if (state.isDead && state.deathInfo != null)
DeathOverlay(
deathInfo: state.deathInfo!,
traits: state.traits,
onResurrect: _handleResurrect,
isAutoResurrectEnabled: widget.controller.autoResurrect,
onToggleAutoResurrect: () {
widget.controller.setAutoResurrect(
!widget.controller.autoResurrect,
);
},
onAdRevive: _handleAdRevive,
isPaidUser: IAPService.instance.isAdRemovalPurchased,
),
// 승리 오버레이 (게임 클리어)
if (widget.controller.isComplete)