1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-15 10:42:55 +08:00

Merge branch 'master' into mania-judgements

This commit is contained in:
Dean Herbert 2017-05-29 21:31:48 +09:00 committed by GitHub
commit 476526714d
16 changed files with 452 additions and 91 deletions

@ -1 +1 @@
Subproject commit aa3e296968873208ca4460b00c0b82fe3aa8ff5c Subproject commit 0f3db5da09d0e7c4d2ef3057030e018f34ba536e

View File

@ -0,0 +1,39 @@
// 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.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseBreadcrumbs : TestCase
{
public override string Description => @"breadcrumb > control";
public override void Reset()
{
base.Reset();
BreadcrumbControl<BreadcrumbTab> c;
Add(c = new BreadcrumbControl<BreadcrumbTab>
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Width = 0.5f,
});
AddStep(@"first", () => c.Current.Value = BreadcrumbTab.Click);
AddStep(@"second", () => c.Current.Value = BreadcrumbTab.The);
AddStep(@"third", () => c.Current.Value = BreadcrumbTab.Circles);
}
private enum BreadcrumbTab
{
Click,
The,
Circles,
}
}
}

View File

@ -223,6 +223,7 @@
<Compile Include="Tests\TestCaseDrawableRoom.cs" /> <Compile Include="Tests\TestCaseDrawableRoom.cs" />
<Compile Include="Tests\TestCaseUserPanel.cs" /> <Compile Include="Tests\TestCaseUserPanel.cs" />
<Compile Include="Tests\TestCaseDirect.cs" /> <Compile Include="Tests\TestCaseDirect.cs" />
<Compile Include="Tests\TestCaseBreadcrumbs.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<ItemGroup /> <ItemGroup />

View File

@ -13,6 +13,7 @@ using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
using OpenTK; using OpenTK;
using osu.Game.Audio;
namespace osu.Game.Rulesets.Mania.Beatmaps namespace osu.Game.Rulesets.Mania.Beatmaps
{ {
@ -161,9 +162,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
pattern.Add(new HoldNote pattern.Add(new HoldNote
{ {
StartTime = HitObject.StartTime, StartTime = HitObject.StartTime,
Samples = HitObject.Samples,
Duration = endTimeData.Duration, Duration = endTimeData.Duration,
Column = column, Column = column,
Head = { Samples = sampleInfoListAt(HitObject.StartTime) },
Tail = { Samples = sampleInfoListAt(endTimeData.EndTime) },
}); });
} }
else if (positionData != null) else if (positionData != null)
@ -178,6 +180,24 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
return pattern; return pattern;
} }
/// <summary>
/// Retrieves the sample info list at a point in time.
/// </summary>
/// <param name="time">The time to retrieve the sample info list from.</param>
/// <returns></returns>
private SampleInfoList sampleInfoListAt(double time)
{
var curveData = HitObject as IHasCurve;
if (curveData == null)
return HitObject.Samples;
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.RepeatCount;
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
return curveData.RepeatSamples[index];
}
} }
} }
} }

View File

@ -0,0 +1,21 @@
// 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.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Mania.Objects
{
public class BarLine : ManiaHitObject
{
/// <summary>
/// The control point which this bar line is part of.
/// </summary>
public TimingControlPoint ControlPoint;
/// <summary>
/// The index of the beat which this bar line represents within the control point.
/// This is a "major" bar line if <see cref="BeatIndex"/> % <see cref="TimingControlPoint.TimeSignature"/> == 0.
/// </summary>
public int BeatIndex;
}
}

View File

