//#define DISABLE_CACHE using Cryville.Common; using System; using System.Collections.Generic; using UnityEngine; namespace Cryville.Crtr.Event { public class ContainerState { public EventBus Bus; public EventContainer Container; public StampedEvent StampedContainer; public ContainerState Parent = null; public ushort Depth; public Dictionary Children = new Dictionary(); readonly HashSet WorkingChildren = new HashSet(); readonly HashSet InvalidatedChildren = new HashSet(); public Dictionary> TypedChildren = new Dictionary>(); public ContainerState GetChild(int index, Type handlerType) { return TypedChildren[handlerType][index]; } public ContainerState GetChild(EventContainer ev) { return Children[ev]; } void NotifyWorkingChanged(EventContainer key) { InvalidatedChildren.Add(key); } void ValidateChildren() { foreach (var cev in InvalidatedChildren) if (Children[cev].Working && !WorkingChildren.Contains(cev)) WorkingChildren.Add(cev); else if (!Children[cev].Working && WorkingChildren.Contains(cev)) WorkingChildren.Remove(cev); InvalidatedChildren.Clear(); } public bool Active { get; set; } private bool m_Working; public bool Working { get { return m_Working; } set { m_Working = value; if (Parent != null) Parent.NotifyWorkingChanged(Container); Bus.NotifyWorkingChanged(this); } } public byte CloneType; public ContainerState rootPrototype = null; private ContainerState prototype = null; public ContainerHandler Handler { get; private set; } public double Time { get { return Bus.Time; } } public ContainerState(EventContainer _ev, ContainerState parent = null) { Container = _ev; if (parent != null) { AddChild(_ev, this, parent); Parent = parent; } Values = new Dictionary(ChartPlayer.motionRegistry.Count); CachedValues = new Dictionary(ChartPlayer.motionRegistry.Count); foreach (var m in ChartPlayer.motionRegistry) Values.Add(m.Key, new RealtimeMotionValue().Init(Parent == null ? m.Value.GlobalInitValue : m.Value.InitValue)); rootPrototype = this; } static void AddChild(EventContainer c, ContainerState s, ContainerState target) { target.Children.Add(c, s); Type t = c.GetType(); if (!target.TypedChildren.ContainsKey(t)) target.TypedChildren.Add(t, new List()); target.TypedChildren[t].Add(s); } public ContainerState Clone(byte ct) { var r = (ContainerState)MemberwiseClone(); var mvs = new Dictionary(ChartPlayer.motionRegistry.Count); foreach (var mv in Values) { mvs.Add(mv.Key, mv.Value.Clone()); } r.Values = mvs; var cvs = new Dictionary(ChartPlayer.motionRegistry.Count); foreach (var cv in CachedValues) { var dv = r.MCPool.Rent(cv.Key); cv.Value.CopyTo(dv); cvs.Add(cv.Key, dv); } r.CachedValues = cvs; r.Children = new Dictionary(); foreach (var child in Children) { var cc = child.Value.Clone(ct); cc.Parent = r; AddChild(child.Key, cc, r); } var pms = new Dictionary(Math.Max(4, PlayingMotions.Count)); foreach (var m in PlayingMotions) pms.Add(m.Key, m.Value); r.PlayingMotions = pms; if (ct == 1) Handler.bs = r; else if (ct == 2) Handler.ts = r; else if (ct == 3) Handler.ns = r; else if (ct >= 16) Handler.ps = r; else throw new InvalidOperationException("Invalid clone type"); r.prototype = this; r.CloneType = ct; return r; } public void CopyTo(byte ct, ContainerState dest) { dest.Working = Working; foreach (var mv in Values) { RealtimeMotionValue dv; if (dest.Values.TryGetValue(mv.Key, out dv)) mv.Value.CopyTo(dv); else dest.Values.Add(mv.Key, mv.Value.Clone()); } foreach (var cv in dest.CachedValues) cv.Value.Valid = false; foreach (var cv in CachedValues) { MotionCache dv; if (!dest.CachedValues.TryGetValue(cv.Key, out dv)) { dest.CachedValues.Add(cv.Key, dv = dest.MCPool.Rent(cv.Key)); } cv.Value.CopyTo(dv); } if (ct != 1) foreach (var cev in WorkingChildren) Children[cev].CopyTo(ct, dest.Children[cev]); else foreach (var child in Children) child.Value.CopyTo(ct, dest.Children[child.Key]); dest.ValidateChildren(); dest.PlayingMotions.Clear(); foreach (var m in PlayingMotions) dest.PlayingMotions.Add(m.Key, m.Value); } public bool Disposed { get; private set; } public void Dispose() { if (Disposed) return; Disposed = true; if (CloneType == 1) Handler.Dispose(); foreach (var s in Children) s.Value.Dispose(); RMVPool.ReturnAll(); MCPool.ReturnAll(); } public void AttachHandler(ContainerHandler h) { if (Handler != null) throw new InvalidOperationException("Handler attached twice"); Handler = h; h.cs = this; } public void AttachSystems(PdtSkin skin, Judge judge) { Handler.AttachSystems(skin, judge); } #endregion #region Motion readonly RMVPool RMVPool = new RMVPool(); readonly MotionCachePool MCPool = new MotionCachePool(); Dictionary PlayingMotions = new Dictionary(4); Dictionary Values; Dictionary CachedValues; /// /// Gets a motion value. /// /// The motion name. /// Returns a cloned motion value instead. /// A motion value. RealtimeMotionValue GetMotionValue(Identifier name, bool clone = false) { RealtimeMotionValue value = Values[name]; if (clone) return value.Clone(); return value; } void InvalidateMotion(Identifier name) { MotionCache cache; if (!CachedValues.TryGetValue(name, out cache)) CachedValues.Add(name, cache = MCPool.Rent(name)); cache.Valid = false; foreach (var c in ActiveChildren) Children[c].InvalidateMotion(name); } public Vector GetRawValue(Identifier key) { MotionCache tr; if (!CachedValues.TryGetValue(key, out tr)) CachedValues.Add(key, tr = MCPool.Rent(key)); Vector r = tr.Value; #if !DISABLE_CACHE if (tr.Valid) return r; #endif float reltime = 0; if (rootPrototype != null) reltime = (float)(Time - rootPrototype.Time); GetMotionValue(key).GetValue(reltime, ref r); if (Parent != null) r.ApplyFrom(Parent.GetRawValue(key)); #if !DISABLE_CACHE tr.Valid = true; #endif return r; } public T GetRawValue(Identifier key) where T : Vector { return (T)GetRawValue(key); } static readonly Identifier n_pt = new Identifier("pt"); public Vector2 ScreenPoint { get { var mv = GetRawValue(n_pt); return mv.ToVector2(ChartPlayer.hitRect); } } static readonly Identifier n_dir = new Identifier("dir"); public Vector3 Direction { get { Vec3 r = GetRawValue(n_dir); return r.ToVector3(); } } static readonly Identifier n_normal = new Identifier("normal"); public Vector3 Normal { get { Vec3 r = GetRawValue(n_normal); return r.ToVector3(); } } public Quaternion QuatDir { get { return Quaternion.LookRotation(Quaternion.Euler(Direction) * Vector3.forward, Normal); } } static readonly Identifier n_sv = new Identifier("sv"); static readonly Identifier n_svm = new Identifier("svm"); public float ScrollVelocity { get { return GetRawValue(n_sv).ToFloat(ChartPlayer.hitRect) * GetRawValue(n_svm).Value; } } static readonly Identifier n_dist = new Identifier("dist"); public float Distance { get { var mv = GetRawValue(n_dist); return mv.ToFloat(ChartPlayer.hitRect); } } static readonly Identifier n_corner = new Identifier("corner"); public bool Corner { get { return GetRawValue(n_corner).Value % 2 >= 1; } } static readonly Identifier n_ctrl0 = new Identifier("ctrl0"); static readonly Identifier n_ctrl1 = new Identifier("ctrl1"); public Vector3 GetControlPoint(bool alt1, float deltaz) { var mv = GetRawValue(alt1 ? n_ctrl1 : n_ctrl0); if (alt1 && mv.IsZero()) { mv = GetRawValue(n_ctrl0); } return mv.ToVector3(ChartPlayer.hitRect, deltaz); } static readonly Identifier n_track = new Identifier("track"); public float Track { get { return GetRawValue(n_track).Value; } } #endregion #region Update bool breakflag = false; public void Break() { Handler.EndUpdate(this); breakflag = true; Working = false; } public void Discard(StampedEvent ev) { Handler.Discard(this, ev); } public void Handle(StampedEvent ev) { if (breakflag) return; if (ev != null) { if (ev.Unstamped is Chart.Motion) { var tev = (Chart.Motion)ev.Unstamped; var mv = RMVPool.Rent(tev.Name); mv.CloneTypeFlag = CloneType; GetMotionValue(tev.Name).CopyTo(mv); PlayingMotions.Add(ev, mv); Update(ev); if (!ev.Unstamped.IsLong) { PlayingMotions.Remove(ev); RMVPool.Return(mv); } } else if (ev.Unstamped is EventContainer) { var cev = (EventContainer)ev.Unstamped; var ccs = GetChild(cev); ccs.Working = true; ccs.StartUpdate(); UpdateMotions(); if (!cev.IsLong) { ccs.Working = false; ccs.BroadcastEndUpdate(); if (CloneType == 1) ccs.Dispose(); } } else if (ev.Unstamped is InstantEvent) { var tev = (InstantEvent)ev.Unstamped; if (tev.IsRelease) { var nev = tev.Original; if (nev is Chart.Motion) { Update(ev); var mv = PlayingMotions[ev.Origin]; if (mv.CloneTypeFlag == CloneType) RMVPool.Return(mv); PlayingMotions.Remove(ev.Origin); } else if (nev is EventContainer) { var cev = (EventContainer)ev.Origin.Unstamped; var ccs = GetChild(cev); UpdateMotions(); ccs.Working = false; ccs.BroadcastEndUpdate(); if (CloneType == 1) ccs.Dispose(); } } } Update(ev.Unstamped == null || ev.Unstamped.Priority >= 0 ? ev : null); } else Update(null); } void Update(StampedEvent ev) { UpdateMotions(); if (ev == null || ev.Unstamped != null) Handler.Update(this, ev); else Handler.ExUpdate(this, ev); foreach (var m in PlayingMotions) Handler.MotionUpdate(CloneType, (Chart.Motion)m.Key.Unstamped); } private void UpdateMotions() { foreach (var m in PlayingMotions) { var tev = (Chart.Motion)m.Key.Unstamped; if (tev.RelativeNode != null && CloneType == 2) continue; var value = GetMotionValue(tev.Name/*, true*/); InvalidateMotion(tev.Name); if (m.Key.Duration == 0) { if (tev.RelativeNode != null) { value.SetRelativeNode(tev.RelativeNode); } else { value.AbsoluteValue.ReplaceFrom(tev.AbsoluteValue); } } else { var scaledTime = (float)((Time - m.Key.Time - ChartPlayer.actualRenderStep * tev.sumfix) / m.Key.Duration); var lerpedTime = MotionLerper.GetEaseTime(scaledTime, tev.transition, tev.rate); if (tev.RelativeNode != null) { var target = value.QueryRelativeNode(tev.RelativeNode.Id); tev.RelativeNode.LerpWith(m.Value.GetRelativeNode(tev.RelativeNode.Id), lerpedTime, ref target); } else { tev.AbsoluteValue.LerpWith(m.Value.AbsoluteValue, lerpedTime, ref value.AbsoluteValue); } } Values[tev.Name] = value; } } public void BroadcastPreInit() { Handler.PreInit(); foreach (var c in Children.Values) { c.BroadcastPreInit(); } } public void BroadcastPostInit() { Handler.PostInit(); foreach (var c in Children.Values) { c.BroadcastPostInit(); } } public void StartUpdate() { Handler.StartUpdate(this); } public void BroadcastEndUpdate() { Handler.EndUpdate(this); foreach (var ls in Children.Values) { if (ls.Working) ls.BroadcastEndUpdate(); } } public void Anchor() { Handler.Anchor(); foreach (var ls in Children.Values) { if (ls.Handler.Alive) ls.Anchor(); } } #endregion } }