using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Events;
namespace Unity.Tutorials.Core.Editor
{
///
/// A generic event for signaling changes in a criterion.
/// Parameters: sender.
///
[Serializable]
public class CriterionEvent : UnityEvent
{
}
///
/// Base class for Criterion implementations.
///
public abstract class Criterion : ScriptableObject
{
///
/// Raised when any Criterion is completed.
///
public static CriterionEvent CriterionCompleted = new CriterionEvent();
///
/// Raised when any Criterion is invalidated.
///
public static CriterionEvent CriterionInvalidated = new CriterionEvent();
///
/// Raised when this criterion is completed.
///
[Header("Events")]
public CriterionEvent Completed;
///
/// Raised when this criterion is invalidated.
///
public CriterionEvent Invalidated;
bool m_Completed;
///
/// Is the Criterion completed. Setting this raises CriterionCompleted/CriterionInvalidated.
///
public bool IsCompleted
{
get { return m_Completed; }
internal set
{
if (value == m_Completed)
return;
m_Completed = value;
if (m_Completed)
{
Completed?.Invoke(this);
CriterionCompleted?.Invoke(this);
}
else
{
Invalidated?.Invoke(this);
CriterionInvalidated?.Invoke(this);
}
}
}
///
/// Resets the completion state.
///
public void ResetCompletionState()
{
m_Completed = false;
}
///
/// Starts testing of the criterion.
///
public virtual void StartTesting()
{
}
///
/// Stops testing of the criterion.
///
public virtual void StopTesting()
{
}
///
/// Runs update logic for the criterion.
///
public virtual void UpdateCompletion()
{
IsCompleted = EvaluateCompletion();
}
///
/// Evaluates if the criterion is completed.
///
///
protected virtual bool EvaluateCompletion()
{
throw new NotImplementedException($"Missing implementation of EvaluateCompletion in: {GetType()}");
}
///
/// Auto-completes the criterion.
///
/// True if the auto-completion succeeded.
public abstract bool AutoComplete();
///
/// Returns FutureObjectReference for this Criterion.
///
///
protected virtual IEnumerable GetFutureObjectReferences()
{
return Enumerable.Empty();
}
///
/// Destroys unreferenced future references.
///
///
/// https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnValidate.html
///
protected virtual void OnValidate()
{
// Find instanceIDs of referenced future references
var referencedFutureReferenceInstanceIDs = new HashSet();
foreach (var futureReference in GetFutureObjectReferences())
referencedFutureReferenceInstanceIDs.Add(futureReference.GetInstanceID());
// Destroy unreferenced future references
var assetPath = AssetDatabase.GetAssetPath(this);
var assets = AssetDatabase.LoadAllAssetsAtPath(assetPath);
foreach (var asset in assets)
{
if (asset is FutureObjectReference
&& ((FutureObjectReference)asset).Criterion == this
&& !referencedFutureReferenceInstanceIDs.Contains(asset.GetInstanceID()))
{
DestroyImmediate(asset, true);
}
}
}
///
/// Creates a default FutureObjectReference for this Criterion.
///
///
protected FutureObjectReference CreateFutureObjectReference()
{
return CreateFutureObjectReference("Future Reference");
}
///
/// Creates a FutureObjectReference by specific name for this Criterion.
///
///
///
protected FutureObjectReference CreateFutureObjectReference(string referenceName)
{
var futureReference = CreateInstance();
futureReference.Criterion = this;
futureReference.ReferenceName = referenceName;
var assetPath = AssetDatabase.GetAssetPath(this);
AssetDatabase.AddObjectToAsset(futureReference, assetPath);
return futureReference;
}
///
/// Updates names of the references.
///
protected void UpdateFutureObjectReferenceNames()
{
// Update future reference names in next editor update due to AssetDatase interactions
EditorApplication.update += UpdateFutureObjectReferenceNamesPostponed;
}
void UpdateFutureObjectReferenceNamesPostponed()
{
// Unsubscribe immediately since it should only be called once
EditorApplication.update -= UpdateFutureObjectReferenceNamesPostponed;
var assetPath = AssetDatabase.GetAssetPath(this);
var tutorialPage = (TutorialPage)AssetDatabase.LoadMainAssetAtPath(assetPath);
var futureReferences = AssetDatabase.LoadAllAssetsAtPath(assetPath)
.Where(o => o is FutureObjectReference)
.Cast();
foreach (var futureReference in futureReferences)
tutorialPage.UpdateFutureObjectReferenceName(futureReference);
}
}
}