using Cryville.Common.Pdt; using Cryville.Crtr.Event; using System.Collections.Generic; using UnityEngine; using UnityEngine.Profiling; namespace Cryville.Crtr { public class SkinContainer { readonly SkinElement _rootElement; readonly DynamicStack[] _stacks = new DynamicStack[2]; class DynamicStack { public readonly List Properties = new List(); public readonly List Elements = new List(); public void Clear() { Properties.Clear(); Elements.Clear(); } } struct DynamicProperty { public RuntimeSkinContext Context { get; set; } public SkinPropertyKey Key { get; set; } public PdtExpression Value { get; set; } } struct DynamicElement { public RuntimeSkinContext Context { get; set; } public SkinSelectors Selectors { get; set; } public SkinElement Element { get; set; } } public SkinContainer(SkinElement rootElement) { _rootElement = rootElement; for (int i = 0; i < _stacks.Length; i++) _stacks[i] = new DynamicStack(); } public void MatchStatic(ISkinnableGroup group) { var stack = _stacks[0]; stack.Clear(); MatchStatic(_rootElement, group, stack, new RuntimeSkinContext(group.SkinContext)); } void MatchStatic(SkinElement rel, ISkinnableGroup group, DynamicStack stack, RuntimeSkinContext ctx) { var rc = ctx.ReadContext; ChartPlayer.etor.ContextTransform = rc.Transform; if (rc.PropSrcs != null) ChartPlayer.etor.ContextCascadeInsert(rc.PropSrcs); foreach (var p in rel.properties) { try { p.Key.ExecuteStatic(group, ctx, p.Value); } catch (EvaluationFailureException) { } if (p.Key.IsValueRequired && !p.Value.IsConstant) stack.Properties.Add( new DynamicProperty { Context = ctx, Key = p.Key, Value = p.Value } ); } ChartPlayer.etor.ContextTransform = null; foreach (var e in rel.elements) { try { var nctxs = e.Key.MatchStatic(group, rc); if (nctxs != null) { var roflag = e.Key.annotations.Contains("if"); var woflag = e.Key.annotations.Contains("then"); foreach (var nctx in nctxs) { var nrctx = new RuntimeSkinContext(nctx, ctx, roflag, woflag); MatchStatic(e.Value, group, stack, nrctx); } } } catch (SelectorNotAvailableException) { stack.Elements.Add( new DynamicElement { Context = ctx, Selectors = e.Key, Element = e.Value } ); } } if (rc.PropSrcs != null) ChartPlayer.etor.ContextCascadeDiscard(); } public void MatchDynamic(ISkinnableGroup group, int dl) { var stack = _stacks[dl]; if (stack.Properties.Count == 0 && stack.Elements.Count == 0) return; var nstack = dl + 1 < _stacks.Length ? _stacks[dl + 1] : null; if (nstack != null) nstack.Clear(); Profiler.BeginSample("SkinContainer.MatchDynamic"); for (int i = 0; i < stack.Properties.Count; i++) { DynamicProperty p = stack.Properties[i]; p.Key.ExecuteDynamic(group, p.Context, p.Value, dl); } for (int i = 0; i < stack.Elements.Count; i++) { DynamicElement e = stack.Elements[i]; var psrcs = e.Context.ReadContext.PropSrcs; if (psrcs != null) ChartPlayer.etor.ContextCascadeInsert(psrcs); SkinContext nctx = null; try { nctx = e.Selectors.MatchDynamic(group, e.Context.ReadContext); } catch (SelectorNotAvailableException) { if (nstack == null) throw; nstack.Elements.Add(e); } if (nctx != null) { MatchDynamic(e.Element, group, dl, nstack, new RuntimeSkinContext( nctx, e.Context, e.Selectors.annotations.Contains("if"), e.Selectors.annotations.Contains("then") )); if (e.Selectors.annotations.Contains("once")) { stack.Elements.RemoveAt(i--); } } if (psrcs != null) ChartPlayer.etor.ContextCascadeDiscard(); } Profiler.EndSample(); } void MatchDynamic(SkinElement rel, ISkinnableGroup group, int dl, DynamicStack stack, RuntimeSkinContext ctx) { var rc = ctx.ReadContext; ChartPlayer.etor.ContextTransform = rc.Transform; if (rc.PropSrcs != null) ChartPlayer.etor.ContextCascadeInsert(rc.PropSrcs); foreach (var p in rel.properties) { p.Key.ExecuteDynamic(group, ctx, p.Value, dl); } ChartPlayer.etor.ContextTransform = null; foreach (var e in rel.elements) { if (e.Key.IsUpdatable(group, dl)) { SkinContext nctx = e.Key.MatchDynamic(group, rc); if (nctx != null) MatchDynamic(e.Value, group, dl, stack, new RuntimeSkinContext( nctx, ctx, e.Key.annotations.Contains("if"), e.Key.annotations.Contains("then") )); } else { if (stack == null) throw new SelectorNotAvailableException(); stack.Elements.Add( new DynamicElement { Context = ctx, Selectors = e.Key, Element = e.Value } ); } } if (rc.PropSrcs != null) ChartPlayer.etor.ContextCascadeDiscard(); } } public class SkinContext { public Transform Transform { get; private set; } public Dictionary PropSrcs { get; private set; } public SkinContext(Transform transform, Dictionary propSrcs = null) { Transform = transform; PropSrcs = propSrcs; } } public struct RuntimeSkinContext { public SkinContext ReadContext { get; private set; } public Transform WriteTransform { get; private set; } public RuntimeSkinContext(SkinContext rw) { ReadContext = rw; WriteTransform = rw.Transform; } public RuntimeSkinContext(SkinContext r, Transform w) { ReadContext = r; WriteTransform = w; } public RuntimeSkinContext(SkinContext newctx, RuntimeSkinContext oldctx, bool roflag, bool woflag) { if (roflag) { ReadContext = newctx; WriteTransform = oldctx.WriteTransform; } else if (woflag) { ReadContext = oldctx.ReadContext; WriteTransform = newctx.Transform; } else { ReadContext = newctx; WriteTransform = newctx.Transform; } } } public interface ISkinnableGroup { string TypeName { get; } SkinContext SkinContext { get; } Anchor OpenedAnchor { get; } bool TryGetAnchorsByName(int name, out IReadOnlyCollection result); void RegisterAnchor(int name); void PushAnchorEvent(double time, int name); } }