1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-13 10:03:05 +08:00

Merge branch 'master' into fix_taiko_sizing

This commit is contained in:
Dean Herbert 2017-08-22 23:19:41 +09:00 committed by GitHub
commit 6f8e85c2e1
28 changed files with 253 additions and 150 deletions

@ -1 +1 @@
Subproject commit f1527e5456cd228ddfb68cf6d56eb5d28dc360bf Subproject commit ba70b8eaa9b79d4248873d4399f3b9e918fc3c8f

View File

@ -28,12 +28,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
private Pattern lastPattern = new Pattern(); private Pattern lastPattern = new Pattern();
private FastRandom random; private FastRandom random;
private Beatmap beatmap; private Beatmap beatmap;
private bool isForCurrentRuleset;
protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original, bool isForCurrentRuleset) private readonly int availableColumns;
private readonly bool isForCurrentRuleset;
public ManiaBeatmapConverter(bool isForCurrentRuleset, int availableColumns)
{ {
this.isForCurrentRuleset = isForCurrentRuleset; if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
this.isForCurrentRuleset = isForCurrentRuleset;
this.availableColumns = availableColumns;
}
protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original)
{
beatmap = original; beatmap = original;
BeatmapDifficulty difficulty = original.BeatmapInfo.Difficulty; BeatmapDifficulty difficulty = original.BeatmapInfo.Difficulty;
@ -41,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate); int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
random = new FastRandom(seed); random = new FastRandom(seed);
return base.ConvertBeatmap(original, isForCurrentRuleset); return base.ConvertBeatmap(original);
} }
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap) protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
@ -89,7 +97,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// <returns>The hit objects generated.</returns> /// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original) private IEnumerable<ManiaHitObject> generateSpecific(HitObject original)
{ {
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern); var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, availableColumns, lastPattern);
Pattern newPattern = generator.Generate(); Pattern newPattern = generator.Generate();
lastPattern = newPattern; lastPattern = newPattern;
@ -113,14 +121,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
Patterns.PatternGenerator conversion = null; Patterns.PatternGenerator conversion = null;
if (distanceData != null) if (distanceData != null)
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern); conversion = new DistanceObjectPatternGenerator(random, original, beatmap, availableColumns, lastPattern);
else if (endTimeData != null) else if (endTimeData != null)
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap); conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, availableColumns);
else if (positionData != null) else if (positionData != null)
{ {
computeDensity(original.StartTime); computeDensity(original.StartTime);
conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair); conversion = new HitObjectPatternGenerator(random, original, beatmap, availableColumns, lastPattern, lastTime, lastPosition, density, lastStair);
recordNote(original.StartTime, positionData.Position); recordNote(original.StartTime, positionData.Position);
} }
@ -142,8 +150,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// </summary> /// </summary>
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
{ {
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern) public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
: base(random, hitObject, beatmap, previousPattern) : base(random, hitObject, beatmap, availableColumns, previousPattern)
{ {
} }

View File

@ -29,8 +29,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private PatternType convertType; private PatternType convertType;
public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern) public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
: base(random, hitObject, beatmap, previousPattern) : base(random, hitObject, beatmap, availableColumns, previousPattern)
{ {
convertType = PatternType.None; convertType = PatternType.None;
if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode) if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)

View File

@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{ {
private readonly double endTime; private readonly double endTime;
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap) public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns)
: base(random, hitObject, beatmap, new Pattern()) : base(random, hitObject, beatmap, availableColumns, new Pattern())
{ {
var endtimeData = HitObject as IHasEndTime; var endtimeData = HitObject as IHasEndTime;

View File

@ -20,9 +20,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private readonly PatternType convertType; private readonly PatternType convertType;
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair) public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair)
: base(random, hitObject, beatmap, previousPattern) : base(random, hitObject, beatmap, availableColumns, previousPattern)
{ {
if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
if (density < 0) throw new ArgumentOutOfRangeException(nameof(density));
StairType = lastStair; StairType = lastStair;
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime); TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);

View File

