mirror of
https://github.com/osukey/osukey.git
synced 2025-08-03 22:56:36 +09:00
Merge remote-tracking branch 'upstream/master' into RefactorInputQueues
This commit is contained in:
@ -566,7 +566,6 @@ namespace osu.Game.Beatmaps
|
||||
using (var stream = new StreamReader(reader.GetStream(mapName)))
|
||||
metadata = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata;
|
||||
|
||||
|
||||
// check if a set already exists with the same online id.
|
||||
if (metadata.OnlineBeatmapSetID != null)
|
||||
beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID);
|
||||
@ -581,7 +580,6 @@ namespace osu.Game.Beatmaps
|
||||
Metadata = metadata
|
||||
};
|
||||
|
||||
|
||||
var mapNames = reader.Filenames.Where(f => f.EndsWith(".osu"));
|
||||
|
||||
foreach (var name in mapNames)
|
||||
@ -691,19 +689,18 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
protected override Storyboard GetStoryboard()
|
||||
{
|
||||
if (BeatmapInfo?.Path == null && BeatmapSetInfo?.StoryboardFile == null)
|
||||
return new Storyboard();
|
||||
|
||||
try
|
||||
{
|
||||
Decoder decoder;
|
||||
using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo?.Path))))
|
||||
decoder = Decoder.GetDecoder(stream);
|
||||
using (var beatmap = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
|
||||
{
|
||||
Decoder decoder = Decoder.GetDecoder(beatmap);
|
||||
|
||||
// try for .osb first and fall back to .osu
|
||||
string storyboardFile = BeatmapSetInfo.StoryboardFile ?? BeatmapInfo.Path;
|
||||
using (var stream = new StreamReader(store.GetStream(getPathForFile(storyboardFile))))
|
||||
return decoder.GetStoryboardDecoder().DecodeStoryboard(stream);
|
||||
if (BeatmapSetInfo?.StoryboardFile == null)
|
||||
return decoder.GetStoryboardDecoder().DecodeStoryboard(beatmap);
|
||||
|
||||
using (var storyboard = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile))))
|
||||
return decoder.GetStoryboardDecoder().DecodeStoryboard(beatmap, storyboard);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -27,7 +27,6 @@ namespace osu.Game.Beatmaps
|
||||
Beatmap = CreateBeatmapConverter(beatmap).Convert(beatmap);
|
||||
Mods = mods ?? new Mod[0];
|
||||
|
||||
|
||||
ApplyMods(Mods);
|
||||
|
||||
PreprocessHitObjects();
|
||||
|
@ -64,7 +64,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public override string ShortName => "dummy";
|
||||
|
||||
public DummyRuleset(RulesetInfo rulesetInfo)
|
||||
public DummyRuleset(RulesetInfo rulesetInfo = null)
|
||||
: base(rulesetInfo)
|
||||
{
|
||||
}
|
||||
|
@ -70,10 +70,11 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
protected abstract void ParseBeatmap(StreamReader stream, Beatmap beatmap);
|
||||
|
||||
public virtual Storyboard DecodeStoryboard(StreamReader stream)
|
||||
public virtual Storyboard DecodeStoryboard(params StreamReader[] streams)
|
||||
{
|
||||
var storyboard = new Storyboard();
|
||||
ParseStoryboard(stream, storyboard);
|
||||
foreach (StreamReader stream in streams)
|
||||
ParseStoryboard(stream, storyboard);
|
||||
return storyboard;
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,6 @@ using OpenTK.Graphics;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Objects.Legacy;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
@ -82,15 +81,12 @@ namespace osu.Game.Beatmaps.Formats
|
||||
case Section.HitObjects:
|
||||
handleHitObjects(line);
|
||||
break;
|
||||
case Section.Variables:
|
||||
handleVariables(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleGeneral(string line)
|
||||
{
|
||||
var pair = splitKeyVal(line, ':');
|
||||
var pair = SplitKeyVal(line, ':');
|
||||
|
||||
var metadata = beatmap.BeatmapInfo.Metadata;
|
||||
switch (pair.Key)
|
||||
@ -149,7 +145,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private void handleEditor(string line)
|
||||
{
|
||||
var pair = splitKeyVal(line, ':');
|
||||
var pair = SplitKeyVal(line, ':');
|
||||
|
||||
switch (pair.Key)
|
||||
{
|
||||
@ -173,7 +169,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private void handleMetadata(string line)
|
||||
{
|
||||
var pair = splitKeyVal(line, ':');
|
||||
var pair = SplitKeyVal(line, ':');
|
||||
|
||||
var metadata = beatmap.BeatmapInfo.Metadata;
|
||||
switch (pair.Key)
|
||||
@ -214,7 +210,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private void handleDifficulty(string line)
|
||||
{
|
||||
var pair = splitKeyVal(line, ':');
|
||||
var pair = SplitKeyVal(line, ':');
|
||||
|
||||
var difficulty = beatmap.BeatmapInfo.BaseDifficulty;
|
||||
switch (pair.Key)
|
||||
@ -242,8 +238,6 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private void handleEvents(string line)
|
||||
{
|
||||
DecodeVariables(ref line);
|
||||
|
||||
string[] split = line.Split(',');
|
||||
|
||||
EventType type;
|
||||
@ -359,7 +353,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private void handleColours(string line)
|
||||
{
|
||||
var pair = splitKeyVal(line, ':');
|
||||
var pair = SplitKeyVal(line, ':');
|
||||
|
||||
string[] split = pair.Value.Split(',');
|
||||
|
||||
@ -400,22 +394,5 @@ namespace osu.Game.Beatmaps.Formats
|
||||
if (obj != null)
|
||||
beatmap.HitObjects.Add(obj);
|
||||
}
|
||||
|
||||
private void handleVariables(string line)
|
||||
{
|
||||
var pair = splitKeyVal(line, '=');
|
||||
Variables[pair.Key] = pair.Value;
|
||||
}
|
||||
|
||||
private KeyValuePair<string, string> splitKeyVal(string line, char separator)
|
||||
{
|
||||
var split = line.Trim().Split(new[] { separator }, 2);
|
||||
|
||||
return new KeyValuePair<string, string>
|
||||
(
|
||||
split[0].Trim(),
|
||||
split.Length > 1 ? split[1].Trim() : string.Empty
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ namespace osu.Game.Beatmaps.Formats
|
||||
}
|
||||
|
||||
protected int BeatmapVersion;
|
||||
protected readonly Dictionary<string, string> Variables = new Dictionary<string, string>();
|
||||
|
||||
public override Decoder GetStoryboardDecoder() => new LegacyStoryboardDecoder(BeatmapVersion);
|
||||
|
||||
@ -82,27 +81,15 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
protected abstract void ProcessSection(Section section, string line);
|
||||
|
||||
/// <summary>
|
||||
/// Decodes any beatmap variables present in a line into their real values.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which may contains variables.</param>
|
||||
protected void DecodeVariables(ref string line)
|
||||
protected KeyValuePair<string, string> SplitKeyVal(string line, char separator)
|
||||
{
|
||||
while (line.IndexOf('$') >= 0)
|
||||
{
|
||||
string origLine = line;
|
||||
string[] split = line.Split(',');
|
||||
for (int i = 0; i < split.Length; i++)
|
||||
{
|
||||
var item = split[i];
|
||||
if (item.StartsWith("$") && Variables.ContainsKey(item))
|
||||
split[i] = Variables[item];
|
||||
}
|
||||
var split = line.Trim().Split(new[] { separator }, 2);
|
||||
|
||||
line = string.Join(",", split);
|
||||
if (line == origLine)
|
||||
break;
|
||||
}
|
||||
return new KeyValuePair<string, string>
|
||||
(
|
||||
split[0].Trim(),
|
||||
split.Length > 1 ? split[1].Trim() : string.Empty
|
||||
);
|
||||
}
|
||||
|
||||
protected enum Section
|
||||
@ -150,7 +137,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
CentreRight,
|
||||
BottomLeft,
|
||||
BottomRight
|
||||
};
|
||||
}
|
||||
|
||||
internal enum StoryLayer
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using OpenTK;
|
||||
@ -19,6 +20,8 @@ namespace osu.Game.Beatmaps.Formats
|
||||
private StoryboardSprite storyboardSprite;
|
||||
private CommandTimelineGroup timelineGroup;
|
||||
|
||||
private readonly Dictionary<string, string> variables = new Dictionary<string, string>();
|
||||
|
||||
public LegacyStoryboardDecoder()
|
||||
{
|
||||
}
|
||||
@ -47,6 +50,9 @@ namespace osu.Game.Beatmaps.Formats
|
||||
case Section.Events:
|
||||
handleEvents(line);
|
||||
break;
|
||||
case Section.Variables:
|
||||
handleVariables(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,7 +65,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
line = line.Substring(1);
|
||||
}
|
||||
|
||||
DecodeVariables(ref line);
|
||||
decodeVariables(ref line);
|
||||
|
||||
string[] split = line.Split(',');
|
||||
|
||||
@ -266,6 +272,35 @@ namespace osu.Game.Beatmaps.Formats
|
||||
throw new InvalidDataException($@"Unknown origin: {value}");
|
||||
}
|
||||
|
||||
private void handleVariables(string line)
|
||||
{
|
||||
var pair = SplitKeyVal(line, '=');
|
||||
variables[pair.Key] = pair.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes any beatmap variables present in a line into their real values.
|
||||
/// </summary>
|
||||
/// <param name="line">The line which may contains variables.</param>
|
||||
private void decodeVariables(ref string line)
|
||||
{
|
||||
while (line.IndexOf('$') >= 0)
|
||||
{
|
||||
string origLine = line;
|
||||
string[] split = line.Split(',');
|
||||
for (int i = 0; i < split.Length; i++)
|
||||
{
|
||||
var item = split[i];
|
||||
if (item.StartsWith("$") && variables.ContainsKey(item))
|
||||
split[i] = variables[item];
|
||||
}
|
||||
|
||||
line = string.Join(",", split);
|
||||
if (line == origLine)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private string cleanFilename(string path) => FileSafety.PathSanitise(path.Trim('\"'));
|
||||
}
|
||||
}
|
||||
|
@ -65,12 +65,15 @@ namespace osu.Game.Configuration
|
||||
|
||||
// Gameplay
|
||||
Set(OsuSetting.DimLevel, 0.3, 0, 1, 0.01);
|
||||
Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01);
|
||||
|
||||
Set(OsuSetting.ShowInterface, true);
|
||||
Set(OsuSetting.KeyOverlay, false);
|
||||
|
||||
Set(OsuSetting.FloatingComments, false);
|
||||
|
||||
Set(OsuSetting.SpeedChangeVisualisation, SpeedChangeVisualisationMethod.Sequential);
|
||||
|
||||
// Update
|
||||
Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
|
||||
|
||||
@ -90,6 +93,7 @@ namespace osu.Game.Configuration
|
||||
GameplayCursorSize,
|
||||
AutoCursorSize,
|
||||
DimLevel,
|
||||
BlurLevel,
|
||||
ShowStoryboard,
|
||||
KeyOverlay,
|
||||
FloatingComments,
|
||||
@ -114,6 +118,7 @@ namespace osu.Game.Configuration
|
||||
ShowFpsDisplay,
|
||||
ChatDisplayHeight,
|
||||
Version,
|
||||
ShowConvertedBeatmaps
|
||||
ShowConvertedBeatmaps,
|
||||
SpeedChangeVisualisation
|
||||
}
|
||||
}
|
||||
|
15
osu.Game/Configuration/SpeedChangeVisualisationMethod.cs
Normal file
15
osu.Game/Configuration/SpeedChangeVisualisationMethod.cs
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace osu.Game.Configuration
|
||||
{
|
||||
public enum SpeedChangeVisualisationMethod
|
||||
{
|
||||
[Description("Sequential")]
|
||||
Sequential,
|
||||
[Description("Overlapping")]
|
||||
Overlapping
|
||||
}
|
||||
}
|
@ -33,15 +33,6 @@ namespace osu.Game.Graphics.Containers
|
||||
// receive input outside our bounds so we can trigger a close event on ourselves.
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceiveMouseInputAt(screenSpacePos);
|
||||
|
||||
protected override bool OnWheel(InputState state)
|
||||
{
|
||||
// always allow wheel to pass through to stuff outside our DrawRectangle.
|
||||
if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position))
|
||||
return false;
|
||||
|
||||
return BlockPassThroughMouse;
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position))
|
||||
|
67
osu.Game/Graphics/Cursor/CursorOverrideContainer.cs
Normal file
67
osu.Game/Graphics/Cursor/CursorOverrideContainer.cs
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Input;
|
||||
|
||||
namespace osu.Game.Graphics.Cursor
|
||||
{
|
||||
/// <summary>
|
||||
/// A container which provides a <see cref="MenuCursor"/> which can be overridden by hovered <see cref="Drawable"/>s.
|
||||
/// </summary>
|
||||
public class CursorOverrideContainer : Container, IProvideCursor
|
||||
{
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container content;
|
||||
|
||||
/// <summary>
|
||||
/// Whether any cursors can be displayed.
|
||||
/// </summary>
|
||||
public bool CanShowCursor;
|
||||
|
||||
public CursorContainer Cursor { get; }
|
||||
public bool ProvidingUserCursor => true;
|
||||
|
||||
public CursorOverrideContainer()
|
||||
{
|
||||
AddRangeInternal(new Drawable[]
|
||||
{
|
||||
Cursor = new MenuCursor { State = Visibility.Hidden },
|
||||
content = new Container { RelativeSizeAxes = Axes.Both }
|
||||
});
|
||||
}
|
||||
|
||||
private InputManager inputManager;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
inputManager = GetContainingInputManager();
|
||||
}
|
||||
|
||||
private IProvideCursor currentTarget;
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!CanShowCursor)
|
||||
{
|
||||
currentTarget?.Cursor?.Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
var newTarget = inputManager.HoveredDrawables.OfType<IProvideCursor>().FirstOrDefault(t => t.ProvidingUserCursor) ?? this;
|
||||
|
||||
if (currentTarget == newTarget)
|
||||
return;
|
||||
|
||||
currentTarget?.Cursor?.Hide();
|
||||
newTarget.Cursor?.Show();
|
||||
|
||||
currentTarget = newTarget;
|
||||
}
|
||||
}
|
||||
}
|
26
osu.Game/Graphics/Cursor/IProvideCursor.cs
Normal file
26
osu.Game/Graphics/Cursor/IProvideCursor.cs
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2007-2018 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.Framework.Graphics.Cursor;
|
||||
|
||||
namespace osu.Game.Graphics.Cursor
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for <see cref="IDrawable"/>s that display cursors which can replace the user's cursor.
|
||||
/// </summary>
|
||||
public interface IProvideCursor : IDrawable
|
||||
{
|
||||
/// <summary>
|
||||
/// The cursor provided by this <see cref="IDrawable"/>.
|
||||
/// May be null if no cursor should be visible.
|
||||
/// </summary>
|
||||
CursorContainer Cursor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether <see cref="Cursor"/> should be displayed as the singular user cursor. This will temporarily hide any other user cursor.
|
||||
/// This value is checked every frame and may be used to control whether multiple cursors are displayed (e.g. watching replays).
|
||||
/// </summary>
|
||||
bool ProvidingUserCursor { get; }
|
||||
}
|
||||
}
|
@ -99,8 +99,8 @@ namespace osu.Game.Graphics.Cursor
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
ActiveCursor.FadeTo(0, 900, Easing.OutQuint);
|
||||
ActiveCursor.ScaleTo(0, 500, Easing.In);
|
||||
ActiveCursor.FadeTo(0, 250, Easing.OutQuint);
|
||||
ActiveCursor.ScaleTo(0.6f, 250, Easing.In);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
@ -17,7 +18,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
public class OsuSliderBar<T> : SliderBar<T>, IHasTooltip, IHasAccentColour
|
||||
where T : struct, IEquatable<T>
|
||||
where T : struct, IEquatable<T>, IComparable, IConvertible
|
||||
{
|
||||
private SampleChannel sample;
|
||||
private double lastSampleTime;
|
||||
@ -32,18 +33,25 @@ namespace osu.Game.Graphics.UserInterface
|
||||
get
|
||||
{
|
||||
var bindableDouble = CurrentNumber as BindableNumber<double>;
|
||||
if (bindableDouble != null)
|
||||
var bindableFloat = CurrentNumber as BindableNumber<float>;
|
||||
var floatValue = bindableDouble?.Value ?? bindableFloat?.Value;
|
||||
|
||||
if (floatValue != null)
|
||||
{
|
||||
if (bindableDouble.MaxValue == 1 && (bindableDouble.MinValue == 0 || bindableDouble.MinValue == -1))
|
||||
return bindableDouble.Value.ToString(@"P0");
|
||||
return bindableDouble.Value.ToString(@"n1");
|
||||
var floatMinValue = bindableDouble?.MinValue ?? bindableFloat.MinValue;
|
||||
var floatMaxValue = bindableDouble?.MaxValue ?? bindableFloat.MaxValue;
|
||||
|
||||
if (floatMaxValue == 1 && (floatMinValue == 0 || floatMinValue == -1))
|
||||
return floatValue.Value.ToString("P0");
|
||||
|
||||
return floatValue.Value.ToString("N1");
|
||||
}
|
||||
|
||||
var bindableInt = CurrentNumber as BindableNumber<int>;
|
||||
if (bindableInt != null)
|
||||
return bindableInt.Value.ToString(@"n0");
|
||||
return bindableInt.Value.ToString("N0");
|
||||
|
||||
return Current.Value.ToString();
|
||||
return Current.Value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,7 +180,6 @@ namespace osu.Game.Graphics.UserInterface
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected class OsuTabDropdownHeader : OsuDropdownHeader
|
||||
{
|
||||
public override Color4 AccentColour
|
||||
|
@ -3,12 +3,13 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Input.Bindings;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface.Volume
|
||||
{
|
||||
public class VolumeControlReceptor : Container, IKeyBindingHandler<GlobalAction>
|
||||
public class VolumeControlReceptor : Container, IKeyBindingHandler<GlobalAction>, IHandleGlobalInput
|
||||
{
|
||||
public Func<GlobalAction, bool> ActionRequested;
|
||||
|
||||
|
@ -20,7 +20,9 @@ namespace osu.Game.Input.Bindings
|
||||
handler = game;
|
||||
}
|
||||
|
||||
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
|
||||
public override IEnumerable<KeyBinding> DefaultKeyBindings => GlobalKeyBindings.Concat(InGameKeyBindings);
|
||||
|
||||
public IEnumerable<KeyBinding> GlobalKeyBindings => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.F8, GlobalAction.ToggleChat),
|
||||
new KeyBinding(InputKey.F9, GlobalAction.ToggleSocial),
|
||||
@ -33,6 +35,11 @@ namespace osu.Game.Input.Bindings
|
||||
new KeyBinding(new[] { InputKey.MouseWheelDown }, GlobalAction.DecreaseVolume),
|
||||
};
|
||||
|
||||
public IEnumerable<KeyBinding> InGameKeyBindings => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.Space, GlobalAction.SkipCutscene)
|
||||
};
|
||||
|
||||
protected override IEnumerable<Drawable> KeyBindingInputQueue =>
|
||||
handler == null ? base.KeyBindingInputQueue : new[] { handler }.Concat(base.KeyBindingInputQueue);
|
||||
}
|
||||
@ -55,5 +62,9 @@ namespace osu.Game.Input.Bindings
|
||||
IncreaseVolume,
|
||||
[Description("Decrease Volume")]
|
||||
DecreaseVolume,
|
||||
|
||||
// In-Game Keybindings
|
||||
[Description("Skip Cutscene")]
|
||||
SkipCutscene
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,6 @@ namespace osu.Game.Online.API
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
13
osu.Game/Online/API/Requests/GetFriendsRequest.cs
Normal file
13
osu.Game/Online/API/Requests/GetFriendsRequest.cs
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class GetFriendsRequest : APIRequest<List<User>>
|
||||
{
|
||||
protected override string Target => @"friends";
|
||||
}
|
||||
}
|
@ -297,8 +297,6 @@ namespace osu.Game
|
||||
else
|
||||
Toolbar.State = Visibility.Visible;
|
||||
};
|
||||
|
||||
Cursor.State = Visibility.Hidden;
|
||||
}
|
||||
|
||||
private void forwardLoggedErrorsToNotifications()
|
||||
@ -447,7 +445,7 @@ namespace osu.Game
|
||||
|
||||
mainContent.Padding = new MarginPadding { Top = ToolbarOffset };
|
||||
|
||||
Cursor.State = currentScreen?.HasLocalCursorDisplayed == false ? Visibility.Visible : Visibility.Hidden;
|
||||
CursorOverrideContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
|
||||
}
|
||||
|
||||
private void screenAdded(Screen newScreen)
|
||||
|
@ -44,6 +44,8 @@ namespace osu.Game
|
||||
|
||||
protected KeyBindingStore KeyBindingStore;
|
||||
|
||||
protected CursorOverrideContainer CursorOverrideContainer;
|
||||
|
||||
protected override string MainResourceFile => @"osu.Game.Resources.dll";
|
||||
|
||||
public APIAccess API;
|
||||
@ -52,8 +54,6 @@ namespace osu.Game
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
protected MenuCursor Cursor;
|
||||
|
||||
public Bindable<WorkingBeatmap> Beatmap { get; private set; }
|
||||
|
||||
private Bindable<bool> fpsDisplayVisible;
|
||||
@ -211,21 +211,14 @@ namespace osu.Game
|
||||
|
||||
GlobalKeyBindingInputManager globalBinding;
|
||||
|
||||
base.Content.Add(new DrawSizePreservingFillContainer
|
||||
CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both };
|
||||
CursorOverrideContainer.Child = globalBinding = new GlobalKeyBindingInputManager(this)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Cursor = new MenuCursor(),
|
||||
globalBinding = new GlobalKeyBindingInputManager(this)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = content = new OsuTooltipContainer(Cursor)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both }
|
||||
};
|
||||
|
||||
base.Content.Add(new DrawSizePreservingFillContainer { Child = CursorOverrideContainer });
|
||||
|
||||
KeyBindingStore.Register(globalBinding);
|
||||
dependencies.Cache(globalBinding);
|
||||
|
@ -219,7 +219,6 @@ namespace osu.Game.Overlays.Chat
|
||||
}
|
||||
else
|
||||
contentFlow.Text = message.Content;
|
||||
|
||||
}
|
||||
|
||||
private class MessageSender : OsuClickableContainer, IHasContextMenu
|
||||
|
@ -65,7 +65,6 @@ namespace osu.Game.Overlays.Direct
|
||||
Colour = Color4.Black.Opacity(0.3f),
|
||||
};
|
||||
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay)
|
||||
{
|
||||
|
@ -174,10 +174,7 @@ namespace osu.Game.Overlays.Direct
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(preview))
|
||||
{
|
||||
Preview = audio.Track.Get(preview);
|
||||
Preview.Volume.Value = 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Overlays.Settings;
|
||||
|
||||
namespace osu.Game.Overlays.KeyBinding
|
||||
@ -12,19 +12,31 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_hot;
|
||||
public override string Header => "Global";
|
||||
|
||||
public GlobalKeyBindingsSection(KeyBindingContainer manager)
|
||||
public GlobalKeyBindingsSection(GlobalKeyBindingInputManager manager)
|
||||
{
|
||||
Add(new DefaultBindingsSubsection(manager));
|
||||
Add(new InGameKeyBindingsSubsection(manager));
|
||||
}
|
||||
|
||||
|
||||
private class DefaultBindingsSubsection : KeyBindingsSubsection
|
||||
{
|
||||
protected override string Header => string.Empty;
|
||||
|
||||
public DefaultBindingsSubsection(KeyBindingContainer manager)
|
||||
public DefaultBindingsSubsection(GlobalKeyBindingInputManager manager)
|
||||
: base(null)
|
||||
{
|
||||
Defaults = manager.DefaultKeyBindings;
|
||||
Defaults = manager.GlobalKeyBindings;
|
||||
}
|
||||
}
|
||||
|
||||
private class InGameKeyBindingsSubsection : KeyBindingsSubsection
|
||||
{
|
||||
protected override string Header => "In Game";
|
||||
|
||||
public InGameKeyBindingsSubsection(GlobalKeyBindingInputManager manager) : base(null)
|
||||
{
|
||||
Defaults = manager.InGameKeyBindings;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -183,8 +183,6 @@ namespace osu.Game.Overlays.MedalSplash
|
||||
description.FadeInFromZero(duration * 2);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,6 @@ namespace osu.Game.Overlays.Music
|
||||
|
||||
private class PlaylistItemHandle : SpriteIcon
|
||||
{
|
||||
|
||||
public PlaylistItemHandle()
|
||||
{
|
||||
Anchor = Anchor.TopLeft;
|
||||
|
@ -230,7 +230,6 @@ namespace osu.Game.Overlays.Music
|
||||
items.ChangeChildDepth(draggedItem, dstIndex);
|
||||
}
|
||||
|
||||
|
||||
private class ItemSearchContainer : FillFlowContainer<PlaylistItem>, IHasFilterableChildren
|
||||
{
|
||||
public IEnumerable<string> FilterTerms => new string[] { };
|
||||
|
@ -168,5 +168,4 @@ namespace osu.Game.Overlays.Notifications
|
||||
// the layout portion of this is being tracked as a framework issue (https://github.com/ppy/osu-framework/issues/1297).
|
||||
protected override bool RequiresChildrenUpdate => true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
|
||||
|
||||
namespace osu.Game.Overlays.Notifications
|
||||
{
|
||||
public class ProgressCompletionNotification : SimpleNotification
|
||||
|
@ -217,7 +217,6 @@ namespace osu.Game.Overlays.Notifications
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
|
@ -122,7 +122,7 @@ namespace osu.Game.Overlays
|
||||
trackSetting(frameworkConfig.GetBindable<string>(FrameworkSetting.AudioDevice), v => display(v, "Audio Device", string.IsNullOrEmpty(v) ? "Default" : v, v));
|
||||
trackSetting(frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowLogOverlay), v => display(v, "Debug Logs", v ? "visible" : "hidden", "Ctrl+F10"));
|
||||
|
||||
Action displayResolution = delegate { display(null, "Screen Resolution", frameworkConfig.Get<int>(FrameworkSetting.Width) + "x" + frameworkConfig.Get<int>(FrameworkSetting.Height)); };
|
||||
void displayResolution() => display(null, "Screen Resolution", frameworkConfig.Get<int>(FrameworkSetting.Width) + "x" + frameworkConfig.Get<int>(FrameworkSetting.Height));
|
||||
|
||||
trackSetting(frameworkConfig.GetBindable<int>(FrameworkSetting.Width), v => displayResolution());
|
||||
trackSetting(frameworkConfig.GetBindable<int>(FrameworkSetting.Height), v => displayResolution());
|
||||
|
@ -319,11 +319,11 @@ namespace osu.Game.Overlays.Profile
|
||||
colourBar.Show();
|
||||
}
|
||||
|
||||
Action<SpriteText> boldItalic = t =>
|
||||
void boldItalic(SpriteText t)
|
||||
{
|
||||
t.Font = @"Exo2.0-BoldItalic";
|
||||
t.Alpha = 1;
|
||||
};
|
||||
}
|
||||
|
||||
if (user.Age != null)
|
||||
{
|
||||
|
@ -22,6 +22,12 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
||||
Bindable = config.GetBindable<double>(OsuSetting.DimLevel),
|
||||
KeyboardStep = 0.1f
|
||||
},
|
||||
new SettingsSlider<double>
|
||||
{
|
||||
LabelText = "Background blur",
|
||||
Bindable = config.GetBindable<double>(OsuSetting.BlurLevel),
|
||||
KeyboardStep = 0.1f
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Show score overlay",
|
||||
|
@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Configuration;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
||||
{
|
||||
public class ScrollingSettings : SettingsSubsection
|
||||
{
|
||||
protected override string Header => "Scrolling";
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
Children = new[]
|
||||
{
|
||||
new SettingsEnumDropdown<SpeedChangeVisualisationMethod>
|
||||
{
|
||||
LabelText = "Visualise speed changes as",
|
||||
Bindable = config.GetBindable<SpeedChangeVisualisationMethod>(OsuSetting.SpeedChangeVisualisation),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
{
|
||||
new GeneralSettings(),
|
||||
new SongSelectSettings(),
|
||||
new ScrollingSettings()
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -4,19 +4,18 @@
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
public class SettingsSlider<T> : SettingsSlider<T, OsuSliderBar<T>>
|
||||
where T : struct, IEquatable<T>
|
||||
where T : struct, IEquatable<T>, IComparable, IConvertible
|
||||
{
|
||||
}
|
||||
|
||||
public class SettingsSlider<T, U> : SettingsItem<T>
|
||||
where T : struct, IEquatable<T>
|
||||
where U : SliderBar<T>, new()
|
||||
where T : struct, IEquatable<T>, IComparable, IConvertible
|
||||
where U : OsuSliderBar<T>, new()
|
||||
{
|
||||
protected override Drawable CreateControl() => new U
|
||||
{
|
||||
|
@ -22,7 +22,8 @@ namespace osu.Game.Overlays.Social
|
||||
public enum SocialSortCriteria
|
||||
{
|
||||
Rank,
|
||||
//Location,
|
||||
Name,
|
||||
Location,
|
||||
//[Description("Time Zone")]
|
||||
//TimeZone,
|
||||
//[Description("World Map")]
|
||||
|
@ -18,7 +18,8 @@ namespace osu.Game.Overlays.Social
|
||||
|
||||
protected override Color4 BackgroundColour => OsuColour.FromHex(@"38202e");
|
||||
protected override float TabStripWidth => 438;
|
||||
protected override SocialTab DefaultTab => SocialTab.OnlinePlayers;
|
||||
|
||||
protected override SocialTab DefaultTab => SocialTab.AllPlayers;
|
||||
protected override FontAwesome Icon => FontAwesome.fa_users;
|
||||
|
||||
protected override Drawable CreateHeaderText()
|
||||
@ -53,12 +54,12 @@ namespace osu.Game.Overlays.Social
|
||||
|
||||
public enum SocialTab
|
||||
{
|
||||
[Description("Online Players")]
|
||||
OnlinePlayers,
|
||||
//[Description("Online Friends")]
|
||||
//OnlineFriends,
|
||||
//[Description("Online Team Members")]
|
||||
//OnlineTeamMembers,
|
||||
[Description("All Players")]
|
||||
AllPlayers,
|
||||
[Description("Friends")]
|
||||
Friends,
|
||||
//[Description("Team Members")]
|
||||
//TeamMembers,
|
||||
//[Description("Chat Channels")]
|
||||
//ChatChannels,
|
||||
}
|
||||
|
15
osu.Game/Overlays/Social/SocialGridPanel.cs
Normal file
15
osu.Game/Overlays/Social/SocialGridPanel.cs
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Overlays.Social
|
||||
{
|
||||
public class SocialGridPanel : SocialPanel
|
||||
{
|
||||
public SocialGridPanel(User user) : base(user)
|
||||
{
|
||||
Width = 300;
|
||||
}
|
||||
}
|
||||
}
|
16
osu.Game/Overlays/Social/SocialListPanel.cs
Normal file
16
osu.Game/Overlays/Social/SocialListPanel.cs
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2007-2018 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.Users;
|
||||
|
||||
namespace osu.Game.Overlays.Social
|
||||
{
|
||||
public class SocialListPanel : SocialPanel
|
||||
{
|
||||
public SocialListPanel(User user) : base(user)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
}
|
||||
}
|
||||
}
|
60
osu.Game/Overlays/Social/SocialPanel.cs
Normal file
60
osu.Game/Overlays/Social/SocialPanel.cs
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Overlays.Social
|
||||
{
|
||||
public class SocialPanel : UserPanel
|
||||
{
|
||||
private const double hover_transition_time = 400;
|
||||
|
||||
public SocialPanel(User user) : base(user)
|
||||
{
|
||||
}
|
||||
|
||||
private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Offset = new Vector2(0f, 1f),
|
||||
Radius = 2f,
|
||||
Colour = Color4.Black.Opacity(0.25f),
|
||||
};
|
||||
|
||||
private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Offset = new Vector2(0f, 5f),
|
||||
Radius = 10f,
|
||||
Colour = Color4.Black.Opacity(0.3f),
|
||||
};
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
Content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint);
|
||||
Content.MoveToY(-4, hover_transition_time, Easing.OutQuint);
|
||||
|
||||
return base.OnHover(state);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
Content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
|
||||
Content.MoveToY(0, hover_transition_time, Easing.OutQuint);
|
||||
|
||||
base.OnHoverLost(state);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
this.FadeInFromZero(200, Easing.Out);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,19 +9,22 @@ using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.SearchableList;
|
||||
using osu.Game.Overlays.Social;
|
||||
using osu.Game.Users;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Threading;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class SocialOverlay : SearchableListOverlay<SocialTab, SocialSortCriteria, SortDirection>, IOnlineComponent
|
||||
{
|
||||
private readonly FillFlowContainer<UserPanel> panelFlow;
|
||||
private APIAccess api;
|
||||
private readonly LoadingAnimation loading;
|
||||
private FillFlowContainer<SocialPanel> panels;
|
||||
|
||||
protected override Color4 BackgroundColour => OsuColour.FromHex(@"60284b");
|
||||
protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"672b51");
|
||||
@ -31,27 +34,15 @@ namespace osu.Game.Overlays
|
||||
protected override SearchableListFilterControl<SocialSortCriteria, SortDirection> CreateFilterControl() => new FilterControl();
|
||||
|
||||
private IEnumerable<User> users;
|
||||
private readonly LoadingAnimation loading;
|
||||
|
||||
public IEnumerable<User> Users
|
||||
{
|
||||
get { return users; }
|
||||
set
|
||||
{
|
||||
if (users?.Equals(value) ?? false) return;
|
||||
users = value;
|
||||
if (users?.Equals(value) ?? false)
|
||||
return;
|
||||
|
||||
if (users == null)
|
||||
panelFlow.Clear();
|
||||
else
|
||||
{
|
||||
panelFlow.ChildrenEnumerable = users.Select(u =>
|
||||
{
|
||||
var p = new UserPanel(u) { Width = 300 };
|
||||
p.Status.BindTo(u.Status);
|
||||
return p;
|
||||
});
|
||||
}
|
||||
users = value?.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,57 +53,153 @@ namespace osu.Game.Overlays
|
||||
ThirdWaveColour = OsuColour.FromHex(@"9b2b6e");
|
||||
FourthWaveColour = OsuColour.FromHex(@"6d214d");
|
||||
|
||||
ScrollFlow.Children = new[]
|
||||
Add(loading = new LoadingAnimation());
|
||||
|
||||
Filter.Search.Current.ValueChanged += text =>
|
||||
{
|
||||
new OsuContextMenuContainer
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = panelFlow = new FillFlowContainer<UserPanel>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding { Top = 20 },
|
||||
Spacing = new Vector2(10f),
|
||||
}
|
||||
},
|
||||
// force searching in players until searching for friends is supported
|
||||
Header.Tabs.Current.Value = SocialTab.AllPlayers;
|
||||
|
||||
if (Filter.Tabs.Current.Value != SocialSortCriteria.Rank)
|
||||
Filter.Tabs.Current.Value = SocialSortCriteria.Rank;
|
||||
}
|
||||
};
|
||||
|
||||
Add(loading = new LoadingAnimation());
|
||||
Header.Tabs.Current.ValueChanged += tab => Scheduler.AddOnce(updateSearch);
|
||||
|
||||
Filter.Tabs.Current.ValueChanged += sortCriteria => Scheduler.AddOnce(updateSearch);
|
||||
|
||||
Filter.DisplayStyleControl.DisplayStyle.ValueChanged += recreatePanels;
|
||||
Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += sortOrder => Scheduler.AddOnce(updateSearch);
|
||||
|
||||
currentQuery.ValueChanged += query =>
|
||||
{
|
||||
queryChangedDebounce?.Cancel();
|
||||
|
||||
if (string.IsNullOrEmpty(query))
|
||||
Scheduler.AddOnce(updateSearch);
|
||||
else
|
||||
queryChangedDebounce = Scheduler.AddDelayed(updateSearch, 500);
|
||||
};
|
||||
|
||||
currentQuery.BindTo(Filter.Search.Current);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(APIAccess api)
|
||||
{
|
||||
if (Users == null)
|
||||
reloadUsers(api);
|
||||
this.api = api;
|
||||
api.Register(this);
|
||||
}
|
||||
|
||||
private void reloadUsers(APIAccess api)
|
||||
private void recreatePanels(PanelDisplayStyle displayStyle)
|
||||
{
|
||||
Users = null;
|
||||
clearPanels();
|
||||
|
||||
// no this is not the correct data source, but it's something.
|
||||
var request = new GetUsersRequest();
|
||||
request.Success += res =>
|
||||
if (Users == null)
|
||||
return;
|
||||
|
||||
var newPanels = new FillFlowContainer<SocialPanel>
|
||||
{
|
||||
Users = res.Select(e => e.User);
|
||||
loading.Hide();
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(10f),
|
||||
Margin = new MarginPadding { Top = 10 },
|
||||
ChildrenEnumerable = Users.Select(u =>
|
||||
{
|
||||
SocialPanel panel;
|
||||
switch (displayStyle)
|
||||
{
|
||||
case PanelDisplayStyle.Grid:
|
||||
panel = new SocialGridPanel(u)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre
|
||||
};
|
||||
break;
|
||||
default:
|
||||
panel = new SocialListPanel(u);
|
||||
break;
|
||||
}
|
||||
panel.Status.BindTo(u.Status);
|
||||
return panel;
|
||||
})
|
||||
};
|
||||
|
||||
api.Queue(request);
|
||||
LoadComponentAsync(newPanels, f =>
|
||||
{
|
||||
if(panels != null)
|
||||
ScrollFlow.Remove(panels);
|
||||
|
||||
ScrollFlow.Add(panels = newPanels);
|
||||
});
|
||||
}
|
||||
|
||||
private APIRequest getUsersRequest;
|
||||
|
||||
private readonly Bindable<string> currentQuery = new Bindable<string>();
|
||||
|
||||
private ScheduledDelegate queryChangedDebounce;
|
||||
|
||||
private void updateSearch()
|
||||
{
|
||||
queryChangedDebounce?.Cancel();
|
||||
|
||||
if (!IsLoaded)
|
||||
return;
|
||||
|
||||
Users = null;
|
||||
clearPanels();
|
||||
loading.Hide();
|
||||
getUsersRequest?.Cancel();
|
||||
|
||||
if (api?.IsLoggedIn != true)
|
||||
return;
|
||||
|
||||
switch (Header.Tabs.Current.Value)
|
||||
{
|
||||
case SocialTab.Friends:
|
||||
var friendRequest = new GetFriendsRequest(); // TODO filter arguments?
|
||||
friendRequest.Success += updateUsers;
|
||||
api.Queue(getUsersRequest = friendRequest);
|
||||
break;
|
||||
default:
|
||||
var userRequest = new GetUsersRequest(); // TODO filter arguments!
|
||||
userRequest.Success += response => updateUsers(response.Select(r => r.User));
|
||||
api.Queue(getUsersRequest = userRequest);
|
||||
break;
|
||||
}
|
||||
loading.Show();
|
||||
}
|
||||
|
||||
private void updateUsers(IEnumerable<User> newUsers)
|
||||
{
|
||||
Users = newUsers;
|
||||
loading.Hide();
|
||||
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
|
||||
}
|
||||
|
||||
private void clearPanels()
|
||||
{
|
||||
if (panels != null)
|
||||
{
|
||||
panels.Expire();
|
||||
panels = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void APIStateChanged(APIAccess api, APIState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case APIState.Online:
|
||||
reloadUsers(api);
|
||||
Scheduler.AddOnce(updateSearch);
|
||||
break;
|
||||
default:
|
||||
Users = null;
|
||||
clearPanels();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -120,7 +207,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
public enum SortDirection
|
||||
{
|
||||
Descending,
|
||||
Ascending,
|
||||
Descending
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,8 @@ namespace osu.Game.Rulesets.Judgements
|
||||
Anchor = Anchor.Centre,
|
||||
Text = judgement.Result.GetDescription().ToUpper(),
|
||||
Font = @"Venera",
|
||||
TextSize = 16
|
||||
Scale = new Vector2(0.85f, 1),
|
||||
TextSize = 12
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
/// Interface for a <see cref="Mod"/> that applies changes to a <see cref="BeatmapConverter{TObject}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TObject">The type of converted <see cref="HitObject"/>.</typeparam>
|
||||
public interface IApplicableToBeatmapConverter<TObject>
|
||||
public interface IApplicableToBeatmapConverter<TObject> : IApplicableMod
|
||||
where TObject : HitObject
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -5,30 +5,30 @@ using System;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public abstract class ModAutoplay<T> : ModAutoplay, IApplicableToRulesetContainer<T>
|
||||
public class ModAutoplay<T> : ModAutoplay, IApplicableToRulesetContainer<T>
|
||||
where T : HitObject
|
||||
{
|
||||
protected abstract Score CreateReplayScore(Beatmap<T> beatmap);
|
||||
protected virtual Score CreateReplayScore(Beatmap<T> beatmap) => new Score { Replay = new Replay() };
|
||||
|
||||
public virtual void ApplyToRulesetContainer(RulesetContainer<T> rulesetContainer)
|
||||
{
|
||||
rulesetContainer.SetReplay(CreateReplayScore(rulesetContainer.Beatmap)?.Replay);
|
||||
}
|
||||
public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0;
|
||||
|
||||
public virtual void ApplyToRulesetContainer(RulesetContainer<T> rulesetContainer) => rulesetContainer.SetReplay(CreateReplayScore(rulesetContainer.Beatmap)?.Replay);
|
||||
}
|
||||
|
||||
public class ModAutoplay : Mod, IApplicableFailOverride
|
||||
public abstract class ModAutoplay : Mod, IApplicableFailOverride
|
||||
{
|
||||
public override string Name => "Autoplay";
|
||||
public override string ShortenedName => "AT";
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_mod_auto;
|
||||
public override string Description => "Watch a perfect automated play through the song";
|
||||
public override double ScoreMultiplier => 0;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) };
|
||||
public bool AllowFail => false;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) };
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public override string Name => "Cinema";
|
||||
public override string ShortenedName => "CN";
|
||||
public override bool HasImplementation => false;
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_mod_cinema;
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
const float ratio = 1.4f;
|
||||
difficulty.CircleSize *= 1.3f; // CS uses a custom 1.3 ratio.
|
||||
difficulty.ApproachRate *= ratio;
|
||||
difficulty.ApproachRate = Math.Min(difficulty.ApproachRate * ratio, 10.0f);
|
||||
difficulty.DrainRate *= ratio;
|
||||
difficulty.OverallDifficulty *= ratio;
|
||||
}
|
||||
|
@ -29,60 +29,55 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// </summary>
|
||||
public virtual Color4 AccentColour { get; set; } = Color4.Gray;
|
||||
|
||||
// Todo: Rulesets should be overriding the resources instead, but we need to figure out where/when to apply overrides first
|
||||
protected virtual string SampleNamespace => null;
|
||||
|
||||
protected List<SampleChannel> Samples = new List<SampleChannel>();
|
||||
protected virtual IEnumerable<SampleInfo> GetSamples() => HitObject.Samples;
|
||||
|
||||
private List<DrawableHitObject> nestedHitObjects;
|
||||
public IReadOnlyList<DrawableHitObject> NestedHitObjects => nestedHitObjects;
|
||||
|
||||
public event Action<DrawableHitObject, Judgement> OnJudgement;
|
||||
public event Action<DrawableHitObject, Judgement> OnJudgementRemoved;
|
||||
|
||||
public IReadOnlyList<Judgement> Judgements => judgements;
|
||||
private readonly List<Judgement> judgements = new List<Judgement>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether a visible judgement should be displayed when this representation is hit.
|
||||
/// </summary>
|
||||
public virtual bool DisplayJudgement => true;
|
||||
|
||||
public override bool RemoveCompletedTransforms => false;
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
|
||||
protected DrawableHitObject(HitObject hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
}
|
||||
/// <summary>
|
||||
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been hit.
|
||||
/// </summary>
|
||||
public bool IsHit => Judgements.Any(j => j.Final && j.IsHit) && (NestedHitObjects?.All(n => n.IsHit) ?? true);
|
||||
|
||||
/// <summary>
|
||||
/// The screen-space point that causes this <see cref="DrawableHitObject"/> to be selected in the Editor.
|
||||
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been judged.
|
||||
/// </summary>
|
||||
public virtual Vector2 SelectionPoint => ScreenSpaceDrawQuad.Centre;
|
||||
|
||||
/// <summary>
|
||||
/// The screen-space quad that outlines this <see cref="DrawableHitObject"/> for selections in the Editor.
|
||||
/// </summary>
|
||||
public virtual Quad SelectionQuad => ScreenSpaceDrawQuad;
|
||||
}
|
||||
|
||||
public abstract class DrawableHitObject<TObject> : DrawableHitObject
|
||||
where TObject : HitObject
|
||||
{
|
||||
public event Action<DrawableHitObject, Judgement> OnJudgement;
|
||||
public event Action<DrawableHitObject, Judgement> OnJudgementRemoved;
|
||||
|
||||
public new readonly TObject HitObject;
|
||||
|
||||
public override bool HandleKeyboardInput => Interactive;
|
||||
public override bool HandleMouseInput => Interactive;
|
||||
public bool Interactive = true;
|
||||
public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (NestedHitObjects?.All(h => h.AllJudged) ?? true);
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="DrawableHitObject"/> can be judged.
|
||||
/// </summary>
|
||||
protected virtual bool ProvidesJudgement => true;
|
||||
|
||||
private readonly List<Judgement> judgements = new List<Judgement>();
|
||||
public IReadOnlyList<Judgement> Judgements => judgements;
|
||||
private bool judgementOccurred;
|
||||
private bool judgementFinalized => judgements.LastOrDefault()?.Final == true;
|
||||
|
||||
protected List<SampleChannel> Samples = new List<SampleChannel>();
|
||||
protected virtual IEnumerable<SampleInfo> GetSamples() => HitObject.Samples;
|
||||
public bool Interactive = true;
|
||||
public override bool HandleKeyboardInput => Interactive;
|
||||
public override bool HandleMouseInput => Interactive;
|
||||
|
||||
// Todo: Rulesets should be overriding the resources instead, but we need to figure out where/when to apply overrides first
|
||||
protected virtual string SampleNamespace => null;
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
public override bool RemoveCompletedTransforms => false;
|
||||
protected override bool RequiresChildrenUpdate => true;
|
||||
|
||||
public readonly Bindable<ArmedState> State = new Bindable<ArmedState>();
|
||||
|
||||
protected DrawableHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
protected DrawableHitObject(HitObject hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
}
|
||||
@ -134,18 +129,52 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
State.TriggerChange();
|
||||
}
|
||||
|
||||
protected void PlaySamples()
|
||||
{
|
||||
Samples.ForEach(s => s?.Play());
|
||||
}
|
||||
|
||||
private bool judgementOccurred;
|
||||
private bool judgementFinalized => judgements.LastOrDefault()?.Final == true;
|
||||
protected abstract void UpdateState(ArmedState state);
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been judged.
|
||||
/// Bind to apply a custom state which can override the default implementation.
|
||||
/// </summary>
|
||||
public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (NestedHitObjects?.All(h => h.AllJudged) ?? true);
|
||||
public event Action<DrawableHitObject, ArmedState> ApplyCustomUpdateState;
|
||||
|
||||
protected void PlaySamples() => Samples.ForEach(s => s?.Play());
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
|
||||
|
||||
while (judgements.Count > 0)
|
||||
{
|
||||
var lastJudgement = judgements[judgements.Count - 1];
|
||||
if (lastJudgement.TimeOffset + endTime <= Time.Current)
|
||||
break;
|
||||
|
||||
judgements.RemoveAt(judgements.Count - 1);
|
||||
State.Value = ArmedState.Idle;
|
||||
|
||||
OnJudgementRemoved?.Invoke(this, lastJudgement);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
UpdateJudgement(false);
|
||||
}
|
||||
|
||||
protected virtual void AddNested(DrawableHitObject h)
|
||||
{
|
||||
if (nestedHitObjects == null)
|
||||
nestedHitObjects = new List<DrawableHitObject>();
|
||||
|
||||
h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j);
|
||||
h.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(d, j);
|
||||
h.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j);
|
||||
|
||||
nestedHitObjects.Add(h);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies that a new judgement has occurred for this <see cref="DrawableHitObject"/>.
|
||||
@ -210,53 +239,30 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// <param name="userTriggered">Whether the user triggered this check.</param>
|
||||
/// <param name="timeOffset">The offset from the <see cref="HitObject"/> end time at which this check occurred. A <paramref name="timeOffset"/> > 0
|
||||
/// implies that this check occurred after the end time of <see cref="HitObject"/>. </param>
|
||||
protected virtual void CheckForJudgements(bool userTriggered, double timeOffset) { }
|
||||
|
||||
protected override void Update()
|
||||
protected virtual void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
{
|
||||
base.Update();
|
||||
|
||||
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
|
||||
|
||||
while (judgements.Count > 0)
|
||||
{
|
||||
var lastJudgement = judgements[judgements.Count - 1];
|
||||
if (lastJudgement.TimeOffset + endTime <= Time.Current)
|
||||
break;
|
||||
|
||||
judgements.RemoveAt(judgements.Count - 1);
|
||||
State.Value = ArmedState.Idle;
|
||||
|
||||
OnJudgementRemoved?.Invoke(this, lastJudgement);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
UpdateJudgement(false);
|
||||
}
|
||||
|
||||
private List<DrawableHitObject<TObject>> nestedHitObjects;
|
||||
protected IEnumerable<DrawableHitObject<TObject>> NestedHitObjects => nestedHitObjects;
|
||||
|
||||
protected virtual void AddNested(DrawableHitObject<TObject> h)
|
||||
{
|
||||
if (nestedHitObjects == null)
|
||||
nestedHitObjects = new List<DrawableHitObject<TObject>>();
|
||||
|
||||
h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j);
|
||||
h.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(d, j);
|
||||
h.ApplyCustomUpdateState += (d, s) => ApplyCustomUpdateState?.Invoke(d, s);
|
||||
nestedHitObjects.Add(h);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind to apply a custom state which can override the default implementation.
|
||||
/// The screen-space point that causes this <see cref="DrawableHitObject"/> to be selected in the Editor.
|
||||
/// </summary>
|
||||
public event Action<DrawableHitObject, ArmedState> ApplyCustomUpdateState;
|
||||
public virtual Vector2 SelectionPoint => ScreenSpaceDrawQuad.Centre;
|
||||
|
||||
protected abstract void UpdateState(ArmedState state);
|
||||
/// <summary>
|
||||
/// The screen-space quad that outlines this <see cref="DrawableHitObject"/> for selections in the Editor.
|
||||
/// </summary>
|
||||
public virtual Quad SelectionQuad => ScreenSpaceDrawQuad;
|
||||
}
|
||||
|
||||
public abstract class DrawableHitObject<TObject> : DrawableHitObject
|
||||
where TObject : HitObject
|
||||
{
|
||||
public new readonly TObject HitObject;
|
||||
|
||||
protected DrawableHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,67 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Drawables
|
||||
{
|
||||
/// <summary>
|
||||
/// A basic class that overrides <see cref="DrawableHitObject{TObject}"/> and implements <see cref="IScrollingHitObject"/>.
|
||||
/// This object does not need to have its <see cref="Drawable.RelativePositionAxes"/> set to be able to scroll, as this will
|
||||
/// will be set by the scrolling container that contains it.
|
||||
/// </summary>
|
||||
public abstract class DrawableScrollingHitObject<TObject> : DrawableHitObject<TObject>, IScrollingHitObject
|
||||
where TObject : HitObject
|
||||
{
|
||||
public BindableDouble LifetimeOffset { get; } = new BindableDouble();
|
||||
|
||||
Axes IScrollingHitObject.ScrollingAxes
|
||||
{
|
||||
set
|
||||
{
|
||||
RelativePositionAxes |= value;
|
||||
|
||||
if ((value & Axes.X) > 0)
|
||||
X = (float)HitObject.StartTime;
|
||||
if ((value & Axes.Y) > 0)
|
||||
Y = (float)HitObject.StartTime;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
protected override bool RequiresChildrenUpdate => true;
|
||||
|
||||
protected DrawableScrollingHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
}
|
||||
|
||||
private double? lifetimeStart;
|
||||
public override double LifetimeStart
|
||||
{
|
||||
get { return lifetimeStart ?? HitObject.StartTime - LifetimeOffset; }
|
||||
set { lifetimeStart = value; }
|
||||
}
|
||||
|
||||
private double? lifetimeEnd;
|
||||
public override double LifetimeEnd
|
||||
{
|
||||
get
|
||||
{
|
||||
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
|
||||
return lifetimeEnd ?? endTime + LifetimeOffset;
|
||||
}
|
||||
set { lifetimeEnd = value; }
|
||||
}
|
||||
|
||||
protected override void AddNested(DrawableHitObject<TObject> h)
|
||||
{
|
||||
var scrollingHitObject = h as IScrollingHitObject;
|
||||
scrollingHitObject?.LifetimeOffset.BindTo(LifetimeOffset);
|
||||
|
||||
base.AddNested(h);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Types
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Types
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -52,7 +52,6 @@ namespace osu.Game.Rulesets.Replays
|
||||
|
||||
protected ReplayFrame()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ReplayFrame(double time, float? mouseX, float? mouseY, ReplayButtonState buttonState)
|
||||
|
@ -34,9 +34,9 @@ namespace osu.Game.Rulesets
|
||||
|
||||
public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay);
|
||||
|
||||
protected Ruleset(RulesetInfo rulesetInfo)
|
||||
protected Ruleset(RulesetInfo rulesetInfo = null)
|
||||
{
|
||||
RulesetInfo = rulesetInfo;
|
||||
RulesetInfo = rulesetInfo ?? createRulesetInfo();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -88,5 +88,17 @@ namespace osu.Game.Rulesets
|
||||
/// <param name="variant">The variant.</param>
|
||||
/// <returns>A descriptive name of the variant.</returns>
|
||||
public virtual string GetVariantName(int variant) => string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Create a ruleset info based on this ruleset.
|
||||
/// </summary>
|
||||
/// <returns>A filled <see cref="RulesetInfo"/>.</returns>
|
||||
private RulesetInfo createRulesetInfo() => new RulesetInfo
|
||||
{
|
||||
Name = Description,
|
||||
ShortName = ShortName,
|
||||
InstantiationInfo = GetType().AssemblyQualifiedName,
|
||||
ID = LegacyID
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -58,30 +58,21 @@ namespace osu.Game.Rulesets
|
||||
{
|
||||
var context = GetContext();
|
||||
|
||||
var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, new RulesetInfo())).ToList();
|
||||
var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, (RulesetInfo)null)).ToList();
|
||||
|
||||
//add all legacy modes in correct order
|
||||
foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID))
|
||||
{
|
||||
var rulesetInfo = createRulesetInfo(r);
|
||||
if (context.RulesetInfo.SingleOrDefault(rsi => rsi.ID == rulesetInfo.ID) == null)
|
||||
{
|
||||
context.RulesetInfo.Add(rulesetInfo);
|
||||
}
|
||||
if (context.RulesetInfo.SingleOrDefault(rsi => rsi.ID == r.RulesetInfo.ID) == null)
|
||||
context.RulesetInfo.Add(r.RulesetInfo);
|
||||
}
|
||||
|
||||
context.SaveChanges();
|
||||
|
||||
//add any other modes
|
||||
foreach (var r in instances.Where(r => r.LegacyID < 0))
|
||||
{
|
||||
var us = createRulesetInfo(r);
|
||||
|
||||
var existing = context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == us.InstantiationInfo);
|
||||
|
||||
if (existing == null)
|
||||
context.RulesetInfo.Add(us);
|
||||
}
|
||||
if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == r.RulesetInfo.InstantiationInfo) == null)
|
||||
context.RulesetInfo.Add(r.RulesetInfo);
|
||||
|
||||
context.SaveChanges();
|
||||
|
||||
@ -124,13 +115,5 @@ namespace osu.Game.Rulesets
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private RulesetInfo createRulesetInfo(Ruleset ruleset) => new RulesetInfo
|
||||
{
|
||||
Name = ruleset.Description,
|
||||
ShortName = ruleset.ShortName,
|
||||
InstantiationInfo = ruleset.GetType().AssemblyQualifiedName,
|
||||
ID = ruleset.LegacyID
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="ScrollingContainer"/> which scrolls linearly relative to the <see cref="MultiplierControlPoint"/> start time.
|
||||
/// </summary>
|
||||
public class LinearScrollingContainer : ScrollingContainer
|
||||
{
|
||||
private readonly MultiplierControlPoint controlPoint;
|
||||
|
||||
public LinearScrollingContainer(MultiplierControlPoint controlPoint)
|
||||
{
|
||||
this.controlPoint = controlPoint;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if ((ScrollingAxes & Axes.X) > 0) X = (float)(controlPoint.StartTime - Time.Current);
|
||||
if ((ScrollingAxes & Axes.Y) > 0) Y = (float)(controlPoint.StartTime - Time.Current);
|
||||
}
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Timing
|
||||
/// <summary>
|
||||
/// The time in milliseconds at which this <see cref="MultiplierControlPoint"/> starts.
|
||||
/// </summary>
|
||||
public readonly double StartTime;
|
||||
public double StartTime;
|
||||
|
||||
/// <summary>
|
||||
/// The multiplier which this <see cref="MultiplierControlPoint"/> provides.
|
||||
|
@ -1,93 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// A container that scrolls relative to the current time. Will autosize to the total duration of all contained hit objects along the scrolling axes.
|
||||
/// </summary>
|
||||
public abstract class ScrollingContainer : Container<DrawableHitObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the range of time that is visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
public readonly BindableDouble VisibleTimeRange = new BindableDouble { Default = 1000 };
|
||||
|
||||
/// <summary>
|
||||
/// The axes through which this <see cref="ScrollingContainer"/> scrolls. This is set by the <see cref="SpeedAdjustmentContainer"/>.
|
||||
/// </summary>
|
||||
internal Axes ScrollingAxes;
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
protected override bool RequiresChildrenUpdate => true;
|
||||
|
||||
/// <summary>
|
||||
/// The control point that defines the speed adjustments for this container. This is set by the <see cref="SpeedAdjustmentContainer"/>.
|
||||
/// </summary>
|
||||
internal MultiplierControlPoint ControlPoint;
|
||||
|
||||
private Cached<double> durationBacking;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ScrollingContainer"/>.
|
||||
/// </summary>
|
||||
protected ScrollingContainer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
RelativePositionAxes = Axes.Both;
|
||||
}
|
||||
|
||||
protected override int Compare(Drawable x, Drawable y)
|
||||
{
|
||||
var hX = (DrawableHitObject)x;
|
||||
var hY = (DrawableHitObject)y;
|
||||
|
||||
int result = hY.HitObject.StartTime.CompareTo(hX.HitObject.StartTime);
|
||||
if (result != 0)
|
||||
return result;
|
||||
return base.Compare(y, x);
|
||||
}
|
||||
|
||||
public override void Add(DrawableHitObject drawable)
|
||||
{
|
||||
durationBacking.Invalidate();
|
||||
base.Add(drawable);
|
||||
}
|
||||
|
||||
public override bool Remove(DrawableHitObject drawable)
|
||||
{
|
||||
durationBacking.Invalidate();
|
||||
return base.Remove(drawable);
|
||||
}
|
||||
|
||||
// Todo: This may underestimate the size of the hit object in some cases, but won't be too much of a problem for now
|
||||
private double computeDuration() => Math.Max(0, Children.Select(c => (c.HitObject as IHasEndTime)?.EndTime ?? c.HitObject.StartTime).DefaultIfEmpty().Max() - ControlPoint.StartTime) + 1000;
|
||||
|
||||
/// <summary>
|
||||
/// An approximate total duration of this scrolling container.
|
||||
/// </summary>
|
||||
public double Duration => durationBacking.IsValid ? durationBacking : (durationBacking.Value = computeDuration());
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
RelativeChildOffset = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)ControlPoint.StartTime : 0, (ScrollingAxes & Axes.Y) > 0 ? (float)ControlPoint.StartTime : 0);
|
||||
|
||||
// We want our size and position-space along the scrolling axes to span our duration to completely enclose all the hit objects
|
||||
Size = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)Duration : Size.X, (ScrollingAxes & Axes.Y) > 0 ? (float)Duration : Size.Y);
|
||||
// And we need to make sure the hit object's position-space doesn't change due to our resizing
|
||||
RelativeChildSize = Size;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// A container that provides the speed adjustments defined by <see cref="MultiplierControlPoint"/>s to affect the scroll speed
|
||||
/// of container <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
public class SpeedAdjustmentContainer : Container<DrawableHitObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the range of time that is visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
public readonly Bindable<double> VisibleTimeRange = new Bindable<double> { Default = 1000 };
|
||||
|
||||
/// <summary>
|
||||
/// Whether to reverse the scrolling direction is reversed.
|
||||
/// </summary>
|
||||
public readonly BindableBool Reversed = new BindableBool();
|
||||
|
||||
protected override Container<DrawableHitObject> Content => content;
|
||||
private readonly Container<DrawableHitObject> content;
|
||||
|
||||
/// <summary>
|
||||
/// The axes which the content of this container will scroll through.
|
||||
/// </summary>
|
||||
public Axes ScrollingAxes
|
||||
{
|
||||
get { return scrollingContainer.ScrollingAxes; }
|
||||
set { scrollingContainer.ScrollingAxes = value; }
|
||||
}
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
protected override bool RequiresChildrenUpdate => true;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MultiplierControlPoint"/> that defines the speed adjustments.
|
||||
/// </summary>
|
||||
public readonly MultiplierControlPoint ControlPoint;
|
||||
|
||||
private readonly ScrollingContainer scrollingContainer;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="SpeedAdjustmentContainer"/>.
|
||||
/// </summary>
|
||||
/// <param name="controlPoint">The <see cref="MultiplierControlPoint"/> that defines the speed adjustments.</param>
|
||||
public SpeedAdjustmentContainer(MultiplierControlPoint controlPoint)
|
||||
{
|
||||
ControlPoint = controlPoint;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
scrollingContainer = CreateScrollingContainer();
|
||||
scrollingContainer.ControlPoint = ControlPoint;
|
||||
scrollingContainer.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||
|
||||
AddInternal(content = scrollingContainer);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
float multiplier = (float)ControlPoint.Multiplier;
|
||||
|
||||
// The speed adjustment happens by modifying our size by the multiplier while maintaining the visible time range as the relatve size for our children
|
||||
Size = new Vector2((ScrollingAxes & Axes.X) > 0 ? multiplier : 1, (ScrollingAxes & Axes.Y) > 0 ? multiplier : 1);
|
||||
|
||||
if (Reversed)
|
||||
{
|
||||
RelativeChildSize = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)-VisibleTimeRange : 1, (ScrollingAxes & Axes.Y) > 0 ? (float)-VisibleTimeRange : 1);
|
||||
RelativeChildOffset = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)VisibleTimeRange : 0, (ScrollingAxes & Axes.Y) > 0 ? (float)VisibleTimeRange : 0);
|
||||
Origin = Anchor = Anchor.BottomRight;
|
||||
}
|
||||
else
|
||||
{
|
||||
RelativeChildSize = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)VisibleTimeRange : 1, (ScrollingAxes & Axes.Y) > 0 ? (float)VisibleTimeRange : 1);
|
||||
RelativeChildOffset = Vector2.Zero;
|
||||
Origin = Anchor = Anchor.TopLeft;
|
||||
}
|
||||
}
|
||||
|
||||
public override double LifetimeStart => ControlPoint.StartTime - VisibleTimeRange;
|
||||
public override double LifetimeEnd => ControlPoint.StartTime + scrollingContainer.Duration + VisibleTimeRange;
|
||||
|
||||
public override void Add(DrawableHitObject drawable)
|
||||
{
|
||||
var scrollingHitObject = drawable as IScrollingHitObject;
|
||||
|
||||
if (scrollingHitObject != null)
|
||||
{
|
||||
scrollingHitObject.LifetimeOffset.BindTo(VisibleTimeRange);
|
||||
scrollingHitObject.ScrollingAxes = ScrollingAxes;
|
||||
}
|
||||
|
||||
base.Add(drawable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether a point in time falls within this <see cref="SpeedAdjustmentContainer"/>s affecting timespan.
|
||||
/// </summary>
|
||||
public bool CanContain(double startTime) => ControlPoint.StartTime <= startTime;
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="ScrollingContainer"/> which contains the scrolling <see cref="DrawableHitObject"/>s of this container.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="ScrollingContainer"/>.</returns>
|
||||
protected virtual ScrollingContainer CreateScrollingContainer() => new LinearScrollingContainer(ControlPoint);
|
||||
}
|
||||
}
|
19
osu.Game/Rulesets/UI/HitObjectContainer.cs
Normal file
19
osu.Game/Rulesets/UI/HitObjectContainer.cs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
public class HitObjectContainer : CompositeDrawable
|
||||
{
|
||||
public virtual IEnumerable<DrawableHitObject> Objects => InternalChildren.Cast<DrawableHitObject>();
|
||||
public virtual IEnumerable<DrawableHitObject> AliveObjects => AliveInternalChildren.Cast<DrawableHitObject>();
|
||||
|
||||
public virtual void Add(DrawableHitObject hitObject) => AddInternal(hitObject);
|
||||
public virtual bool Remove(DrawableHitObject hitObject) => RemoveInternal(hitObject);
|
||||
}
|
||||
}
|
@ -2,14 +2,12 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Framework.Allocation;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
@ -18,18 +16,20 @@ namespace osu.Game.Rulesets.UI
|
||||
/// <summary>
|
||||
/// The HitObjects contained in this Playfield.
|
||||
/// </summary>
|
||||
public HitObjectContainer HitObjects { get; protected set; }
|
||||
public HitObjectContainer HitObjects { get; private set; }
|
||||
|
||||
public Container<Drawable> ScaledContent;
|
||||
|
||||
/// <summary>
|
||||
/// Whether we are currently providing the local user a gameplay cursor.
|
||||
/// </summary>
|
||||
public virtual bool ProvidingUserCursor => false;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container<Drawable> content;
|
||||
|
||||
private List<Playfield> nestedPlayfields;
|
||||
|
||||
/// <summary>
|
||||
/// All the <see cref="Playfield"/>s nested inside this playfield.
|
||||
/// </summary>
|
||||
public IReadOnlyList<Playfield> NestedPlayfields => nestedPlayfields;
|
||||
|
||||
/// <summary>
|
||||
/// A container for keeping track of DrawableHitObjects.
|
||||
/// </summary>
|
||||
@ -51,16 +51,14 @@ namespace osu.Game.Rulesets.UI
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
HitObjects = new HitObjectContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
HitObjects = CreateHitObjectContainer();
|
||||
HitObjects.RelativeSizeAxes = Axes.Both;
|
||||
|
||||
Add(HitObjects);
|
||||
}
|
||||
|
||||
@ -73,7 +71,7 @@ namespace osu.Game.Rulesets.UI
|
||||
/// <summary>
|
||||
/// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield.
|
||||
/// </summary>
|
||||
public virtual void PostProcess() { }
|
||||
public virtual void PostProcess() => nestedPlayfields?.ForEach(p => p.PostProcess());
|
||||
|
||||
/// <summary>
|
||||
/// Adds a DrawableHitObject to this Playfield.
|
||||
@ -88,19 +86,23 @@ namespace osu.Game.Rulesets.UI
|
||||
public virtual void Remove(DrawableHitObject h) => HitObjects.Remove(h);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a new <see cref="Judgement"/> occurs on a <see cref="DrawableHitObject"/>.
|
||||
/// Registers a <see cref="Playfield"/> as a nested <see cref="Playfield"/>.
|
||||
/// This does not add the <see cref="Playfield"/> to the draw hierarchy.
|
||||
/// </summary>
|
||||
/// <param name="judgedObject">The object that <paramref name="judgement"/> occured for.</param>
|
||||
/// <param name="judgement">The <see cref="Judgement"/> that occurred.</param>
|
||||
public virtual void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { }
|
||||
|
||||
public class HitObjectContainer : CompositeDrawable
|
||||
/// <param name="otherPlayfield">The <see cref="Playfield"/> to add.</param>
|
||||
protected void AddNested(Playfield otherPlayfield)
|
||||
{
|
||||
public virtual IEnumerable<DrawableHitObject> Objects => InternalChildren.OfType<DrawableHitObject>();
|
||||
public virtual void Add(DrawableHitObject hitObject) => AddInternal(hitObject);
|
||||
public virtual bool Remove(DrawableHitObject hitObject) => RemoveInternal(hitObject);
|
||||
if (nestedPlayfields == null)
|
||||
nestedPlayfields = new List<Playfield>();
|
||||
|
||||
nestedPlayfields.Add(otherPlayfield);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the container that will be used to contain the <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer();
|
||||
|
||||
private class ScaledContainer : Container
|
||||
{
|
||||
/// <summary>
|
||||
@ -110,6 +112,12 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
//dividing by the customwidth will effectively scale our content to the required container size.
|
||||
protected override Vector2 DrawScale => CustomWidth.HasValue ? new Vector2(DrawSize.X / CustomWidth.Value) : base.DrawScale;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
RelativeChildSize = new Vector2(DrawScale.X, RelativeChildSize.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@ -43,11 +44,6 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
public PassThroughInputManager KeyBindingInputManager;
|
||||
|
||||
/// <summary>
|
||||
/// Whether we are currently providing the local user a gameplay cursor.
|
||||
/// </summary>
|
||||
public virtual bool ProvidingUserCursor => false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether we have a replay loaded currently.
|
||||
/// </summary>
|
||||
@ -61,6 +57,11 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
public Playfield Playfield => playfield.Value;
|
||||
|
||||
/// <summary>
|
||||
/// The cursor provided by this <see cref="RulesetContainer"/>. May be null if no cursor is provided.
|
||||
/// </summary>
|
||||
public readonly CursorContainer Cursor;
|
||||
|
||||
protected readonly Ruleset Ruleset;
|
||||
|
||||
/// <summary>
|
||||
@ -71,6 +72,8 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
Ruleset = ruleset;
|
||||
playfield = new Lazy<Playfield>(CreatePlayfield);
|
||||
|
||||
Cursor = CreateCursor();
|
||||
}
|
||||
|
||||
public abstract ScoreProcessor CreateScoreProcessor();
|
||||
@ -98,6 +101,12 @@ namespace osu.Game.Rulesets.UI
|
||||
ReplayInputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates the cursor. May be null if the <see cref="RulesetContainer"/> doesn't provide a custom cursor.
|
||||
/// </summary>
|
||||
protected virtual CursorContainer CreateCursor() => null;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Playfield.
|
||||
/// </summary>
|
||||
@ -144,8 +153,6 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
protected readonly bool IsForCurrentRuleset;
|
||||
|
||||
public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor;
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor<TObject>(this);
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
@ -212,6 +219,9 @@ namespace osu.Game.Rulesets.UI
|
||||
AddInternal(KeyBindingInputManager);
|
||||
KeyBindingInputManager.Add(Playfield);
|
||||
|
||||
if (Cursor != null)
|
||||
KeyBindingInputManager.Add(Cursor);
|
||||
|
||||
loadObjects();
|
||||
}
|
||||
|
||||
@ -252,12 +262,7 @@ namespace osu.Game.Rulesets.UI
|
||||
if (drawableObject == null)
|
||||
continue;
|
||||
|
||||
drawableObject.OnJudgement += (d, j) =>
|
||||
{
|
||||
Playfield.OnJudgement(d, j);
|
||||
OnJudgement?.Invoke(j);
|
||||
};
|
||||
|
||||
drawableObject.OnJudgement += (d, j) => OnJudgement?.Invoke(j);
|
||||
drawableObject.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(j);
|
||||
|
||||
Playfield.Add(drawableObject);
|
||||
|
25
osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs
Normal file
25
osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
public enum ScrollingDirection
|
||||
{
|
||||
/// <summary>
|
||||
/// Hitobjects will scroll vertically from the bottom of the hitobject container.
|
||||
/// </summary>
|
||||
Up,
|
||||
/// <summary>
|
||||
/// Hitobjects will scroll vertically from the top of the hitobject container.
|
||||
/// </summary>
|
||||
Down,
|
||||
/// <summary>
|
||||
/// Hitobjects will scroll horizontally from the right of the hitobject container.
|
||||
/// </summary>
|
||||
Left,
|
||||
/// <summary>
|
||||
/// Hitobjects will scroll horizontally from the left of the hitobject container.
|
||||
/// </summary>
|
||||
Right
|
||||
}
|
||||
}
|
116
osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs
Normal file
116
osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using osu.Game.Rulesets.UI.Scrolling.Visualisers;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
public class ScrollingHitObjectContainer : HitObjectContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// The duration required to scroll through one length of the <see cref="ScrollingHitObjectContainer"/> before any control point adjustments.
|
||||
/// </summary>
|
||||
public readonly BindableDouble TimeRange = new BindableDouble
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = double.MaxValue
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The control points that adjust the scrolling speed.
|
||||
/// </summary>
|
||||
protected readonly SortedList<MultiplierControlPoint> ControlPoints = new SortedList<MultiplierControlPoint>();
|
||||
|
||||
private readonly ScrollingDirection direction;
|
||||
|
||||
private Cached initialStateCache = new Cached();
|
||||
|
||||
public ScrollingHitObjectContainer(ScrollingDirection direction)
|
||||
{
|
||||
this.direction = direction;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
TimeRange.ValueChanged += v => initialStateCache.Invalidate();
|
||||
}
|
||||
|
||||
private ISpeedChangeVisualiser speedChangeVisualiser;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
switch (config.Get<SpeedChangeVisualisationMethod>(OsuSetting.SpeedChangeVisualisation))
|
||||
{
|
||||
case SpeedChangeVisualisationMethod.Sequential:
|
||||
speedChangeVisualiser = new SequentialSpeedChangeVisualiser(ControlPoints);
|
||||
break;
|
||||
case SpeedChangeVisualisationMethod.Overlapping:
|
||||
speedChangeVisualiser = new OverlappingSpeedChangeVisualiser(ControlPoints);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Add(DrawableHitObject hitObject)
|
||||
{
|
||||
initialStateCache.Invalidate();
|
||||
base.Add(hitObject);
|
||||
}
|
||||
|
||||
public override bool Remove(DrawableHitObject hitObject)
|
||||
{
|
||||
var result = base.Remove(hitObject);
|
||||
if (result)
|
||||
initialStateCache.Invalidate();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void AddControlPoint(MultiplierControlPoint controlPoint)
|
||||
{
|
||||
ControlPoints.Add(controlPoint);
|
||||
initialStateCache.Invalidate();
|
||||
}
|
||||
|
||||
public bool RemoveControlPoint(MultiplierControlPoint controlPoint)
|
||||
{
|
||||
var result = ControlPoints.Remove(controlPoint);
|
||||
if (result)
|
||||
initialStateCache.Invalidate();
|
||||
return result;
|
||||
}
|
||||
|
||||
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
|
||||
{
|
||||
if ((invalidation & (Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo)) > 0)
|
||||
initialStateCache.Invalidate();
|
||||
|
||||
return base.Invalidate(invalidation, source, shallPropagate);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!initialStateCache.IsValid)
|
||||
{
|
||||
speedChangeVisualiser.ComputeInitialStates(Objects, direction, TimeRange, DrawSize);
|
||||
initialStateCache.Validate();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildrenLife()
|
||||
{
|
||||
base.UpdateAfterChildrenLife();
|
||||
|
||||
// We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions
|
||||
speedChangeVisualiser.ComputePositions(AliveObjects, direction, Time.Current, TimeRange, DrawSize);
|
||||
}
|
||||
}
|
||||
}
|
122
osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs
Normal file
122
osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of <see cref="Playfield"/> specialized towards scrolling <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
public abstract class ScrollingPlayfield : Playfield
|
||||
{
|
||||
/// <summary>
|
||||
/// The default span of time visible by the length of the scrolling axes.
|
||||
/// This is clamped between <see cref="time_span_min"/> and <see cref="time_span_max"/>.
|
||||
/// </summary>
|
||||
private const double time_span_default = 1500;
|
||||
/// <summary>
|
||||
/// The minimum span of time that may be visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
private const double time_span_min = 50;
|
||||
/// <summary>
|
||||
/// The maximum span of time that may be visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
private const double time_span_max = 10000;
|
||||
/// <summary>
|
||||
/// The step increase/decrease of the span of time visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
private const double time_span_step = 50;
|
||||
|
||||
/// <summary>
|
||||
/// The span of time that is visible by the length of the scrolling axes.
|
||||
/// For example, only hit objects with start time less than or equal to 1000 will be visible with <see cref="VisibleTimeRange"/> = 1000.
|
||||
/// </summary>
|
||||
public readonly BindableDouble VisibleTimeRange = new BindableDouble(time_span_default)
|
||||
{
|
||||
Default = time_span_default,
|
||||
MinValue = time_span_min,
|
||||
MaxValue = time_span_max
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Whether the player can change <see cref="VisibleTimeRange"/>.
|
||||
/// </summary>
|
||||
protected virtual bool UserScrollSpeedAdjustment => true;
|
||||
|
||||
/// <summary>
|
||||
/// The container that contains the <see cref="SpeedAdjustmentContainer"/>s and <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
public new ScrollingHitObjectContainer HitObjects => (ScrollingHitObjectContainer)base.HitObjects;
|
||||
|
||||
private readonly ScrollingDirection direction;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ScrollingPlayfield"/>.
|
||||
/// </summary>
|
||||
/// <param name="scrollingAxes">The axes on which <see cref="DrawableHitObject"/>s in this container should scroll.</param>
|
||||
/// <param name="customWidth">Whether we want our internal coordinate system to be scaled to a specified width</param>
|
||||
protected ScrollingPlayfield(ScrollingDirection direction, float? customWidth = null)
|
||||
: base(customWidth)
|
||||
{
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
HitObjects.TimeRange.BindTo(VisibleTimeRange);
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
if (!UserScrollSpeedAdjustment)
|
||||
return false;
|
||||
|
||||
if (state.Keyboard.ControlPressed)
|
||||
{
|
||||
switch (args.Key)
|
||||
{
|
||||
case Key.Minus:
|
||||
transformVisibleTimeRangeTo(VisibleTimeRange + time_span_step, 200, Easing.OutQuint);
|
||||
break;
|
||||
case Key.Plus:
|
||||
transformVisibleTimeRangeTo(VisibleTimeRange - time_span_step, 200, Easing.OutQuint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void transformVisibleTimeRangeTo(double newTimeRange, double duration = 0, Easing easing = Easing.None)
|
||||
{
|
||||
this.TransformTo(this.PopulateTransform(new TransformVisibleTimeRange(), newTimeRange, duration, easing));
|
||||
}
|
||||
|
||||
protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer(direction);
|
||||
|
||||
private class TransformVisibleTimeRange : Transform<double, ScrollingPlayfield>
|
||||
{
|
||||
private double valueAt(double time)
|
||||
{
|
||||
if (time < StartTime) return StartValue;
|
||||
if (time >= EndTime) return EndValue;
|
||||
|
||||
return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
|
||||
}
|
||||
|
||||
public override string TargetMember => "VisibleTimeRange.Value";
|
||||
|
||||
protected override void Apply(ScrollingPlayfield d, double time) => d.VisibleTimeRange.Value = valueAt(time);
|
||||
protected override void ReadIntoStartValue(ScrollingPlayfield d) => StartValue = d.VisibleTimeRange.Value;
|
||||
}
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Game.Rulesets.UI
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of <see cref="RulesetContainer{TPlayfield,TObject}"/> that supports a <see cref="ScrollingPlayfield"/>.
|
||||
@ -70,12 +70,10 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
// Perform some post processing of the timing changes
|
||||
timingChanges = timingChanges
|
||||
// Collapse sections after the last hit object
|
||||
.Where(s => s.StartTime <= lastObjectTime)
|
||||
// Collapse sections with the same start time
|
||||
.GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime)
|
||||
// Collapse sections with the same beat length
|
||||
.GroupBy(s => s.TimingPoint.BeatLength * s.DifficultyPoint.SpeedMultiplier).Select(g => g.First());
|
||||
// Collapse sections after the last hit object
|
||||
.Where(s => s.StartTime <= lastObjectTime)
|
||||
// Collapse sections with the same start time
|
||||
.GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime);
|
||||
|
||||
DefaultControlPoints.AddRange(timingChanges);
|
||||
|
||||
@ -88,8 +86,8 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield playfield)
|
||||
{
|
||||
playfield.HitObjects.AddSpeedAdjustment(CreateSpeedAdjustmentContainer(controlPoint));
|
||||
playfield.NestedPlayfields.ForEach(p => applySpeedAdjustment(controlPoint, p));
|
||||
playfield.HitObjects.AddControlPoint(controlPoint);
|
||||
playfield.NestedPlayfields?.OfType<ScrollingPlayfield>().ForEach(p => applySpeedAdjustment(controlPoint, p));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -108,12 +106,5 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
return new MultiplierControlPoint(time, DefaultControlPoints[index].DeepClone());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="SpeedAdjustmentContainer"/> that facilitates the movement of hit objects.
|
||||
/// </summary>
|
||||
/// <param name="controlPoint">The <see cref="MultiplierControlPoint"/> that provides the speed adjustments for the hitobjects.</param>
|
||||
/// <returns>The <see cref="SpeedAdjustmentContainer"/>.</returns>
|
||||
protected virtual SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new SpeedAdjustmentContainer(controlPoint);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
||||
{
|
||||
public interface ISpeedChangeVisualiser
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the states of <see cref="DrawableHitObject"/>s that are constant, such as lifetime and spatial length.
|
||||
/// This is invoked once whenever <paramref name="timeRange"/> or <paramref name="length"/> changes.
|
||||
/// </summary>
|
||||
/// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose states should be computed.</param>
|
||||
/// <param name="direction">The scrolling direction.</param>
|
||||
/// <param name="timeRange">The duration required to scroll through one length of the screen before any control point adjustments.</param>
|
||||
/// <param name="length">The length of the screen that is scrolled through.</param>
|
||||
void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length);
|
||||
|
||||
/// <summary>
|
||||
/// Computes the states of <see cref="DrawableHitObject"/>s that change depending on <paramref name="currentTime"/>, such as position.
|
||||
/// This is invoked once per frame.
|
||||
/// </summary>
|
||||
/// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose states should be computed.</param>
|
||||
/// <param name="direction">The scrolling direction.</param>
|
||||
/// <param name="currentTime">The current time.</param>
|
||||
/// <param name="timeRange">The duration required to scroll through one length of the screen before any control point adjustments.</param>
|
||||
/// <param name="length">The length of the screen that is scrolled through.</param>
|
||||
void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length);
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
||||
{
|
||||
public class OverlappingSpeedChangeVisualiser : ISpeedChangeVisualiser
|
||||
{
|
||||
private readonly SortedList<MultiplierControlPoint> controlPoints;
|
||||
|
||||
public OverlappingSpeedChangeVisualiser(SortedList<MultiplierControlPoint> controlPoints)
|
||||
{
|
||||
this.controlPoints = controlPoints;
|
||||
}
|
||||
|
||||
public void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length)
|
||||
{
|
||||
foreach (var obj in hitObjects)
|
||||
{
|
||||
var controlPoint = controlPointAt(obj.HitObject.StartTime);
|
||||
obj.LifetimeStart = obj.HitObject.StartTime - timeRange / controlPoint.Multiplier;
|
||||
|
||||
if (obj.NestedHitObjects != null)
|
||||
{
|
||||
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
|
||||
ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length)
|
||||
{
|
||||
foreach (var obj in hitObjects)
|
||||
{
|
||||
var controlPoint = controlPointAt(obj.HitObject.StartTime);
|
||||
|
||||
var position = (obj.HitObject.StartTime - currentTime) * controlPoint.Multiplier / timeRange;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
obj.Y = (float)(position * length.Y);
|
||||
break;
|
||||
case ScrollingDirection.Down:
|
||||
obj.Y = (float)(-position * length.Y);
|
||||
break;
|
||||
case ScrollingDirection.Left:
|
||||
obj.X = (float)(position * length.X);
|
||||
break;
|
||||
case ScrollingDirection.Right:
|
||||
obj.X = (float)(-position * length.X);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint();
|
||||
private MultiplierControlPoint controlPointAt(double time)
|
||||
{
|
||||
if (controlPoints.Count == 0)
|
||||
return new MultiplierControlPoint(double.NegativeInfinity);
|
||||
|
||||
if (time < controlPoints[0].StartTime)
|
||||
return controlPoints[0];
|
||||
|
||||
searchPoint.StartTime = time;
|
||||
int index = controlPoints.BinarySearch(searchPoint);
|
||||
|
||||
if (index < 0)
|
||||
index = ~index - 1;
|
||||
|
||||
return controlPoints[index];
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
||||
{
|
||||
public class SequentialSpeedChangeVisualiser : ISpeedChangeVisualiser
|
||||
{
|
||||
private readonly Dictionary<DrawableHitObject, double> hitObjectPositions = new Dictionary<DrawableHitObject, double>();
|
||||
|
||||
private readonly IReadOnlyList<MultiplierControlPoint> controlPoints;
|
||||
|
||||
public SequentialSpeedChangeVisualiser(IReadOnlyList<MultiplierControlPoint> controlPoints)
|
||||
{
|
||||
this.controlPoints = controlPoints;
|
||||
}
|
||||
|
||||
public void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length)
|
||||
{
|
||||
foreach (var obj in hitObjects)
|
||||
{
|
||||
var startPosition = hitObjectPositions[obj] = positionAt(obj.HitObject.StartTime, timeRange);
|
||||
|
||||
obj.LifetimeStart = obj.HitObject.StartTime - timeRange - 1000;
|
||||
|
||||
if (obj.HitObject is IHasEndTime endTime)
|
||||
{
|
||||
var diff = positionAt(endTime.EndTime, timeRange) - startPosition;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
case ScrollingDirection.Down:
|
||||
obj.Height = (float)(diff * length.Y);
|
||||
break;
|
||||
case ScrollingDirection.Left:
|
||||
case ScrollingDirection.Right:
|
||||
obj.Width = (float)(diff * length.X);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.NestedHitObjects != null)
|
||||
{
|
||||
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
|
||||
ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length)
|
||||
{
|
||||
var timelinePosition = positionAt(currentTime, timeRange);
|
||||
|
||||
foreach (var obj in hitObjects)
|
||||
{
|
||||
var finalPosition = hitObjectPositions[obj] - timelinePosition;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
obj.Y = (float)(finalPosition * length.Y);
|
||||
break;
|
||||
case ScrollingDirection.Down:
|
||||
obj.Y = (float)(-finalPosition * length.Y);
|
||||
break;
|
||||
case ScrollingDirection.Left:
|
||||
obj.X = (float)(finalPosition * length.X);
|
||||
break;
|
||||
case ScrollingDirection.Right:
|
||||
obj.X = (float)(-finalPosition * length.X);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double positionAt(double time, double timeRange)
|
||||
{
|
||||
double length = 0;
|
||||
for (int i = 0; i < controlPoints.Count; i++)
|
||||
{
|
||||
var current = controlPoints[i];
|
||||
var next = i < controlPoints.Count - 1 ? controlPoints[i + 1] : null;
|
||||
|
||||
if (i > 0 && current.StartTime > time)
|
||||
continue;
|
||||
|
||||
// Duration of the current control point
|
||||
var currentDuration = (next?.StartTime ?? double.PositiveInfinity) - current.StartTime;
|
||||
|
||||
length += Math.Min(currentDuration, time - current.StartTime) * current.Multiplier / timeRange;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,266 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of <see cref="Playfield"/> specialized towards scrolling <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
public class ScrollingPlayfield : Playfield
|
||||
{
|
||||
/// <summary>
|
||||
/// The default span of time visible by the length of the scrolling axes.
|
||||
/// This is clamped between <see cref="time_span_min"/> and <see cref="time_span_max"/>.
|
||||
/// </summary>
|
||||
private const double time_span_default = 1500;
|
||||
/// <summary>
|
||||
/// The minimum span of time that may be visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
private const double time_span_min = 50;
|
||||
/// <summary>
|
||||
/// The maximum span of time that may be visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
private const double time_span_max = 10000;
|
||||
/// <summary>
|
||||
/// The step increase/decrease of the span of time visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
private const double time_span_step = 50;
|
||||
|
||||
/// <summary>
|
||||
/// The span of time that is visible by the length of the scrolling axes.
|
||||
/// For example, only hit objects with start time less than or equal to 1000 will be visible with <see cref="VisibleTimeRange"/> = 1000.
|
||||
/// </summary>
|
||||
public readonly BindableDouble VisibleTimeRange = new BindableDouble(time_span_default)
|
||||
{
|
||||
Default = time_span_default,
|
||||
MinValue = time_span_min,
|
||||
MaxValue = time_span_max
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Whether to reverse the scrolling direction is reversed. Note that this does _not_ invert the hit objects.
|
||||
/// </summary>
|
||||
protected readonly BindableBool Reversed = new BindableBool();
|
||||
|
||||
/// <summary>
|
||||
/// The container that contains the <see cref="SpeedAdjustmentContainer"/>s and <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
public new readonly ScrollingHitObjectContainer HitObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ScrollingPlayfield"/>.
|
||||
/// </summary>
|
||||
/// <param name="scrollingAxes">The axes on which <see cref="DrawableHitObject"/>s in this container should scroll.</param>
|
||||
/// <param name="customWidth">Whether we want our internal coordinate system to be scaled to a specified width</param>
|
||||
protected ScrollingPlayfield(Axes scrollingAxes, float? customWidth = null)
|
||||
: base(customWidth)
|
||||
{
|
||||
base.HitObjects = HitObjects = new ScrollingHitObjectContainer(scrollingAxes) { RelativeSizeAxes = Axes.Both };
|
||||
HitObjects.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||
HitObjects.Reversed.BindTo(Reversed);
|
||||
}
|
||||
|
||||
private List<ScrollingPlayfield> nestedPlayfields;
|
||||
/// <summary>
|
||||
/// All the <see cref="ScrollingPlayfield"/>s nested inside this playfield.
|
||||
/// </summary>
|
||||
public IEnumerable<ScrollingPlayfield> NestedPlayfields => nestedPlayfields;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="ScrollingPlayfield"/> to this playfield. The nested <see cref="ScrollingPlayfield"/>
|
||||
/// will be given all of the same speed adjustments as this playfield.
|
||||
/// </summary>
|
||||
/// <param name="otherPlayfield">The <see cref="ScrollingPlayfield"/> to add.</param>
|
||||
protected void AddNested(ScrollingPlayfield otherPlayfield)
|
||||
{
|
||||
if (nestedPlayfields == null)
|
||||
nestedPlayfields = new List<ScrollingPlayfield>();
|
||||
|
||||
nestedPlayfields.Add(otherPlayfield);
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
if (state.Keyboard.ControlPressed)
|
||||
{
|
||||
switch (args.Key)
|
||||
{
|
||||
case Key.Minus:
|
||||
transformVisibleTimeRangeTo(VisibleTimeRange + time_span_step, 200, Easing.OutQuint);
|
||||
break;
|
||||
case Key.Plus:
|
||||
transformVisibleTimeRangeTo(VisibleTimeRange - time_span_step, 200, Easing.OutQuint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void transformVisibleTimeRangeTo(double newTimeRange, double duration = 0, Easing easing = Easing.None)
|
||||
{
|
||||
this.TransformTo(this.PopulateTransform(new TransformVisibleTimeRange(), newTimeRange, duration, easing));
|
||||
}
|
||||
|
||||
private class TransformVisibleTimeRange : Transform<double, ScrollingPlayfield>
|
||||
{
|
||||
private double valueAt(double time)
|
||||
{
|
||||
if (time < StartTime) return StartValue;
|
||||
if (time >= EndTime) return EndValue;
|
||||
|
||||
return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
|
||||
}
|
||||
|
||||
public override string TargetMember => "VisibleTimeRange.Value";
|
||||
|
||||
protected override void Apply(ScrollingPlayfield d, double time) => d.VisibleTimeRange.Value = valueAt(time);
|
||||
protected override void ReadIntoStartValue(ScrollingPlayfield d) => StartValue = d.VisibleTimeRange.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A container that provides the foundation for sorting <see cref="DrawableHitObject"/>s into <see cref="SpeedAdjustmentContainer"/>s.
|
||||
/// </summary>
|
||||
public class ScrollingHitObjectContainer : HitObjectContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the range of time that is visible by the length of the scrolling axes.
|
||||
/// For example, only hit objects with start time less than or equal to 1000 will be visible with <see cref="VisibleTimeRange"/> = 1000.
|
||||
/// </summary>
|
||||
public readonly BindableDouble VisibleTimeRange = new BindableDouble { Default = 1000 };
|
||||
|
||||
/// <summary>
|
||||
/// Whether to reverse the scrolling direction is reversed.
|
||||
/// </summary>
|
||||
public readonly BindableBool Reversed = new BindableBool();
|
||||
|
||||
private readonly SortedContainer speedAdjustments;
|
||||
public IReadOnlyList<SpeedAdjustmentContainer> SpeedAdjustments => speedAdjustments;
|
||||
|
||||
private readonly SpeedAdjustmentContainer defaultSpeedAdjustment;
|
||||
|
||||
private readonly Axes scrollingAxes;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ScrollingHitObjectContainer"/>.
|
||||
/// </summary>
|
||||
/// <param name="scrollingAxes">The axes upon which hit objects should appear to scroll inside this container.</param>
|
||||
public ScrollingHitObjectContainer(Axes scrollingAxes)
|
||||
{
|
||||
this.scrollingAxes = scrollingAxes;
|
||||
|
||||
AddInternal(speedAdjustments = new SortedContainer { RelativeSizeAxes = Axes.Both });
|
||||
|
||||
// Default speed adjustment
|
||||
AddSpeedAdjustment(defaultSpeedAdjustment = new SpeedAdjustmentContainer(new MultiplierControlPoint(0)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
/// <param name="speedAdjustment">The <see cref="SpeedAdjustmentContainer"/>.</param>
|
||||
public void AddSpeedAdjustment(SpeedAdjustmentContainer speedAdjustment)
|
||||
{
|
||||
speedAdjustment.ScrollingAxes = scrollingAxes;
|
||||
speedAdjustment.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||
speedAdjustment.Reversed.BindTo(Reversed);
|
||||
|
||||
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);
|
||||
for (int i = 0; i < existingAdjustment.Count; i++)
|
||||
{
|
||||
DrawableHitObject hitObject = existingAdjustment[i];
|
||||
|
||||
if (!speedAdjustment.CanContain(hitObject.HitObject.StartTime))
|
||||
continue;
|
||||
|
||||
existingAdjustment.Remove(hitObject);
|
||||
speedAdjustment.Add(hitObject);
|
||||
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
speedAdjustments.Add(speedAdjustment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a <see cref="SpeedAdjustmentContainer"/> from this container, re-sorting all hit objects
|
||||
/// which it contained into new <see cref="SpeedAdjustmentContainer"/>s.
|
||||
/// </summary>
|
||||
/// <param name="speedAdjustment">The <see cref="SpeedAdjustmentContainer"/> to remove.</param>
|
||||
public void RemoveSpeedAdjustment(SpeedAdjustmentContainer speedAdjustment)
|
||||
{
|
||||
if (speedAdjustment == defaultSpeedAdjustment)
|
||||
throw new InvalidOperationException($"The default {nameof(SpeedAdjustmentContainer)} must not be removed.");
|
||||
|
||||
if (!speedAdjustments.Remove(speedAdjustment))
|
||||
return;
|
||||
|
||||
while (speedAdjustment.Count > 0)
|
||||
{
|
||||
DrawableHitObject hitObject = speedAdjustment[0];
|
||||
|
||||
speedAdjustment.Remove(hitObject);
|
||||
Add(hitObject);
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<DrawableHitObject> Objects => speedAdjustments.SelectMany(s => s.Children);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a hit object to this <see cref="ScrollingHitObjectContainer"/>. The hit objects will be queued to be processed
|
||||
/// new <see cref="SpeedAdjustmentContainer"/>s are added to this <see cref="ScrollingHitObjectContainer"/>.
|
||||
/// </summary>
|
||||
/// <param name="hitObject">The hit object to add.</param>
|
||||
public override void Add(DrawableHitObject hitObject)
|
||||
{
|
||||
if (!(hitObject is IScrollingHitObject))
|
||||
throw new InvalidOperationException($"Hit objects added to a {nameof(ScrollingHitObjectContainer)} must implement {nameof(IScrollingHitObject)}.");
|
||||
|
||||
adjustmentContainerAt(hitObject.HitObject.StartTime).Add(hitObject);
|
||||
}
|
||||
|
||||
public override bool Remove(DrawableHitObject hitObject) => speedAdjustments.Any(s => s.Remove(hitObject));
|
||||
|
||||
/// <summary>
|
||||
/// Finds the <see cref="SpeedAdjustmentContainer"/> which provides the speed adjustment active at a time.
|
||||
/// If there is no <see cref="SpeedAdjustmentContainer"/> active at the time, then the first (time-wise) speed adjustment is returned.
|
||||
/// </summary>
|
||||
/// <param name="time">The time to find the active <see cref="SpeedAdjustmentContainer"/> at.</param>
|
||||
/// <returns>The <see cref="SpeedAdjustmentContainer"/> active at <paramref name="time"/>. Null if there are no speed adjustments.</returns>
|
||||
private SpeedAdjustmentContainer adjustmentContainerAt(double time) => speedAdjustments.FirstOrDefault(c => c.CanContain(time)) ?? defaultSpeedAdjustment;
|
||||
|
||||
private class SortedContainer : Container<SpeedAdjustmentContainer>
|
||||
{
|
||||
protected override int Compare(Drawable x, Drawable y)
|
||||
{
|
||||
var sX = (SpeedAdjustmentContainer)x;
|
||||
var sY = (SpeedAdjustmentContainer)y;
|
||||
|
||||
int result = sY.ControlPoint.StartTime.CompareTo(sX.ControlPoint.StartTime);
|
||||
if (result != 0)
|
||||
return result;
|
||||
return base.Compare(y, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using OpenTK;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
@ -19,10 +20,7 @@ namespace osu.Game.Screens.Backgrounds
|
||||
|
||||
public WorkingBeatmap Beatmap
|
||||
{
|
||||
get
|
||||
{
|
||||
return beatmap;
|
||||
}
|
||||
get { return beatmap; }
|
||||
set
|
||||
{
|
||||
if (beatmap == value && beatmap != null)
|
||||
@ -56,11 +54,8 @@ namespace osu.Game.Screens.Backgrounds
|
||||
Beatmap = beatmap;
|
||||
}
|
||||
|
||||
public void BlurTo(Vector2 sigma, double duration, Easing easing = Easing.None)
|
||||
{
|
||||
background?.BlurTo(sigma, duration, easing);
|
||||
blurTarget = sigma;
|
||||
}
|
||||
public TransformSequence<Background> BlurTo(Vector2 sigma, double duration, Easing easing = Easing.None)
|
||||
=> background?.BlurTo(blurTarget = sigma, duration, easing);
|
||||
|
||||
public override bool Equals(BackgroundScreen other)
|
||||
{
|
||||
|
@ -5,6 +5,5 @@ namespace osu.Game.Screens.Backgrounds
|
||||
{
|
||||
public class BackgroundScreenEmpty : BackgroundScreen
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +157,6 @@ namespace osu.Game.Screens.Menu
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,7 @@ namespace osu.Game.Screens.Menu
|
||||
private Color4 iconColour;
|
||||
|
||||
public override bool ShowOverlaysOnEnter => false;
|
||||
|
||||
public override bool HasLocalCursorDisplayed => true;
|
||||
public override bool CursorVisible => false;
|
||||
|
||||
public Disclaimer()
|
||||
{
|
||||
|
@ -31,9 +31,8 @@ namespace osu.Game.Screens.Menu
|
||||
private SampleChannel welcome;
|
||||
private SampleChannel seeya;
|
||||
|
||||
public override bool HasLocalCursorDisplayed => true;
|
||||
|
||||
public override bool ShowOverlaysOnEnter => false;
|
||||
public override bool CursorVisible => false;
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenEmpty();
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
@ -188,7 +187,7 @@ namespace osu.Game.Screens.Menu
|
||||
mediumRing.ResizeTo(130, 340, Easing.OutQuad);
|
||||
mediumRing.Foreground.ResizeTo(1, 880, Easing.Out);
|
||||
|
||||
Func<double> remainingTime = () => length - TransformDelay;
|
||||
double remainingTime() => length - TransformDelay;
|
||||
|
||||
using (BeginDelayedSequence(250, true))
|
||||
{
|
||||
|
@ -231,7 +231,7 @@ namespace osu.Game.Screens.Menu
|
||||
/// <param name="waitForPrevious">If true, the new animation is delayed until all previous transforms finish. If false, existing transformed are cleared.</param>
|
||||
public void AppendAnimatingAction(Action action, bool waitForPrevious)
|
||||
{
|
||||
Action runnableAction = () =>
|
||||
void runnableAction()
|
||||
{
|
||||
if (waitForPrevious)
|
||||
this.DelayUntilTransformsFinished().Schedule(action);
|
||||
@ -240,12 +240,12 @@ namespace osu.Game.Screens.Menu
|
||||
ClearTransforms();
|
||||
action();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (IsLoaded)
|
||||
runnableAction();
|
||||
else
|
||||
Schedule(() => runnableAction());
|
||||
Schedule(runnableAction);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -229,7 +229,6 @@ namespace osu.Game.Screens.Multiplayer
|
||||
{
|
||||
coverContainer.FadeIn(transition_duration);
|
||||
|
||||
|
||||
LoadComponentAsync(new BeatmapSetCover(value.BeatmapSet)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
|
@ -35,9 +35,12 @@ namespace osu.Game.Screens
|
||||
/// </summary>
|
||||
public virtual bool ShowOverlaysOnEnter => true;
|
||||
|
||||
protected new OsuGameBase Game => base.Game as OsuGameBase;
|
||||
/// <summary>
|
||||
/// Whether this <see cref="OsuScreen"/> allows the cursor to be displayed.
|
||||
/// </summary>
|
||||
public virtual bool CursorVisible => true;
|
||||
|
||||
public virtual bool HasLocalCursorDisplayed => false;
|
||||
protected new OsuGameBase Game => base.Game as OsuGameBase;
|
||||
|
||||
private OsuLogo logo;
|
||||
|
||||
|
@ -23,22 +23,22 @@ using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Screens.Play.BreaksOverlay;
|
||||
using osu.Game.Storyboards.Drawables;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
public class Player : OsuScreen
|
||||
public class Player : OsuScreen, IProvideCursor
|
||||
{
|
||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
|
||||
|
||||
public override bool ShowOverlaysOnEnter => false;
|
||||
|
||||
public override bool HasLocalCursorDisplayed => !pauseContainer.IsPaused && !HasFailed && RulesetContainer.ProvidingUserCursor;
|
||||
|
||||
public Action RestartRequested;
|
||||
|
||||
public override bool AllowBeatmapRulesetChange => false;
|
||||
@ -51,6 +51,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public int RestartCount;
|
||||
|
||||
public CursorContainer Cursor => RulesetContainer.Cursor;
|
||||
public bool ProvidingUserCursor => RulesetContainer?.Cursor != null && !RulesetContainer.HasReplayLoaded;
|
||||
|
||||
private IAdjustableClock adjustableSourceClock;
|
||||
private FramedOffsetClock offsetClock;
|
||||
private DecoupleableInterpolatingFramedClock decoupledClock;
|
||||
@ -67,6 +70,7 @@ namespace osu.Game.Screens.Play
|
||||
#region User Settings
|
||||
|
||||
private Bindable<double> dimLevel;
|
||||
private Bindable<double> blurLevel;
|
||||
private Bindable<bool> showStoryboard;
|
||||
private Bindable<bool> mouseWheelDisabled;
|
||||
private Bindable<double> userAudioOffset;
|
||||
@ -90,6 +94,7 @@ namespace osu.Game.Screens.Play
|
||||
this.api = api;
|
||||
|
||||
dimLevel = config.GetBindable<double>(OsuSetting.DimLevel);
|
||||
blurLevel = config.GetBindable<double>(OsuSetting.BlurLevel);
|
||||
showStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
|
||||
|
||||
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
|
||||
@ -174,13 +179,13 @@ namespace osu.Game.Screens.Play
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SkipButton(firstObjectTime) { AudioClock = decoupledClock },
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = offsetClock,
|
||||
Child = RulesetContainer,
|
||||
},
|
||||
new SkipButton(firstObjectTime) { AudioClock = decoupledClock },
|
||||
hudOverlay = new HUDOverlay
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
@ -192,7 +197,7 @@ namespace osu.Game.Screens.Play
|
||||
Origin = Anchor.Centre,
|
||||
Clock = decoupledClock,
|
||||
Breaks = beatmap.Breaks
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
failOverlay = new FailOverlay
|
||||
@ -316,10 +321,9 @@ namespace osu.Game.Screens.Play
|
||||
if (!loadedSuccessfully)
|
||||
return;
|
||||
|
||||
(Background as BackgroundScreenBeatmap)?.BlurTo(Vector2.Zero, 1000, Easing.OutQuint);
|
||||
|
||||
dimLevel.ValueChanged += dimLevel_ValueChanged;
|
||||
showStoryboard.ValueChanged += showStoryboard_ValueChanged;
|
||||
dimLevel.ValueChanged += _ => updateBackgroundElements();
|
||||
blurLevel.ValueChanged += _ => updateBackgroundElements();
|
||||
showStoryboard.ValueChanged += _ => updateBackgroundElements();
|
||||
updateBackgroundElements();
|
||||
|
||||
Content.Alpha = 0;
|
||||
@ -375,14 +379,12 @@ namespace osu.Game.Screens.Play
|
||||
return true;
|
||||
}
|
||||
|
||||
private void dimLevel_ValueChanged(double newValue)
|
||||
=> updateBackgroundElements();
|
||||
|
||||
private void showStoryboard_ValueChanged(bool newValue)
|
||||
=> updateBackgroundElements();
|
||||
|
||||
private void updateBackgroundElements()
|
||||
{
|
||||
if (!IsCurrentScreen) return;
|
||||
|
||||
const float duration = 800;
|
||||
|
||||
var opacity = 1 - (float)dimLevel;
|
||||
|
||||
if (showStoryboard && storyboard == null)
|
||||
@ -391,17 +393,16 @@ namespace osu.Game.Screens.Play
|
||||
var beatmap = Beatmap.Value;
|
||||
var storyboardVisible = showStoryboard && beatmap.Storyboard.HasDrawable;
|
||||
|
||||
storyboardContainer.FadeColour(new Color4(opacity, opacity, opacity, 1), 800);
|
||||
storyboardContainer.FadeTo(storyboardVisible && opacity > 0 ? 1 : 0);
|
||||
storyboardContainer
|
||||
.FadeColour(OsuColour.Gray(opacity), duration, Easing.OutQuint)
|
||||
.FadeTo(storyboardVisible && opacity > 0 ? 1 : 0, duration, Easing.OutQuint);
|
||||
|
||||
Background?.FadeTo(!storyboardVisible || beatmap.Background == null ? opacity : 0, 800, Easing.OutQuint);
|
||||
(Background as BackgroundScreenBeatmap)?.BlurTo(new Vector2((float)blurLevel.Value * 25), duration, Easing.OutQuint);
|
||||
Background?.FadeTo(!storyboardVisible || beatmap.Background == null ? opacity : 0, duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private void fadeOut()
|
||||
{
|
||||
dimLevel.ValueChanged -= dimLevel_ValueChanged;
|
||||
showStoryboard.ValueChanged -= showStoryboard_ValueChanged;
|
||||
|
||||
const float fade_out_duration = 250;
|
||||
|
||||
RulesetContainer?.FadeOut(fade_out_duration);
|
||||
|
@ -11,7 +11,7 @@ using osu.Game.Overlays.Settings;
|
||||
namespace osu.Game.Screens.Play.ReplaySettings
|
||||
{
|
||||
public class ReplaySliderBar<T> : SettingsSlider<T>
|
||||
where T : struct, IEquatable<T>
|
||||
where T : struct, IEquatable<T>, IComparable, IConvertible
|
||||
{
|
||||
protected override Drawable CreateControl() => new Sliderbar
|
||||
{
|
||||
@ -21,6 +21,8 @@ namespace osu.Game.Screens.Play.ReplaySettings
|
||||
|
||||
private class Sliderbar : OsuSliderBar<T>
|
||||
{
|
||||
public override string TooltipText => $"{CurrentNumber.Value}";
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
|
@ -14,13 +14,14 @@ using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Input.Bindings;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
public class SkipButton : Container
|
||||
public class SkipButton : OverlayContainer, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
private readonly double startTime;
|
||||
public IAdjustableClock AudioClock;
|
||||
@ -35,6 +36,8 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
this.startTime = startTime;
|
||||
|
||||
State = Visibility.Visible;
|
||||
|
||||
RelativePositionAxes = Axes.Both;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
@ -111,26 +114,36 @@ namespace osu.Game.Screens.Play
|
||||
Expire();
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
this.FadeIn();
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
this.FadeOut();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
remainingTimeBox.ResizeWidthTo((float)Math.Max(0, 1 - (Time.Current - displayTime) / (beginFadeTime - displayTime)), 120, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
public bool OnPressed(GlobalAction action)
|
||||
{
|
||||
if (args.Repeat) return false;
|
||||
|
||||
switch (args.Key)
|
||||
switch (action)
|
||||
{
|
||||
case Key.Space:
|
||||
case GlobalAction.SkipCutscene:
|
||||
button.TriggerOnClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnKeyDown(state, args);
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(GlobalAction action) => false;
|
||||
|
||||
private class FadeContainer : Container, IStateful<Visibility>
|
||||
{
|
||||
public event Action<Visibility> StateChanged;
|
||||
|
@ -236,8 +236,8 @@ namespace osu.Game.Screens.Select
|
||||
/// <returns>True if a selection could be made, else False.</returns>
|
||||
public bool SelectNextRandom()
|
||||
{
|
||||
var visible = beatmapSets.Where(s => !s.Filtered).ToList();
|
||||
if (!visible.Any())
|
||||
var visibleSets = beatmapSets.Where(s => !s.Filtered).ToList();
|
||||
if (!visibleSets.Any())
|
||||
return false;
|
||||
|
||||
if (selectedBeatmap != null)
|
||||
@ -254,20 +254,21 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
if (RandomAlgorithm == RandomSelectAlgorithm.RandomPermutation)
|
||||
{
|
||||
var notYetVisitedSets = visible.Except(previouslyVisitedRandomSets).ToList();
|
||||
var notYetVisitedSets = visibleSets.Except(previouslyVisitedRandomSets).ToList();
|
||||
if (!notYetVisitedSets.Any())
|
||||
{
|
||||
previouslyVisitedRandomSets.Clear();
|
||||
notYetVisitedSets = visible;
|
||||
previouslyVisitedRandomSets.RemoveAll(s => visibleSets.Contains(s));
|
||||
notYetVisitedSets = visibleSets;
|
||||
}
|
||||
|
||||
set = notYetVisitedSets.ElementAt(RNG.Next(notYetVisitedSets.Count));
|
||||
previouslyVisitedRandomSets.Add(set);
|
||||
}
|
||||
else
|
||||
set = visible.ElementAt(RNG.Next(visible.Count));
|
||||
set = visibleSets.ElementAt(RNG.Next(visibleSets.Count));
|
||||
|
||||
select(set.Beatmaps.Skip(RNG.Next(set.Beatmaps.Count())).FirstOrDefault());
|
||||
var visibleBeatmaps = set.Beatmaps.Where(s => !s.Filtered).ToList();
|
||||
select(visibleBeatmaps[RNG.Next(visibleBeatmaps.Count)]);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,8 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
get { return scope; }
|
||||
set
|
||||
{
|
||||
if (value == scope) return;
|
||||
if (value == scope)
|
||||
return;
|
||||
|
||||
scope = value;
|
||||
updateScores();
|
||||
@ -107,8 +108,6 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
get { return placeholderState; }
|
||||
set
|
||||
{
|
||||
if (value == placeholderState) return;
|
||||
|
||||
if (value != PlaceholderState.Successful)
|
||||
{
|
||||
getScoresRequest?.Cancel();
|
||||
@ -116,6 +115,9 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
Scores = null;
|
||||
}
|
||||
|
||||
if (value == placeholderState)
|
||||
return;
|
||||
|
||||
switch (placeholderState = value)
|
||||
{
|
||||
case PlaceholderState.NetworkFailure:
|
||||
@ -171,7 +173,8 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
get { return beatmap; }
|
||||
set
|
||||
{
|
||||
if (beatmap == value) return;
|
||||
if (beatmap == value)
|
||||
return;
|
||||
|
||||
beatmap = value;
|
||||
Scores = null;
|
||||
@ -259,7 +262,8 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
|
||||
private void onUpdateFailed(Exception e)
|
||||
{
|
||||
if (e is OperationCanceledException) return;
|
||||
if (e is OperationCanceledException)
|
||||
return;
|
||||
|
||||
PlaceholderState = PlaceholderState.NetworkFailure;
|
||||
Logger.Error(e, @"Couldn't fetch beatmap scores!");
|
||||
@ -294,7 +298,8 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
if (!scrollContainer.IsScrolledToEnd())
|
||||
fadeStart -= LeaderboardScore.HEIGHT;
|
||||
|
||||
if (scrollFlow == null) return;
|
||||
if (scrollFlow == null)
|
||||
return;
|
||||
|
||||
foreach (var c in scrollFlow.Children)
|
||||
{
|
||||
|
@ -266,7 +266,7 @@ namespace osu.Game.Screens.Select
|
||||
/// </summary>
|
||||
private void carouselSelectionChanged(BeatmapInfo beatmap)
|
||||
{
|
||||
Action performLoad = delegate
|
||||
void performLoad()
|
||||
{
|
||||
// We may be arriving here due to another component changing the bindable Beatmap.
|
||||
// In these cases, the other component has already loaded the beatmap, so we don't need to do so again.
|
||||
@ -279,7 +279,7 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
|
||||
UpdateBeatmap(Beatmap.Value);
|
||||
};
|
||||
}
|
||||
|
||||
if (beatmap?.Equals(beatmapNoDebounce) == true)
|
||||
return;
|
||||
|
@ -265,7 +265,7 @@ namespace osu.Game.Screens.Tournament
|
||||
|
||||
private void writeResults(string text)
|
||||
{
|
||||
Action writeAction = () =>
|
||||
void writeAction()
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -280,9 +280,9 @@ namespace osu.Game.Screens.Tournament
|
||||
{
|
||||
Logger.Error(ex, "Failed to write results.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
writeOp = writeOp?.ContinueWith(t => { writeAction(); }) ?? Task.Run(writeAction);
|
||||
writeOp = writeOp?.ContinueWith(t => { writeAction(); }) ?? Task.Run((Action)writeAction);
|
||||
}
|
||||
|
||||
private void reloadTeams()
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -18,24 +17,19 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public abstract class TestCasePlayer : ScreenTestCase
|
||||
{
|
||||
private readonly Type ruleset;
|
||||
private readonly Ruleset ruleset;
|
||||
|
||||
protected Player Player;
|
||||
|
||||
private TestWorkingBeatmap working;
|
||||
|
||||
/// <summary>
|
||||
/// Create a TestCase which runs through the Player screen.
|
||||
/// </summary>
|
||||
/// <param name="ruleset">An optional ruleset type which we want to target. If not provided we'll allow all rulesets to be tested.</param>
|
||||
protected TestCasePlayer(Type ruleset)
|
||||
protected TestCasePlayer(Ruleset ruleset)
|
||||
{
|
||||
this.ruleset = ruleset;
|
||||
}
|
||||
|
||||
protected TestCasePlayer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -48,14 +42,21 @@ namespace osu.Game.Tests.Visual
|
||||
Depth = int.MaxValue
|
||||
});
|
||||
|
||||
string instantiation = ruleset?.AssemblyQualifiedName;
|
||||
|
||||
foreach (var r in rulesets.AvailableRulesets.Where(rs => instantiation == null || rs.InstantiationInfo == instantiation))
|
||||
if (ruleset != null)
|
||||
{
|
||||
Player p = null;
|
||||
AddStep(r.Name, () => p = loadPlayerFor(r));
|
||||
AddStep(ruleset.RulesetInfo.Name, () => p = loadPlayerFor(ruleset));
|
||||
AddUntilStep(() => p.IsLoaded);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var r in rulesets.AvailableRulesets)
|
||||
{
|
||||
Player p = null;
|
||||
AddStep(r.Name, () => p = loadPlayerFor(r));
|
||||
AddUntilStep(() => p.IsLoaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Beatmap CreateBeatmap()
|
||||
@ -69,21 +70,21 @@ namespace osu.Game.Tests.Visual
|
||||
return beatmap;
|
||||
}
|
||||
|
||||
private Player loadPlayerFor(RulesetInfo r)
|
||||
private Player loadPlayerFor(RulesetInfo ri) => loadPlayerFor(ri.CreateInstance());
|
||||
|
||||
private Player loadPlayerFor(Ruleset r)
|
||||
{
|
||||
var beatmap = CreateBeatmap();
|
||||
|
||||
beatmap.BeatmapInfo.Ruleset = r;
|
||||
|
||||
var instance = r.CreateInstance();
|
||||
beatmap.BeatmapInfo.Ruleset = r.RulesetInfo;
|
||||
|
||||
working = new TestWorkingBeatmap(beatmap);
|
||||
working.Mods.Value = new[] { instance.GetAllMods().First(m => m is ModNoFail) };
|
||||
working.Mods.Value = new[] { r.GetAllMods().First(m => m is ModNoFail) };
|
||||
|
||||
if (Player != null)
|
||||
Remove(Player);
|
||||
|
||||
var player = CreatePlayer(working, instance);
|
||||
var player = CreatePlayer(working, r);
|
||||
|
||||
LoadComponentAsync(player, LoadScreen);
|
||||
|
||||
|
@ -44,7 +44,7 @@ namespace osu.Game.Users
|
||||
new Avatar(user)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
OnLoadComplete = d => d.FadeInFromZero(200),
|
||||
OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -39,10 +39,14 @@ namespace osu.Game.Users
|
||||
public string AvatarUrl;
|
||||
|
||||
[JsonProperty(@"cover_url")]
|
||||
public string CoverUrl;
|
||||
public string CoverUrl
|
||||
{
|
||||
get { return Cover?.Url; }
|
||||
set { Cover = new UserCover { Url = value }; }
|
||||
}
|
||||
|
||||
//[JsonProperty(@"cover")]
|
||||
//public UserCover Cover;
|
||||
[JsonProperty(@"cover")]
|
||||
public UserCover Cover;
|
||||
|
||||
public class UserCover
|
||||
{
|
||||
|
@ -28,9 +28,12 @@ namespace osu.Game.Users
|
||||
private const float content_padding = 10;
|
||||
private const float status_height = 30;
|
||||
|
||||
private readonly Container statusBar;
|
||||
private readonly Box statusBg;
|
||||
private readonly OsuSpriteText statusMessage;
|
||||
private Container statusBar;
|
||||
private Box statusBg;
|
||||
private OsuSpriteText statusMessage;
|
||||
|
||||
private Container content;
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
public readonly Bindable<UserStatus> Status = new Bindable<UserStatus>();
|
||||
|
||||
@ -45,133 +48,7 @@ namespace osu.Game.Users
|
||||
|
||||
this.user = user;
|
||||
|
||||
FillFlowContainer infoContainer;
|
||||
|
||||
Height = height - status_height;
|
||||
Masking = true;
|
||||
CornerRadius = 5;
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = Color4.Black.Opacity(0.25f),
|
||||
Radius = 4,
|
||||
};
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DelayedLoadWrapper(new UserCoverBackground(user)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
FillMode = FillMode.Fill,
|
||||
OnLoadComplete = d => d.FadeInFromZero(200),
|
||||
}, 0) { RelativeSizeAxes = Axes.Both },
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.7f),
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Top = content_padding, Left = content_padding, Right = content_padding },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new UpdateableAvatar
|
||||
{
|
||||
Size = new Vector2(height - status_height - content_padding * 2),
|
||||
User = user,
|
||||
Masking = true,
|
||||
CornerRadius = 5,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = Color4.Black.Opacity(0.25f),
|
||||
Radius = 4,
|
||||
},
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = height - status_height - content_padding },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = user.Username,
|
||||
TextSize = 18,
|
||||
Font = @"Exo2.0-SemiBoldItalic",
|
||||
},
|
||||
infoContainer = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = 20f,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(5f, 0f),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DrawableFlag(user.Country)
|
||||
{
|
||||
Width = 30f,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
statusBar = new Container
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Alpha = 0f,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
statusBg = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Spacing = new Vector2(5f, 0f),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Icon = FontAwesome.fa_circle_o,
|
||||
Shadow = true,
|
||||
Size = new Vector2(14),
|
||||
},
|
||||
statusMessage = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = @"Exo2.0-Semibold",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (user.IsSupporter)
|
||||
infoContainer.Add(new SupporterIcon
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 20f,
|
||||
});
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
@ -180,6 +57,139 @@ namespace osu.Game.Users
|
||||
if (colours == null)
|
||||
throw new ArgumentNullException(nameof(colours));
|
||||
|
||||
FillFlowContainer infoContainer;
|
||||
|
||||
AddInternal(content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
CornerRadius = 5,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = Color4.Black.Opacity(0.25f),
|
||||
Radius = 4,
|
||||
},
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DelayedLoadWrapper(new UserCoverBackground(user)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
FillMode = FillMode.Fill,
|
||||
OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out)
|
||||
}, 300) { RelativeSizeAxes = Axes.Both },
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.7f),
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Top = content_padding, Horizontal = content_padding },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new UpdateableAvatar
|
||||
{
|
||||
Size = new Vector2(height - status_height - content_padding * 2),
|
||||
User = user,
|
||||
Masking = true,
|
||||
CornerRadius = 5,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = Color4.Black.Opacity(0.25f),
|
||||
Radius = 4,
|
||||
},
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = height - status_height - content_padding },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = user.Username,
|
||||
TextSize = 18,
|
||||
Font = @"Exo2.0-SemiBoldItalic",
|
||||
},
|
||||
infoContainer = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = 20f,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(5f, 0f),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DrawableFlag(user.Country)
|
||||
{
|
||||
Width = 30f,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
statusBar = new Container
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Alpha = 0f,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
statusBg = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Spacing = new Vector2(5f, 0f),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Icon = FontAwesome.fa_circle_o,
|
||||
Shadow = true,
|
||||
Size = new Vector2(14),
|
||||
},
|
||||
statusMessage = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = @"Exo2.0-Semibold",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
if (user.IsSupporter)
|
||||
{
|
||||
infoContainer.Add(new SupporterIcon
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 20f,
|
||||
});
|
||||
}
|
||||
|
||||
Status.ValueChanged += displayStatus;
|
||||
Status.ValueChanged += status => statusBg.FadeColour(status?.GetAppropriateColour(colours) ?? colours.Gray5, 500, Easing.OutQuint);
|
||||
|
||||
|
@ -265,10 +265,12 @@
|
||||
<Compile Include="Beatmaps\Formats\JsonBeatmapDecoder.cs" />
|
||||
<Compile Include="Beatmaps\Formats\LegacyDecoder.cs" />
|
||||
<Compile Include="Beatmaps\Formats\LegacyStoryboardDecoder.cs" />
|
||||
<Compile Include="Configuration\SpeedChangeVisualisationMethod.cs" />
|
||||
<Compile Include="Database\DatabaseContextFactory.cs" />
|
||||
<Compile Include="Database\IHasPrimaryKey.cs" />
|
||||
<Compile Include="Graphics\Textures\LargeTextureStore.cs" />
|
||||
<Compile Include="Overlays\Profile\SupporterIcon.cs" />
|
||||
<Compile Include="Online\API\Requests\GetFriendsRequest.cs" />
|
||||
<Compile Include="Overlays\Settings\DangerousSettingsButton.cs" />
|
||||
<Compile Include="Graphics\UserInterface\HoverClickSounds.cs" />
|
||||
<Compile Include="Graphics\UserInterface\HoverSounds.cs" />
|
||||
@ -310,11 +312,23 @@
|
||||
<Compile Include="Overlays\Profile\Sections\Ranks\PaginatedScoreContainer.cs" />
|
||||
<Compile Include="Overlays\Profile\Sections\Ranks\DrawableTotalScore.cs" />
|
||||
<Compile Include="Overlays\Profile\Sections\Ranks\ScoreModsContainer.cs" />
|
||||
<Compile Include="Overlays\Settings\Sections\Gameplay\ScrollingSettings.cs" />
|
||||
<Compile Include="Overlays\Settings\Sections\Maintenance\DeleteAllBeatmapsDialog.cs" />
|
||||
<Compile Include="Rulesets\Mods\IApplicableFailOverride.cs" />
|
||||
<Compile Include="Rulesets\Mods\IApplicableMod.cs" />
|
||||
<Compile Include="Rulesets\Mods\IApplicableToBeatmapConverter.cs" />
|
||||
<Compile Include="Overlays\Social\SocialGridPanel.cs" />
|
||||
<Compile Include="Overlays\Social\SocialListPanel.cs" />
|
||||
<Compile Include="Overlays\Social\SocialPanel.cs" />
|
||||
<Compile Include="Rulesets\Mods\IApplicableToDrawableHitObject.cs" />
|
||||
<Compile Include="Rulesets\UI\HitObjectContainer.cs" />
|
||||
<Compile Include="Rulesets\UI\Scrolling\Visualisers\SequentialSpeedChangeVisualiser.cs" />
|
||||
<Compile Include="Rulesets\UI\Scrolling\Visualisers\ISpeedChangeVisualiser.cs" />
|
||||
<Compile Include="Rulesets\UI\Scrolling\Visualisers\OverlappingSpeedChangeVisualiser.cs" />
|
||||
<Compile Include="Rulesets\UI\Scrolling\ScrollingDirection.cs" />
|
||||
<Compile Include="Rulesets\UI\Scrolling\ScrollingHitObjectContainer.cs" />
|
||||
<Compile Include="Rulesets\UI\Scrolling\ScrollingPlayfield.cs" />
|
||||
<Compile Include="Rulesets\UI\Scrolling\ScrollingRulesetContainer.cs" />
|
||||
<Compile Include="Screens\Select\ImportFromStablePopup.cs" />
|
||||
<Compile Include="Overlays\Settings\SettingsButton.cs" />
|
||||
<Compile Include="Rulesets\Edit\Layers\Selection\OriginHandle.cs" />
|
||||
@ -374,8 +388,10 @@
|
||||
<Compile Include="Graphics\Containers\ParallaxContainer.cs" />
|
||||
<Compile Include="Graphics\Containers\ReverseChildIDFillFlowContainer.cs" />
|
||||
<Compile Include="Graphics\Containers\SectionsContainer.cs" />
|
||||
<Compile Include="Graphics\Cursor\IProvideCursor.cs" />
|
||||
<Compile Include="Graphics\Cursor\MenuCursor.cs" />
|
||||
<Compile Include="Graphics\Cursor\OsuContextMenuContainer.cs" />
|
||||
<Compile Include="Graphics\Cursor\CursorOverrideContainer.cs" />
|
||||
<Compile Include="Graphics\Cursor\OsuTooltipContainer.cs" />
|
||||
<Compile Include="Graphics\IHasAccentColour.cs" />
|
||||
<Compile Include="Graphics\OsuColour.cs" />
|
||||
@ -615,7 +631,6 @@
|
||||
<Compile Include="Rulesets\Objects\CircularArcApproximator.cs" />
|
||||
<Compile Include="Rulesets\Objects\Drawables\ArmedState.cs" />
|
||||
<Compile Include="Rulesets\Objects\Drawables\DrawableHitObject.cs" />
|
||||
<Compile Include="Rulesets\Objects\Drawables\DrawableScrollingHitObject.cs" />
|
||||
<Compile Include="Rulesets\Scoring\HitResult.cs" />
|
||||
<Compile Include="Rulesets\Objects\Drawables\IDrawableHitObjectWithProxiedApproach.cs" />
|
||||
<Compile Include="Rulesets\Objects\Drawables\IScrollingHitObject.cs" />
|
||||
@ -666,16 +681,11 @@
|
||||
<Compile Include="Rulesets\Scoring\ScoreProcessor.cs" />
|
||||
<Compile Include="Rulesets\Scoring\ScoreRank.cs" />
|
||||
<Compile Include="Rulesets\Scoring\ScoreStore.cs" />
|
||||
<Compile Include="Rulesets\Timing\LinearScrollingContainer.cs" />
|
||||
<Compile Include="Rulesets\Timing\MultiplierControlPoint.cs" />
|
||||
<Compile Include="Rulesets\Timing\ScrollingContainer.cs" />
|
||||
<Compile Include="Rulesets\Timing\SpeedAdjustmentContainer.cs" />
|
||||
<Compile Include="Rulesets\UI\ModIcon.cs" />
|
||||
<Compile Include="Rulesets\UI\Playfield.cs" />
|
||||
<Compile Include="Rulesets\UI\RulesetContainer.cs" />
|
||||
<Compile Include="Rulesets\UI\RulesetInputManager.cs" />
|
||||
<Compile Include="Rulesets\UI\ScrollingPlayfield.cs" />
|
||||
<Compile Include="Rulesets\UI\ScrollingRulesetContainer.cs" />
|
||||
<Compile Include="Screens\BackgroundScreen.cs" />
|
||||
<Compile Include="Screens\Backgrounds\BackgroundScreenBeatmap.cs" />
|
||||
<Compile Include="Screens\Backgrounds\BackgroundScreenCustom.cs" />
|
||||
|
Reference in New Issue
Block a user