Beispiel #1
0
        /// <summary> Returns the custom index for the object, if any, otherwise for the line, if any, otherwise 1. </summary>
        public int GetCustomIndex(TimingLine line = null)
        {
            if (line == null)
            {
                line = beatmap.GetTimingLine(time);
            }

            return(customIndex ?? line?.customIndex ?? 1);
        }
Beispiel #2
0
 private List <TimingLine> GetTimingLines(string[] aLines)
 {
     // find the [TimingPoints] section and parse each timing line
     return(ParserStatic.ParseSection(aLines, "TimingPoints", aLine =>
     {
         string[] args = aLine.Split(',');
         return TimingLine.IsUninherited(args) ? new UninheritedLine(args) : (TimingLine) new InheritedLine(args);
     }).ToList());
 }
Beispiel #3
0
        private HitSample GetEdgeSample(double time, Beatmap.Sampleset?sampleset, HitSound?hitSound)
        {
            TimingLine line = beatmap.GetTimingLine(time, hitSoundLeniency: true);

            return
                (new HitSample(
                     line.customIndex,
                     sampleset ?? line.sampleset,
                     hitSound,
                     HitSample.HitSource.Edge,
                     time));
        }
Beispiel #4
0
        /// <summary> Returns the previous timing line in the timing line list, if any,
        /// otherwise null, O(1). Optionally skips concurrent objects. </summary>
        public TimingLine Prev(bool skipConcurrent = false)
        {
            TimingLine prev = null;

            for (int i = timingLineIndex; i >= 0; --i)
            {
                prev = beatmap.timingLines[i];
                if (!skipConcurrent || prev.offset != offset)
                {
                    break;
                }
            }

            return(prev);
        }
Beispiel #5
0
        /// <summary> Returns the next timing line in the timing line list, if any,
        /// otherwise null, O(1). Optionally skips concurrent lines. </summary>
        public TimingLine Next(bool skipConcurrent = false)
        {
            TimingLine next = null;

            for (int i = timingLineIndex; i < beatmap.timingLines.Count; ++i)
            {
                next = beatmap.timingLines[i];
                if (!skipConcurrent || next.offset != offset)
                {
                    break;
                }
            }

            return(next);
        }
Beispiel #6
0
        /// <summary> Returns all used combinations of customs, samplesets and hit sounds for this object.
        /// Assumes the game mode is taiko (special rules apply).
        /// <br></br><br></br>
        /// Special Rules:<br></br>
        /// - taiko-hitwhistle plays on big kat <br></br>
        /// - taiko-hitfinish plays on big don <br></br>
        /// - taiko-hitclap and taiko-hitnormal are always used as they play whenever the user presses keys
        /// </summary>
        public IEnumerable <HitSample> GetUsedHitSamplesTaiko()
        {
            TimingLine line = beatmap.GetTimingLine(time, hitSoundLeniency: true);

            yield return(new HitSample(line?.customIndex ?? 1, line.sampleset, HitSound.Clap, HitSample.HitSource.Edge, line.offset, true));

            yield return(new HitSample(line?.customIndex ?? 1, line.sampleset, HitSound.Normal, HitSample.HitSource.Edge, line.offset, true));

            bool isKat = HasHitSound(HitSound.Clap) || HasHitSound(HitSound.Whistle);
            bool isBig = HasHitSound(HitSound.Finish);

            HitSound hitSound;

            if (isBig)
            {
                if (isKat)
                {
                    hitSound = HitSound.Whistle;
                }
                else
                {
                    hitSound = HitSound.Finish;
                }
            }
            else
            if (isKat)
            {
                hitSound = HitSound.Clap;
            }
            else
            {
                hitSound = HitSound.Normal;
            }

            // In case the hit object's custom index/sampleset/additions are different from the timing line's.
            yield return(new HitSample(GetCustomIndex(line), GetSampleset(true), hitSound, HitSample.HitSource.Edge, time, true));
        }
