refactor(core): 애니메이션 컴포저 및 저장 데이터 개선
- CanvasWalkingComposer 정리 - SaveData 모델 확장
This commit is contained in:
@@ -1,9 +1,15 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:asciineverdie/src/core/model/equipment_item.dart';
|
||||
import 'package:asciineverdie/src/core/model/equipment_slot.dart';
|
||||
import 'package:asciineverdie/src/core/model/monster_grade.dart';
|
||||
import 'package:asciineverdie/src/core/util/deterministic_random.dart';
|
||||
import 'package:asciineverdie/src/core/model/game_state.dart';
|
||||
|
||||
const int kSaveVersion = 2;
|
||||
/// 세이브 파일 버전
|
||||
/// - v2: 장비 이름만 저장 (레거시)
|
||||
/// - v3: 장비 전체 정보 저장 (level, rarity, stats 포함)
|
||||
const int kSaveVersion = 3;
|
||||
|
||||
class GameSave {
|
||||
GameSave({
|
||||
@@ -79,17 +85,7 @@ class GameSave {
|
||||
.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,
|
||||
'items': equipment.items.map((e) => e.toJson()).toList(),
|
||||
'bestIndex': equipment.bestIndex,
|
||||
},
|
||||
'skills': skillBook.skills
|
||||
@@ -106,6 +102,8 @@ class GameSave {
|
||||
'type': progress.currentTask.type.name,
|
||||
'monsterBaseName': progress.currentTask.monsterBaseName,
|
||||
'monsterPart': progress.currentTask.monsterPart,
|
||||
'monsterLevel': progress.currentTask.monsterLevel,
|
||||
'monsterGrade': progress.currentTask.monsterGrade?.name,
|
||||
},
|
||||
'plotStages': progress.plotStageCount,
|
||||
'questCount': progress.questCount,
|
||||
@@ -183,20 +181,7 @@ class GameSave {
|
||||
)
|
||||
.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,
|
||||
),
|
||||
equipment: _equipmentFromJson(equipmentJson, json['version'] as int? ?? 2),
|
||||
skillBook: SkillBook(
|
||||
skills: skillsJson
|
||||
.map(
|
||||
@@ -289,11 +274,23 @@ TaskInfo _taskInfoFromJson(Map<String, dynamic> json) {
|
||||
(t) => t.name == typeName,
|
||||
orElse: () => TaskType.neutral,
|
||||
);
|
||||
|
||||
// monsterGrade 파싱
|
||||
final gradeName = json['monsterGrade'] as String?;
|
||||
final monsterGrade = gradeName != null
|
||||
? MonsterGrade.values.firstWhere(
|
||||
(g) => g.name == gradeName,
|
||||
orElse: () => MonsterGrade.normal,
|
||||
)
|
||||
: null;
|
||||
|
||||
return TaskInfo(
|
||||
caption: json['caption'] as String? ?? '',
|
||||
type: type,
|
||||
monsterBaseName: json['monsterBaseName'] as String?,
|
||||
monsterPart: json['monsterPart'] as String?,
|
||||
monsterLevel: json['monsterLevel'] as int?,
|
||||
monsterGrade: monsterGrade,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -315,3 +312,46 @@ QuestMonsterInfo? _questMonsterFromJson(Map<String, dynamic>? json) {
|
||||
monsterIndex: json['index'] as int? ?? -1,
|
||||
);
|
||||
}
|
||||
|
||||
/// 장비 데이터 역직렬화 (버전별 분기 처리)
|
||||
///
|
||||
/// - v2 이하: 레거시 문자열 기반 (이름만 저장)
|
||||
/// - v3 이상: 전체 EquipmentItem 정보 저장
|
||||
Equipment _equipmentFromJson(Map<String, dynamic> json, int version) {
|
||||
// v3 이상: 새로운 형식 (items 배열)
|
||||
if (version >= 3 && json['items'] != null) {
|
||||
final itemsList = json['items'] as List<dynamic>;
|
||||
final items = <EquipmentItem>[];
|
||||
|
||||
for (var i = 0; i < Equipment.slotCount; i++) {
|
||||
if (i < itemsList.length) {
|
||||
final itemJson = itemsList[i] as Map<String, dynamic>;
|
||||
items.add(EquipmentItem.fromJson(itemJson));
|
||||
} else {
|
||||
// 누락된 슬롯은 빈 아이템으로 채움
|
||||
items.add(EquipmentItem.empty(EquipmentSlot.values[i]));
|
||||
}
|
||||
}
|
||||
|
||||
return Equipment(
|
||||
items: items,
|
||||
bestIndex: json['bestIndex'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
// v2 이하: 레거시 형식 (문자열 기반)
|
||||
return Equipment.fromStrings(
|
||||
weapon: json['weapon'] as String? ?? 'Keyboard',
|
||||
shield: json['shield'] as String? ?? '',
|
||||
helm: json['helm'] as String? ?? '',
|
||||
hauberk: json['hauberk'] as String? ?? '',
|
||||
brassairts: json['brassairts'] as String? ?? '',
|
||||
vambraces: json['vambraces'] as String? ?? '',
|
||||
gauntlets: json['gauntlets'] as String? ?? '',
|
||||
gambeson: json['gambeson'] as String? ?? '',
|
||||
cuisses: json['cuisses'] as String? ?? '',
|
||||
greaves: json['greaves'] as String? ?? '',
|
||||
sollerets: json['sollerets'] as String? ?? '',
|
||||
bestIndex: json['bestIndex'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user