Merge branch 'master' into breadcrumbs

This commit is contained in:
Dean Herbert
2017-05-29 15:59:37 +09:00
40 changed files with 745 additions and 165 deletions

View File

@ -428,6 +428,9 @@ namespace osu.Game.Beatmaps.Formats
break;
}
}
foreach (var hitObject in beatmap.HitObjects)
hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty);
}
internal enum LegacySampleBank

View File

@ -3,6 +3,7 @@
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Input;
using System;
using System.Linq;
@ -23,11 +24,19 @@ namespace osu.Game.Graphics.UserInterface
set
{
focus = value;
if (!focus)
TriggerFocusLost();
if (!focus && HasFocus)
inputManager.ChangeFocus(null);
}
}
private InputManager inputManager;
[BackgroundDependencyLoader]
private void load(UserInputManager inputManager)
{
this.inputManager = inputManager;
}
protected override bool OnFocus(InputState state)
{
var result = base.OnFocus(state);

View File

@ -5,18 +5,21 @@ using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Graphics.Containers;
using osu.Game.Beatmaps.ControlPoints;
using osu.Framework.Audio.Track;
using System;
namespace osu.Game.Graphics.UserInterface
{
public class TwoLayerButton : ClickableContainer
{
private readonly TextAwesome icon;
private readonly BouncingIcon bouncingIcon;
public Box IconLayer;
public Box TextLayer;
@ -95,11 +98,10 @@ namespace osu.Game.Graphics.UserInterface
},
}
},
icon = new TextAwesome
bouncingIcon = new BouncingIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 25,
},
}
},
@ -146,7 +148,7 @@ namespace osu.Game.Graphics.UserInterface
{
set
{
icon.Icon = value;
bouncingIcon.Icon = value;
}
}
@ -162,58 +164,20 @@ namespace osu.Game.Graphics.UserInterface
protected override bool OnHover(InputState state)
{
icon.ClearTransforms();
ResizeTo(SIZE_EXTENDED, transform_time, EasingTypes.OutElastic);
int duration = 0; //(int)(Game.Audio.BeatLength / 2);
if (duration == 0) duration = pulse_length;
IconLayer.FadeColour(HoverColour, transform_time, EasingTypes.OutElastic);
const double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration;
double startTime = Time.Current + offset;
// basic pulse
icon.Transforms.Add(new TransformScale
{
StartValue = new Vector2(1.1f),
EndValue = Vector2.One,
StartTime = startTime,
EndTime = startTime + duration,
Easing = EasingTypes.Out,
LoopCount = -1,
LoopDelay = duration
});
bouncingIcon.ScaleTo(1.1f, transform_time, EasingTypes.OutElastic);
return true;
}
protected override void OnHoverLost(InputState state)
{
icon.ClearTransforms();
ResizeTo(SIZE_RETRACTED, transform_time, EasingTypes.OutElastic);
IconLayer.FadeColour(TextLayer.Colour, transform_time, EasingTypes.OutElastic);
int duration = 0; //(int)(Game.Audio.BeatLength);
if (duration == 0) duration = pulse_length * 2;
const double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration;
double startTime = Time.Current + offset;
// slow pulse
icon.Transforms.Add(new TransformScale
{
StartValue = new Vector2(1.1f),
EndValue = Vector2.One,
StartTime = startTime,
EndTime = startTime + duration,
Easing = EasingTypes.Out,
LoopCount = -1,
LoopDelay = duration
});
bouncingIcon.ScaleTo(1, transform_time, EasingTypes.OutElastic);
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
@ -239,5 +203,45 @@ namespace osu.Game.Graphics.UserInterface
return base.OnClick(state);
}
private class BouncingIcon : BeatSyncedContainer
{
private const double beat_in_time = 60;
private readonly TextAwesome icon;
public FontAwesome Icon { set { icon.Icon = value; } }
public BouncingIcon()
{
EarlyActivationMilliseconds = beat_in_time;
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
icon = new TextAwesome
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 25
}
};
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
var beatLength = timingPoint.BeatLength;
float amplitudeAdjust = Math.Min(1, 0.4f + amplitudes.Maximum);
if (beatIndex < 0) return;
icon.ScaleTo(1 - 0.1f * amplitudeAdjust, beat_in_time, EasingTypes.Out);
using (icon.BeginDelayedSequence(beat_in_time))
icon.ScaleTo(1, beatLength * 2, EasingTypes.OutQuint);
}
}
}
}

