예제 #1
0
        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);
        }