feat: 초기 커밋

- Progress Quest 6.4 Flutter 포팅 프로젝트
- 게임 루프, 상태 관리, UI 구현
- 캐릭터 생성, 인벤토리, 장비, 주문 시스템
- 시장/판매/구매 메커니즘
This commit is contained in:
JiWoong Sul
2025-12-09 17:24:04 +09:00
commit 08054d97c1
168 changed files with 12876 additions and 0 deletions

View File

@@ -0,0 +1,237 @@
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<String, dynamic> 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,
},
'inventory': {
'gold': inventory.gold,
'items': inventory.items
.map((e) => {'name': e.name, 'count': e.count})
.toList(),
},
'equipment': {
'weapon': equipment.weapon,
'shield': equipment.shield,
'armor': equipment.armor,
'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,
},
'plotStages': progress.plotStageCount,
'questCount': progress.questCount,
},
'queue': queue.entries
.map(
(e) => {
'kind': e.kind.name,
'duration': e.durationMillis,
'caption': e.caption,
'taskType': e.taskType.name,
},
)
.toList(),
};
}
static GameSave fromJson(Map<String, dynamic> json) {
final traitsJson = json['traits'] as Map<String, dynamic>;
final statsJson = json['stats'] as Map<String, dynamic>;
final inventoryJson = json['inventory'] as Map<String, dynamic>;
final equipmentJson = json['equipment'] as Map<String, dynamic>;
final progressJson = json['progress'] as Map<String, dynamic>;
final queueJson = (json['queue'] as List<dynamic>? ?? []).cast<dynamic>();
final spellsJson = (json['spells'] as List<dynamic>? ?? []).cast<dynamic>();
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,
),
inventory: Inventory(
gold: inventoryJson['gold'] as int? ?? 0,
items: (inventoryJson['items'] as List<dynamic>? ?? [])
.map(
(e) => InventoryEntry(
name: (e as Map<String, dynamic>)['name'] as String? ?? '',
count: (e)['count'] as int? ?? 0,
),
)
.toList(),
),
equipment: Equipment(
weapon: equipmentJson['weapon'] as String? ?? 'Sharp Stick',
shield: equipmentJson['shield'] as String? ?? '',
armor: equipmentJson['armor'] as String? ?? '',
bestIndex: equipmentJson['bestIndex'] as int? ?? 0,
),
spellBook: SpellBook(
spells: spellsJson
.map(
(e) => SpellEntry(
name: (e as Map<String, dynamic>)['name'] as String? ?? '',
rank: (e)['rank'] as String? ?? 'I',
),
)
.toList(),
),
progress: ProgressState(
task: _barFromJson(progressJson['task'] as Map<String, dynamic>? ?? {}),
quest: _barFromJson(
progressJson['quest'] as Map<String, dynamic>? ?? {},
),
plot: _barFromJson(progressJson['plot'] as Map<String, dynamic>? ?? {}),
exp: _barFromJson(progressJson['exp'] as Map<String, dynamic>? ?? {}),
encumbrance: _barFromJson(
progressJson['encumbrance'] as Map<String, dynamic>? ?? {},
),
currentTask: _taskInfoFromJson(
progressJson['taskInfo'] as Map<String, dynamic>? ??
<String, dynamic>{},
),
plotStageCount: progressJson['plotStages'] as int? ?? 1,
questCount: progressJson['questCount'] as int? ?? 0,
),
queue: QueueState(
entries: Queue<QueueEntry>.from(
queueJson.map((e) {
final m = e as Map<String, dynamic>;
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<String, dynamic> _barToJson(ProgressBarState bar) => {
'pos': bar.position,
'max': bar.max,
};
ProgressBarState _barFromJson(Map<String, dynamic> json) => ProgressBarState(
position: json['pos'] as int? ?? 0,
max: json['max'] as int? ?? 1,
);
TaskInfo _taskInfoFromJson(Map<String, dynamic> 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);
}