From 991c9ccdcca2f063dcec799fffcef9eda67b6298 Mon Sep 17 00:00:00 2001 From: LubieKakao1212 Date: Wed, 21 Dec 2022 15:13:55 +0100 Subject: [PATCH] Achivment system (untested) --- Achivments.meta | 8 + Achivments/AchivmentData.cs | 22 ++ Achivments/AchivmentData.cs.meta | 11 + Achivments/AchivmentDefinition.cs | 34 ++++ Achivments/AchivmentDefinition.cs.meta | 11 + Achivments/AchivmentDefinitionCollection.cs | 13 ++ .../AchivmentDefinitionCollection.cs.meta | 11 + Achivments/AchivmentManager.cs | 191 ++++++++++++++++++ Achivments/AchivmentManager.cs.meta | 11 + Achivments/AchivmentManagerConfig.cs | 25 +++ Achivments/AchivmentManagerConfig.cs.meta | 11 + Achivments/Backend.meta | 8 + Achivments/IAchivmentCallbackReciever.cs | 23 +++ Achivments/IAchivmentCallbackReciever.cs.meta | 11 + Achivments/IAchivmentManagerConfig.cs | 21 ++ Achivments/IAchivmentManagerConfig.cs.meta | 11 + Achivments/IAchivmentStorage.cs | 11 + Achivments/IAchivmentStorage.cs.meta | 11 + Achivments/NEG.Utils.Achivments.asmdef | 17 ++ Achivments/NEG.Utils.Achivments.asmdef.meta | 7 + 20 files changed, 468 insertions(+) create mode 100644 Achivments.meta create mode 100644 Achivments/AchivmentData.cs create mode 100644 Achivments/AchivmentData.cs.meta create mode 100644 Achivments/AchivmentDefinition.cs create mode 100644 Achivments/AchivmentDefinition.cs.meta create mode 100644 Achivments/AchivmentDefinitionCollection.cs create mode 100644 Achivments/AchivmentDefinitionCollection.cs.meta create mode 100644 Achivments/AchivmentManager.cs create mode 100644 Achivments/AchivmentManager.cs.meta create mode 100644 Achivments/AchivmentManagerConfig.cs create mode 100644 Achivments/AchivmentManagerConfig.cs.meta create mode 100644 Achivments/Backend.meta create mode 100644 Achivments/IAchivmentCallbackReciever.cs create mode 100644 Achivments/IAchivmentCallbackReciever.cs.meta create mode 100644 Achivments/IAchivmentManagerConfig.cs create mode 100644 Achivments/IAchivmentManagerConfig.cs.meta create mode 100644 Achivments/IAchivmentStorage.cs create mode 100644 Achivments/IAchivmentStorage.cs.meta create mode 100644 Achivments/NEG.Utils.Achivments.asmdef create mode 100644 Achivments/NEG.Utils.Achivments.asmdef.meta diff --git a/Achivments.meta b/Achivments.meta new file mode 100644 index 0000000..b169e48 --- /dev/null +++ b/Achivments.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ff9bb206aea50d14997771b9a0cc2b04 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Achivments/AchivmentData.cs b/Achivments/AchivmentData.cs new file mode 100644 index 0000000..274abbd --- /dev/null +++ b/Achivments/AchivmentData.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json.Linq; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace NEG.Utils.Achivments +{ + public class AchivmentData + { + public AchivmentDefinition Achivment { get; private set; } + + public int ProgressLeft { get; internal set; } + + public bool IsCompleted => ProgressLeft <= 0; + + public AchivmentData(AchivmentDefinition achivment, int progressLeft) + { + Achivment = achivment; + ProgressLeft = progressLeft; + } + } +} \ No newline at end of file diff --git a/Achivments/AchivmentData.cs.meta b/Achivments/AchivmentData.cs.meta new file mode 100644 index 0000000..b67c171 --- /dev/null +++ b/Achivments/AchivmentData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fad96d35cce2d3e469d300e6e48a048a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Achivments/AchivmentDefinition.cs b/Achivments/AchivmentDefinition.cs new file mode 100644 index 0000000..703d34d --- /dev/null +++ b/Achivments/AchivmentDefinition.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json.Linq; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace NEG.Utils.Achivments +{ + [CreateAssetMenu(menuName = "Achivments/AchivmentDefinition")] + public class AchivmentDefinition : ScriptableObject + { + public JToken AdditionalData + { + get + { + additionalData ??= JObject.Parse(additionalDataInitializer); + return additionalData; + } + } + + [field: SerializeField] + public string Id { get; private set; } + + [field: Tooltip("Amount of progress required for completion, required to be at leas 1, otherwise would be considered completed from the beginning")] + [field: Min(1)] + [field: SerializeField] + public int ProgressRequired { get; private set; } = 1; + + [SerializeField] + [Tooltip("Temporary until json editor is a thing")] + private string additionalDataInitializer; + + private JToken additionalData; + } +} \ No newline at end of file diff --git a/Achivments/AchivmentDefinition.cs.meta b/Achivments/AchivmentDefinition.cs.meta new file mode 100644 index 0000000..aaaa367 --- /dev/null +++ b/Achivments/AchivmentDefinition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f9bd42dd58472044b8ecc0d69caa7da8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Achivments/AchivmentDefinitionCollection.cs b/Achivments/AchivmentDefinitionCollection.cs new file mode 100644 index 0000000..9feb592 --- /dev/null +++ b/Achivments/AchivmentDefinitionCollection.cs @@ -0,0 +1,13 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace NEG.Utils.Achivments +{ + [CreateAssetMenu(menuName = "Achivments/Collection")] + public class AchivmentDefinitionCollection : ScriptableObject + { + [field: SerializeField] + public List Achivments { get; private set; } = new List(); + } +} \ No newline at end of file diff --git a/Achivments/AchivmentDefinitionCollection.cs.meta b/Achivments/AchivmentDefinitionCollection.cs.meta new file mode 100644 index 0000000..59d4a05 --- /dev/null +++ b/Achivments/AchivmentDefinitionCollection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ef37a873be859d042bda22dee624e429 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Achivments/AchivmentManager.cs b/Achivments/AchivmentManager.cs new file mode 100644 index 0000000..ead9a9a --- /dev/null +++ b/Achivments/AchivmentManager.cs @@ -0,0 +1,191 @@ +using System.Collections; +using System.Collections.Generic; +using System; +using UnityEngine; +using UnityEngine.AddressableAssets; +using System.Runtime.CompilerServices; + +namespace NEG.Utils.Achivments +{ + public class AchivmentManager + { + public class Builder + { + public const string DefaultAchivmentsConfigLabel = "Achivments"; + + private AchivmentManager manager = new AchivmentManager(); + private IAchivmentStorage storage; + + public static Builder FromDefaultConfig() + { + return FromLabeledConfig(DefaultAchivmentsConfigLabel); + } + + public static Builder FromLabeledConfig(string label) + { + var builder = new Builder(); + + var handle = Addressables.LoadAssetsAsync(DefaultAchivmentsConfigLabel, 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(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(IAchivmentCallbackReciever callbackReciever) + { + manager.AddCallbackReciever(callbackReciever); + return this; + } + + public AchivmentManager Build() + { + if (storage != null) + { + manager.LoadFrom(storage); + } + return manager; + } + } + + public delegate void AchivmentSetCallback(AchivmentData achivment); + public delegate void AchivmentProgressSetCallback(AchivmentData achivment, int progressLeft); + + public event AchivmentSetCallback AchivmentSet; + public event AchivmentProgressSetCallback AchivmentProgressSet; + + private Dictionary definitionCache; + private Dictionary dataCache; + + private AchivmentManager() + { + definitionCache = new Dictionary(); + dataCache = new Dictionary(); + } + + private void RegisterAchivment(AchivmentDefinition definition) + { + if (!definitionCache.ContainsKey(definition.Id)) + { + definitionCache.Add(definition.Id, definition); + dataCache.Add(definition, new AchivmentData(definition, definition.ProgressRequired)); + } + 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(IAchivmentCallbackReciever callback) + { + AchivmentSet += callback.SetAchivment; + AchivmentProgressSet += callback.SetAchivmentProgress; + } + + public void RemoveCallbackReciever(IAchivmentCallbackReciever callback) + { + AchivmentSet -= callback.SetAchivment; + AchivmentProgressSet -= callback.SetAchivmentProgress; + } + + public void SetAchivment(string id) + { + var data = GetAchivmentForId(id); + data.ProgressLeft = 0; + AchivmentSet?.Invoke(data); + } + + public void AddProgress(string id, int amount) + { + var data = GetAchivmentForId(id); + if (data.IsCompleted) + { + return; + } + data.ProgressLeft = Mathf.Max(data.ProgressLeft - amount, 0); + AchivmentProgressSet?.Invoke(data, data.Achivment.ProgressRequired - data.ProgressLeft); + + if (data.IsCompleted) + { + AchivmentSet?.Invoke(data); + } + } + + private AchivmentData GetAchivmentForId(string id) + { + var def = definitionCache.GetValueOrDefault(id); + if (def != null) + { + return dataCache[def]; + } + else + { + throw new ApplicationException("Invalid achivment id"); + } + } + } +} \ No newline at end of file diff --git a/Achivments/AchivmentManager.cs.meta b/Achivments/AchivmentManager.cs.meta new file mode 100644 index 0000000..fc0282b --- /dev/null +++ b/Achivments/AchivmentManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 841f326ebfd59e646835bf81432d3ae4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Achivments/AchivmentManagerConfig.cs b/Achivments/AchivmentManagerConfig.cs new file mode 100644 index 0000000..19402d8 --- /dev/null +++ b/Achivments/AchivmentManagerConfig.cs @@ -0,0 +1,25 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace NEG.Utils.Achivments +{ + [CreateAssetMenu(menuName = "Achivments/BaseConfig")] + public class AchivmentManagerConfig : ScriptableObject, IAchivmentManagerConfig + { + public const string DefaultAchivmentsCollectionLabel = "Achivments"; + + [field: SerializeField] + public string AchivmentCollectionAssetLabel { get; private set; } = DefaultAchivmentsCollectionLabel; + + public void Apply(AchivmentManager.Builder builder) + { + builder.WithLabeledDefinitions(AchivmentCollectionAssetLabel); + } + + public void ApplyLast(AchivmentManager.Builder builder) + { + + } + } +} \ No newline at end of file diff --git a/Achivments/AchivmentManagerConfig.cs.meta b/Achivments/AchivmentManagerConfig.cs.meta new file mode 100644 index 0000000..3e43f82 --- /dev/null +++ b/Achivments/AchivmentManagerConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 88120b6e616164f489387a6a32a25dee +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Achivments/Backend.meta b/Achivments/Backend.meta new file mode 100644 index 0000000..4ba2000 --- /dev/null +++ b/Achivments/Backend.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 48ec3460d89c2f144827761002d7f4d9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Achivments/IAchivmentCallbackReciever.cs b/Achivments/IAchivmentCallbackReciever.cs new file mode 100644 index 0000000..e57defb --- /dev/null +++ b/Achivments/IAchivmentCallbackReciever.cs @@ -0,0 +1,23 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace NEG.Utils.Achivments +{ + public interface IAchivmentCallbackReciever + { + /// + /// Called when an achivment is completed + /// + /// + /// + void SetAchivment(AchivmentData achivment); + + /// + /// Called when achivment progress changes + /// + /// + /// Current achivment progress, equal to - + void SetAchivmentProgress(AchivmentData achivment, int progress); + } +} \ No newline at end of file diff --git a/Achivments/IAchivmentCallbackReciever.cs.meta b/Achivments/IAchivmentCallbackReciever.cs.meta new file mode 100644 index 0000000..beb55a4 --- /dev/null +++ b/Achivments/IAchivmentCallbackReciever.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7218fed73c5be2c4ba31eca9fe44d37a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Achivments/IAchivmentManagerConfig.cs b/Achivments/IAchivmentManagerConfig.cs new file mode 100644 index 0000000..10351e1 --- /dev/null +++ b/Achivments/IAchivmentManagerConfig.cs @@ -0,0 +1,21 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace NEG.Utils.Achivments +{ + public interface IAchivmentManagerConfig + { + /// + /// Used to Apply config data + /// + /// + void Apply(AchivmentManager.Builder builder); + + /// + /// Called after was called on every other config + /// + /// + void ApplyLast(AchivmentManager.Builder builder); + } +} \ No newline at end of file diff --git a/Achivments/IAchivmentManagerConfig.cs.meta b/Achivments/IAchivmentManagerConfig.cs.meta new file mode 100644 index 0000000..2fa22ce --- /dev/null +++ b/Achivments/IAchivmentManagerConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd4b0d54603883447b6458263d6b3605 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Achivments/IAchivmentStorage.cs b/Achivments/IAchivmentStorage.cs new file mode 100644 index 0000000..eade987 --- /dev/null +++ b/Achivments/IAchivmentStorage.cs @@ -0,0 +1,11 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace NEG.Utils.Achivments +{ + public interface IAchivmentStorage + { + public int GetStoredAchivmentProgress(string id); + } +} \ No newline at end of file diff --git a/Achivments/IAchivmentStorage.cs.meta b/Achivments/IAchivmentStorage.cs.meta new file mode 100644 index 0000000..3204777 --- /dev/null +++ b/Achivments/IAchivmentStorage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cfab45a1ce7cc0a4899f35a61b6a60f6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Achivments/NEG.Utils.Achivments.asmdef b/Achivments/NEG.Utils.Achivments.asmdef new file mode 100644 index 0000000..85119ec --- /dev/null +++ b/Achivments/NEG.Utils.Achivments.asmdef @@ -0,0 +1,17 @@ +{ + "name": "NEG.Utils.Achivments", + "rootNamespace": "", + "references": [ + "GUID:9e24947de15b9834991c9d8411ea37cf", + "GUID:84651a3751eca9349aac36a66bba901b" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Achivments/NEG.Utils.Achivments.asmdef.meta b/Achivments/NEG.Utils.Achivments.asmdef.meta new file mode 100644 index 0000000..14bc2c4 --- /dev/null +++ b/Achivments/NEG.Utils.Achivments.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 380ad496eab7ace4b98ceede94941223 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: