refactor(model): 모델 및 스토리지 정리
- GameState, GameStatistics, HallOfFame 개선 - MonsterGrade, HallOfFameStorage 정리
This commit is contained in:
@@ -763,6 +763,7 @@ class ProgressState {
|
|||||||
this.monstersKilled = 0,
|
this.monstersKilled = 0,
|
||||||
this.deathCount = 0,
|
this.deathCount = 0,
|
||||||
this.finalBossState = FinalBossState.notSpawned,
|
this.finalBossState = FinalBossState.notSpawned,
|
||||||
|
this.pendingActCompletion = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
final ProgressBarState task;
|
final ProgressBarState task;
|
||||||
@@ -795,6 +796,9 @@ class ProgressState {
|
|||||||
/// 최종 보스 상태 (Act V)
|
/// 최종 보스 상태 (Act V)
|
||||||
final FinalBossState finalBossState;
|
final FinalBossState finalBossState;
|
||||||
|
|
||||||
|
/// Act Boss 처치 대기 중 여부 (처치 후 시네마틱 재생 트리거)
|
||||||
|
final bool pendingActCompletion;
|
||||||
|
|
||||||
factory ProgressState.empty() => ProgressState(
|
factory ProgressState.empty() => ProgressState(
|
||||||
task: ProgressBarState.empty(),
|
task: ProgressBarState.empty(),
|
||||||
quest: ProgressBarState.empty(),
|
quest: ProgressBarState.empty(),
|
||||||
@@ -826,6 +830,7 @@ class ProgressState {
|
|||||||
int? monstersKilled,
|
int? monstersKilled,
|
||||||
int? deathCount,
|
int? deathCount,
|
||||||
FinalBossState? finalBossState,
|
FinalBossState? finalBossState,
|
||||||
|
bool? pendingActCompletion,
|
||||||
}) {
|
}) {
|
||||||
return ProgressState(
|
return ProgressState(
|
||||||
task: task ?? this.task,
|
task: task ?? this.task,
|
||||||
@@ -843,6 +848,7 @@ class ProgressState {
|
|||||||
monstersKilled: monstersKilled ?? this.monstersKilled,
|
monstersKilled: monstersKilled ?? this.monstersKilled,
|
||||||
deathCount: deathCount ?? this.deathCount,
|
deathCount: deathCount ?? this.deathCount,
|
||||||
finalBossState: finalBossState ?? this.finalBossState,
|
finalBossState: finalBossState ?? this.finalBossState,
|
||||||
|
pendingActCompletion: pendingActCompletion ?? this.pendingActCompletion,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,7 @@
|
|||||||
///
|
///
|
||||||
/// 세션 및 누적 통계를 추적하는 모델
|
/// 세션 및 누적 통계를 추적하는 모델
|
||||||
class GameStatistics {
|
class GameStatistics {
|
||||||
const GameStatistics({
|
const GameStatistics({required this.session, required this.cumulative});
|
||||||
required this.session,
|
|
||||||
required this.cumulative,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// 현재 세션 통계
|
/// 현재 세션 통계
|
||||||
final SessionStatistics session;
|
final SessionStatistics session;
|
||||||
@@ -47,10 +44,7 @@ class GameStatistics {
|
|||||||
|
|
||||||
/// JSON 직렬화
|
/// JSON 직렬화
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {'session': session.toJson(), 'cumulative': cumulative.toJson()};
|
||||||
'session': session.toJson(),
|
|
||||||
'cumulative': cumulative.toJson(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// JSON 역직렬화
|
/// JSON 역직렬화
|
||||||
@@ -212,8 +206,7 @@ class SessionStatistics {
|
|||||||
|
|
||||||
/// 스킬 사용 기록
|
/// 스킬 사용 기록
|
||||||
SessionStatistics recordSkillUse({required bool isCritical}) {
|
SessionStatistics recordSkillUse({required bool isCritical}) {
|
||||||
final newCriticalStreak =
|
final newCriticalStreak = isCritical ? currentCriticalStreak + 1 : 0;
|
||||||
isCritical ? currentCriticalStreak + 1 : 0;
|
|
||||||
final newMaxStreak = newCriticalStreak > maxCriticalStreak
|
final newMaxStreak = newCriticalStreak > maxCriticalStreak
|
||||||
? newCriticalStreak
|
? newCriticalStreak
|
||||||
: maxCriticalStreak;
|
: maxCriticalStreak;
|
||||||
@@ -227,10 +220,7 @@ class SessionStatistics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 데미지 기록
|
/// 데미지 기록
|
||||||
SessionStatistics recordDamage({
|
SessionStatistics recordDamage({int dealt = 0, int taken = 0}) {
|
||||||
int dealt = 0,
|
|
||||||
int taken = 0,
|
|
||||||
}) {
|
|
||||||
return copyWith(
|
return copyWith(
|
||||||
totalDamageDealt: totalDamageDealt + dealt,
|
totalDamageDealt: totalDamageDealt + dealt,
|
||||||
totalDamageTaken: totalDamageTaken + taken,
|
totalDamageTaken: totalDamageTaken + taken,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class HallOfFameEntry {
|
|||||||
required this.monstersKilled,
|
required this.monstersKilled,
|
||||||
required this.questsCompleted,
|
required this.questsCompleted,
|
||||||
required this.clearedAt,
|
required this.clearedAt,
|
||||||
|
this.raceId = '',
|
||||||
this.finalStats,
|
this.finalStats,
|
||||||
this.finalEquipment,
|
this.finalEquipment,
|
||||||
this.finalSkills,
|
this.finalSkills,
|
||||||
@@ -30,9 +31,12 @@ class HallOfFameEntry {
|
|||||||
/// 캐릭터 이름
|
/// 캐릭터 이름
|
||||||
final String characterName;
|
final String characterName;
|
||||||
|
|
||||||
/// 종족
|
/// 종족 (표시용 이름)
|
||||||
final String race;
|
final String race;
|
||||||
|
|
||||||
|
/// 종족 ID (ASCII 캐릭터 프레임용)
|
||||||
|
final String raceId;
|
||||||
|
|
||||||
/// 클래스
|
/// 클래스
|
||||||
final String klass;
|
final String klass;
|
||||||
|
|
||||||
@@ -87,6 +91,7 @@ class HallOfFameEntry {
|
|||||||
String? id,
|
String? id,
|
||||||
String? characterName,
|
String? characterName,
|
||||||
String? race,
|
String? race,
|
||||||
|
String? raceId,
|
||||||
String? klass,
|
String? klass,
|
||||||
int? level,
|
int? level,
|
||||||
int? totalPlayTimeMs,
|
int? totalPlayTimeMs,
|
||||||
@@ -102,6 +107,7 @@ class HallOfFameEntry {
|
|||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
characterName: characterName ?? this.characterName,
|
characterName: characterName ?? this.characterName,
|
||||||
race: race ?? this.race,
|
race: race ?? this.race,
|
||||||
|
raceId: raceId ?? this.raceId,
|
||||||
klass: klass ?? this.klass,
|
klass: klass ?? this.klass,
|
||||||
level: level ?? this.level,
|
level: level ?? this.level,
|
||||||
totalPlayTimeMs: totalPlayTimeMs ?? this.totalPlayTimeMs,
|
totalPlayTimeMs: totalPlayTimeMs ?? this.totalPlayTimeMs,
|
||||||
@@ -126,6 +132,7 @@ class HallOfFameEntry {
|
|||||||
id: DateTime.now().millisecondsSinceEpoch.toString(),
|
id: DateTime.now().millisecondsSinceEpoch.toString(),
|
||||||
characterName: state.traits.name,
|
characterName: state.traits.name,
|
||||||
race: state.traits.race,
|
race: state.traits.race,
|
||||||
|
raceId: state.traits.raceId,
|
||||||
klass: state.traits.klass,
|
klass: state.traits.klass,
|
||||||
level: state.traits.level,
|
level: state.traits.level,
|
||||||
totalPlayTimeMs: state.skillSystem.elapsedMs,
|
totalPlayTimeMs: state.skillSystem.elapsedMs,
|
||||||
@@ -147,6 +154,7 @@ class HallOfFameEntry {
|
|||||||
'id': id,
|
'id': id,
|
||||||
'characterName': characterName,
|
'characterName': characterName,
|
||||||
'race': race,
|
'race': race,
|
||||||
|
'raceId': raceId,
|
||||||
'klass': klass,
|
'klass': klass,
|
||||||
'level': level,
|
'level': level,
|
||||||
'totalPlayTimeMs': totalPlayTimeMs,
|
'totalPlayTimeMs': totalPlayTimeMs,
|
||||||
@@ -166,6 +174,7 @@ class HallOfFameEntry {
|
|||||||
id: json['id'] as String,
|
id: json['id'] as String,
|
||||||
characterName: json['characterName'] as String,
|
characterName: json['characterName'] as String,
|
||||||
race: json['race'] as String,
|
race: json['race'] as String,
|
||||||
|
raceId: json['raceId'] as String? ?? '',
|
||||||
klass: json['klass'] as String,
|
klass: json['klass'] as String,
|
||||||
level: json['level'] as int,
|
level: json['level'] as int,
|
||||||
totalPlayTimeMs: json['totalPlayTimeMs'] as int,
|
totalPlayTimeMs: json['totalPlayTimeMs'] as int,
|
||||||
@@ -178,13 +187,13 @@ class HallOfFameEntry {
|
|||||||
: null,
|
: null,
|
||||||
finalEquipment: json['finalEquipment'] != null
|
finalEquipment: json['finalEquipment'] != null
|
||||||
? (json['finalEquipment'] as List<dynamic>)
|
? (json['finalEquipment'] as List<dynamic>)
|
||||||
.map((e) => EquipmentItem.fromJson(e as Map<String, dynamic>))
|
.map((e) => EquipmentItem.fromJson(e as Map<String, dynamic>))
|
||||||
.toList()
|
.toList()
|
||||||
: null,
|
: null,
|
||||||
finalSkills: json['finalSkills'] != null
|
finalSkills: json['finalSkills'] != null
|
||||||
? (json['finalSkills'] as List<dynamic>)
|
? (json['finalSkills'] as List<dynamic>)
|
||||||
.map((s) => Map<String, String>.from(s as Map))
|
.map((s) => Map<String, String>.from(s as Map))
|
||||||
.toList()
|
.toList()
|
||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -232,6 +241,12 @@ class HallOfFame {
|
|||||||
return HallOfFame(entries: newEntries);
|
return HallOfFame(entries: newEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 엔트리 삭제 (디버그용)
|
||||||
|
HallOfFame removeEntry(String id) {
|
||||||
|
final newEntries = entries.where((e) => e.id != id).toList();
|
||||||
|
return HallOfFame(entries: newEntries);
|
||||||
|
}
|
||||||
|
|
||||||
/// JSON으로 직렬화
|
/// JSON으로 직렬화
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {'entries': entries.map((e) => e.toJson()).toList()};
|
return {'entries': entries.map((e) => e.toJson()).toList()};
|
||||||
@@ -258,7 +273,8 @@ extension HallOfFameArenaX on HallOfFame {
|
|||||||
final levelScore = entry.level * 100;
|
final levelScore = entry.level * 100;
|
||||||
|
|
||||||
// 2. 장비 점수 (전체 슬롯 합계)
|
// 2. 장비 점수 (전체 슬롯 합계)
|
||||||
final equipScore = entry.finalEquipment?.fold<int>(
|
final equipScore =
|
||||||
|
entry.finalEquipment?.fold<int>(
|
||||||
0,
|
0,
|
||||||
(sum, item) => sum + ItemService.calculateEquipmentScore(item),
|
(sum, item) => sum + ItemService.calculateEquipmentScore(item),
|
||||||
) ??
|
) ??
|
||||||
@@ -267,8 +283,8 @@ extension HallOfFameArenaX on HallOfFame {
|
|||||||
// 3. 전투력 점수 (ATK + DEF + HP/10)
|
// 3. 전투력 점수 (ATK + DEF + HP/10)
|
||||||
final combatScore = entry.finalStats != null
|
final combatScore = entry.finalStats != null
|
||||||
? (entry.finalStats!.atk +
|
? (entry.finalStats!.atk +
|
||||||
entry.finalStats!.def +
|
entry.finalStats!.def +
|
||||||
entry.finalStats!.hpMax ~/ 10)
|
entry.finalStats!.hpMax ~/ 10)
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
return levelScore + equipScore + (combatScore ~/ 10);
|
return levelScore + equipScore + (combatScore ~/ 10);
|
||||||
|
|||||||
@@ -22,39 +22,39 @@ enum MonsterGrade {
|
|||||||
extension MonsterGradeExtension on MonsterGrade {
|
extension MonsterGradeExtension on MonsterGrade {
|
||||||
/// 물약 드랍 확률 보너스 (0.0 ~ 1.0)
|
/// 물약 드랍 확률 보너스 (0.0 ~ 1.0)
|
||||||
double get potionDropBonus => switch (this) {
|
double get potionDropBonus => switch (this) {
|
||||||
MonsterGrade.normal => 0.0,
|
MonsterGrade.normal => 0.0,
|
||||||
MonsterGrade.elite => 0.05, // +5%
|
MonsterGrade.elite => 0.05, // +5%
|
||||||
MonsterGrade.boss => 0.15, // +15%
|
MonsterGrade.boss => 0.15, // +15%
|
||||||
};
|
};
|
||||||
|
|
||||||
/// UI 표시용 접두사 (몬스터 이름 앞에 붙음)
|
/// UI 표시용 접두사 (몬스터 이름 앞에 붙음)
|
||||||
String get displayPrefix => switch (this) {
|
String get displayPrefix => switch (this) {
|
||||||
MonsterGrade.normal => '',
|
MonsterGrade.normal => '',
|
||||||
MonsterGrade.elite => '★ ',
|
MonsterGrade.elite => '★ ',
|
||||||
MonsterGrade.boss => '★★★ ',
|
MonsterGrade.boss => '★★★ ',
|
||||||
};
|
};
|
||||||
|
|
||||||
/// 스탯 배율 (전투력 강화)
|
/// 스탯 배율 (전투력 강화)
|
||||||
double get statMultiplier => switch (this) {
|
double get statMultiplier => switch (this) {
|
||||||
MonsterGrade.normal => 1.0,
|
MonsterGrade.normal => 1.0,
|
||||||
MonsterGrade.elite => 1.3, // +30% 스탯
|
MonsterGrade.elite => 1.3, // +30% 스탯
|
||||||
MonsterGrade.boss => 1.8, // +80% 스탯
|
MonsterGrade.boss => 1.8, // +80% 스탯
|
||||||
};
|
};
|
||||||
|
|
||||||
/// 경험치 배율
|
/// 경험치 배율
|
||||||
double get expMultiplier => switch (this) {
|
double get expMultiplier => switch (this) {
|
||||||
MonsterGrade.normal => 1.0,
|
MonsterGrade.normal => 1.0,
|
||||||
MonsterGrade.elite => 1.5, // +50% 경험치
|
MonsterGrade.elite => 1.5, // +50% 경험치
|
||||||
MonsterGrade.boss => 2.5, // +150% 경험치
|
MonsterGrade.boss => 2.5, // +150% 경험치
|
||||||
};
|
};
|
||||||
|
|
||||||
/// UI 표시용 색상
|
/// UI 표시용 색상
|
||||||
/// - Normal: 기본 텍스트 색상 (null 반환 → 기본 스타일 사용)
|
/// - Normal: 기본 텍스트 색상 (null 반환 → 기본 스타일 사용)
|
||||||
/// - Elite: 파란색 (#7AA2F7)
|
/// - Elite: 파란색 (#7AA2F7)
|
||||||
/// - Boss: 금색 (#E0AF68)
|
/// - Boss: 금색 (#E0AF68)
|
||||||
Color? get displayColor => switch (this) {
|
Color? get displayColor => switch (this) {
|
||||||
MonsterGrade.normal => null,
|
MonsterGrade.normal => null,
|
||||||
MonsterGrade.elite => const Color(0xFF7AA2F7), // MP 파랑
|
MonsterGrade.elite => const Color(0xFF7AA2F7), // MP 파랑
|
||||||
MonsterGrade.boss => const Color(0xFFE0AF68), // 골드
|
MonsterGrade.boss => const Color(0xFFE0AF68), // 골드
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,13 @@ class HallOfFameStorage {
|
|||||||
return save(updated);
|
return save(updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 엔트리 삭제 및 저장 (디버그용)
|
||||||
|
Future<bool> deleteEntry(String id) async {
|
||||||
|
final hallOfFame = await load();
|
||||||
|
final updated = hallOfFame.removeEntry(id);
|
||||||
|
return save(updated);
|
||||||
|
}
|
||||||
|
|
||||||
/// 명예의 전당 초기화 (테스트용)
|
/// 명예의 전당 초기화 (테스트용)
|
||||||
Future<bool> clear() async {
|
Future<bool> clear() async {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user