@ -25,11 +25,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// </summary> /// </summary>
protected readonly FastRandom Random; protected readonly FastRandom Random;
protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern) protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
: base(hitObject, beatmap, previousPattern) : base(hitObject, beatmap, availableColumns, previousPattern)
{ {
Random = random; if (random == null) throw new ArgumentNullException(nameof(random));
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
Random = random;
RandomStart = AvailableColumns == 8 ? 1 : 0; RandomStart = AvailableColumns == 8 ? 1 : 0;
} }
@ -62,6 +66,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// <returns>The amount of notes to be generated.</returns> /// <returns>The amount of notes to be generated.</returns>
protected int GetRandomNoteCount(double p2, double p3, double p4 = 0, double p5 = 0, double p6 = 0) protected int GetRandomNoteCount(double p2, double p3, double p4 = 0, double p5 = 0, double p6 = 0)
{ {
if (p2 < 0 || p2 > 1) throw new ArgumentOutOfRangeException(nameof(p2));
if (p3 < 0 || p3 > 1) throw new ArgumentOutOfRangeException(nameof(p3));
if (p4 < 0 || p4 > 1) throw new ArgumentOutOfRangeException(nameof(p4));
if (p5 < 0 || p5 > 1) throw new ArgumentOutOfRangeException(nameof(p5));
if (p6 < 0 || p6 > 1) throw new ArgumentOutOfRangeException(nameof(p6));
double val = Random.NextDouble(); double val = Random.NextDouble();
if (val >= 1 - p6) if (val >= 1 - p6)
return 6; return 6;

View File

@ -32,13 +32,17 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
/// </summary> /// </summary>
protected readonly Beatmap Beatmap; protected readonly Beatmap Beatmap;
protected PatternGenerator(HitObject hitObject, Beatmap beatmap, Pattern previousPattern) protected PatternGenerator(HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
{ {
PreviousPattern = previousPattern; if (hitObject == null) throw new ArgumentNullException(nameof(hitObject));
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
HitObject = hitObject; HitObject = hitObject;
Beatmap = beatmap; Beatmap = beatmap;
AvailableColumns = availableColumns;
AvailableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize); PreviousPattern = previousPattern;
} }
/// <summary> /// <summary>

View File

@ -6,6 +6,7 @@ using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using System.Collections.Generic; using System.Collections.Generic;
using System;
namespace osu.Game.Rulesets.Mania namespace osu.Game.Rulesets.Mania
{ {
@ -21,6 +22,6 @@ namespace osu.Game.Rulesets.Mania
return 0; return 0;
} }
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(); protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize)));
} }
} }

View File

@ -125,6 +125,7 @@ namespace osu.Game.Rulesets.Mania.UI
for (int i = 0; i < columnCount; i++) for (int i = 0; i < columnCount; i++)
{ {
var c = new Column(); var c = new Column();
c.Reversed.BindTo(Reversed);
c.VisibleTimeRange.BindTo(VisibleTimeRange); c.VisibleTimeRange.BindTo(VisibleTimeRange);
columns.Add(c); columns.Add(c);

View File

@ -33,9 +33,10 @@ namespace osu.Game.Rulesets.Mania.UI
public class ManiaRulesetContainer : ScrollingRulesetContainer<ManiaPlayfield, ManiaHitObject, ManiaJudgement> public class ManiaRulesetContainer : ScrollingRulesetContainer<ManiaPlayfield, ManiaHitObject, ManiaJudgement>
{ {
/// <summary> /// <summary>
/// Preferred column count. This will only have an effect during the initialization of the play field. /// The number of columns which the <see cref="ManiaPlayfield"/> should display, and which
/// the beatmap converter will attempt to convert beatmaps to use.
/// </summary> /// </summary>
public int PreferredColumns; private int availableColumns;
public IEnumerable<DrawableBarLine> BarLines; public IEnumerable<DrawableBarLine> BarLines;
@ -76,26 +77,35 @@ namespace osu.Game.Rulesets.Mania.UI
BarLines.ForEach(Playfield.Add); BarLines.ForEach(Playfield.Add);
} }
protected override void ApplyBeatmap() protected sealed override Playfield<ManiaHitObject, ManiaJudgement> CreatePlayfield() => new ManiaPlayfield(availableColumns)
{
base.ApplyBeatmap();
PreferredColumns = (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize));
}
protected sealed override Playfield<ManiaHitObject, ManiaJudgement> CreatePlayfield() => new ManiaPlayfield(PreferredColumns)
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
// Invert by default for now (should be moved to config/skin later)
Scale = new Vector2(1, -1)
}; };
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo); public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo);
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(); protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter()
{
if (IsForCurrentRuleset)
availableColumns = (int)Math.Max(1, Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize));
else
{
float percentSliderOrSpinner = (float)WorkingBeatmap.Beatmap.HitObjects.Count(h => h is IHasEndTime) / WorkingBeatmap.Beatmap.HitObjects.Count;
if (percentSliderOrSpinner < 0.2)
availableColumns = 7;
else if (percentSliderOrSpinner < 0.3 || Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize) >= 5)
availableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 5 ? 7 : 6;
else if (percentSliderOrSpinner > 0.6)
availableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 4 ? 5 : 4;
else
availableColumns = Math.Max(4, Math.Min((int)Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) + 1, 7));
}
return new ManiaBeatmapConverter(IsForCurrentRuleset, availableColumns);
}
protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h) protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h)
{ {

View File

@ -39,19 +39,22 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
/// </summary> /// </summary>
private const float taiko_base_distance = 100; private const float taiko_base_distance = 100;
private bool isForCurrentRuleset; private readonly bool isForCurrentRuleset;
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(HitObject) }; protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(HitObject) };
protected override Beatmap<TaikoHitObject> ConvertBeatmap(Beatmap original, bool isForCurrentRuleset) public TaikoBeatmapConverter(bool isForCurrentRuleset)
{ {
this.isForCurrentRuleset = isForCurrentRuleset; this.isForCurrentRuleset = isForCurrentRuleset;
}
protected override Beatmap<TaikoHitObject> ConvertBeatmap(Beatmap original)
{
// Rewrite the beatmap info to add the slider velocity multiplier // Rewrite the beatmap info to add the slider velocity multiplier
BeatmapInfo info = original.BeatmapInfo.DeepClone(); BeatmapInfo info = original.BeatmapInfo.DeepClone();
info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier; info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier;
Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original, isForCurrentRuleset); Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original);
// Post processing step to transform hit objects with the same start time into strong hits // Post processing step to transform hit objects with the same start time into strong hits
converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x => converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x =>