View File

@ -74,7 +74,7 @@ namespace osu.Game.Graphics.UserInterface.Volume
return;
}
volumeMeterMaster.TriggerWheel(state);
volumeMeterMaster.TriggerOnWheel(state);
}
[BackgroundDependencyLoader]

View File

@ -255,6 +255,9 @@ namespace osu.Game
settings.ToggleVisibility();
return true;
case Key.D:
if (state.Keyboard.ShiftPressed || state.Keyboard.AltPressed)
return false;
direct.ToggleVisibility();
return true;
}

View File

@ -135,17 +135,22 @@ namespace osu.Game.Overlays
channelTabs.Current.ValueChanged += newChannel => CurrentChannel = newChannel;
}
private double startDragChatHeight;
protected override bool OnDragStart(InputState state)
{
if (channelTabs.Hovering)
return true;
if (!channelTabs.Hovering)
return base.OnDragStart(state);
return base.OnDragStart(state);
startDragChatHeight = chatHeight.Value;
return true;
}
protected override bool OnDrag(InputState state)
{
chatHeight.Value = Height - state.Mouse.Delta.Y / Parent.DrawSize.Y;
Trace.Assert(state.Mouse.PositionMouseDown != null);
chatHeight.Value = startDragChatHeight - (state.Mouse.Position.Y - state.Mouse.PositionMouseDown.Value.Y) / Parent.DrawSize.Y;
return base.OnDrag(state);
}
@ -165,7 +170,7 @@ namespace osu.Game.Overlays
protected override bool OnFocus(InputState state)
{
//this is necessary as inputTextBox is masked away and therefore can't get focus :(
inputTextBox.TriggerFocus();
InputManager.ChangeFocus(inputTextBox);
return false;
}

View File

@ -71,7 +71,7 @@ namespace osu.Game.Overlays.Dialog
private void pressButtonAtIndex(int index)
{
if (index < Buttons.Count())
Buttons.Skip(index).First().TriggerClick();
Buttons.Skip(index).First().TriggerOnClick();
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Dialog
if (args.Key == Key.Enter)
{
Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.TriggerClick();
Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.TriggerOnClick();
return true;
}

View File

@ -189,7 +189,7 @@ namespace osu.Game.Overlays
protected override bool OnFocus(InputState state)
{
filter.Search.TriggerFocus();
InputManager.ChangeFocus(filter.Search);
return false;
}

View File

@ -66,7 +66,7 @@ namespace osu.Game.Overlays
settingsSection.Bounding = true;
FadeIn(transition_time, EasingTypes.OutQuint);
settingsSection.TriggerFocus();
InputManager.ChangeFocus(settingsSection);
}
protected override void PopOut()

View File

@ -17,6 +17,7 @@ using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions;
using osu.Framework.Input;
namespace osu.Game.Overlays.Music
{
@ -35,10 +36,12 @@ namespace osu.Game.Overlays.Music
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
public IEnumerable<BeatmapSetInfo> BeatmapSets;
private InputManager inputManager;
[BackgroundDependencyLoader]
private void load(OsuGameBase game, BeatmapDatabase beatmaps, OsuColour colours)
private void load(OsuGameBase game, BeatmapDatabase beatmaps, OsuColour colours, UserInputManager inputManager)
{
this.inputManager = inputManager;
this.beatmaps = beatmaps;
trackManager = game.Audio.Track;
@ -100,7 +103,7 @@ namespace osu.Game.Overlays.Music
protected override void PopIn()
{
filter.Search.HoldFocus = true;
Schedule(() => filter.Search.TriggerFocus());
Schedule(() => inputManager.ChangeFocus(filter.Search));
ResizeTo(new Vector2(1, playlist_height), transition_duration, EasingTypes.OutQuint);
FadeIn(transition_duration, EasingTypes.OutQuint);

View File

@ -266,24 +266,27 @@ namespace osu.Game.Overlays
{
progressBar.IsEnabled = beatmap != null;
bool audioEquals = beatmapBacking.Value?.BeatmapInfo?.AudioEquals(current?.BeatmapInfo) ?? false;
TransformDirection direction = TransformDirection.None;
TransformDirection direction;
if (audioEquals)
direction = TransformDirection.None;
else if (queuedDirection.HasValue)
if (current != null)
{
direction = queuedDirection.Value;
queuedDirection = null;
}
else
{
//figure out the best direction based on order in playlist.
var last = current == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo.ID).Count();
var next = beatmapBacking.Value == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmapBacking.Value.BeatmapSetInfo.ID).Count();
bool audioEquals = beatmapBacking.Value?.BeatmapInfo?.AudioEquals(current.BeatmapInfo) ?? false;
direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
if (audioEquals)
direction = TransformDirection.None;
else if (queuedDirection.HasValue)
{
direction = queuedDirection.Value;
queuedDirection = null;
}
else
{
//figure out the best direction based on order in playlist.
var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo.ID).Count();
var next = beatmapBacking.Value == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmapBacking.Value.BeatmapSetInfo.ID).Count();
direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
}
}
current = beatmapBacking.Value;

