mvaganov

Timer.cs

Oct 24th, 2017 (edited)
275
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. using System.Collections.Generic;
  2. #if UNITY_5_3_OR_NEWER
  3. using System.Collections;
  4. using System.Text;
  5. using UnityEngine;
  6. #if UNITY_EDITOR
  7. using UnityEditor;
  8. using System.Linq;
  9. #endif
  10. #endif
  11.  
  12. /* // example code:
  13. Chrono.setTimeout (() => {
  14.     Chrono.Log("This will print 3 seconds after setTimeout was called!");
  15. }, 3000);
  16. // Code above will work like you expect if Chrono.Update() is being called regularly, which will happen if Unity has at least one Timer or MainTimer instance.
  17. */
  18.  
  19. // author: mvaganov@hotmail.com
  20. // license: Copyfree, public domain. This is free code! Great artists, steal this code!
  21. // latest version at: https://pastebin.com/raw/h61nAC3E -- (2020/11/26)
  22. namespace NonStandard {
  23. #if UNITY_5_3_OR_NEWER
  24.     public class Timer : MonoBehaviour {
  25.         [Tooltip("When to trigger"), ContextMenuItem("Create Main Timer", "CraeteMainTimer")]
  26.         public float seconds = 1;
  27.         [Tooltip("Transform to teleport to\nSceneAsset to load a new scene\nAudioClip to play audio\nGameObject to SetActivate(true)")]
  28.         public ObjectPtr whatToActivate = new ObjectPtr();
  29.         [Tooltip("restart a timer after triggering")]
  30.         public bool repeat = false;
  31.         [Tooltip("attempt to deactivate instead of activate")]
  32.         public bool deactivate = false;
  33.         public int a;
  34.  
  35.         private void DoTimer() {
  36.             if (repeat) {
  37.                 Chrono.setTimeout(DoTimer, (long)(seconds * 1000));
  38.             }
  39.             Chrono.ScheduledTask todo = Chrono.setTimeout(whatToActivate.Data, (long)(seconds * 1000));
  40.             todo.who = this;
  41.             todo.activate = !deactivate;
  42.         }
  43.         private void Awake() { MainTimer.Instance(); }
  44.         void Start() { if (whatToActivate.Data != null) { DoTimer(); } }
  45.  
  46. #if UNITY_EDITOR
  47.         public void CreateMainTimer() {
  48.             MainTimer found = FindObjectOfType<MainTimer>();
  49.             if (found == null) {
  50.                 gameObject.AddComponent<MainTimer>();
  51.             } else {
  52.                 Debug.LogWarning("will not create another " + nameof(MainTimer) + ", one already at " + found.transform.HierarchyPath());
  53.             }
  54.         }
  55. #endif
  56.     }
  57.  
  58.     public static class TransformExtention {
  59.         public static string HierarchyPath(this Transform t) {
  60.             StringBuilder sb = new StringBuilder();
  61.             sb.Append(t.name);
  62.             t = t.parent;
  63.             while (t != null) {
  64.                 sb.Insert(0, t.name + "/");
  65.                 t = t.parent;
  66.             }
  67.             return sb.ToString();
  68.         }
  69.     }
  70.  
  71.     /// <summary>
  72.     /// a class that enables the timer queue to be observed in the editor at runtime
  73.     /// </summary>
  74.     public class MainTimer : MonoBehaviour {
  75.         public Chrono timerQueue;
  76.         [System.Serializable]
  77.         public class PauseEvents {
  78.             [Tooltip("do this when time is paused")] public UnityEngine.Events.UnityEvent onPause;
  79.             [Tooltip("do this when time is unpaused")] public UnityEngine.Events.UnityEvent onUnpause;
  80.         }
  81.         public PauseEvents pauseEvents;
  82.  
  83.         public void Awake() {
  84.             if (timerQueue != Chrono.Instance && timerQueue != null) { Chrono.Instance.Absorb(timerQueue); }
  85.             timerQueue = Chrono.Instance;
  86.             timerQueue.linkedToMainThread = System.Threading.Thread.CurrentThread;
  87.             timerQueue.Awake();
  88.             timerQueue.onPause += () => { pauseEvents.onPause.Invoke(); };
  89.             timerQueue.onUnpause += () => { pauseEvents.onUnpause.Invoke(); };
  90.         }
  91.         private void Start() { timerQueue.Start(); }
  92.         public void Update() { timerQueue.Update(); }
  93.         void Pause() { timerQueue.Pause(); }
  94.         void Unpause() { timerQueue.Unpause(); }
  95.         void OnApplicationPause(bool paused) { timerQueue.OnApplicationPause(paused); }
  96.         void OnDisable() { timerQueue.OnDisable(); }
  97.         void OnEnable() { timerQueue.OnEnable(); }
  98.         private void OnApplicationQuit() { timerQueue.OnApplicationQuit(); }
  99. #if UNITY_EDITOR
  100.         public void OnValidate() { if (timerQueue == null) { timerQueue = Chrono.Instance; } timerQueue.OnValidate(); }
  101. #endif
  102.  
  103.         internal static MainTimer s_instance;
  104.  
  105.         public static MainTimer Instance() {
  106.             if (s_instance != null) return s_instance;
  107.             s_instance = FindObjectOfType<MainTimer>();
  108.             if (s_instance == null) { // if it doesn't exist
  109.                 GameObject g = new GameObject("<" + typeof(Chrono).Name + ">");
  110.                 s_instance = g.AddComponent<MainTimer>(); // create one
  111.             }
  112.             return s_instance;
  113.         }
  114.     }
  115. #endif
  116.  
  117.     [System.Serializable]
  118.     public class Chrono {
  119.         // https://docs.microsoft.com/en-us/dotnet/api/system.timers.timer?redirectedfrom=MSDN&view=netframework-4.8
  120.         // explicitly NOT using System.Timers.Timer because it's multi-threaded, and many Unity methods, notably the time keeping ones, must be called from the main thread.
  121.  
  122.         /// The singleton
  123.         private static Chrono s_instance = null;
  124.         /// [Tooltip("keeps track of how long each update takes. If a timer-update takes longer than this, stop executing events and do them later. Less than 0 for no limit, 0 for one event per update.")]
  125.         public int maxMillisecondsPerUpdate = 100;
  126.         private long maxTicksPerUpdate;
  127.         public long updateCount { get; private set; }
  128.         public static long UpdateCount => Instance.updateCount;
  129.         /// queue of things to do using game time. use this for in-game events that can be paused or slowed down with time dialation.
  130.         public List<ScheduledTask> queue = new List<ScheduledTask>();
  131.         /// queue of things to do using real-time game time. use this for UI events, or things that use the real-world as a reference, that should always work at the same rate
  132.         public List<ScheduledTask> queueRealtime = new List<ScheduledTask>();
  133.         /// While this is zero, use system time. As soon as time becomes perturbed, by pause or time scale, this now keeps track of game-time. To reset time back to realtime, use SynchToRealtime()
  134.         private long alternativeTicks = 0;
  135.         /// [Tooltip("stop advancing time & executing the queue?")]
  136.         public bool paused = false;
  137.         private bool pausedLastFrame = false;
  138.         internal System.Threading.Thread linkedToMainThread = null;
  139.         /// The timer counts in milliseconds, Unity measures in fractions of a second. This value reconciles fractional milliseconds.
  140.         private float leftOverTime = 0;
  141.         /// if actions are interrupted, probably by a deadline, this keeps track of what was being done
  142.         private List<ScheduledTask> _currentlyDoing = new List<ScheduledTask>();
  143.         private int currentlyDoneThingIndex = 0;
  144.  
  145.         public System.Action onPause, onUnpause;
  146.  
  147.         [System.Serializable]
  148.         public class ScheduledTask {
  149.             public string description;
  150.             /// Unix Epoch Time Milliseconds
  151.             public long when;
  152.             /// could be a delegate, or an executable object
  153.             public object what;
  154.             /// a parameter for 'who' wants this particular 'what' to be done, like a context.
  155.             public object who;
  156.             /// whether or not to DO or UN-do
  157.             public bool activate = true;
  158.             /// what could be a delegate, or an executable object, as executed by a Trigger
  159.             public ScheduledTask(long when, object what, string description = null, object who = null) {
  160.                 if (description == null) {
  161.                     if (what != null && typeof(System.Action).IsAssignableFrom(what.GetType())) {
  162.                         System.Action a = what as System.Action;
  163.                         description = a.Method.Name;
  164.                     } else if (what != null) {
  165.                         description = what.ToString();
  166.                     }
  167.                 }
  168.                 this.description = description; this.when = when; this.what = what; this.who = who;
  169.             }
  170.             /// comparer, used to sort into a list
  171.             public class Comparer : IComparer<ScheduledTask> {
  172.                 public int Compare(ScheduledTask x, ScheduledTask y) { return x.when.CompareTo(y.when); }
  173.             }
  174.             public static Comparer compare = new Comparer();
  175.         }
  176.  
  177.         public static void Log(string text, bool error = false) {
  178. #if UNITY_5_3_OR_NEWER
  179.             if (error) Debug.LogError(text);
  180.             else Debug.Log(text);
  181. #else
  182.             System.Console.WriteLine(text);
  183. #endif
  184.         }
  185.  
  186.         public static Chrono Instance => (s_instance != null) ? s_instance : s_instance = new Chrono();
  187.  
  188.         /// Unix Time: milliseconds since Jan 1 1970
  189.         public static long NowRealtime { get { return System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond; } }
  190.         /// a time keeping mechanism that is very fast. no guarantees about the range of values, just that time is kept.
  191.         public static long NowRealTicks { get { return System.Environment.TickCount; } }
  192.  
  193.         /// game time right now (modified by pausing or Time.timeScale)
  194.         public long now { get { return (alternativeTicks == 0) ? NowRealtime : alternativeTicks; } }
  195.         public long nowTicks { get { return (alternativeTicks == 0) ? NowRealTicks : alternativeTicks; } }
  196.  
  197.         /// game time right now (modified by pausing or Time.timeScale)
  198.         public static long Now { get { return Instance.now; } }
  199.         public static long NowTicks { get { return Instance.nowTicks; } }
  200.  
  201.         private static bool _quitting = false;
  202.         public static bool Quitting { get { return _quitting; } internal set { _quitting = value; } }
  203.         public void OnApplicationQuit() { _quitting = true; }
  204.  
  205.         /// clears the difference between game time and real time
  206.         public void SyncToRealtime() { alternativeTicks = 0; }
  207.  
  208.         public void Absorb(Chrono otherTimerQueue) {
  209.             for (int i = 0; i < otherTimerQueue.queue.Count; ++i) {
  210.                 ScheduledTask todo = otherTimerQueue.queue[i];
  211.                 queue.Insert(BestIndexFor(todo.when, queue), todo);
  212.             }
  213.             otherTimerQueue.queue.Clear();
  214.             for (int i = 0; i < otherTimerQueue.queue.Count; ++i) {
  215.                 ScheduledTask todo = otherTimerQueue.queueRealtime[i];
  216.                 queueRealtime.Insert(BestIndexFor(todo.when, queueRealtime), todo);
  217.             }
  218.             otherTimerQueue.queueRealtime.Clear();
  219.         }
  220.  
  221.         private int BestIndexFor(long soon, List<ScheduledTask> a_queue) {
  222.             int index = 0;
  223.             if (a_queue.Count < 8) { // for small lists (which happen A LOT), linear search is fine.
  224.                 for (index = 0; index < a_queue.Count; ++index) {
  225.                     if (a_queue[index].when > soon) break;
  226.                 }
  227.             } else {
  228.                 ScheduledTask toInsert = new ScheduledTask(soon, null);
  229.                 index = a_queue.BinarySearch(toInsert, ScheduledTask.compare);
  230.                 if (index < 0) { index = ~index; }
  231.             }
  232.             return index;
  233.         }
  234.  
  235.         /// <summary>as the JavaScript function</summary>
  236.         /// <param name="action">Action. an object to trigger, expected to be a delegate or System.Action</param>
  237.         /// <param name="delayMilliseconds">Delay milliseconds.</param>
  238.         public ScheduledTask SetTimeout(System.Action action, long delayMilliseconds) {
  239.             return SetTimeout((object)action, delayMilliseconds);
  240.         }
  241.         /// <summary>as the JavaScript function</summary>
  242.         /// <param name="action">Action. an object to trigger, expected to be a delegate or System.Action</param>
  243.         /// <param name="delayMilliseconds">Delay milliseconds.</param>
  244.         public ScheduledTask SetTimeout(object action, long delayMilliseconds) {
  245.             long soon = nowTicks + delayMilliseconds;// * System.TimeSpan.TicksPerMillisecond;
  246.             ScheduledTask todo = new ScheduledTask(soon, action);
  247.             queue.Insert(BestIndexFor(soon, queue), todo);
  248.             LinkedToMainThreadCheck();
  249.             return todo;
  250.         }
  251.        
  252.         public List<ScheduledTask> UnsetTimeout(object action) {
  253.             List<ScheduledTask> unset = new List<ScheduledTask>();
  254.             for (int i = queue.Count-1; i >= 0; --i) {
  255.                 if (queue[i].what == action) {
  256.                     unset.Add(queue[i]);
  257.                     queue.RemoveAt(i);
  258.                 }
  259.             }
  260.             return unset;
  261.         }
  262.  
  263.         /// <summary>as the JavaScript function</summary>
  264.         /// <param name="action">Action. an object to trigger, expected to be a delegate or System.Action</param>
  265.         /// <param name="delayMilliseconds">Delay milliseconds.</param>
  266.         public ScheduledTask SetTimeoutRealtime(object action, long delayMilliseconds) {
  267.             long soon = NowRealTicks + delayMilliseconds;// * System.TimeSpan.TicksPerMillisecond;
  268.             ScheduledTask todo = new ScheduledTask(soon, action);
  269.             queueRealtime.Insert(BestIndexFor(soon, queueRealtime), todo);
  270.             LinkedToMainThreadCheck();
  271.             return todo;
  272.         }
  273.  
  274.         public void LinkedToMainThreadCheck() {
  275.             if (linkedToMainThread == null) {
  276.                 string extraMessage = "";
  277. #if UNITY_5_3_OR_NEWER
  278.                 extraMessage = "Create a " + nameof(Timer) + " or " + nameof(MainTimer) + " instance";
  279. #endif
  280.                 Log("timer queue given work without being bound to main thread. " + extraMessage, true);
  281.             }
  282.         }
  283.  
  284.         /// <param name="action">Action. what to do</param>
  285.         /// <param name="delayMilliseconds">Delay milliseconds. in how-many-milliseconds to do it</param>
  286.         public static ScheduledTask setTimeout(object action, long delayMilliseconds) {
  287.             return Instance.SetTimeout(action, delayMilliseconds);
  288.         }
  289.  
  290.         public static List<ScheduledTask> unsetTimeout(System.Action action) {
  291.             return Instance.UnsetTimeout(action);
  292.         }
  293.        
  294.         /// <param name="action">Action. what to do</param>
  295.         /// <param name="delayMilliseconds">Delay milliseconds. in how-many-milliseconds to do it</param>
  296.         public static ScheduledTask setTimeoutRealtime(object action, long delayMilliseconds) {
  297.             return Instance.SetTimeoutRealtime(action, delayMilliseconds);
  298.         }
  299.  
  300.         /// Allows implicit conversion of lambda expressions and delegates
  301.         /// <param name="action">Action. what to do</param>
  302.         /// <param name="delayMilliseconds">Delay milliseconds. in how-many-milliseconds to do it</param>
  303.         public static ScheduledTask setTimeout(System.Action action, long delayMilliseconds) {
  304.             return Instance.SetTimeout(action, delayMilliseconds);
  305.         }
  306.  
  307.         /// <summary>Allows implicit conversion of lambda expressions and delegates</summary>
  308.         /// <param name="action">Action. what to do</param>
  309.         /// <param name="delayMilliseconds">Delay milliseconds. in how-many-milliseconds to do it</param>
  310.         public static ScheduledTask setTimeoutRealtime(System.Action action, long delayMilliseconds) {
  311.             return Instance.SetTimeoutRealtime(action, delayMilliseconds);
  312.         }
  313.  
  314.         //void OnApplicationPause(bool paused) { if(alternativeTime == 0) { alternativeTime = now; } }
  315.         public void Pause() { paused = true; }
  316.         public void Unpause() { paused = false; }
  317.         public void OnApplicationPause(bool paused) { if (alternativeTicks == 0) { alternativeTicks = nowTicks; } }
  318.         public void OnDisable() { OnApplicationPause(true); }
  319.         public void OnEnable() { OnApplicationPause(false); }
  320.  
  321.         /// used to handle pause/unpause behavior
  322.         public delegate void BooleanAction(bool b);
  323.         public static void EquateUnityEditorPauseWithApplicationPause(BooleanAction b) {
  324. #if UNITY_EDITOR
  325.             // This method is run whenever the playmode state is changed.
  326.             UnityEditor.EditorApplication.pauseStateChanged += (UnityEditor.PauseState ps) => {
  327.                 b(ps == UnityEditor.PauseState.Paused);
  328.             };
  329. #endif
  330.         }
  331.  
  332.         protected void Init() { EquateUnityEditorPauseWithApplicationPause(OnApplicationPause); }
  333.  
  334.         public void Awake() {
  335.             if (s_instance != null && s_instance != this) { throw new System.Exception("There should only be one " + GetType()); }
  336.             s_instance = this;
  337.         }
  338.  
  339.         void RefreshTiming() { maxTicksPerUpdate = maxMillisecondsPerUpdate * System.TimeSpan.TicksPerMillisecond; }
  340.  
  341. #if UNITY_EDITOR
  342.         public void OnValidate() { RefreshTiming(); }
  343. #endif
  344.  
  345.         public void Start() { Init(); RefreshTiming(); }
  346.  
  347.         public void Update() {
  348.             updateCount++;
  349.             // handle pause behavior
  350.             if (paused) {
  351.                 if (!pausedLastFrame) {
  352.                     alternativeTicks = nowTicks;
  353.                     if (onPause != null) { onPause.Invoke(); }
  354.                     pausedLastFrame = true;
  355.                 }
  356.             } else if (pausedLastFrame) {
  357.                 if (onUnpause != null) { onUnpause.Invoke(); }
  358.                 pausedLastFrame = false;
  359.             }
  360.             // pump the timer queues, both realtime (takes priority) and game-time
  361.             long now_t, nowForReals = NowRealTicks;
  362.             long deadline = nowForReals + maxTicksPerUpdate;
  363.             int thingsDone = 0;
  364.             if (queueRealtime.Count > 0) {
  365.                 thingsDone = DoWhatIsNeededNow(queueRealtime, nowForReals, deadline);
  366.             }
  367.             if (queue.Count > 0 && !paused) {
  368.                 if (alternativeTicks == 0) {
  369.                     now_t = nowForReals;
  370. #if UNITY_5_3_OR_NEWER
  371.                     if (Time.timeScale != 1) { alternativeTicks = now_t; }
  372. #endif
  373.                 } else {
  374.                     float deltaTimeTicks =
  375. #if UNITY_5_3_OR_NEWER
  376.                     (Time.deltaTime * 1000);
  377. #else
  378.                     (__lastUpdate != 0) ? (NowRealTicks - __lastUpdate) : 0;
  379. #endif
  380.                     long deltaTimeTicksLong = (long)(deltaTimeTicks + leftOverTime);
  381.                     alternativeTicks += deltaTimeTicksLong;
  382.                     leftOverTime = deltaTimeTicks - deltaTimeTicksLong;
  383.                     now_t = alternativeTicks;
  384.                 }
  385.                 if (thingsDone == 0 || now_t < deadline) {
  386.                     thingsDone += DoWhatIsNeededNow(queue, now_t, deadline);
  387.                 }
  388.             }
  389. #if !UNITY_5_3_OR_NEWER
  390.             __lastUpdate = NowRealTicks;
  391. #endif
  392.         }
  393. #if !UNITY_5_3_OR_NEWER
  394.         private long __lastUpdate = 0;
  395. #endif
  396.  
  397.         int DoWhatIsNeededNow(List<ScheduledTask> a_queue, long now_t, long deadline) {
  398.             bool tryToDoMore;
  399.             int thingsDone = 0;
  400.             do {
  401.                 tryToDoMore = false;
  402.                 if (a_queue.Count > 0 && a_queue[0].when <= now_t) {
  403.                     if (_currentlyDoing.Count == 0) {
  404.                         // the things to do in the queue might add to the queue, so to prevent infinite looping...
  405.                         // separate out the elements to do right now
  406.                         for (int i = 0; i < a_queue.Count; ++i) {
  407.                             if (a_queue[i].when > now_t) { break; }
  408.                             _currentlyDoing.Add(a_queue[i]);
  409.                         }
  410.                         // if there's nothing to do, get out of this potential loop
  411.                         if (_currentlyDoing.Count == 0) { break; }
  412.                         a_queue.RemoveRange(0, _currentlyDoing.Count);
  413.                         tryToDoMore = false;
  414.                     }
  415.                     // do what is scheduled to do right now
  416.                     while (currentlyDoneThingIndex < _currentlyDoing.Count) {
  417.                         ScheduledTask todo = _currentlyDoing[currentlyDoneThingIndex++];
  418.                         // if DoActivate adds to the queue, it won't get executed this cycle
  419.                         NonStandard.ActivateAnything.DoActivate(todo.what, this, todo.who, todo.activate);
  420.                         ++thingsDone;
  421.                         // if it took too long to do that thing, stop and hold the rest of the things to do till later.
  422.                         if (maxTicksPerUpdate >= 0 && NowRealTicks > deadline) {
  423.                             break;
  424.                         }
  425.                     }
  426.                     if (currentlyDoneThingIndex >= _currentlyDoing.Count) {
  427.                         _currentlyDoing.Clear();
  428.                         currentlyDoneThingIndex = 0;
  429.                         tryToDoMore = NowRealTicks < deadline && a_queue.Count > 0;
  430.                     }
  431.                 }
  432.             } while (tryToDoMore);
  433.             return thingsDone;
  434.         }
  435.     }
  436.  
  437.     /// This class serves as a store for static Utility functions related to 'activating things'.
  438.     public static class ActivateAnything {
  439.         /// <summary>'Activates' something, in a ways that is possibly specific to circumstances.</summary>
  440.         /// <param name="whatToActivate">what needs to be activated. In "The straw that broke the camel's back", this is the "camel's back", which we can assume is breaking (or unbreaking?) during this function.</param>
  441.         /// <param name="causedActivate">what triggered the activation. In "The straw that broke the camel's back", this is "the straw". Depending on the straw, breaking may happen differently.</param>
  442.         /// <param name="doingActivate">what is doing the activation. In "The straw that broke the camel's back", this is "the camel".</param>
  443.         /// <param name="activate">whether to activate or deactivate. In "The straw that broke the camel's back", this is whether to break or unbreak the-camel's-back.</param>
  444.         /// <param name="delayInSeconds">Delay in seconds.</param>
  445.         public static void DoActivate(
  446.             object whatToActivate, object causedActivate, object doingActivate, bool activate,
  447.             float delayInSeconds
  448.         ) {
  449.             if (delayInSeconds <= 0) {
  450.                 DoActivate(whatToActivate, causedActivate, doingActivate, activate);
  451.             } else {
  452.                 Chrono.setTimeout(() => {
  453.                     DoActivate(whatToActivate, causedActivate, doingActivate, activate);
  454.                 }, (long)(delayInSeconds * 1000));
  455.             }
  456.         }
  457.  
  458.         /// <summary>'Activates' something, in a ways that is possibly specific to circumstances.</summary>
  459.         /// <param name="whatToActivate">what needs to be activated. In "The straw that broke the camel's back", this is the "camel's back", which we can assume is breaking (or unbreaking?) during this function.</param>
  460.         /// <param name="causedActivate">what triggered the activation. In "The straw that broke the camel's back", this is "the (1?) straw". Depending on the straw, or straw count, breaking may happen differently.</param>
  461.         /// <param name="doingActivate">what is doing the activation. In "The straw that broke the camel's back", this is "the camel", or possibly, "the straw".</param>
  462.         /// <param name="activate">whether to activate or deactivate. In "The straw that broke the camel's back", this is whether to break or unbreak the-camel's-back.</param>
  463.         public static void DoActivate(
  464.             object whatToActivate, object causedActivate, object doingActivate, bool activate
  465.         ) {
  466.             if (whatToActivate == null) { Chrono.Log("Don't know how to activate null"); return; }
  467.             if (whatToActivate is IReference) {
  468.                 whatToActivate = ((IReference)whatToActivate).Dereference();
  469.             }
  470.             System.Type type = whatToActivate.GetType();
  471.             if (typeof(System.Action).IsAssignableFrom(type)) {
  472.                 System.Action a = whatToActivate as System.Action;
  473.                 a.Invoke();
  474. #if UNITY_5_3_OR_NEWER
  475.             } else if (typeof(UnityEngine.Events.UnityEvent).IsAssignableFrom(type)) {
  476.                 UnityEngine.Events.UnityEvent a = whatToActivate as UnityEngine.Events.UnityEvent;
  477.                 a.Invoke();
  478.             } else if (type == typeof(Transform)) {
  479.                 Transform targetLocation = ConvertToTransform(whatToActivate);
  480.                 Transform toMove = ConvertToTransform(causedActivate);
  481.                 if (toMove != null) {
  482.                     toMove.position = targetLocation.position;
  483.                 }
  484.             } else if (type == typeof(AudioClip) || type == typeof(AudioSource)) {
  485.                 AudioSource asource = null;
  486.                 if (type == typeof(AudioSource)) {
  487.                     asource = whatToActivate as AudioSource;
  488.                 }
  489.                 if (asource == null) {
  490.                     GameObject go = ConvertToGameObject(doingActivate);
  491.                     if (go != null) {
  492.                         asource = go.AddComponent<AudioSource>();
  493.                     } else {
  494.                         throw new System.Exception("can't create audio without a game object to put it on.");
  495.                     }
  496.                 }
  497.                 if (type == typeof(AudioClip)) {
  498.                     asource.clip = whatToActivate as AudioClip;
  499.                 }
  500.                 if (activate) {
  501.                     asource.Play();
  502.                 } else {
  503.                     asource.Stop();
  504.                 }
  505.             } else if (type == typeof(ParticleSystem)) {
  506.                 ParticleSystem ps = whatToActivate as ParticleSystem;
  507.                 if (activate) {
  508.                     Transform t = ps.transform;
  509.                     GameObject go = ConvertToGameObject(doingActivate);
  510.                     t.position = go.transform.position;
  511.                     t.rotation = go.transform.rotation;
  512.                     ParticleSystem.ShapeModule sm = ps.shape;
  513.                     if (sm.shapeType == ParticleSystemShapeType.Mesh) {
  514.                         sm.mesh = go.GetComponent<MeshFilter>().mesh;
  515.                         sm.scale = go.transform.lossyScale;
  516.                     } else if (sm.shapeType == ParticleSystemShapeType.MeshRenderer) {
  517.                         sm.meshRenderer = go.GetComponent<MeshRenderer>();
  518.                     }
  519.                     ps.Play();
  520.                 } else {
  521.                     ps.Stop();
  522.                 }
  523.             } else if (type == typeof(GameObject)) {
  524.                 GameObject go = (whatToActivate as GameObject);
  525.                 if (go == null) {
  526.                     Debug.LogWarning("GameObject destroyed?");
  527.                 } else {
  528.                     go.SetActive(activate);
  529.                 }
  530.             } else if (type == typeof(UnityEngine.Color)) {
  531.                 GameObject go = ConvertToGameObject(doingActivate);
  532.                 RememberedOriginalColor r = go.GetComponent<RememberedOriginalColor>();
  533.                 if (activate) {
  534.                     Color c = (Color)whatToActivate;
  535.                     if (r == null) {
  536.                         r = go.AddComponent<RememberedOriginalColor>();
  537.                         r.oldColor = go.GetComponent<Renderer>().material.color;
  538.                     }
  539.                     go.GetComponent<Renderer>().material.color = c;
  540.                 } else {
  541.                     if (r != null) {
  542.                         go.GetComponent<Renderer>().material.color = r.oldColor;
  543.                         UnityEngine.Object.Destroy(r);
  544.                     }
  545.                 }
  546.             } else if (type == typeof(UnityEngine.Material)) {
  547.                 GameObject go = ConvertToGameObject(doingActivate);
  548.                 RememberedOriginalMaterial r = go.GetComponent<RememberedOriginalMaterial>();
  549.                 if (activate) {
  550.                     Material m = whatToActivate as Material;
  551.                     if (r == null) {
  552.                         r = go.AddComponent<RememberedOriginalMaterial>();
  553.                         r.oldMaterial = go.GetComponent<Renderer>().material;
  554.                     }
  555.                     go.GetComponent<Renderer>().material = m;
  556.                 } else {
  557.                     if (r != null) {
  558.                         go.GetComponent<Renderer>().material = r.oldMaterial;
  559.                         UnityEngine.Object.Destroy(r);
  560.                     }
  561.                 }
  562.             } else if (typeof(IEnumerable).IsAssignableFrom(type)) {
  563.                 IEnumerable ienum = whatToActivate as IEnumerable;
  564.                 IEnumerator iter = ienum.GetEnumerator();
  565.                 while (iter.MoveNext()) {
  566.                     DoActivate(iter.Current, causedActivate, doingActivate, activate);
  567.                 }
  568.             } else if (type == typeof(Animation)) {
  569.                 if (activate) {
  570.                     (whatToActivate as Animation).Play();
  571.                 } else {
  572.                     (whatToActivate as Animation).Stop();
  573.                 }
  574. #endif
  575.             } else {
  576.                 System.Reflection.MethodInfo[] m = type.GetMethods();
  577.                 bool invoked = false;
  578.                 for (int i = 0; i < m.Length; ++i) {
  579.                     System.Reflection.MethodInfo method = m[i];
  580.                     if ((activate && method.Name == "DoActivateTrigger")
  581.                     || (!activate && method.Name == "DoDeactivateTrigger")) {
  582.                         switch (method.GetParameters().Length) {
  583.                         case 0: method.Invoke(whatToActivate, new object[] { }); invoked = true; break;
  584.                         case 1: method.Invoke(whatToActivate, new object[] { causedActivate }); invoked = true; break;
  585.                         case 2: method.Invoke(whatToActivate, new object[] { causedActivate, doingActivate }); invoked = true; break;
  586.                         }
  587.                         break;
  588.                     }
  589.                 }
  590.                 if (!invoked) {
  591.                     Chrono.Log("Don't know how to " + ((activate) ? "DoActivateTrigger" : "DoDeactivateTrigger") + " a \'" + type + "\' (" + whatToActivate + ") with \'" + doingActivate + "\', triggered by \'" + causedActivate + "\'", true);
  592.                 }
  593.             }
  594.         }
  595. #if UNITY_5_3_OR_NEWER
  596.         public class RememberedOriginalMaterial : MonoBehaviour { public Material oldMaterial; }
  597.         public class RememberedOriginalColor : MonoBehaviour { public Color oldColor; }
  598.  
  599.         public static GameObject ConvertToGameObject(object obj) {
  600.             if (obj is GameObject) { return obj as GameObject; }
  601.             if (obj is Component) { return (obj as Component).gameObject; }
  602.             if (obj is Collision) { return (obj as Collision).gameObject; }
  603.             if (obj is Collision2D) { return (obj as Collision2D).gameObject; }
  604.             return null;
  605.         }
  606.  
  607.         public static Transform ConvertToTransform(object obj) {
  608.             if (obj is Transform) { return obj as Transform; }
  609.             GameObject go = ConvertToGameObject(obj);
  610.             if (go != null) { return go.transform; }
  611.             return null;
  612.         }
  613. #endif
  614.     }
  615.  
  616.     public interface IReference { object Dereference(); }
  617.  
  618. #if UNITY_5_3_OR_NEWER
  619.     [System.Serializable]
  620.     public struct ObjectPtr : IReference {
  621.         public Object data;
  622.         public Object Data { get { return data; } set { data = value; } }
  623.         public object Dereference() {
  624.             return data;
  625.         }
  626.         public TYPE GetAs<TYPE>() where TYPE : class { return data as TYPE; }
  627.     }
  628. #endif
  629.  
  630. #if UNITY_EDITOR
  631.     // used to get lists of classes for the ObjectPtr property
  632.     public static class Reflection {
  633.         public static System.Type[] GetTypesInNamespace(string nameSpace, bool includeComponentTypes = false, System.Reflection.Assembly assembly = null) {
  634.             if (assembly == null) {
  635.                 assembly = System.Reflection.Assembly.GetExecutingAssembly();
  636.             }
  637.             System.Type[] types = assembly.GetTypes().Where(t =>
  638.                 System.String.Equals(t.Namespace, nameSpace, System.StringComparison.Ordinal)
  639.                 && (includeComponentTypes || !t.ToString().Contains('+'))).ToArray();
  640.             return types;
  641.         }
  642.         public static string CleanFront(string str, string trimMe) {
  643.             if (str.StartsWith(trimMe)) { return str.Substring(trimMe.Length); }
  644.             return str;
  645.         }
  646.         public static List<string> TypeNamesCleaned(System.Type[] validTypes, string namespaceToClean) {
  647.             List<string> list = new List<string>();
  648.             for (int i = 0; i < validTypes.Length; ++i) {
  649.                 string typename = validTypes[i].ToString();
  650.                 typename = CleanFront(typename, namespaceToClean + ".");
  651.                 list.Add(typename);
  652.             }
  653.             return list;
  654.         }
  655.  
  656.         public static T EditorGUI_EnumPopup<T>(Rect _position, T value) {
  657.             System.Type t = typeof(T);
  658.             if (t.IsEnum) {
  659.                 string[] names = System.Enum.GetNames(t);
  660.                 string thisone = value.ToString();
  661.                 int index = System.Array.IndexOf(names, thisone);
  662.                 index = EditorGUI.Popup(_position, index, names);
  663.                 value = (T)System.Enum.Parse(t, names[index]);
  664.             }
  665.             return value;
  666.         }
  667.     }
  668. #endif
  669. }
  670.  
  671. #if UNITY_EDITOR
  672. // used to create scriptable objects with the ObjectPtr property
  673. public static class ScriptableObjectUtility {
  674.     /// This makes it easy to create, name and place unique new ScriptableObject asset files.
  675.     public static T CreateAsset<T>() where T : ScriptableObject { return CreateAsset(typeof(T)) as T; }
  676.     public static ScriptableObject CreateAsset(System.Type t, string filename = "", string path = "") {
  677.         ScriptableObject asset = ScriptableObject.CreateInstance(t);
  678.         string whereItWasSaved = SaveScriptableObjectAsAsset(asset, filename, path);
  679.         asset = Resources.Load(whereItWasSaved, t) as ScriptableObject;
  680.         return asset;
  681.     }
  682.  
  683.     public static string SaveScriptableObjectAsAsset(ScriptableObject asset, string filename = "", string path = "") {
  684.         System.Type t = asset.GetType();
  685.         if (path == "") {
  686.             path = AssetDatabase.GetAssetPath(Selection.activeObject);
  687.             if (path == "") {
  688.                 path = UnityEngine.SceneManagement.SceneManager.GetActiveScene().path;//"Assets";
  689.                 Debug.Log(path);
  690.                 int idx = path.LastIndexOf("/");
  691.                 if (idx < 0) {
  692.                     path = "Assets";
  693.                 } else {
  694.                     path = path.Substring(0, idx);
  695.                     if (filename == "") {
  696.                         string typename = t.ToString();
  697.                         int idx2 = typename.LastIndexOf(".");
  698.                         if (idx > 0) { typename = typename.Substring(idx2); }
  699.                         filename = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name + typename + ".asset";
  700.                     }
  701.                     Debug.Log(path + " //// " + filename);
  702.                 }
  703.                 //              Debug.Log(UnityEngine.SceneManagement.SceneManager.GetActiveScene().path);
  704.             } else if (System.IO.Path.GetExtension(path) != "") {
  705.                 path = path.Replace(System.IO.Path.GetFileName(AssetDatabase.GetAssetPath(Selection.activeObject)), "");
  706.             }
  707.         }
  708.         if (filename.Length == 0) { filename = "New " + t.ToString() + ".asset"; }
  709.         string fullpath = path + "/" + filename;
  710.         string assetPathAndName = AssetDatabase.GenerateUniqueAssetPath(fullpath);
  711.         AssetDatabase.CreateAsset(asset, assetPathAndName);
  712.         AssetDatabase.SaveAssets();
  713.         AssetDatabase.Refresh();
  714.         EditorUtility.FocusProjectWindow();
  715.         Selection.activeObject = asset;
  716.         Debug.Log("saved " + fullpath);
  717.         return fullpath;
  718.     }
  719. }
  720.  
  721. // enables the ObjectPtr property, not fully utilized by Timer, but certainly utilized by NonStandardAssets
  722. [CustomPropertyDrawer(typeof(NonStandard.ObjectPtr))]
  723. public class PropertyDrawer_ObjectPtr : PropertyDrawer {
  724.     delegate Object SelectNextObjectFunction();
  725.     public static bool showLabel = true;
  726.     public int choice = 0;
  727.     string[] choices_name = { };
  728.     SelectNextObjectFunction[] choices_selectFunc = { };
  729.     Object choicesAreFor;
  730.     System.Type[] possibleResponses;
  731.     string[] cached_typeCreationList_names;
  732.     SelectNextObjectFunction[] cached_TypeCreationList_function;
  733.  
  734.     public static string setToNull = "set to null", delete = "delete";
  735.     public static float defaultOptionWidth = 16, defaultLabelWidth = 48, unitHeight = 16;
  736.     /// <summary>The namespaces to get default selectable classes from</summary>
  737.     protected virtual string[] GetNamespacesForNewComponentOptions() { return null; }
  738.  
  739.     public override float GetPropertyHeight(SerializedProperty _property, GUIContent label) {
  740.         return StandardCalcPropertyHeight();
  741.     }
  742.  
  743.     public static float StandardCalcPropertyHeight() {
  744.         // SerializedProperty asset = _property.FindPropertyRelative("data");
  745.         return unitHeight;//base.GetPropertyHeight (asset, label);
  746.     }
  747.  
  748.     /// <summary>
  749.     /// When the ObjectPtr points to nothing, this method generates the objects that can be created by default
  750.     /// </summary>
  751.     /// <param name="self">Self.</param>
  752.     /// <param name="names">Names.</param>
  753.     /// <param name="functions">Functions.</param>
  754.     private void GenerateTypeCreationList(Component self, out string[] names, out SelectNextObjectFunction[] functions) {
  755.         List<string> list = new List<string>();
  756.         List<SelectNextObjectFunction> list_of_data = new List<SelectNextObjectFunction>();
  757.         string[] theList = GetNamespacesForNewComponentOptions();
  758.         if (theList != null) {
  759.             for (int i = 0; i < theList.Length; ++i) {
  760.                 string namespaceName = theList[i];
  761.                 possibleResponses = NonStandard.Reflection.GetTypesInNamespace(namespaceName);
  762.                 list.AddRange(NonStandard.Reflection.TypeNamesCleaned(possibleResponses, namespaceName));
  763.                 for (int t = 0; t < possibleResponses.Length; t++) {
  764.                     System.Type nextT = possibleResponses[t];
  765.                     list_of_data.Add(() => {
  766.                         return CreateSelectedClass(nextT, self);
  767.                     });
  768.                 }
  769.             }
  770.         }
  771.         list.Insert(0, (theList != null) ? "<-- select Object or create..." : "<-- select Object");
  772.         list_of_data.Insert(0, null);
  773.         names = list.ToArray();
  774.         functions = list_of_data.ToArray();
  775.     }
  776.  
  777.     private void CleanTypename(ref string typename) {
  778.         int lastDot = typename.LastIndexOf('.');
  779.         if (lastDot >= 0) { typename = typename.Substring(lastDot + 1); }
  780.     }
  781.  
  782.     private void GenerateChoicesForSelectedObject(Component self, out string[] names, out SelectNextObjectFunction[] functions) {
  783.         List<string> components = new List<string>();
  784.         List<SelectNextObjectFunction> nextSelectionFunc = new List<SelectNextObjectFunction>();
  785.         string typename = choicesAreFor.GetType().ToString();
  786.         CleanTypename(ref typename);
  787.         components.Add(typename);
  788.         nextSelectionFunc.Add(null);
  789.         GameObject go = choicesAreFor as GameObject;
  790.         bool addSetToNull = true;
  791.         Object addDelete = null;
  792.         if (go != null) {
  793.             Component[] c = go.GetComponents<Component>();
  794.             for (int i = 0; i < c.Length; i++) {
  795.                 Component comp = c[i];
  796.                 if (comp != self) {
  797.                     typename = comp.GetType().ToString();
  798.                     CleanTypename(ref typename);
  799.                     components.Add(typename);
  800.                     nextSelectionFunc.Add(() => { return comp; });
  801.                 }
  802.             }
  803.             addSetToNull = true;
  804.         } else if (choicesAreFor is Component) {
  805.             components.Add(".gameObject");
  806.             GameObject gob = (choicesAreFor as Component).gameObject;
  807.             nextSelectionFunc.Add(() => { return gob; });
  808.             addSetToNull = true;
  809.             addDelete = choicesAreFor;
  810.         }
  811.         if (addSetToNull) {
  812.             components.Add(setToNull);
  813.             nextSelectionFunc.Add(() => {
  814.                 choice = 0; return null;
  815.             });
  816.         }
  817.         if (addDelete != null) {
  818.             components.Add(delete);
  819.             nextSelectionFunc.Add(() => {
  820.                 //Object.DestroyImmediate(addDelete);
  821.                 itemsToCleanup.Add(addDelete);
  822.                 choice = 0; return null;
  823.             });
  824.         }
  825.         names = components.ToArray();
  826.         functions = nextSelectionFunc.ToArray();
  827.     }
  828.  
  829.     [ExecuteInEditMode]
  830.     private class IndirectCleaner : MonoBehaviour {
  831.         public List<Object> itemsToCleanup;
  832.         private void Update() {
  833.             for (int i = itemsToCleanup.Count - 1; i >= 0; --i) {
  834.                 Object o = itemsToCleanup[i];
  835.                 if (o != null) { Object.DestroyImmediate(o); }
  836.             }
  837.             itemsToCleanup.Clear();
  838.             DestroyImmediate(this); // cleaning lady disposes of herself too
  839.         }
  840.     }
  841.     private List<Object> itemsToCleanup = new List<Object>();
  842.     private void RequestCleanup(Component self) {
  843.         // if any items need to be deleted, don't do it now! the UI is in the middle of being drawn!
  844.         // create a separate process that will do it for you
  845.         IndirectCleaner cleaner = self.gameObject.AddComponent<IndirectCleaner>();
  846.         cleaner.itemsToCleanup = this.itemsToCleanup;
  847.     }
  848.  
  849.     /// <summary>called right after an object is assigned</summary>
  850.     public virtual Object FilterImmidiate(Object obj, Component self) {
  851.         return obj;
  852.     }
  853.  
  854.     /// <summary>called right after a new component is created to be assigned</summary>
  855.     protected virtual Object FilterNewComponent(System.Type nextT, Component self, Component newlyCreatedComponent) {
  856.         return newlyCreatedComponent;
  857.     }
  858.  
  859.     /// <summary>called just before UI is finished. This is the last chance to adjust the new setting.</summary>
  860.     public virtual Object FilterFinal(Object newObjToReference, Object prevObj, Component self) {
  861.         return newObjToReference;
  862.     }
  863.  
  864.     private Object CreateSelectedClass(System.Type nextT, Component self) {
  865.         Object obj = null;
  866.         if (self != null && self.gameObject != null) {
  867.             GameObject go = self.gameObject;
  868.             if (nextT.IsSubclassOf(typeof(ScriptableObject))) {
  869.                 obj = ScriptableObjectUtility.CreateAsset(nextT);
  870.             } else {
  871.                 Component newComponent = go.AddComponent(nextT);
  872.                 obj = FilterNewComponent(nextT, self, newComponent);
  873.             }
  874.         }
  875.         return obj;
  876.     }
  877.  
  878.     public static SerializedProperty ObjectPtrAsset(SerializedProperty _property) {
  879.         SerializedProperty asset = _property.FindPropertyRelative("data");
  880.         //asset = asset.FindPropertyRelative("data");
  881.         return asset;
  882.     }
  883.  
  884.     public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label) {
  885.         EditorGUI.BeginProperty(_position, GUIContent.none, _property);
  886.         SerializedProperty asset = ObjectPtrAsset(_property);
  887.         int oldIndent = EditorGUI.indentLevel;
  888.         EditorGUI.indentLevel = 0;
  889.         if (PropertyDrawer_ObjectPtr.showLabel) {
  890.             _position = EditorGUI.PrefixLabel(_position, GUIUtility.GetControlID(FocusType.Passive), _label);
  891.         }
  892.         Component self = _property.serializedObject.targetObject as Component;
  893.         if (asset != null) {
  894.             Object prevObj = asset.objectReferenceValue;
  895.             asset.objectReferenceValue = EditorGUIObjectReference(_position, asset.objectReferenceValue, self);
  896.             asset.objectReferenceValue = FilterFinal(asset.objectReferenceValue, prevObj, self);
  897.             //Contingentable cself = self as Contingentable;
  898.             //if(prevObj != asset.objectReferenceValue && cself != null && cself.ContingencyRecursionCheck() != null) {
  899.             //  Debug.LogWarning("Disallowing recursion of " + asset.objectReferenceValue);
  900.             //  asset.objectReferenceValue = prevObj;
  901.             //}
  902.         }
  903.         EditorGUI.indentLevel = oldIndent;
  904.         EditorGUI.EndProperty();
  905.         if (itemsToCleanup.Count != 0) { RequestCleanup(self); }
  906.     }
  907.  
  908.     // TODO rename this to DoGUI
  909.     public virtual Object EditorGUIObjectReference(Rect _position, Object obj, Component self) {
  910.         int oldIndent = EditorGUI.indentLevel;
  911.         EditorGUI.indentLevel = 0;
  912.         obj = StandardEditorGUIObjectReference(_position, obj, self);
  913.         EditorGUI.indentLevel = oldIndent;
  914.         return obj;
  915.     }
  916.  
  917.     public Object ShowObjectPtrChoicesPopup(Rect _position, Object obj, Component self, bool recalculateChoices) {
  918.         // if the object needs to have it's alternate forms calculated
  919.         if (recalculateChoices || choicesAreFor != obj || choices_name.Length == 0) {
  920.             choicesAreFor = obj;
  921.             // if these choices are for an actual object
  922.             if (choicesAreFor != null) {
  923.                 GenerateChoicesForSelectedObject(self, out choices_name, out choices_selectFunc);
  924.                 choice = 0;
  925.             } else {
  926.                 if (cached_typeCreationList_names == null) {
  927.                     GenerateTypeCreationList(self,
  928.                         out cached_typeCreationList_names, out cached_TypeCreationList_function);
  929.                 }
  930.                 choices_name = cached_typeCreationList_names;
  931.                 choices_selectFunc = cached_TypeCreationList_function;
  932.             }
  933.         }
  934.         // give the alternate options for the object
  935.         int lastChoice = choice;
  936.         _position.x += _position.width;
  937.         _position.width = defaultOptionWidth;
  938.         choice = EditorGUI.Popup(_position, choice, choices_name);
  939.         if (lastChoice != choice) {
  940.             if (choices_selectFunc[choice] != null) {
  941.                 obj = choices_selectFunc[choice]();
  942.             }
  943.         }
  944.         return obj;
  945.     }
  946.  
  947.     public Object StandardEditorGUIObjectReference(Rect _position, Object obj, Component self) {
  948.         float originalWidth = _position.width;
  949.         _position.width = originalWidth - defaultOptionWidth;
  950.         Object prevSelection = obj;
  951.         obj = EditorGUI.ObjectField(_position, obj, typeof(Object), true);
  952.         obj = FilterImmidiate(obj, self);
  953.         obj = ShowObjectPtrChoicesPopup(_position, obj, self, obj != prevSelection);
  954.         return obj;
  955.     }
  956.  
  957.     public Object DoGUIEnumLabeledString<T>(Rect _position, Object obj, Component self,
  958.         ref T enumValue, ref string textValue) {
  959.         int oldindent = EditorGUI.indentLevel;
  960.         EditorGUI.indentLevel = 0;
  961.         Rect r = _position;
  962.         float w = defaultOptionWidth, wl = defaultLabelWidth;
  963.         r.width = wl;
  964.         enumValue = NonStandard.Reflection.EditorGUI_EnumPopup<T>(r, enumValue);
  965.         r.x += r.width;
  966.         r.width = _position.width - w - wl;
  967.         textValue = EditorGUI.TextField(r, textValue);
  968.         obj = ShowObjectPtrChoicesPopup(r, obj, self, true);
  969.         r.x += r.width;
  970.         r.width = w;
  971.         EditorGUI.indentLevel = oldindent;
  972.         return obj;
  973.     }
  974.     public Object DoGUIEnumLabeledObject<T>(Rect _position, Object obj, Component self,
  975.         ref T enumValue, ref Object objectValue) {
  976.         int oldindent = EditorGUI.indentLevel;
  977.         EditorGUI.indentLevel = 0;
  978.         Rect r = _position;
  979.         float w = defaultOptionWidth, wl = defaultLabelWidth;
  980.         r.width = wl;
  981.         enumValue = NonStandard.Reflection.EditorGUI_EnumPopup<T>(r, enumValue);
  982.         r.x += r.width;
  983.         r.width = _position.width - w - wl;
  984.         objectValue = EditorGUI.ObjectField(r, objectValue, typeof(Object), true);
  985.         obj = FilterImmidiate(obj, self);
  986.         obj = ShowObjectPtrChoicesPopup(r, obj, self, true);
  987.         r.x += r.width;
  988.         r.width = w;
  989.         EditorGUI.indentLevel = oldindent;
  990.         return obj;
  991.     }
  992. }
  993. #endif
RAW Paste Data