View File

@ -135,6 +135,6 @@ namespace osu.Game.Rulesets.Taiko
return difficulty; return difficulty;
} }
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter(); protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter(true);
} }
} }

View File

@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Taiko.UI
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter(); protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter(IsForCurrentRuleset);
public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);

View File

@ -30,11 +30,15 @@ namespace osu.Game.Beatmaps
public abstract class DifficultyCalculator<T> : DifficultyCalculator where T : HitObject public abstract class DifficultyCalculator<T> : DifficultyCalculator where T : HitObject
{ {
protected readonly Beatmap Beatmap;
protected List<T> Objects; protected List<T> Objects;
protected DifficultyCalculator(Beatmap beatmap) protected DifficultyCalculator(Beatmap beatmap)
{ {
Objects = CreateBeatmapConverter().Convert(beatmap, true).HitObjects; Beatmap = beatmap;
Objects = CreateBeatmapConverter().Convert(beatmap).HitObjects;
foreach (var h in Objects) foreach (var h in Objects)
h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty); h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty);

View File

@ -11,6 +11,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Configuration; using osu.Game.Configuration;
using System; using System;
using System.Diagnostics;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
namespace osu.Game.Graphics.Cursor namespace osu.Game.Graphics.Cursor
@ -21,20 +22,31 @@ namespace osu.Game.Graphics.Cursor
private bool dragging; private bool dragging;
private bool startRotation;
protected override bool OnMouseMove(InputState state) protected override bool OnMouseMove(InputState state)
{ {
if (dragging) if (dragging)
{ {
Vector2 offset = state.Mouse.Position - state.Mouse.PositionMouseDown ?? state.Mouse.Delta; Debug.Assert(state.Mouse.PositionMouseDown != null);
float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f;
// Always rotate in the direction of least distance // don't start rotating until we're moved a minimum distance away from the mouse down location,
float diff = (degrees - ActiveCursor.Rotation) % 360; // else it can have an annoying effect.
if (diff < -180) diff += 360; startRotation |= Vector2.Distance(state.Mouse.Position, state.Mouse.PositionMouseDown.Value) > 30;
if (diff > 180) diff -= 360;
degrees = ActiveCursor.Rotation + diff;
ActiveCursor.RotateTo(degrees, 600, Easing.OutQuint); if (startRotation)
{
Vector2 offset = state.Mouse.Position - state.Mouse.PositionMouseDown.Value;
float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f;
// Always rotate in the direction of least distance
float diff = (degrees - ActiveCursor.Rotation) % 360;
if (diff < -180) diff += 360;
if (diff > 180) diff -= 360;
degrees = ActiveCursor.Rotation + diff;
ActiveCursor.RotateTo(degrees, 600, Easing.OutQuint);
}
} }
return base.OnMouseMove(state); return base.OnMouseMove(state);
@ -61,6 +73,7 @@ namespace osu.Game.Graphics.Cursor
if (!state.Mouse.HasMainButtonPressed) if (!state.Mouse.HasMainButtonPressed)
{ {
dragging = false; dragging = false;
startRotation = false;
((Cursor)ActiveCursor).AdditiveLayer.FadeOut(500, Easing.OutQuint); ((Cursor)ActiveCursor).AdditiveLayer.FadeOut(500, Easing.OutQuint);
ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), Easing.OutElasticHalf); ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), Easing.OutElasticHalf);

View File

@ -3,11 +3,11 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Threading; using osu.Framework.Threading;
using OpenTK; using OpenTK;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface.Volume namespace osu.Game.Graphics.UserInterface.Volume
{ {
@ -64,15 +64,25 @@ namespace osu.Game.Graphics.UserInterface.Volume
volumeMeterMusic.Bindable.ValueChanged -= volumeChanged; volumeMeterMusic.Bindable.ValueChanged -= volumeChanged;
} }
public void Adjust(InputState state) public bool Adjust(GlobalAction action)
{ {
if (State == Visibility.Hidden) switch (action)
{ {
Show(); case GlobalAction.DecreaseVolume:
return; if (State == Visibility.Hidden)
Show();
else
volumeMeterMaster.Decrease();
return true;
case GlobalAction.IncreaseVolume:
if (State == Visibility.Hidden)
Show();
else
volumeMeterMaster.Increase();
return true;
} }
volumeMeterMaster.TriggerOnWheel(state); return false;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -3,32 +3,16 @@
using System; using System;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input; using osu.Framework.Input.Bindings;
using OpenTK.Input; using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface.Volume namespace osu.Game.Graphics.UserInterface.Volume
{ {
internal class VolumeControlReceptor : Container internal class VolumeControlReceptor : Container, IKeyBindingHandler<GlobalAction>
{ {
public Action<InputState> ActionRequested; public Func<GlobalAction, bool> ActionRequested;
protected override bool OnWheel(InputState state) public bool OnPressed(GlobalAction action) => ActionRequested?.Invoke(action) ?? false;
{ public bool OnReleased(GlobalAction action) => false;
ActionRequested?.Invoke(state);
return true;
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
switch (args.Key)
{
case Key.Up:
case Key.Down:
ActionRequested?.Invoke(state);
return true;
}
return base.OnKeyDown(state, args);
}
} }
} }

View File

@ -4,15 +4,16 @@
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings;
using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface.Volume namespace osu.Game.Graphics.UserInterface.Volume
{ {
internal class VolumeMeter : Container internal class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
{ {
private readonly Box meterFill; private readonly Box meterFill;
public BindableDouble Bindable { get; } = new BindableDouble(); public BindableDouble Bindable { get; } = new BindableDouble();
@ -76,12 +77,35 @@ namespace osu.Game.Graphics.UserInterface.Volume
} }
} }
protected override bool OnWheel(InputState state) public void Increase()
{ {
Volume += 0.05f * state.Mouse.WheelDelta; Volume += 0.05f;
return true; }
public void Decrease()
{
Volume -= 0.05f;
} }
private void updateFill() => meterFill.ScaleTo(new Vector2(1, (float)Volume), 300, Easing.OutQuint); private void updateFill() => meterFill.ScaleTo(new Vector2(1, (float)Volume), 300, Easing.OutQuint);
public bool OnPressed(GlobalAction action)
{
if (!IsHovered) return false;
switch (action)
{
case GlobalAction.DecreaseVolume:
Decrease();
return true;
case GlobalAction.IncreaseVolume:
Increase();
return true;
}
return false;
}
public bool OnReleased(GlobalAction action) => false;
} }
} }

