142 lines
5.2 KiB
Dart
142 lines
5.2 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:hive_flutter/hive_flutter.dart';
|
|
import '../models/sake_item.dart';
|
|
import '../providers/theme_provider.dart';
|
|
|
|
/// Badge unlock result
|
|
class BadgeUnlock {
|
|
final String id;
|
|
final String name;
|
|
final String icon;
|
|
|
|
BadgeUnlock(this.id, this.name, this.icon);
|
|
}
|
|
|
|
/// Gamification Service: Badge unlock logic
|
|
class GamificationService {
|
|
/// Check badge conditions and unlock new badges
|
|
/// Returns: List of newly unlocked badges
|
|
static Future<List<BadgeUnlock>> checkAndUnlockBadges(WidgetRef ref) async {
|
|
final userProfile = ref.read(userProfileProvider);
|
|
final unlockedBadges = userProfile.unlockedBadges.toSet();
|
|
final newlyUnlocked = <BadgeUnlock>[];
|
|
|
|
// Get all SakeItems from Hive
|
|
final box = Hive.box<SakeItem>('sake_items');
|
|
final allItems = box.values.toList();
|
|
|
|
// 1. Badge: 初めての一歩 (first_step)
|
|
// Condition: Register at least 1 sake
|
|
if (!unlockedBadges.contains('first_step') && allItems.isNotEmpty) {
|
|
newlyUnlocked.add(BadgeUnlock('first_step', '初めての一歩', '🍶'));
|
|
}
|
|
|
|
// 2. Badge: 東北制覇 (regional_tohoku)
|
|
// Condition: Register sake from all 6 Tohoku prefectures
|
|
if (!unlockedBadges.contains('regional_tohoku')) {
|
|
final tohokuPrefectures = {'青森県', '岩手県', '宮城県', '秋田県', '山形県', '福島県'};
|
|
final registeredPrefectures = allItems
|
|
.map((item) => item.displayData.prefecture)
|
|
.where((pref) => tohokuPrefectures.contains(pref))
|
|
.toSet();
|
|
|
|
if (registeredPrefectures.length == 6) {
|
|
newlyUnlocked.add(BadgeUnlock('regional_tohoku', '東北制覇', '👹'));
|
|
}
|
|
}
|
|
|
|
// 3. Badge: 辛口党 (flavor_dry)
|
|
// Condition: Register 10+ sake with SMV >= +5
|
|
if (!unlockedBadges.contains('flavor_dry')) {
|
|
final dryItems = allItems.where((item) {
|
|
final smv = item.hiddenSpecs.sakeMeterValue;
|
|
return smv != null && smv >= 5.0;
|
|
}).toList();
|
|
|
|
if (dryItems.length >= 10) {
|
|
newlyUnlocked.add(BadgeUnlock('flavor_dry', '辛口党', '🌶️'));
|
|
}
|
|
}
|
|
|
|
// 4. Badge: 関東制覇 (regional_kanto)
|
|
// Condition: Register sake from all 7 Kanto prefectures
|
|
if (!unlockedBadges.contains('regional_kanto')) {
|
|
final kantoPrefectures = {'茨城県', '栃木県', '群馬県', '埼玉県', '千葉県', '東京都', '神奈川県'};
|
|
final registeredPrefectures = allItems
|
|
.map((item) => item.displayData.prefecture)
|
|
.where((pref) => kantoPrefectures.contains(pref))
|
|
.toSet();
|
|
|
|
if (registeredPrefectures.length == 7) {
|
|
newlyUnlocked.add(BadgeUnlock('regional_kanto', '関東制覇', '🗻'));
|
|
}
|
|
}
|
|
|
|
// 5. Badge: 関西制覇 (regional_kansai)
|
|
// Condition: Register sake from all 6 Kansai prefectures
|
|
if (!unlockedBadges.contains('regional_kansai')) {
|
|
final kansaiPrefectures = {'滋賀県', '京都府', '大阪府', '兵庫県', '奈良県', '和歌山県'};
|
|
final registeredPrefectures = allItems
|
|
.map((item) => item.displayData.prefecture)
|
|
.where((pref) => kansaiPrefectures.contains(pref))
|
|
.toSet();
|
|
|
|
if (registeredPrefectures.length == 6) {
|
|
newlyUnlocked.add(BadgeUnlock('regional_kansai', '関西制覇', '🏯'));
|
|
}
|
|
}
|
|
|
|
// 6. Badge: 愛好家 (collector_10)
|
|
// Condition: Register 10+ sake
|
|
if (!unlockedBadges.contains('collector_10') && allItems.length >= 10) {
|
|
newlyUnlocked.add(BadgeUnlock('collector_10', '愛好家', '🎉'));
|
|
}
|
|
|
|
// 7. Badge: コレクター (collector_50)
|
|
// Condition: Register 50+ sake
|
|
if (!unlockedBadges.contains('collector_50') && allItems.length >= 50) {
|
|
newlyUnlocked.add(BadgeUnlock('collector_50', 'コレクター', '📚'));
|
|
}
|
|
|
|
// 8. Badge: レジェンド (collector_100)
|
|
// Condition: Register 100+ sake
|
|
if (!unlockedBadges.contains('collector_100') && allItems.length >= 100) {
|
|
newlyUnlocked.add(BadgeUnlock('collector_100', 'レジェンド', '👑'));
|
|
}
|
|
|
|
// 9. Badge: 甘口党 (flavor_sweet)
|
|
// Condition: Register 10+ sake with SMV <= -3
|
|
if (!unlockedBadges.contains('flavor_sweet')) {
|
|
final sweetItems = allItems.where((item) {
|
|
final smv = item.hiddenSpecs.sakeMeterValue;
|
|
return smv != null && smv <= -3.0;
|
|
}).toList();
|
|
|
|
if (sweetItems.length >= 10) {
|
|
newlyUnlocked.add(BadgeUnlock('flavor_sweet', '甘口党', '🍯'));
|
|
}
|
|
}
|
|
|
|
// 10. Badge: 香りの貴族 (flavor_aromatic)
|
|
// Condition: Register 10+ sake with high aroma score (>= 4.0)
|
|
if (!unlockedBadges.contains('flavor_aromatic')) {
|
|
final aromaticItems = allItems.where((item) {
|
|
final tasteStats = item.hiddenSpecs.sakeTasteStats;
|
|
return tasteStats.aroma >= 4.0;
|
|
}).toList();
|
|
|
|
if (aromaticItems.length >= 10) {
|
|
newlyUnlocked.add(BadgeUnlock('flavor_aromatic', '香りの貴族', '🌸'));
|
|
}
|
|
}
|
|
|
|
// Update UserProfile if there are newly unlocked badges
|
|
if (newlyUnlocked.isNotEmpty) {
|
|
final updatedBadges = [...unlockedBadges, ...newlyUnlocked.map((b) => b.id)];
|
|
await ref.read(userProfileProvider.notifier).updateUnlockedBadges(updatedBadges);
|
|
}
|
|
|
|
return newlyUnlocked;
|
|
}
|
|
}
|