- DeathInfo, DeathCause 클래스 정의 (game_state.dart) - 사망 원인, 상실 장비 수, 사망 시점 정보 기록 - ShopService 구현 (shop_service.dart) - 장비 가격 계산 (레벨 * 50 * 희귀도 배율) - 슬롯별 장비 생성 (프로그래밍 테마) - 자동 구매 (빈 슬롯에 Common 장비) - ResurrectionService 구현 (resurrection_service.dart) - 사망 처리: 모든 장비 상실, 기본 무기만 유지 - 부활 처리: HP/MP 회복, 자동 장비 구매 - progress_service.dart 사망 판정 로직 추가 - 전투 중 HP <= 0 시 사망 처리 - ProgressTickResult에 playerDied 플래그 추가 - progress_loop.dart 사망 시 루프 정지 - onPlayerDied 콜백 추가 - 사망 상태에서 틱 진행 방지 - DeathOverlay 위젯 구현 (death_overlay.dart) - ASCII 스컬 아트, 사망 원인, 상실 정보 표시 - 부활 버튼 - GameSessionController 사망/부활 상태 관리 - GameSessionStatus.dead 상태 추가 - resurrect() 메서드로 부활 처리
177 lines
5.1 KiB
Dart
177 lines
5.1 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:askiineverdie/src/core/engine/progress_loop.dart';
|
|
import 'package:askiineverdie/src/core/engine/progress_service.dart';
|
|
import 'package:askiineverdie/src/core/engine/resurrection_service.dart';
|
|
import 'package:askiineverdie/src/core/engine/shop_service.dart';
|
|
import 'package:askiineverdie/src/core/model/game_state.dart';
|
|
import 'package:askiineverdie/src/core/storage/save_manager.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
enum GameSessionStatus { idle, loading, running, error, dead }
|
|
|
|
/// Presentation-friendly wrapper that owns ProgressLoop and SaveManager.
|
|
class GameSessionController extends ChangeNotifier {
|
|
GameSessionController({
|
|
required this.progressService,
|
|
required this.saveManager,
|
|
this.autoSaveConfig = const AutoSaveConfig(),
|
|
Duration tickInterval = const Duration(milliseconds: 50),
|
|
DateTime Function()? now,
|
|
}) : _tickInterval = tickInterval,
|
|
_now = now ?? DateTime.now;
|
|
|
|
final ProgressService progressService;
|
|
final SaveManager saveManager;
|
|
final AutoSaveConfig autoSaveConfig;
|
|
|
|
final Duration _tickInterval;
|
|
final DateTime Function() _now;
|
|
|
|
ProgressLoop? _loop;
|
|
StreamSubscription<GameState>? _subscription;
|
|
bool _cheatsEnabled = false;
|
|
|
|
GameSessionStatus _status = GameSessionStatus.idle;
|
|
GameState? _state;
|
|
String? _error;
|
|
|
|
GameSessionStatus get status => _status;
|
|
GameState? get state => _state;
|
|
String? get error => _error;
|
|
bool get isRunning => _status == GameSessionStatus.running;
|
|
bool get cheatsEnabled => _cheatsEnabled;
|
|
|
|
/// 현재 ProgressLoop 인스턴스 (치트 기능용)
|
|
ProgressLoop? get loop => _loop;
|
|
|
|
Future<void> startNew(
|
|
GameState initialState, {
|
|
bool cheatsEnabled = false,
|
|
bool isNewGame = true,
|
|
}) async {
|
|
await _stopLoop(saveOnStop: false);
|
|
|
|
// 새 게임인 경우 초기화 (프롤로그 태스크 설정)
|
|
final state = isNewGame
|
|
? progressService.initializeNewGame(initialState)
|
|
: initialState;
|
|
|
|
_state = state;
|
|
_error = null;
|
|
_status = GameSessionStatus.running;
|
|
_cheatsEnabled = cheatsEnabled;
|
|
|
|
_loop = ProgressLoop(
|
|
initialState: state,
|
|
progressService: progressService,
|
|
saveManager: saveManager,
|
|
autoSaveConfig: autoSaveConfig,
|
|
tickInterval: _tickInterval,
|
|
now: _now,
|
|
cheatsEnabled: cheatsEnabled,
|
|
onPlayerDied: _onPlayerDied,
|
|
);
|
|
|
|
_subscription = _loop!.stream.listen((next) {
|
|
_state = next;
|
|
notifyListeners();
|
|
});
|
|
|
|
_loop!.start();
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> loadAndStart({
|
|
String? fileName,
|
|
bool cheatsEnabled = false,
|
|
}) async {
|
|
_status = GameSessionStatus.loading;
|
|
_error = null;
|
|
notifyListeners();
|
|
|
|
final (outcome, loaded) = await saveManager.loadState(fileName: fileName);
|
|
if (!outcome.success || loaded == null) {
|
|
_status = GameSessionStatus.error;
|
|
_error = outcome.error ?? 'Unknown error';
|
|
notifyListeners();
|
|
return;
|
|
}
|
|
|
|
await startNew(loaded, cheatsEnabled: cheatsEnabled, isNewGame: false);
|
|
}
|
|
|
|
Future<void> pause({bool saveOnStop = false}) async {
|
|
await _stopLoop(saveOnStop: saveOnStop);
|
|
_status = GameSessionStatus.idle;
|
|
notifyListeners();
|
|
}
|
|
|
|
/// 일시 정지 상태에서 재개
|
|
Future<void> resume() async {
|
|
if (_state == null || _status != GameSessionStatus.idle) return;
|
|
await startNew(_state!, cheatsEnabled: _cheatsEnabled, isNewGame: false);
|
|
}
|
|
|
|
/// 일시 정지/재개 토글
|
|
Future<void> togglePause() async {
|
|
if (isRunning) {
|
|
await pause(saveOnStop: true);
|
|
} else if (_state != null && _status == GameSessionStatus.idle) {
|
|
await resume();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
final stop = _stopLoop(saveOnStop: false);
|
|
if (stop != null) {
|
|
unawaited(stop);
|
|
}
|
|
super.dispose();
|
|
}
|
|
|
|
Future<void>? _stopLoop({required bool saveOnStop}) {
|
|
final loop = _loop;
|
|
final sub = _subscription;
|
|
_loop = null;
|
|
_subscription = null;
|
|
|
|
sub?.cancel();
|
|
if (loop == null) return null;
|
|
return loop.stop(saveOnStop: saveOnStop);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Phase 4: 사망/부활 처리
|
|
// ============================================================================
|
|
|
|
/// 플레이어 사망 콜백 (ProgressLoop에서 호출)
|
|
void _onPlayerDied() {
|
|
_status = GameSessionStatus.dead;
|
|
notifyListeners();
|
|
}
|
|
|
|
/// 플레이어 부활 처리
|
|
///
|
|
/// HP/MP 회복, 빈 슬롯에 장비 자동 구매, 게임 재개
|
|
Future<void> resurrect() async {
|
|
if (_state == null || !_state!.isDead) return;
|
|
|
|
// ResurrectionService를 사용하여 부활 처리
|
|
final shopService = ShopService(rng: _state!.rng);
|
|
final resurrectionService = ResurrectionService(shopService: shopService);
|
|
|
|
final resurrectedState = resurrectionService.processResurrection(_state!);
|
|
|
|
// 저장
|
|
await saveManager.saveState(resurrectedState);
|
|
|
|
// 게임 재개
|
|
await startNew(resurrectedState, cheatsEnabled: _cheatsEnabled, isNewGame: false);
|
|
}
|
|
|
|
/// 사망 상태 여부
|
|
bool get isDead => _status == GameSessionStatus.dead || (_state?.isDead ?? false);
|
|
}
|