From 5bab0ca6485422f2e34afef589b5afae5aaeb3e1 Mon Sep 17 00:00:00 2001 From: PopSlime Date: Sun, 14 May 2023 12:06:32 +0800 Subject: [PATCH] Add area judge functions. --- Assets/Cryville/Crtr/PdtEvaluator.cs | 139 ++++++++++++++++++++++++++- Assets/Cryville/Crtr/PropSrc.cs | 17 ++++ 2 files changed, 154 insertions(+), 2 deletions(-) diff --git a/Assets/Cryville/Crtr/PdtEvaluator.cs b/Assets/Cryville/Crtr/PdtEvaluator.cs index a86ca71..faa7f6d 100644 --- a/Assets/Cryville/Crtr/PdtEvaluator.cs +++ b/Assets/Cryville/Crtr/PdtEvaluator.cs @@ -195,10 +195,19 @@ namespace Cryville.Crtr { Func cccb = k => ContextCascadeLookup(k); _ctxops.Add(IdentifierManager.Shared.Request("attack_timing"), new func_attack_timing(cccb)); - _ctxops.Add(IdentifierManager.Shared.Request("enter_timing"), new func_enter_timing(cccb)); _ctxops.Add(IdentifierManager.Shared.Request("release_timing"), new func_release_timing(cccb)); - _ctxops.Add(IdentifierManager.Shared.Request("leave_timing"), new func_leave_timing(cccb)); _ctxops.Add(IdentifierManager.Shared.Request("contact_timing"), new func_contact_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)); + + Func jacb = 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("contact_timed_area"), new func_contact_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)); } #region Operators #pragma warning disable IDE1006 @@ -637,6 +646,7 @@ namespace Cryville.Crtr { } #endregion #region Judge Functions + #region Timing static readonly int _var_fn = IdentifierManager.Shared.Request("judge_clip_from"); static readonly int _var_tn = IdentifierManager.Shared.Request("judge_clip_to"); static readonly int _var_ft = IdentifierManager.Shared.Request("input_time_from"); @@ -703,6 +713,131 @@ namespace Cryville.Crtr { } } #endregion + #region Timed Area + abstract class AreaJudgeFunction : JudgeFunction { + readonly Func _jacb; + readonly PdtEvaluator _etor; + float _area; + readonly PropOp _areaOp; + Vector4 _vec; + readonly PropSrc _vecSrc; + 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 float EvaluateArea(Vector4? vec) { + if (vec == null) { + _etor.ContextSelfValue = PropSrc.Null; + } + else { + _vec = vec.Value; + _vecSrc.Invalidate(); + _etor.ContextSelfValue = _vecSrc; + } + _etor.Evaluate(_areaOp, _jacb(GetOperand(2).AsIdentifier())); + _etor.ContextSelfValue = null; + return _area; + } + protected bool IntersectsWithAreaBounds(Vector4 fiv, Vector4 tiv, bool enter) { + for (int i = 1; i <= ChartPlayer.areaJudgePrecision; i++) { + float r = i / ChartPlayer.areaJudgePrecision; + if ((EvaluateArea((1 - r) * fiv + r * tiv) > 0) == enter) return true; + } + return false; + } + protected bool GetInternalVectors(float t0, float t1, float ft, float tt, Vector4 fv, Vector4 tv, out Vector4 fiv, out Vector4 tiv) { + var dt = tt - ft; + if (ft < t0) { + if (tt < t0) goto failed; + else if (tt < t1) tiv = tv; + else { + var tr = (t1 - ft) / dt; + tiv = (1 - tr) * fv + tr * tv; + } + var fr = (t0 - ft) / dt; + fiv = (1 - fr) * fv + fr * tv; + } + else if (ft < t1) { + fiv = fv; + if (tt < t1) tiv = tv; + else { + var tr = (t1 - ft) / dt; + tiv = (1 - tr) * fv + tr * tv; + } + } + else goto failed; + return true; + failed: + fiv = tiv = default(Vector4); + return false; + } + } + class func_attack_timed_area : AreaJudgeFunction { + public func_attack_timed_area(Func ctxcb, Func jacb, PdtEvaluator etor) : base(3, ctxcb, jacb, etor) { } + protected override float ExecuteImpl(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv) { + if (fv != null) return 0; + var t0 = GetOperand(0).AsNumber() + fn; + var t1 = GetOperand(1).AsNumber() + tn; + if (tt <= t0 || tt > t1) return 0; + return EvaluateArea(tv); + } + } + class func_release_timed_area : AreaJudgeFunction { + public func_release_timed_area(Func ctxcb, Func jacb, PdtEvaluator etor) : base(3, ctxcb, jacb, etor) { } + protected override float ExecuteImpl(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv) { + if (tv != null) return 0; + var t0 = GetOperand(0).AsNumber() + fn; + var t1 = GetOperand(1).AsNumber() + tn; + if (ft > t0 && ft <= t1) return 0; + return EvaluateArea(fv); + } + } + class func_contact_timed_area : AreaJudgeFunction { + public func_contact_timed_area(Func ctxcb, Func jacb, PdtEvaluator etor) : base(3, ctxcb, jacb, etor) { } + protected override float ExecuteImpl(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv) { + if (fv == null || tv == null) return 0; + var t0 = GetOperand(0).AsNumber() + fn; + var t1 = GetOperand(1).AsNumber() + tn; + Vector4 fiv, tiv; + if (!GetInternalVectors(t0, t1, ft, tt, fv.Value, tv.Value, out fiv, out tiv)) return 0; + if (EvaluateArea(fiv) > 0) return 1; + return IntersectsWithAreaBounds(fiv, tiv, true) ? 1 : 0; + } + } + 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 float ExecuteImpl(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv) { + if (fv == null || tv == null) return 0; + if ((EvaluateArea(fv) > 0) == _enter) return 0; + var t0 = GetOperand(0).AsNumber() + fn; + var t1 = GetOperand(1).AsNumber() + tn; + Vector4 fiv, tiv; + if (!GetInternalVectors(t0, t1, ft, tt, fv.Value, tv.Value, out fiv, out tiv)) return 0; + return IntersectsWithAreaBounds(fiv, tiv, _enter) ? 1 : 0; + } + } + 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 float ExecuteImpl(float fn, float tn, float ft, float tt, Vector4? fv, Vector4? tv) { + if (fv == null || tv == null) return 0; + var t0 = GetOperand(0).AsNumber() + fn; + var t1 = GetOperand(1).AsNumber() + tn; + if ((EvaluateArea(fv) > 0 && ft > t0 && tt < t1) == _enter) return 0; + Vector4 fiv, tiv; + if (!GetInternalVectors(t0, t1, ft, tt, fv.Value, tv.Value, out fiv, out tiv)) return 0; + return IntersectsWithAreaBounds(fiv, tiv, _enter) ? 1 : 0; + } + } + #endregion + #endregion static unsafe class oputil { public static float AsNumber(PropSrc src) { if (src == null) throw new ArgumentNullException("src"); diff --git a/Assets/Cryville/Crtr/PropSrc.cs b/Assets/Cryville/Crtr/PropSrc.cs index c20d4a3..1c3efe8 100644 --- a/Assets/Cryville/Crtr/PropSrc.cs +++ b/Assets/Cryville/Crtr/PropSrc.cs @@ -1,6 +1,7 @@ using Cryville.Common.Pdt; using System; using RBeatTime = Cryville.Crtr.BeatTime; +using RVector4 = UnityEngine.Vector4; namespace Cryville.Crtr { public abstract class PropSrc { @@ -95,5 +96,21 @@ namespace Cryville.Crtr { } } } + public class Vector4 : FixedBuffer { + readonly Func _cb; + public Vector4(Func cb) : base(PdtInternalType.Vector, 4 * sizeof(float) + sizeof(int)) { _cb = cb; } + protected unsafe override void InternalGet() { + base.InternalGet(); + var vec = _cb(); + fixed (byte* _ptr = buf) { + float* ptr = (float*)_ptr; + *ptr++ = vec.x; + *ptr++ = vec.y; + *ptr++ = vec.z; + *ptr++ = vec.w; + *(int*)ptr = PdtInternalType.Number; + } + } + } } }