import 'package:asciineverdie/src/core/engine/game_mutations.dart'; import 'package:asciineverdie/src/core/engine/progress_service.dart'; import 'package:asciineverdie/src/core/engine/reward_service.dart'; import 'package:asciineverdie/src/core/model/combat_state.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/game_statistics.dart'; import 'package:asciineverdie/src/core/model/hall_of_fame.dart'; import 'package:asciineverdie/src/core/model/monetization_state.dart'; import 'package:asciineverdie/src/core/model/monster_combat_stats.dart'; import 'package:asciineverdie/src/core/model/pq_config.dart'; import 'package:asciineverdie/src/core/storage/hall_of_fame_storage.dart'; import 'package:asciineverdie/src/core/storage/save_manager.dart'; import 'package:asciineverdie/src/core/storage/save_repository.dart'; import 'package:asciineverdie/src/core/storage/save_service.dart'; import 'package:asciineverdie/src/core/storage/statistics_storage.dart'; import 'package:asciineverdie/src/core/util/balance_constants.dart'; export 'package:asciineverdie/src/core/storage/save_repository.dart' show SaveOutcome; export 'package:asciineverdie/src/core/storage/save_service.dart' show SaveFileInfo; /// 테스트용 Fake SaveManager /// /// 여러 테스트 파일에서 중복되던 Mock을 통합 class FakeSaveManager implements SaveManager { final List savedStates = []; /// 커스텀 로드 동작 설정 (SaveOutcome, GameState?, bool, MonetizationState?) Function(String?)? onLoad; /// 저장 결과 설정 (기본: 성공) SaveOutcome saveOutcome = const SaveOutcome.success(); @override Future saveState( GameState state, { String? fileName, bool cheatsEnabled = false, MonetizationState? monetization, }) async { savedStates.add(state); return saveOutcome; } @override Future<(SaveOutcome, GameState?, bool, MonetizationState?)> loadState({ String? fileName, }) async { if (onLoad != null) { return onLoad!(fileName); } return (const SaveOutcome.success(), null, false, null); } @override Future> listSaves() async => []; @override Future deleteSave({String? fileName}) async { return const SaveOutcome.success(); } @override Future saveExists({String? fileName}) async => false; } /// 테스트용 팩토리 클래스 /// /// 테스트에서 자주 사용되는 객체 생성을 중앙화 class MockFactories { MockFactories._(); /// 기본 PqConfig 생성 static const PqConfig config = PqConfig(); /// GameMutations 생성 static GameMutations createMutations([PqConfig? cfg]) { return GameMutations(cfg ?? config); } /// ProgressService 생성 static ProgressService createProgressService([PqConfig? cfg]) { final c = cfg ?? config; final mutations = GameMutations(c); return ProgressService( config: c, mutations: mutations, rewards: RewardService(mutations, c), ); } /// 기본 GameState 생성 /// /// [seed]: 결정론적 랜덤 시드 /// [level]: 캐릭터 레벨 static GameState createGameState({ int seed = 42, int level = 1, }) { return GameState.withSeed(seed: seed); } /// 테스트용 CombatState 생성 static CombatState createCombat({ int playerHpMax = 100, int playerHpCurrent = 100, int monsterHpMax = 50, int monsterHpCurrent = 50, int monsterLevel = 1, String monsterName = 'Test Monster', }) { final playerStats = CombatStats.empty().copyWith( hpMax: playerHpMax, hpCurrent: playerHpCurrent, mpMax: 50, mpCurrent: 50, atk: 20, def: 10, attackDelayMs: 1000, ); final monsterStats = MonsterCombatStats( name: monsterName, level: monsterLevel, atk: 10, def: 5, magDef: 5, hpMax: monsterHpMax, hpCurrent: monsterHpCurrent, criRate: 0.05, criDamage: 1.5, evasion: 0.0, accuracy: 0.8, attackDelayMs: 1000, expReward: 100, ); return CombatState( playerStats: playerStats, monsterStats: monsterStats, playerAttackAccumulatorMs: 0, monsterAttackAccumulatorMs: 0, totalDamageDealt: 0, totalDamageTaken: 0, turnsElapsed: 0, isActive: true, ); } /// 테스트용 MonsterCombatStats 생성 static MonsterCombatStats createMonsterStats({ String name = 'Test Monster', int level = 1, int atk = 10, int def = 5, int magDef = 5, int hpMax = 100, int? hpCurrent, double criRate = 0.05, double criDamage = 1.5, double evasion = 0.0, double accuracy = 0.8, int attackDelayMs = 1000, int expReward = 100, }) { return MonsterCombatStats( name: name, level: level, atk: atk, def: def, magDef: magDef, hpMax: hpMax, hpCurrent: hpCurrent ?? hpMax, criRate: criRate, criDamage: criDamage, evasion: evasion, accuracy: accuracy, attackDelayMs: attackDelayMs, expReward: expReward, ); } /// 밸런스 상수 기반 몬스터 스탯 생성 static MonsterCombatStats createBalancedMonsterStats({ required int level, MonsterType type = MonsterType.normal, }) { final base = MonsterBaseStats.generate(level, type); return MonsterCombatStats( name: 'Balanced Monster Lv$level', level: level, atk: base.atk, def: base.def, magDef: base.def, // 물리 방어와 동일 hpMax: base.hp, hpCurrent: base.hp, criRate: 0.05, criDamage: 1.5, evasion: 0.0, accuracy: 0.8, attackDelayMs: 1000, expReward: base.exp, ); } } /// 테스트용 Fake HallOfFameStorage /// /// 파일 시스템 접근 없이 메모리에서 동작 class FakeHallOfFameStorage extends HallOfFameStorage { HallOfFame _hallOfFame = HallOfFame.empty(); @override Future load() async => _hallOfFame; @override Future save(HallOfFame hallOfFame) async { _hallOfFame = hallOfFame; return true; } @override Future addEntry(HallOfFameEntry entry) async { _hallOfFame = _hallOfFame.addEntry(entry); return true; } @override Future deleteEntry(String id) async { _hallOfFame = _hallOfFame.removeEntry(id); return true; } @override Future clear() async { _hallOfFame = HallOfFame.empty(); return true; } } /// 테스트용 Fake StatisticsStorage /// /// 파일 시스템 접근 없이 메모리에서 동작 class FakeStatisticsStorage extends StatisticsStorage { CumulativeStatistics _stats = CumulativeStatistics.empty(); @override Future loadCumulative() async => _stats; @override Future saveCumulative(CumulativeStatistics stats) async { _stats = stats; return true; } @override Future mergeSession(SessionStatistics session) async { _stats = _stats.mergeSession(session); return true; } @override Future updateHighestLevel(int level) async { if (level <= _stats.highestLevel) return true; _stats = _stats.updateHighestLevel(level); return true; } @override Future updateHighestGold(int gold) async { if (gold <= _stats.highestGoldHeld) return true; _stats = _stats.updateHighestGold(gold); return true; } @override Future recordGameStart() async { _stats = _stats.recordGameStart(); return true; } @override Future recordGameComplete() async { _stats = _stats.recordGameComplete(); return true; } @override Future clear() async { _stats = CumulativeStatistics.empty(); return true; } }