/// 게임 통계 (Game Statistics) /// /// 세션 및 누적 통계를 추적하는 모델 class GameStatistics { const GameStatistics({ required this.session, required this.cumulative, }); /// 현재 세션 통계 final SessionStatistics session; /// 누적 통계 final CumulativeStatistics cumulative; /// 빈 통계 factory GameStatistics.empty() => GameStatistics( session: SessionStatistics.empty(), cumulative: CumulativeStatistics.empty(), ); /// 새 세션 시작 (세션 통계 초기화, 누적 통계 유지) GameStatistics startNewSession() { return GameStatistics( session: SessionStatistics.empty(), cumulative: cumulative, ); } /// 세션 종료 시 누적 통계 업데이트 GameStatistics endSession() { return GameStatistics( session: session, cumulative: cumulative.mergeSession(session), ); } GameStatistics copyWith({ SessionStatistics? session, CumulativeStatistics? cumulative, }) { return GameStatistics( session: session ?? this.session, cumulative: cumulative ?? this.cumulative, ); } /// JSON 직렬화 Map toJson() { return { 'session': session.toJson(), 'cumulative': cumulative.toJson(), }; } /// JSON 역직렬화 factory GameStatistics.fromJson(Map json) { return GameStatistics( session: SessionStatistics.fromJson( json['session'] as Map? ?? {}, ), cumulative: CumulativeStatistics.fromJson( json['cumulative'] as Map? ?? {}, ), ); } } /// 세션 통계 (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 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 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 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 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, ); } }