refactor(model): freezed 패키지 도입으로 보일러플레이트 제거

- ItemStats, CombatStats, EquipmentItem을 freezed로 마이그레이션
- copyWith, toJson/fromJson 자동 생성
- 세이브 파일 호환성 유지
This commit is contained in:
JiWoong Sul
2026-01-15 17:05:26 +09:00
parent f466e1c408
commit e77c3c4a05
11 changed files with 2337 additions and 481 deletions

View File

@@ -1,51 +1,38 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:asciineverdie/src/core/model/equipment_slot.dart';
import 'package:asciineverdie/src/core/model/item_stats.dart';
part 'equipment_item.freezed.dart';
part 'equipment_item.g.dart';
/// 장비 아이템
///
/// 개별 장비의 이름, 슬롯, 레벨, 스탯 등을 포함하는 클래스.
/// 불변(immutable) 객체로 설계됨.
class EquipmentItem {
const EquipmentItem({
required this.name,
required this.slot,
required this.level,
required this.weight,
required this.stats,
required this.rarity,
});
@freezed
class EquipmentItem with _$EquipmentItem {
const EquipmentItem._();
/// 아이템 이름 (예: "Flaming Sword of Doom")
final String name;
const factory EquipmentItem({
/// 아이템 이름 (예: "Flaming Sword of Doom")
required String name,
/// 장착 슬롯
@JsonKey(fromJson: _slotFromJson, toJson: _slotToJson)
required EquipmentSlot slot,
/// 아이템 레벨
required int level,
/// 무게 (STR 기반 휴대 제한용)
required int weight,
/// 아이템 스탯 보정치
required ItemStats stats,
/// 희귀도
@JsonKey(fromJson: _rarityFromJson, toJson: _rarityToJson)
required ItemRarity rarity,
}) = _EquipmentItem;
/// 장착 슬롯
final EquipmentSlot slot;
/// 아이템 레벨
final int level;
/// 무게 (STR 기반 휴대 제한용)
final int weight;
/// 아이템 스탯 보정치
final ItemStats stats;
/// 희귀도
final ItemRarity rarity;
/// 가중치 (자동 장착 비교용)
///
/// 가중치 = 기본값 + (레벨 * 10) + (희귀도 보너스) + (스탯 합계)
int get itemWeight {
const baseValue = 10;
return baseValue + (level * 10) + rarity.weightBonus + stats.totalStatValue;
}
/// 빈 아이템 여부
bool get isEmpty => name.isEmpty;
/// 유효한 아이템 여부
bool get isNotEmpty => name.isNotEmpty;
factory EquipmentItem.fromJson(Map<String, dynamic> json) =>
_$EquipmentItemFromJson(json);
/// 빈 아이템 생성 (특정 슬롯)
factory EquipmentItem.empty(EquipmentSlot slot) {
@@ -54,7 +41,7 @@ class EquipmentItem {
slot: slot,
level: 0,
weight: 0,
stats: ItemStats.empty,
stats: const ItemStats(),
rarity: ItemRarity.common,
);
}
@@ -71,59 +58,39 @@ class EquipmentItem {
);
}
EquipmentItem copyWith({
String? name,
EquipmentSlot? slot,
int? level,
int? weight,
ItemStats? stats,
ItemRarity? rarity,
}) {
return EquipmentItem(
name: name ?? this.name,
slot: slot ?? this.slot,
level: level ?? this.level,
weight: weight ?? this.weight,
stats: stats ?? this.stats,
rarity: rarity ?? this.rarity,
);
/// 가중치 (자동 장착 비교용)
///
/// 가중치 = 기본값 + (레벨 * 10) + (희귀도 보너스) + (스탯 합계)
int get itemWeight {
const baseValue = 10;
return baseValue + (level * 10) + rarity.weightBonus + stats.totalStatValue;
}
/// JSON으로 직렬화
Map<String, dynamic> toJson() {
return {
'name': name,
'slot': slot.name,
'level': level,
'weight': weight,
'stats': stats.toJson(),
'rarity': rarity.name,
};
}
/// 빈 아이템 여부
bool get isEmpty => name.isEmpty;
/// JSON에서 역직렬화
factory EquipmentItem.fromJson(Map<String, dynamic> json) {
final slotName = json['slot'] as String? ?? 'weapon';
final rarityName = json['rarity'] as String? ?? 'common';
return EquipmentItem(
name: json['name'] as String? ?? '',
slot: EquipmentSlot.values.firstWhere(
(s) => s.name == slotName,
orElse: () => EquipmentSlot.weapon,
),
level: json['level'] as int? ?? 0,
weight: json['weight'] as int? ?? 0,
stats: json['stats'] != null
? ItemStats.fromJson(json['stats'] as Map<String, dynamic>)
: ItemStats.empty,
rarity: ItemRarity.values.firstWhere(
(r) => r.name == rarityName,
orElse: () => ItemRarity.common,
),
);
}
/// 유효한 아이템 여부
bool get isNotEmpty => name.isNotEmpty;
@override
String toString() => name.isEmpty ? '(empty)' : name;
}
// JSON 변환 헬퍼 (세이브 파일 호환성 유지)
EquipmentSlot _slotFromJson(String? value) {
return EquipmentSlot.values.firstWhere(
(s) => s.name == value,
orElse: () => EquipmentSlot.weapon,
);
}
String _slotToJson(EquipmentSlot slot) => slot.name;
ItemRarity _rarityFromJson(String? value) {
return ItemRarity.values.firstWhere(
(r) => r.name == value,
orElse: () => ItemRarity.common,
);
}
String _rarityToJson(ItemRarity rarity) => rarity.name;