import 'dart:collection'; import 'package:askiineverdie/src/core/util/deterministic_random.dart'; import 'package:askiineverdie/src/core/model/game_state.dart'; const int kSaveVersion = 2; class GameSave { GameSave({ required this.version, required this.rngState, required this.traits, required this.stats, required this.inventory, required this.equipment, required this.spellBook, required this.progress, required this.queue, }); factory GameSave.fromState(GameState state) { return GameSave( version: kSaveVersion, rngState: state.rng.state, traits: state.traits, stats: state.stats, inventory: state.inventory, equipment: state.equipment, spellBook: state.spellBook, progress: state.progress, queue: state.queue, ); } final int version; final int rngState; final Traits traits; final Stats stats; final Inventory inventory; final Equipment equipment; final SpellBook spellBook; final ProgressState progress; final QueueState queue; Map toJson() { return { 'version': version, 'rng': rngState, 'traits': { 'name': traits.name, 'race': traits.race, 'klass': traits.klass, 'level': traits.level, 'motto': traits.motto, 'guild': traits.guild, }, 'stats': { 'str': stats.str, 'con': stats.con, 'dex': stats.dex, 'int': stats.intelligence, 'wis': stats.wis, 'cha': stats.cha, 'hpMax': stats.hpMax, 'mpMax': stats.mpMax, 'hpCurrent': stats.hpCurrent, 'mpCurrent': stats.mpCurrent, }, 'inventory': { 'gold': inventory.gold, 'items': inventory.items .map((e) => {'name': e.name, 'count': e.count}) .toList(), }, 'equipment': { 'weapon': equipment.weapon, 'shield': equipment.shield, 'helm': equipment.helm, 'hauberk': equipment.hauberk, 'brassairts': equipment.brassairts, 'vambraces': equipment.vambraces, 'gauntlets': equipment.gauntlets, 'gambeson': equipment.gambeson, 'cuisses': equipment.cuisses, 'greaves': equipment.greaves, 'sollerets': equipment.sollerets, 'bestIndex': equipment.bestIndex, }, 'spells': spellBook.spells .map((e) => {'name': e.name, 'rank': e.rank}) .toList(), 'progress': { 'task': _barToJson(progress.task), 'quest': _barToJson(progress.quest), 'plot': _barToJson(progress.plot), 'exp': _barToJson(progress.exp), 'encumbrance': _barToJson(progress.encumbrance), 'taskInfo': { 'caption': progress.currentTask.caption, 'type': progress.currentTask.type.name, 'monsterBaseName': progress.currentTask.monsterBaseName, 'monsterPart': progress.currentTask.monsterPart, }, 'plotStages': progress.plotStageCount, 'questCount': progress.questCount, 'plotHistory': progress.plotHistory .map((e) => {'caption': e.caption, 'complete': e.isComplete}) .toList(), 'questHistory': progress.questHistory .map((e) => {'caption': e.caption, 'complete': e.isComplete}) .toList(), 'questMonster': progress.currentQuestMonster != null ? { 'data': progress.currentQuestMonster!.monsterData, 'index': progress.currentQuestMonster!.monsterIndex, } : null, }, 'queue': queue.entries .map( (e) => { 'kind': e.kind.name, 'duration': e.durationMillis, 'caption': e.caption, 'taskType': e.taskType.name, }, ) .toList(), }; } static GameSave fromJson(Map json) { final traitsJson = json['traits'] as Map; final statsJson = json['stats'] as Map; final inventoryJson = json['inventory'] as Map; final equipmentJson = json['equipment'] as Map; final progressJson = json['progress'] as Map; final queueJson = (json['queue'] as List? ?? []).cast(); final spellsJson = (json['spells'] as List? ?? []).cast(); return GameSave( version: json['version'] as int? ?? kSaveVersion, rngState: json['rng'] as int? ?? 0, traits: Traits( name: traitsJson['name'] as String? ?? '', race: traitsJson['race'] as String? ?? '', klass: traitsJson['klass'] as String? ?? '', level: traitsJson['level'] as int? ?? 1, motto: traitsJson['motto'] as String? ?? '', guild: traitsJson['guild'] as String? ?? '', ), stats: Stats( str: statsJson['str'] as int? ?? 0, con: statsJson['con'] as int? ?? 0, dex: statsJson['dex'] as int? ?? 0, intelligence: statsJson['int'] as int? ?? 0, wis: statsJson['wis'] as int? ?? 0, cha: statsJson['cha'] as int? ?? 0, hpMax: statsJson['hpMax'] as int? ?? 0, mpMax: statsJson['mpMax'] as int? ?? 0, hpCurrent: statsJson['hpCurrent'] as int?, mpCurrent: statsJson['mpCurrent'] as int?, ), inventory: Inventory( gold: inventoryJson['gold'] as int? ?? 0, items: (inventoryJson['items'] as List? ?? []) .map( (e) => InventoryEntry( name: (e as Map)['name'] as String? ?? '', count: (e)['count'] as int? ?? 0, ), ) .toList(), ), equipment: Equipment.fromStrings( weapon: equipmentJson['weapon'] as String? ?? 'Keyboard', shield: equipmentJson['shield'] as String? ?? '', helm: equipmentJson['helm'] as String? ?? '', hauberk: equipmentJson['hauberk'] as String? ?? '', brassairts: equipmentJson['brassairts'] as String? ?? '', vambraces: equipmentJson['vambraces'] as String? ?? '', gauntlets: equipmentJson['gauntlets'] as String? ?? '', gambeson: equipmentJson['gambeson'] as String? ?? '', cuisses: equipmentJson['cuisses'] as String? ?? '', greaves: equipmentJson['greaves'] as String? ?? '', sollerets: equipmentJson['sollerets'] as String? ?? '', bestIndex: equipmentJson['bestIndex'] as int? ?? 0, ), spellBook: SpellBook( spells: spellsJson .map( (e) => SpellEntry( name: (e as Map)['name'] as String? ?? '', rank: (e)['rank'] as String? ?? 'I', ), ) .toList(), ), progress: ProgressState( task: _barFromJson(progressJson['task'] as Map? ?? {}), quest: _barFromJson( progressJson['quest'] as Map? ?? {}, ), plot: _barFromJson(progressJson['plot'] as Map? ?? {}), exp: _barFromJson(progressJson['exp'] as Map? ?? {}), encumbrance: _barFromJson( progressJson['encumbrance'] as Map? ?? {}, ), currentTask: _taskInfoFromJson( progressJson['taskInfo'] as Map? ?? {}, ), plotStageCount: progressJson['plotStages'] as int? ?? 1, questCount: progressJson['questCount'] as int? ?? 0, plotHistory: _historyListFromJson( progressJson['plotHistory'] as List?, ), questHistory: _historyListFromJson( progressJson['questHistory'] as List?, ), currentQuestMonster: _questMonsterFromJson( progressJson['questMonster'] as Map?, ), ), queue: QueueState( entries: Queue.from( queueJson.map((e) { final m = e as Map; final kind = QueueKind.values.firstWhere( (k) => k.name == m['kind'], orElse: () => QueueKind.task, ); final taskType = TaskType.values.firstWhere( (t) => t.name == m['taskType'], orElse: () => TaskType.neutral, ); return QueueEntry( kind: kind, durationMillis: m['duration'] as int? ?? 0, caption: m['caption'] as String? ?? '', taskType: taskType, ); }), ), ), ); } GameState toState() { return GameState( rng: DeterministicRandom.fromState(rngState), traits: traits, stats: stats, inventory: inventory, equipment: equipment, spellBook: spellBook, progress: progress, queue: queue, ); } } Map _barToJson(ProgressBarState bar) => { 'pos': bar.position, 'max': bar.max, }; ProgressBarState _barFromJson(Map json) => ProgressBarState( position: json['pos'] as int? ?? 0, max: json['max'] as int? ?? 1, ); TaskInfo _taskInfoFromJson(Map json) { final typeName = json['type'] as String?; final type = TaskType.values.firstWhere( (t) => t.name == typeName, orElse: () => TaskType.neutral, ); return TaskInfo( caption: json['caption'] as String? ?? '', type: type, monsterBaseName: json['monsterBaseName'] as String?, monsterPart: json['monsterPart'] as String?, ); } List _historyListFromJson(List? json) { if (json == null || json.isEmpty) return []; return json.map((e) { final m = e as Map; return HistoryEntry( caption: m['caption'] as String? ?? '', isComplete: m['complete'] as bool? ?? false, ); }).toList(); } QuestMonsterInfo? _questMonsterFromJson(Map? json) { if (json == null) return null; return QuestMonsterInfo( monsterData: json['data'] as String? ?? '', monsterIndex: json['index'] as int? ?? -1, ); }