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/monster_combat_stats.dart'; import 'package:asciineverdie/src/core/model/pq_config.dart'; import 'package:asciineverdie/src/core/util/balance_constants.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { late ProgressService service; late PqConfig config; setUp(() { config = const PqConfig(); final mutations = GameMutations(config); service = ProgressService( config: config, mutations: mutations, rewards: RewardService(mutations, config), ); }); test('tick advances task bar and recalculates encumbrance', () { final state = GameState.withSeed( seed: 42, stats: const Stats( str: 5, con: 0, dex: 0, intelligence: 0, wis: 0, cha: 0, hpMax: 0, mpMax: 0, ), inventory: const Inventory( gold: 5, items: [InventoryEntry(name: 'Rock', count: 3)], ), progress: const ProgressState( task: ProgressBarState(position: 0, max: 80), quest: ProgressBarState(position: 0, max: 10), plot: ProgressBarState(position: 0, max: 10), exp: ProgressBarState(position: 0, max: 50), encumbrance: ProgressBarState(position: 0, max: 0), currentTask: TaskInfo(caption: 'Test', type: TaskType.kill), plotStageCount: 1, questCount: 0, ), ); final result = service.tick(state, 150); expect(result.leveledUp, isFalse); expect(result.completedQuest, isFalse); expect(result.completedAct, isFalse); expect(result.state.progress.task.position, 80); expect(result.state.progress.task.max, 80); expect(result.state.progress.encumbrance.position, 3); expect(result.state.progress.encumbrance.max, 15); expect(result.state.progress.currentTask.caption, 'Test'); }); test('tick levels up when EXP is full during kill task', () { // 레벨업에 필요한 경험치 (레벨 1 → 2) final requiredExp = ExpConstants.requiredExp(1); // 몬스터가 requiredExp 이상의 경험치를 주도록 설정 final monsterStats = MonsterCombatStats( name: 'Test Monster', level: 10, atk: 10, def: 5, hpMax: 100, hpCurrent: 0, // 몬스터 사망 상태 criRate: 0.05, criDamage: 1.5, evasion: 0.0, accuracy: 0.8, attackDelayMs: 1000, expReward: requiredExp + 100, // 레벨업에 충분한 경험치 ); final combatState = CombatState( playerStats: CombatStats.empty(), monsterStats: monsterStats, playerAttackAccumulatorMs: 0, monsterAttackAccumulatorMs: 0, totalDamageDealt: 100, totalDamageTaken: 0, turnsElapsed: 1, isActive: true, ); final initial = GameState.withSeed( seed: 7, traits: const Traits( name: 'Hero', race: 'Human', klass: 'Fighter', level: 1, motto: '', guild: '', ), stats: const Stats( str: 10, con: 9, dex: 8, intelligence: 7, wis: 6, cha: 5, hpMax: 10, mpMax: 11, ), progress: ProgressState( task: const ProgressBarState(position: 1000, max: 1000), quest: const ProgressBarState(position: 0, max: 10), plot: const ProgressBarState(position: 0, max: 10), exp: ProgressBarState(position: requiredExp - 50, max: requiredExp), encumbrance: const ProgressBarState(position: 0, max: 0), currentTask: const TaskInfo(caption: 'Battle', type: TaskType.kill), plotStageCount: 1, questCount: 0, currentCombat: combatState, ), ); final result = service.tick(initial, 50); expect(result.leveledUp, isTrue); expect(result.shouldAutosave, isTrue); expect(result.state.traits.level, 2); expect(result.state.stats.hpMax, greaterThan(initial.stats.hpMax)); expect(result.state.stats.mpMax, greaterThan(initial.stats.mpMax)); // 새 레벨의 필요 경험치로 초기화됨 expect(result.state.progress.exp.max, ExpConstants.requiredExp(2)); // 태스크 완료 후 새 태스크가 자동으로 시작됨 expect(result.state.progress.task.position, 0); expect(result.state.progress.task.max, greaterThan(0)); }); test('quest completion enqueues next task and resets quest bar', () { final initial = GameState.withSeed( seed: 99, traits: const Traits( name: 'Questor', race: 'Elf', klass: 'Mage', level: 3, motto: '', guild: '', ), stats: const Stats( str: 4, con: 5, dex: 6, intelligence: 7, wis: 8, cha: 9, hpMax: 12, mpMax: 10, ), progress: const ProgressState( task: ProgressBarState(position: 2000, max: 2000), quest: ProgressBarState(position: 4, max: 5), plot: ProgressBarState(position: 0, max: 20), exp: ProgressBarState(position: 0, max: 30), encumbrance: ProgressBarState(position: 0, max: 0), currentTask: TaskInfo(caption: 'Hunt', type: TaskType.kill), plotStageCount: 2, questCount: 1, ), queue: QueueState(entries: const []), ); final result = service.tick(initial, 50); final nextState = result.state; expect(result.completedQuest, isTrue); expect(nextState.progress.questCount, 2); expect(nextState.progress.quest.position, 0); expect(nextState.progress.quest.max, inInclusiveRange(50, 149)); expect(nextState.progress.currentTask.type, TaskType.neutral); expect(nextState.progress.task.position, 0); expect(nextState.queue.entries, isEmpty); }); }