436 lines
12 KiB
C#
436 lines
12 KiB
C#
//#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<EventContainer, ContainerState> Children
|
|
= new Dictionary<EventContainer, ContainerState>();
|
|
readonly HashSet<EventContainer> WorkingChildren
|
|
= new HashSet<EventContainer>();
|
|
readonly HashSet<EventContainer> InvalidatedChildren
|
|
= new HashSet<EventContainer>();
|
|
public Dictionary<Type, List<ContainerState>> TypedChildren
|
|
= new Dictionary<Type, List<ContainerState>>();
|
|
|
|
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;
|
|
private ContainerState rootPrototype = null;
|
|
private ContainerState prototype = null;
|
|
|
|
public ContainerHandler Handler {
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public double Time {
|
|
get {
|
|
return Bus.Time;
|
|
}
|
|
}
|
|
|
|
readonly RMVPool RMVPool = new RMVPool();
|
|
readonly MotionCachePool MCPool = new MotionCachePool();
|
|
Dictionary<StampedEvent, RealtimeMotionValue> PlayingMotions = new Dictionary<StampedEvent, RealtimeMotionValue>(4);
|
|
Dictionary<Identifier, RealtimeMotionValue> Values;
|
|
Dictionary<Identifier, MotionCache> CachedValues;
|
|
|
|
/// <summary>
|
|
/// Gets a motion value.
|
|
/// </summary>
|
|
/// <param name="name">The motion name.</param>
|
|
/// <param name="clone">Returns a cloned motion value instead.</param>
|
|
/// <returns>A motion value.</returns>
|
|
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;
|
|
ValidateChildren();
|
|
foreach (var c in WorkingChildren)
|
|
Children[c].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<Identifier, RealtimeMotionValue>(ChartPlayer.motionRegistry.Count);
|
|
CachedValues = new Dictionary<Identifier, MotionCache>(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<ContainerState>());
|
|
target.TypedChildren[t].Add(s);
|
|
}
|
|
|
|
public ContainerState Clone(byte ct) {
|
|
var r = (ContainerState)MemberwiseClone();
|
|
|
|
var mvs = new Dictionary<Identifier, RealtimeMotionValue>(ChartPlayer.motionRegistry.Count);
|
|
foreach (var mv in Values) {
|
|
mvs.Add(mv.Key, mv.Value.Clone());
|
|
}
|
|
r.Values = mvs;
|
|
|
|
var cvs = new Dictionary<Identifier, MotionCache>(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<EventContainer, ContainerState>();
|
|
foreach (var child in Children) {
|
|
var cc = child.Value.Clone(ct);
|
|
cc.Parent = r;
|
|
AddChild(child.Key, cc, r);
|
|
}
|
|
|
|
var pms = new Dictionary<StampedEvent, RealtimeMotionValue>(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;
|
|
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 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 < 16 && Handler != null) 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);
|
|
}
|
|
|
|
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<T>(Identifier key) where T : Vector {
|
|
return (T)GetRawValue(key);
|
|
}
|
|
|
|
static readonly Identifier n_pt = new Identifier("pt");
|
|
public Vector2 ScreenPoint {
|
|
get {
|
|
var mv = GetRawValue<VecPt>(n_pt);
|
|
return mv.ToVector2(ChartPlayer.hitRect);
|
|
}
|
|
}
|
|
|
|
static readonly Identifier n_dir = new Identifier("dir");
|
|
public Vector3 Direction {
|
|
get {
|
|
Vec3 r = GetRawValue<Vec3>(n_dir);
|
|
return r.ToVector3();
|
|
}
|
|
}
|
|
|
|
static readonly Identifier n_normal = new Identifier("normal");
|
|
public Vector3 Normal {
|
|
get {
|
|
Vec3 r = GetRawValue<Vec3>(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<VecPtComp>(n_sv).ToFloat(ChartPlayer.hitRect)
|
|
* GetRawValue<Vec1m>(n_svm).Value;
|
|
}
|
|
}
|
|
|
|
static readonly Identifier n_dist = new Identifier("dist");
|
|
public float Distance {
|
|
get {
|
|
var mv = GetRawValue<VecPtComp>(n_dist);
|
|
return mv.ToFloat(ChartPlayer.hitRect);
|
|
}
|
|
}
|
|
|
|
static readonly Identifier n_corner = new Identifier("corner");
|
|
public bool Corner {
|
|
get {
|
|
return GetRawValue<VecI1>(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<VecCtrl>(alt1 ? n_ctrl1 : n_ctrl0);
|
|
if (alt1 && mv.IsZero()) {
|
|
mv = GetRawValue<VecCtrl>(n_ctrl0);
|
|
}
|
|
return mv.ToVector3(ChartPlayer.hitRect, deltaz);
|
|
}
|
|
|
|
static readonly Identifier n_track = new Identifier("track");
|
|
public float Track {
|
|
get {
|
|
return GetRawValue<Vec1>(n_track).Value;
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
}
|