- Progress Quest 6.4 Flutter 포팅 프로젝트 - 게임 루프, 상태 관리, UI 구현 - 캐릭터 생성, 인벤토리, 장비, 주문 시스템 - 시장/판매/구매 메커니즘
139 lines
3.8 KiB
Dart
139 lines
3.8 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:askiineverdie/src/core/model/game_state.dart';
|
|
import 'package:askiineverdie/src/core/storage/save_manager.dart';
|
|
import 'package:askiineverdie/src/core/engine/progress_service.dart';
|
|
|
|
class AutoSaveConfig {
|
|
const AutoSaveConfig({
|
|
this.onLevelUp = true,
|
|
this.onQuestComplete = true,
|
|
this.onActComplete = true,
|
|
this.onStop = true,
|
|
});
|
|
|
|
final bool onLevelUp;
|
|
final bool onQuestComplete;
|
|
final bool onActComplete;
|
|
final bool onStop;
|
|
|
|
bool shouldSave(ProgressTickResult result) {
|
|
return (onLevelUp && result.leveledUp) ||
|
|
(onQuestComplete && result.completedQuest) ||
|
|
(onActComplete && result.completedAct);
|
|
}
|
|
}
|
|
|
|
/// Runs the periodic timer loop that advances tasks/quests/plots.
|
|
class ProgressLoop {
|
|
ProgressLoop({
|
|
required GameState initialState,
|
|
required this.progressService,
|
|
this.saveManager,
|
|
Duration tickInterval = const Duration(milliseconds: 50),
|
|
AutoSaveConfig autoSaveConfig = const AutoSaveConfig(),
|
|
DateTime Function()? now,
|
|
this.cheatsEnabled = false,
|
|
}) : _state = initialState,
|
|
_tickInterval = tickInterval,
|
|
_autoSaveConfig = autoSaveConfig,
|
|
_now = now ?? DateTime.now,
|
|
_stateController = StreamController<GameState>.broadcast();
|
|
|
|
final ProgressService progressService;
|
|
final SaveManager? saveManager;
|
|
final Duration _tickInterval;
|
|
final AutoSaveConfig _autoSaveConfig;
|
|
final DateTime Function() _now;
|
|
final StreamController<GameState> _stateController;
|
|
bool cheatsEnabled;
|
|
|
|
Timer? _timer;
|
|
int? _lastTickMs;
|
|
int _speedMultiplier = 1;
|
|
|
|
GameState get current => _state;
|
|
Stream<GameState> get stream => _stateController.stream;
|
|
GameState _state;
|
|
|
|
/// 현재 배속 (1x, 2x, 5x)
|
|
int get speedMultiplier => _speedMultiplier;
|
|
|
|
/// 배속 순환: 1 -> 2 -> 5 -> 1
|
|
void cycleSpeed() {
|
|
_speedMultiplier = switch (_speedMultiplier) {
|
|
1 => 2,
|
|
2 => 5,
|
|
_ => 1,
|
|
};
|
|
}
|
|
|
|
void start() {
|
|
_lastTickMs = _now().millisecondsSinceEpoch;
|
|
_timer ??= Timer.periodic(_tickInterval, (_) => tickOnce());
|
|
}
|
|
|
|
Future<void> stop({bool saveOnStop = false}) async {
|
|
_timer?.cancel();
|
|
_timer = null;
|
|
if (saveOnStop && _autoSaveConfig.onStop && saveManager != null) {
|
|
await saveManager!.saveState(_state);
|
|
}
|
|
}
|
|
|
|
void dispose() {
|
|
_timer?.cancel();
|
|
_stateController.close();
|
|
}
|
|
|
|
/// Run one iteration of the loop (used by Timer or manual stepping).
|
|
GameState tickOnce({int? deltaMillis}) {
|
|
final baseDelta = deltaMillis ?? _computeDelta();
|
|
final delta = baseDelta * _speedMultiplier;
|
|
final result = progressService.tick(_state, delta);
|
|
_state = result.state;
|
|
_stateController.add(_state);
|
|
|
|
if (saveManager != null && _autoSaveConfig.shouldSave(result)) {
|
|
saveManager!.saveState(_state);
|
|
}
|
|
return _state;
|
|
}
|
|
|
|
/// Replace state (e.g., after loading) and reset timing.
|
|
void replaceState(GameState newState) {
|
|
_state = newState;
|
|
_stateController.add(newState);
|
|
_lastTickMs = _now().millisecondsSinceEpoch;
|
|
}
|
|
|
|
// Developer-only helpers mirroring original cheat panel actions.
|
|
void cheatCompleteTask() {
|
|
if (!cheatsEnabled) return;
|
|
_state = progressService.forceTaskComplete(_state);
|
|
_stateController.add(_state);
|
|
}
|
|
|
|
void cheatCompleteQuest() {
|
|
if (!cheatsEnabled) return;
|
|
_state = progressService.forceQuestComplete(_state);
|
|
_stateController.add(_state);
|
|
}
|
|
|
|
void cheatCompletePlot() {
|
|
if (!cheatsEnabled) return;
|
|
_state = progressService.forcePlotComplete(_state);
|
|
_stateController.add(_state);
|
|
}
|
|
|
|
int _computeDelta() {
|
|
final nowMs = _now().millisecondsSinceEpoch;
|
|
final last = _lastTickMs;
|
|
_lastTickMs = nowMs;
|
|
if (last == null) return 0;
|
|
final delta = nowMs - last;
|
|
if (delta < 0) return 0;
|
|
return delta;
|
|
}
|
|
}
|