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 /// /// /// /// throws an if there is no achievment under id 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 .
///
/// /// throws an if there is no achievment under id or an if achievment under id is of a different type public void SetToggleAchivment(string id) { ManipulateAchievment(id, (achievment) => achievment.CompletionState = true); } /// /// Gets a completion state from a .
///
/// /// throws an if there is no achievment under id or an if achievment under id is of a different type 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 .
///
/// /// throws an if there is no achievment under id or an if achievment under id is of a different type 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 .
///
/// /// throws an if there is no achievment under id or an if achievment under id is of a different type public void ChangeIntProgress(string id, int delta) { ManipulateAchievment(id, (achievment) => achievment.CurrentProgress += delta); } /// /// Gets current progress from a .
///
/// /// throws an if there is no achievment under id or an if achievment under id is of a different type 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 .
///
/// /// throws an if there is no achievment under id or an if achievment under id is of a different type 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 .
///
/// /// throws an if there is no achievment under id or an if achievment under id is of a different type public void ChangeFloatProgress(string id, float delta) { ManipulateAchievment(id, (achievment) => achievment.CurrentProgress += delta); } /// /// Gets current progress from a .
///
/// /// throws an if there is no achievment under id or an if achievment under id is of a different type 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 there is no achievment under id or an if achievment under id is of a different type public T GetAchievmentForId(string id) where T : AchievmentData { return ValidateAchievmentType(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 AchievmentException($"Invalid achivment id {id}", id); } } private T ValidateAchievmentType(AchievmentData data) where T : AchievmentData { if (data is not T convetred) { throw new AchievmentTypeException($"Attempting to perform an operation on an invalid achievment type. Expected {typeof(T)} got {data.GetType()}", data.Achivment.Id, typeof(T), 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); } } } }