View File

@ -51,9 +51,12 @@ namespace osu.Game.Overlays.Settings.Sections.General
Spacing = new Vector2(0f, 5f);
}
private InputManager inputManager;
[BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuColour colours, APIAccess api)
private void load(OsuColour colours, APIAccess api, UserInputManager inputManager)
{
this.inputManager = inputManager;
this.colours = colours;
api?.Register(this);
}
@ -160,12 +163,12 @@ namespace osu.Game.Overlays.Settings.Sections.General
break;
}
form?.TriggerFocus();
if (form != null) inputManager.ChangeFocus(form);
}
protected override bool OnFocus(InputState state)
{
form?.TriggerFocus();
if (form != null) inputManager.ChangeFocus(form);
return base.OnFocus(state);
}
@ -174,6 +177,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
private TextBox username;
private TextBox password;
private APIAccess api;
private InputManager inputManager;
private void performLogin()
{
@ -182,8 +186,9 @@ namespace osu.Game.Overlays.Settings.Sections.General
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api, OsuConfigManager config)
private void load(APIAccess api, OsuConfigManager config, UserInputManager inputManager)
{
this.inputManager = inputManager;
this.api = api;
Direction = FillDirection.Vertical;
Spacing = new Vector2(0, 5);
@ -232,14 +237,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
protected override bool OnFocus(InputState state)
{
Schedule(() =>
{
if (string.IsNullOrEmpty(username.Text))
username.TriggerFocus();
else
password.TriggerFocus();
});
Schedule(() => { inputManager.ChangeFocus(string.IsNullOrEmpty(username.Text) ? username : password); });
return base.OnFocus(state);
}
}

View File

@ -134,12 +134,13 @@ namespace osu.Game.Overlays
FadeTo(0, TRANSITION_LENGTH / 2);
searchTextBox.HoldFocus = false;
searchTextBox.TriggerFocusLost();
if (searchTextBox.HasFocus)
InputManager.ChangeFocus(null);
}
protected override bool OnFocus(InputState state)
{
searchTextBox.TriggerFocus(state);
InputManager.ChangeFocus(searchTextBox);
return false;
}

View File

@ -15,28 +15,51 @@ using System.Linq;
namespace osu.Game.Rulesets.Objects.Drawables
{
public abstract class DrawableHitObject<TObject, TJudgement> : Container
where TObject : HitObject
where TJudgement : Judgement
public abstract class DrawableHitObject : Container
{
public event Action<DrawableHitObject<TObject, TJudgement>> OnJudgement;
public TObject HitObject;
public readonly HitObject HitObject;
/// <summary>
/// The colour used for various elements of this DrawableHitObject.
/// </summary>
public virtual Color4 AccentColour { get; set; }
protected DrawableHitObject(HitObject hitObject)
{
HitObject = hitObject;
}
}
public abstract class DrawableHitObject<TObject> : DrawableHitObject
where TObject : HitObject
{
public new readonly TObject HitObject;
protected DrawableHitObject(TObject hitObject)
: base(hitObject)
{
HitObject = hitObject;
}
}
public abstract class DrawableHitObject<TObject, TJudgement> : DrawableHitObject<TObject>
where TObject : HitObject
where TJudgement : Judgement
{
public event Action<DrawableHitObject<TObject, TJudgement>> OnJudgement;
public override bool HandleInput => Interactive;
public bool Interactive = true;
public TJudgement Judgement;
protected abstract TJudgement CreateJudgement();
protected List<SampleChannel> Samples = new List<SampleChannel>();
protected abstract void UpdateState(ArmedState state);
protected DrawableHitObject(TObject hitObject)
: base(hitObject)
{
}
private ArmedState state;
public ArmedState State
@ -59,8 +82,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
}
}
protected List<SampleChannel> Samples = new List<SampleChannel>();
protected void PlaySamples()
{
Samples.ForEach(s => s?.Play());
@ -79,11 +100,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// </summary>
public bool Judged => (Judgement?.Result ?? HitResult.None) != HitResult.None && (NestedHitObjects?.All(h => h.Judged) ?? true);
protected DrawableHitObject(TObject hitObject)
{
HitObject = hitObject;
}
/// <summary>
/// Process a hit of this hitobject. Carries out judgement.
/// </summary>
@ -176,5 +192,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
h.OnJudgement += d => OnJudgement?.Invoke(d);
nestedHitObjects.Add(h);
}
protected abstract TJudgement CreateJudgement();
protected abstract void UpdateState(ArmedState state);
}
}

