拼ui 一些业务逻辑x实现
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[Serializable]
|
||||
public class GlobalAchievementMetricEntry
|
||||
{
|
||||
public GlobalAchievementMetricType metricType;
|
||||
public float value;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class GlobalAchievementStateEntry
|
||||
{
|
||||
public int achievementId;
|
||||
public bool unlocked;
|
||||
public long unlockedUtcTicks;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class GlobalAchievementSaveData
|
||||
{
|
||||
public int version = 1;
|
||||
public string lastLoginDateStamp;
|
||||
public string trustedDateStamp;
|
||||
public List<GlobalAchievementMetricEntry> metrics = new List<GlobalAchievementMetricEntry>();
|
||||
public List<GlobalAchievementStateEntry> states = new List<GlobalAchievementStateEntry>();
|
||||
}
|
||||
|
||||
public struct GlobalAchievementSnapshot
|
||||
{
|
||||
public GlobalAchievementSO definition;
|
||||
public float currentValue;
|
||||
public float normalizedProgress;
|
||||
public bool unlocked;
|
||||
public DateTime unlockedUtc;
|
||||
public string unlockedDateTimeText;
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f9bc8ea38a77534a9d329e1992fb750
|
||||
@@ -0,0 +1,71 @@
|
||||
using UnityEngine;
|
||||
|
||||
public enum GlobalAchievementMetricType
|
||||
{
|
||||
TotalCoinsEarned = 0,
|
||||
OwnedHeroCount = 1,
|
||||
OwnedSongCount = 2,
|
||||
BestSingleSongTotalScore = 3,
|
||||
TotalSongBestScoreSum = 4,
|
||||
LoginDays = 5,
|
||||
}
|
||||
|
||||
[CreateAssetMenu(fileName = "GA_", menuName = "SO_Data/GlobalAchievement")]
|
||||
public class GlobalAchievementSO : ScriptableObject
|
||||
{
|
||||
[Header("identity")]
|
||||
public int achievementId;
|
||||
public string displayName;
|
||||
public string achievementSeriesKey;
|
||||
[Range(0, 6)] public int achievementLevel;
|
||||
|
||||
[TextArea(2, 5)]
|
||||
public string description;
|
||||
|
||||
[Header("display")]
|
||||
public Sprite icon;
|
||||
public int sortOrder;
|
||||
public bool hiddenUntilUnlocked;
|
||||
|
||||
[Header("runtime mirror")]
|
||||
[SerializeField] private bool isUnlocked;
|
||||
[SerializeField] private string unlockedDateTime;
|
||||
|
||||
[Header("trigger")]
|
||||
public GlobalAchievementMetricType metricType = GlobalAchievementMetricType.LoginDays;
|
||||
public float targetValue = 1f;
|
||||
|
||||
[Header("steam")]
|
||||
public bool syncWithSteam;
|
||||
public string steamAchievementApiName;
|
||||
|
||||
public bool IsUnlocked
|
||||
{
|
||||
get { return isUnlocked; }
|
||||
}
|
||||
|
||||
public string UnlockedDateTime
|
||||
{
|
||||
get { return unlockedDateTime; }
|
||||
}
|
||||
|
||||
public void SetUnlockedState(bool value, long unlockedUtcTicks = 0L)
|
||||
{
|
||||
isUnlocked = value;
|
||||
if (value && unlockedUtcTicks > 0L)
|
||||
{
|
||||
unlockedDateTime = new System.DateTime(unlockedUtcTicks, System.DateTimeKind.Utc).ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
else if (!value)
|
||||
{
|
||||
unlockedDateTime = string.Empty;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
UnityEditor.EditorUtility.SetDirty(this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c385da50177c22f468bf5b7012e70372
|
||||
@@ -0,0 +1,571 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public sealed class GlobalAchievementService : MonoBehaviour
|
||||
{
|
||||
private const string SaveCategory = "global_achievement";
|
||||
private const string SaveKey = "runtime_state";
|
||||
public const string DefaultResourcesPath = "so/globalachievments";
|
||||
public const string DefaultEditorAssetFolder = "Assets/Resources/so/globalachievments";
|
||||
|
||||
public static GlobalAchievementService Instance { get; private set; }
|
||||
|
||||
public event Action<GlobalAchievementSO> OnAchievementUnlocked;
|
||||
|
||||
private readonly List<GlobalAchievementSO> definitions = new List<GlobalAchievementSO>();
|
||||
private readonly Dictionary<int, GlobalAchievementStateEntry> stateById = new Dictionary<int, GlobalAchievementStateEntry>();
|
||||
private readonly Dictionary<GlobalAchievementMetricType, GlobalAchievementMetricEntry> metricByType = new Dictionary<GlobalAchievementMetricType, GlobalAchievementMetricEntry>();
|
||||
|
||||
private GlobalAchievementSaveData saveData;
|
||||
private bool initialized;
|
||||
private bool loginReportedThisSession;
|
||||
private int lastSaveFrame = -1;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
private static void Bootstrap()
|
||||
{
|
||||
EnsureInstance();
|
||||
}
|
||||
|
||||
public static GlobalAchievementService EnsureInstance()
|
||||
{
|
||||
if (Instance != null)
|
||||
{
|
||||
return Instance;
|
||||
}
|
||||
|
||||
var host = new GameObject("__global_achievement_service");
|
||||
host.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
|
||||
DontDestroyOnLoad(host);
|
||||
Instance = host.AddComponent<GlobalAchievementService>();
|
||||
return Instance;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
InitializeIfNeeded();
|
||||
}
|
||||
|
||||
private void OnApplicationPause(bool pauseStatus)
|
||||
{
|
||||
if (pauseStatus)
|
||||
{
|
||||
SaveNow();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationQuit()
|
||||
{
|
||||
SaveNow();
|
||||
}
|
||||
|
||||
public void InitializeIfNeeded()
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
GlobalAchievementSaveData loaded;
|
||||
if (!SecureSaveVault.TryLoadJson(SaveCategory, SaveKey, out loaded))
|
||||
{
|
||||
loaded = new GlobalAchievementSaveData();
|
||||
}
|
||||
|
||||
saveData = loaded ?? new GlobalAchievementSaveData();
|
||||
RebuildCaches();
|
||||
LoadDefinitionsFromResources();
|
||||
ReportLoginSessionStart();
|
||||
RefreshDerivedMetrics();
|
||||
SaveNow();
|
||||
}
|
||||
|
||||
public void LoadDefinitionsFromResources(string resourcesPath = DefaultResourcesPath)
|
||||
{
|
||||
InitializeIfNeeded();
|
||||
|
||||
definitions.Clear();
|
||||
GlobalAchievementSO[] loadedDefinitions = Resources.LoadAll<GlobalAchievementSO>(resourcesPath);
|
||||
if (loadedDefinitions != null && loadedDefinitions.Length > 0)
|
||||
{
|
||||
definitions.AddRange(loadedDefinitions);
|
||||
definitions.Sort(CompareDefinitions);
|
||||
}
|
||||
|
||||
SyncMirrorFlagsToDefinitions();
|
||||
EvaluateAll();
|
||||
}
|
||||
|
||||
public IReadOnlyList<GlobalAchievementSO> GetDefinitions()
|
||||
{
|
||||
InitializeIfNeeded();
|
||||
return definitions;
|
||||
}
|
||||
|
||||
public List<GlobalAchievementSnapshot> GetSnapshots()
|
||||
{
|
||||
InitializeIfNeeded();
|
||||
var snapshots = new List<GlobalAchievementSnapshot>(definitions.Count);
|
||||
for (int i = 0; i < definitions.Count; i++)
|
||||
{
|
||||
GlobalAchievementSO definition = definitions[i];
|
||||
if (definition == null || definition.achievementId <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GlobalAchievementStateEntry state = GetOrCreateState(definition.achievementId);
|
||||
float currentValue = GetMetric(definition.metricType);
|
||||
float targetValue = Mathf.Max(1f, definition.targetValue);
|
||||
snapshots.Add(new GlobalAchievementSnapshot
|
||||
{
|
||||
definition = definition,
|
||||
currentValue = currentValue,
|
||||
normalizedProgress = Mathf.Clamp01(currentValue / targetValue),
|
||||
unlocked = state.unlocked,
|
||||
unlockedUtc = state.unlockedUtcTicks > 0 ? new DateTime(state.unlockedUtcTicks, DateTimeKind.Utc) : default(DateTime),
|
||||
unlockedDateTimeText = state.unlockedUtcTicks > 0 ? new DateTime(state.unlockedUtcTicks, DateTimeKind.Utc).ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss") : string.Empty,
|
||||
});
|
||||
}
|
||||
|
||||
return snapshots;
|
||||
}
|
||||
|
||||
public float GetMetric(GlobalAchievementMetricType metricType)
|
||||
{
|
||||
InitializeIfNeeded();
|
||||
GlobalAchievementMetricEntry metric;
|
||||
if (metricByType.TryGetValue(metricType, out metric))
|
||||
{
|
||||
return Mathf.Max(0f, metric.value);
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public void AddMetric(GlobalAchievementMetricType metricType, float delta)
|
||||
{
|
||||
if (Mathf.Approximately(delta, 0f))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InitializeIfNeeded();
|
||||
GlobalAchievementMetricEntry metric = GetOrCreateMetric(metricType);
|
||||
float nextValue = Mathf.Max(0f, metric.value + delta);
|
||||
if (Mathf.Approximately(metric.value, nextValue))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
metric.value = nextValue;
|
||||
PersistMetricsIfChanged(true);
|
||||
}
|
||||
|
||||
public void SetMetric(GlobalAchievementMetricType metricType, float value)
|
||||
{
|
||||
InitializeIfNeeded();
|
||||
PersistMetricsIfChanged(SetMetricValue(metricType, value));
|
||||
}
|
||||
|
||||
public void SetMetricAtLeast(GlobalAchievementMetricType metricType, float value)
|
||||
{
|
||||
InitializeIfNeeded();
|
||||
GlobalAchievementMetricEntry metric = GetOrCreateMetric(metricType);
|
||||
float nextValue = Mathf.Max(0f, value);
|
||||
if (nextValue <= metric.value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
metric.value = nextValue;
|
||||
PersistMetricsIfChanged(true);
|
||||
}
|
||||
|
||||
public void ReportLoginSessionStart()
|
||||
{
|
||||
InitializeIfNeeded();
|
||||
if (loginReportedThisSession)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
loginReportedThisSession = true;
|
||||
string currentDateStamp = DateTime.Now.ToString("yyyy-MM-dd");
|
||||
if (!string.IsNullOrEmpty(saveData.trustedDateStamp) && string.CompareOrdinal(currentDateStamp, saveData.trustedDateStamp) < 0)
|
||||
{
|
||||
currentDateStamp = saveData.trustedDateStamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
saveData.trustedDateStamp = currentDateStamp;
|
||||
}
|
||||
|
||||
if (!string.Equals(saveData.lastLoginDateStamp, currentDateStamp, StringComparison.Ordinal))
|
||||
{
|
||||
saveData.lastLoginDateStamp = currentDateStamp;
|
||||
AddMetric(GlobalAchievementMetricType.LoginDays, 1f);
|
||||
return;
|
||||
}
|
||||
|
||||
SaveNow();
|
||||
}
|
||||
|
||||
public void ReportCurrentCoins(int currentCoins)
|
||||
{
|
||||
// No current-coin achievement type now, but this remains available as a central probe point
|
||||
// for future expansion without changing economy call sites.
|
||||
}
|
||||
|
||||
public void ReportCoinsEarned(int amount)
|
||||
{
|
||||
if (amount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddMetric(GlobalAchievementMetricType.TotalCoinsEarned, amount);
|
||||
}
|
||||
|
||||
public void RefreshOwnedHeroCount()
|
||||
{
|
||||
InitializeIfNeeded();
|
||||
PersistMetricsIfChanged(RefreshOwnedHeroCountInternal(true));
|
||||
}
|
||||
|
||||
public void RefreshOwnedSongCountAndScores()
|
||||
{
|
||||
InitializeIfNeeded();
|
||||
PersistMetricsIfChanged(RefreshOwnedSongCountAndScoresInternal(true));
|
||||
}
|
||||
|
||||
public void RefreshDerivedMetrics()
|
||||
{
|
||||
InitializeIfNeeded();
|
||||
StoreOwnershipLedger.EnsureInstance().ForceSyncMirrorFlags();
|
||||
|
||||
bool changed = false;
|
||||
changed |= RefreshOwnedHeroCountInternal(false);
|
||||
changed |= RefreshOwnedSongCountAndScoresInternal(false);
|
||||
PersistMetricsIfChanged(changed);
|
||||
}
|
||||
|
||||
public void SaveNow()
|
||||
{
|
||||
if (!initialized || saveData == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Application.isPlaying && lastSaveFrame == Time.frameCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lastSaveFrame = Application.isPlaying ? Time.frameCount : -1;
|
||||
SecureSaveVault.SaveJson(SaveCategory, SaveKey, saveData);
|
||||
}
|
||||
|
||||
private void EvaluateAll()
|
||||
{
|
||||
for (int i = 0; i < definitions.Count; i++)
|
||||
{
|
||||
GlobalAchievementSO definition = definitions[i];
|
||||
if (definition == null || definition.achievementId <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GlobalAchievementStateEntry state = GetOrCreateState(definition.achievementId);
|
||||
if (state.unlocked)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float currentValue = GetMetric(definition.metricType);
|
||||
if (currentValue + 0.0001f < Mathf.Max(0f, definition.targetValue))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Unlock(definition, state);
|
||||
}
|
||||
|
||||
SyncMirrorFlagsToDefinitions();
|
||||
}
|
||||
|
||||
private bool RefreshOwnedHeroCountInternal(bool syncOwnershipFirst)
|
||||
{
|
||||
if (syncOwnershipFirst)
|
||||
{
|
||||
StoreOwnershipLedger.EnsureInstance().ForceSyncMirrorFlags();
|
||||
}
|
||||
|
||||
AllyHero_SO[] heroes = Resources.LoadAll<AllyHero_SO>("so/ally");
|
||||
if (heroes == null || heroes.Length == 0)
|
||||
{
|
||||
return SetMetricValue(GlobalAchievementMetricType.OwnedHeroCount, 0f);
|
||||
}
|
||||
|
||||
var uniqueIds = new HashSet<int>();
|
||||
int unlockedCount = 0;
|
||||
for (int i = 0; i < heroes.Length; i++)
|
||||
{
|
||||
AllyHero_SO hero = heroes[i];
|
||||
if (hero == null || hero.ally_heroID <= 0 || !uniqueIds.Add(hero.ally_heroID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hero.isUnlocked)
|
||||
{
|
||||
unlockedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return SetMetricValue(GlobalAchievementMetricType.OwnedHeroCount, unlockedCount);
|
||||
}
|
||||
|
||||
private bool RefreshOwnedSongCountAndScoresInternal(bool syncOwnershipFirst)
|
||||
{
|
||||
if (syncOwnershipFirst)
|
||||
{
|
||||
StoreOwnershipLedger.EnsureInstance().ForceSyncMirrorFlags();
|
||||
}
|
||||
|
||||
SongData[] songs = Resources.LoadAll<SongData>("song_songIndex");
|
||||
if (songs == null || songs.Length == 0)
|
||||
{
|
||||
bool emptyChanged = false;
|
||||
emptyChanged |= SetMetricValue(GlobalAchievementMetricType.OwnedSongCount, 0f);
|
||||
emptyChanged |= SetMetricValue(GlobalAchievementMetricType.BestSingleSongTotalScore, 0f);
|
||||
emptyChanged |= SetMetricValue(GlobalAchievementMetricType.TotalSongBestScoreSum, 0f);
|
||||
return emptyChanged;
|
||||
}
|
||||
|
||||
var uniqueIds = new HashSet<int>();
|
||||
int ownedSongCount = 0;
|
||||
int bestSingleSongScore = 0;
|
||||
long totalSongBestScore = 0L;
|
||||
|
||||
for (int i = 0; i < songs.Length; i++)
|
||||
{
|
||||
SongData song = songs[i];
|
||||
if (song == null || song.songID <= 0 || !uniqueIds.Add(song.songID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
song.LoadPersistent();
|
||||
|
||||
if (song.isUnlocked)
|
||||
{
|
||||
ownedSongCount++;
|
||||
}
|
||||
|
||||
int highestScore;
|
||||
int bestDifficulty;
|
||||
string bestDifficultyName;
|
||||
song.GetAbsoluteHighestScore(out highestScore, out bestDifficulty, out bestDifficultyName);
|
||||
if (highestScore > bestSingleSongScore)
|
||||
{
|
||||
bestSingleSongScore = highestScore;
|
||||
}
|
||||
|
||||
totalSongBestScore += Mathf.Max(0, highestScore);
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
changed |= SetMetricValue(GlobalAchievementMetricType.OwnedSongCount, ownedSongCount);
|
||||
changed |= SetMetricValue(GlobalAchievementMetricType.BestSingleSongTotalScore, bestSingleSongScore);
|
||||
changed |= SetMetricValue(GlobalAchievementMetricType.TotalSongBestScoreSum, totalSongBestScore > float.MaxValue ? float.MaxValue : totalSongBestScore);
|
||||
return changed;
|
||||
}
|
||||
|
||||
private bool SetMetricValue(GlobalAchievementMetricType metricType, float value)
|
||||
{
|
||||
GlobalAchievementMetricEntry metric = GetOrCreateMetric(metricType);
|
||||
float nextValue = Mathf.Max(0f, value);
|
||||
if (Mathf.Approximately(metric.value, nextValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
metric.value = nextValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void PersistMetricsIfChanged(bool changed)
|
||||
{
|
||||
if (!changed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EvaluateAll();
|
||||
SaveNow();
|
||||
}
|
||||
|
||||
private void Unlock(GlobalAchievementSO definition, GlobalAchievementStateEntry state)
|
||||
{
|
||||
state.unlocked = true;
|
||||
state.unlockedUtcTicks = DateTime.UtcNow.Ticks;
|
||||
definition.SetUnlockedState(true, state.unlockedUtcTicks);
|
||||
GlobalAchievementSteamBridge.TryUnlock(definition);
|
||||
if (OnAchievementUnlocked != null)
|
||||
{
|
||||
OnAchievementUnlocked(definition);
|
||||
}
|
||||
}
|
||||
|
||||
private GlobalAchievementMetricEntry GetOrCreateMetric(GlobalAchievementMetricType metricType)
|
||||
{
|
||||
GlobalAchievementMetricEntry metric;
|
||||
if (metricByType.TryGetValue(metricType, out metric))
|
||||
{
|
||||
return metric;
|
||||
}
|
||||
|
||||
metric = new GlobalAchievementMetricEntry
|
||||
{
|
||||
metricType = metricType,
|
||||
value = 0f
|
||||
};
|
||||
metricByType.Add(metricType, metric);
|
||||
saveData.metrics.Add(metric);
|
||||
return metric;
|
||||
}
|
||||
|
||||
private GlobalAchievementStateEntry GetOrCreateState(int achievementId)
|
||||
{
|
||||
GlobalAchievementStateEntry state;
|
||||
if (stateById.TryGetValue(achievementId, out state))
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
state = new GlobalAchievementStateEntry
|
||||
{
|
||||
achievementId = achievementId,
|
||||
unlocked = false,
|
||||
unlockedUtcTicks = 0L
|
||||
};
|
||||
stateById.Add(achievementId, state);
|
||||
saveData.states.Add(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
private void RebuildCaches()
|
||||
{
|
||||
stateById.Clear();
|
||||
metricByType.Clear();
|
||||
|
||||
if (saveData.metrics == null)
|
||||
{
|
||||
saveData.metrics = new List<GlobalAchievementMetricEntry>();
|
||||
}
|
||||
|
||||
if (saveData.states == null)
|
||||
{
|
||||
saveData.states = new List<GlobalAchievementStateEntry>();
|
||||
}
|
||||
|
||||
for (int i = 0; i < saveData.metrics.Count; i++)
|
||||
{
|
||||
GlobalAchievementMetricEntry entry = saveData.metrics[i];
|
||||
if (entry == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
metricByType[entry.metricType] = entry;
|
||||
}
|
||||
|
||||
for (int i = 0; i < saveData.states.Count; i++)
|
||||
{
|
||||
GlobalAchievementStateEntry entry = saveData.states[i];
|
||||
if (entry == null || entry.achievementId <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
stateById[entry.achievementId] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
private static int CompareDefinitions(GlobalAchievementSO left, GlobalAchievementSO right)
|
||||
{
|
||||
if (left == null && right == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (left == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (right == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int seriesCompare = string.CompareOrdinal(left.achievementSeriesKey, right.achievementSeriesKey);
|
||||
if (seriesCompare != 0)
|
||||
{
|
||||
return seriesCompare;
|
||||
}
|
||||
|
||||
int levelCompare = left.achievementLevel.CompareTo(right.achievementLevel);
|
||||
if (levelCompare != 0)
|
||||
{
|
||||
return levelCompare;
|
||||
}
|
||||
|
||||
int sortCompare = left.sortOrder.CompareTo(right.sortOrder);
|
||||
if (sortCompare != 0)
|
||||
{
|
||||
return sortCompare;
|
||||
}
|
||||
|
||||
int metricCompare = left.metricType.CompareTo(right.metricType);
|
||||
if (metricCompare != 0)
|
||||
{
|
||||
return metricCompare;
|
||||
}
|
||||
|
||||
int targetCompare = left.targetValue.CompareTo(right.targetValue);
|
||||
if (targetCompare != 0)
|
||||
{
|
||||
return targetCompare;
|
||||
}
|
||||
|
||||
return left.achievementId.CompareTo(right.achievementId);
|
||||
}
|
||||
|
||||
private void SyncMirrorFlagsToDefinitions()
|
||||
{
|
||||
for (int i = 0; i < definitions.Count; i++)
|
||||
{
|
||||
GlobalAchievementSO definition = definitions[i];
|
||||
if (definition == null || definition.achievementId <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GlobalAchievementStateEntry state;
|
||||
bool unlocked = stateById.TryGetValue(definition.achievementId, out state) && state != null && state.unlocked;
|
||||
definition.SetUnlockedState(unlocked, unlocked && state != null ? state.unlockedUtcTicks : 0L);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2d505205caa8e044a45b0e2f9742128
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
#if !(UNITY_STANDALONE_WIN || UNITY_STANDALONE_LINUX || UNITY_STANDALONE_OSX || STEAMWORKS_WIN || STEAMWORKS_LIN_OSX)
|
||||
#define DISABLESTEAMWORKS
|
||||
#endif
|
||||
|
||||
#if !DISABLESTEAMWORKS
|
||||
using Steamworks;
|
||||
#endif
|
||||
|
||||
public static class GlobalAchievementSteamBridge
|
||||
{
|
||||
public static bool TryUnlock(GlobalAchievementSO definition)
|
||||
{
|
||||
if (definition == null || !definition.syncWithSteam || string.IsNullOrWhiteSpace(definition.steamAchievementApiName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !DISABLESTEAMWORKS
|
||||
if (!SteamManager.Initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = SteamUserStats.SetAchievement(definition.steamAchievementApiName);
|
||||
if (success)
|
||||
{
|
||||
SteamUserStats.StoreStats();
|
||||
}
|
||||
|
||||
return success;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf76c26d20df2d74b962e90d0718e503
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f434937296e6d7d4a8b4d68734976ec1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d01a922a7463b36418c80900c9ef668d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+1
@@ -0,0 +1 @@
|
||||
Deprecated. Place GlobalAchievementSO assets in Assets/Resources/so/globalachievments instead.
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f049e141d4f4444a9e650c5f9bbe2ea
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user