feat(core): 장비 시스템 및 게임 상태 모델 확장
- Equipment 클래스를 11개 슬롯으로 확장 (원본 Main.dfm 충실) - TaskInfo에 몬스터 정보(baseName, part) 추가 - Stats에 현재 HP/MP 필드 추가 - 히스토리 기능 구현 (plotHistory, questHistory) - pq_logic winEquip/winStatIndex 원본 로직 개선 - 퀘스트 몬스터 처리 로직 구현 - SaveData 직렬화 확장
This commit is contained in:
@@ -90,16 +90,37 @@ enum TaskType {
|
||||
}
|
||||
|
||||
class TaskInfo {
|
||||
const TaskInfo({required this.caption, required this.type});
|
||||
const TaskInfo({
|
||||
required this.caption,
|
||||
required this.type,
|
||||
this.monsterBaseName,
|
||||
this.monsterPart,
|
||||
});
|
||||
|
||||
final String caption;
|
||||
final TaskType type;
|
||||
|
||||
/// 킬 태스크의 몬스터 기본 이름 (형용사 제외, 예: "Goblin")
|
||||
final String? monsterBaseName;
|
||||
|
||||
/// 킬 태스크의 전리품 부위 (예: "claw", "tail", "*"는 WinItem)
|
||||
final String? monsterPart;
|
||||
|
||||
factory TaskInfo.empty() =>
|
||||
const TaskInfo(caption: '', type: TaskType.neutral);
|
||||
|
||||
TaskInfo copyWith({String? caption, TaskType? type}) {
|
||||
return TaskInfo(caption: caption ?? this.caption, type: type ?? this.type);
|
||||
TaskInfo copyWith({
|
||||
String? caption,
|
||||
TaskType? type,
|
||||
String? monsterBaseName,
|
||||
String? monsterPart,
|
||||
}) {
|
||||
return TaskInfo(
|
||||
caption: caption ?? this.caption,
|
||||
type: type ?? this.type,
|
||||
monsterBaseName: monsterBaseName ?? this.monsterBaseName,
|
||||
monsterPart: monsterPart ?? this.monsterPart,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +179,8 @@ class Stats {
|
||||
required this.cha,
|
||||
required this.hpMax,
|
||||
required this.mpMax,
|
||||
this.hpCurrent,
|
||||
this.mpCurrent,
|
||||
});
|
||||
|
||||
final int str;
|
||||
@@ -169,6 +192,18 @@ class Stats {
|
||||
final int hpMax;
|
||||
final int mpMax;
|
||||
|
||||
/// 현재 HP (null이면 hpMax와 동일)
|
||||
final int? hpCurrent;
|
||||
|
||||
/// 현재 MP (null이면 mpMax와 동일)
|
||||
final int? mpCurrent;
|
||||
|
||||
/// 실제 현재 HP 값
|
||||
int get hp => hpCurrent ?? hpMax;
|
||||
|
||||
/// 실제 현재 MP 값
|
||||
int get mp => mpCurrent ?? mpMax;
|
||||
|
||||
factory Stats.empty() => const Stats(
|
||||
str: 0,
|
||||
con: 0,
|
||||
@@ -189,6 +224,8 @@ class Stats {
|
||||
int? cha,
|
||||
int? hpMax,
|
||||
int? mpMax,
|
||||
int? hpCurrent,
|
||||
int? mpCurrent,
|
||||
}) {
|
||||
return Stats(
|
||||
str: str ?? this.str,
|
||||
@@ -199,6 +236,8 @@ class Stats {
|
||||
cha: cha ?? this.cha,
|
||||
hpMax: hpMax ?? this.hpMax,
|
||||
mpMax: mpMax ?? this.mpMax,
|
||||
hpCurrent: hpCurrent ?? this.hpCurrent,
|
||||
mpCurrent: mpCurrent ?? this.mpCurrent,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -227,38 +266,118 @@ class Inventory {
|
||||
}
|
||||
}
|
||||
|
||||
/// 장비 (원본 Main.dfm Equips ListView, 11개 슬롯)
|
||||
class Equipment {
|
||||
const Equipment({
|
||||
required this.weapon,
|
||||
required this.shield,
|
||||
required this.armor,
|
||||
required this.helm,
|
||||
required this.hauberk,
|
||||
required this.brassairts,
|
||||
required this.vambraces,
|
||||
required this.gauntlets,
|
||||
required this.gambeson,
|
||||
required this.cuisses,
|
||||
required this.greaves,
|
||||
required this.sollerets,
|
||||
required this.bestIndex,
|
||||
});
|
||||
|
||||
final String weapon;
|
||||
final String shield;
|
||||
final String armor;
|
||||
final String weapon; // 0: 무기
|
||||
final String shield; // 1: 방패
|
||||
final String helm; // 2: 투구
|
||||
final String hauberk; // 3: 사슬갑옷
|
||||
final String brassairts; // 4: 상완갑
|
||||
final String vambraces; // 5: 전완갑
|
||||
final String gauntlets; // 6: 건틀릿
|
||||
final String gambeson; // 7: 갬비슨
|
||||
final String cuisses; // 8: 허벅지갑
|
||||
final String greaves; // 9: 정강이갑
|
||||
final String sollerets; // 10: 철제신발
|
||||
|
||||
/// Tracks best slot index (mirror of Equips.Tag in original code; 0=weapon,1=shield,2=armor).
|
||||
/// 최고 아이템 슬롯 인덱스 (원본 Equips.Tag, 0-10)
|
||||
final int bestIndex;
|
||||
|
||||
/// 슬롯 개수
|
||||
static const slotCount = 11;
|
||||
|
||||
factory Equipment.empty() => const Equipment(
|
||||
weapon: 'Sharp Stick',
|
||||
shield: '',
|
||||
armor: '',
|
||||
helm: '',
|
||||
hauberk: '',
|
||||
brassairts: '',
|
||||
vambraces: '',
|
||||
gauntlets: '',
|
||||
gambeson: '',
|
||||
cuisses: '',
|
||||
greaves: '',
|
||||
sollerets: '',
|
||||
bestIndex: 0,
|
||||
);
|
||||
|
||||
/// 인덱스로 슬롯 값 가져오기
|
||||
String getByIndex(int index) {
|
||||
return switch (index) {
|
||||
0 => weapon,
|
||||
1 => shield,
|
||||
2 => helm,
|
||||
3 => hauberk,
|
||||
4 => brassairts,
|
||||
5 => vambraces,
|
||||
6 => gauntlets,
|
||||
7 => gambeson,
|
||||
8 => cuisses,
|
||||
9 => greaves,
|
||||
10 => sollerets,
|
||||
_ => '',
|
||||
};
|
||||
}
|
||||
|
||||
/// 인덱스로 슬롯 값 설정한 새 Equipment 반환
|
||||
Equipment setByIndex(int index, String value) {
|
||||
return switch (index) {
|
||||
0 => copyWith(weapon: value),
|
||||
1 => copyWith(shield: value),
|
||||
2 => copyWith(helm: value),
|
||||
3 => copyWith(hauberk: value),
|
||||
4 => copyWith(brassairts: value),
|
||||
5 => copyWith(vambraces: value),
|
||||
6 => copyWith(gauntlets: value),
|
||||
7 => copyWith(gambeson: value),
|
||||
8 => copyWith(cuisses: value),
|
||||
9 => copyWith(greaves: value),
|
||||
10 => copyWith(sollerets: value),
|
||||
_ => this,
|
||||
};
|
||||
}
|
||||
|
||||
Equipment copyWith({
|
||||
String? weapon,
|
||||
String? shield,
|
||||
String? armor,
|
||||
String? helm,
|
||||
String? hauberk,
|
||||
String? brassairts,
|
||||
String? vambraces,
|
||||
String? gauntlets,
|
||||
String? gambeson,
|
||||
String? cuisses,
|
||||
String? greaves,
|
||||
String? sollerets,
|
||||
int? bestIndex,
|
||||
}) {
|
||||
return Equipment(
|
||||
weapon: weapon ?? this.weapon,
|
||||
shield: shield ?? this.shield,
|
||||
armor: armor ?? this.armor,
|
||||
helm: helm ?? this.helm,
|
||||
hauberk: hauberk ?? this.hauberk,
|
||||
brassairts: brassairts ?? this.brassairts,
|
||||
vambraces: vambraces ?? this.vambraces,
|
||||
gauntlets: gauntlets ?? this.gauntlets,
|
||||
gambeson: gambeson ?? this.gambeson,
|
||||
cuisses: cuisses ?? this.cuisses,
|
||||
greaves: greaves ?? this.greaves,
|
||||
sollerets: sollerets ?? this.sollerets,
|
||||
bestIndex: bestIndex ?? this.bestIndex,
|
||||
);
|
||||
}
|
||||
@@ -304,6 +423,40 @@ class ProgressBarState {
|
||||
}
|
||||
}
|
||||
|
||||
/// 히스토리 엔트리 (Plot/Quest 진행 기록)
|
||||
class HistoryEntry {
|
||||
const HistoryEntry({required this.caption, required this.isComplete});
|
||||
|
||||
/// 표시 텍스트 (예: "Prologue", "Act I", "Exterminate the Goblins")
|
||||
final String caption;
|
||||
|
||||
/// 완료 여부 (원본 StateIndex: 0=진행중, 1=완료)
|
||||
final bool isComplete;
|
||||
|
||||
HistoryEntry copyWith({String? caption, bool? isComplete}) {
|
||||
return HistoryEntry(
|
||||
caption: caption ?? this.caption,
|
||||
isComplete: isComplete ?? this.isComplete,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 현재 퀘스트 몬스터 정보 (원본 fQuest)
|
||||
class QuestMonsterInfo {
|
||||
const QuestMonsterInfo({
|
||||
required this.monsterData,
|
||||
required this.monsterIndex,
|
||||
});
|
||||
|
||||
/// 몬스터 데이터 문자열 (예: "Goblin|3|ear")
|
||||
final String monsterData;
|
||||
|
||||
/// 몬스터 인덱스 (Config.monsters에서의 인덱스)
|
||||
final int monsterIndex;
|
||||
|
||||
static const empty = QuestMonsterInfo(monsterData: '', monsterIndex: -1);
|
||||
}
|
||||
|
||||
class ProgressState {
|
||||
const ProgressState({
|
||||
required this.task,
|
||||
@@ -314,6 +467,9 @@ class ProgressState {
|
||||
required this.currentTask,
|
||||
required this.plotStageCount,
|
||||
required this.questCount,
|
||||
this.plotHistory = const [],
|
||||
this.questHistory = const [],
|
||||
this.currentQuestMonster,
|
||||
});
|
||||
|
||||
final ProgressBarState task;
|
||||
@@ -325,6 +481,15 @@ class ProgressState {
|
||||
final int plotStageCount;
|
||||
final int questCount;
|
||||
|
||||
/// 플롯 히스토리 (Prologue, Act I, Act II, ...)
|
||||
final List<HistoryEntry> plotHistory;
|
||||
|
||||
/// 퀘스트 히스토리 (완료된/진행중인 퀘스트 목록)
|
||||
final List<HistoryEntry> questHistory;
|
||||
|
||||
/// 현재 퀘스트 몬스터 정보 (Exterminate 타입용)
|
||||
final QuestMonsterInfo? currentQuestMonster;
|
||||
|
||||
factory ProgressState.empty() => ProgressState(
|
||||
task: ProgressBarState.empty(),
|
||||
quest: ProgressBarState.empty(),
|
||||
@@ -334,6 +499,9 @@ class ProgressState {
|
||||
currentTask: TaskInfo.empty(),
|
||||
plotStageCount: 1, // Prologue
|
||||
questCount: 0,
|
||||
plotHistory: const [HistoryEntry(caption: 'Prologue', isComplete: false)],
|
||||
questHistory: const [],
|
||||
currentQuestMonster: null,
|
||||
);
|
||||
|
||||
ProgressState copyWith({
|
||||
@@ -345,6 +513,9 @@ class ProgressState {
|
||||
TaskInfo? currentTask,
|
||||
int? plotStageCount,
|
||||
int? questCount,
|
||||
List<HistoryEntry>? plotHistory,
|
||||
List<HistoryEntry>? questHistory,
|
||||
QuestMonsterInfo? currentQuestMonster,
|
||||
}) {
|
||||
return ProgressState(
|
||||
task: task ?? this.task,
|
||||
@@ -355,6 +526,9 @@ class ProgressState {
|
||||
currentTask: currentTask ?? this.currentTask,
|
||||
plotStageCount: plotStageCount ?? this.plotStageCount,
|
||||
questCount: questCount ?? this.questCount,
|
||||
plotHistory: plotHistory ?? this.plotHistory,
|
||||
questHistory: questHistory ?? this.questHistory,
|
||||
currentQuestMonster: currentQuestMonster ?? this.currentQuestMonster,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user