View File

@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Objects
/// <summary>
/// The time at which the HitObject starts.
/// </summary>
public double StartTime;
public virtual double StartTime { get; set; }
/// <summary>
/// The samples to be played when this hit object is hit.

View File

@ -0,0 +1,55 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Objects
{
/// <summary>
/// Compares two hit objects by their start time, falling back to creation order if their start time is equal.
/// </summary>
public class HitObjectStartTimeComparer : Drawable.CreationOrderDepthComparer
{
public override int Compare(Drawable x, Drawable y)
{
var hitObjectX = x as DrawableHitObject;
var hitObjectY = y as DrawableHitObject;
// If either of the two drawables are not hit objects, fall back to the base comparer
if (hitObjectX?.HitObject == null || hitObjectY?.HitObject == null)
return base.Compare(x, y);
// Compare by start time
int i = hitObjectX.HitObject.StartTime.CompareTo(hitObjectY.HitObject.StartTime);
if (i != 0)
return i;
return base.Compare(x, y);
}
}
/// <summary>
/// Compares two hit objects by their start time, falling back to creation order if their start time is equal.
/// This will compare the two hit objects in reverse order.
/// </summary>
public class HitObjectReverseStartTimeComparer : Drawable.ReverseCreationOrderDepthComparer
{
public override int Compare(Drawable x, Drawable y)
{
var hitObjectX = x as DrawableHitObject;
var hitObjectY = y as DrawableHitObject;
// If either of the two drawables are not hit objects, fall back to the base comparer
if (hitObjectX?.HitObject == null || hitObjectY?.HitObject == null)
return base.Compare(x, y);
// Compare by start time
int i = hitObjectY.HitObject.StartTime.CompareTo(hitObjectX.HitObject.StartTime);
if (i != 0)
return i;
return base.Compare(x, y);
}
}
}

View File

@ -6,20 +6,30 @@ using System;
using System.Collections.Generic;
using OpenTK;
using osu.Game.Audio;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Database;
namespace osu.Game.Rulesets.Objects.Legacy
{
internal abstract class ConvertSlider : HitObject, IHasCurve
{
/// <summary>
/// Scoring distance with a speed-adjusted beat length of 1 second.
/// </summary>
private const float base_scoring_distance = 100;
public List<Vector2> ControlPoints { get; set; }
public CurveType CurveType { get; set; }
public double Distance { get; set; }
public List<SampleInfoList> RepeatSamples { get; set; }
public int RepeatCount { get; set; } = 1;
public double EndTime { get; set; }
public double Duration { get; set; }
public double EndTime => StartTime + RepeatCount * Distance / Velocity;
public double Duration => EndTime - StartTime;
public double Velocity = 1;
public Vector2 PositionAt(double progress)
{
@ -35,5 +45,17 @@ namespace osu.Game.Rulesets.Objects.Legacy
{
throw new NotImplementedException();
}
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
base.ApplyDefaults(controlPointInfo, difficulty);
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier / difficultyPoint.SpeedMultiplier;
Velocity = scoringDistance / timingPoint.BeatLength;
}
}
}

View File

