732 lines
25 KiB
C#
732 lines
25 KiB
C#
using Cryville.Common.Pdt;
|
|
using Cryville.Common.Unity.Input;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Cryville.Crtr {
|
|
#region Obsolete
|
|
#if false
|
|
[Obsolete]
|
|
public class Judge {
|
|
/// <summary>
|
|
/// The computed positions of the notes.
|
|
/// </summary>
|
|
readonly Dictionary<StampedEvent.Judge, Vector2> pos
|
|
= new Dictionary<StampedEvent.Judge, Vector2>();
|
|
|
|
struct ActiveJudge : IEquatable<ActiveJudge> {
|
|
public StampedEvent.Judge Event;
|
|
public NoteHandler NoteHandler;
|
|
public bool Equals(ActiveJudge other) {
|
|
return Event == other.Event;
|
|
}
|
|
public override int GetHashCode() {
|
|
return Event.GetHashCode();
|
|
}
|
|
public ActiveJudge(StampedEvent.Judge ev, NoteHandler noteHandler) {
|
|
Event = ev; NoteHandler = noteHandler;
|
|
}
|
|
}
|
|
struct JudgeIdentifier : IEquatable<JudgeIdentifier> {
|
|
public NoteHandler NoteHandler { get; private set; }
|
|
public Chart.Note Note { get { return NoteHandler.Event; } }
|
|
public PrimaryJudge TargetJudge { get; private set; }
|
|
public int Layer { get { return TargetJudge.layer; } }
|
|
public override bool Equals(object obj) {
|
|
if (!(obj is JudgeIdentifier)) return false;
|
|
return Equals((JudgeIdentifier)obj);
|
|
}
|
|
public bool Equals(JudgeIdentifier other) {
|
|
return NoteHandler.Equals(other.NoteHandler) && Layer == other.Layer;
|
|
}
|
|
public override int GetHashCode() {
|
|
return NoteHandler.GetHashCode() ^ Layer;
|
|
}
|
|
public JudgeIdentifier(NoteHandler n, PrimaryJudge j) {
|
|
NoteHandler = n; TargetJudge = j;
|
|
}
|
|
}
|
|
struct JudgeEventIdentifier {
|
|
public JudgeIdentifier JudgeIdentifier { get; private set; }
|
|
public StampedEvent.Judge Event { get; private set; }
|
|
public JudgeEventIdentifier(JudgeIdentifier jid, StampedEvent.Judge ev) {
|
|
JudgeIdentifier = jid;
|
|
Event = ev;
|
|
}
|
|
}
|
|
|
|
readonly Dictionary<JudgeIdentifier, JudgeState> stateList = new Dictionary<JudgeIdentifier, JudgeState>();
|
|
readonly List<JudgeIdentifier> activeList = new List<JudgeIdentifier>();
|
|
readonly Dictionary<JudgeIdentifier, List<StampedEvent.Judge>> activeEvents = new Dictionary<JudgeIdentifier, List<StampedEvent.Judge>>();
|
|
readonly Dictionary<JudgeIdentifier, PrimaryJudge> immList = new Dictionary<JudgeIdentifier, PrimaryJudge>();
|
|
readonly List<StampedEvent.Judge> hitList = new List<StampedEvent.Judge>();
|
|
readonly List<JudgeEventIdentifier> missList = new List<JudgeEventIdentifier>();
|
|
readonly List<NoteHandler> deactivateList = new List<NoteHandler>();
|
|
|
|
readonly Dictionary<int, PointerInfo> ptr = new Dictionary<int, PointerInfo>();
|
|
readonly List<int> invalidatedPtr = new List<int>();
|
|
class JudgeState {
|
|
/// <summary>
|
|
/// The ID of the pointers currently holding on the note, the first of which is the main pointer.
|
|
/// </summary>
|
|
public readonly List<int> PointerIds = new List<int>();
|
|
/// <summary>
|
|
/// The ID of the pointers currently positioned on the note, but not sliding in nor holding down.
|
|
/// </summary>
|
|
public readonly List<int> NotOwnedPointerIds = new List<int>();
|
|
public Vector2 Position;
|
|
public Vector2 ImmediatePosition;
|
|
}
|
|
|
|
public readonly Dictionary<string, float> scores = new Dictionary<string, float>();
|
|
|
|
readonly CompiledRuleset ruleset;
|
|
|
|
public Judge(CompiledRuleset r) {
|
|
ruleset = r;
|
|
foreach (var s in r.scores)
|
|
scores.Add(s.Key, s.Value.init);
|
|
}
|
|
public void RecordPos(StampedEvent.Judge ev, Vector2 pt) { pos[ev] = pt; }
|
|
internal void Issue(StampedEvent.Judge ev, NoteHandler nh) {
|
|
// Logger.Log("main", 0, "Judge", "Issue: {0} {1} {2}", ev.Time, ev.Container.GetHashCode(), ruleset.primary_judges.First(i => i.Value == ev.TargetJudge).Key);
|
|
// var u = new ActiveJudge(ev, nh);
|
|
/*if (ev.TargetJudge.PassCh.ContainsKey("br"))
|
|
Logger.Log("main", 0, "Judge", "{0}", ev.StartEvent == null);*/
|
|
if (ev.StartEvent != null) {
|
|
var jid = new JudgeIdentifier(nh, ev.StartEvent.TargetJudge);
|
|
int i = activeList.IndexOf(jid);
|
|
if (i >= 0) missList.Add(new JudgeEventIdentifier(jid, ev.StartEvent));
|
|
}
|
|
else {
|
|
var jid = new JudgeIdentifier(nh, ev.TargetJudge);
|
|
if (!activeList.Contains(jid)) {
|
|
var i = activeList.FindLastIndex(j => j.TargetJudge.priority <= ev.TargetJudge.priority);
|
|
activeList.Insert(i + 1, jid);
|
|
}
|
|
if (!activeEvents.ContainsKey(jid)) activeEvents.Add(jid, new List<StampedEvent.Judge> { ev });
|
|
else activeEvents[jid].Add(ev);
|
|
if (!stateList.ContainsKey(jid)) {
|
|
stateList.Add(jid, CreateJudgeState(ev.TargetJudge, pos[ev]));
|
|
}
|
|
stateList[jid].Position = pos[ev];
|
|
}
|
|
}
|
|
internal void IssueImmediate(NoteHandler nh, string pjn, Vector2 pt) {
|
|
var key = new JudgeIdentifier(nh, ruleset.primary_judges[pjn]);
|
|
if (!immList.ContainsKey(key)) immList.Add(key, ruleset.primary_judges[pjn]);
|
|
if (!stateList.ContainsKey(key)) {
|
|
stateList.Add(key, CreateJudgeState(ruleset.primary_judges[pjn], pt));
|
|
}
|
|
stateList[key].ImmediatePosition = pt;
|
|
}
|
|
JudgeState CreateJudgeState(PrimaryJudge judge, Vector2 pt) {
|
|
var state = new JudgeState();
|
|
var etor = new Evaluator();
|
|
foreach (var p in ptr) {
|
|
var info = p.Value;
|
|
var wp = ScreenToWorldPoint(info.Position);
|
|
etor.Context = new EvaluatorContext {
|
|
Extra = new Dictionary<string, object> {
|
|
{ "npos", pt },
|
|
{ "-hitpos", wp }
|
|
}
|
|
};
|
|
if (MatchArea(judge.PassCh.Values.First().area, etor))
|
|
state.NotOwnedPointerIds.Add(info.Id);
|
|
}
|
|
return state;
|
|
}
|
|
Vector2 ScreenToWorldPoint(Vector2 pp) {
|
|
Vector3 sp = pp;
|
|
sp.y = Screen.height - sp.y;
|
|
sp.z = -Camera.main.transform.position.z;
|
|
return Camera.main.ScreenToWorldPoint(sp);
|
|
}
|
|
public void NewFrame() {
|
|
ptr.Clear();
|
|
}
|
|
public void Feed(PointerInfo info) {
|
|
var etor = new Evaluator();
|
|
var wp = ScreenToWorldPoint(info.Position);
|
|
foreach (var u in activeList) {
|
|
foreach (var j in activeEvents[u]) {
|
|
var t = j.NoteTime;
|
|
var deltat = info.Time - t;
|
|
var pj = j.TargetJudge;
|
|
etor.Context = new EvaluatorContext() {
|
|
Extra = new Dictionary<string, object>() {
|
|
{ "npos", pos[j] },
|
|
{ "-hitpos", wp }
|
|
}
|
|
};
|
|
foreach (var sj in pj.PassCh) {
|
|
var flag = false;
|
|
if (!MatchSecondaryJudge(u.NoteHandler, info.Id, stateList[u], sj.Value, j.IsEndJudge, info, deltat, wp, etor))
|
|
continue;
|
|
foreach (var tj in sj.Value.PassCh) {
|
|
if (!MatchTertiaryJudge(tj.Value, deltat, wp, etor))
|
|
continue;
|
|
// TODO Fire hit input
|
|
foreach (var s in tj.Value.scores)
|
|
UpdateScore(s.Key, s.Value);
|
|
Logger.Log("main", 0, "Judge", "Hit F: {0} {1}", sj.Key, tj.Key);
|
|
hitList.Add(j);
|
|
flag = true; break;
|
|
}
|
|
if (flag) break;
|
|
}
|
|
}
|
|
FlushHitList(u);
|
|
}
|
|
FlushDeactivateList();
|
|
foreach (var u in immList) {
|
|
var pj = u.Value;
|
|
etor.Context = new EvaluatorContext() {
|
|
Extra = new Dictionary<string, object>() {
|
|
{ "-hitpos", wp }
|
|
}
|
|
};
|
|
foreach (var sj in pj.PassCh) {
|
|
if (!MatchSecondaryJudge(u.Key.NoteHandler, info.Id, stateList[u.Key], sj.Value, false, info, 0, wp, etor, true))
|
|
continue;
|
|
if (MatchImmJudgeSubProcess(u.Key.NoteHandler, info.Id, sj.Key, sj.Value, sj.Value.type.ToString(), false, 0, wp, etor))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
public void Update(PointerInfo info) {
|
|
var etor = new Evaluator();
|
|
var wp = ScreenToWorldPoint(info.Position);
|
|
foreach (var u in activeList) {
|
|
foreach (var j in activeEvents[u]) {
|
|
var t = j.NoteTime;
|
|
var deltat = info.Time - t;
|
|
var pj = j.TargetJudge;
|
|
etor.Context = new EvaluatorContext() {
|
|
Extra = new Dictionary<string, object>() {
|
|
{ "npos", pos[j] },
|
|
{ "-hitpos", wp }
|
|
}
|
|
};
|
|
foreach (var sj in pj.PassCh) {
|
|
var flag = false;
|
|
if (!MatchSecondaryActiveJudge(u.NoteHandler, info.Id, stateList[u], sj.Value, j.IsEndJudge, info, deltat, wp, etor))
|
|
continue;
|
|
foreach (var tj in sj.Value.PassCh) {
|
|
if (!MatchTertiaryJudge(tj.Value, deltat, wp, etor))
|
|
continue;
|
|
// TODO Fire hit input
|
|
foreach (var s in tj.Value.scores)
|
|
UpdateScore(s.Key, s.Value);
|
|
Logger.Log("main", 0, "Judge", "Hit U: {0} {1}", sj.Key, tj.Key);
|
|
hitList.Add(j);
|
|
flag = true; break;
|
|
}
|
|
if (flag) break;
|
|
}
|
|
}
|
|
FlushHitList(u);
|
|
}
|
|
FlushDeactivateList();
|
|
if (!invalidatedPtr.Contains(info.Id)) ptr[info.Id] = info;
|
|
}
|
|
|
|
public void Trash(double time) {
|
|
var etor = new Evaluator();
|
|
foreach (var u in activeList) {
|
|
foreach (var j in activeEvents[u]) {
|
|
if (MatchImmJudge(u.NoteHandler, j.TargetJudge, j.IsEndJudge, time - j.NoteTime, stateList[u], etor))
|
|
hitList.Add(j);
|
|
}
|
|
// Logger.Log("main", 0, "Judge", "Active Events: {0}", activeEvents[u].Count);
|
|
FlushHitList(u);
|
|
}
|
|
FlushDeactivateList();
|
|
// Logger.Log("main", 0, "Judge", "Active States: {0}", activeList.Count);
|
|
|
|
foreach (var u in missList) {
|
|
// TODO this check shouldn't be done here, instead, invalidate/compensate those already in the active events
|
|
if (!activeEvents.ContainsKey(u.JudgeIdentifier)) continue;
|
|
var evs = activeEvents[u.JudgeIdentifier];
|
|
var ev = u.Event;
|
|
if (!evs.Contains(ev)) continue;
|
|
var pj = ev.TargetJudge;
|
|
var flag = false;
|
|
foreach (var sj in pj.PassCh) {
|
|
if (!MatchSecondaryMissJudge(u.JudgeIdentifier.NoteHandler, sj.Value, etor))
|
|
continue;
|
|
foreach (var tj in sj.Value.PassCh) {
|
|
/*if (!MatchTertiaryJudge(j, tj.Value, etor))
|
|
continue;*/
|
|
foreach (var s in tj.Value.scores)
|
|
UpdateScore(s.Key, s.Value);
|
|
Logger.Log("main", 0, "Judge", "Miss: {0}", tj.Key);
|
|
flag = true; break;
|
|
}
|
|
if (flag) break;
|
|
}
|
|
if (!flag)
|
|
Logger.Log("main", 3, "Judge", "Unjudged miss");
|
|
evs.Remove(ev);
|
|
if (u.Event.IsEndJudge) deactivateList.Add(u.JudgeIdentifier.NoteHandler);
|
|
}
|
|
FlushDeactivateList();
|
|
missList.Clear();
|
|
|
|
foreach (var j in immList) {
|
|
MatchImmJudge(j.Key.NoteHandler, j.Value, false, 0, stateList[j.Key], etor);
|
|
}
|
|
/*var rmimm = new List<JudgeIdentifier>();
|
|
foreach (var i in immList) {
|
|
if (i.Value.Active) i.Value.Active = false;
|
|
else rmimm.Add(i.Key);
|
|
}
|
|
foreach (var i in rmimm) immList.Remove(i);*/
|
|
// Logger.Log("main", 0, "Judge", "Active List: {0}", activeList.Count);
|
|
immList.Clear();
|
|
}
|
|
|
|
void FlushHitList(JudgeIdentifier jid) {
|
|
if (activeEvents.ContainsKey(jid))
|
|
foreach (var j in hitList) {
|
|
activeEvents[jid].Remove(j);
|
|
// TODO items in stateList need to be destroyed
|
|
}
|
|
hitList.Clear();
|
|
}
|
|
void FlushDeactivateList() {
|
|
for (int i = activeList.Count - 1; i >= 0; i--) {
|
|
if (deactivateList.Contains(activeList[i].NoteHandler)) {
|
|
activeEvents.Remove(activeList[i]);
|
|
activeList.RemoveAt(i);
|
|
}
|
|
}
|
|
deactivateList.Clear();
|
|
}
|
|
|
|
bool MatchImmJudge(NoteHandler nh, PrimaryJudge pj, bool endjudge, double deltat, JudgeState v, Evaluator etor) {
|
|
bool flag = false;
|
|
var ptrids = new Dictionary<int, int>();
|
|
foreach (var sj in pj.PassCh) {
|
|
var wp = ScreenToWorldPoint(v.ImmediatePosition);
|
|
ptrids.Clear();
|
|
foreach (var p in ptr) {
|
|
int id = p.Key;
|
|
Vector3 sp2 = p.Value.Position;
|
|
sp2.y = Screen.height - sp2.y;
|
|
sp2.z = -Camera.main.transform.position.z;
|
|
var wp2 = (Vector2)Camera.main.ScreenToWorldPoint(sp2);
|
|
etor.Context = new EvaluatorContext() {
|
|
Extra = new Dictionary<string, object>() {
|
|
{ "npos", v.Position },
|
|
{ "-hitpos", wp2 }
|
|
}
|
|
};
|
|
if (MatchArea(sj.Value.area, etor)) {
|
|
if (v.NotOwnedPointerIds.Contains(id)) continue;
|
|
bool flag2 = !v.PointerIds.Contains(id);
|
|
if (flag2) ptrids.Add(id, 1);
|
|
if (flag) continue;
|
|
if (sj.Value.type.HasFlag(JudgeType.enterp)) {
|
|
if (v.PointerIds.Count == 0) {
|
|
flag = MatchImmJudgeSubProcess(nh, p.Key, sj.Key, sj.Value, "enterp", endjudge, deltat, wp, etor);
|
|
}
|
|
}
|
|
else if (sj.Value.type.HasFlag(JudgeType.enters)) {
|
|
if (v.PointerIds.Count > 0) {
|
|
flag = MatchImmJudgeSubProcess(nh, p.Key, sj.Key, sj.Value, "enters", endjudge, deltat, wp, etor);
|
|
}
|
|
}
|
|
if (flag && sj.Value.@override && flag2) ptrids[id] = 2;
|
|
// else if ((int)sj.Value.type >= 4096) continue;
|
|
/*if (!v.PointerIds.Contains(id)) {
|
|
// Logger.Log("main", 0, "Judge", "push {0}", id);
|
|
if (sj.Value.@override) v.PointerIds.Insert(0, id);
|
|
else v.PointerIds.Add(id);
|
|
}*/
|
|
}
|
|
else {
|
|
if (v.PointerIds.Contains(id)) ptrids.Add(id, 0);
|
|
if (v.NotOwnedPointerIds.Contains(id)) ptrids.Add(id, -1);
|
|
if (flag) continue;
|
|
if (sj.Value.type.HasFlag(JudgeType.leaveb)) {
|
|
// Logger.Log("main", 0, "Judge", "POS {0}", v.Position);
|
|
if (v.PointerIds.Count == 1 && v.PointerIds[0] == id) {
|
|
flag = MatchImmJudgeSubProcess(nh, p.Key, sj.Key, sj.Value, "leaveb", endjudge, deltat, wp, etor);
|
|
}
|
|
}
|
|
else if (sj.Value.type.HasFlag(JudgeType.leavec)) {
|
|
if (v.PointerIds.Count > 1 && v.PointerIds[0] == id) {
|
|
flag = MatchImmJudgeSubProcess(nh, p.Key, sj.Key, sj.Value, "leavec", endjudge, deltat, wp, etor);
|
|
}
|
|
}
|
|
else if (sj.Value.type.HasFlag(JudgeType.leaves)) {
|
|
if (v.PointerIds.Count > 1 && v.PointerIds.IndexOf(id) > 0) {
|
|
flag = MatchImmJudgeSubProcess(nh, p.Key, sj.Key, sj.Value, "leaves", endjudge, deltat, wp, etor);
|
|
}
|
|
}
|
|
// else if ((int)sj.Value.type >= 4096) continue;
|
|
/*if (v.PointerIds.Contains(id)) {
|
|
// Logger.Log("main", 0, "Judge", "pop {0}", id);
|
|
v.PointerIds.Remove(id);
|
|
}*/
|
|
}
|
|
}
|
|
if (flag) {
|
|
foreach (var i in ptrids) {
|
|
switch (i.Value) {
|
|
case -1: v.NotOwnedPointerIds.Remove(i.Key); break;
|
|
case 0: v.PointerIds.Remove(i.Key); break;
|
|
case 1: v.PointerIds.Add(i.Key); break;
|
|
case 2: v.PointerIds.Insert(0, i.Key); break;
|
|
}
|
|
}
|
|
// Logger.Log("main", 0, "Judge", "{0}", v.PointerIds.Count);
|
|
return true;
|
|
}
|
|
}
|
|
foreach (var i in ptrids) {
|
|
switch (i.Value) {
|
|
case -1: v.NotOwnedPointerIds.Remove(i.Key); break;
|
|
case 0: v.PointerIds.Remove(i.Key); break;
|
|
case 1: v.PointerIds.Add(i.Key); break;
|
|
case 2: v.PointerIds.Insert(0, i.Key); break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
bool MatchImmJudgeSubProcess(NoteHandler nh, int ptrid, string sj_name, SecondaryJudge sj, string judge_type, bool endjudge, double deltat, Vector2 wp, IEvaluator etor) {
|
|
TryInvalidate(nh, sj, ptrid);
|
|
if (endjudge) Compensate(nh);
|
|
foreach (var tj in sj.PassCh) {
|
|
if (MatchTertiaryJudge(tj.Value, deltat, wp, etor)) {
|
|
foreach (var s in tj.Value.scores)
|
|
UpdateScore(s.Key, s.Value);
|
|
Logger.Log("main", 0, "Judge", "Hit I {2}: {0} {1}", sj_name, tj.Key, judge_type);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
void TryInvalidate(NoteHandler nh, SecondaryJudge sj, int ptrid) {
|
|
if (sj.invalidate.HasFlag(Invalidation.finger) && ptrid != -1) {
|
|
invalidatedPtr.Add(ptrid);
|
|
ptr.Remove(ptrid);
|
|
}
|
|
if (sj.invalidate.HasFlag(Invalidation.note)) {
|
|
List<StampedEvent.Judge> invalidatedList = nh.Invalidate();
|
|
// TODO also handle events in `activeEvents`
|
|
foreach (var j in invalidatedList) {
|
|
if (j.StartEvent != null) continue;
|
|
Invalidate(j);
|
|
}
|
|
deactivateList.Add(nh);
|
|
}
|
|
}
|
|
void Invalidate(StampedEvent.Judge j) {
|
|
bool flag = false;
|
|
foreach (var sj in j.TargetJudge.PassCh) {
|
|
if (!sj.Value.type.HasFlag(JudgeType.invalidate)) continue;
|
|
foreach (var tj in sj.Value.PassCh) {
|
|
foreach (var s in tj.Value.scores)
|
|
UpdateScore(s.Key, s.Value);
|
|
Logger.Log("main", 0, "Judge", "Invalidate: {0}", tj.Key);
|
|
flag = true; break;
|
|
}
|
|
if (flag) break;
|
|
}
|
|
if (!flag) Logger.Log("main", 3, "Judge", "Unjudged invalidation");
|
|
}
|
|
void Compensate(NoteHandler nh) {
|
|
List<StampedEvent.Judge> compensateList = nh.Invalidate();
|
|
// Logger.Log("main", 0, "Judge", "comp {0}", compensateList.Count);
|
|
// TODO also handle events in `activeEvents`
|
|
foreach (var j in compensateList) {
|
|
if (j.StartEvent != null) continue;
|
|
bool flag = false;
|
|
foreach (var sj in j.TargetJudge.PassCh) {
|
|
if (!sj.Value.type.HasFlag(JudgeType.compensate)) continue;
|
|
foreach (var tj in sj.Value.PassCh) {
|
|
foreach (var s in tj.Value.scores)
|
|
UpdateScore(s.Key, s.Value);
|
|
Logger.Log("main", 0, "Judge", "Compensate: {0}", tj.Key);
|
|
flag = true; break;
|
|
}
|
|
if (flag) break;
|
|
}
|
|
if (!flag) Logger.Log("main", 3, "Judge", "Unjudged compensation");
|
|
}
|
|
deactivateList.Add(nh);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Matches an attack or release judge.
|
|
/// </summary>
|
|
bool MatchSecondaryJudge(NoteHandler nh, int ptrid, JudgeState state, SecondaryJudge sj, bool endjudge, PointerInfo p, double deltat, Vector2 wp, IEvaluator etor, bool immsec = false) {
|
|
bool flag = false;
|
|
int pid = p.Id;
|
|
var phase = p.Phase;
|
|
if (sj.type == JudgeType.none) return false;
|
|
if (!immsec && sj.area != null) if (!MatchArea(sj.area, etor)) return false;
|
|
if (phase == PointerPhase.Begin) {
|
|
if (sj.type.HasFlag(JudgeType.attackp)) {
|
|
if (state.PointerIds.Count == 0) flag = true;
|
|
}
|
|
if (sj.type.HasFlag(JudgeType.attacks)) {
|
|
if (state.PointerIds.Count > 0) flag = true;
|
|
}
|
|
/*if (!state.PointerIds.Contains(pid)) {
|
|
if (sj.@override) ptrop = 2;
|
|
else ptrop = 1;
|
|
}*/
|
|
}
|
|
if (phase.HasFlag(PointerPhase.End)) {
|
|
if (sj.type.HasFlag(JudgeType.releaseb)) {
|
|
if (state.PointerIds.Count == 1 && state.PointerIds[0] == pid) flag = true;
|
|
}
|
|
if (sj.type.HasFlag(JudgeType.releasec)) {
|
|
if (state.PointerIds.Count > 1 && state.PointerIds[0] == pid) flag = true;
|
|
}
|
|
if (sj.type.HasFlag(JudgeType.releases)) {
|
|
if (state.PointerIds.IndexOf(pid) > 0) flag = true;
|
|
}
|
|
//if (state.PointerIds.IndexOf(pid) >= 0) ptrop = 0;
|
|
}
|
|
if (!flag) return false;
|
|
if (sj.time != null) {
|
|
var tr = sj.time.Eval<List<float>>(etor);
|
|
if (deltat < tr[0] || deltat > tr[1]) return false;
|
|
}
|
|
if (endjudge) Compensate(nh);
|
|
TryInvalidate(nh, sj, ptrid);
|
|
return true;
|
|
}
|
|
/// <summary>
|
|
/// Matches a contact judge.
|
|
/// </summary>
|
|
bool MatchSecondaryActiveJudge(NoteHandler nh, int ptrid, JudgeState state, SecondaryJudge sj, bool endjudge, PointerInfo p, double deltat, Vector2 wp, IEvaluator etor) {
|
|
bool flag = false;
|
|
int pid = p.Id;
|
|
// Logger.Log("main", 0, "Judge", "Pointers: {0}", state.PointerIds.Count);
|
|
if (sj.type == JudgeType.none) return false;
|
|
if (sj.type.HasFlag(JudgeType.contactp)) {
|
|
if (state.PointerIds.Count > 0 && pid == state.PointerIds[0]) flag = true;
|
|
}
|
|
if (sj.type.HasFlag(JudgeType.contacts)) {
|
|
if (state.PointerIds.Count > 0 && pid != state.PointerIds[0]) flag = true;
|
|
// if (sj.@override) stateList[j].PointerId = pid;
|
|
}
|
|
if (!flag) return false;
|
|
if (sj.time != null) {
|
|
var tr = sj.time.Eval<List<float>>(etor);
|
|
if (deltat < tr[0] || deltat > tr[1]) return false;
|
|
}
|
|
if (!MatchArea(sj.area, etor)) return false;
|
|
TryInvalidate(nh, sj, ptrid);
|
|
if (endjudge) Compensate(nh);
|
|
return true;
|
|
// TODO Invalidate & Call
|
|
}
|
|
bool MatchSecondaryMissJudge(NoteHandler nh, SecondaryJudge sj, Evaluator etor) {
|
|
if (sj.type.HasFlag(JudgeType.miss)) {
|
|
TryInvalidate(nh, sj, -1);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
bool MatchTertiaryJudge(TertiaryJudge tj, double deltat, Vector3 wp, IEvaluator etor) {
|
|
if (tj.time != null) {
|
|
var tr = tj.time.Eval<List<float>>(etor);
|
|
if (deltat < tr[0] || deltat > tr[1]) return false;
|
|
}
|
|
if (!MatchArea(tj.area, etor)) return false;
|
|
return true;
|
|
}
|
|
bool MatchArea(Expression exp, IEvaluator etor) {
|
|
if (exp != null) {
|
|
var a = exp.Eval<float>(etor);
|
|
if (a > 0) return false;
|
|
}
|
|
return true;
|
|
}
|
|
void UpdateScore(string key, Expression value) {
|
|
var etor = new Evaluator();
|
|
var extra = new Dictionary<string, object>(scores.Count);
|
|
foreach (var s in scores) extra.Add(s.Key, s.Value);
|
|
etor.Context = new EvaluatorContext() { Extra = extra };
|
|
var ak = key.Split(' ');
|
|
string name = ak[ak.Length - 1];
|
|
bool setMode = false;
|
|
float result = scores[name];
|
|
var prop = ruleset.scores[name];
|
|
if (ak.Length > 1)
|
|
for (int i = 0; i < ak.Length - 1; i++)
|
|
if (ak[i] == "@set") setMode = true;
|
|
if (setMode)
|
|
result = value.Eval<float>(etor);
|
|
else
|
|
result += value.Eval<float>(etor);
|
|
if (prop.range != null) {
|
|
var range = prop.range.Eval<List<float>>(etor);
|
|
if (result < range[0]) result = range[0];
|
|
else if (result > range[1]) result = range[1];
|
|
}
|
|
if (prop.min != null && result < scores[prop.min])
|
|
scores[prop.min] = result;
|
|
if (prop.max != null && result > scores[prop.max])
|
|
scores[prop.max] = result;
|
|
scores[name] = result;
|
|
|
|
foreach (var s in scores) extra[s.Key] = s.Value;
|
|
etor.Context = new EvaluatorContext() { Extra = extra };
|
|
foreach (var s in ruleset.scores.Keys) {
|
|
if (ruleset.scores[s].value != null) {
|
|
scores[s] = ruleset.scores[s].value.Eval<float>(etor);
|
|
}
|
|
}
|
|
ScoreCache.Clear();
|
|
}
|
|
|
|
readonly Dictionary<string, string> ScoreCache = new Dictionary<string, string>();
|
|
public Dictionary<string, string> GetScoreStrings() {
|
|
if (ScoreCache.Count == 0) {
|
|
foreach (var s in scores) {
|
|
ScoreCache.Add(s.Key, s.Value.ToString(ruleset.scores[s.Key].format));
|
|
}
|
|
}
|
|
return ScoreCache;
|
|
}
|
|
public string FormatScores() {
|
|
using (gstring.Block()) {
|
|
bool flag = false;
|
|
gstring result = new gstring(0);
|
|
foreach (var s in GetScoreStrings()) {
|
|
result += gstring.Format(flag ? "\n{0}: {1}" : "{0}: {1}", s.Key, (string)s.Value);
|
|
flag = true;
|
|
}
|
|
return result.Intern();
|
|
}
|
|
}
|
|
}
|
|
[Obsolete]
|
|
public class PrimaryJudge {
|
|
public List<string> pass;
|
|
public Dictionary<string, SecondaryJudge> PassCh
|
|
= new Dictionary<string, SecondaryJudge>();
|
|
public Expression time;
|
|
/// <summary>
|
|
/// The layer of the primary judge. Judges on different layers do not interfere with each other.
|
|
/// </summary>
|
|
public int layer = 0;
|
|
/// <summary>
|
|
/// The priority of the primary judge. Judges with higher priority are handled first on the same layer.
|
|
/// </summary>
|
|
public int priority = 0;
|
|
}
|
|
[Obsolete]
|
|
public class SecondaryJudge {
|
|
public JudgeType type;
|
|
public List<string> call;
|
|
public Dictionary<string, SecondaryJudge> CallCh
|
|
= new Dictionary<string, SecondaryJudge>();
|
|
public bool @return;
|
|
public List<string> pass;
|
|
public Dictionary<string, TertiaryJudge> PassCh
|
|
= new Dictionary<string, TertiaryJudge>();
|
|
public Expression time;
|
|
public Expression area;
|
|
public Invalidation invalidate;
|
|
public bool @override;
|
|
}
|
|
[Flags][Obsolete]
|
|
public enum JudgeType {
|
|
none = 0,
|
|
attackp = 1, attacks = 2, attack = 3,
|
|
releaseb = 4, releasec = 8, releasep = 12, releases = 16, release = 28,
|
|
enterp = 32, enters = 64, enter = 96,
|
|
leaveb = 128, leavec = 256, leavep = 384, leaves = 512, leave = 896,
|
|
setp = 33, sets = 66, set = 99,
|
|
unsetb = 132, unsetc = 264, unsetp = 396, unsets = 528, unset = 924,
|
|
contactp = 1024, contacts = 2048, contact = 3072,
|
|
miss = 4096, compensate = 8192, invalidate = 16384
|
|
}
|
|
[Flags][Obsolete]
|
|
public enum Invalidation {
|
|
none = 0, finger = 1, note = 2
|
|
}
|
|
[Obsolete]
|
|
public class TertiaryJudge {
|
|
public Expression time;
|
|
public Expression area;
|
|
public Dictionary<string, Expression> scores
|
|
= new Dictionary<string, Expression>();
|
|
}
|
|
[Obsolete]
|
|
public class Score {
|
|
public Expression range;
|
|
public Expression value;
|
|
public float init = 0;
|
|
public string min;
|
|
public string max;
|
|
public string format = "";
|
|
}
|
|
#endif
|
|
#endregion
|
|
public class Judge {
|
|
readonly PdtRuleset rs;
|
|
public Judge() {
|
|
rs = ChartPlayer.pruleset;
|
|
foreach (var s in rs.scores)
|
|
scores.Add(s.Key, s.Value.init);
|
|
}
|
|
public void StartFrame() {
|
|
|
|
}
|
|
public void Feed(InputEvent ev) {
|
|
|
|
}
|
|
public void EndFrame() {
|
|
|
|
}
|
|
public readonly Dictionary<string, float> scores = new Dictionary<string, float>();
|
|
readonly Dictionary<string, string> ScoreCache = new Dictionary<string, string>();
|
|
public Dictionary<string, string> GetFormattedScoreStrings() {
|
|
if (ScoreCache.Count == 0) {
|
|
foreach (var s in scores)
|
|
ScoreCache.Add(s.Key, s.Value.ToString(rs.scores[s.Key].format));
|
|
}
|
|
return ScoreCache;
|
|
}
|
|
public string GetFullFormattedScoreString() {
|
|
bool flag = false;
|
|
string result = "";
|
|
foreach (var s in GetFormattedScoreStrings()) {
|
|
result += string.Format(flag ? "\n{0}: {1}" : "{0}: {1}", s.Key, s.Value);
|
|
flag = true;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
public class InputDefinition {
|
|
public int dim;
|
|
public bool notnull;
|
|
public Dictionary<string, PdtExpression> pass;
|
|
}
|
|
public class JudgeDefinition {
|
|
public PdtExpression clip;
|
|
public PdtExpression hit;
|
|
public string[] pass;
|
|
public string miss;
|
|
public Dictionary<string, PdtExpression> scores;
|
|
}
|
|
public class ScoreOperation {
|
|
public string name;
|
|
public PdtOperator op;
|
|
}
|
|
public class ScoreDefinition {
|
|
public PdtExpression value;
|
|
public float init = 0;
|
|
public string format = "";
|
|
}
|
|
}
|