View File

@ -26,7 +26,10 @@ namespace osu.Game.Input.Bindings
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings), new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings),
new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar), new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar),
new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings), new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings),
new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleDirect), new KeyBinding(new[] { InputKey.Up }, GlobalAction.IncreaseVolume),
new KeyBinding(new[] { InputKey.MouseWheelUp }, GlobalAction.IncreaseVolume),
new KeyBinding(new[] { InputKey.Down }, GlobalAction.DecreaseVolume),
new KeyBinding(new[] { InputKey.MouseWheelDown }, GlobalAction.DecreaseVolume),
}; };
protected override IEnumerable<Drawable> KeyBindingInputQueue => protected override IEnumerable<Drawable> KeyBindingInputQueue =>
@ -47,5 +50,9 @@ namespace osu.Game.Input.Bindings
ToggleSettings, ToggleSettings,
[Description("Toggle osu!direct")] [Description("Toggle osu!direct")]
ToggleDirect, ToggleDirect,
[Description("Increase Volume")]
IncreaseVolume,
[Description("Decrease Volume")]
DecreaseVolume,
} }
} }

View File

@ -8,7 +8,6 @@ using osu.Game.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Framework.Input;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Game.Graphics.UserInterface.Volume; using osu.Game.Graphics.UserInterface.Volume;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -160,7 +159,7 @@ namespace osu.Game
new VolumeControlReceptor new VolumeControlReceptor
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
ActionRequested = delegate(InputState state) { volume.Adjust(state); } ActionRequested = action => volume.Adjust(action)
}, },
mainContent = new Container mainContent = new Container
{ {

View File

@ -136,40 +136,49 @@ namespace osu.Game.Overlays.KeyBinding
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{ {
if (HasFocus) if (!HasFocus || !bindTarget.IsHovered)
{ return base.OnMouseDown(state, args);
if (bindTarget.IsHovered)
{
if (!AllowMainMouseButtons)
{
switch (args.Button)
{
case MouseButton.Left:
case MouseButton.Right:
return true;
}
}
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state)); if (!AllowMainMouseButtons)
return true; {
switch (args.Button)
{
case MouseButton.Left:
case MouseButton.Right:
return true;
} }
} }
return base.OnMouseDown(state, args); bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
return true;
} }
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{ {
if (HasFocus && !state.Mouse.Buttons.Any()) // don't do anything until the last button is released.
if (!HasFocus || state.Mouse.Buttons.Any())
return base.OnMouseUp(state, args);
if (bindTarget.IsHovered)
finalise();
else
updateBindTarget();
return true;
}
protected override bool OnWheel(InputState state)
{
if (HasFocus)
{ {
if (bindTarget.IsHovered) if (bindTarget.IsHovered)
{
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
finalise(); finalise();
else return true;
updateBindTarget(); }
return true;
} }
return base.OnMouseUp(state, args); return base.OnWheel(state);
} }
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
@ -196,13 +205,10 @@ namespace osu.Game.Overlays.KeyBinding
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
{ {
if (HasFocus) if (!HasFocus) return base.OnKeyUp(state, args);
{
finalise();
return true;
}
return base.OnKeyUp(state, args); finalise();
return true;
} }
private void finalise() private void finalise()

