From 6f70c18d080a7ac495f5726851350cd2c19787d9 Mon Sep 17 00:00:00 2001 From: JiWoong Sul Date: Mon, 12 Jan 2026 16:17:06 +0900 Subject: [PATCH] =?UTF-8?q?refactor(model):=20=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?=EB=B0=8F=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EC=A7=80=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GameState, GameStatistics, HallOfFame 개선 - MonsterGrade, HallOfFameStorage 정리 --- lib/src/core/model/game_state.dart | 6 +++ lib/src/core/model/game_statistics.dart | 18 ++------- lib/src/core/model/hall_of_fame.dart | 32 +++++++++++---- lib/src/core/model/monster_grade.dart | 40 +++++++++---------- .../core/storage/hall_of_fame_storage.dart | 7 ++++ 5 files changed, 61 insertions(+), 42 deletions(-) diff --git a/lib/src/core/model/game_state.dart b/lib/src/core/model/game_state.dart index 38f6759..64577fb 100644 --- a/lib/src/core/model/game_state.dart +++ b/lib/src/core/model/game_state.dart @@ -763,6 +763,7 @@ class ProgressState { this.monstersKilled = 0, this.deathCount = 0, this.finalBossState = FinalBossState.notSpawned, + this.pendingActCompletion = false, }); final ProgressBarState task; @@ -795,6 +796,9 @@ class ProgressState { /// 최종 보스 상태 (Act V) final FinalBossState finalBossState; + /// Act Boss 처치 대기 중 여부 (처치 후 시네마틱 재생 트리거) + final bool pendingActCompletion; + factory ProgressState.empty() => ProgressState( task: ProgressBarState.empty(), quest: ProgressBarState.empty(), @@ -826,6 +830,7 @@ class ProgressState { int? monstersKilled, int? deathCount, FinalBossState? finalBossState, + bool? pendingActCompletion, }) { return ProgressState( task: task ?? this.task, @@ -843,6 +848,7 @@ class ProgressState { monstersKilled: monstersKilled ?? this.monstersKilled, deathCount: deathCount ?? this.deathCount, finalBossState: finalBossState ?? this.finalBossState, + pendingActCompletion: pendingActCompletion ?? this.pendingActCompletion, ); } } diff --git a/lib/src/core/model/game_statistics.dart b/lib/src/core/model/game_statistics.dart index 5990d9c..e91fd24 100644 --- a/lib/src/core/model/game_statistics.dart +++ b/lib/src/core/model/game_statistics.dart @@ -2,10 +2,7 @@ /// /// 세션 및 누적 통계를 추적하는 모델 class GameStatistics { - const GameStatistics({ - required this.session, - required this.cumulative, - }); + const GameStatistics({required this.session, required this.cumulative}); /// 현재 세션 통계 final SessionStatistics session; @@ -47,10 +44,7 @@ class GameStatistics { /// JSON 직렬화 Map toJson() { - return { - 'session': session.toJson(), - 'cumulative': cumulative.toJson(), - }; + return {'session': session.toJson(), 'cumulative': cumulative.toJson()}; } /// JSON 역직렬화 @@ -212,8 +206,7 @@ class SessionStatistics { /// 스킬 사용 기록 SessionStatistics recordSkillUse({required bool isCritical}) { - final newCriticalStreak = - isCritical ? currentCriticalStreak + 1 : 0; + final newCriticalStreak = isCritical ? currentCriticalStreak + 1 : 0; final newMaxStreak = newCriticalStreak > maxCriticalStreak ? newCriticalStreak : maxCriticalStreak; @@ -227,10 +220,7 @@ class SessionStatistics { } /// 데미지 기록 - SessionStatistics recordDamage({ - int dealt = 0, - int taken = 0, - }) { + SessionStatistics recordDamage({int dealt = 0, int taken = 0}) { return copyWith( totalDamageDealt: totalDamageDealt + dealt, totalDamageTaken: totalDamageTaken + taken, diff --git a/lib/src/core/model/hall_of_fame.dart b/lib/src/core/model/hall_of_fame.dart index 4403a07..53e1f22 100644 --- a/lib/src/core/model/hall_of_fame.dart +++ b/lib/src/core/model/hall_of_fame.dart @@ -19,6 +19,7 @@ class HallOfFameEntry { required this.monstersKilled, required this.questsCompleted, required this.clearedAt, + this.raceId = '', this.finalStats, this.finalEquipment, this.finalSkills, @@ -30,9 +31,12 @@ class HallOfFameEntry { /// 캐릭터 이름 final String characterName; - /// 종족 + /// 종족 (표시용 이름) final String race; + /// 종족 ID (ASCII 캐릭터 프레임용) + final String raceId; + /// 클래스 final String klass; @@ -87,6 +91,7 @@ class HallOfFameEntry { String? id, String? characterName, String? race, + String? raceId, String? klass, int? level, int? totalPlayTimeMs, @@ -102,6 +107,7 @@ class HallOfFameEntry { id: id ?? this.id, characterName: characterName ?? this.characterName, race: race ?? this.race, + raceId: raceId ?? this.raceId, klass: klass ?? this.klass, level: level ?? this.level, totalPlayTimeMs: totalPlayTimeMs ?? this.totalPlayTimeMs, @@ -126,6 +132,7 @@ class HallOfFameEntry { id: DateTime.now().millisecondsSinceEpoch.toString(), characterName: state.traits.name, race: state.traits.race, + raceId: state.traits.raceId, klass: state.traits.klass, level: state.traits.level, totalPlayTimeMs: state.skillSystem.elapsedMs, @@ -147,6 +154,7 @@ class HallOfFameEntry { 'id': id, 'characterName': characterName, 'race': race, + 'raceId': raceId, 'klass': klass, 'level': level, 'totalPlayTimeMs': totalPlayTimeMs, @@ -166,6 +174,7 @@ class HallOfFameEntry { id: json['id'] as String, characterName: json['characterName'] as String, race: json['race'] as String, + raceId: json['raceId'] as String? ?? '', klass: json['klass'] as String, level: json['level'] as int, totalPlayTimeMs: json['totalPlayTimeMs'] as int, @@ -178,13 +187,13 @@ class HallOfFameEntry { : null, finalEquipment: json['finalEquipment'] != null ? (json['finalEquipment'] as List) - .map((e) => EquipmentItem.fromJson(e as Map)) - .toList() + .map((e) => EquipmentItem.fromJson(e as Map)) + .toList() : null, finalSkills: json['finalSkills'] != null ? (json['finalSkills'] as List) - .map((s) => Map.from(s as Map)) - .toList() + .map((s) => Map.from(s as Map)) + .toList() : null, ); } @@ -232,6 +241,12 @@ class HallOfFame { return HallOfFame(entries: newEntries); } + /// 엔트리 삭제 (디버그용) + HallOfFame removeEntry(String id) { + final newEntries = entries.where((e) => e.id != id).toList(); + return HallOfFame(entries: newEntries); + } + /// JSON으로 직렬화 Map toJson() { return {'entries': entries.map((e) => e.toJson()).toList()}; @@ -258,7 +273,8 @@ extension HallOfFameArenaX on HallOfFame { final levelScore = entry.level * 100; // 2. 장비 점수 (전체 슬롯 합계) - final equipScore = entry.finalEquipment?.fold( + final equipScore = + entry.finalEquipment?.fold( 0, (sum, item) => sum + ItemService.calculateEquipmentScore(item), ) ?? @@ -267,8 +283,8 @@ extension HallOfFameArenaX on HallOfFame { // 3. 전투력 점수 (ATK + DEF + HP/10) final combatScore = entry.finalStats != null ? (entry.finalStats!.atk + - entry.finalStats!.def + - entry.finalStats!.hpMax ~/ 10) + entry.finalStats!.def + + entry.finalStats!.hpMax ~/ 10) : 0; return levelScore + equipScore + (combatScore ~/ 10); diff --git a/lib/src/core/model/monster_grade.dart b/lib/src/core/model/monster_grade.dart index 5ae9298..c7110a9 100644 --- a/lib/src/core/model/monster_grade.dart +++ b/lib/src/core/model/monster_grade.dart @@ -22,39 +22,39 @@ enum MonsterGrade { extension MonsterGradeExtension on MonsterGrade { /// 물약 드랍 확률 보너스 (0.0 ~ 1.0) double get potionDropBonus => switch (this) { - MonsterGrade.normal => 0.0, - MonsterGrade.elite => 0.05, // +5% - MonsterGrade.boss => 0.15, // +15% - }; + MonsterGrade.normal => 0.0, + MonsterGrade.elite => 0.05, // +5% + MonsterGrade.boss => 0.15, // +15% + }; /// UI 표시용 접두사 (몬스터 이름 앞에 붙음) String get displayPrefix => switch (this) { - MonsterGrade.normal => '', - MonsterGrade.elite => '★ ', - MonsterGrade.boss => '★★★ ', - }; + MonsterGrade.normal => '', + MonsterGrade.elite => '★ ', + MonsterGrade.boss => '★★★ ', + }; /// 스탯 배율 (전투력 강화) double get statMultiplier => switch (this) { - MonsterGrade.normal => 1.0, - MonsterGrade.elite => 1.3, // +30% 스탯 - MonsterGrade.boss => 1.8, // +80% 스탯 - }; + MonsterGrade.normal => 1.0, + MonsterGrade.elite => 1.3, // +30% 스탯 + MonsterGrade.boss => 1.8, // +80% 스탯 + }; /// 경험치 배율 double get expMultiplier => switch (this) { - MonsterGrade.normal => 1.0, - MonsterGrade.elite => 1.5, // +50% 경험치 - MonsterGrade.boss => 2.5, // +150% 경험치 - }; + MonsterGrade.normal => 1.0, + MonsterGrade.elite => 1.5, // +50% 경험치 + MonsterGrade.boss => 2.5, // +150% 경험치 + }; /// UI 표시용 색상 /// - Normal: 기본 텍스트 색상 (null 반환 → 기본 스타일 사용) /// - Elite: 파란색 (#7AA2F7) /// - Boss: 금색 (#E0AF68) Color? get displayColor => switch (this) { - MonsterGrade.normal => null, - MonsterGrade.elite => const Color(0xFF7AA2F7), // MP 파랑 - MonsterGrade.boss => const Color(0xFFE0AF68), // 골드 - }; + MonsterGrade.normal => null, + MonsterGrade.elite => const Color(0xFF7AA2F7), // MP 파랑 + MonsterGrade.boss => const Color(0xFFE0AF68), // 골드 + }; } diff --git a/lib/src/core/storage/hall_of_fame_storage.dart b/lib/src/core/storage/hall_of_fame_storage.dart index af266cc..2c3b27b 100644 --- a/lib/src/core/storage/hall_of_fame_storage.dart +++ b/lib/src/core/storage/hall_of_fame_storage.dart @@ -64,6 +64,13 @@ class HallOfFameStorage { return save(updated); } + /// 엔트리 삭제 및 저장 (디버그용) + Future deleteEntry(String id) async { + final hallOfFame = await load(); + final updated = hallOfFame.removeEntry(id); + return save(updated); + } + /// 명예의 전당 초기화 (테스트용) Future clear() async { try {