Add area judge functions.

This commit is contained in:
2023-05-14 12:06:32 +08:00
parent fb31e833cb
commit 5bab0ca648
2 changed files with 154 additions and 2 deletions

View File

@@ -195,10 +195,19 @@ namespace Cryville.Crtr {
Func<int, PropSrc> 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<int, PdtExpression> 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<int, PdtExpression> _jacb;
readonly PdtEvaluator _etor;
float _area;
readonly PropOp _areaOp;
Vector4 _vec;
readonly PropSrc _vecSrc;
public AreaJudgeFunction(int pc, Func<int, PropSrc> ctxcb, Func<int, PdtExpression> 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<int, PropSrc> ctxcb, Func<int, PdtExpression> 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<int, PropSrc> ctxcb, Func<int, PdtExpression> 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<int, PropSrc> ctxcb, Func<int, PdtExpression> 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<int, PropSrc> ctxcb, Func<int, PdtExpression> 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<int, PropSrc> ctxcb, Func<int, PdtExpression> 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");

View File

@@ -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<RVector4> _cb;
public Vector4(Func<RVector4> 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;
}
}
}
}
}