//#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 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(); } 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; private ContainerState rootPrototype = null; private ContainerState prototype = null; public ContainerHandler Handler { get; private set; } public float Time { get { return Bus.Time; } } readonly RMVPool RMVPool = new RMVPool(); protected Dictionary PlayingMotions = new Dictionary(); protected Dictionary Values; protected Dictionary CachedValues; protected class CacheEntry { public bool Valid { get; set; } public Vector Value { get; set; } public CacheEntry Clone() { return new CacheEntry { Valid = Valid, Value = Value == null ? null : Value.Clone() }; } } /// /// 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; if (!Values.TryGetValue(name, out value)) { value = new RealtimeMotionValue().Init(Parent == null ? ChartPlayer.motionRegistry[name].GlobalInitValue : ChartPlayer.motionRegistry[name].InitValue ); Values.Add(name, value); } if (clone) return value.Clone(); return value; } void InvalidateMotion(Identifier name) { CacheEntry cache; if (!CachedValues.TryGetValue(name, out cache)) CachedValues.Add(name, cache = new CacheEntry()); cache.Valid = false; foreach (var c in Children) c.Value.InvalidateMotion(name); } public ContainerState(Chart c, 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)); } 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) { cvs.Add(cv.Key, cv.Value.Clone()); } 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(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; if (prototype == null) r.rootPrototype = this; else r.rootPrototype = rootPrototype; 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 CachedValues) { CacheEntry dv; if (dest.CachedValues.TryGetValue(cv.Key, out dv)) { dv.Valid = cv.Value.Valid; if (cv.Value.Value != null) cv.Value.Value.CopyTo(dv.Value); } else dest.CachedValues.Add(cv.Key, cv.Value.Clone()); } 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]); ValidateChildren(); RMVPool.ReturnAll(); 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 < 16 && Handler != null) Handler.Dispose(); foreach (var s in Children) s.Value.Dispose(); RMVPool.ReturnAll(); } public void AttachHandler(ContainerHandler h) { if (Handler != null) throw new InvalidOperationException(); Handler = h; h.cs = this; } public void AttachSystems(PdtSkin skin, Judge judge) { Handler.AttachSystems(skin, judge); } public Vector GetRawValue(Identifier key) { CacheEntry tr; if (!CachedValues.TryGetValue(key, out tr)) CachedValues.Add(key, tr = new CacheEntry { Valid = false }); if (tr.Value == null) tr.Value = (Vector)ReflectionHelper.InvokeEmptyConstructor(ChartPlayer.motionRegistry[key].Type); Vector r = tr.Value; #if !DISABLE_CACHE if (tr.Valid) return r; #endif float reltime = 0; if (rootPrototype != null) reltime = 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; } } bool breakflag = false; public void Break() { Handler.EndUpdate(this); breakflag = true; Working = false; } public void Handle(StampedEvent ev, Action callback = null) { if (breakflag) return; if (ev != null) { if (ev.Unstamped is Chart.Motion) { var tev = (Chart.Motion)ev.Unstamped; var mv = RMVPool.Rent(tev.Name); GetMotionValue(tev.Name).CopyTo(mv); PlayingMotions.Add(ev, mv); Callback(ev, callback); if (!ev.Unstamped.IsLong) PlayingMotions.Remove(ev); } else if (ev.Unstamped is EventContainer) { var cev = (EventContainer)ev.Unstamped; var ccs = GetChild(cev); ccs.Working = true; ccs.StartUpdate(); UpdateMotions(); if (!ev.Unstamped.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) { Callback(ev, callback); 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(); } } } Callback(ev.Unstamped == null || ev.Unstamped.Priority >= 0 ? ev : null, callback); } else Callback(null, callback); } void Callback(StampedEvent ev, Action callback) { UpdateMotions(); if (callback != null) callback(ev); 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 = (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(); } } } }