Beispiel #7
0
        /// <summary> Returns all used combinations of customs, samplesets and hit sounds for this object.
        /// This assumes the game mode is not taiko (special rules apply to taiko only). </summary>
        private IEnumerable <HitSample> GetUsedHitSamplesNonTaiko()
        {
            // Spinners have no impact sound.
            if (!(this is Spinner))
            {
                // Head
                foreach (HitSound splitStartHitSound in SplitHitSound(GetStartHitSound().GetValueOrDefault()))
                {
                    yield return(GetEdgeSample(time, GetStartSampleset(true), splitStartHitSound));
                }
                yield return(GetEdgeSample(time, GetStartSampleset(false), HitSound.Normal));
            }

            // Hold notes can not have a hit sounds on their tails.
            if (!(this is HoldNote))
            {
                // Tail
                foreach (HitSound splitEndHitSound in SplitHitSound(GetEndHitSound().GetValueOrDefault()))
                {
                    yield return(GetEdgeSample(GetEndTime(), GetEndSampleset(true), splitEndHitSound));
                }
                yield return(GetEdgeSample(GetEndTime(), GetEndSampleset(false), HitSound.Normal));
            }

            if (this is Slider slider)
            {
                // Reverse
                for (int i = 0; i < slider.reverseHitSounds.Count; ++i)
                {
                    HitSound?reverseHitSound = slider.reverseHitSounds.ElementAt(i);

                    double theoreticalStart = time - beatmap.GetTheoreticalUnsnap(time);
                    double reverseTime      = Timestamp.Round(theoreticalStart + slider.GetCurveDuration() * (i + 1));

                    foreach (HitSound splitReverseHitSound in SplitHitSound(reverseHitSound.GetValueOrDefault()))
                    {
                        yield return(GetEdgeSample(reverseTime, slider.GetReverseSampleset(i, true), splitReverseHitSound));
                    }
                    yield return(GetEdgeSample(reverseTime, slider.GetReverseSampleset(i), HitSound.Normal));
                }

                List <TimingLine> lines =
                    beatmap.timingLines.Where(line =>
                                              line.offset > slider.time &&
                                              line.offset <= slider.endTime).ToList();
                lines.Add(beatmap.GetTimingLine(slider.time, hitSoundLeniency: true));

                // Body, only applies to standard. Catch has droplets instead of body. Taiko and mania have a body but play no background sound.
                if (beatmap.generalSettings.mode == Beatmap.Mode.Standard)
                {
                    foreach (TimingLine line in lines)
                    {
                        // Priority: object sampleset > line sampleset
                        // The addition is ignored for sliderslides, it seems.
                        Beatmap.Sampleset effectiveSampleset =
                            sampleset != Beatmap.Sampleset.Auto ?
                            sampleset :
                            line.sampleset;

                        // Additions are not ignored for sliderwhistles, however.
                        if (slider.hitSound == HitSound.Whistle)
                        {
                            effectiveSampleset = addition != Beatmap.Sampleset.Auto ? addition : effectiveSampleset;
                        }

                        // The regular sliderslide will always play regardless of using sliderwhistle.
                        yield return(new HitSample(
                                         line.customIndex,
                                         effectiveSampleset,
                                         HitSound.None,
                                         HitSample.HitSource.Body,
                                         line.offset));

                        if (hitSound != HitSound.None)
                        {
                            yield return(new HitSample(
                                             line.customIndex,
                                             effectiveSampleset,
                                             hitSound,
                                             HitSample.HitSource.Body,
                                             line.offset));
                        }
                    }
                }

                // Tick, only applies to standard and catch. Mania has no ticks, taiko sliders play regular impacts.
                if (beatmap.generalSettings.mode == Beatmap.Mode.Standard ||
                    beatmap.generalSettings.mode == Beatmap.Mode.Catch)
                {
                    foreach (double tickTime in slider.sliderTickTimes)
                    {
                        TimingLine line = beatmap.GetTimingLine(tickTime);

                        // If no line exists, we use the default settings.
                        int customIndex = line?.customIndex ?? 1;

                        // Unlike the slider body (for sliderwhistles) and edges, slider ticks are unaffected by additions.
                        Beatmap.Sampleset sampleset = GetSampleset(false, tickTime);

                        // Defaults to normal if none is set (before any timing line).
                        if (sampleset == Beatmap.Sampleset.Auto)
                        {
                            sampleset = Beatmap.Sampleset.Normal;
                        }

                        yield return(new HitSample(customIndex, sampleset, null, HitSample.HitSource.Tick, tickTime));
                    }
                }
            }
        }