@ -0,0 +1,74 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
/// <summary>
/// Visualises a <see cref="BarLine"/>. Although this derives DrawableManiaHitObject,
/// this does not handle input/sound like a normal hit object.
/// </summary>
public class DrawableBarLine : DrawableManiaHitObject<BarLine>
{
/// <summary>
/// Height of major bar line triangles.
/// </summary>
private const float triangle_height = 12;
/// <summary>
/// Offset of the major bar line triangles from the sides of the bar line.
/// </summary>
private const float triangle_offset = 9;
public DrawableBarLine(BarLine barLine)
: base(barLine)
{
RelativeSizeAxes = Axes.X;
Height = 1;
Add(new Box
{
Name = "Bar line",
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
});
bool isMajor = barLine.BeatIndex % (int)barLine.ControlPoint.TimeSignature == 0;
if (isMajor)
{
Add(new EquilateralTriangle
{
Name = "Left triangle",
Anchor = Anchor.BottomLeft,
Origin = Anchor.TopCentre,
Size = new Vector2(triangle_height),
X = -triangle_offset,
Rotation = 90
});
Add(new EquilateralTriangle
{
Name = "Right triangle",
Anchor = Anchor.BottomRight,
Origin = Anchor.TopCentre,
Size = new Vector2(triangle_height),
X = triangle_offset,
Rotation = -90
});
}
if (!isMajor && barLine.BeatIndex % 2 == 1)
Alpha = 0.2f;
}
protected override void UpdateState(ArmedState state)
{
}
}
}

View File

@ -58,6 +58,9 @@ namespace osu.Game.Rulesets.Mania.Objects
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate; tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate;
Head.ApplyDefaults(controlPointInfo, difficulty);
Tail.ApplyDefaults(controlPointInfo, difficulty);
} }
/// <summary> /// <summary>

View File

@ -6,9 +6,11 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenTK; using OpenTK;
using OpenTK.Input; using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Lists; using osu.Framework.Lists;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Beatmaps; using osu.Game.Rulesets.Beatmaps;
@ -85,6 +87,34 @@ namespace osu.Game.Rulesets.Mania.UI
}; };
} }
[BackgroundDependencyLoader]
private void load()
{
var maniaPlayfield = (ManiaPlayfield)Playfield;
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
SortedList<TimingControlPoint> timingPoints = Beatmap.ControlPointInfo.TimingPoints;
for (int i = 0; i < timingPoints.Count; i++)
{
TimingControlPoint point = timingPoints[i];
// Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object
double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature;
int index = 0;
for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
{
maniaPlayfield.Add(new DrawableBarLine(new BarLine
{
StartTime = t,
ControlPoint = point,
BeatIndex = index
}));
}
}
}
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(); protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter();

View File

