using System.Collections; using System.Collections.Generic; using System; using System.Linq; using UnityEngine; using UnityEngine.AddressableAssets; using System.Runtime.CompilerServices; using UnityEditor; namespace NEG.Utils.Achievments { using AchievmentTypes; public class AchievmentManager { public class Builder { public const string DefaultAchivmentsConfigLabel = "Achivments"; private AchievmentManager manager = new AchievmentManager(); private IAchivmentStorage storage; public static Builder FromDefaultConfig() { return FromLabeledConfig(DefaultAchivmentsConfigLabel); } public static Builder FromLabeledConfig(string label) { var builder = new Builder(); var handle = Addressables.LoadAssetsAsync((IEnumerable)new string[] { label }, delegate { }, Addressables.MergeMode.Union, false); var configs = handle.WaitForCompletion(); foreach (var config in configs) { config.Apply(builder); } foreach (var config in configs) { config.ApplyLast(builder); } Addressables.Release(handle); return builder; } public Builder WithLabeledDefinitions(string label) { var handle = Addressables.LoadAssetsAsync((IEnumerable)new string[] { label }, delegate { }, Addressables.MergeMode.Union, false); var achivmentCollections = handle.WaitForCompletion(); foreach (var achivmentCollection in achivmentCollections) WithDefinitionsFrom(achivmentCollection); return this; } public Builder WithDefinitionsFrom(AchivmentDefinitionCollection collection) { foreach (var def in collection.Achivments) { manager.RegisterAchivment(def); } return this; } public Builder LoadedFrom(IAchivmentStorage storageIn) { if (storage != null) { throw new ApplicationException("Cannot Load achivment data from multiple storage instances"); } this.storage = storageIn; return this; } public Builder WithCallbackReciever(IAchievmentCallbackReciever callbackReciever) { manager.AddCallbackReciever(callbackReciever); return this; } public AchievmentManager Build() { //TODO /*if (storage != null) { manager.LoadFrom(storage); }*/ return manager; } } public delegate void AchievmentCompletedCallback(AchievmentData achivment); public delegate void AchievmentStateChangedCallback(AchievmentData achivment); public event AchievmentCompletedCallback AchievmentCompleted; public event AchievmentStateChangedCallback AchievmentStateChanged; private Dictionary definitionCache; private Dictionary dataCache; private AchievmentManager() { definitionCache = new Dictionary(); dataCache = new Dictionary(); } private void RegisterAchivment(AchievmentDefinition definition) { if (!definitionCache.ContainsKey(definition.Id)) { definitionCache.Add(definition.Id, definition); dataCache.Add(definition, definition.Construct()); } else { Debug.LogWarning($"Duplicate Achivment with ID: {definition.Id}"); } } /*private void LoadFrom(IAchivmentStorage achivmentStorage) { foreach (var entry in definitionCache) { var storedProgress = achivmentStorage.GetStoredAchivmentProgress(entry.Key); dataCache[entry.Value].ProgressLeft = storedProgress; } }*/ public void AddCallbackRecievers(IEnumerable initialCallbacks) { foreach (var callback in initialCallbacks) { AddCallbackReciever(callback); } } public void AddCallbackReciever(IAchievmentCallbackReciever callback) { AchievmentCompleted += callback.AchievmentCompleted; AchievmentStateChanged += callback.AchievmentStateChanged; } public void RemoveCallbackReciever(IAchievmentCallbackReciever callback) { AchievmentCompleted -= callback.AchievmentCompleted; AchievmentStateChanged -= callback.AchievmentStateChanged; } #region Achievment Manipulation (Sets, Gets) /// /// Returns if an achivment at a given id is completed /// /// /// public bool IsCompleted(string id) { return GetAchievmentForId(id).IsCompleted; } #region Toggle /// /// Sets a as completed, after which sends , also if the achievment is completed sends a .
/// If there is no achievment at a given id throws an . If achievment at a given id is of a wrong tupe throws an ///
/// public void SetToggleAchivment(string id) { ManipulateAchievment(id, (achievment) => achievment.CompletionState = true); } /// /// Gets a completion state from a .
/// If there is no achievment at a given id throws an . If achievment at a given id is of a wrong tupe throws an ///
/// public bool GetToggleState(string id) { return GetAchievmentForId(id).CompletionState; } #endregion #region Int /// /// Sets progress of a given to , after which sends , also if the achievment is completed sends a .
/// If there is no achievment at a given id throws an . If achievment at a given id is of a wrong tupe throws an . ///
/// public void SetIntProgress(string id, int progress) { ManipulateAchievment(id, (achievment) => achievment.CurrentProgress = progress); } /// /// Changes progress of a given by , after which sends , also if the achievment is completed sends a .
/// If there is no achievment at a given id throws an . If achievment at a given id is of a wrong tupe throws an ///
/// public void CangeIntProgress(string id, int delta) { ManipulateAchievment(id, (achievment) => achievment.CurrentProgress += delta); } /// /// Gets current progress from a .
/// If there is no achievment at a given id throws an . If achievment at a given id is of a wrong tupe throws an ///
/// public int GetIntProgress(string id) { return GetAchievmentForId(id).CurrentProgress; } #endregion #region Float /// /// Sets progress of a given to , after which sends , also if the achievment is completed sends a .
/// If there is no achievment at a given id throws an . If achievment at a given id is of a wrong tupe throws an ///
/// public void SetFloatProgress(string id, float progress) { ManipulateAchievment(id, (achievment) => achievment.CurrentProgress = progress); } /// /// Changes progress of a given by , after which sends , also if the achievment is completed sends a .
/// If there is no achievment at a given id throws an . If achievment at a given id is of a wrong tupe throws an ///
/// public void CangeFloatProgress(string id, float delta) { ManipulateAchievment(id, (achievment) => achievment.CurrentProgress += delta); } /// /// Gets current progress from a .
/// If there is no achievment at a given id throws an . If achievment at a given id is of a wrong tupe throws an ///
/// public float GetFloatProgress(string id) { return GetAchievmentForId(id).CurrentProgress; } #endregion #endregion /// /// Returns an achievment of type under /// /// Type of the achievment /// Id of requested achievment /// throws an if either there is no achievment under id or achievment under id is of a different type public T GetAchievmentForId(string id) where T : AchievmentData { return CheckAchievmentType(GetAchievmentForId(id)); } /// /// Returns an achievment under /// /// Id of requested achievment /// throws an if there is no achievment under id public AchievmentData GetAchievmentForId(string id) { var def = definitionCache.GetValueOrDefault(id); if (def != null) { return dataCache[def]; } else { throw new ApplicationException($"Invalid achivment id {id}"); } } private T CheckAchievmentType(AchievmentData data) where T : AchievmentData { if (data is not T convetred) { throw new ApplicationException($"Attempting to perform an operation on an invalid achievment type. Expected {typeof(T)} got {data.GetType()}"); } return convetred; } /// /// /// /// /// /// Action to perform on the achievment private void ManipulateAchievment(string id, Action manipulation) where T : AchievmentData { var data = GetAchievmentForId(id); if (CheckNotCompleted(data)) { return; } manipulation(data); SendUpdateCallbacks(data); } /// /// Helper method to print a warning if an achievment is already completed /// /// /// Completion state private bool CheckNotCompleted(AchievmentData data) { if (data.IsCompleted) { Debug.LogWarning($"Achievment already completed: {data.Achivment.Id}"); } return data.IsCompleted; } private void SendUpdateCallbacks(AchievmentData data) { AchievmentStateChanged?.Invoke(data); if (data.IsCompleted) { AchievmentCompleted?.Invoke(data); } } } }