using Cryville.Common.Pdt; using Cryville.Common.Unity.Input; using System.Collections.Generic; namespace Cryville.Crtr { #region Obsolete #if false [Obsolete] public class Judge { /// /// The computed positions of the notes. /// readonly Dictionary pos = new Dictionary(); struct ActiveJudge : IEquatable { 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 { 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 stateList = new Dictionary(); readonly List activeList = new List(); readonly Dictionary> activeEvents = new Dictionary>(); readonly Dictionary immList = new Dictionary(); readonly List hitList = new List(); readonly List missList = new List(); readonly List deactivateList = new List(); readonly Dictionary ptr = new Dictionary(); readonly List invalidatedPtr = new List(); class JudgeState { /// /// The ID of the pointers currently holding on the note, the first of which is the main pointer. /// public readonly List PointerIds = new List(); /// /// The ID of the pointers currently positioned on the note, but not sliding in nor holding down. /// public readonly List NotOwnedPointerIds = new List(); public Vector2 Position; public Vector2 ImmediatePosition; } public readonly Dictionary scores = new Dictionary(); 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 { 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 { { "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() { { "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() { { "-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() { { "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(); 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(); 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() { { "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 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 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); } /// /// Matches an attack or release judge. /// 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>(etor); if (deltat < tr[0] || deltat > tr[1]) return false; } if (endjudge) Compensate(nh); TryInvalidate(nh, sj, ptrid); return true; } /// /// Matches a contact judge. /// 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>(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>(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(etor); if (a > 0) return false; } return true; } void UpdateScore(string key, Expression value) { var etor = new Evaluator(); var extra = new Dictionary(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(etor); else result += value.Eval(etor); if (prop.range != null) { var range = prop.range.Eval>(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(etor); } } ScoreCache.Clear(); } readonly Dictionary ScoreCache = new Dictionary(); public Dictionary 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 pass; public Dictionary PassCh = new Dictionary(); public Expression time; /// /// The layer of the primary judge. Judges on different layers do not interfere with each other. /// public int layer = 0; /// /// The priority of the primary judge. Judges with higher priority are handled first on the same layer. /// public int priority = 0; } [Obsolete] public class SecondaryJudge { public JudgeType type; public List call; public Dictionary CallCh = new Dictionary(); public bool @return; public List pass; public Dictionary PassCh = new Dictionary(); 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 scores = new Dictionary(); } [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 scores = new Dictionary(); readonly Dictionary ScoreCache = new Dictionary(); public Dictionary 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 pass; } public class JudgeDefinition { public PdtExpression clip; public PdtExpression hit; public string[] pass; public string miss; public Dictionary scores; } public class ScoreOperation { public string name; public PdtOperator op; } public class ScoreDefinition { public PdtExpression value; public float init = 0; public string format = ""; } }