feat(item): Phase 2 아이템 시스템 구현
- ItemStats, ItemRarity 클래스 추가 (아이템 스탯/희귀도) - EquipmentItem 클래스 추가 (개별 장비 아이템) - ItemService 추가 (아이템 생성/관리/무게 시스템) - Equipment 클래스 확장 (EquipmentItem 기반, 기존 API 호환) - CombatStats에서 장비 스탯 반영 - 레거시 세이브 파일 호환성 유지
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:askiineverdie/src/core/model/combat_state.dart';
|
||||
import 'package:askiineverdie/src/core/model/equipment_item.dart';
|
||||
import 'package:askiineverdie/src/core/model/equipment_slot.dart';
|
||||
import 'package:askiineverdie/src/core/model/item_stats.dart';
|
||||
import 'package:askiineverdie/src/core/util/deterministic_random.dart';
|
||||
|
||||
/// Minimal skeletal state to mirror Progress Quest structures.
|
||||
@@ -275,33 +278,17 @@ class Inventory {
|
||||
}
|
||||
|
||||
/// 장비 (원본 Main.dfm Equips ListView, 11개 슬롯)
|
||||
///
|
||||
/// Phase 2에서 EquipmentItem 기반으로 확장됨.
|
||||
/// 기존 문자열 API(weapon, shield 등)는 호환성을 위해 유지.
|
||||
class Equipment {
|
||||
const Equipment({
|
||||
required this.weapon,
|
||||
required this.shield,
|
||||
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,
|
||||
Equipment({
|
||||
required this.items,
|
||||
required this.bestIndex,
|
||||
});
|
||||
}) : assert(items.length == slotCount, 'Equipment must have $slotCount items');
|
||||
|
||||
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: 철제신발
|
||||
/// 장비 아이템 목록 (11개 슬롯)
|
||||
final List<EquipmentItem> items;
|
||||
|
||||
/// 최고 아이템 슬롯 인덱스 (원본 Equips.Tag, 0-10)
|
||||
final int bestIndex;
|
||||
@@ -309,83 +296,168 @@ class Equipment {
|
||||
/// 슬롯 개수
|
||||
static const slotCount = 11;
|
||||
|
||||
factory Equipment.empty() => const Equipment(
|
||||
weapon: 'Keyboard',
|
||||
shield: '',
|
||||
helm: '',
|
||||
hauberk: '',
|
||||
brassairts: '',
|
||||
vambraces: '',
|
||||
gauntlets: '',
|
||||
gambeson: '',
|
||||
cuisses: '',
|
||||
greaves: '',
|
||||
sollerets: '',
|
||||
bestIndex: 0,
|
||||
);
|
||||
// ============================================================================
|
||||
// 문자열 API (기존 코드 호환성)
|
||||
// ============================================================================
|
||||
|
||||
/// 인덱스로 슬롯 값 가져오기
|
||||
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,
|
||||
_ => '',
|
||||
};
|
||||
String get weapon => items[0].name; // 0: 무기
|
||||
String get shield => items[1].name; // 1: 방패
|
||||
String get helm => items[2].name; // 2: 투구
|
||||
String get hauberk => items[3].name; // 3: 사슬갑옷
|
||||
String get brassairts => items[4].name; // 4: 상완갑
|
||||
String get vambraces => items[5].name; // 5: 전완갑
|
||||
String get gauntlets => items[6].name; // 6: 건틀릿
|
||||
String get gambeson => items[7].name; // 7: 갬비슨
|
||||
String get cuisses => items[8].name; // 8: 허벅지갑
|
||||
String get greaves => items[9].name; // 9: 정강이갑
|
||||
String get sollerets => items[10].name; // 10: 철제신발
|
||||
|
||||
// ============================================================================
|
||||
// EquipmentItem API
|
||||
// ============================================================================
|
||||
|
||||
EquipmentItem get weaponItem => items[0];
|
||||
EquipmentItem get shieldItem => items[1];
|
||||
EquipmentItem get helmItem => items[2];
|
||||
EquipmentItem get hauberkItem => items[3];
|
||||
EquipmentItem get brassairtsItem => items[4];
|
||||
EquipmentItem get vambracesItem => items[5];
|
||||
EquipmentItem get gauntletsItem => items[6];
|
||||
EquipmentItem get gambesonItem => items[7];
|
||||
EquipmentItem get cuissesItem => items[8];
|
||||
EquipmentItem get greavesItem => items[9];
|
||||
EquipmentItem get solleretsItem => items[10];
|
||||
|
||||
/// 모든 장비 스탯 합산
|
||||
ItemStats get totalStats {
|
||||
return items.fold(
|
||||
ItemStats.empty,
|
||||
(sum, item) => sum + item.stats,
|
||||
);
|
||||
}
|
||||
|
||||
/// 인덱스로 슬롯 값 설정한 새 Equipment 반환
|
||||
/// 모든 장비 무게 합산
|
||||
int get totalWeight {
|
||||
return items.fold(0, (sum, item) => sum + item.weight);
|
||||
}
|
||||
|
||||
/// 장착된 아이템 목록 (빈 슬롯 제외)
|
||||
List<EquipmentItem> get equippedItems {
|
||||
return items.where((item) => item.isNotEmpty).toList();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 팩토리 메서드
|
||||
// ============================================================================
|
||||
|
||||
factory Equipment.empty() {
|
||||
return Equipment(
|
||||
items: [
|
||||
EquipmentItem.defaultWeapon(), // 0: 무기 (Keyboard)
|
||||
EquipmentItem.empty(EquipmentSlot.shield), // 1: 방패
|
||||
EquipmentItem.empty(EquipmentSlot.helm), // 2: 투구
|
||||
EquipmentItem.empty(EquipmentSlot.hauberk), // 3: 사슬갑옷
|
||||
EquipmentItem.empty(EquipmentSlot.brassairts), // 4: 상완갑
|
||||
EquipmentItem.empty(EquipmentSlot.vambraces), // 5: 전완갑
|
||||
EquipmentItem.empty(EquipmentSlot.gauntlets), // 6: 건틀릿
|
||||
EquipmentItem.empty(EquipmentSlot.gambeson), // 7: 갬비슨
|
||||
EquipmentItem.empty(EquipmentSlot.cuisses), // 8: 허벅지갑
|
||||
EquipmentItem.empty(EquipmentSlot.greaves), // 9: 정강이갑
|
||||
EquipmentItem.empty(EquipmentSlot.sollerets), // 10: 철제신발
|
||||
],
|
||||
bestIndex: 0,
|
||||
);
|
||||
}
|
||||
|
||||
/// 레거시 문자열 기반 생성자 (세이브 파일 호환용)
|
||||
factory Equipment.fromStrings({
|
||||
required String weapon,
|
||||
required String shield,
|
||||
required String helm,
|
||||
required String hauberk,
|
||||
required String brassairts,
|
||||
required String vambraces,
|
||||
required String gauntlets,
|
||||
required String gambeson,
|
||||
required String cuisses,
|
||||
required String greaves,
|
||||
required String sollerets,
|
||||
required int bestIndex,
|
||||
}) {
|
||||
return Equipment(
|
||||
items: [
|
||||
_itemFromString(weapon, EquipmentSlot.weapon),
|
||||
_itemFromString(shield, EquipmentSlot.shield),
|
||||
_itemFromString(helm, EquipmentSlot.helm),
|
||||
_itemFromString(hauberk, EquipmentSlot.hauberk),
|
||||
_itemFromString(brassairts, EquipmentSlot.brassairts),
|
||||
_itemFromString(vambraces, EquipmentSlot.vambraces),
|
||||
_itemFromString(gauntlets, EquipmentSlot.gauntlets),
|
||||
_itemFromString(gambeson, EquipmentSlot.gambeson),
|
||||
_itemFromString(cuisses, EquipmentSlot.cuisses),
|
||||
_itemFromString(greaves, EquipmentSlot.greaves),
|
||||
_itemFromString(sollerets, EquipmentSlot.sollerets),
|
||||
],
|
||||
bestIndex: bestIndex,
|
||||
);
|
||||
}
|
||||
|
||||
/// 문자열에서 기본 EquipmentItem 생성 (레거시 호환)
|
||||
static EquipmentItem _itemFromString(String name, EquipmentSlot slot) {
|
||||
if (name.isEmpty) {
|
||||
return EquipmentItem.empty(slot);
|
||||
}
|
||||
// 레거시 아이템: 레벨 1, Common, 기본 스탯
|
||||
return EquipmentItem(
|
||||
name: name,
|
||||
slot: slot,
|
||||
level: 1,
|
||||
weight: 5,
|
||||
stats: ItemStats.empty,
|
||||
rarity: ItemRarity.common,
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 유틸리티 메서드
|
||||
// ============================================================================
|
||||
|
||||
/// 인덱스로 슬롯 이름 가져오기 (기존 API 호환)
|
||||
String getByIndex(int index) {
|
||||
if (index < 0 || index >= slotCount) return '';
|
||||
return items[index].name;
|
||||
}
|
||||
|
||||
/// 인덱스로 EquipmentItem 가져오기
|
||||
EquipmentItem getItemByIndex(int index) {
|
||||
if (index < 0 || index >= slotCount) {
|
||||
return EquipmentItem.empty(EquipmentSlot.weapon);
|
||||
}
|
||||
return items[index];
|
||||
}
|
||||
|
||||
/// 인덱스로 슬롯 값 설정 (문자열, 기존 API 호환)
|
||||
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,
|
||||
};
|
||||
if (index < 0 || index >= slotCount) return this;
|
||||
final slot = EquipmentSlot.values[index];
|
||||
final newItem = _itemFromString(value, slot);
|
||||
return setItemByIndex(index, newItem);
|
||||
}
|
||||
|
||||
/// 인덱스로 EquipmentItem 설정
|
||||
Equipment setItemByIndex(int index, EquipmentItem item) {
|
||||
if (index < 0 || index >= slotCount) return this;
|
||||
final newItems = List<EquipmentItem>.from(items);
|
||||
newItems[index] = item;
|
||||
return Equipment(items: newItems, bestIndex: bestIndex);
|
||||
}
|
||||
|
||||
Equipment copyWith({
|
||||
String? weapon,
|
||||
String? shield,
|
||||
String? helm,
|
||||
String? hauberk,
|
||||
String? brassairts,
|
||||
String? vambraces,
|
||||
String? gauntlets,
|
||||
String? gambeson,
|
||||
String? cuisses,
|
||||
String? greaves,
|
||||
String? sollerets,
|
||||
List<EquipmentItem>? items,
|
||||
int? bestIndex,
|
||||
}) {
|
||||
return Equipment(
|
||||
weapon: weapon ?? this.weapon,
|
||||
shield: shield ?? this.shield,
|
||||
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,
|
||||
items: items ?? List<EquipmentItem>.from(this.items),
|
||||
bestIndex: bestIndex ?? this.bestIndex,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user