using Cryville.Common; using Cryville.Common.Collections.Specialized; using Cryville.Common.Math; using Cryville.Common.Pdt; using Cryville.Crtr.Config; using Cryville.Crtr.Event; using Cryville.Crtr.Ruleset; using Cryville.Crtr.Skin; using System; using System.Collections.Generic; using System.Globalization; using UnityEngine; namespace Cryville.Crtr { public class PdtEvaluator : PdtEvaluatorBase { static PdtEvaluator m_instance; public static PdtEvaluator Instance { get { m_instance ??= new PdtEvaluator(); return m_instance; } } readonly Dictionary _shortops = new(); readonly IntKeyedDictionary _ctxops = new(); static readonly byte[] _nullbuf = new byte[0]; readonly byte[] _numbuf = new byte[4]; readonly PropSrc _vecsrc; Vector _vec; static readonly int _var_w = IdentifierManager.Shared.Request("w"); static readonly int _var_h = IdentifierManager.Shared.Request("h"); static readonly int _var_inf = IdentifierManager.Shared.Request("inf"); static readonly int _var_true = IdentifierManager.Shared.Request("true"); static readonly int _var_false = IdentifierManager.Shared.Request("false"); static readonly int _var_null = IdentifierManager.Shared.Request("null"); static readonly int _var_current_time = IdentifierManager.Shared.Request("current_time"); protected override void GetVariable(int name, bool forced, out int type, out byte[] value) { if (name == _var_w) { LoadNum(ChartPlayer.hitRect.width); type = PdtInternalType.Number; value = _numbuf; } else if (name == _var_h) { LoadNum(ChartPlayer.hitRect.height); type = PdtInternalType.Number; value = _numbuf; } else if (name == _var_inf) { LoadNum(float.PositiveInfinity); type = PdtInternalType.Number; value = _numbuf; } else if (name == _var_true) { LoadNum(1); type = PdtInternalType.Number; value = _numbuf; } else if (name == _var_false) { LoadNum(0); type = PdtInternalType.Number; value = _numbuf; } else if (name == _var_null) { LoadIdent(0); type = PdtInternalType.Undefined; value = _numbuf; } else { if (ContextEvent != null && ContextEvent.PropSrcs.TryGetValue(name, out PropSrc prop)) { prop.Get(out type, out value); } else if (ContextState != null && ChartPlayer.motionRegistry.ContainsKey(new Identifier(name))) { _vec = ContextState.GetComputedValue(name); _vecsrc.Invalidate(); _vecsrc.Get(out type, out value); RevokePotentialConstant(); } else if (ContextState != null && ContextState.Handler.PropSrcs.TryGetValue(name, out prop)) { prop.Get(out type, out value); RevokePotentialConstant(); } else if (ContextSkinContainer != null && ContextSkinContainer.Variables.TryGetValue(name, out PropStores.Float variable)) { variable.Source.Get(out type, out value); } else if (ContextRulesetConfig != null && ContextRulesetConfig.TryGetMappedSource(name, out prop)) { prop.Get(out type, out value); } else if (ContextJudge != null && ContextJudge.TryGetScoreSrc(name, out prop)) { prop.Get(out type, out value); RevokePotentialConstant(); } else { PropSrc result = ContextCascadeLookup(name); if (result != null) { result.Get(out type, out value); } else if (forced) { type = PdtInternalType.Error; value = _nullbuf; } else { type = PdtInternalType.Undefined; LoadIdent(name); value = _numbuf; } } } } unsafe void LoadIdent(int value) { fixed (byte* ptr = _numbuf) *(int*)ptr = value; } unsafe void LoadNum(float value) { fixed (byte* ptr = _numbuf) *(float*)ptr = value; } static readonly int _op_sep = IdentifierManager.Shared.Request(","); static readonly int _func_int_map = IdentifierManager.Shared.Request("int_map"); static readonly int _func_map = IdentifierManager.Shared.Request("map"); protected override PdtOperator GetOperator(PdtOperatorSignature sig) { if (_shortops.TryGetValue(sig, out PdtOperator result)) { return result; } else if (_ctxops.TryGetValue(sig.Name, out result)) { return result; } else if (sig.Name == _op_sep) { result = new op_arr(sig.ParamCount); _shortops.Add(new PdtOperatorSignature(_op_sep, sig.ParamCount), result); return result; } else if (sig.Name == _func_int_map) { result = new func_int_map(sig.ParamCount); _shortops.Add(new PdtOperatorSignature(_func_int_map, sig.ParamCount), result); return result; } else if (sig.Name == _func_map) { result = new func_map(sig.ParamCount); _shortops.Add(new PdtOperatorSignature(_func_map, sig.ParamCount), result); return result; } else throw new KeyNotFoundException(string.Format("Undefined operator {0}", sig)); } static readonly int _colop_and = IdentifierManager.Shared.Request("&"); static readonly int _colop_or = IdentifierManager.Shared.Request("|"); protected override bool Collapse(int name, PdtVariableMemory param) { if (name == _colop_and) return (param.Type == PdtInternalType.Number || param.Type == PdtInternalType.Vector) && param.AsNumber() <= 0; else if (name == _colop_or) return (param.Type != PdtInternalType.Number && param.Type != PdtInternalType.Vector) || param.AsNumber() > 0; else throw new KeyNotFoundException(string.Format("Undefined collapse operator {0}", IdentifierManager.Shared.Retrieve(name))); } public ChartEvent ContextEvent { get; set; } public ContainerState ContextState { get; set; } public SkinContainer ContextSkinContainer { get; set; } public Transform ContextTransform { get; set; } public RulesetConfigStore ContextRulesetConfig { get; set; } public Judge ContextJudge { get; set; } public PropSrc ContextSelfValue { get; set; } readonly Stack ContextCascadeBlocks = new(); public void ContextCascadeInsertBlock() { ContextCascadeBlocks.Push(_cascadeHeight); } public void ContextCascadeDiscardBlock() { ContextCascadeBlocks.Pop(); } readonly IntKeyedDictionary[] ContextCascade = new IntKeyedDictionary[256]; int _cascadeHeight; public void ContextCascadeInsert() { _cascadeHeight++; } public void ContextCascadeInsert(IntKeyedDictionary srcs) { ContextCascadeInsert(); foreach (var src in srcs) ContextCascadeUpdate(src.Key, src.Value); } public void ContextCascadeUpdate(int key, PropSrc value) { ContextCascade[_cascadeHeight - 1][key] = value; } public PropSrc ContextCascadeLookup(int name) { for (int i = _cascadeHeight - 1; i >= ContextCascadeBlocks.Peek(); i--) { var cas = ContextCascade[i]; if (cas.TryGetValue(name, out PropSrc result)) { return result; } } return null; } public void ContextCascadeDiscard() { ContextCascade[--_cascadeHeight].Clear(); } public void Reset() { _cascadeHeight = 0; ContextCascadeBlocks.Clear(); ContextCascadeBlocks.Push(0); ContextEvent = null; ContextJudge = null; ContextRulesetConfig = null; ContextSelfValue = null; ContextSkinContainer = null; ContextState = null; ContextTransform = null; } public PdtEvaluator() { ContextCascadeBlocks.Push(0); for (int i = 0; i < ContextCascade.Length; i++) ContextCascade[i] = new IntKeyedDictionary(); _vecsrc = new VectorSrc(() => _vec); _shortops.Add(new PdtOperatorSignature("@", 2), new op_at_2()); _shortops.Add(new PdtOperatorSignature("*", 2), new op_mul_2()); _shortops.Add(new PdtOperatorSignature("/", 2), new op_div_2()); _shortops.Add(new PdtOperatorSignature("%", 2), new op_mod_2()); _shortops.Add(new PdtOperatorSignature("+", 1), new op_add_1()); _shortops.Add(new PdtOperatorSignature("+", 2), new op_add_2()); _shortops.Add(new PdtOperatorSignature("-", 1), new op_sub_1()); _shortops.Add(new PdtOperatorSignature("-", 2), new op_sub_2()); _shortops.Add(new PdtOperatorSignature("=", 2), new op_eq_2()); _shortops.Add(new PdtOperatorSignature("<", 2), new op_lt_2()); _shortops.Add(new PdtOperatorSignature(">", 2), new op_gt_2()); _shortops.Add(new PdtOperatorSignature("!", 1), new op_not_1()); _shortops.Add(new PdtOperatorSignature("frame_seq", 3), new func_frame_seq()); _shortops.Add(new PdtOperatorSignature("is", 2), new func_is()); _shortops.Add(new PdtOperatorSignature("is_in", 1), new func_is_in()); _ctxops.Add(IdentifierManager.Shared.Request("screen_edge"), new func_screen_edge(() => ContextTransform)); _ctxops.Add(IdentifierManager.Shared.Request("int"), new func_int(() => ContextSelfValue)); _ctxops.Add(IdentifierManager.Shared.Request("clamp"), new func_clamp(() => ContextSelfValue)); _ctxops.Add(IdentifierManager.Shared.Request("min"), new func_min(() => ContextSelfValue)); _ctxops.Add(IdentifierManager.Shared.Request("max"), new func_max(() => ContextSelfValue)); _ctxops.Add(IdentifierManager.Shared.Request("abs"), new func_abs(() => ContextSelfValue)); _ctxops.Add(IdentifierManager.Shared.Request("anim"), new func_anim(() => ContextSelfValue)); _ctxops.Add(IdentifierManager.Shared.Request("cubic_bezier"), new func_cubic_bezier(() => ContextSelfValue)); _ctxops.Add(IdentifierManager.Shared.Request("ease"), new func_cubic_bezier_fixed(0.25f, 0.1f, 0.25f, 1f, () => ContextSelfValue)); _ctxops.Add(IdentifierManager.Shared.Request("ease_in"), new func_cubic_bezier_fixed(0.42f, 0f, 1f, 1f, () => ContextSelfValue)); _ctxops.Add(IdentifierManager.Shared.Request("ease_out"), new func_cubic_bezier_fixed(0f, 0f, 0.58f, 1f, () => ContextSelfValue)); _ctxops.Add(IdentifierManager.Shared.Request("ease_in_out"), new func_cubic_bezier_fixed(0.42f, 0f, 0.58f, 1f, () => ContextSelfValue)); _ctxops.Add(IdentifierManager.Shared.Request("precision"), new func_precision(() => ContextSelfValue)); _ctxops.Add(IdentifierManager.Shared.Request("interval"), new func_interval(() => ContextSelfValue)); _ctxops.Add(IdentifierManager.Shared.Request("circle"), new func_sphere(() => ContextSelfValue)); _ctxops.Add(IdentifierManager.Shared.Request("sphere"), new func_sphere(() => ContextSelfValue)); PropSrc cccb(int k) => ContextCascadeLookup(k); _ctxops.Add(IdentifierManager.Shared.Request("attack_timing"), new func_attack_timing(cccb)); _ctxops.Add(IdentifierManager.Shared.Request("release_timing"), new func_release_timing(cccb)); _ctxops.Add(IdentifierManager.Shared.Request("enter_timing"), new func_enter_timing(cccb)); _ctxops.Add(IdentifierManager.Shared.Request("leave_timing"), new func_leave_timing(cccb)); PdtExpression jacb(int k) => ContextJudge._areaFuncs[new Identifier(k)]; _ctxops.Add(IdentifierManager.Shared.Request("attack_timed_area"), new func_attack_timed_area(cccb, jacb, this)); _ctxops.Add(IdentifierManager.Shared.Request("release_timed_area"), new func_release_timed_area(cccb, jacb, this)); _ctxops.Add(IdentifierManager.Shared.Request("enter_timed_area"), new func_enter_or_leave_timed_area(cccb, jacb, this, true)); _ctxops.Add(IdentifierManager.Shared.Request("leave_timed_area"), new func_enter_or_leave_timed_area(cccb, jacb, this, false)); _ctxops.Add(IdentifierManager.Shared.Request("enter_timing_area"), new func_enter_or_leave_timing_area(cccb, jacb, this, true)); _ctxops.Add(IdentifierManager.Shared.Request("leave_timing_area"), new func_enter_or_leave_timing_area(cccb, jacb, this, false)); _ctxops.Add(IdentifierManager.Shared.Request("in_timing"), new func_in_timing(cccb)); _ctxops.Add(IdentifierManager.Shared.Request("in_area"), new func_in_area(cccb, jacb, this)); } #region Operators #pragma warning disable IDE1006 #region Basic Operators class op_add_1 : PdtOperator { public op_add_1() : base(1) { } protected override void Execute() { float result = GetOperand(0).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_add_2 : PdtOperator { public op_add_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() + GetOperand(1).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_sub_1 : PdtOperator { public op_sub_1() : base(1) { } protected override void Execute() { float result = -GetOperand(0).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_sub_2 : PdtOperator { public op_sub_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() - GetOperand(1).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_mul_2 : PdtOperator { public op_mul_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() * GetOperand(1).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_div_2 : PdtOperator { public op_div_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() / GetOperand(1).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_mod_2 : PdtOperator { public op_mod_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() % GetOperand(1).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_eq_2 : PdtOperator { public op_eq_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() == GetOperand(1).AsNumber() ? 1 : 0; GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_lt_2 : PdtOperator { public op_lt_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() < GetOperand(1).AsNumber() ? 1 : 0; GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_gt_2 : PdtOperator { public op_gt_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() > GetOperand(1).AsNumber() ? 1 : 0; GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_not_1 : PdtOperator { public op_not_1() : base(1) { } protected override void Execute() { float result = GetOperand(0).AsNumber() <= 0 ? 1 : 0; GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_arr : PdtOperator { public op_arr(int pc) : base(pc) { } protected override void Execute() { var o0 = GetOperand(0); int type = o0.Type; int len = o0.Length; bool blit = !IsBlittable(type); for (var i = 1; i < LoadedOperandCount; i++) { var o = GetOperand(i); if (o.Type != type) throw new InvalidOperationException("Cannot create variant type array"); else if (!IsBlittable(o.Type)) blit = true; len += o.Length; } if (blit) GetReturnFrame(PdtInternalType.Array, len + 2 * sizeof(int)).SetArraySuffix(type, LoadedOperandCount); else GetReturnFrame(PdtInternalType.Vector, len + sizeof(int)).SetArraySuffix(type); } static bool IsBlittable(int type) { return type == PdtInternalType.Number; } } class op_at_2 : PdtOperator { public op_at_2() : base(2) { } protected override void Execute() { int _; var op0 = GetOperand(0); var op1 = (int)Math.Round(GetOperand(1).AsNumber()); if (op0.Type == PdtInternalType.Vector) { op0.GetArraySuffix(out _, out _); if (op1 >= (op0.Length - sizeof(int)) / sizeof(float)) throw new IndexOutOfRangeException(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(op0.AsNumber(op1 * sizeof(float))); } else if (op0.Type == PdtInternalType.Number) { if (op1 != 0) throw new IndexOutOfRangeException(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(op0.AsNumber()); } else throw new InvalidOperationException("Not a vector or number"); } } #endregion #region Basic Functions class func_frame_seq : PdtOperator { public func_frame_seq() : base(3) { } protected override unsafe void Execute() { string pf = GetOperand(0).AsString(); int f = (int)GetOperand(1).AsNumber(); int t = (int)GetOperand(2).AsNumber(); int pfl = pf.Length; int fc = t - f + 1; if (fc <= 0) throw new ArgumentException("Start index is greater than end index"); int l = (f == 0 ? 1 : 0) + pfl * fc; for (int p = 1, i = 1; p < 4; p++, i *= 10) { int j = i * 10 - 1; if (f <= j && t >= i) { int m = Math.Max(f, i); int n = Math.Min(t, j); l += (n - m + 1) * i; } } l *= sizeof(char); l += sizeof(int) * (fc + 2); var ret = GetReturnFrame(PdtInternalType.Array, l); ret.SetArraySuffix(PdtInternalType.String, fc); int o = 0; for (int i = f; i <= t; i++) { var s = pf + i.ToString(CultureInfo.InvariantCulture); ret.SetString(s, o); o += sizeof(int) + s.Length * sizeof(char); } } } class func_int_map : PdtOperator { public func_int_map(int pc) : base(pc) { if (pc < 4) throw new ArgumentOutOfRangeException("Too few parameters for int_map"); } protected override unsafe void Execute() { var value = GetOperand(0).AsNumber(); var offset = GetOperand(1).AsNumber(); var step = GetOperand(2).AsNumber(); var index = (int)((value - offset) / step); if (index < 0 || index >= LoadedOperandCount - 3) index = 0; var hit = GetOperand(index + 3); var ret = GetReturnFrame(hit.Type, hit.Length); hit.CopyTo(ret); } } class func_is : PdtOperator { public func_is() : base(2) { } protected override unsafe void Execute() { GetReturnFrame(PdtInternalType.Number, sizeof(float)) .SetNumber(GetOperand(0).Equals(GetOperand(1)) ? 1 : 0); } } class func_is_in : PdtOperator { public func_is_in() : base(1) { } protected override unsafe void Execute() { var arg = GetOperand(0); if (arg.Type == PdtInternalType.Number && arg.AsNumber() <= 0) { GetReturnFrame(PdtInternalType.Null, 0); } else { var ret = GetReturnFrame(arg.Type, arg.Length); arg.CopyTo(ret); } } } class func_map : PdtOperator { public func_map(int pc) : base(pc) { if (pc < 2) throw new ArgumentOutOfRangeException("Too few parameters for map"); } protected override unsafe void Execute() { var value = GetOperand(0).AsNumber(); var index = (int)value; if (index < 0 || index >= LoadedOperandCount - 1) index = 0; var hit = GetOperand(index + 1); var ret = GetReturnFrame(hit.Type, hit.Length); hit.CopyTo(ret); } } #endregion #region Contextual Functions class func_screen_edge : PdtOperator { readonly Func _ctxcb; public func_screen_edge(Func ctxcb) : base(1) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ctx = _ctxcb(); var ray = new Ray(ctx.position, ctx.rotation * Vector3.forward); ChartPlayer.frustumPlanes[(int)GetOperand(0).AsNumber()].Raycast(ray, out float dist); var ret = GetReturnFrame(PdtInternalType.Vector, sizeof(Vector3) + sizeof(int)); ret.Set(ray.GetPoint(dist)); ret.SetArraySuffix(PdtInternalType.Number); } } class func_int : PdtOperator { readonly Func _ctxcb; public func_int(Func ctxcb) : base(1) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); var v = LoadedOperandCount switch { 0 => oputil.AsNumber(_ctxcb()), 1 => GetOperand(0).AsNumber(), _ => throw new ArgumentException("Argument count not 0 or 1"), }; ret.SetNumber(Mathf.Floor(v)); } } class func_clamp : PdtOperator { readonly Func _ctxcb; public func_clamp(Func ctxcb) : base(3) { _ctxcb = ctxcb; } protected override unsafe void Execute() { float min, value, max; switch (LoadedOperandCount) { case 2: value = oputil.AsNumber(_ctxcb()); min = GetOperand(0).AsNumber(); max = GetOperand(1).AsNumber(); break; case 3: value = GetOperand(0).AsNumber(); min = GetOperand(1).AsNumber(); max = GetOperand(2).AsNumber(); break; default: throw new ArgumentException("Argument count not 2 or 3"); } var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); ret.SetNumber(Mathf.Clamp(value, min, max)); } } class func_min : PdtOperator { readonly Func _ctxcb; public func_min(Func ctxcb) : base(2) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); float a = GetOperand(0).AsNumber(); var b = LoadedOperandCount switch { 1 => oputil.AsNumber(_ctxcb()), 2 => GetOperand(1).AsNumber(), _ => throw new ArgumentException("Argument count not 2 or 3"), }; ret.SetNumber(Mathf.Min(a, b)); } } class func_max : PdtOperator { readonly Func _ctxcb; public func_max(Func ctxcb) : base(2) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); var a = GetOperand(0).AsNumber(); var b = LoadedOperandCount switch { 1 => oputil.AsNumber(_ctxcb()), 2 => GetOperand(1).AsNumber(), _ => throw new ArgumentException("Argument count not 2 or 3"), }; ret.SetNumber(Mathf.Max(a, b)); } } class func_abs : PdtOperator { readonly Func _ctxcb; public func_abs(Func ctxcb) : base(1) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); var arg = LoadedOperandCount switch { 0 => oputil.AsNumber(_ctxcb()), 1 => GetOperand(0).AsNumber(), _ => throw new ArgumentException("Argument count not 0 or 1"), }; ret.SetNumber(Mathf.Abs(arg)); } } class func_anim : PdtOperator { readonly Func _ctxcb; public func_anim(Func ctxcb) : base(3) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var op1 = GetOperand(0); var op2 = GetOperand(1); var dim1 = GetDimension(op1); var dim2 = GetDimension(op2); var dim0 = Math.Min(dim1, dim2); Vector4 trans; switch (LoadedOperandCount) { case 2: var num = oputil.AsNumber(_ctxcb()); trans = new Vector4(num, num, num, num); break; case 3: trans = GetTransition(GetOperand(2)); break; default: throw new ArgumentException("Argument count not 2 or 3"); } if (dim0 == 1) { GetReturnFrame(PdtInternalType.Number, sizeof(float)) .SetNumber(op1.AsNumber() * (1 - trans.x) + op2.AsNumber() * trans.x); } else { var ret = GetReturnFrame(PdtInternalType.Vector, dim0 * sizeof(float) + sizeof(int)); for (int i = 0; i < dim0 * sizeof(float); i += sizeof(float)) { ret.SetNumber(op1.AsNumber(i) * (1 - trans[i]) + op2.AsNumber(i) * trans[i], i); } ret.SetArraySuffix(PdtInternalType.Number); } } static int GetDimension(PdtVariableMemory op) { switch (op.Type) { case PdtInternalType.Number: return 1; case PdtInternalType.Vector: int arrtype, _; op.GetArraySuffix(out arrtype, out _); if (arrtype != PdtInternalType.Number) throw new ArgumentException("Not animatable"); return (op.Length - sizeof(int)) / sizeof(float); default: throw new ArgumentException("Not animatable"); } } static Vector4 GetTransition(PdtVariableMemory op) { switch (op.Type) { case PdtInternalType.Number: var num = op.AsNumber(); return new Vector4(num, num, num, num); case PdtInternalType.Vector: int arrtype, _; op.GetArraySuffix(out arrtype, out _); if (arrtype != PdtInternalType.Number) throw new ArgumentException("Not animatable"); switch ((op.Length - sizeof(int)) / sizeof(float)) { case 0: throw new ArgumentException("Empty vector"); case 1: num = op.AsNumber(); return new Vector4(num, num, num, num); case 2: return new Vector4(op.AsNumber(), op.AsNumber(sizeof(float)), 0, 0); case 3: return new Vector4(op.AsNumber(), op.AsNumber(sizeof(float)), op.AsNumber(2 * sizeof(float)), 0); default: return new Vector4(op.AsNumber(), op.AsNumber(sizeof(float)), op.AsNumber(2 * sizeof(float)), op.AsNumber(3 * sizeof(float))); } default: throw new ArgumentException("Not animatable"); } } } class func_cubic_bezier : PdtOperator { readonly Func _ctxcb; public func_cubic_bezier(Func ctxcb) : base(5) { _ctxcb = ctxcb; } protected override unsafe void Execute() { float x1 = GetOperand(0).AsNumber(), y1 = GetOperand(1).AsNumber(); float x2 = GetOperand(2).AsNumber(), y2 = GetOperand(3).AsNumber(); var time = LoadedOperandCount switch { 4 => oputil.AsNumber(_ctxcb()), 5 => GetOperand(4).AsNumber(), _ => throw new ArgumentException("Argument count not 4 or 5"), }; GetReturnFrame(PdtInternalType.Number, sizeof(float)) .SetNumber(CubicBezier.Evaluate(time, x1, y1, x2, y2, 1e-3f)); } } class func_cubic_bezier_fixed : PdtOperator { readonly float x1, y1, x2, y2; readonly Func _ctxcb; public func_cubic_bezier_fixed(float x1, float y1, float x2, float y2, Func ctxcb) : base(1) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; _ctxcb = ctxcb; } protected override void Execute() { var time = LoadedOperandCount switch { 0 => oputil.AsNumber(_ctxcb()), 1 => GetOperand(0).AsNumber(), _ => throw new ArgumentException("Argument count not 0 or 1"), }; GetReturnFrame(PdtInternalType.Number, sizeof(float)) .SetNumber(CubicBezier.Evaluate(time, x1, y1, x2, y2, 1e-3f)); } } class func_precision : PdtOperator { readonly Func _ctxcb; public func_precision(Func ctxcb) : base(2) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); float v, p; switch (LoadedOperandCount) { case 1: v = oputil.AsNumber(_ctxcb()); p = GetOperand(0).AsNumber(); break; case 2: v = GetOperand(0).AsNumber(); p = GetOperand(1).AsNumber(); break; default: throw new ArgumentException("Argument count not 1 or 2"); } double pp = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(p))) + 1); double dp = pp * Math.Round(p / pp, 7); ret.SetNumber((float)(Math.Round((double)v / dp) * dp)); } } #endregion #region Area Functions class func_interval : PdtOperator { readonly Func _ctxcb; public func_interval(Func ctxcb) : base(3) { _ctxcb = ctxcb; } protected override unsafe void Execute() { float min, value, max; switch (LoadedOperandCount) { case 2: value = oputil.AsNumber(_ctxcb()); min = GetOperand(0).AsNumber(); max = GetOperand(1).AsNumber(); break; case 3: value = GetOperand(0).AsNumber(); min = GetOperand(1).AsNumber(); max = GetOperand(2).AsNumber(); break; default: throw new ArgumentException("Argument count not 2 or 3"); } float center = (max + min) / 2; float extent = (max - min) / 2; var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); ret.SetNumber(1 - Math.Abs(value - center) / extent); } } class func_sphere : PdtOperator { readonly Func _ctxcb; public func_sphere(Func ctxcb) : base(3) { _ctxcb = ctxcb; } protected override unsafe void Execute() { Vector4? value, center; float radius; switch (LoadedOperandCount) { case 2: value = oputil.AsVector(_ctxcb()); center = oputil.AsVector(GetOperand(0)); radius = GetOperand(1).AsNumber(); break; case 3: value = oputil.AsVector(GetOperand(0)); center = oputil.AsVector(GetOperand(1)); radius = GetOperand(2).AsNumber(); break; default: throw new ArgumentException("Argument count not 2 or 3"); } if (value == null) { GetReturnFrame(PdtInternalType.Null, 0); return; } if (center == null) throw new ArgumentException("Null sphere center"); var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); ret.SetNumber(1 - Vector4.Distance(center.Value, value.Value) / radius); } } #endregion #region Judge Functions #region Primary static readonly int _var_fn = IdentifierManager.Shared.Request("judge_time_from"); static readonly int _var_tn = IdentifierManager.Shared.Request("judge_time_to"); static readonly int _var_ft = IdentifierManager.Shared.Request("input_time_from"); static readonly int _var_tt = IdentifierManager.Shared.Request("input_time_to"); static readonly int _var_fv = IdentifierManager.Shared.Request("input_vec_from"); static readonly int _var_tv = IdentifierManager.Shared.Request("input_vec_to"); abstract class JudgeFunction : PdtOperator { readonly Func _ctxcb; protected JudgeFunction(int pc, Func ctxcb) : base(pc) { _ctxcb = ctxcb; } protected sealed override void Execute() { var fn = oputil.AsNumber(_ctxcb(_var_fn)); var tn = oputil.AsNumber(_ctxcb(_var_tn)); var ft = oputil.AsNumber(_ctxcb(_var_ft)); var tt = oputil.AsNumber(_ctxcb(_var_tt)); var fv = oputil.AsVector(_ctxcb(_var_fv)); var tv = oputil.AsVector(_ctxcb(_var_tv)); var result = ExecuteImpl(fn, tn, ft, tt, fv, tv); var ret = GetReturnFrame(PdtInternalType.Vector, 6 * sizeof(float) + sizeof(int)); ret.SetArraySuffix(PdtInternalType.Number); ret.SetNumber(result.Value); if (result.Value <= 0) return; ret.SetNumber(result.Time, sizeof(float)); Vector4 rv; if (result.Time == ft) { if (!fv.HasValue) { if (result.Time == tt) { if (!tv.HasValue) throw new ArgumentException("Internal judge error"); rv = tv.Value; } else throw new ArgumentException("Internal judge error"); } else rv = fv.Value; } else if (result.Time == tt) { if (!tv.HasValue) throw new ArgumentException("Internal judge error"); rv = tv.Value; } else { if (!fv.HasValue || !tv.HasValue) throw new ArgumentException("Internal judge error"); var tr = (result.Time - ft) / (tt - ft); rv = (1 - tr) * fv.Value + tr * tv.Value; } for (int i = 0; i < 4; i++) { ret.SetNumber(rv[i], (i + 2) * sizeof(float)); } } protected abstract JudgeFunctionResult ExecuteImpl(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv); protected struct JudgeFunctionResult { /// /// The result value, represented with a signed distance or a result boolean (0 or 1.) /// public float Value; public float Time; public JudgeFunctionResult(float value, float time = 0) { Value = value; Time = time; } } } #region Timing class func_attack_timing : JudgeFunction { public func_attack_timing(Func ctxcb) : base(2, ctxcb) { } protected override JudgeFunctionResult ExecuteImpl(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv) { if (fv != null) return new JudgeFunctionResult(); var t0 = GetOperand(0).AsNumber() + fn; var t1 = GetOperand(1).AsNumber() + tn; return new JudgeFunctionResult((tt > t0 && tt <= t1) ? 1 : 0, tt); } } class func_enter_timing : JudgeFunction { public func_enter_timing(Func ctxcb) : base(1, ctxcb) { } protected override JudgeFunctionResult ExecuteImpl(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv) { if (fv == null || tv == null) return new JudgeFunctionResult(); var t0 = GetOperand(0).AsNumber() + fn; return new JudgeFunctionResult((ft < t0 && tt >= t0) ? 1 : 0, t0); } } class func_release_timing : JudgeFunction { public func_release_timing(Func ctxcb) : base(2, ctxcb) { } protected override JudgeFunctionResult ExecuteImpl(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv) { if (tv != null) return new JudgeFunctionResult(); var t0 = GetOperand(0).AsNumber() + fn; var t1 = GetOperand(1).AsNumber() + tn; return new JudgeFunctionResult((ft > t0 && ft <= t1) ? 1 : 0, ft); } } class func_leave_timing : JudgeFunction { public func_leave_timing(Func ctxcb) : base(1, ctxcb) { } protected override JudgeFunctionResult ExecuteImpl(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv) { if (fv == null || tv == null) return new JudgeFunctionResult(); var t1 = GetOperand(0).AsNumber() + tn; return new JudgeFunctionResult((ft < t1 && tt >= t1) ? 1 : 0, t1); } } #endregion #region Timed Area / Timing Area abstract class AreaJudgeFunction : JudgeFunction { readonly Func _jacb; readonly PdtEvaluator _etor; float _area; readonly PropOp _areaOp; Vector4 _vec; readonly PropSrc _vecSrc; PdtExpression _areaFunc; public AreaJudgeFunction(int pc, Func ctxcb, Func jacb, PdtEvaluator etor) : base(pc, ctxcb) { _jacb = jacb; _etor = etor; _areaOp = new PropOp.Float(v => _area = v); _vecSrc = new PropSrc.Vector4(() => _vec); } protected sealed override JudgeFunctionResult ExecuteImpl(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv) { _areaFunc = _jacb(GetOperand(2).AsIdentifier()); return ExecuteImpl2(fn, tn, ft, tt, fv, tv); } protected abstract JudgeFunctionResult ExecuteImpl2(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv); protected float EvaluateArea(Vector4? vec) { if (vec == null) { _etor.ContextSelfValue = PropSrc.Null; } else { _vec = vec.Value; _vecSrc.Invalidate(); _etor.ContextSelfValue = _vecSrc; } _etor.Evaluate(_areaOp, _areaFunc); _etor.ContextSelfValue = null; return _area; } protected JudgeFunctionResult IntersectsWithAreaBounds(float ft, float tt, Vector4 fiv, Vector4 tiv, bool enter) { for (int i = 1; i <= ChartPlayer.areaJudgePrecision; i++) { float r = (float)i / ChartPlayer.areaJudgePrecision; if ((EvaluateArea((1 - r) * fiv + r * tiv) > 0) == enter) { float l = (float)(i - 1) / ChartPlayer.areaJudgePrecision; while (!Mathf.Approximately(l, r)) { float m = (l + r) / 2; float v = EvaluateArea((1 - m) * fiv + m * tiv); if (v == 0) return new JudgeFunctionResult(1, (1 - m) * ft + m * tt); else if ((v > 0) == enter) r = m; else l = m; } return new JudgeFunctionResult(1, (1 - l) * ft + l * tt); } } return new JudgeFunctionResult(); } protected bool GetInternalTimeAndVector(float t0, float t1, ref float ft, ref float tt, ref Vector4 fv, ref Vector4 tv) { if (ft < t0) { if (tt < t0) return false; var fr = (t0 - ft) / (tt - ft); ft = (1 - fr) * ft + fr * tt; fv = (1 - fr) * fv + fr * tv; } else if (ft < t1) { } else return false; if (tt < t1) { } else { var tr = (t1 - ft) / (tt - ft); tt = (1 - tr) * ft + tr * tt; tv = (1 - tr) * fv + tr * tv; } return true; } } class func_attack_timed_area : AreaJudgeFunction { public func_attack_timed_area(Func ctxcb, Func jacb, PdtEvaluator etor) : base(3, ctxcb, jacb, etor) { } protected override JudgeFunctionResult ExecuteImpl2(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv) { if (fv != null) return new JudgeFunctionResult(); var t0 = GetOperand(0).AsNumber() + fn; var t1 = GetOperand(1).AsNumber() + tn; if (tt <= t0 || tt > t1) return new JudgeFunctionResult(); return new JudgeFunctionResult(EvaluateArea(tv), tt); } } class func_release_timed_area : AreaJudgeFunction { public func_release_timed_area(Func ctxcb, Func jacb, PdtEvaluator etor) : base(3, ctxcb, jacb, etor) { } protected override JudgeFunctionResult ExecuteImpl2(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv) { if (tv != null) return new JudgeFunctionResult(); var t0 = GetOperand(0).AsNumber() + fn; var t1 = GetOperand(1).AsNumber() + tn; if (ft <= t0 || ft > t1) return new JudgeFunctionResult(); return new JudgeFunctionResult(EvaluateArea(fv), ft); } } class func_enter_or_leave_timed_area : AreaJudgeFunction { readonly bool _enter; public func_enter_or_leave_timed_area(Func ctxcb, Func jacb, PdtEvaluator etor, bool enter) : base(3, ctxcb, jacb, etor) { _enter = enter; } protected override JudgeFunctionResult ExecuteImpl2(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv) { if (fv == null || tv == null) return new JudgeFunctionResult(); var t0 = GetOperand(0).AsNumber() + fn; var t1 = GetOperand(1).AsNumber() + tn; Vector4 fiv = fv.Value, tiv = tv.Value; if (!GetInternalTimeAndVector(t0, t1, ref ft, ref tt, ref fiv, ref tiv)) return new JudgeFunctionResult(); if ((EvaluateArea(fiv) > 0) == _enter) return new JudgeFunctionResult(); return IntersectsWithAreaBounds(ft, tt, fiv, tiv, _enter); } } class func_enter_or_leave_timing_area : AreaJudgeFunction { readonly bool _enter; public func_enter_or_leave_timing_area(Func ctxcb, Func jacb, PdtEvaluator etor, bool enter) : base(3, ctxcb, jacb, etor) { _enter = enter; } protected override JudgeFunctionResult ExecuteImpl2(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv) { if (fv == null || tv == null) return new JudgeFunctionResult(); var t0 = GetOperand(0).AsNumber() + fn; var t1 = GetOperand(1).AsNumber() + tn; if ((EvaluateArea(fv) > 0 && ft > t0 && ft < t1) == _enter) return new JudgeFunctionResult(); Vector4 fiv = fv.Value, tiv = tv.Value; if (!GetInternalTimeAndVector(t0, t1, ref ft, ref tt, ref fiv, ref tiv)) return new JudgeFunctionResult(); return IntersectsWithAreaBounds(ft, tt, fiv, tiv, _enter); } } #endregion #endregion #region Secondary static readonly int _var_jt = IdentifierManager.Shared.Request("hit_time"); static readonly int _var_jv = IdentifierManager.Shared.Request("hit_vec"); class func_in_timing : PdtOperator { readonly Func _ctxcb; public func_in_timing(Func ctxcb) : base(2) { _ctxcb = ctxcb; } protected sealed override void Execute() { var fn = oputil.AsNumber(_ctxcb(_var_fn)); var tn = oputil.AsNumber(_ctxcb(_var_tn)); var t0 = GetOperand(0).AsNumber() + fn; var t1 = GetOperand(1).AsNumber() + tn; var jt = oputil.AsNumber(_ctxcb(_var_jt)); var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); ret.SetNumber((jt > t0 && jt <= t1) ? 1 : 0); } } class func_in_area : PdtOperator { readonly Func _ctxcb; readonly Func _jacb; readonly PdtEvaluator _etor; float _area; readonly PropOp _areaOp; Vector4 _vec; readonly PropSrc _vecSrc; public func_in_area(Func ctxcb, Func jacb, PdtEvaluator etor) : base(2) { _ctxcb = ctxcb; _jacb = jacb; _etor = etor; _areaOp = new PropOp.Float(v => _area = v); _vecSrc = new PropSrc.Vector4(() => _vec); } protected sealed override void Execute() { var jv = oputil.AsVector(_ctxcb(_var_jv)); if (jv == null) { _etor.ContextSelfValue = PropSrc.Null; } else { _vec = jv.Value; _vecSrc.Invalidate(); _etor.ContextSelfValue = _vecSrc; } _etor.Evaluate(_areaOp, _jacb(GetOperand(0).AsIdentifier())); _etor.ContextSelfValue = null; var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); ret.SetNumber(_area); } } #endregion #endregion static unsafe class oputil { public static float AsNumber(PropSrc src) { if (src == null) throw new ArgumentNullException("src"); src.Get(out int type, out byte[] value); if (type != PdtInternalType.Number && type != PdtInternalType.Vector) throw new ArgumentException("Not a number"); fixed (byte* ptr = value) { return *(float*)ptr; } } public static Vector4? AsVector(PropSrc src) { if (src == null) throw new ArgumentNullException("src"); src.Get(out int type, out byte[] value); if (type == PdtInternalType.Vector) { fixed (byte* ptr = value) { return *(Vector4*)ptr; } } else if (type == PdtInternalType.Number) { fixed (byte* ptr = value) { return new Vector4(*(float*)ptr, 0, 0, 0); } } else if (type == PdtInternalType.Null) { return null; } else throw new ArgumentException("Not a vector"); } public static Vector4? AsVector(PdtVariableMemory op) { if (op.Type == PdtInternalType.Vector) { return ((op.Length - sizeof(int)) / sizeof(float)) switch { 0 => null, 1 => (Vector4?)new Vector4(op.AsNumber(), 0, 0, 0), 2 => (Vector4?)new Vector4(op.AsNumber(), op.AsNumber(sizeof(float)), 0, 0), 3 => (Vector4?)new Vector4(op.AsNumber(), op.AsNumber(sizeof(float)), op.AsNumber(2 * sizeof(float)), 0), _ => (Vector4?)new Vector4(op.AsNumber(), op.AsNumber(sizeof(float)), op.AsNumber(2 * sizeof(float)), op.AsNumber(3 * sizeof(float))), }; } else if (op.Type == PdtInternalType.Number) { return new Vector4(op.AsNumber(), 0, 0, 0); } else if (op.Type == PdtInternalType.Null) { return null; } else throw new ArgumentException("Not a vector"); } } #pragma warning restore IDE1006 #endregion } }