View File

@ -22,7 +22,7 @@ namespace osu.Game.Overlays
private ScrollContainer scrollContainer; private ScrollContainer scrollContainer;
private FlowContainer<NotificationSection> sections; private FlowContainer<NotificationSection> sections;
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Width = width; Width = width;
@ -72,6 +72,13 @@ namespace osu.Game.Overlays
private int runningDepth; private int runningDepth;
private void notificationClosed()
{
// hide ourselves if all notifications have been dismissed.
if (sections.Select(c => c.DisplayedCount).Sum() > 0)
State = Visibility.Hidden;
}
public void Post(Notification notification) public void Post(Notification notification)
{ {
Schedule(() => Schedule(() =>
@ -81,6 +88,8 @@ namespace osu.Game.Overlays
++runningDepth; ++runningDepth;
notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth; notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth;
notification.Closed += notificationClosed;
var hasCompletionTarget = notification as IHasCompletionTarget; var hasCompletionTarget = notification as IHasCompletionTarget;
if (hasCompletionTarget != null) if (hasCompletionTarget != null)
hasCompletionTarget.CompletionTarget = Post; hasCompletionTarget.CompletionTarget = Post;

View File

@ -19,9 +19,9 @@ namespace osu.Game.Overlays.Notifications
public abstract class Notification : Container public abstract class Notification : Container
{ {
/// <summary> /// <summary>
/// Use requested close. /// User requested close.
/// </summary> /// </summary>
public Action Closed; public event Action Closed;
/// <summary> /// <summary>
/// Run on user activating the notification. Return true to close. /// Run on user activating the notification. Return true to close.

View File

@ -24,6 +24,8 @@ namespace osu.Game.Overlays.Notifications
private FlowContainer<Notification> notifications; private FlowContainer<Notification> notifications;
public int DisplayedCount => notifications.Count;
public void Add(Notification notification) public void Add(Notification notification)
{ {
notifications.Add(notification); notifications.Add(notification);

View File

@ -26,21 +26,19 @@ namespace osu.Game.Rulesets.Beatmaps
/// Converts a Beatmap using this Beatmap Converter. /// Converts a Beatmap using this Beatmap Converter.
/// </summary> /// </summary>
/// <param name="original">The un-converted Beatmap.</param> /// <param name="original">The un-converted Beatmap.</param>
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
/// <returns>The converted Beatmap.</returns> /// <returns>The converted Beatmap.</returns>
public Beatmap<T> Convert(Beatmap original, bool isForCurrentRuleset) public Beatmap<T> Convert(Beatmap original)
{ {
// We always operate on a clone of the original beatmap, to not modify it game-wide // We always operate on a clone of the original beatmap, to not modify it game-wide
return ConvertBeatmap(new Beatmap(original), isForCurrentRuleset); return ConvertBeatmap(new Beatmap(original));
} }
/// <summary> /// <summary>
/// Performs the conversion of a Beatmap using this Beatmap Converter. /// Performs the conversion of a Beatmap using this Beatmap Converter.
/// </summary> /// </summary>
/// <param name="original">The un-converted Beatmap.</param> /// <param name="original">The un-converted Beatmap.</param>
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
/// <returns>The converted Beatmap.</returns> /// <returns>The converted Beatmap.</returns>
protected virtual Beatmap<T> ConvertBeatmap(Beatmap original, bool isForCurrentRuleset) protected virtual Beatmap<T> ConvertBeatmap(Beatmap original)
{ {
return new Beatmap<T> return new Beatmap<T>
{ {

View File

@ -139,16 +139,30 @@ namespace osu.Game.Rulesets.UI
protected IEnumerable<Mod> Mods; protected IEnumerable<Mod> Mods;
/// <summary> /// <summary>
/// The <see cref="WorkingBeatmap"/> this <see cref="RulesetContainer{TObject}"/> was created with.
/// </summary>
protected readonly WorkingBeatmap WorkingBeatmap;
/// <summary>
/// Whether the specified beatmap is assumed to be specific to the current ruleset.
/// </summary>
protected readonly bool IsForCurrentRuleset;
/// <summary>
/// Whether to assume the beatmap passed into this <see cref="RulesetContainer{TObject}"/> is for the current ruleset.
/// Creates a hit renderer for a beatmap. /// Creates a hit renderer for a beatmap.
/// </summary> /// </summary>
/// <param name="ruleset">The ruleset being repesented.</param> /// <param name="ruleset">The ruleset being repesented.</param>
/// <param name="beatmap">The beatmap to create the hit renderer for.</param> /// <param name="workingBeatmap">The beatmap to create the hit renderer for.</param>
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param> /// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
internal RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) : base(ruleset) internal RulesetContainer(Ruleset ruleset, WorkingBeatmap workingBeatmap, bool isForCurrentRuleset)
: base(ruleset)
{ {
Debug.Assert(beatmap != null, "RulesetContainer initialized with a null beatmap."); Debug.Assert(workingBeatmap != null, "RulesetContainer initialized with a null beatmap.");
Mods = beatmap.Mods.Value; WorkingBeatmap = workingBeatmap;
IsForCurrentRuleset = isForCurrentRuleset;
Mods = workingBeatmap.Mods.Value;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
@ -156,11 +170,11 @@ namespace osu.Game.Rulesets.UI
BeatmapProcessor<TObject> processor = CreateBeatmapProcessor(); BeatmapProcessor<TObject> processor = CreateBeatmapProcessor();
// Check if the beatmap can be converted // Check if the beatmap can be converted
if (!converter.CanConvert(beatmap.Beatmap)) if (!converter.CanConvert(workingBeatmap.Beatmap))
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmap)} can not be converted for the current ruleset (converter: {converter})."); throw new BeatmapInvalidForRulesetException($"{nameof(Beatmap)} can not be converted for the current ruleset (converter: {converter}).");
// Convert the beatmap // Convert the beatmap
Beatmap = converter.Convert(beatmap.Beatmap, isForCurrentRuleset); Beatmap = converter.Convert(workingBeatmap.Beatmap);
// Apply difficulty adjustments from mods before using Difficulty. // Apply difficulty adjustments from mods before using Difficulty.
foreach (var mod in Mods.OfType<IApplicableToDifficulty>()) foreach (var mod in Mods.OfType<IApplicableToDifficulty>())
@ -173,8 +187,6 @@ namespace osu.Game.Rulesets.UI
// Post-process the beatmap // Post-process the beatmap
processor.PostProcess(Beatmap); processor.PostProcess(Beatmap);
ApplyBeatmap();
// Add mods, should always be the last thing applied to give full control to mods // Add mods, should always be the last thing applied to give full control to mods
applyMods(Mods); applyMods(Mods);
} }
@ -192,11 +204,6 @@ namespace osu.Game.Rulesets.UI
mod.ApplyToRulesetContainer(this); mod.ApplyToRulesetContainer(this);
} }
/// <summary>
/// Called when the beatmap of this hit renderer has been set. Used to apply any default values from the beatmap.
/// </summary>
protected virtual void ApplyBeatmap() { }
/// <summary> /// <summary>
/// Creates a processor to perform post-processing operations /// Creates a processor to perform post-processing operations
/// on HitObjects in converted Beatmaps. /// on HitObjects in converted Beatmaps.

View File

@ -173,7 +173,8 @@ namespace osu.Game.Rulesets.UI
} }
/// <summary> /// <summary>
/// Adds a <see cref="SpeedAdjustmentContainer"/> to this container. /// Adds a <see cref="SpeedAdjustmentContainer"/> to this container, re-sorting all hit objects
/// in the last <see cref="SpeedAdjustmentContainer"/> that occurred (time-wise) before it.
/// </summary> /// </summary>
/// <param name="speedAdjustment">The <see cref="SpeedAdjustmentContainer"/>.</param> /// <param name="speedAdjustment">The <see cref="SpeedAdjustmentContainer"/>.</param>
public void AddSpeedAdjustment(SpeedAdjustmentContainer speedAdjustment) public void AddSpeedAdjustment(SpeedAdjustmentContainer speedAdjustment)
@ -184,6 +185,8 @@ namespace osu.Game.Rulesets.UI
if (speedAdjustments.Count > 0) if (speedAdjustments.Count > 0)
{ {
// We need to re-sort all hit objects in the speed adjustment container prior to figure out if they
// should now lie within this one
var existingAdjustment = adjustmentContainerAt(speedAdjustment.ControlPoint.StartTime); var existingAdjustment = adjustmentContainerAt(speedAdjustment.ControlPoint.StartTime);
for (int i = 0; i < existingAdjustment.Count; i++) for (int i = 0; i < existingAdjustment.Count; i++)
{ {

View File

@ -39,17 +39,6 @@ namespace osu.Game.Rulesets.UI
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{
DefaultControlPoints.ForEach(c => applySpeedAdjustment(c, Playfield));
}
private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield<TObject, TJudgement> playfield)
{
playfield.HitObjects.AddSpeedAdjustment(CreateSpeedAdjustmentContainer(controlPoint));
playfield.NestedPlayfields.ForEach(p => applySpeedAdjustment(controlPoint, p));
}
protected override void ApplyBeatmap()
{ {
// Calculate default multiplier control points // Calculate default multiplier control points
var lastTimingPoint = new TimingControlPoint(); var lastTimingPoint = new TimingControlPoint();
@ -95,6 +84,14 @@ namespace osu.Game.Rulesets.UI
// If we have no control points, add a default one // If we have no control points, add a default one
if (DefaultControlPoints.Count == 0) if (DefaultControlPoints.Count == 0)
DefaultControlPoints.Add(new MultiplierControlPoint()); DefaultControlPoints.Add(new MultiplierControlPoint());
DefaultControlPoints.ForEach(c => applySpeedAdjustment(c, Playfield));
}
private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield<TObject, TJudgement> playfield)
{
playfield.HitObjects.AddSpeedAdjustment(CreateSpeedAdjustmentContainer(controlPoint));
playfield.NestedPlayfields.ForEach(p => applySpeedAdjustment(controlPoint, p));
} }
/// <summary> /// <summary>