refactor(model): 통계 모델 분리
- game_statistics에서 cumulative_statistics, session_statistics 분리 - task_info import 경로 업데이트
This commit is contained in:
276
lib/src/core/model/cumulative_statistics.dart
Normal file
276
lib/src/core/model/cumulative_statistics.dart
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
import 'package:asciineverdie/src/core/model/session_statistics.dart';
|
||||||
|
|
||||||
|
/// 누적 통계 (Cumulative Statistics)
|
||||||
|
///
|
||||||
|
/// GameStatistics에서 분리된 모든 게임 세션의 누적 통계 모델.
|
||||||
|
class CumulativeStatistics {
|
||||||
|
const CumulativeStatistics({
|
||||||
|
required this.totalPlayTimeMs,
|
||||||
|
required this.totalMonstersKilled,
|
||||||
|
required this.totalGoldEarned,
|
||||||
|
required this.totalGoldSpent,
|
||||||
|
required this.totalSkillsUsed,
|
||||||
|
required this.totalCriticalHits,
|
||||||
|
required this.bestCriticalStreak,
|
||||||
|
required this.totalDamageDealt,
|
||||||
|
required this.totalDamageTaken,
|
||||||
|
required this.totalPotionsUsed,
|
||||||
|
required this.totalItemsSold,
|
||||||
|
required this.totalQuestsCompleted,
|
||||||
|
required this.totalDeaths,
|
||||||
|
required this.totalBossesDefeated,
|
||||||
|
required this.totalLevelUps,
|
||||||
|
required this.highestLevel,
|
||||||
|
required this.highestGoldHeld,
|
||||||
|
required this.gamesCompleted,
|
||||||
|
required this.gamesStarted,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// 총 플레이 시간 (밀리초)
|
||||||
|
final int totalPlayTimeMs;
|
||||||
|
|
||||||
|
/// 총 처치한 몬스터 수
|
||||||
|
final int totalMonstersKilled;
|
||||||
|
|
||||||
|
/// 총 획득한 골드
|
||||||
|
final int totalGoldEarned;
|
||||||
|
|
||||||
|
/// 총 소비한 골드
|
||||||
|
final int totalGoldSpent;
|
||||||
|
|
||||||
|
/// 총 스킬 사용 횟수
|
||||||
|
final int totalSkillsUsed;
|
||||||
|
|
||||||
|
/// 총 크리티컬 히트 횟수
|
||||||
|
final int totalCriticalHits;
|
||||||
|
|
||||||
|
/// 최고 연속 크리티컬
|
||||||
|
final int bestCriticalStreak;
|
||||||
|
|
||||||
|
/// 총 입힌 데미지
|
||||||
|
final int totalDamageDealt;
|
||||||
|
|
||||||
|
/// 총 받은 데미지
|
||||||
|
final int totalDamageTaken;
|
||||||
|
|
||||||
|
/// 총 사용한 물약 수
|
||||||
|
final int totalPotionsUsed;
|
||||||
|
|
||||||
|
/// 총 판매한 아이템 수
|
||||||
|
final int totalItemsSold;
|
||||||
|
|
||||||
|
/// 총 완료한 퀘스트 수
|
||||||
|
final int totalQuestsCompleted;
|
||||||
|
|
||||||
|
/// 총 사망 횟수
|
||||||
|
final int totalDeaths;
|
||||||
|
|
||||||
|
/// 총 처치한 보스 수
|
||||||
|
final int totalBossesDefeated;
|
||||||
|
|
||||||
|
/// 총 레벨업 횟수
|
||||||
|
final int totalLevelUps;
|
||||||
|
|
||||||
|
/// 최고 달성 레벨
|
||||||
|
final int highestLevel;
|
||||||
|
|
||||||
|
/// 최대 보유 골드
|
||||||
|
final int highestGoldHeld;
|
||||||
|
|
||||||
|
/// 클리어한 게임 수
|
||||||
|
final int gamesCompleted;
|
||||||
|
|
||||||
|
/// 시작한 게임 수
|
||||||
|
final int gamesStarted;
|
||||||
|
|
||||||
|
/// 총 플레이 시간 Duration
|
||||||
|
Duration get totalPlayTime => Duration(milliseconds: totalPlayTimeMs);
|
||||||
|
|
||||||
|
/// 총 플레이 시간 포맷 (HH:MM:SS)
|
||||||
|
String get formattedTotalPlayTime {
|
||||||
|
final hours = totalPlayTime.inHours;
|
||||||
|
final minutes = totalPlayTime.inMinutes % 60;
|
||||||
|
final seconds = totalPlayTime.inSeconds % 60;
|
||||||
|
return '${hours.toString().padLeft(2, '0')}:'
|
||||||
|
'${minutes.toString().padLeft(2, '0')}:'
|
||||||
|
'${seconds.toString().padLeft(2, '0')}';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 평균 게임당 플레이 시간
|
||||||
|
Duration get averagePlayTimePerGame {
|
||||||
|
if (gamesStarted <= 0) return Duration.zero;
|
||||||
|
return Duration(milliseconds: totalPlayTimeMs ~/ gamesStarted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 게임 완료율
|
||||||
|
double get completionRate {
|
||||||
|
if (gamesStarted <= 0) return 0;
|
||||||
|
return gamesCompleted / gamesStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 빈 누적 통계
|
||||||
|
factory CumulativeStatistics.empty() => const CumulativeStatistics(
|
||||||
|
totalPlayTimeMs: 0,
|
||||||
|
totalMonstersKilled: 0,
|
||||||
|
totalGoldEarned: 0,
|
||||||
|
totalGoldSpent: 0,
|
||||||
|
totalSkillsUsed: 0,
|
||||||
|
totalCriticalHits: 0,
|
||||||
|
bestCriticalStreak: 0,
|
||||||
|
totalDamageDealt: 0,
|
||||||
|
totalDamageTaken: 0,
|
||||||
|
totalPotionsUsed: 0,
|
||||||
|
totalItemsSold: 0,
|
||||||
|
totalQuestsCompleted: 0,
|
||||||
|
totalDeaths: 0,
|
||||||
|
totalBossesDefeated: 0,
|
||||||
|
totalLevelUps: 0,
|
||||||
|
highestLevel: 0,
|
||||||
|
highestGoldHeld: 0,
|
||||||
|
gamesCompleted: 0,
|
||||||
|
gamesStarted: 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// 세션 통계 병합
|
||||||
|
CumulativeStatistics mergeSession(SessionStatistics session) {
|
||||||
|
return CumulativeStatistics(
|
||||||
|
totalPlayTimeMs: totalPlayTimeMs + session.playTimeMs,
|
||||||
|
totalMonstersKilled: totalMonstersKilled + session.monstersKilled,
|
||||||
|
totalGoldEarned: totalGoldEarned + session.goldEarned,
|
||||||
|
totalGoldSpent: totalGoldSpent + session.goldSpent,
|
||||||
|
totalSkillsUsed: totalSkillsUsed + session.skillsUsed,
|
||||||
|
totalCriticalHits: totalCriticalHits + session.criticalHits,
|
||||||
|
bestCriticalStreak: session.maxCriticalStreak > bestCriticalStreak
|
||||||
|
? session.maxCriticalStreak
|
||||||
|
: bestCriticalStreak,
|
||||||
|
totalDamageDealt: totalDamageDealt + session.totalDamageDealt,
|
||||||
|
totalDamageTaken: totalDamageTaken + session.totalDamageTaken,
|
||||||
|
totalPotionsUsed: totalPotionsUsed + session.potionsUsed,
|
||||||
|
totalItemsSold: totalItemsSold + session.itemsSold,
|
||||||
|
totalQuestsCompleted: totalQuestsCompleted + session.questsCompleted,
|
||||||
|
totalDeaths: totalDeaths + session.deathCount,
|
||||||
|
totalBossesDefeated: totalBossesDefeated + session.bossesDefeated,
|
||||||
|
totalLevelUps: totalLevelUps + session.levelUps,
|
||||||
|
highestLevel: highestLevel,
|
||||||
|
highestGoldHeld: highestGoldHeld,
|
||||||
|
gamesCompleted: gamesCompleted,
|
||||||
|
gamesStarted: gamesStarted,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 최고 레벨 업데이트
|
||||||
|
CumulativeStatistics updateHighestLevel(int level) {
|
||||||
|
if (level <= highestLevel) return this;
|
||||||
|
return copyWith(highestLevel: level);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 최대 골드 업데이트
|
||||||
|
CumulativeStatistics updateHighestGold(int gold) {
|
||||||
|
if (gold <= highestGoldHeld) return this;
|
||||||
|
return copyWith(highestGoldHeld: gold);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 새 게임 시작 기록
|
||||||
|
CumulativeStatistics recordGameStart() {
|
||||||
|
return copyWith(gamesStarted: gamesStarted + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 게임 클리어 기록
|
||||||
|
CumulativeStatistics recordGameComplete() {
|
||||||
|
return copyWith(gamesCompleted: gamesCompleted + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
CumulativeStatistics copyWith({
|
||||||
|
int? totalPlayTimeMs,
|
||||||
|
int? totalMonstersKilled,
|
||||||
|
int? totalGoldEarned,
|
||||||
|
int? totalGoldSpent,
|
||||||
|
int? totalSkillsUsed,
|
||||||
|
int? totalCriticalHits,
|
||||||
|
int? bestCriticalStreak,
|
||||||
|
int? totalDamageDealt,
|
||||||
|
int? totalDamageTaken,
|
||||||
|
int? totalPotionsUsed,
|
||||||
|
int? totalItemsSold,
|
||||||
|
int? totalQuestsCompleted,
|
||||||
|
int? totalDeaths,
|
||||||
|
int? totalBossesDefeated,
|
||||||
|
int? totalLevelUps,
|
||||||
|
int? highestLevel,
|
||||||
|
int? highestGoldHeld,
|
||||||
|
int? gamesCompleted,
|
||||||
|
int? gamesStarted,
|
||||||
|
}) {
|
||||||
|
return CumulativeStatistics(
|
||||||
|
totalPlayTimeMs: totalPlayTimeMs ?? this.totalPlayTimeMs,
|
||||||
|
totalMonstersKilled: totalMonstersKilled ?? this.totalMonstersKilled,
|
||||||
|
totalGoldEarned: totalGoldEarned ?? this.totalGoldEarned,
|
||||||
|
totalGoldSpent: totalGoldSpent ?? this.totalGoldSpent,
|
||||||
|
totalSkillsUsed: totalSkillsUsed ?? this.totalSkillsUsed,
|
||||||
|
totalCriticalHits: totalCriticalHits ?? this.totalCriticalHits,
|
||||||
|
bestCriticalStreak: bestCriticalStreak ?? this.bestCriticalStreak,
|
||||||
|
totalDamageDealt: totalDamageDealt ?? this.totalDamageDealt,
|
||||||
|
totalDamageTaken: totalDamageTaken ?? this.totalDamageTaken,
|
||||||
|
totalPotionsUsed: totalPotionsUsed ?? this.totalPotionsUsed,
|
||||||
|
totalItemsSold: totalItemsSold ?? this.totalItemsSold,
|
||||||
|
totalQuestsCompleted: totalQuestsCompleted ?? this.totalQuestsCompleted,
|
||||||
|
totalDeaths: totalDeaths ?? this.totalDeaths,
|
||||||
|
totalBossesDefeated: totalBossesDefeated ?? this.totalBossesDefeated,
|
||||||
|
totalLevelUps: totalLevelUps ?? this.totalLevelUps,
|
||||||
|
highestLevel: highestLevel ?? this.highestLevel,
|
||||||
|
highestGoldHeld: highestGoldHeld ?? this.highestGoldHeld,
|
||||||
|
gamesCompleted: gamesCompleted ?? this.gamesCompleted,
|
||||||
|
gamesStarted: gamesStarted ?? this.gamesStarted,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// JSON 직렬화
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'totalPlayTimeMs': totalPlayTimeMs,
|
||||||
|
'totalMonstersKilled': totalMonstersKilled,
|
||||||
|
'totalGoldEarned': totalGoldEarned,
|
||||||
|
'totalGoldSpent': totalGoldSpent,
|
||||||
|
'totalSkillsUsed': totalSkillsUsed,
|
||||||
|
'totalCriticalHits': totalCriticalHits,
|
||||||
|
'bestCriticalStreak': bestCriticalStreak,
|
||||||
|
'totalDamageDealt': totalDamageDealt,
|
||||||
|
'totalDamageTaken': totalDamageTaken,
|
||||||
|
'totalPotionsUsed': totalPotionsUsed,
|
||||||
|
'totalItemsSold': totalItemsSold,
|
||||||
|
'totalQuestsCompleted': totalQuestsCompleted,
|
||||||
|
'totalDeaths': totalDeaths,
|
||||||
|
'totalBossesDefeated': totalBossesDefeated,
|
||||||
|
'totalLevelUps': totalLevelUps,
|
||||||
|
'highestLevel': highestLevel,
|
||||||
|
'highestGoldHeld': highestGoldHeld,
|
||||||
|
'gamesCompleted': gamesCompleted,
|
||||||
|
'gamesStarted': gamesStarted,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// JSON 역직렬화
|
||||||
|
factory CumulativeStatistics.fromJson(Map<String, dynamic> json) {
|
||||||
|
return CumulativeStatistics(
|
||||||
|
totalPlayTimeMs: json['totalPlayTimeMs'] as int? ?? 0,
|
||||||
|
totalMonstersKilled: json['totalMonstersKilled'] as int? ?? 0,
|
||||||
|
totalGoldEarned: json['totalGoldEarned'] as int? ?? 0,
|
||||||
|
totalGoldSpent: json['totalGoldSpent'] as int? ?? 0,
|
||||||
|
totalSkillsUsed: json['totalSkillsUsed'] as int? ?? 0,
|
||||||
|
totalCriticalHits: json['totalCriticalHits'] as int? ?? 0,
|
||||||
|
bestCriticalStreak: json['bestCriticalStreak'] as int? ?? 0,
|
||||||
|
totalDamageDealt: json['totalDamageDealt'] as int? ?? 0,
|
||||||
|
totalDamageTaken: json['totalDamageTaken'] as int? ?? 0,
|
||||||
|
totalPotionsUsed: json['totalPotionsUsed'] as int? ?? 0,
|
||||||
|
totalItemsSold: json['totalItemsSold'] as int? ?? 0,
|
||||||
|
totalQuestsCompleted: json['totalQuestsCompleted'] as int? ?? 0,
|
||||||
|
totalDeaths: json['totalDeaths'] as int? ?? 0,
|
||||||
|
totalBossesDefeated: json['totalBossesDefeated'] as int? ?? 0,
|
||||||
|
totalLevelUps: json['totalLevelUps'] as int? ?? 0,
|
||||||
|
highestLevel: json['highestLevel'] as int? ?? 0,
|
||||||
|
highestGoldHeld: json['highestGoldHeld'] as int? ?? 0,
|
||||||
|
gamesCompleted: json['gamesCompleted'] as int? ?? 0,
|
||||||
|
gamesStarted: json['gamesStarted'] as int? ?? 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
|
import 'package:asciineverdie/src/core/model/cumulative_statistics.dart';
|
||||||
|
import 'package:asciineverdie/src/core/model/session_statistics.dart';
|
||||||
|
|
||||||
|
// 하위 호환성(backward compatibility)을 위한 re-export
|
||||||
|
export 'package:asciineverdie/src/core/model/cumulative_statistics.dart';
|
||||||
|
export 'package:asciineverdie/src/core/model/session_statistics.dart';
|
||||||
|
|
||||||
/// 게임 통계 (Game Statistics)
|
/// 게임 통계 (Game Statistics)
|
||||||
///
|
///
|
||||||
/// 세션 및 누적 통계를 추적하는 모델
|
/// 세션 및 누적 통계를 추적하는 모델.
|
||||||
|
/// 세부 구현은 SessionStatistics와 CumulativeStatistics로 분리됨.
|
||||||
class GameStatistics {
|
class GameStatistics {
|
||||||
const GameStatistics({required this.session, required this.cumulative});
|
const GameStatistics({required this.session, required this.cumulative});
|
||||||
|
|
||||||
@@ -59,558 +67,3 @@ class GameStatistics {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 세션 통계 (Session Statistics)
|
|
||||||
///
|
|
||||||
/// 현재 게임 세션의 통계
|
|
||||||
class SessionStatistics {
|
|
||||||
const SessionStatistics({
|
|
||||||
required this.playTimeMs,
|
|
||||||
required this.monstersKilled,
|
|
||||||
required this.goldEarned,
|
|
||||||
required this.goldSpent,
|
|
||||||
required this.skillsUsed,
|
|
||||||
required this.criticalHits,
|
|
||||||
required this.maxCriticalStreak,
|
|
||||||
required this.currentCriticalStreak,
|
|
||||||
required this.totalDamageDealt,
|
|
||||||
required this.totalDamageTaken,
|
|
||||||
required this.potionsUsed,
|
|
||||||
required this.itemsSold,
|
|
||||||
required this.questsCompleted,
|
|
||||||
required this.deathCount,
|
|
||||||
required this.bossesDefeated,
|
|
||||||
required this.levelUps,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// 플레이 시간 (밀리초)
|
|
||||||
final int playTimeMs;
|
|
||||||
|
|
||||||
/// 처치한 몬스터 수
|
|
||||||
final int monstersKilled;
|
|
||||||
|
|
||||||
/// 획득한 골드 총량
|
|
||||||
final int goldEarned;
|
|
||||||
|
|
||||||
/// 소비한 골드 총량
|
|
||||||
final int goldSpent;
|
|
||||||
|
|
||||||
/// 사용한 스킬 횟수
|
|
||||||
final int skillsUsed;
|
|
||||||
|
|
||||||
/// 크리티컬 히트 횟수
|
|
||||||
final int criticalHits;
|
|
||||||
|
|
||||||
/// 최대 연속 크리티컬
|
|
||||||
final int maxCriticalStreak;
|
|
||||||
|
|
||||||
/// 현재 연속 크리티컬 (내부 추적용)
|
|
||||||
final int currentCriticalStreak;
|
|
||||||
|
|
||||||
/// 총 입힌 데미지
|
|
||||||
final int totalDamageDealt;
|
|
||||||
|
|
||||||
/// 총 받은 데미지
|
|
||||||
final int totalDamageTaken;
|
|
||||||
|
|
||||||
/// 사용한 물약 수
|
|
||||||
final int potionsUsed;
|
|
||||||
|
|
||||||
/// 판매한 아이템 수
|
|
||||||
final int itemsSold;
|
|
||||||
|
|
||||||
/// 완료한 퀘스트 수
|
|
||||||
final int questsCompleted;
|
|
||||||
|
|
||||||
/// 사망 횟수
|
|
||||||
final int deathCount;
|
|
||||||
|
|
||||||
/// 처치한 보스 수
|
|
||||||
final int bossesDefeated;
|
|
||||||
|
|
||||||
/// 레벨업 횟수
|
|
||||||
final int levelUps;
|
|
||||||
|
|
||||||
/// 플레이 시간 Duration
|
|
||||||
Duration get playTime => Duration(milliseconds: playTimeMs);
|
|
||||||
|
|
||||||
/// 플레이 시간 포맷 (HH:MM:SS)
|
|
||||||
String get formattedPlayTime {
|
|
||||||
final hours = playTime.inHours;
|
|
||||||
final minutes = playTime.inMinutes % 60;
|
|
||||||
final seconds = playTime.inSeconds % 60;
|
|
||||||
return '${hours.toString().padLeft(2, '0')}:'
|
|
||||||
'${minutes.toString().padLeft(2, '0')}:'
|
|
||||||
'${seconds.toString().padLeft(2, '0')}';
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 평균 DPS (damage per second)
|
|
||||||
double get averageDps {
|
|
||||||
if (playTimeMs <= 0) return 0;
|
|
||||||
return totalDamageDealt / (playTimeMs / 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 킬당 평균 골드
|
|
||||||
double get goldPerKill {
|
|
||||||
if (monstersKilled <= 0) return 0;
|
|
||||||
return goldEarned / monstersKilled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 크리티컬 비율
|
|
||||||
double get criticalRate {
|
|
||||||
if (skillsUsed <= 0) return 0;
|
|
||||||
return criticalHits / skillsUsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 빈 세션 통계
|
|
||||||
factory SessionStatistics.empty() => const SessionStatistics(
|
|
||||||
playTimeMs: 0,
|
|
||||||
monstersKilled: 0,
|
|
||||||
goldEarned: 0,
|
|
||||||
goldSpent: 0,
|
|
||||||
skillsUsed: 0,
|
|
||||||
criticalHits: 0,
|
|
||||||
maxCriticalStreak: 0,
|
|
||||||
currentCriticalStreak: 0,
|
|
||||||
totalDamageDealt: 0,
|
|
||||||
totalDamageTaken: 0,
|
|
||||||
potionsUsed: 0,
|
|
||||||
itemsSold: 0,
|
|
||||||
questsCompleted: 0,
|
|
||||||
deathCount: 0,
|
|
||||||
bossesDefeated: 0,
|
|
||||||
levelUps: 0,
|
|
||||||
);
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 이벤트 기록 메서드
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
/// 몬스터 처치 기록
|
|
||||||
SessionStatistics recordKill({bool isBoss = false}) {
|
|
||||||
return copyWith(
|
|
||||||
monstersKilled: monstersKilled + 1,
|
|
||||||
bossesDefeated: isBoss ? bossesDefeated + 1 : bossesDefeated,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 골드 획득 기록
|
|
||||||
SessionStatistics recordGoldEarned(int amount) {
|
|
||||||
return copyWith(goldEarned: goldEarned + amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 골드 소비 기록
|
|
||||||
SessionStatistics recordGoldSpent(int amount) {
|
|
||||||
return copyWith(goldSpent: goldSpent + amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 스킬 사용 기록
|
|
||||||
SessionStatistics recordSkillUse({required bool isCritical}) {
|
|
||||||
final newCriticalStreak = isCritical ? currentCriticalStreak + 1 : 0;
|
|
||||||
final newMaxStreak = newCriticalStreak > maxCriticalStreak
|
|
||||||
? newCriticalStreak
|
|
||||||
: maxCriticalStreak;
|
|
||||||
|
|
||||||
return copyWith(
|
|
||||||
skillsUsed: skillsUsed + 1,
|
|
||||||
criticalHits: isCritical ? criticalHits + 1 : criticalHits,
|
|
||||||
currentCriticalStreak: newCriticalStreak,
|
|
||||||
maxCriticalStreak: newMaxStreak,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 데미지 기록
|
|
||||||
SessionStatistics recordDamage({int dealt = 0, int taken = 0}) {
|
|
||||||
return copyWith(
|
|
||||||
totalDamageDealt: totalDamageDealt + dealt,
|
|
||||||
totalDamageTaken: totalDamageTaken + taken,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 물약 사용 기록
|
|
||||||
SessionStatistics recordPotionUse() {
|
|
||||||
return copyWith(potionsUsed: potionsUsed + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 아이템 판매 기록
|
|
||||||
SessionStatistics recordItemSold(int count) {
|
|
||||||
return copyWith(itemsSold: itemsSold + count);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 퀘스트 완료 기록
|
|
||||||
SessionStatistics recordQuestComplete() {
|
|
||||||
return copyWith(questsCompleted: questsCompleted + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 사망 기록
|
|
||||||
SessionStatistics recordDeath() {
|
|
||||||
return copyWith(deathCount: deathCount + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 레벨업 기록
|
|
||||||
SessionStatistics recordLevelUp() {
|
|
||||||
return copyWith(levelUps: levelUps + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 플레이 시간 업데이트
|
|
||||||
SessionStatistics updatePlayTime(int elapsedMs) {
|
|
||||||
return copyWith(playTimeMs: elapsedMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
SessionStatistics copyWith({
|
|
||||||
int? playTimeMs,
|
|
||||||
int? monstersKilled,
|
|
||||||
int? goldEarned,
|
|
||||||
int? goldSpent,
|
|
||||||
int? skillsUsed,
|
|
||||||
int? criticalHits,
|
|
||||||
int? maxCriticalStreak,
|
|
||||||
int? currentCriticalStreak,
|
|
||||||
int? totalDamageDealt,
|
|
||||||
int? totalDamageTaken,
|
|
||||||
int? potionsUsed,
|
|
||||||
int? itemsSold,
|
|
||||||
int? questsCompleted,
|
|
||||||
int? deathCount,
|
|
||||||
int? bossesDefeated,
|
|
||||||
int? levelUps,
|
|
||||||
}) {
|
|
||||||
return SessionStatistics(
|
|
||||||
playTimeMs: playTimeMs ?? this.playTimeMs,
|
|
||||||
monstersKilled: monstersKilled ?? this.monstersKilled,
|
|
||||||
goldEarned: goldEarned ?? this.goldEarned,
|
|
||||||
goldSpent: goldSpent ?? this.goldSpent,
|
|
||||||
skillsUsed: skillsUsed ?? this.skillsUsed,
|
|
||||||
criticalHits: criticalHits ?? this.criticalHits,
|
|
||||||
maxCriticalStreak: maxCriticalStreak ?? this.maxCriticalStreak,
|
|
||||||
currentCriticalStreak:
|
|
||||||
currentCriticalStreak ?? this.currentCriticalStreak,
|
|
||||||
totalDamageDealt: totalDamageDealt ?? this.totalDamageDealt,
|
|
||||||
totalDamageTaken: totalDamageTaken ?? this.totalDamageTaken,
|
|
||||||
potionsUsed: potionsUsed ?? this.potionsUsed,
|
|
||||||
itemsSold: itemsSold ?? this.itemsSold,
|
|
||||||
questsCompleted: questsCompleted ?? this.questsCompleted,
|
|
||||||
deathCount: deathCount ?? this.deathCount,
|
|
||||||
bossesDefeated: bossesDefeated ?? this.bossesDefeated,
|
|
||||||
levelUps: levelUps ?? this.levelUps,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// JSON 직렬화
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'playTimeMs': playTimeMs,
|
|
||||||
'monstersKilled': monstersKilled,
|
|
||||||
'goldEarned': goldEarned,
|
|
||||||
'goldSpent': goldSpent,
|
|
||||||
'skillsUsed': skillsUsed,
|
|
||||||
'criticalHits': criticalHits,
|
|
||||||
'maxCriticalStreak': maxCriticalStreak,
|
|
||||||
'totalDamageDealt': totalDamageDealt,
|
|
||||||
'totalDamageTaken': totalDamageTaken,
|
|
||||||
'potionsUsed': potionsUsed,
|
|
||||||
'itemsSold': itemsSold,
|
|
||||||
'questsCompleted': questsCompleted,
|
|
||||||
'deathCount': deathCount,
|
|
||||||
'bossesDefeated': bossesDefeated,
|
|
||||||
'levelUps': levelUps,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// JSON 역직렬화
|
|
||||||
factory SessionStatistics.fromJson(Map<String, dynamic> json) {
|
|
||||||
return SessionStatistics(
|
|
||||||
playTimeMs: json['playTimeMs'] as int? ?? 0,
|
|
||||||
monstersKilled: json['monstersKilled'] as int? ?? 0,
|
|
||||||
goldEarned: json['goldEarned'] as int? ?? 0,
|
|
||||||
goldSpent: json['goldSpent'] as int? ?? 0,
|
|
||||||
skillsUsed: json['skillsUsed'] as int? ?? 0,
|
|
||||||
criticalHits: json['criticalHits'] as int? ?? 0,
|
|
||||||
maxCriticalStreak: json['maxCriticalStreak'] as int? ?? 0,
|
|
||||||
currentCriticalStreak: 0, // 세션간 유지 안 함
|
|
||||||
totalDamageDealt: json['totalDamageDealt'] as int? ?? 0,
|
|
||||||
totalDamageTaken: json['totalDamageTaken'] as int? ?? 0,
|
|
||||||
potionsUsed: json['potionsUsed'] as int? ?? 0,
|
|
||||||
itemsSold: json['itemsSold'] as int? ?? 0,
|
|
||||||
questsCompleted: json['questsCompleted'] as int? ?? 0,
|
|
||||||
deathCount: json['deathCount'] as int? ?? 0,
|
|
||||||
bossesDefeated: json['bossesDefeated'] as int? ?? 0,
|
|
||||||
levelUps: json['levelUps'] as int? ?? 0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 누적 통계 (Cumulative Statistics)
|
|
||||||
///
|
|
||||||
/// 모든 게임 세션의 누적 통계
|
|
||||||
class CumulativeStatistics {
|
|
||||||
const CumulativeStatistics({
|
|
||||||
required this.totalPlayTimeMs,
|
|
||||||
required this.totalMonstersKilled,
|
|
||||||
required this.totalGoldEarned,
|
|
||||||
required this.totalGoldSpent,
|
|
||||||
required this.totalSkillsUsed,
|
|
||||||
required this.totalCriticalHits,
|
|
||||||
required this.bestCriticalStreak,
|
|
||||||
required this.totalDamageDealt,
|
|
||||||
required this.totalDamageTaken,
|
|
||||||
required this.totalPotionsUsed,
|
|
||||||
required this.totalItemsSold,
|
|
||||||
required this.totalQuestsCompleted,
|
|
||||||
required this.totalDeaths,
|
|
||||||
required this.totalBossesDefeated,
|
|
||||||
required this.totalLevelUps,
|
|
||||||
required this.highestLevel,
|
|
||||||
required this.highestGoldHeld,
|
|
||||||
required this.gamesCompleted,
|
|
||||||
required this.gamesStarted,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// 총 플레이 시간 (밀리초)
|
|
||||||
final int totalPlayTimeMs;
|
|
||||||
|
|
||||||
/// 총 처치한 몬스터 수
|
|
||||||
final int totalMonstersKilled;
|
|
||||||
|
|
||||||
/// 총 획득한 골드
|
|
||||||
final int totalGoldEarned;
|
|
||||||
|
|
||||||
/// 총 소비한 골드
|
|
||||||
final int totalGoldSpent;
|
|
||||||
|
|
||||||
/// 총 스킬 사용 횟수
|
|
||||||
final int totalSkillsUsed;
|
|
||||||
|
|
||||||
/// 총 크리티컬 히트 횟수
|
|
||||||
final int totalCriticalHits;
|
|
||||||
|
|
||||||
/// 최고 연속 크리티컬
|
|
||||||
final int bestCriticalStreak;
|
|
||||||
|
|
||||||
/// 총 입힌 데미지
|
|
||||||
final int totalDamageDealt;
|
|
||||||
|
|
||||||
/// 총 받은 데미지
|
|
||||||
final int totalDamageTaken;
|
|
||||||
|
|
||||||
/// 총 사용한 물약 수
|
|
||||||
final int totalPotionsUsed;
|
|
||||||
|
|
||||||
/// 총 판매한 아이템 수
|
|
||||||
final int totalItemsSold;
|
|
||||||
|
|
||||||
/// 총 완료한 퀘스트 수
|
|
||||||
final int totalQuestsCompleted;
|
|
||||||
|
|
||||||
/// 총 사망 횟수
|
|
||||||
final int totalDeaths;
|
|
||||||
|
|
||||||
/// 총 처치한 보스 수
|
|
||||||
final int totalBossesDefeated;
|
|
||||||
|
|
||||||
/// 총 레벨업 횟수
|
|
||||||
final int totalLevelUps;
|
|
||||||
|
|
||||||
/// 최고 달성 레벨
|
|
||||||
final int highestLevel;
|
|
||||||
|
|
||||||
/// 최대 보유 골드
|
|
||||||
final int highestGoldHeld;
|
|
||||||
|
|
||||||
/// 클리어한 게임 수
|
|
||||||
final int gamesCompleted;
|
|
||||||
|
|
||||||
/// 시작한 게임 수
|
|
||||||
final int gamesStarted;
|
|
||||||
|
|
||||||
/// 총 플레이 시간 Duration
|
|
||||||
Duration get totalPlayTime => Duration(milliseconds: totalPlayTimeMs);
|
|
||||||
|
|
||||||
/// 총 플레이 시간 포맷 (HH:MM:SS)
|
|
||||||
String get formattedTotalPlayTime {
|
|
||||||
final hours = totalPlayTime.inHours;
|
|
||||||
final minutes = totalPlayTime.inMinutes % 60;
|
|
||||||
final seconds = totalPlayTime.inSeconds % 60;
|
|
||||||
return '${hours.toString().padLeft(2, '0')}:'
|
|
||||||
'${minutes.toString().padLeft(2, '0')}:'
|
|
||||||
'${seconds.toString().padLeft(2, '0')}';
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 평균 게임당 플레이 시간
|
|
||||||
Duration get averagePlayTimePerGame {
|
|
||||||
if (gamesStarted <= 0) return Duration.zero;
|
|
||||||
return Duration(milliseconds: totalPlayTimeMs ~/ gamesStarted);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 게임 완료율
|
|
||||||
double get completionRate {
|
|
||||||
if (gamesStarted <= 0) return 0;
|
|
||||||
return gamesCompleted / gamesStarted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 빈 누적 통계
|
|
||||||
factory CumulativeStatistics.empty() => const CumulativeStatistics(
|
|
||||||
totalPlayTimeMs: 0,
|
|
||||||
totalMonstersKilled: 0,
|
|
||||||
totalGoldEarned: 0,
|
|
||||||
totalGoldSpent: 0,
|
|
||||||
totalSkillsUsed: 0,
|
|
||||||
totalCriticalHits: 0,
|
|
||||||
bestCriticalStreak: 0,
|
|
||||||
totalDamageDealt: 0,
|
|
||||||
totalDamageTaken: 0,
|
|
||||||
totalPotionsUsed: 0,
|
|
||||||
totalItemsSold: 0,
|
|
||||||
totalQuestsCompleted: 0,
|
|
||||||
totalDeaths: 0,
|
|
||||||
totalBossesDefeated: 0,
|
|
||||||
totalLevelUps: 0,
|
|
||||||
highestLevel: 0,
|
|
||||||
highestGoldHeld: 0,
|
|
||||||
gamesCompleted: 0,
|
|
||||||
gamesStarted: 0,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// 세션 통계 병합
|
|
||||||
CumulativeStatistics mergeSession(SessionStatistics session) {
|
|
||||||
return CumulativeStatistics(
|
|
||||||
totalPlayTimeMs: totalPlayTimeMs + session.playTimeMs,
|
|
||||||
totalMonstersKilled: totalMonstersKilled + session.monstersKilled,
|
|
||||||
totalGoldEarned: totalGoldEarned + session.goldEarned,
|
|
||||||
totalGoldSpent: totalGoldSpent + session.goldSpent,
|
|
||||||
totalSkillsUsed: totalSkillsUsed + session.skillsUsed,
|
|
||||||
totalCriticalHits: totalCriticalHits + session.criticalHits,
|
|
||||||
bestCriticalStreak: session.maxCriticalStreak > bestCriticalStreak
|
|
||||||
? session.maxCriticalStreak
|
|
||||||
: bestCriticalStreak,
|
|
||||||
totalDamageDealt: totalDamageDealt + session.totalDamageDealt,
|
|
||||||
totalDamageTaken: totalDamageTaken + session.totalDamageTaken,
|
|
||||||
totalPotionsUsed: totalPotionsUsed + session.potionsUsed,
|
|
||||||
totalItemsSold: totalItemsSold + session.itemsSold,
|
|
||||||
totalQuestsCompleted: totalQuestsCompleted + session.questsCompleted,
|
|
||||||
totalDeaths: totalDeaths + session.deathCount,
|
|
||||||
totalBossesDefeated: totalBossesDefeated + session.bossesDefeated,
|
|
||||||
totalLevelUps: totalLevelUps + session.levelUps,
|
|
||||||
highestLevel: highestLevel, // 별도 업데이트 필요
|
|
||||||
highestGoldHeld: highestGoldHeld, // 별도 업데이트 필요
|
|
||||||
gamesCompleted: gamesCompleted, // 별도 업데이트 필요
|
|
||||||
gamesStarted: gamesStarted, // 별도 업데이트 필요
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 최고 레벨 업데이트
|
|
||||||
CumulativeStatistics updateHighestLevel(int level) {
|
|
||||||
if (level <= highestLevel) return this;
|
|
||||||
return copyWith(highestLevel: level);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 최대 골드 업데이트
|
|
||||||
CumulativeStatistics updateHighestGold(int gold) {
|
|
||||||
if (gold <= highestGoldHeld) return this;
|
|
||||||
return copyWith(highestGoldHeld: gold);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 새 게임 시작 기록
|
|
||||||
CumulativeStatistics recordGameStart() {
|
|
||||||
return copyWith(gamesStarted: gamesStarted + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 게임 클리어 기록
|
|
||||||
CumulativeStatistics recordGameComplete() {
|
|
||||||
return copyWith(gamesCompleted: gamesCompleted + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
CumulativeStatistics copyWith({
|
|
||||||
int? totalPlayTimeMs,
|
|
||||||
int? totalMonstersKilled,
|
|
||||||
int? totalGoldEarned,
|
|
||||||
int? totalGoldSpent,
|
|
||||||
int? totalSkillsUsed,
|
|
||||||
int? totalCriticalHits,
|
|
||||||
int? bestCriticalStreak,
|
|
||||||
int? totalDamageDealt,
|
|
||||||
int? totalDamageTaken,
|
|
||||||
int? totalPotionsUsed,
|
|
||||||
int? totalItemsSold,
|
|
||||||
int? totalQuestsCompleted,
|
|
||||||
int? totalDeaths,
|
|
||||||
int? totalBossesDefeated,
|
|
||||||
int? totalLevelUps,
|
|
||||||
int? highestLevel,
|
|
||||||
int? highestGoldHeld,
|
|
||||||
int? gamesCompleted,
|
|
||||||
int? gamesStarted,
|
|
||||||
}) {
|
|
||||||
return CumulativeStatistics(
|
|
||||||
totalPlayTimeMs: totalPlayTimeMs ?? this.totalPlayTimeMs,
|
|
||||||
totalMonstersKilled: totalMonstersKilled ?? this.totalMonstersKilled,
|
|
||||||
totalGoldEarned: totalGoldEarned ?? this.totalGoldEarned,
|
|
||||||
totalGoldSpent: totalGoldSpent ?? this.totalGoldSpent,
|
|
||||||
totalSkillsUsed: totalSkillsUsed ?? this.totalSkillsUsed,
|
|
||||||
totalCriticalHits: totalCriticalHits ?? this.totalCriticalHits,
|
|
||||||
bestCriticalStreak: bestCriticalStreak ?? this.bestCriticalStreak,
|
|
||||||
totalDamageDealt: totalDamageDealt ?? this.totalDamageDealt,
|
|
||||||
totalDamageTaken: totalDamageTaken ?? this.totalDamageTaken,
|
|
||||||
totalPotionsUsed: totalPotionsUsed ?? this.totalPotionsUsed,
|
|
||||||
totalItemsSold: totalItemsSold ?? this.totalItemsSold,
|
|
||||||
totalQuestsCompleted: totalQuestsCompleted ?? this.totalQuestsCompleted,
|
|
||||||
totalDeaths: totalDeaths ?? this.totalDeaths,
|
|
||||||
totalBossesDefeated: totalBossesDefeated ?? this.totalBossesDefeated,
|
|
||||||
totalLevelUps: totalLevelUps ?? this.totalLevelUps,
|
|
||||||
highestLevel: highestLevel ?? this.highestLevel,
|
|
||||||
highestGoldHeld: highestGoldHeld ?? this.highestGoldHeld,
|
|
||||||
gamesCompleted: gamesCompleted ?? this.gamesCompleted,
|
|
||||||
gamesStarted: gamesStarted ?? this.gamesStarted,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// JSON 직렬화
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'totalPlayTimeMs': totalPlayTimeMs,
|
|
||||||
'totalMonstersKilled': totalMonstersKilled,
|
|
||||||
'totalGoldEarned': totalGoldEarned,
|
|
||||||
'totalGoldSpent': totalGoldSpent,
|
|
||||||
'totalSkillsUsed': totalSkillsUsed,
|
|
||||||
'totalCriticalHits': totalCriticalHits,
|
|
||||||
'bestCriticalStreak': bestCriticalStreak,
|
|
||||||
'totalDamageDealt': totalDamageDealt,
|
|
||||||
'totalDamageTaken': totalDamageTaken,
|
|
||||||
'totalPotionsUsed': totalPotionsUsed,
|
|
||||||
'totalItemsSold': totalItemsSold,
|
|
||||||
'totalQuestsCompleted': totalQuestsCompleted,
|
|
||||||
'totalDeaths': totalDeaths,
|
|
||||||
'totalBossesDefeated': totalBossesDefeated,
|
|
||||||
'totalLevelUps': totalLevelUps,
|
|
||||||
'highestLevel': highestLevel,
|
|
||||||
'highestGoldHeld': highestGoldHeld,
|
|
||||||
'gamesCompleted': gamesCompleted,
|
|
||||||
'gamesStarted': gamesStarted,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// JSON 역직렬화
|
|
||||||
factory CumulativeStatistics.fromJson(Map<String, dynamic> json) {
|
|
||||||
return CumulativeStatistics(
|
|
||||||
totalPlayTimeMs: json['totalPlayTimeMs'] as int? ?? 0,
|
|
||||||
totalMonstersKilled: json['totalMonstersKilled'] as int? ?? 0,
|
|
||||||
totalGoldEarned: json['totalGoldEarned'] as int? ?? 0,
|
|
||||||
totalGoldSpent: json['totalGoldSpent'] as int? ?? 0,
|
|
||||||
totalSkillsUsed: json['totalSkillsUsed'] as int? ?? 0,
|
|
||||||
totalCriticalHits: json['totalCriticalHits'] as int? ?? 0,
|
|
||||||
bestCriticalStreak: json['bestCriticalStreak'] as int? ?? 0,
|
|
||||||
totalDamageDealt: json['totalDamageDealt'] as int? ?? 0,
|
|
||||||
totalDamageTaken: json['totalDamageTaken'] as int? ?? 0,
|
|
||||||
totalPotionsUsed: json['totalPotionsUsed'] as int? ?? 0,
|
|
||||||
totalItemsSold: json['totalItemsSold'] as int? ?? 0,
|
|
||||||
totalQuestsCompleted: json['totalQuestsCompleted'] as int? ?? 0,
|
|
||||||
totalDeaths: json['totalDeaths'] as int? ?? 0,
|
|
||||||
totalBossesDefeated: json['totalBossesDefeated'] as int? ?? 0,
|
|
||||||
totalLevelUps: json['totalLevelUps'] as int? ?? 0,
|
|
||||||
highestLevel: json['highestLevel'] as int? ?? 0,
|
|
||||||
highestGoldHeld: json['highestGoldHeld'] as int? ?? 0,
|
|
||||||
gamesCompleted: json['gamesCompleted'] as int? ?? 0,
|
|
||||||
gamesStarted: json['gamesStarted'] as int? ?? 0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
279
lib/src/core/model/session_statistics.dart
Normal file
279
lib/src/core/model/session_statistics.dart
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
/// 세션 통계 (Session Statistics)
|
||||||
|
///
|
||||||
|
/// GameStatistics에서 분리된 현재 게임 세션의 통계 모델.
|
||||||
|
class SessionStatistics {
|
||||||
|
const SessionStatistics({
|
||||||
|
required this.playTimeMs,
|
||||||
|
required this.monstersKilled,
|
||||||
|
required this.goldEarned,
|
||||||
|
required this.goldSpent,
|
||||||
|
required this.skillsUsed,
|
||||||
|
required this.criticalHits,
|
||||||
|
required this.maxCriticalStreak,
|
||||||
|
required this.currentCriticalStreak,
|
||||||
|
required this.totalDamageDealt,
|
||||||
|
required this.totalDamageTaken,
|
||||||
|
required this.potionsUsed,
|
||||||
|
required this.itemsSold,
|
||||||
|
required this.questsCompleted,
|
||||||
|
required this.deathCount,
|
||||||
|
required this.bossesDefeated,
|
||||||
|
required this.levelUps,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// 플레이 시간 (밀리초)
|
||||||
|
final int playTimeMs;
|
||||||
|
|
||||||
|
/// 처치한 몬스터 수
|
||||||
|
final int monstersKilled;
|
||||||
|
|
||||||
|
/// 획득한 골드 총량
|
||||||
|
final int goldEarned;
|
||||||
|
|
||||||
|
/// 소비한 골드 총량
|
||||||
|
final int goldSpent;
|
||||||
|
|
||||||
|
/// 사용한 스킬 횟수
|
||||||
|
final int skillsUsed;
|
||||||
|
|
||||||
|
/// 크리티컬 히트 횟수
|
||||||
|
final int criticalHits;
|
||||||
|
|
||||||
|
/// 최대 연속 크리티컬
|
||||||
|
final int maxCriticalStreak;
|
||||||
|
|
||||||
|
/// 현재 연속 크리티컬 (내부 추적용)
|
||||||
|
final int currentCriticalStreak;
|
||||||
|
|
||||||
|
/// 총 입힌 데미지
|
||||||
|
final int totalDamageDealt;
|
||||||
|
|
||||||
|
/// 총 받은 데미지
|
||||||
|
final int totalDamageTaken;
|
||||||
|
|
||||||
|
/// 사용한 물약 수
|
||||||
|
final int potionsUsed;
|
||||||
|
|
||||||
|
/// 판매한 아이템 수
|
||||||
|
final int itemsSold;
|
||||||
|
|
||||||
|
/// 완료한 퀘스트 수
|
||||||
|
final int questsCompleted;
|
||||||
|
|
||||||
|
/// 사망 횟수
|
||||||
|
final int deathCount;
|
||||||
|
|
||||||
|
/// 처치한 보스 수
|
||||||
|
final int bossesDefeated;
|
||||||
|
|
||||||
|
/// 레벨업 횟수
|
||||||
|
final int levelUps;
|
||||||
|
|
||||||
|
/// 플레이 시간 Duration
|
||||||
|
Duration get playTime => Duration(milliseconds: playTimeMs);
|
||||||
|
|
||||||
|
/// 플레이 시간 포맷 (HH:MM:SS)
|
||||||
|
String get formattedPlayTime {
|
||||||
|
final hours = playTime.inHours;
|
||||||
|
final minutes = playTime.inMinutes % 60;
|
||||||
|
final seconds = playTime.inSeconds % 60;
|
||||||
|
return '${hours.toString().padLeft(2, '0')}:'
|
||||||
|
'${minutes.toString().padLeft(2, '0')}:'
|
||||||
|
'${seconds.toString().padLeft(2, '0')}';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 평균 DPS (damage per second)
|
||||||
|
double get averageDps {
|
||||||
|
if (playTimeMs <= 0) return 0;
|
||||||
|
return totalDamageDealt / (playTimeMs / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 킬당 평균 골드
|
||||||
|
double get goldPerKill {
|
||||||
|
if (monstersKilled <= 0) return 0;
|
||||||
|
return goldEarned / monstersKilled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 크리티컬 비율
|
||||||
|
double get criticalRate {
|
||||||
|
if (skillsUsed <= 0) return 0;
|
||||||
|
return criticalHits / skillsUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 빈 세션 통계
|
||||||
|
factory SessionStatistics.empty() => const SessionStatistics(
|
||||||
|
playTimeMs: 0,
|
||||||
|
monstersKilled: 0,
|
||||||
|
goldEarned: 0,
|
||||||
|
goldSpent: 0,
|
||||||
|
skillsUsed: 0,
|
||||||
|
criticalHits: 0,
|
||||||
|
maxCriticalStreak: 0,
|
||||||
|
currentCriticalStreak: 0,
|
||||||
|
totalDamageDealt: 0,
|
||||||
|
totalDamageTaken: 0,
|
||||||
|
potionsUsed: 0,
|
||||||
|
itemsSold: 0,
|
||||||
|
questsCompleted: 0,
|
||||||
|
deathCount: 0,
|
||||||
|
bossesDefeated: 0,
|
||||||
|
levelUps: 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 이벤트 기록 메서드
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// 몬스터 처치 기록
|
||||||
|
SessionStatistics recordKill({bool isBoss = false}) {
|
||||||
|
return copyWith(
|
||||||
|
monstersKilled: monstersKilled + 1,
|
||||||
|
bossesDefeated: isBoss ? bossesDefeated + 1 : bossesDefeated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 골드 획득 기록
|
||||||
|
SessionStatistics recordGoldEarned(int amount) {
|
||||||
|
return copyWith(goldEarned: goldEarned + amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 골드 소비 기록
|
||||||
|
SessionStatistics recordGoldSpent(int amount) {
|
||||||
|
return copyWith(goldSpent: goldSpent + amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 스킬 사용 기록
|
||||||
|
SessionStatistics recordSkillUse({required bool isCritical}) {
|
||||||
|
final newCriticalStreak = isCritical ? currentCriticalStreak + 1 : 0;
|
||||||
|
final newMaxStreak = newCriticalStreak > maxCriticalStreak
|
||||||
|
? newCriticalStreak
|
||||||
|
: maxCriticalStreak;
|
||||||
|
|
||||||
|
return copyWith(
|
||||||
|
skillsUsed: skillsUsed + 1,
|
||||||
|
criticalHits: isCritical ? criticalHits + 1 : criticalHits,
|
||||||
|
currentCriticalStreak: newCriticalStreak,
|
||||||
|
maxCriticalStreak: newMaxStreak,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 데미지 기록
|
||||||
|
SessionStatistics recordDamage({int dealt = 0, int taken = 0}) {
|
||||||
|
return copyWith(
|
||||||
|
totalDamageDealt: totalDamageDealt + dealt,
|
||||||
|
totalDamageTaken: totalDamageTaken + taken,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 물약 사용 기록
|
||||||
|
SessionStatistics recordPotionUse() {
|
||||||
|
return copyWith(potionsUsed: potionsUsed + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 아이템 판매 기록
|
||||||
|
SessionStatistics recordItemSold(int count) {
|
||||||
|
return copyWith(itemsSold: itemsSold + count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 퀘스트 완료 기록
|
||||||
|
SessionStatistics recordQuestComplete() {
|
||||||
|
return copyWith(questsCompleted: questsCompleted + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 사망 기록
|
||||||
|
SessionStatistics recordDeath() {
|
||||||
|
return copyWith(deathCount: deathCount + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 레벨업 기록
|
||||||
|
SessionStatistics recordLevelUp() {
|
||||||
|
return copyWith(levelUps: levelUps + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 플레이 시간 업데이트
|
||||||
|
SessionStatistics updatePlayTime(int elapsedMs) {
|
||||||
|
return copyWith(playTimeMs: elapsedMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionStatistics copyWith({
|
||||||
|
int? playTimeMs,
|
||||||
|
int? monstersKilled,
|
||||||
|
int? goldEarned,
|
||||||
|
int? goldSpent,
|
||||||
|
int? skillsUsed,
|
||||||
|
int? criticalHits,
|
||||||
|
int? maxCriticalStreak,
|
||||||
|
int? currentCriticalStreak,
|
||||||
|
int? totalDamageDealt,
|
||||||
|
int? totalDamageTaken,
|
||||||
|
int? potionsUsed,
|
||||||
|
int? itemsSold,
|
||||||
|
int? questsCompleted,
|
||||||
|
int? deathCount,
|
||||||
|
int? bossesDefeated,
|
||||||
|
int? levelUps,
|
||||||
|
}) {
|
||||||
|
return SessionStatistics(
|
||||||
|
playTimeMs: playTimeMs ?? this.playTimeMs,
|
||||||
|
monstersKilled: monstersKilled ?? this.monstersKilled,
|
||||||
|
goldEarned: goldEarned ?? this.goldEarned,
|
||||||
|
goldSpent: goldSpent ?? this.goldSpent,
|
||||||
|
skillsUsed: skillsUsed ?? this.skillsUsed,
|
||||||
|
criticalHits: criticalHits ?? this.criticalHits,
|
||||||
|
maxCriticalStreak: maxCriticalStreak ?? this.maxCriticalStreak,
|
||||||
|
currentCriticalStreak:
|
||||||
|
currentCriticalStreak ?? this.currentCriticalStreak,
|
||||||
|
totalDamageDealt: totalDamageDealt ?? this.totalDamageDealt,
|
||||||
|
totalDamageTaken: totalDamageTaken ?? this.totalDamageTaken,
|
||||||
|
potionsUsed: potionsUsed ?? this.potionsUsed,
|
||||||
|
itemsSold: itemsSold ?? this.itemsSold,
|
||||||
|
questsCompleted: questsCompleted ?? this.questsCompleted,
|
||||||
|
deathCount: deathCount ?? this.deathCount,
|
||||||
|
bossesDefeated: bossesDefeated ?? this.bossesDefeated,
|
||||||
|
levelUps: levelUps ?? this.levelUps,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// JSON 직렬화
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'playTimeMs': playTimeMs,
|
||||||
|
'monstersKilled': monstersKilled,
|
||||||
|
'goldEarned': goldEarned,
|
||||||
|
'goldSpent': goldSpent,
|
||||||
|
'skillsUsed': skillsUsed,
|
||||||
|
'criticalHits': criticalHits,
|
||||||
|
'maxCriticalStreak': maxCriticalStreak,
|
||||||
|
'totalDamageDealt': totalDamageDealt,
|
||||||
|
'totalDamageTaken': totalDamageTaken,
|
||||||
|
'potionsUsed': potionsUsed,
|
||||||
|
'itemsSold': itemsSold,
|
||||||
|
'questsCompleted': questsCompleted,
|
||||||
|
'deathCount': deathCount,
|
||||||
|
'bossesDefeated': bossesDefeated,
|
||||||
|
'levelUps': levelUps,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// JSON 역직렬화
|
||||||
|
factory SessionStatistics.fromJson(Map<String, dynamic> json) {
|
||||||
|
return SessionStatistics(
|
||||||
|
playTimeMs: json['playTimeMs'] as int? ?? 0,
|
||||||
|
monstersKilled: json['monstersKilled'] as int? ?? 0,
|
||||||
|
goldEarned: json['goldEarned'] as int? ?? 0,
|
||||||
|
goldSpent: json['goldSpent'] as int? ?? 0,
|
||||||
|
skillsUsed: json['skillsUsed'] as int? ?? 0,
|
||||||
|
criticalHits: json['criticalHits'] as int? ?? 0,
|
||||||
|
maxCriticalStreak: json['maxCriticalStreak'] as int? ?? 0,
|
||||||
|
currentCriticalStreak: 0, // 세션간 유지 안 함
|
||||||
|
totalDamageDealt: json['totalDamageDealt'] as int? ?? 0,
|
||||||
|
totalDamageTaken: json['totalDamageTaken'] as int? ?? 0,
|
||||||
|
potionsUsed: json['potionsUsed'] as int? ?? 0,
|
||||||
|
itemsSold: json['itemsSold'] as int? ?? 0,
|
||||||
|
questsCompleted: json['questsCompleted'] as int? ?? 0,
|
||||||
|
deathCount: json['deathCount'] as int? ?? 0,
|
||||||
|
bossesDefeated: json['bossesDefeated'] as int? ?? 0,
|
||||||
|
levelUps: json['levelUps'] as int? ?? 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:asciineverdie/src/core/animation/monster_size.dart';
|
import 'package:asciineverdie/src/shared/animation/monster_size.dart';
|
||||||
import 'package:asciineverdie/src/core/model/monster_grade.dart';
|
import 'package:asciineverdie/src/core/model/monster_grade.dart';
|
||||||
|
|
||||||
/// 태스크 타입 (원본 fTask.Caption 값들에 대응)
|
/// 태스크 타입 (원본 fTask.Caption 값들에 대응)
|
||||||
|
|||||||
Reference in New Issue
Block a user