You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
358 lines
13 KiB
C#
358 lines
13 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using System;
|
|
using System.Linq;
|
|
|
|
/// <summary>
|
|
/// Controls interactions with the Achievement System
|
|
/// </summary>
|
|
[System.Serializable]
|
|
public class AchievementManager : MonoBehaviour
|
|
{
|
|
[Tooltip("The number of seconds an achievement will stay on the screen after being unlocked or progress is made.")]
|
|
public float DisplayTime = 3;
|
|
[Tooltip("The total number of achievements which can be on the screen at any one time.")]
|
|
public int NumberOnScreen = 3;
|
|
[Tooltip("If true, progress notifications will display their exact progress. If false it will show the closest bracket.")]
|
|
public bool ShowExactProgress = false;
|
|
[Tooltip("If true, achievement unlocks/progress update notifications will be displayed on the player's screen.")]
|
|
public bool DisplayAchievements;
|
|
[Tooltip("The location on the screen where achievement notifications should be displayed.")]
|
|
public AchievementStackLocation StackLocation;
|
|
[Tooltip("If true, the state of all achievements will be saved without any call to the manual save function (Recommended = true)")]
|
|
public bool AutoSave;
|
|
[Tooltip("The message which will be displayed on the UI if an achievement is marked as a spoiler.")]
|
|
public string SpoilerAchievementMessage = "Hidden";
|
|
[Tooltip("The sound which plays when an achievement is unlocked is displayed to a user. Sounds are only played when Display Achievements is true.")]
|
|
public AudioClip AchievedSound;
|
|
[Tooltip("The sound which plays when a progress update is displayed to a user. Sounds are only played when Display Achievements is true.")]
|
|
public AudioClip ProgressMadeSound;
|
|
|
|
private AudioSource AudioSource;
|
|
|
|
[SerializeField] public List<AchievementState> States = new List<AchievementState>(); //List of achievement states (achieved, progress and last notification)
|
|
[SerializeField] public List<AchievementInfromation> AchievementList = new List<AchievementInfromation>(); //List of all available achievements
|
|
|
|
[Tooltip("If true, one achievement will be automatically unlocked once all others have been completed")]
|
|
public bool UseFinalAchievement = false;
|
|
[Tooltip("The key of the final achievement")]
|
|
public string FinalAchievementKey;
|
|
|
|
public static AchievementManager instance = null; //Singleton Instance
|
|
public AchievenmentStack Stack;
|
|
|
|
void Awake()
|
|
{
|
|
if (instance == null)
|
|
{
|
|
instance = this;
|
|
}
|
|
else if (instance != this)
|
|
{
|
|
Destroy(gameObject);
|
|
}
|
|
DontDestroyOnLoad(gameObject);
|
|
AudioSource = gameObject.GetComponent<AudioSource>();
|
|
Stack = GetComponentInChildren<AchievenmentStack>();
|
|
LoadAchievementState();
|
|
}
|
|
|
|
private void PlaySound (AudioClip Sound)
|
|
{
|
|
if(AudioSource != null)
|
|
{
|
|
AudioSource.clip = Sound;
|
|
AudioSource.Play();
|
|
}
|
|
}
|
|
# region Miscellaneous
|
|
/// <summary>
|
|
/// Does an achievement exist in the list
|
|
/// </summary>
|
|
/// <param name="Key">The Key of the achievement to test</param>
|
|
/// <returns>true : if exists. false : does not exist</returns>
|
|
public bool AchievementExists(string Key)
|
|
{
|
|
return AchievementExists(AchievementList.FindIndex(x => x.Key.Equals(Key)));
|
|
}
|
|
/// <summary>
|
|
/// Does an achievement exist in the list
|
|
/// </summary>
|
|
/// <param name="Index">The index of the achievement to test</param>
|
|
/// <returns>true : if exists. false : does not exist</returns>
|
|
public bool AchievementExists(int Index)
|
|
{
|
|
return Index <= AchievementList.Count && Index >= 0;
|
|
}
|
|
/// <summary>
|
|
/// Returns the total number of achievements which have been unlocked.
|
|
/// </summary>
|
|
public int GetAchievedCount()
|
|
{
|
|
int Count = (from AchievementState i in States
|
|
where i.Achieved == true
|
|
select i).Count();
|
|
return Count;
|
|
}
|
|
/// <summary>
|
|
/// Returns the current percentage of unlocked achievements.
|
|
/// </summary>
|
|
public float GetAchievedPercentage()
|
|
{
|
|
if(States.Count == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
return (float)GetAchievedCount() / States.Count * 100;
|
|
}
|
|
#endregion
|
|
|
|
#region Unlock and Progress
|
|
/// <summary>
|
|
/// Fully unlocks a progression or goal achievement.
|
|
/// </summary>
|
|
/// <param name="Key">The Key of the achievement to be unlocked</param>
|
|
public void Unlock(string Key)
|
|
{
|
|
Unlock(FindAchievementIndex(Key));
|
|
}
|
|
/// <summary>
|
|
/// Fully unlocks a progression or goal achievement.
|
|
/// </summary>
|
|
/// <param name="Index">The index of the achievement to be unlocked</param>
|
|
public void Unlock(int Index)
|
|
{
|
|
if (!States[Index].Achieved)
|
|
{
|
|
// Set current progress to the achievement's progress goal
|
|
States[Index].Progress = AchievementList[Index].ProgressGoal;
|
|
|
|
// Check if the achievement is recursive
|
|
if (AchievementList[Index].isRecursive)
|
|
{
|
|
// Update the current achievement's progress goal
|
|
var achievementInfromation = AchievementList[Index];
|
|
achievementInfromation.ProgressGoal += AchievementList[Index].recursiveAddition;
|
|
|
|
// Check if the new progress goal has reached or exceeded 50
|
|
if (AchievementList[Index].ProgressGoal >= 50)
|
|
{
|
|
// Mark achievement as completed
|
|
States[Index].Achieved = true; // Mark as achieved
|
|
States[Index].Progress = AchievementList[Index].ProgressGoal; // Set progress to goal
|
|
}
|
|
else
|
|
{
|
|
// Reset progress for the next goal
|
|
States[Index].Progress = 0;
|
|
}
|
|
|
|
// Notify the reward system about the achievement modification
|
|
// NotifyRewardSystem(Index); // Implement this method to handle rewards
|
|
}
|
|
else
|
|
{
|
|
// For non-recursive achievements, mark as achieved directly
|
|
States[Index].Achieved = true; // Mark as achieved
|
|
DisplayUnlock(Index); // Display achievement notification
|
|
}
|
|
|
|
// Save the updated states
|
|
AutoSaveStates(); // Save states automatically if enabled
|
|
|
|
// Check for final achievement
|
|
if (UseFinalAchievement)
|
|
{
|
|
int Find = States.FindIndex(x => !x.Achieved);
|
|
bool CompletedAll = (Find == -1 || AchievementList[Find].Key.Equals(FinalAchievementKey));
|
|
if (CompletedAll)
|
|
{
|
|
Unlock(FinalAchievementKey); // Unlock the final achievement if all others are completed
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the progress of an achievement to a specific value.
|
|
/// </summary>
|
|
/// <param name="Key">The Key of the achievement</param>
|
|
/// <param name="Progress">Set progress to this value</param>
|
|
public void SetAchievementProgress(string Key, float Progress)
|
|
{
|
|
SetAchievementProgress(FindAchievementIndex(Key), Progress);
|
|
}
|
|
/// <summary>
|
|
/// Set the progress of an achievement to a specific value.
|
|
/// </summary>
|
|
/// <param name="Index">The index of the achievement</param>
|
|
/// <param name="Progress">Set progress to this value</param>
|
|
public void SetAchievementProgress(int Index, float Progress)
|
|
{
|
|
if(AchievementList[Index].Progression)
|
|
{
|
|
if (States[Index].Progress >= AchievementList[Index].ProgressGoal)
|
|
{
|
|
Unlock(Index);
|
|
}
|
|
else
|
|
{
|
|
States[Index].Progress = Progress;
|
|
DisplayUnlock(Index);
|
|
AutoSaveStates();
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Adds the input amount of progress to an achievement. Clamps achievement progress to its max value.
|
|
/// </summary>
|
|
/// <param name="Key">The Key of the achievement</param>
|
|
/// <param name="Progress">Add this number to progress</param>
|
|
public void AddAchievementProgress(string Key, float Progress)
|
|
{
|
|
AddAchievementProgress(FindAchievementIndex(Key), Progress);
|
|
}
|
|
/// <summary>
|
|
/// Adds the input amount of progress to an achievement. Clamps achievement progress to its max value.
|
|
/// </summary>
|
|
/// <param name="Index">The index of the achievement</param>
|
|
/// <param name="Progress">Add this number to progress</param>
|
|
public void AddAchievementProgress(int Index, float Progress)
|
|
{
|
|
if (AchievementList[Index].Progression)
|
|
{
|
|
if (States[Index].Progress + Progress >= AchievementList[Index].ProgressGoal)
|
|
{
|
|
Unlock(Index);
|
|
}
|
|
else
|
|
{
|
|
States[Index].Progress += Progress;
|
|
DisplayUnlock(Index);
|
|
AutoSaveStates();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public AchievementState GetAchievementState(string key)
|
|
{
|
|
return States[FindAchievementIndex(key)];
|
|
}
|
|
|
|
public AchievementInfromation GetAchivementFromKey(string key)
|
|
{
|
|
return AchievementList[FindAchievementIndex(key)];
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Saving and Loading
|
|
/// <summary>
|
|
/// Saves progress and achieved states to player prefs. Used to allow reload of data between game loads. This function is automatically called if the Auto Save setting is set to true.
|
|
/// </summary>
|
|
public void SaveAchievementState()
|
|
{
|
|
for (int i = 0; i < States.Count; i++)
|
|
{
|
|
PlayerPrefs.SetString("AchievementState_" + i, JsonUtility.ToJson(States[i]));
|
|
PlayerPrefs.SetString("AchievementInfo_" + i, JsonUtility.ToJson(AchievementList[i]));
|
|
}
|
|
PlayerPrefs.Save(); // Ensure data is saved
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads all progress and achievement states from player prefs. This function is automatically called if the Auto Load setting is set to true.
|
|
/// </summary>
|
|
public void LoadAchievementState()
|
|
{
|
|
States.Clear();
|
|
AchievementList.Clear(); // Clear existing data
|
|
|
|
int i = 0; // Counter for achievement index
|
|
while (PlayerPrefs.HasKey("AchievementState_" + i))
|
|
{
|
|
AchievementState loadedState = JsonUtility.FromJson<AchievementState>(PlayerPrefs.GetString("AchievementState_" + i));
|
|
States.Add(loadedState);
|
|
|
|
AchievementInfromation loadedInfo = JsonUtility.FromJson<AchievementInfromation>(PlayerPrefs.GetString("AchievementInfo_" + i));
|
|
AchievementList.Add(loadedInfo);
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears all saved progress and achieved states.
|
|
/// </summary>
|
|
public void ResetAchievementState()
|
|
{
|
|
States.Clear();
|
|
for (int i = 0; i < AchievementList.Count; i++)
|
|
{
|
|
PlayerPrefs.DeleteKey("AchievementState_" + i);
|
|
PlayerPrefs.DeleteKey("AchievementInfo_" + i);
|
|
States.Add(new AchievementState());
|
|
}
|
|
SaveAchievementState();
|
|
}
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Find the index of an achievement with a cetain key
|
|
/// </summary>
|
|
/// <param name="Key">Key of achievevment</param>
|
|
private int FindAchievementIndex(string Key)
|
|
{
|
|
int index = AchievementList.FindIndex(x => x.Key.Equals(Key));
|
|
Debug.Log("index: " + index);
|
|
if (!States[index].Achieved)
|
|
{
|
|
return index;
|
|
}
|
|
return -1;
|
|
}
|
|
/// <summary>
|
|
/// Test if AutoSave is valid. If true, save list
|
|
/// </summary>
|
|
private void AutoSaveStates()
|
|
{
|
|
if (AutoSave)
|
|
{
|
|
SaveAchievementState();
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Display achievements progress to screen
|
|
/// </summary>
|
|
/// <param name="Index">Index of achievement to display</param>
|
|
private void DisplayUnlock(int Index)
|
|
{
|
|
if (DisplayAchievements && !AchievementList[Index].Spoiler || States[Index].Achieved)
|
|
{
|
|
//If not achieved
|
|
if (AchievementList[Index].Progression && States[Index].Progress < AchievementList[Index].ProgressGoal)
|
|
{
|
|
int Steps = (int)AchievementList[Index].ProgressGoal / (int)AchievementList[Index].NotificationFrequency;
|
|
|
|
//Loop through all notification point backwards from last possible option
|
|
for (int i = Steps; i > States[Index].LastProgressUpdate; i--)
|
|
{
|
|
//When it finds the largest valid notification point
|
|
if (States[Index].Progress >= AchievementList[Index].NotificationFrequency * i)
|
|
{
|
|
PlaySound(ProgressMadeSound);
|
|
States[Index].LastProgressUpdate = i;
|
|
Stack.ScheduleAchievementDisplay(Index);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PlaySound(AchievedSound);
|
|
Stack.ScheduleAchievementDisplay(Index);
|
|
}
|
|
}
|
|
}
|
|
} |