Beispiel #8
0
        /// <summary> Returns all used combinations of customs, samplesets and hit sounds for this object. </summary>
        public IEnumerable <HitSample> GetUsedHitSamples()
        {
            // Head
            foreach (HitSound splitStartHitSound in SplitHitSound(GetStartHitSound().GetValueOrDefault()))
            {
                yield return(GetEdgeSample(time, GetStartSampleset(true), splitStartHitSound));
            }
            yield return(GetEdgeSample(time, GetStartSampleset(false), HitSound.Normal));

            // Hold notes can not have a hit sounds on their tails.
            if (!(this is HoldNote))
            {
                // Tail
                foreach (HitSound splitEndHitSound in SplitHitSound(GetEndHitSound().GetValueOrDefault()))
                {
                    yield return(GetEdgeSample(GetEndTime(), GetEndSampleset(true), splitEndHitSound));
                }
                yield return(GetEdgeSample(GetEndTime(), GetEndSampleset(false), HitSound.Normal));
            }

            if (this is Slider slider)
            {
                // Reverse
                for (int i = 0; i < slider.reverseHitSounds.Count; ++i)
                {
                    HitSound?         reverseHitSound  = slider.reverseHitSounds.ElementAt(i);
                    Beatmap.Sampleset?reverseSampleset = slider.GetReverseSampleset(i);
                    Beatmap.Sampleset?reverseAddition  =
                        slider.reverseAdditions.Any() ?   // not a thing in file version 9
                        slider.reverseAdditions.ElementAt(i) :
                        (Beatmap.Sampleset?)null;

                    double reverseTime = slider.GetCurveDuration() * (i + 1);

                    foreach (HitSound splitReverseHitSound in SplitHitSound(reverseHitSound.GetValueOrDefault()))
                    {
                        yield return(GetEdgeSample(reverseTime, reverseAddition ?? reverseSampleset, splitReverseHitSound));
                    }
                    yield return(GetEdgeSample(reverseTime, reverseSampleset, HitSound.Normal));
                }

                List <TimingLine> lines =
                    beatmap.timingLines.Where(aLine =>
                                              aLine.offset > slider.time &&
                                              aLine.offset <= slider.endTime).ToList();
                lines.Add(beatmap.GetTimingLine(slider.time, true));

                // Body
                foreach (TimingLine line in lines)
                {
                    yield return(new HitSample(line.customIndex, line.sampleset, hitSound, HitSample.HitSource.Body, line.offset));
                }

                // Tick
                foreach (double tickTime in slider.sliderTickTimes)
                {
                    TimingLine line = beatmap.GetTimingLine(tickTime);

                    // If no line exists, we use the default settings.
                    int customIndex             = line?.customIndex ?? 1;
                    Beatmap.Sampleset sampleset = GetSampleset(true, tickTime);

                    // Defaults to normal if none is set (before any timing line).
                    if (sampleset == Beatmap.Sampleset.Auto)
                    {
                        sampleset = Beatmap.Sampleset.Normal;
                    }

                    yield return(new HitSample(customIndex, sampleset, null, HitSample.HitSource.Tick, tickTime));
                }
            }
        }