@ -21,6 +21,7 @@ using osu.Game.Rulesets.Mania.Timing;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Graphics.Transforms; using osu.Framework.Graphics.Transforms;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Rulesets.Mania.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
{ {
@ -57,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly FlowContainer<Column> columns; private readonly FlowContainer<Column> columns;
public IEnumerable<Column> Columns => columns.Children; public IEnumerable<Column> Columns => columns.Children;
private readonly ControlPointContainer barlineContainer; private readonly ControlPointContainer barLineContainer;
private List<Color4> normalColumnColours = new List<Color4>(); private List<Color4> normalColumnColours = new List<Color4>();
private Color4 specialColumnColour; private Color4 specialColumnColour;
@ -77,35 +78,51 @@ namespace osu.Game.Rulesets.Mania.UI
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Both,
AutoSizeAxes = Axes.X,
Masking = true, Masking = true,
Children = new Drawable[] Children = new Drawable[]
{ {
new Box new Container
{ {
RelativeSizeAxes = Axes.Both, Name = "Masked elements",
Colour = Color4.Black Anchor = Anchor.TopCentre,
}, Origin = Anchor.TopCentre,
columns = new FillFlowContainer<Column>
{
Name = "Columns",
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X, AutoSizeAxes = Axes.X,
Direction = FillDirection.Horizontal, Masking = true,
Padding = new MarginPadding { Left = 1, Right = 1 }, Children = new Drawable[]
Spacing = new Vector2(1, 0) {
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
},
columns = new FillFlowContainer<Column>
{
Name = "Columns",
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Direction = FillDirection.Horizontal,
Padding = new MarginPadding { Left = 1, Right = 1 },
Spacing = new Vector2(1, 0)
}
}
}, },
new Container new Container
{ {
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = HIT_TARGET_POSITION }, Padding = new MarginPadding { Top = HIT_TARGET_POSITION },
Children = new[] Children = new[]
{ {
barlineContainer = new ControlPointContainer(timingChanges) barLineContainer = new ControlPointContainer(timingChanges)
{ {
Name = "Bar lines", Name = "Bar lines",
RelativeSizeAxes = Axes.Both, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y
// Width is set in the Update method
} }
} }
} }
@ -190,6 +207,7 @@ namespace osu.Game.Rulesets.Mania.UI
} }
public override void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> h) => Columns.ElementAt(h.HitObject.Column).Add(h); public override void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> h) => Columns.ElementAt(h.HitObject.Column).Add(h);
public void Add(DrawableBarLine barline) => barLineContainer.Add(barline);
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{ {
@ -224,7 +242,7 @@ namespace osu.Game.Rulesets.Mania.UI
timeSpan = MathHelper.Clamp(timeSpan, time_span_min, time_span_max); timeSpan = MathHelper.Clamp(timeSpan, time_span_min, time_span_max);
barlineContainer.TimeSpan = value; barLineContainer.TimeSpan = value;
Columns.ForEach(c => c.ControlPointContainer.TimeSpan = value); Columns.ForEach(c => c.ControlPointContainer.TimeSpan = value);
} }
} }
@ -234,6 +252,13 @@ namespace osu.Game.Rulesets.Mania.UI
TransformTo(() => TimeSpan, newTimeSpan, duration, easing, new TransformTimeSpan()); TransformTo(() => TimeSpan, newTimeSpan, duration, easing, new TransformTimeSpan());
} }
protected override void Update()
{
// Due to masking differences, it is not possible to get the width of the columns container automatically
// While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually
barLineContainer.Width = columns.Width;
}
private class TransformTimeSpan : Transform<double> private class TransformTimeSpan : Transform<double>
{ {
public override double CurrentValue public override double CurrentValue

View File

@ -62,6 +62,7 @@
<Compile Include="Judgements\ManiaHitResult.cs" /> <Compile Include="Judgements\ManiaHitResult.cs" />
<Compile Include="Judgements\ManiaJudgement.cs" /> <Compile Include="Judgements\ManiaJudgement.cs" />
<Compile Include="ManiaDifficultyCalculator.cs" /> <Compile Include="ManiaDifficultyCalculator.cs" />
<Compile Include="Objects\Drawables\DrawableBarLine.cs" />
<Compile Include="Objects\Drawables\DrawableHoldNote.cs" /> <Compile Include="Objects\Drawables\DrawableHoldNote.cs" />
<Compile Include="Objects\Drawables\DrawableHoldNoteTick.cs" /> <Compile Include="Objects\Drawables\DrawableHoldNoteTick.cs" />
<Compile Include="Objects\Drawables\DrawableManiaHitObject.cs" /> <Compile Include="Objects\Drawables\DrawableManiaHitObject.cs" />
@ -70,6 +71,7 @@
<Compile Include="Objects\Drawables\Pieces\NotePiece.cs" /> <Compile Include="Objects\Drawables\Pieces\NotePiece.cs" />
<Compile Include="Objects\Types\IHasColumn.cs" /> <Compile Include="Objects\Types\IHasColumn.cs" />
<Compile Include="Scoring\ManiaScoreProcessor.cs" /> <Compile Include="Scoring\ManiaScoreProcessor.cs" />
<Compile Include="Objects\BarLine.cs" />
<Compile Include="Objects\HoldNote.cs" /> <Compile Include="Objects\HoldNote.cs" />
<Compile Include="Objects\HoldNoteTick.cs" /> <Compile Include="Objects\HoldNoteTick.cs" />
<Compile Include="Objects\ManiaHitObject.cs" /> <Compile Include="Objects\ManiaHitObject.cs" />

View File

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

View File

@ -0,0 +1,84 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using System.Linq;
namespace osu.Game.Graphics.UserInterface
{
public class BreadcrumbControl<T> : OsuTabControl<T>
{
private const float padding = 10;
protected override TabItem<T> CreateTabItem(T value) => new BreadcrumbTabItem(value);
public BreadcrumbControl()
{
Height = 26;
TabContainer.Spacing = new Vector2(padding, 0f);
Current.ValueChanged += tab =>
{
foreach (var t in TabContainer.Children.OfType<BreadcrumbTabItem>())
{
var tIndex = TabContainer.IndexOf(t);
var tabIndex = TabContainer.IndexOf(TabMap[tab]);
t.State = tIndex < tabIndex ? Visibility.Hidden : Visibility.Visible;
t.Chevron.FadeTo(tIndex <= tabIndex ? 0f : 1f, 500, EasingTypes.OutQuint);
}
};
}
private class BreadcrumbTabItem : OsuTabItem, IStateful<Visibility>
{
public readonly TextAwesome Chevron;
//don't allow clicking between transitions and don't make the chevron clickable
protected override bool InternalContains(Vector2 screenSpacePos) => Alpha == 1f && Text.Contains(screenSpacePos);
public override bool HandleInput => State == Visibility.Visible;
private Visibility state;
public Visibility State
{
get { return state; }
set
{
if (value == state) return;
state = value;
const float transition_duration = 500;
if (State == Visibility.Visible)
{
FadeIn(transition_duration, EasingTypes.OutQuint);
ScaleTo(new Vector2(1f), transition_duration, EasingTypes.OutQuint);
}
else
{
FadeOut(transition_duration, EasingTypes.OutQuint);
ScaleTo(new Vector2(0.8f, 1f), transition_duration, EasingTypes.OutQuint);
}
}
}
public BreadcrumbTabItem(T value) : base(value)
{
Text.TextSize = 16;
Padding = new MarginPadding { Right = padding + 8 }; //padding + chevron width
Add(Chevron = new TextAwesome
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreLeft,
TextSize = 12,
Icon = FontAwesome.fa_chevron_right,
Margin = new MarginPadding { Left = padding },
Alpha = 0f,
});
}
}
}
}

View File

@ -5,18 +5,21 @@ using osu.Framework.Audio.Sample;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input; using osu.Framework.Input;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Framework.Extensions.Color4Extensions; 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 namespace osu.Game.Graphics.UserInterface
{ {
public class TwoLayerButton : ClickableContainer public class TwoLayerButton : ClickableContainer
{ {
private readonly TextAwesome icon; private readonly BouncingIcon bouncingIcon;
public Box IconLayer; public Box IconLayer;
public Box TextLayer; public Box TextLayer;
@ -95,11 +98,10 @@ namespace osu.Game.Graphics.UserInterface
}, },
} }
}, },
icon = new TextAwesome bouncingIcon = new BouncingIcon
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
TextSize = 25,
}, },
} }
}, },
@ -146,7 +148,7 @@ namespace osu.Game.Graphics.UserInterface
{ {
set set
{ {
icon.Icon = value; bouncingIcon.Icon = value;
} }
} }
@ -162,58 +164,20 @@ namespace osu.Game.Graphics.UserInterface
protected override bool OnHover(InputState state) protected override bool OnHover(InputState state)
{ {
icon.ClearTransforms();
ResizeTo(SIZE_EXTENDED, transform_time, EasingTypes.OutElastic); 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); IconLayer.FadeColour(HoverColour, transform_time, EasingTypes.OutElastic);
const double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration; bouncingIcon.ScaleTo(1.1f, transform_time, EasingTypes.OutElastic);
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
});
return true; return true;
} }
protected override void OnHoverLost(InputState state) protected override void OnHoverLost(InputState state)
{ {
icon.ClearTransforms();
ResizeTo(SIZE_RETRACTED, transform_time, EasingTypes.OutElastic); ResizeTo(SIZE_RETRACTED, transform_time, EasingTypes.OutElastic);
IconLayer.FadeColour(TextLayer.Colour, transform_time, EasingTypes.OutElastic); IconLayer.FadeColour(TextLayer.Colour, transform_time, EasingTypes.OutElastic);
int duration = 0; //(int)(Game.Audio.BeatLength); bouncingIcon.ScaleTo(1, transform_time, EasingTypes.OutElastic);
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
});
} }
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
@ -239,5 +203,45 @@ namespace osu.Game.Graphics.UserInterface
return base.OnClick(state); 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