@ -135,7 +135,7 @@ namespace osu.Game.Screens.Menu
switch (args.Key)
{
case Key.Space:
osuLogo.TriggerClick(state);
osuLogo.TriggerOnClick(state);
return true;
case Key.Escape:
switch (State)
@ -144,7 +144,7 @@ namespace osu.Game.Screens.Menu
State = MenuState.Initial;
return true;
case MenuState.Play:
backButton.TriggerClick();
backButton.TriggerOnClick();
return true;
}
@ -178,10 +178,10 @@ namespace osu.Game.Screens.Menu
State = MenuState.TopLevel;
return;
case MenuState.TopLevel:
buttonsTopLevel.First().TriggerClick();
buttonsTopLevel.First().TriggerOnClick();
return;
case MenuState.Play:
buttonsPlay.First().TriggerClick();
buttonsPlay.First().TriggerOnClick();
return;
}
}

View File

@ -26,7 +26,7 @@ namespace osu.Game.Screens.Play
{
if (!args.Repeat && args.Key == Key.Escape)
{
Buttons.Children.Last().TriggerClick();
Buttons.Children.Last().TriggerOnClick();
return true;
}

View File

@ -131,13 +131,13 @@ namespace osu.Game.Screens.Play
public override bool HandleInput => true;
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => target.Children.Any(c => c.TriggerKeyDown(state, args));
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => target.Children.Any(c => c.TriggerOnKeyDown(state, args));
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => target.Children.Any(c => c.TriggerKeyUp(state, args));
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => target.Children.Any(c => c.TriggerOnKeyUp(state, args));
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => target.Children.Any(c => c.TriggerMouseDown(state, args));
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => target.Children.Any(c => c.TriggerOnMouseDown(state, args));
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => target.Children.Any(c => c.TriggerMouseUp(state, args));
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => target.Children.Any(c => c.TriggerOnMouseUp(state, args));
}
}
}

View File

@ -136,7 +136,7 @@ namespace osu.Game.Screens.Play
{
if (!args.Repeat && args.Key == Key.Escape)
{
Buttons.Children.First().TriggerClick();
Buttons.Children.First().TriggerOnClick();
return true;
}

View File

@ -293,7 +293,7 @@ namespace osu.Game.Screens.Play
protected override bool OnExiting(Screen next)
{
if (HasFailed || !ValidForResume || pauseContainer.AllowExit || HitRenderer.HasReplayLoaded)
if (HasFailed || !ValidForResume || pauseContainer?.AllowExit != false || HitRenderer?.HasReplayLoaded != false)
{
fadeOut();
return base.OnExiting(next);
@ -310,7 +310,7 @@ namespace osu.Game.Screens.Play
HitRenderer?.FadeOut(fade_out_duration);
Content.FadeOut(fade_out_duration);
hudOverlay.ScaleTo(0.7f, fade_out_duration * 3, EasingTypes.In);
hudOverlay?.ScaleTo(0.7f, fade_out_duration * 3, EasingTypes.In);
Background?.FadeTo(1f, fade_out_duration);
}

View File

@ -127,7 +127,7 @@ namespace osu.Game.Screens.Play
switch (args.Key)
{
case Key.Space:
button.TriggerClick();
button.TriggerOnClick();
return true;
}

View File

@ -154,7 +154,8 @@ namespace osu.Game.Screens.Select
public void Deactivate()
{
searchTextBox.HoldFocus = false;
searchTextBox.TriggerFocusLost();
if (searchTextBox.HasFocus)
inputManager.ChangeFocus(searchTextBox);
}
public void Activate()
@ -164,9 +165,13 @@ namespace osu.Game.Screens.Select
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
[BackgroundDependencyLoader(permitNulls:true)]
private void load(OsuColour colours, OsuGame osu)
private InputManager inputManager;
[BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuColour colours, OsuGame osu, UserInputManager inputManager)
{
this.inputManager = inputManager;
sortTabs.AccentColour = colours.GreenLight;
if (osu != null)

View File

@ -176,6 +176,7 @@
<Compile Include="Rulesets\Objects\Drawables\IDrawableHitObjectWithProxiedApproach.cs" />
<Compile Include="Rulesets\Judgements\Judgement.cs" />
<Compile Include="Rulesets\Objects\HitObjectParser.cs" />
<Compile Include="Rulesets\Objects\HitObjectStartTimeComparer.cs" />
<Compile Include="Rulesets\Objects\Types\IHasCombo.cs" />
<Compile Include="Rulesets\Objects\Types\IHasEndTime.cs" />
<Compile Include="Rulesets\Objects\Types\IHasDistance.cs" />