public JudgeResult?UserPressed(time_t timeStamp) { if (AutoPlay) { return(null); } m_userHeld = true; m_userWhen = timeStamp; if (m_ticks.Count == 0) { return(null); } var tick = m_ticks[0]; // Don't ACTUALLY handle holds handled in here if (tick.IsHold) { OnHoldPressed?.Invoke(timeStamp, tick.AssociatedObject); m_lastPressedObject = tick.AssociatedObject; return(null); } else { OnChipPressed?.Invoke(timeStamp, tick.AssociatedObject); } m_ticks.RemoveAt(0); time_t diff = tick.Position + JudgementOffset - timeStamp; time_t absDiff = MathL.Abs(diff.Seconds); time_t offsetTime = timeStamp - JudgementOffset; JudgeResult result; if (absDiff <= PERF_RADIUS) { result = new JudgeResult(diff, JudgeKind.Perfect); } else if (absDiff <= CRIT_RADIUS) { result = new JudgeResult(diff, JudgeKind.Critical); } else if (absDiff <= NEAR_RADIUS) { result = new JudgeResult(diff, JudgeKind.Near); } // TODO(local): Is this how we want to handle misses? else { result = new JudgeResult(diff, JudgeKind.Bad); } OnTickProcessed?.Invoke(tick.AssociatedObject, offsetTime, result); return(result); }
public JudgeResult?UserPressed(time_t position) { // This check just makes sure that we can process ticks. // If there are no state ticks, there should never be score ticks left. if (HasStateTicks) { Debug.Assert(HasScoreTicks); } else { SpawnKeyBeam?.Invoke(Label, JudgeKind.Passive, false); return(null); } switch (m_state) { case JudgeState.Idle: { var nextStateTick = NextStateTick; time_t difference = position - (nextStateTick.Position + JudgementOffset); time_t absDifference = Math.Abs((double)difference); if (nextStateTick.State == JudgeState.ChipAwaitPress && absDifference <= m_chipMissRadius) { var scoreTick = NextScoreTick; Debug.Assert(scoreTick.Kind == TickKind.Chip); Debug.Assert(scoreTick.Position == nextStateTick.Position); // `difference` applies to both ticks, don't recalculate JudgeResult result; if (absDifference <= m_chipPerfectRadius) { result = new JudgeResult(difference, JudgeKind.Perfect); } else if (absDifference <= m_chipCriticalRadius) { result = new JudgeResult(difference, JudgeKind.Critical); } else if (absDifference <= m_chipNearRadius) { result = new JudgeResult(difference, JudgeKind.Near); } // TODO(local): Is this how we want to handle misses? else { result = new JudgeResult(difference, JudgeKind.Bad); } OnTickProcessed?.Invoke(scoreTick.Entity, position, result, difference < 0); OnChipPressed?.Invoke(position, scoreTick.Entity); SpawnKeyBeam?.Invoke(scoreTick.Entity.Lane, result.Kind, difference < 0); AdvanceStateTick(); AdvanceScoreTick(); // state stays idle after a chip press, chips are instantaneous IsBeingPlayed = true; return(result); } else if (nextStateTick.State == JudgeState.HoldAwaitPress && absDifference <= m_holdActivateRadius) { OnHoldPressed?.Invoke(position, nextStateTick.Entity); AdvanceStateTick(); // No need to advance a score tick, we haven't judged anything // state is `hold on` because ofc we pressed the hold! m_state = JudgeState.HoldOn; m_currentStateTick = nextStateTick; IsBeingPlayed = true; } // do nothing when pressed otherwise else { SpawnKeyBeam?.Invoke(Label, JudgeKind.Passive, false); } } break; case JudgeState.HoldOff: { OnHoldPressed?.Invoke(position, m_currentStateTick.Entity); m_state = JudgeState.HoldOn; IsBeingPlayed = true; } break; case JudgeState.HoldOn: throw new InvalidOperationException(); } return(null); }
protected override void AdvancePosition(time_t position) { // remove old ticks first while (!AutoPlay && m_ticks.Count > 0) { var tick = m_ticks[0]; time_t radius = tick.IsHold ? HOLD_RADIUS : MAX_RADIUS; if (tick.Position + JudgementOffset < position - radius) { m_ticks.RemoveAt(0); OnTickProcessed?.Invoke(tick.AssociatedObject, position, new JudgeResult(tick.Position + JudgementOffset - position, JudgeKind.Miss)); } else { break; } } while (m_ticks.Count > 0) { var tick = m_ticks[0]; if (AutoPlay && position >= tick.Position) { m_ticks.RemoveAt(0); if (tick.IsAutoTick) { if (tick.Position == tick.AssociatedObject.AbsolutePosition) { OnHoldPressed?.Invoke(position, tick.AssociatedObject); } //else OnHoldReleased?.Invoke(position, tick.AssociatedObject); } else if (tick.IsHold) { OnTickProcessed?.Invoke(tick.AssociatedObject, position - JudgementOffset, new JudgeResult(0, JudgeKind.Passive)); } else { OnChipPressed?.Invoke(position, tick.AssociatedObject); OnTickProcessed?.Invoke(tick.AssociatedObject, tick.Position, new JudgeResult(0, JudgeKind.Perfect)); } } else // ===== NO AUTO PLAY ===== { if (!tick.IsHold) { break; } time_t check = tick.AssociatedObject.AbsolutePosition + JudgementOffset - m_userWhen; if (check > MISS_RADIUS) { break; } time_t diff = tick.Position + JudgementOffset - position; time_t absDiff = MathL.Abs(diff.Seconds); if (m_userHeld && diff > 0 && absDiff <= HOLD_RADIUS) { m_ticks.RemoveAt(0); OnTickProcessed?.Invoke(tick.AssociatedObject, position - JudgementOffset, new JudgeResult(diff, JudgeKind.Passive)); } else { break; } } } }