@ -27,6 +27,7 @@ namespace osu.Game.Overlays.Mods
public class ModButton : ModButtonEmpty, IHasTooltip public class ModButton : ModButtonEmpty, IHasTooltip
{ {
private ModIcon foregroundIcon; private ModIcon foregroundIcon;
private ModIcon backgroundIcon;
private readonly SpriteText text; private readonly SpriteText text;
private readonly Container<ModIcon> iconsContainer; private readonly Container<ModIcon> iconsContainer;
private SampleChannel sampleOn, sampleOff; private SampleChannel sampleOn, sampleOff;
@ -35,38 +36,67 @@ namespace osu.Game.Overlays.Mods
public string TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty; public string TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty;
private int _selectedIndex = -1; private const EasingTypes mod_switch_easing = EasingTypes.InOutSine;
private int selectedIndex private const double mod_switch_duration = 120;
// A selected index of -1 means not selected.
private int selectedIndex = -1;
protected int SelectedIndex
{ {
get get
{ {
return _selectedIndex; return selectedIndex;
} }
set set
{ {
if (value == _selectedIndex) return; if (value == selectedIndex) return;
_selectedIndex = value;
int direction = value < selectedIndex ? -1 : 1;
bool beforeSelected = Selected;
Mod modBefore = SelectedMod ?? Mods[0];
if (value >= Mods.Length) if (value >= Mods.Length)
selectedIndex = -1;
else if (value < -1)
selectedIndex = Mods.Length - 1;
else
selectedIndex = value;
Mod modAfter = SelectedMod ?? Mods[0];
if (beforeSelected != Selected)
{ {
_selectedIndex = -1; iconsContainer.RotateTo(Selected ? 5f : 0f, 300, EasingTypes.OutElastic);
} iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, EasingTypes.OutElastic);
else if (value <= -2) }
{
_selectedIndex = Mods.Length - 1; if (modBefore != modAfter)
{
const float rotate_angle = 16;
foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing);
backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing);
backgroundIcon.Icon = modAfter.Icon;
using (iconsContainer.BeginDelayedSequence(mod_switch_duration, true))
{
foregroundIcon.RotateTo(-rotate_angle * direction);
foregroundIcon.RotateTo(0f, mod_switch_duration, mod_switch_easing);
backgroundIcon.RotateTo(rotate_angle * direction);
backgroundIcon.RotateTo(0f, mod_switch_duration, mod_switch_easing);
iconsContainer.Schedule(() => displayMod(modAfter));
}
} }
iconsContainer.RotateTo(Selected ? 5f : 0f, 300, EasingTypes.OutElastic);
iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, EasingTypes.OutElastic);
foregroundIcon.Highlighted = Selected; foregroundIcon.Highlighted = Selected;
if (mod != null)
displayMod(SelectedMod ?? Mods[0]);
} }
} }
public bool Selected => selectedIndex != -1; public bool Selected => SelectedIndex != -1;
private Color4 selectedColour; private Color4 selectedColour;
public Color4 SelectedColour public Color4 SelectedColour
@ -117,7 +147,7 @@ namespace osu.Game.Overlays.Mods
// the mods from Mod, only multiple if Mod is a MultiMod // the mods from Mod, only multiple if Mod is a MultiMod
public override Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex); public override Mod SelectedMod => Mods.ElementAtOrDefault(SelectedIndex);
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio) private void load(AudioManager audio)
@ -142,23 +172,25 @@ namespace osu.Game.Overlays.Mods
public void SelectNext() public void SelectNext()
{ {
(++selectedIndex == -1 ? sampleOff : sampleOn).Play(); (++SelectedIndex == -1 ? sampleOff : sampleOn).Play();
Action?.Invoke(SelectedMod); Action?.Invoke(SelectedMod);
} }
public void SelectPrevious() public void SelectPrevious()
{ {
(--selectedIndex == -1 ? sampleOff : sampleOn).Play(); (--SelectedIndex == -1 ? sampleOff : sampleOn).Play();
Action?.Invoke(SelectedMod); Action?.Invoke(SelectedMod);
} }
public void Deselect() public void Deselect()
{ {
selectedIndex = -1; SelectedIndex = -1;
} }
private void displayMod(Mod mod) private void displayMod(Mod mod)
{ {
if (backgroundIcon != null)
backgroundIcon.Icon = foregroundIcon.Icon;
foregroundIcon.Icon = mod.Icon; foregroundIcon.Icon = mod.Icon;
text.Text = mod.Name; text.Text = mod.Name;
} }
@ -170,17 +202,17 @@ namespace osu.Game.Overlays.Mods
{ {
iconsContainer.Add(new[] iconsContainer.Add(new[]
{ {
new ModIcon(Mods[0]) backgroundIcon = new ModIcon(Mods[1])
{ {
Origin = Anchor.Centre, Origin = Anchor.BottomRight,
Anchor = Anchor.Centre, Anchor = Anchor.BottomRight,
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Position = new Vector2(1.5f), Position = new Vector2(1.5f),
}, },
foregroundIcon = new ModIcon(Mods[0]) foregroundIcon = new ModIcon(Mods[0])
{ {
Origin = Anchor.Centre, Origin = Anchor.BottomRight,
Anchor = Anchor.Centre, Anchor = Anchor.BottomRight,
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Position = new Vector2(-1.5f), Position = new Vector2(-1.5f),
}, },

View File

@ -6,20 +6,30 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using OpenTK; using OpenTK;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Database;
namespace osu.Game.Rulesets.Objects.Legacy namespace osu.Game.Rulesets.Objects.Legacy
{ {
internal abstract class ConvertSlider : HitObject, IHasCurve 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 List<Vector2> ControlPoints { get; set; }
public CurveType CurveType { get; set; } public CurveType CurveType { get; set; }
public double Distance { get; set; } public double Distance { get; set; }
public List<SampleInfoList> RepeatSamples { get; set; } public List<SampleInfoList> RepeatSamples { get; set; }
public int RepeatCount { get; set; } = 1; public int RepeatCount { get; set; } = 1;
public double EndTime { get; set; } public double EndTime => StartTime + RepeatCount * Distance / Velocity;
public double Duration { get; set; } public double Duration => EndTime - StartTime;
public double Velocity = 1;
public Vector2 PositionAt(double progress) public Vector2 PositionAt(double progress)
{ {
@ -35,5 +45,17 @@ namespace osu.Game.Rulesets.Objects.Legacy
{ {
throw new NotImplementedException(); 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

@ -451,6 +451,7 @@
<Compile Include="Overlays\Direct\SlimEnumDropdown.cs" /> <Compile Include="Overlays\Direct\SlimEnumDropdown.cs" />
<Compile Include="Graphics\Containers\ReverseDepthFillFlowContainer.cs" /> <Compile Include="Graphics\Containers\ReverseDepthFillFlowContainer.cs" />
<Compile Include="Database\RankStatus.cs" /> <Compile Include="Database\RankStatus.cs" />
<Compile Include="Graphics\UserInterface\BreadcrumbControl.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj"> <ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">