Files
breakpilot-lehrer/breakpilot-drive/UnityScripts/Core/AchievementManager.cs
Benjamin Boenisch 5a31f52310 Initial commit: breakpilot-lehrer - Lehrer KI Platform
Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website,
Klausur-Service, School-Service, Voice-Service, Geo-Service,
BreakPilot Drive, Agent-Core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:26 +01:00

349 lines
10 KiB
C#

// ==============================================
// AchievementManager.cs - Achievement System
// ==============================================
// Handles achievement display and notifications.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace BreakpilotDrive
{
[Serializable]
public class Achievement
{
public string id;
public string name;
public string description;
public string icon;
public string category;
public int threshold;
public int progress;
public bool unlocked;
}
[Serializable]
public class AchievementResponse
{
public string user_id;
public int total;
public int unlocked_count;
public Achievement[] achievements;
}
[Serializable]
private class AchievementArrayWrapper
{
public Achievement[] achievements;
}
public class AchievementManager : MonoBehaviour
{
public static AchievementManager Instance { get; private set; }
[Header("UI References")]
[SerializeField] private GameObject achievementPopupPrefab;
[SerializeField] private Transform popupContainer;
[SerializeField] private float popupDuration = 3f;
[Header("Audio")]
[SerializeField] private AudioClip unlockSound;
// Events
public UnityEvent<Achievement> OnAchievementUnlocked;
// Cached data
private List<Achievement> achievements = new List<Achievement>();
private HashSet<string> previouslyUnlocked = new HashSet<string>();
private Queue<Achievement> popupQueue = new Queue<Achievement>();
private bool isShowingPopup = false;
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
// ==============================================
// Load Achievements from API
// ==============================================
public IEnumerator LoadAchievements(string userId, Action<AchievementResponse> onComplete = null)
{
string url = $"{GetBaseUrl()}/achievements/{userId}";
using (var request = UnityEngine.Networking.UnityWebRequest.Get(url))
{
// Add auth header if available
string authHeader = AuthManager.Instance?.GetAuthHeader();
if (!string.IsNullOrEmpty(authHeader))
{
request.SetRequestHeader("Authorization", authHeader);
}
yield return request.SendWebRequest();
if (request.result == UnityEngine.Networking.UnityWebRequest.Result.Success)
{
var response = JsonUtility.FromJson<AchievementResponse>(request.downloadHandler.text);
// Store previously unlocked for comparison
previouslyUnlocked.Clear();
foreach (var a in achievements)
{
if (a.unlocked)
previouslyUnlocked.Add(a.id);
}
// Update achievements
achievements.Clear();
if (response.achievements != null)
{
achievements.AddRange(response.achievements);
}
onComplete?.Invoke(response);
}
else
{
Debug.LogWarning($"Failed to load achievements: {request.error}");
onComplete?.Invoke(null);
}
}
}
// ==============================================
// Check for New Achievements
// ==============================================
public void CheckForNewUnlocks()
{
foreach (var achievement in achievements)
{
if (achievement.unlocked && !previouslyUnlocked.Contains(achievement.id))
{
// New unlock!
ShowAchievementPopup(achievement);
OnAchievementUnlocked?.Invoke(achievement);
}
}
}
// Call after each game session to check for new achievements
public IEnumerator RefreshAndCheckAchievements(string userId)
{
yield return LoadAchievements(userId, (response) =>
{
if (response != null)
{
CheckForNewUnlocks();
}
});
}
// ==============================================
// Achievement Popup
// ==============================================
public void ShowAchievementPopup(Achievement achievement)
{
popupQueue.Enqueue(achievement);
if (!isShowingPopup)
{
StartCoroutine(ProcessPopupQueue());
}
}
private IEnumerator ProcessPopupQueue()
{
isShowingPopup = true;
while (popupQueue.Count > 0)
{
var achievement = popupQueue.Dequeue();
yield return ShowSinglePopup(achievement);
yield return new WaitForSeconds(0.3f); // Gap between popups
}
isShowingPopup = false;
}
private IEnumerator ShowSinglePopup(Achievement achievement)
{
Debug.Log($"[Achievement] Unlocked: {achievement.name}");
// Play sound
if (unlockSound != null && AudioManager.Instance != null)
{
AudioManager.Instance.PlaySFX(unlockSound);
}
// Create popup UI
if (achievementPopupPrefab != null && popupContainer != null)
{
var popup = Instantiate(achievementPopupPrefab, popupContainer);
var popupScript = popup.GetComponent<AchievementPopup>();
if (popupScript != null)
{
popupScript.Setup(achievement);
}
yield return new WaitForSeconds(popupDuration);
if (popup != null)
{
Destroy(popup);
}
}
else
{
// Fallback: just wait
yield return new WaitForSeconds(popupDuration);
}
}
// ==============================================
// Public API
// ==============================================
public List<Achievement> GetAllAchievements()
{
return new List<Achievement>(achievements);
}
public List<Achievement> GetUnlockedAchievements()
{
return achievements.FindAll(a => a.unlocked);
}
public List<Achievement> GetLockedAchievements()
{
return achievements.FindAll(a => !a.unlocked);
}
public List<Achievement> GetAchievementsByCategory(string category)
{
return achievements.FindAll(a => a.category == category);
}
public int GetUnlockedCount()
{
int count = 0;
foreach (var a in achievements)
{
if (a.unlocked) count++;
}
return count;
}
public int GetTotalCount()
{
return achievements.Count;
}
public float GetCompletionPercentage()
{
if (achievements.Count == 0) return 0f;
return (float)GetUnlockedCount() / achievements.Count * 100f;
}
public Achievement GetAchievement(string id)
{
return achievements.Find(a => a.id == id);
}
// ==============================================
// Icon Mapping
// ==============================================
public Sprite GetIconSprite(string iconName)
{
// Load from Resources/Icons/Achievements/
string path = $"Icons/Achievements/{iconName}";
var sprite = Resources.Load<Sprite>(path);
if (sprite == null)
{
// Fallback icon
sprite = Resources.Load<Sprite>("Icons/Achievements/default");
}
return sprite;
}
// ==============================================
// Helpers
// ==============================================
private string GetBaseUrl()
{
if (BreakpilotAPI.Instance != null)
{
return "http://localhost:8000/api/game"; // TODO: Get from BreakpilotAPI
}
return "http://localhost:8000/api/game";
}
}
// ==============================================
// Achievement Popup Component
// ==============================================
public class AchievementPopup : MonoBehaviour
{
[SerializeField] private UnityEngine.UI.Text titleText;
[SerializeField] private UnityEngine.UI.Text descriptionText;
[SerializeField] private UnityEngine.UI.Image iconImage;
public void Setup(Achievement achievement)
{
if (titleText != null)
titleText.text = achievement.name;
if (descriptionText != null)
descriptionText.text = achievement.description;
if (iconImage != null)
{
var sprite = AchievementManager.Instance?.GetIconSprite(achievement.icon);
if (sprite != null)
iconImage.sprite = sprite;
}
// Animate in
StartCoroutine(AnimateIn());
}
private IEnumerator AnimateIn()
{
var canvasGroup = GetComponent<CanvasGroup>();
if (canvasGroup != null)
{
canvasGroup.alpha = 0f;
float duration = 0.3f;
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
canvasGroup.alpha = Mathf.Lerp(0f, 1f, elapsed / duration);
yield return null;
}
canvasGroup.alpha = 1f;
}
}
}
}