using Cryville.Common; using Cryville.Common.Buffers; using Cryville.Common.Collections.Specialized; using Cryville.Crtr.Ruleset; using Cryville.Crtr.Skin; using System; using System.Collections.Generic; using System.Globalization; using System.Runtime.CompilerServices; using UnityEngine; namespace Cryville.Crtr.Event { public abstract class ContainerHandler : ISkinnableGroup { #region Struct public ContainerHandler() { } /// /// Prehandling , prehandling the events. /// public ContainerState ps; /// /// Backward , disposing events at the backward clipping plane, firing anchor state and temporary state. /// public ContainerState bs; /// /// Current , handling events occuring at the current time. /// public ContainerState cs; /// /// Anchor , computing the start position of the temporary state. /// public ContainerState ns; /// /// Temporary , rendering events between the clipping planes. /// public ContainerState ts; protected Transform RootTransform; readonly List _comps = new(); protected IEnumerable Components { get { return _comps; } } public Vector3 Position { get; protected set; } public Quaternion Rotation { get; protected set; } public bool Alive { get; private set; } bool PreGraphicalActive; public void SetPreGraphicalActive(bool value, ContainerState s) { if (PreGraphicalActive == value) return; PreGraphicalActive = value; if (PreGraphicalActive) StartPreGraphicalUpdate(s); else EndPreGraphicalUpdate(s); } bool GraphicalActive; public void SetGraphicalActive(bool value, ContainerState s) { if (GraphicalActive == value) return; GraphicalActive = value; if (GraphicalActive) StartGraphicalUpdate(s); else EndGraphicalUpdate(s); } public EventContainer Container { get { return cs.Container; } } static readonly int _var_current_time = IdentifierManager.Shared.Request("current_time"); static readonly int _var_invisible_bounds = IdentifierManager.Shared.Request("invisible_bounds"); public readonly IntKeyedDictionary PropSrcs = new(); SkinContainer skinContainer; protected Judge judge; public void AttachSystems(PdtSkin skin, Judge judge) { skinContainer = new SkinContainer(this, skin.elements); this.judge = judge; } public readonly IntKeyedDictionary> Anchors = new(); public readonly IntKeyedDictionary DynamicAnchors = new(); public readonly IntKeyedDictionary DynamicAnchorSetTime = new(); Anchor a_cur; Anchor a_head; Anchor a_tail; static readonly int _a_cur = IdentifierManager.Shared.Request("cur"); static readonly int _a_head = IdentifierManager.Shared.Request("head"); static readonly int _a_tail = IdentifierManager.Shared.Request("tail"); double atime_head; double atime_tail; public Anchor RegisterAnchor(int name, bool dyn = false, int propSrcCount = 0) { var strname = IdentifierManager.Shared.Retrieve(name); var go = new GameObject("." + strname).transform; go.SetParent(RootTransform, false); var result = new Anchor(name, go, propSrcCount); if (dyn) { if (DynamicAnchors.ContainsKey(name)) throw new ArgumentException(string.Format("The anchor \"{0}\" already exists", strname)); DynamicAnchors.Add(name, result); DynamicAnchorSetTime.Add(name, double.NaN); } if (!Anchors.TryGetValue(name, out List list)) Anchors.Add(name, list = new List()); list.Add(result); return result; } protected void OpenAnchor(Anchor anchor) { if (_openedAnchor != null) throw new InvalidOperationException("An anchor has been opened"); anchor.Transform.gameObject.SetActive(true); _openedAnchor = anchor; } protected void CloseAnchor() { _openedAnchor = null; } #endregion #region Logic #region Init methods: Called on prehandle const string TagRootTransform = "RT"; public virtual void PreInit() { RootTransform = new GameObject(TypeName + ":" + Container.GetHashCode().ToString(CultureInfo.InvariantCulture)).transform; RootTransform.tag = TagRootTransform; SkinContext = new SkinContext(RootTransform); if (cs.Parent != null) RootTransform.SetParent(cs.Parent.Handler.RootTransform, false); a_cur = RegisterAnchor(_a_cur); a_head = RegisterAnchor(_a_head, true); a_tail = RegisterAnchor(_a_tail, true); } public virtual void Init() { PdtEvaluator.Instance.ContextState = ps; PdtEvaluator.Instance.ContextEvent = Container; skinContainer.MatchStatic(); MatchDynamic(ps, 0); PdtEvaluator.Instance.ContextEvent = null; PdtEvaluator.Instance.ContextState = null; foreach (Transform child in RootTransform) { if (child.CompareTag(TagRootTransform)) continue; _comps.AddRange(child.GetComponentsInChildren()); } foreach (var i in _comps) i.Init(); } public virtual void PostInit() { PropSrcs.Add(_var_current_time, new PropSrc.Float(() => (float)cs.rootPrototype.Time)); PropSrcs.Add(_var_invisible_bounds, new PropSrc.Boolean(() => atime_head > atime_tail)); RootTransform.gameObject.SetActive(false); } #endregion #region Start methods public virtual void StartPhysicalUpdate(ContainerState s) { if (s.CloneType < 16) Alive = true; else if (s.CloneType == 17) Init(); } public virtual void StartLogicalUpdate(ContainerState s) { } protected virtual void StartPreGraphicalUpdate(ContainerState s) { } protected virtual void StartGraphicalUpdate(ContainerState s) { if (RootTransform) RootTransform.gameObject.SetActive(true); } #endregion public virtual void Update(ContainerState s, StampedEvent ev) { if (ev is StampedEvent.Anchor tev) { if (tev.Target == a_head) { if (s.CloneType == 2) SetGraphicalActive(true, s); else SetPreGraphicalActive(true, s); } if (RootTransform) { OpenAnchor(tev.Target); #if UNITY_5_6_OR_NEWER tev.Target.Transform.SetPositionAndRotation(Position, Rotation); #else tev.Target.Transform.position = Position; tev.Target.Transform.rotation = Rotation; #endif MatchDynamic(s, 2); CloseAnchor(); } if (tev.Target == a_tail) { if (s.CloneType == 2) SetGraphicalActive(false, s); else SetPreGraphicalActive(false, s); } ReturnAnchorEvent(tev); } else if (RootTransform && s.CloneType == 2) MatchDynamic(s, 2); } #region End methods protected virtual void EndGraphicalUpdate(ContainerState s) { } protected virtual void EndPreGraphicalUpdate(ContainerState s) { } public virtual void EndLogicalUpdate(ContainerState s) { } public virtual void EndPhysicalUpdate(ContainerState s) { } public virtual void Dispose() { if (RootTransform) GameObject.Destroy(RootTransform.gameObject); Alive = false; } public virtual void DisposeAll() { } #endregion #region Utils [MethodImpl(MethodImplOptions.AggressiveInlining)] protected static bool CanDoGraphicalUpdate(ContainerState s) { return s.CloneType >= 2 && s.CloneType < 16; } [MethodImpl(MethodImplOptions.AggressiveInlining)] void MatchDynamic(ContainerState s, int dl) { PdtEvaluator.Instance.ContextState = s; PdtEvaluator.Instance.ContextEvent = Container; skinContainer.MatchDynamic(dl); PdtEvaluator.Instance.ContextEvent = null; PdtEvaluator.Instance.ContextState = null; } #endregion #region Anchor public virtual void PreAnchor() { foreach (var p in PropSrcs) p.Value.Invalidate(); foreach (var a in DynamicAnchors) DynamicAnchorSetTime[a.Key] = double.NaN; atime_head = cs.StampedContainer.Time; atime_tail = atime_head + cs.StampedContainer.Duration; MatchDynamic(cs, 1); foreach (var i in _comps) i.Tick(skinContainer, cs.Time); if (double.IsNaN(DynamicAnchorSetTime[_a_head])) DynamicAnchorSetTime[_a_head] = atime_head; if (double.IsNaN(DynamicAnchorSetTime[_a_tail])) DynamicAnchorSetTime[_a_tail] = atime_tail; PushAnchorEvent(ns, atime_head, a_head, -1, true); PushAnchorEvent(ns, atime_tail, a_tail, 1, true); foreach (var anchors in Anchors) foreach (var anchor in anchors.Value) anchor.Transform.gameObject.SetActive(false); } public virtual void Anchor() { if (cs.Active) PushAnchorEvent(ts, cs.Time, a_cur); foreach (var t in DynamicAnchorSetTime) { if (double.IsNaN(t.Value)) continue; int priority = 0; bool forced = true; if (t.Key == _a_head) { priority = -1; } else if (t.Key == _a_tail) { priority = 1; } else forced = false; PushAnchorEvent(ts, t.Value, DynamicAnchors[t.Key], priority, forced); } } static readonly SimpleObjectPool anchorEvPool = new(1024); void PushAnchorEvent(ContainerState state, double time, Anchor anchor, int priority = 0, bool forced = false) { var tev = anchorEvPool.Rent(); tev.Time = time; tev.Container = Container; tev.Target = anchor; tev.Forced = forced; tev.SetPriority(priority); state.Bus.PushTempEvent(tev); } public virtual void Discard(ContainerState s, StampedEvent ev) { if (ev is StampedEvent.Anchor anchor) { ReturnAnchorEvent(anchor); } } void ReturnAnchorEvent(StampedEvent.Anchor ev) { ev.Container = null; ev.Target = null; anchorEvPool.Return(ev); } #endregion #endregion #region ISkinnableGroup public abstract string TypeName { get; } public SkinContext SkinContext { get; private set; } public int AtAnchorDynamicLevel { get { return 2; } } Anchor _openedAnchor; public int OpenedAnchorName { get { return _openedAnchor == null ? 0 : _openedAnchor.Name; } } public bool TryGetAnchorsByName(int name, out IReadOnlyCollection result) { var ret = Anchors.TryGetValue(name, out List anchors); result = anchors; return ret; } void ISkinnableGroup.RegisterAnchor(int name) { RegisterAnchor(name, true); } public void PushAnchorEvent(double time, int name) { if (!DynamicAnchors.ContainsKey(name)) throw new ArgumentException(string.Format("Specified anchor \"{0}\" not found", IdentifierManager.Shared.Retrieve(name))); if (name == _a_head) atime_head = time; else if (name == _a_tail) atime_tail = time; DynamicAnchorSetTime[name] = time; } #endregion } }