mirror of
https://github.com/osukey/osukey.git
synced 2025-08-04 15:16:38 +09:00
Merge branch 'master' into channelselection-minimum-size
This commit is contained in:
@ -100,6 +100,11 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public bool Equals(BeatmapInfo other)
|
||||
{
|
||||
if (ID == 0 || other?.ID == 0)
|
||||
// one of the two BeatmapInfos we are comparing isn't sourced from a database.
|
||||
// fall back to reference equality.
|
||||
return ReferenceEquals(this, other);
|
||||
|
||||
return ID == other?.ID;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,9 @@ using osu.Game.IPC;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Rulesets;
|
||||
using SQLite.Net;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Game.Online.API;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
@ -63,6 +66,10 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
private readonly BeatmapStore beatmaps;
|
||||
|
||||
private readonly APIAccess api;
|
||||
|
||||
private readonly List<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>();
|
||||
|
||||
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
|
||||
private BeatmapIPCChannel ipc;
|
||||
|
||||
@ -76,7 +83,7 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
public Func<Storage> GetStableStorage { private get; set; }
|
||||
|
||||
public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, IIpcHost importHost = null)
|
||||
public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null)
|
||||
{
|
||||
beatmaps = new BeatmapStore(connection);
|
||||
beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s);
|
||||
@ -88,6 +95,7 @@ namespace osu.Game.Beatmaps
|
||||
this.files = files;
|
||||
this.connection = connection;
|
||||
this.rulesets = rulesets;
|
||||
this.api = api;
|
||||
|
||||
if (importHost != null)
|
||||
ipc = new BeatmapIPCChannel(importHost, this);
|
||||
@ -177,6 +185,74 @@ namespace osu.Game.Beatmaps
|
||||
beatmaps.Add(beatmapSetInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads a beatmap.
|
||||
/// </summary>
|
||||
/// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
|
||||
/// <returns>A new <see cref="DownloadBeatmapSetRequest"/>, or an existing one if a download is already in progress.</returns>
|
||||
public DownloadBeatmapSetRequest Download(BeatmapSetInfo beatmapSetInfo)
|
||||
{
|
||||
var existing = GetExistingDownload(beatmapSetInfo);
|
||||
|
||||
if (existing != null) return existing;
|
||||
|
||||
if (api == null) return null;
|
||||
|
||||
ProgressNotification downloadNotification = new ProgressNotification
|
||||
{
|
||||
Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
|
||||
};
|
||||
|
||||
var request = new DownloadBeatmapSetRequest(beatmapSetInfo);
|
||||
|
||||
request.DownloadProgressed += progress =>
|
||||
{
|
||||
downloadNotification.State = ProgressNotificationState.Active;
|
||||
downloadNotification.Progress = progress;
|
||||
};
|
||||
|
||||
request.Success += data =>
|
||||
{
|
||||
downloadNotification.State = ProgressNotificationState.Completed;
|
||||
|
||||
using (var stream = new MemoryStream(data))
|
||||
using (var archive = new OszArchiveReader(stream))
|
||||
Import(archive);
|
||||
|
||||
currentDownloads.Remove(request);
|
||||
};
|
||||
|
||||
request.Failure += data =>
|
||||
{
|
||||
downloadNotification.State = ProgressNotificationState.Completed;
|
||||
Logger.Error(data, "Failed to get beatmap download information");
|
||||
currentDownloads.Remove(request);
|
||||
};
|
||||
|
||||
downloadNotification.CancelRequested += () =>
|
||||
{
|
||||
request.Cancel();
|
||||
currentDownloads.Remove(request);
|
||||
downloadNotification.State = ProgressNotificationState.Cancelled;
|
||||
return true;
|
||||
};
|
||||
|
||||
currentDownloads.Add(request);
|
||||
PostNotification?.Invoke(downloadNotification);
|
||||
|
||||
// don't run in the main api queue as this is a long-running task.
|
||||
Task.Run(() => request.Perform(api));
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an existing download request if it exists.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The <see cref="BeatmapSetInfo"/> whose download request is wanted.</param>
|
||||
/// <returns>The <see cref="DownloadBeatmapSetRequest"/> object if it exists, or null.</returns>
|
||||
public DownloadBeatmapSetRequest GetExistingDownload(BeatmapSetInfo beatmap) => currentDownloads.Find(d => d.BeatmapSet.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID);
|
||||
|
||||
/// <summary>
|
||||
/// Delete a beatmap from the manager.
|
||||
/// Is a no-op for already deleted beatmaps.
|
||||
|
@ -87,7 +87,9 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
if (track != null) return track;
|
||||
|
||||
track = GetTrack();
|
||||
// we want to ensure that we always have a track, even if it's a fake one.
|
||||
track = GetTrack() ?? new TrackVirtual();
|
||||
|
||||
applyRateAdjustments();
|
||||
return track;
|
||||
}
|
||||
@ -107,10 +109,17 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
track?.Dispose();
|
||||
track = null;
|
||||
background?.Dispose();
|
||||
background = null;
|
||||
}
|
||||
|
||||
public void DisposeTrack()
|
||||
{
|
||||
lock (trackLock)
|
||||
{
|
||||
track?.Dispose();
|
||||
track = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ namespace osu.Game.Graphics.Cursor
|
||||
},
|
||||
AdditiveLayer = new Sprite
|
||||
{
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Blending = BlendingMode.Additive,
|
||||
Colour = colour.Pink,
|
||||
Alpha = 0,
|
||||
Texture = textures.Get(@"Cursor/menu-cursor-additive"),
|
||||
|
@ -57,6 +57,12 @@ namespace osu.Game.Graphics
|
||||
private void load(FontStore store)
|
||||
{
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
updateTexture();
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,8 @@ namespace osu.Game.Graphics.UserInterface
|
||||
colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In);
|
||||
flash();
|
||||
|
||||
this.Delay(click_duration).Schedule(delegate {
|
||||
this.Delay(click_duration).Schedule(delegate
|
||||
{
|
||||
colourContainer.ResizeTo(new Vector2(0.8f, 1f));
|
||||
spriteText.Spacing = Vector2.Zero;
|
||||
glowContainer.FadeOut();
|
||||
@ -140,7 +141,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
colourContainer.Add(flash);
|
||||
|
||||
flash.Colour = ButtonColour;
|
||||
flash.BlendingMode = BlendingMode.Additive;
|
||||
flash.Blending = BlendingMode.Additive;
|
||||
flash.Alpha = 0.3f;
|
||||
flash.FadeOutFromOne(click_duration);
|
||||
flash.Expire();
|
||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
hover = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Blending = BlendingMode.Additive,
|
||||
Colour = Color4.White.Opacity(0.1f),
|
||||
Alpha = 0,
|
||||
},
|
||||
|
@ -96,7 +96,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuDropdownMenuItem(item) { AccentColour = accentColour };
|
||||
|
||||
#region DrawableOsuDropdownMenuItem
|
||||
protected class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour
|
||||
public class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour
|
||||
{
|
||||
private Color4? accentColour;
|
||||
public Color4 AccentColour
|
||||
|
@ -15,11 +15,7 @@ namespace osu.Game.Graphics.UserInterface.Volume
|
||||
{
|
||||
private readonly VolumeMeter volumeMeterMaster;
|
||||
|
||||
private void volumeChanged(double newVolume)
|
||||
{
|
||||
Show();
|
||||
schedulePopOut();
|
||||
}
|
||||
protected override bool BlockPassThroughMouse => false;
|
||||
|
||||
public VolumeControl()
|
||||
{
|
||||
@ -85,6 +81,12 @@ namespace osu.Game.Graphics.UserInterface.Volume
|
||||
return false;
|
||||
}
|
||||
|
||||
private void volumeChanged(double newVolume)
|
||||
{
|
||||
Show();
|
||||
schedulePopOut();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
|
24
osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs
Normal file
24
osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using System;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class DownloadBeatmapSetRequest : APIDownloadRequest
|
||||
{
|
||||
public readonly BeatmapSetInfo BeatmapSet;
|
||||
|
||||
public Action<float> DownloadProgressed;
|
||||
|
||||
public DownloadBeatmapSetRequest(BeatmapSetInfo set)
|
||||
{
|
||||
BeatmapSet = set;
|
||||
|
||||
Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total);
|
||||
}
|
||||
|
||||
protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download";
|
||||
}
|
||||
}
|
@ -106,9 +106,15 @@ namespace osu.Game
|
||||
|
||||
connection.CreateTable<StoreVersion>();
|
||||
|
||||
dependencies.Cache(API = new APIAccess
|
||||
{
|
||||
Username = LocalConfig.Get<string>(OsuSetting.Username),
|
||||
Token = LocalConfig.Get<string>(OsuSetting.Token)
|
||||
});
|
||||
|
||||
dependencies.Cache(RulesetStore = new RulesetStore(connection));
|
||||
dependencies.Cache(FileStore = new FileStore(connection, Host.Storage));
|
||||
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, connection, RulesetStore, Host));
|
||||
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, connection, RulesetStore, API, Host));
|
||||
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, connection, Host, BeatmapManager, RulesetStore));
|
||||
dependencies.Cache(KeyBindingStore = new KeyBindingStore(connection, RulesetStore));
|
||||
dependencies.Cache(new OsuColour());
|
||||
@ -144,18 +150,21 @@ namespace osu.Game
|
||||
Beatmap = new NonNullableBindable<WorkingBeatmap>(defaultBeatmap);
|
||||
BeatmapManager.DefaultBeatmap = defaultBeatmap;
|
||||
|
||||
dependencies.Cache(API = new APIAccess
|
||||
{
|
||||
Username = LocalConfig.Get<string>(OsuSetting.Username),
|
||||
Token = LocalConfig.Get<string>(OsuSetting.Token)
|
||||
});
|
||||
|
||||
Beatmap.ValueChanged += b =>
|
||||
{
|
||||
var trackLoaded = lastBeatmap?.TrackLoaded ?? false;
|
||||
|
||||
// compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)
|
||||
if (lastBeatmap?.Track != b.Track)
|
||||
if (!trackLoaded || lastBeatmap?.Track != b.Track)
|
||||
{
|
||||
lastBeatmap?.Track?.Dispose();
|
||||
if (trackLoaded)
|
||||
{
|
||||
Debug.Assert(lastBeatmap != null);
|
||||
Debug.Assert(lastBeatmap.Track != null);
|
||||
|
||||
lastBeatmap.DisposeTrack();
|
||||
}
|
||||
|
||||
Audio.Track.AddItem(b.Track);
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,6 @@
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -20,8 +18,8 @@ using osu.Framework.Input;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps.IO;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Online.API.Requests;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
@ -97,6 +95,11 @@ namespace osu.Game.Overlays.Direct
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
var downloadRequest = beatmaps.GetExistingDownload(SetInfo);
|
||||
|
||||
if (downloadRequest != null)
|
||||
attachDownload(downloadRequest);
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
@ -115,23 +118,8 @@ namespace osu.Game.Overlays.Direct
|
||||
base.OnHoverLost(state);
|
||||
}
|
||||
|
||||
// this should eventually be moved to a more central place, like BeatmapManager.
|
||||
private DownloadBeatmapSetRequest downloadRequest;
|
||||
|
||||
protected void StartDownload()
|
||||
{
|
||||
if (api == null) return;
|
||||
|
||||
// we already have an active download running.
|
||||
if (downloadRequest != null)
|
||||
{
|
||||
content.MoveToX(-5, 50, Easing.OutSine).Then()
|
||||
.MoveToX(5, 100, Easing.InOutSine).Then()
|
||||
.MoveToX(-5, 100, Easing.InOutSine).Then()
|
||||
.MoveToX(0, 50, Easing.InSine).Then();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!api.LocalUser.Value.IsSupporter)
|
||||
{
|
||||
notifications.Post(new SimpleNotification
|
||||
@ -142,72 +130,43 @@ namespace osu.Game.Overlays.Direct
|
||||
return;
|
||||
}
|
||||
|
||||
if (beatmaps.GetExistingDownload(SetInfo) != null)
|
||||
{
|
||||
// we already have an active download running.
|
||||
content.MoveToX(-5, 50, Easing.OutSine).Then()
|
||||
.MoveToX(5, 100, Easing.InOutSine).Then()
|
||||
.MoveToX(-5, 100, Easing.InOutSine).Then()
|
||||
.MoveToX(0, 50, Easing.InSine).Then();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var request = beatmaps.Download(SetInfo);
|
||||
|
||||
attachDownload(request);
|
||||
}
|
||||
|
||||
private void attachDownload(DownloadBeatmapSetRequest request)
|
||||
{
|
||||
progressBar.FadeIn(400, Easing.OutQuint);
|
||||
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
|
||||
|
||||
progressBar.Current.Value = 0;
|
||||
|
||||
ProgressNotification downloadNotification = new ProgressNotification
|
||||
{
|
||||
Text = $"Downloading {SetInfo.Metadata.Artist} - {SetInfo.Metadata.Title}",
|
||||
};
|
||||
|
||||
downloadRequest = new DownloadBeatmapSetRequest(SetInfo);
|
||||
downloadRequest.Failure += e =>
|
||||
request.Failure += e =>
|
||||
{
|
||||
progressBar.Current.Value = 0;
|
||||
progressBar.FadeOut(500);
|
||||
downloadNotification.State = ProgressNotificationState.Completed;
|
||||
Logger.Error(e, "Failed to get beatmap download information");
|
||||
|
||||
downloadRequest = null;
|
||||
};
|
||||
|
||||
downloadRequest.Progress += (current, total) =>
|
||||
{
|
||||
float progress = (float)current / total;
|
||||
request.DownloadProgressed += progress => progressBar.Current.Value = progress;
|
||||
|
||||
progressBar.Current.Value = progress;
|
||||
|
||||
downloadNotification.State = ProgressNotificationState.Active;
|
||||
downloadNotification.Progress = progress;
|
||||
};
|
||||
|
||||
downloadRequest.Success += data =>
|
||||
request.Success += data =>
|
||||
{
|
||||
progressBar.Current.Value = 1;
|
||||
progressBar.FadeOut(500);
|
||||
|
||||
downloadNotification.State = ProgressNotificationState.Completed;
|
||||
|
||||
using (var stream = new MemoryStream(data))
|
||||
using (var archive = new OszArchiveReader(stream))
|
||||
beatmaps.Import(archive);
|
||||
};
|
||||
|
||||
downloadNotification.CancelRequested += () =>
|
||||
{
|
||||
downloadRequest.Cancel();
|
||||
downloadRequest = null;
|
||||
return true;
|
||||
};
|
||||
|
||||
notifications.Post(downloadNotification);
|
||||
|
||||
// don't run in the main api queue as this is a long-running task.
|
||||
Task.Run(() => downloadRequest.Perform(api));
|
||||
}
|
||||
|
||||
public class DownloadBeatmapSetRequest : APIDownloadRequest
|
||||
{
|
||||
private readonly BeatmapSetInfo beatmapSet;
|
||||
|
||||
public DownloadBeatmapSetRequest(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
this.beatmapSet = beatmapSet;
|
||||
}
|
||||
|
||||
protected override string Target => $@"beatmapsets/{beatmapSet.OnlineBeatmapSetID}/download";
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
@ -27,6 +27,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
private APIAccess api;
|
||||
private RulesetStore rulesets;
|
||||
private BeatmapManager beatmaps;
|
||||
|
||||
private readonly FillFlowContainer resultCountsContainer;
|
||||
private readonly OsuSpriteText resultCountsText;
|
||||
@ -46,9 +47,26 @@ namespace osu.Game.Overlays
|
||||
set
|
||||
{
|
||||
if (beatmapSets?.Equals(value) ?? false) return;
|
||||
|
||||
beatmapSets = value;
|
||||
|
||||
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
|
||||
if (beatmapSets == null) return;
|
||||
|
||||
var artists = new List<string>();
|
||||
var songs = new List<string>();
|
||||
var tags = new List<string>();
|
||||
foreach (var s in beatmapSets)
|
||||
{
|
||||
artists.Add(s.Metadata.Artist);
|
||||
songs.Add(s.Metadata.Title);
|
||||
tags.AddRange(s.Metadata.Tags.Split(' '));
|
||||
}
|
||||
|
||||
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
|
||||
|
||||
if (beatmapSets.Any() && panels == null)
|
||||
// real use case? currently only seems to be for test case
|
||||
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,6 +165,8 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
this.api = api;
|
||||
this.rulesets = rulesets;
|
||||
this.beatmaps = beatmaps;
|
||||
|
||||
resultCountsContainer.Colour = colours.Yellow;
|
||||
|
||||
beatmaps.BeatmapSetAdded += setAdded;
|
||||
@ -156,6 +176,7 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
// if a new map was imported, we should remove it from search results (download completed etc.)
|
||||
panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire();
|
||||
BeatmapSets = BeatmapSets?.Where(b => b.OnlineBeatmapSetID != set.OnlineBeatmapSetID);
|
||||
}
|
||||
|
||||
private void updateResultCounts()
|
||||
@ -237,20 +258,11 @@ namespace osu.Game.Overlays
|
||||
|
||||
getSetsRequest.Success += r =>
|
||||
{
|
||||
BeatmapSets = r?.Select(response => response.ToBeatmapSet(rulesets));
|
||||
if (BeatmapSets == null) return;
|
||||
BeatmapSets = r?.
|
||||
Select(response => response.ToBeatmapSet(rulesets)).
|
||||
Where(b => beatmaps.QueryBeatmapSet(q => q.OnlineBeatmapSetID == b.OnlineBeatmapSetID) == null);
|
||||
|
||||
var artists = new List<string>();
|
||||
var songs = new List<string>();
|
||||
var tags = new List<string>();
|
||||
foreach (var s in BeatmapSets)
|
||||
{
|
||||
artists.Add(s.Metadata.Artist);
|
||||
songs.Add(s.Metadata.Title);
|
||||
tags.AddRange(s.Metadata.Tags.Split(' '));
|
||||
}
|
||||
|
||||
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
|
||||
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
|
||||
};
|
||||
|
||||
api.Queue(getSetsRequest);
|
||||
|
@ -302,8 +302,8 @@ namespace osu.Game.Overlays
|
||||
else
|
||||
{
|
||||
//figure out the best direction based on order in playlist.
|
||||
var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo.ID).Count();
|
||||
var next = beatmap == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmap.BeatmapSetInfo.ID).Count();
|
||||
var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count();
|
||||
var next = beatmap == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmap.BeatmapSetInfo?.ID).Count();
|
||||
|
||||
direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ namespace osu.Game.Overlays.Settings
|
||||
backgroundBox = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Blending = BlendingMode.Additive,
|
||||
Colour = OsuColour.Gray(60),
|
||||
Alpha = 0,
|
||||
},
|
||||
|
@ -85,7 +85,7 @@ namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.Gray(80).Opacity(180),
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Blending = BlendingMode.Additive,
|
||||
Alpha = 0,
|
||||
},
|
||||
Flow = new FillFlowContainer
|
||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.Gray(150).Opacity(180),
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Blending = BlendingMode.Additive,
|
||||
Depth = 2,
|
||||
Alpha = 0,
|
||||
});
|
||||
|
@ -16,11 +16,9 @@ namespace osu.Game.Rulesets.Judgements
|
||||
/// <summary>
|
||||
/// A drawable object which visualises the hit result of a <see cref="Judgements.Judgement"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TJudgement">The type of judgement to visualise.</typeparam>
|
||||
public class DrawableJudgement<TJudgement> : Container
|
||||
where TJudgement : Judgement
|
||||
public class DrawableJudgement : Container
|
||||
{
|
||||
protected readonly TJudgement Judgement;
|
||||
protected readonly Judgement Judgement;
|
||||
|
||||
protected readonly SpriteText JudgementText;
|
||||
|
||||
@ -28,21 +26,19 @@ namespace osu.Game.Rulesets.Judgements
|
||||
/// Creates a drawable which visualises a <see cref="Judgements.Judgement"/>.
|
||||
/// </summary>
|
||||
/// <param name="judgement">The judgement to visualise.</param>
|
||||
public DrawableJudgement(TJudgement judgement)
|
||||
public DrawableJudgement(Judgement judgement)
|
||||
{
|
||||
Judgement = judgement;
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
string resultString = judgement.Result == HitResult.Hit ? judgement.ResultString : judgement.Result.GetDescription();
|
||||
|
||||
Children = new[]
|
||||
{
|
||||
JudgementText = new OsuSpriteText
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Text = resultString.ToUpper(),
|
||||
Text = judgement.Result.GetDescription().ToUpper(),
|
||||
Font = @"Venera",
|
||||
TextSize = 16
|
||||
}
|
||||
@ -68,6 +64,8 @@ namespace osu.Game.Rulesets.Judgements
|
||||
|
||||
switch (Judgement.Result)
|
||||
{
|
||||
case HitResult.None:
|
||||
break;
|
||||
case HitResult.Miss:
|
||||
this.ScaleTo(1.6f);
|
||||
this.ScaleTo(1, 100, Easing.In);
|
||||
@ -77,7 +75,7 @@ namespace osu.Game.Rulesets.Judgements
|
||||
|
||||
this.Delay(600).FadeOut(200);
|
||||
break;
|
||||
case HitResult.Hit:
|
||||
default:
|
||||
this.ScaleTo(0.9f);
|
||||
this.ScaleTo(1, 500, Easing.OutElastic);
|
||||
|
||||
|
@ -1,19 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Judgements
|
||||
{
|
||||
/// <summary>
|
||||
/// Inidicates that the judgement this is attached to is a partial judgement and the scoring value may change.
|
||||
/// </summary>
|
||||
public interface IPartialJudgement
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that this partial judgement has changed and requires reprocessing.
|
||||
/// <para>
|
||||
/// This is set to false once the judgement has been re-processed.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
bool Changed { get; set; }
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Judgements
|
||||
{
|
||||
public abstract class Judgement
|
||||
public class Judgement
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether this judgement is the result of a hit or a miss.
|
||||
@ -13,20 +13,35 @@ namespace osu.Game.Rulesets.Judgements
|
||||
public HitResult Result;
|
||||
|
||||
/// <summary>
|
||||
/// The offset at which this judgement occurred.
|
||||
/// The maximum <see cref="HitResult"/> that can be achieved.
|
||||
/// </summary>
|
||||
public double TimeOffset;
|
||||
public virtual HitResult MaxResult => HitResult.Perfect;
|
||||
|
||||
public bool IsHit => Result > HitResult.Miss;
|
||||
|
||||
/// <summary>
|
||||
/// The offset from a perfect hit at which this judgement occurred.
|
||||
/// Populated when added via <see cref="DrawableHitObject{TObject}.AddJudgement"/>.
|
||||
/// </summary>
|
||||
public double TimeOffset { get; internal set; }
|
||||
|
||||
public virtual bool AffectsCombo => true;
|
||||
|
||||
/// <summary>
|
||||
/// The string representation for the result achieved.
|
||||
/// The numeric representation for the result achieved.
|
||||
/// </summary>
|
||||
public abstract string ResultString { get; }
|
||||
public int NumericResult => NumericResultFor(Result);
|
||||
|
||||
/// <summary>
|
||||
/// The string representation for the max result achievable.
|
||||
/// The numeric representation for the maximum achievable result.
|
||||
/// </summary>
|
||||
public abstract string MaxResultString { get; }
|
||||
public int MaxNumericResult => NumericResultFor(MaxResult);
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="HitResult"/> to a numeric score representation.
|
||||
/// </summary>
|
||||
/// <param name="result">The value to convert.</param>
|
||||
/// <returns>The number.</returns>
|
||||
protected virtual int NumericResultFor(HitResult result) => result > HitResult.Miss ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
protected abstract Score CreateReplayScore(Beatmap<T> beatmap);
|
||||
|
||||
public void ApplyToRulesetContainer(RulesetContainer<T> rulesetContainer)
|
||||
public virtual void ApplyToRulesetContainer(RulesetContainer<T> rulesetContainer)
|
||||
{
|
||||
rulesetContainer.SetReplay(CreateReplayScore(rulesetContainer.Beatmap)?.Replay);
|
||||
}
|
||||
|
@ -12,17 +12,18 @@ using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Audio;
|
||||
using System.Linq;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Drawables
|
||||
{
|
||||
public abstract class DrawableHitObject : Container
|
||||
public abstract class DrawableHitObject : Container, IHasAccentColour
|
||||
{
|
||||
public readonly HitObject HitObject;
|
||||
|
||||
/// <summary>
|
||||
/// The colour used for various elements of this DrawableHitObject.
|
||||
/// </summary>
|
||||
public virtual Color4 AccentColour { get; set; }
|
||||
public virtual Color4 AccentColour { get; set; } = Color4.Gray;
|
||||
|
||||
protected DrawableHitObject(HitObject hitObject)
|
||||
{
|
||||
@ -33,32 +34,29 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
public abstract class DrawableHitObject<TObject> : DrawableHitObject
|
||||
where TObject : HitObject
|
||||
{
|
||||
public event Action<DrawableHitObject, Judgement> OnJudgement;
|
||||
|
||||
public new readonly TObject HitObject;
|
||||
|
||||
protected DrawableHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class DrawableHitObject<TObject, TJudgement> : DrawableHitObject<TObject>
|
||||
where TObject : HitObject
|
||||
where TJudgement : Judgement
|
||||
{
|
||||
public event Action<DrawableHitObject<TObject, TJudgement>> OnJudgement;
|
||||
|
||||
public override bool HandleInput => Interactive;
|
||||
|
||||
public bool Interactive = true;
|
||||
|
||||
public TJudgement Judgement;
|
||||
/// <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;
|
||||
|
||||
protected List<SampleChannel> Samples = new List<SampleChannel>();
|
||||
|
||||
protected DrawableHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
|
||||
Depth = (float)hitObject.StartTime;
|
||||
}
|
||||
|
||||
private ArmedState state;
|
||||
@ -95,69 +93,80 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
UpdateState(State);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this hit object and all of its nested hit objects have been judged.
|
||||
/// </summary>
|
||||
public bool Judged => (Judgement?.Result ?? HitResult.None) != HitResult.None && (NestedHitObjects?.All(h => h.Judged) ?? true);
|
||||
private bool hasJudgementResult;
|
||||
private bool judgementOccurred;
|
||||
|
||||
/// <summary>
|
||||
/// Process a hit of this hitobject. Carries out judgement.
|
||||
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been judged.
|
||||
/// </summary>
|
||||
/// <returns>Whether a hit was processed.</returns>
|
||||
protected bool UpdateJudgement(bool userTriggered)
|
||||
public virtual bool AllJudged => (!ProvidesJudgement || hasJudgementResult) && (NestedHitObjects?.All(h => h.AllJudged) ?? true);
|
||||
|
||||
/// <summary>
|
||||
/// Notifies that a new judgement has occurred for this <see cref="DrawableHitObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="judgement">The <see cref="Judgement"/>.</param>
|
||||
protected void AddJudgement(Judgement judgement)
|
||||
{
|
||||
if (Judgement == null)
|
||||
return false;
|
||||
hasJudgementResult = judgement.Result >= HitResult.Miss;
|
||||
judgementOccurred = true;
|
||||
|
||||
var partial = Judgement as IPartialJudgement;
|
||||
// Ensure that the judgement is given a valid time offset, because this may not get set by the caller
|
||||
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
|
||||
judgement.TimeOffset = Time.Current - endTime;
|
||||
|
||||
// Never re-process non-partial hits
|
||||
if (Judgement.Result != HitResult.None && partial == null)
|
||||
return false;
|
||||
judgements.Add(judgement);
|
||||
|
||||
// Update the judgement state
|
||||
double endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
|
||||
Judgement.TimeOffset = Time.Current - endTime;
|
||||
|
||||
// Update the judgement state
|
||||
bool hadResult = Judgement.Result != HitResult.None;
|
||||
CheckJudgement(userTriggered);
|
||||
|
||||
// Don't process judgements with no result
|
||||
if (Judgement.Result == HitResult.None)
|
||||
return false;
|
||||
|
||||
// Don't process judgements that previously had results but the results were unchanged
|
||||
if (hadResult && partial?.Changed != true)
|
||||
return false;
|
||||
|
||||
switch (Judgement.Result)
|
||||
switch (judgement.Result)
|
||||
{
|
||||
default:
|
||||
State = ArmedState.Hit;
|
||||
case HitResult.None:
|
||||
break;
|
||||
case HitResult.Miss:
|
||||
State = ArmedState.Miss;
|
||||
break;
|
||||
default:
|
||||
State = ArmedState.Hit;
|
||||
break;
|
||||
}
|
||||
|
||||
OnJudgement?.Invoke(this);
|
||||
|
||||
if (partial != null)
|
||||
partial.Changed = false;
|
||||
|
||||
return true;
|
||||
OnJudgement?.Invoke(this, judgement);
|
||||
}
|
||||
|
||||
protected virtual void CheckJudgement(bool userTriggered)
|
||||
/// <summary>
|
||||
/// Processes this <see cref="DrawableHitObject"/>, checking if any judgements have occurred.
|
||||
/// </summary>
|
||||
/// <param name="userTriggered">Whether the user triggered this process.</param>
|
||||
/// <returns>Whether a judgement has occurred from this <see cref="DrawableHitObject"/> or any nested <see cref="DrawableHitObject"/>s.</returns>
|
||||
protected bool UpdateJudgement(bool userTriggered)
|
||||
{
|
||||
judgementOccurred = false;
|
||||
|
||||
if (AllJudged || State != ArmedState.Idle)
|
||||
return false;
|
||||
|
||||
if (NestedHitObjects != null)
|
||||
{
|
||||
foreach (var d in NestedHitObjects)
|
||||
d.CheckJudgement(userTriggered);
|
||||
judgementOccurred |= d.UpdateJudgement(userTriggered);
|
||||
}
|
||||
|
||||
if (!ProvidesJudgement || hasJudgementResult || judgementOccurred)
|
||||
return judgementOccurred;
|
||||
|
||||
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
|
||||
CheckForJudgements(userTriggered, Time.Current - endTime);
|
||||
|
||||
return judgementOccurred;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if any judgements have occurred for this <see cref="DrawableHitObject"/>. This method must construct
|
||||
/// all <see cref="Judgement"/>s and notify of them through <see cref="AddJudgement"/>.
|
||||
/// </summary>
|
||||
/// <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 UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
@ -178,25 +187,20 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
channel.Volume.Value = sample.Volume;
|
||||
Samples.Add(channel);
|
||||
}
|
||||
|
||||
//we may be setting a custom judgement in test cases or what not.
|
||||
if (Judgement == null)
|
||||
Judgement = CreateJudgement();
|
||||
}
|
||||
|
||||
private List<DrawableHitObject<TObject, TJudgement>> nestedHitObjects;
|
||||
protected IEnumerable<DrawableHitObject<TObject, TJudgement>> NestedHitObjects => nestedHitObjects;
|
||||
private List<DrawableHitObject<TObject>> nestedHitObjects;
|
||||
protected IEnumerable<DrawableHitObject<TObject>> NestedHitObjects => nestedHitObjects;
|
||||
|
||||
protected virtual void AddNested(DrawableHitObject<TObject, TJudgement> h)
|
||||
protected virtual void AddNested(DrawableHitObject<TObject> h)
|
||||
{
|
||||
if (nestedHitObjects == null)
|
||||
nestedHitObjects = new List<DrawableHitObject<TObject, TJudgement>>();
|
||||
nestedHitObjects = new List<DrawableHitObject<TObject>>();
|
||||
|
||||
h.OnJudgement += d => OnJudgement?.Invoke(d);
|
||||
h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j);
|
||||
nestedHitObjects.Add(h);
|
||||
}
|
||||
|
||||
protected abstract TJudgement CreateJudgement();
|
||||
protected abstract void UpdateState(ArmedState state);
|
||||
}
|
||||
}
|
||||
|
@ -3,19 +3,17 @@
|
||||
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Drawables
|
||||
{
|
||||
/// <summary>
|
||||
/// A basic class that overrides <see cref="DrawableHitObject{TObject, TJudgement}"/> and implements <see cref="IScrollingHitObject"/>.
|
||||
/// 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, TJudgement> : DrawableHitObject<TObject, TJudgement>, IScrollingHitObject
|
||||
public abstract class DrawableScrollingHitObject<TObject> : DrawableHitObject<TObject>, IScrollingHitObject
|
||||
where TObject : HitObject
|
||||
where TJudgement : Judgement
|
||||
{
|
||||
public BindableDouble LifetimeOffset { get; } = new BindableDouble();
|
||||
|
||||
@ -57,7 +55,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
set { lifetimeEnd = value; }
|
||||
}
|
||||
|
||||
protected override void AddNested(DrawableHitObject<TObject, TJudgement> h)
|
||||
protected override void AddNested(DrawableHitObject<TObject> h)
|
||||
{
|
||||
var scrollingHitObject = h as IScrollingHitObject;
|
||||
scrollingHitObject?.LifetimeOffset.BindTo(LifetimeOffset);
|
||||
|
@ -10,17 +10,34 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// <summary>
|
||||
/// Indicates that the object has not been judged yet.
|
||||
/// </summary>
|
||||
[Description("")]
|
||||
[Description(@"")]
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the object has been judged as a miss.
|
||||
/// </summary>
|
||||
[Description(@"Miss")]
|
||||
Miss,
|
||||
|
||||
[Description(@"Meh")]
|
||||
Meh,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the object has been judged as a hit.
|
||||
/// Optional judgement.
|
||||
/// </summary>
|
||||
[Description(@"Hit")]
|
||||
Hit,
|
||||
[Description(@"OK")]
|
||||
Ok,
|
||||
|
||||
[Description(@"Good")]
|
||||
Good,
|
||||
|
||||
[Description(@"Great")]
|
||||
Great,
|
||||
|
||||
/// <summary>
|
||||
/// Optional judgement.
|
||||
/// </summary>
|
||||
[Description(@"Perfect")]
|
||||
Perfect,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,11 @@ namespace osu.Game.Rulesets.Objects
|
||||
/// </summary>
|
||||
public SampleInfoList Samples = new SampleInfoList();
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="HitObject"/> is in Kiai time.
|
||||
/// </summary>
|
||||
public bool Kiai { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Applies default values to this HitObject.
|
||||
/// </summary>
|
||||
@ -38,6 +43,9 @@ namespace osu.Game.Rulesets.Objects
|
||||
public virtual void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
SoundControlPoint soundPoint = controlPointInfo.SoundPointAt(StartTime);
|
||||
EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime);
|
||||
|
||||
Kiai |= effectPoint.KiaiMode;
|
||||
|
||||
// Initialize first sample
|
||||
Samples.ForEach(s => initializeSampleInfo(s, soundPoint));
|
||||
|
@ -135,14 +135,13 @@ namespace osu.Game.Rulesets.Scoring
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class ScoreProcessor<TObject, TJudgement> : ScoreProcessor
|
||||
public abstract class ScoreProcessor<TObject> : ScoreProcessor
|
||||
where TObject : HitObject
|
||||
where TJudgement : Judgement
|
||||
{
|
||||
/// <summary>
|
||||
/// All judgements held by this ScoreProcessor.
|
||||
/// </summary>
|
||||
protected readonly List<TJudgement> Judgements = new List<TJudgement>();
|
||||
protected readonly List<Judgement> Judgements = new List<Judgement>();
|
||||
|
||||
public override bool HasFailed => Health.Value == Health.MinValue;
|
||||
|
||||
@ -150,7 +149,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
{
|
||||
}
|
||||
|
||||
protected ScoreProcessor(RulesetContainer<TObject, TJudgement> rulesetContainer)
|
||||
protected ScoreProcessor(RulesetContainer<TObject> rulesetContainer)
|
||||
{
|
||||
Judgements.Capacity = rulesetContainer.Beatmap.HitObjects.Count;
|
||||
|
||||
@ -171,7 +170,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
/// Adds a judgement to this ScoreProcessor.
|
||||
/// </summary>
|
||||
/// <param name="judgement">The judgement to add.</param>
|
||||
protected void AddJudgement(TJudgement judgement)
|
||||
protected void AddJudgement(Judgement judgement)
|
||||
{
|
||||
bool exists = Judgements.Contains(judgement);
|
||||
|
||||
@ -181,10 +180,12 @@ namespace osu.Game.Rulesets.Scoring
|
||||
{
|
||||
switch (judgement.Result)
|
||||
{
|
||||
case HitResult.None:
|
||||
break;
|
||||
case HitResult.Miss:
|
||||
Combo.Value = 0;
|
||||
break;
|
||||
case HitResult.Hit:
|
||||
default:
|
||||
Combo.Value++;
|
||||
break;
|
||||
}
|
||||
@ -195,8 +196,6 @@ namespace osu.Game.Rulesets.Scoring
|
||||
|
||||
NotifyNewJudgement(judgement);
|
||||
}
|
||||
else
|
||||
OnJudgementChanged(judgement);
|
||||
|
||||
UpdateFailed();
|
||||
}
|
||||
@ -210,20 +209,8 @@ namespace osu.Game.Rulesets.Scoring
|
||||
|
||||
/// <summary>
|
||||
/// Updates any values that need post-processing. Invoked when a new judgement has occurred.
|
||||
/// <para>
|
||||
/// This is not triggered when existing judgements are changed - for that see <see cref="OnJudgementChanged(TJudgement)"/>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="judgement">The judgement that triggered this calculation.</param>
|
||||
protected abstract void OnNewJudgement(TJudgement judgement);
|
||||
|
||||
/// <summary>
|
||||
/// Updates any values that need post-processing. Invoked when an existing judgement has changed.
|
||||
/// <para>
|
||||
/// This is not triggered when a new judgement has occurred - for that see <see cref="OnNewJudgement(TJudgement)"/>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="judgement">The judgement that triggered this calculation.</param>
|
||||
protected virtual void OnJudgementChanged(TJudgement judgement) { }
|
||||
protected abstract void OnNewJudgement(Judgement judgement);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
@ -14,9 +13,7 @@ using System.Linq;
|
||||
|
||||
namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
public abstract class Playfield<TObject, TJudgement> : Container
|
||||
where TObject : HitObject
|
||||
where TJudgement : Judgement
|
||||
public abstract class Playfield : Container
|
||||
{
|
||||
/// <summary>
|
||||
/// The HitObjects contained in this Playfield.
|
||||
@ -70,7 +67,7 @@ namespace osu.Game.Rulesets.UI
|
||||
public override Axes RelativeSizeAxes
|
||||
{
|
||||
get { return Axes.Both; }
|
||||
set { throw new InvalidOperationException($@"{nameof(Playfield<TObject, TJudgement>)}'s {nameof(RelativeSizeAxes)} should never be changed from {Axes.Both}"); }
|
||||
set { throw new InvalidOperationException($@"{nameof(Playfield)}'s {nameof(RelativeSizeAxes)} should never be changed from {Axes.Both}"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -82,19 +79,20 @@ namespace osu.Game.Rulesets.UI
|
||||
/// Adds a DrawableHitObject to this Playfield.
|
||||
/// </summary>
|
||||
/// <param name="h">The DrawableHitObject to add.</param>
|
||||
public virtual void Add(DrawableHitObject<TObject, TJudgement> h) => HitObjects.Add(h);
|
||||
public virtual void Add(DrawableHitObject h) => HitObjects.Add(h);
|
||||
|
||||
/// <summary>
|
||||
/// Remove a DrawableHitObject from this Playfield.
|
||||
/// </summary>
|
||||
/// <param name="h">The DrawableHitObject to remove.</param>
|
||||
public virtual void Remove(DrawableHitObject<TObject, TJudgement> h) => HitObjects.Remove(h);
|
||||
public virtual void Remove(DrawableHitObject h) => HitObjects.Remove(h);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an object's Judgement is updated.
|
||||
/// Triggered when a new <see cref="Judgement"/> occurs on a <see cref="DrawableHitObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="judgedObject">The object that Judgement has been updated for.</param>
|
||||
public virtual void OnJudgement(DrawableHitObject<TObject, TJudgement> judgedObject) { }
|
||||
/// <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
|
||||
{
|
||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.UI
|
||||
/// <summary>
|
||||
/// Base RulesetContainer. Doesn't hold objects.
|
||||
/// <para>
|
||||
/// Should not be derived - derive <see cref="RulesetContainer{TObject,TJudgement}"/> instead.
|
||||
/// Should not be derived - derive <see cref="RulesetContainer{TObject}"/> instead.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public abstract class RulesetContainer : Container
|
||||
@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.UI
|
||||
public event Action OnAllJudged;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to apply adjustments to the child <see cref="Playfield{TObject,TJudgement}"/> based on our own size.
|
||||
/// Whether to apply adjustments to the child <see cref="Playfield"/> based on our own size.
|
||||
/// </summary>
|
||||
public bool AspectAdjust = true;
|
||||
|
||||
@ -77,13 +77,6 @@ namespace osu.Game.Rulesets.UI
|
||||
Ruleset = ruleset;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
KeyBindingInputManager = CreateInputManager();
|
||||
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether all HitObjects have been judged, and invokes OnAllJudged.
|
||||
/// </summary>
|
||||
@ -123,13 +116,15 @@ namespace osu.Game.Rulesets.UI
|
||||
/// RulesetContainer that applies conversion to Beatmaps. Does not contain a Playfield
|
||||
/// and does not load drawable hit objects.
|
||||
/// <para>
|
||||
/// Should not be derived - derive <see cref="RulesetContainer{TObject,TJudgement}"/> instead.
|
||||
/// Should not be derived - derive <see cref="RulesetContainer{TObject}"/> instead.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <typeparam name="TObject">The type of HitObject contained by this RulesetContainer.</typeparam>
|
||||
public abstract class RulesetContainer<TObject> : RulesetContainer
|
||||
where TObject : HitObject
|
||||
{
|
||||
public event Action<Judgement> OnJudgement;
|
||||
|
||||
/// <summary>
|
||||
/// The Beatmap
|
||||
/// </summary>
|
||||
@ -155,6 +150,20 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
protected readonly bool IsForCurrentRuleset;
|
||||
|
||||
public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor;
|
||||
|
||||
protected override bool AllObjectsJudged => drawableObjects.All(h => h.AllJudged);
|
||||
|
||||
/// <summary>
|
||||
/// The playfield.
|
||||
/// </summary>
|
||||
public Playfield Playfield { get; private set; }
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
private Container content;
|
||||
|
||||
private readonly List<DrawableHitObject<TObject>> drawableObjects = new List<DrawableHitObject<TObject>>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether to assume the beatmap passed into this <see cref="RulesetContainer{TObject}"/> is for the current ruleset.
|
||||
/// Creates a hit renderer for a beatmap.
|
||||
@ -162,7 +171,7 @@ namespace osu.Game.Rulesets.UI
|
||||
/// <param name="ruleset">The ruleset being repesented.</param>
|
||||
/// <param name="workingBeatmap">The beatmap to create the hit renderer for.</param>
|
||||
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
|
||||
internal RulesetContainer(Ruleset ruleset, WorkingBeatmap workingBeatmap, bool isForCurrentRuleset)
|
||||
protected RulesetContainer(Ruleset ruleset, WorkingBeatmap workingBeatmap, bool isForCurrentRuleset)
|
||||
: base(ruleset)
|
||||
{
|
||||
Debug.Assert(workingBeatmap != null, "RulesetContainer initialized with a null beatmap.");
|
||||
@ -194,78 +203,13 @@ namespace osu.Game.Rulesets.UI
|
||||
// Post-process the beatmap
|
||||
processor.PostProcess(Beatmap);
|
||||
|
||||
KeyBindingInputManager = CreateInputManager();
|
||||
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
|
||||
|
||||
// Add mods, should always be the last thing applied to give full control to mods
|
||||
applyMods(Mods);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the active mods to this RulesetContainer.
|
||||
/// </summary>
|
||||
/// <param name="mods"></param>
|
||||
private void applyMods(IEnumerable<Mod> mods)
|
||||
{
|
||||
if (mods == null)
|
||||
return;
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableMod<TObject>>())
|
||||
mod.ApplyToRulesetContainer(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a processor to perform post-processing operations
|
||||
/// on HitObjects in converted Beatmaps.
|
||||
/// </summary>
|
||||
/// <returns>The Beatmap processor.</returns>
|
||||
protected virtual BeatmapProcessor<TObject> CreateBeatmapProcessor() => new BeatmapProcessor<TObject>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a converter to convert Beatmap to a specific mode.
|
||||
/// </summary>
|
||||
/// <returns>The Beatmap converter.</returns>
|
||||
protected abstract BeatmapConverter<TObject> CreateBeatmapConverter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A derivable RulesetContainer that manages the Playfield and HitObjects.
|
||||
/// </summary>
|
||||
/// <typeparam name="TObject">The type of HitObject contained by this RulesetContainer.</typeparam>
|
||||
/// <typeparam name="TJudgement">The type of Judgement of DrawableHitObjects contained by this RulesetContainer.</typeparam>
|
||||
public abstract class RulesetContainer<TObject, TJudgement> : RulesetContainer<TObject>
|
||||
where TObject : HitObject
|
||||
where TJudgement : Judgement
|
||||
{
|
||||
public event Action<TJudgement> OnJudgement;
|
||||
|
||||
public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor;
|
||||
|
||||
/// <summary>
|
||||
/// All the converted hit objects contained by this hit renderer.
|
||||
/// </summary>
|
||||
public new IEnumerable<TObject> Objects => Beatmap.HitObjects;
|
||||
|
||||
protected override bool AllObjectsJudged => drawableObjects.All(h => h.Judged);
|
||||
|
||||
/// <summary>
|
||||
/// The playfield.
|
||||
/// </summary>
|
||||
public Playfield<TObject, TJudgement> Playfield { get; private set; }
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
private Container content;
|
||||
|
||||
private readonly List<DrawableHitObject<TObject, TJudgement>> drawableObjects = new List<DrawableHitObject<TObject, TJudgement>>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a hit renderer for a beatmap.
|
||||
/// </summary>
|
||||
/// <param name="ruleset">The ruleset being repesented.</param>
|
||||
/// <param name="beatmap">The beatmap to create the hit renderer for.</param>
|
||||
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
|
||||
protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||
: base(ruleset, beatmap, isForCurrentRuleset)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
@ -280,6 +224,19 @@ namespace osu.Game.Rulesets.UI
|
||||
loadObjects();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the active mods to this RulesetContainer.
|
||||
/// </summary>
|
||||
/// <param name="mods"></param>
|
||||
private void applyMods(IEnumerable<Mod> mods)
|
||||
{
|
||||
if (mods == null)
|
||||
return;
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableMod<TObject>>())
|
||||
mod.ApplyToRulesetContainer(this);
|
||||
}
|
||||
|
||||
public override void SetReplay(Replay replay)
|
||||
{
|
||||
base.SetReplay(replay);
|
||||
@ -302,7 +259,12 @@ namespace osu.Game.Rulesets.UI
|
||||
if (drawableObject == null)
|
||||
continue;
|
||||
|
||||
drawableObject.OnJudgement += onJudgement;
|
||||
drawableObject.OnJudgement += (d, j) =>
|
||||
{
|
||||
Playfield.OnJudgement(d, j);
|
||||
OnJudgement?.Invoke(j);
|
||||
CheckAllJudged();
|
||||
};
|
||||
|
||||
drawableObjects.Add(drawableObject);
|
||||
Playfield.Add(drawableObject);
|
||||
@ -319,36 +281,36 @@ namespace osu.Game.Rulesets.UI
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In some cases we want to apply changes to the relative size of our contained <see cref="Playfield{TObject, TJudgement}"/> based on custom conditions.
|
||||
/// Creates a processor to perform post-processing operations
|
||||
/// on HitObjects in converted Beatmaps.
|
||||
/// </summary>
|
||||
/// <returns>The Beatmap processor.</returns>
|
||||
protected virtual BeatmapProcessor<TObject> CreateBeatmapProcessor() => new BeatmapProcessor<TObject>();
|
||||
|
||||
/// <summary>
|
||||
/// In some cases we want to apply changes to the relative size of our contained <see cref="Playfield"/> based on custom conditions.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f); //a sane default
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an object's Judgement is updated.
|
||||
/// Creates a converter to convert Beatmap to a specific mode.
|
||||
/// </summary>
|
||||
/// <param name="judgedObject">The object that Judgement has been updated for.</param>
|
||||
private void onJudgement(DrawableHitObject<TObject, TJudgement> judgedObject)
|
||||
{
|
||||
Playfield.OnJudgement(judgedObject);
|
||||
|
||||
OnJudgement?.Invoke(judgedObject.Judgement);
|
||||
|
||||
CheckAllJudged();
|
||||
}
|
||||
/// <returns>The Beatmap converter.</returns>
|
||||
protected abstract BeatmapConverter<TObject> CreateBeatmapConverter();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a DrawableHitObject from a HitObject.
|
||||
/// </summary>
|
||||
/// <param name="h">The HitObject to make drawable.</param>
|
||||
/// <returns>The DrawableHitObject.</returns>
|
||||
protected abstract DrawableHitObject<TObject, TJudgement> GetVisualRepresentation(TObject h);
|
||||
protected abstract DrawableHitObject<TObject> GetVisualRepresentation(TObject h);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Playfield.
|
||||
/// </summary>
|
||||
/// <returns>The Playfield.</returns>
|
||||
protected abstract Playfield<TObject, TJudgement> CreatePlayfield();
|
||||
protected abstract Playfield CreatePlayfield();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -356,11 +318,9 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
/// <typeparam name="TPlayfield">The type of Playfield contained by this RulesetContainer.</typeparam>
|
||||
/// <typeparam name="TObject">The type of HitObject contained by this RulesetContainer.</typeparam>
|
||||
/// <typeparam name="TJudgement">The type of Judgement of DrawableHitObjects contained by this RulesetContainer.</typeparam>
|
||||
public abstract class RulesetContainer<TPlayfield, TObject, TJudgement> : RulesetContainer<TObject, TJudgement>
|
||||
public abstract class RulesetContainer<TPlayfield, TObject> : RulesetContainer<TObject>
|
||||
where TObject : HitObject
|
||||
where TJudgement : Judgement
|
||||
where TPlayfield : Playfield<TObject, TJudgement>
|
||||
where TPlayfield : Playfield
|
||||
{
|
||||
/// <summary>
|
||||
/// The playfield.
|
||||
|
@ -11,19 +11,15 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of <see cref="Playfield{TObject, TJudgement}"/> specialized towards scrolling <see cref="DrawableHitObject"/>s.
|
||||
/// A type of <see cref="Playfield"/> specialized towards scrolling <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
public class ScrollingPlayfield<TObject, TJudgement> : Playfield<TObject, TJudgement>
|
||||
where TObject : HitObject
|
||||
where TJudgement : Judgement
|
||||
public class ScrollingPlayfield : Playfield
|
||||
{
|
||||
/// <summary>
|
||||
/// The default span of time visible by the length of the scrolling axes.
|
||||
@ -65,7 +61,7 @@ namespace osu.Game.Rulesets.UI
|
||||
public new readonly ScrollingHitObjectContainer HitObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ScrollingPlayfield{TObject, TJudgement}"/>.
|
||||
/// 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>
|
||||
@ -77,21 +73,21 @@ namespace osu.Game.Rulesets.UI
|
||||
HitObjects.Reversed.BindTo(Reversed);
|
||||
}
|
||||
|
||||
private List<ScrollingPlayfield<TObject, TJudgement>> nestedPlayfields;
|
||||
private List<ScrollingPlayfield> nestedPlayfields;
|
||||
/// <summary>
|
||||
/// All the <see cref="ScrollingPlayfield{TObject, TJudgement}"/>s nested inside this playfield.
|
||||
/// All the <see cref="ScrollingPlayfield"/>s nested inside this playfield.
|
||||
/// </summary>
|
||||
public IEnumerable<ScrollingPlayfield<TObject, TJudgement>> NestedPlayfields => nestedPlayfields;
|
||||
public IEnumerable<ScrollingPlayfield> NestedPlayfields => nestedPlayfields;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="ScrollingPlayfield{TObject, TJudgement}"/> to this playfield. The nested <see cref="ScrollingPlayfield{TObject, TJudgement}"/>
|
||||
/// 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{TObject, TJudgement}"/> to add.</param>
|
||||
protected void AddNested(ScrollingPlayfield<TObject, TJudgement> otherPlayfield)
|
||||
/// <param name="otherPlayfield">The <see cref="ScrollingPlayfield"/> to add.</param>
|
||||
protected void AddNested(ScrollingPlayfield otherPlayfield)
|
||||
{
|
||||
if (nestedPlayfields == null)
|
||||
nestedPlayfields = new List<ScrollingPlayfield<TObject, TJudgement>>();
|
||||
nestedPlayfields = new List<ScrollingPlayfield>();
|
||||
|
||||
nestedPlayfields.Add(otherPlayfield);
|
||||
}
|
||||
@ -119,7 +115,7 @@ namespace osu.Game.Rulesets.UI
|
||||
this.TransformTo(this.PopulateTransform(new TransformVisibleTimeRange(), newTimeRange, duration, easing));
|
||||
}
|
||||
|
||||
private class TransformVisibleTimeRange : Transform<double, ScrollingPlayfield<TObject, TJudgement>>
|
||||
private class TransformVisibleTimeRange : Transform<double, ScrollingPlayfield>
|
||||
{
|
||||
private double valueAt(double time)
|
||||
{
|
||||
@ -131,8 +127,8 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
public override string TargetMember => "VisibleTimeRange.Value";
|
||||
|
||||
protected override void Apply(ScrollingPlayfield<TObject, TJudgement> d, double time) => d.VisibleTimeRange.Value = valueAt(time);
|
||||
protected override void ReadIntoStartValue(ScrollingPlayfield<TObject, TJudgement> d) => StartValue = d.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>
|
||||
|
@ -9,7 +9,6 @@ using osu.Framework.Lists;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.IO.Serialization;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
@ -17,17 +16,16 @@ using osu.Game.Rulesets.Timing;
|
||||
namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of <see cref="RulesetContainer{TPlayfield,TObject,TJudgement}"/> that supports a <see cref="ScrollingPlayfield{TObject, TJudgement}"/>.
|
||||
/// <see cref="HitObject"/>s inside this <see cref="RulesetContainer{TPlayfield,TObject,TJudgement}"/> will scroll within the playfield.
|
||||
/// A type of <see cref="RulesetContainer{TPlayfield,TObject}"/> that supports a <see cref="ScrollingPlayfield"/>.
|
||||
/// <see cref="HitObject"/>s inside this <see cref="RulesetContainer{TPlayfield,TObject}"/> will scroll within the playfield.
|
||||
/// </summary>
|
||||
public abstract class ScrollingRulesetContainer<TPlayfield, TObject, TJudgement> : RulesetContainer<TPlayfield, TObject, TJudgement>
|
||||
public abstract class ScrollingRulesetContainer<TPlayfield, TObject> : RulesetContainer<TPlayfield, TObject>
|
||||
where TObject : HitObject
|
||||
where TJudgement : Judgement
|
||||
where TPlayfield : ScrollingPlayfield<TObject, TJudgement>
|
||||
where TPlayfield : ScrollingPlayfield
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the default <see cref="MultiplierControlPoint"/>s that adjust the scrolling rate of <see cref="HitObject"/>s
|
||||
/// inside this <see cref="RulesetContainer{TPlayfield,TObject,TJudgement}"/>.
|
||||
/// inside this <see cref="RulesetContainer{TPlayfield,TObject}"/>.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected readonly SortedList<MultiplierControlPoint> DefaultControlPoints = new SortedList<MultiplierControlPoint>(Comparer<MultiplierControlPoint>.Default);
|
||||
@ -88,7 +86,7 @@ namespace osu.Game.Rulesets.UI
|
||||
DefaultControlPoints.ForEach(c => applySpeedAdjustment(c, Playfield));
|
||||
}
|
||||
|
||||
private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield<TObject, TJudgement> playfield)
|
||||
private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield playfield)
|
||||
{
|
||||
playfield.HitObjects.AddSpeedAdjustment(CreateSpeedAdjustmentContainer(controlPoint));
|
||||
playfield.NestedPlayfields.ForEach(p => applySpeedAdjustment(controlPoint, p));
|
||||
|
@ -83,7 +83,7 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
EdgeSmoothness = new Vector2(1.5f, 0),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Blending = BlendingMode.Additive,
|
||||
Colour = Color4.White,
|
||||
Alpha = 0,
|
||||
},
|
||||
|
@ -52,7 +52,7 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
Alpha = 0,
|
||||
Triangles = false,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Blending = BlendingMode.Additive,
|
||||
Interactive = false,
|
||||
Colour = Color4.DarkGray,
|
||||
Ripple = false
|
||||
|
@ -73,7 +73,7 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
texture = Texture.WhitePixel;
|
||||
AccentColour = new Color4(1, 1, 1, 0.2f);
|
||||
BlendingMode = BlendingMode.Additive;
|
||||
Blending = BlendingMode.Additive;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Screens.Menu
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = box_width,
|
||||
Alpha = 0,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Blending = BlendingMode.Additive,
|
||||
},
|
||||
rightBox = new Box
|
||||
{
|
||||
@ -59,7 +59,7 @@ namespace osu.Game.Screens.Menu
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = box_width,
|
||||
Alpha = 0,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Blending = BlendingMode.Additive,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Blending = BlendingMode.Additive,
|
||||
Alpha = 0
|
||||
}
|
||||
}
|
||||
@ -169,7 +169,7 @@ namespace osu.Game.Screens.Menu
|
||||
flashLayer = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Blending = BlendingMode.Additive,
|
||||
Colour = Color4.White,
|
||||
Alpha = 0,
|
||||
},
|
||||
@ -272,14 +272,22 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
const float scale_adjust_cutoff = 0.4f;
|
||||
const float velocity_adjust_cutoff = 0.98f;
|
||||
const float paused_velocity = 0.5f;
|
||||
|
||||
var maxAmplitude = lastBeatIndex >= 0 ? Beatmap.Value.Track?.CurrentAmplitudes.Maximum ?? 0 : 0;
|
||||
logoAmplitudeContainer.ScaleTo(1 - Math.Max(0, maxAmplitude - scale_adjust_cutoff) * 0.04f, 75, Easing.OutQuint);
|
||||
if (Beatmap.Value.Track.IsRunning)
|
||||
{
|
||||
var maxAmplitude = lastBeatIndex >= 0 ? Beatmap.Value.Track.CurrentAmplitudes.Maximum : 0;
|
||||
logoAmplitudeContainer.ScaleTo(1 - Math.Max(0, maxAmplitude - scale_adjust_cutoff) * 0.04f, 75, Easing.OutQuint);
|
||||
|
||||
if (maxAmplitude > velocity_adjust_cutoff)
|
||||
triangles.Velocity = 1 + Math.Max(0, maxAmplitude - velocity_adjust_cutoff) * 50;
|
||||
if (maxAmplitude > velocity_adjust_cutoff)
|
||||
triangles.Velocity = 1 + Math.Max(0, maxAmplitude - velocity_adjust_cutoff) * 50;
|
||||
else
|
||||
triangles.Velocity = (float)Interpolation.Damp(triangles.Velocity, 1, 0.995f, Time.Elapsed);
|
||||
}
|
||||
else
|
||||
triangles.Velocity = (float)Interpolation.Damp(triangles.Velocity, 1, 0.995f, Time.Elapsed);
|
||||
{
|
||||
triangles.Velocity = paused_velocity;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
|
@ -28,6 +28,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public override void Add(KeyCounter key)
|
||||
{
|
||||
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||
|
||||
base.Add(key);
|
||||
key.IsCounting = IsCounting;
|
||||
key.FadeTime = FadeTime;
|
||||
|
@ -100,7 +100,7 @@ namespace osu.Game.Screens
|
||||
|
||||
Colour = getColourFor(GetType()),
|
||||
Alpha = 0.2f,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Blending = BlendingMode.Additive,
|
||||
},
|
||||
textContainer = new FillFlowContainer
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -10,6 +11,8 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
public class BeatmapDetailArea : Container
|
||||
{
|
||||
private const float details_padding = 10;
|
||||
|
||||
private readonly Container content;
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
@ -66,9 +69,8 @@ namespace osu.Game.Screens.Select
|
||||
Details = new BeatmapDetails
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Masking = true,
|
||||
Height = 352,
|
||||
Alpha = 0,
|
||||
Margin = new MarginPadding { Top = details_padding },
|
||||
},
|
||||
Leaderboard = new Leaderboard
|
||||
{
|
||||
@ -76,5 +78,12 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
Details.Height = Math.Min(DrawHeight - details_padding * 3 - BeatmapDetailAreaTabControl.HEIGHT, 450);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,71 +9,189 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Game.Screens.Select.Details;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
public class BeatmapDetails : Container
|
||||
{
|
||||
private readonly MetadataSegment description;
|
||||
private readonly MetadataSegment source;
|
||||
private readonly MetadataSegment tags;
|
||||
private const float spacing = 10;
|
||||
private const float transition_duration = 250;
|
||||
|
||||
private readonly DifficultyRow circleSize;
|
||||
private readonly DifficultyRow drainRate;
|
||||
private readonly DifficultyRow overallDifficulty;
|
||||
private readonly DifficultyRow approachRate;
|
||||
private readonly DifficultyRow stars;
|
||||
private readonly FillFlowContainer top, statsFlow;
|
||||
private readonly AdvancedStats advanced;
|
||||
private readonly DetailBox ratingsContainer;
|
||||
private readonly UserRatings ratings;
|
||||
private readonly ScrollContainer metadataScroll;
|
||||
private readonly MetadataSection description, source, tags;
|
||||
private readonly Container failRetryContainer;
|
||||
private readonly FailRetryGraph failRetryGraph;
|
||||
private readonly DimmedLoadingAnimation loading;
|
||||
|
||||
private readonly Container ratingsContainer;
|
||||
private readonly Bar ratingsBar;
|
||||
private readonly OsuSpriteText negativeRatings;
|
||||
private readonly OsuSpriteText positiveRatings;
|
||||
private readonly BarGraph ratingsGraph;
|
||||
|
||||
private readonly FillFlowContainer retryFailContainer;
|
||||
private readonly BarGraph retryGraph;
|
||||
private readonly BarGraph failGraph;
|
||||
private APIAccess api;
|
||||
|
||||
private ScheduledDelegate pendingBeatmapSwitch;
|
||||
private BeatmapInfo beatmap;
|
||||
|
||||
private BeatmapInfo beatmap;
|
||||
public BeatmapInfo Beatmap
|
||||
{
|
||||
get { return beatmap; }
|
||||
|
||||
set
|
||||
{
|
||||
if (beatmap == value) return;
|
||||
|
||||
if (value == beatmap) return;
|
||||
beatmap = value;
|
||||
|
||||
pendingBeatmapSwitch?.Cancel();
|
||||
pendingBeatmapSwitch = Schedule(updateStats);
|
||||
pendingBeatmapSwitch = Schedule(updateStatistics);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStats()
|
||||
public BeatmapDetails()
|
||||
{
|
||||
if (beatmap == null) return;
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.5f),
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Horizontal = spacing },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
top = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
statsFlow = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Width = 0.5f,
|
||||
Spacing = new Vector2(spacing),
|
||||
Padding = new MarginPadding { Right = spacing / 2 },
|
||||
Children = new[]
|
||||
{
|
||||
new DetailBox
|
||||
{
|
||||
Child = advanced = new AdvancedStats
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Horizontal = spacing, Top = spacing * 2, Bottom = spacing },
|
||||
},
|
||||
},
|
||||
ratingsContainer = new DetailBox
|
||||
{
|
||||
Child = ratings = new UserRatings
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 134,
|
||||
Padding = new MarginPadding { Horizontal = spacing, Top = spacing },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
metadataScroll = new ScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.5f,
|
||||
ScrollbarVisible = false,
|
||||
Padding = new MarginPadding { Left = spacing / 2 },
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
LayoutDuration = transition_duration,
|
||||
Spacing = new Vector2(spacing * 2),
|
||||
Margin = new MarginPadding { Top = spacing * 2 },
|
||||
Children = new[]
|
||||
{
|
||||
description = new MetadataSection("Description")
|
||||
{
|
||||
TextColour = Color4.White.Opacity(0.75f),
|
||||
},
|
||||
source = new MetadataSection("Source")
|
||||
{
|
||||
TextColour = Color4.White.Opacity(0.75f),
|
||||
},
|
||||
tags = new MetadataSection("Tags"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
failRetryContainer = new Container
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "Points of Failure",
|
||||
Font = @"Exo2.0-Bold",
|
||||
TextSize = 14,
|
||||
},
|
||||
failRetryGraph = new FailRetryGraph
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = 14 + spacing / 2 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
loading = new DimmedLoadingAnimation
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
description.Text = beatmap.Version;
|
||||
source.Text = beatmap.Metadata.Source;
|
||||
tags.Text = beatmap.Metadata.Tags;
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, APIAccess api)
|
||||
{
|
||||
this.api = api;
|
||||
tags.TextColour = colours.Yellow;
|
||||
}
|
||||
|
||||
circleSize.Value = beatmap.Difficulty.CircleSize;
|
||||
drainRate.Value = beatmap.Difficulty.DrainRate;
|
||||
overallDifficulty.Value = beatmap.Difficulty.OverallDifficulty;
|
||||
approachRate.Value = beatmap.Difficulty.ApproachRate;
|
||||
stars.Value = (float)beatmap.StarDifficulty;
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
var requestedBeatmap = beatmap;
|
||||
metadataScroll.Height = statsFlow.DrawHeight;
|
||||
failRetryContainer.Height = DrawHeight - Padding.TotalVertical - (top.DrawHeight + spacing / 2);
|
||||
}
|
||||
|
||||
private void updateStatistics()
|
||||
{
|
||||
if (Beatmap == null)
|
||||
{
|
||||
clearStats();
|
||||
return;
|
||||
}
|
||||
|
||||
ratingsContainer.FadeIn(transition_duration);
|
||||
advanced.Beatmap = Beatmap;
|
||||
description.Text = Beatmap.Version;
|
||||
source.Text = Beatmap.Metadata.Source;
|
||||
tags.Text = Beatmap.Metadata.Tags;
|
||||
|
||||
var requestedBeatmap = Beatmap;
|
||||
if (requestedBeatmap.Metrics == null)
|
||||
{
|
||||
var lookup = new GetBeatmapDetailsRequest(requestedBeatmap);
|
||||
@ -84,413 +202,195 @@ namespace osu.Game.Screens.Select
|
||||
return;
|
||||
|
||||
requestedBeatmap.Metrics = res;
|
||||
Schedule(() => updateMetrics(res));
|
||||
Schedule(() => displayMetrics(res));
|
||||
};
|
||||
lookup.Failure += e => Schedule(() => updateMetrics(null));
|
||||
lookup.Failure += e => Schedule(() => displayMetrics(null));
|
||||
|
||||
api.Queue(lookup);
|
||||
loading.Show();
|
||||
}
|
||||
|
||||
updateMetrics(requestedBeatmap.Metrics, false);
|
||||
displayMetrics(requestedBeatmap.Metrics, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update displayed metrics.
|
||||
/// </summary>
|
||||
/// <param name="metrics">New metrics to overwrite the existing display. Can be null.</param>
|
||||
/// <param name="failOnMissing">Whether to hide the display on null or empty metrics. If false, we will dim as if waiting for further updates.</param>
|
||||
private void updateMetrics(BeatmapMetrics metrics, bool failOnMissing = true)
|
||||
private void displayMetrics(BeatmapMetrics metrics, bool failOnMissing = true)
|
||||
{
|
||||
var hasRatings = metrics?.Ratings.Any() ?? false;
|
||||
var hasRetriesFails = (metrics?.Retries.Any() ?? false) && metrics.Fails.Any();
|
||||
var hasRatings = metrics?.Ratings?.Any() ?? false;
|
||||
var hasRetriesFails = (metrics?.Retries?.Any() ?? false) && (metrics.Fails?.Any() ?? false);
|
||||
|
||||
if (failOnMissing)
|
||||
loading.Hide();
|
||||
if (failOnMissing) loading.Hide();
|
||||
|
||||
if (hasRatings)
|
||||
{
|
||||
var ratings = metrics.Ratings.ToList();
|
||||
ratingsContainer.Show();
|
||||
|
||||
negativeRatings.Text = ratings.GetRange(0, ratings.Count / 2).Sum().ToString();
|
||||
positiveRatings.Text = ratings.GetRange(ratings.Count / 2, ratings.Count / 2).Sum().ToString();
|
||||
ratingsBar.Length = (float)ratings.GetRange(0, ratings.Count / 2).Sum() / ratings.Sum();
|
||||
|
||||
ratingsGraph.Values = ratings.Select(rating => (float)rating);
|
||||
|
||||
ratingsContainer.FadeColour(Color4.White, 500, Easing.Out);
|
||||
ratings.Metrics = metrics;
|
||||
ratings.FadeIn(transition_duration);
|
||||
}
|
||||
else if (failOnMissing)
|
||||
ratingsGraph.Values = new float[10];
|
||||
{
|
||||
ratings.Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = new int[10],
|
||||
};
|
||||
}
|
||||
else
|
||||
ratingsContainer.FadeColour(Color4.Gray, 500, Easing.Out);
|
||||
{
|
||||
ratings.FadeTo(0.25f, transition_duration);
|
||||
}
|
||||
|
||||
if (hasRetriesFails)
|
||||
{
|
||||
var retries = metrics.Retries;
|
||||
var fails = metrics.Fails;
|
||||
retryFailContainer.Show();
|
||||
|
||||
float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max();
|
||||
failGraph.MaxValue = maxValue;
|
||||
retryGraph.MaxValue = maxValue;
|
||||
|
||||
failGraph.Values = fails.Select(fail => (float)fail);
|
||||
retryGraph.Values = retries.Zip(fails, (retry, fail) => retry + MathHelper.Clamp(fail, 0, maxValue));
|
||||
|
||||
retryFailContainer.FadeColour(Color4.White, 500, Easing.Out);
|
||||
failRetryGraph.Metrics = metrics;
|
||||
failRetryContainer.FadeIn(transition_duration);
|
||||
}
|
||||
else if (failOnMissing)
|
||||
{
|
||||
failGraph.Values = new float[100];
|
||||
retryGraph.Values = new float[100];
|
||||
failRetryGraph.Metrics = new BeatmapMetrics
|
||||
{
|
||||
Fails = new int[100],
|
||||
Retries = new int[100],
|
||||
};
|
||||
}
|
||||
else
|
||||
retryFailContainer.FadeColour(Color4.Gray, 500, Easing.Out);
|
||||
{
|
||||
failRetryContainer.FadeTo(0.25f, transition_duration);
|
||||
}
|
||||
}
|
||||
|
||||
public BeatmapDetails()
|
||||
private void clearStats()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
description.Text = null;
|
||||
source.Text = null;
|
||||
tags.Text = null;
|
||||
advanced.Beatmap = new BeatmapInfo
|
||||
{
|
||||
new Box
|
||||
StarDifficulty = 0,
|
||||
Difficulty = new BeatmapDifficulty
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.5f,
|
||||
CircleSize = 0,
|
||||
DrainRate = 0,
|
||||
OverallDifficulty = 0,
|
||||
ApproachRate = 0,
|
||||
},
|
||||
new FillFlowContainer<MetadataSegment>
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Width = 0.4f,
|
||||
Direction = FillDirection.Vertical,
|
||||
LayoutDuration = 200,
|
||||
LayoutEasing = Easing.OutQuint,
|
||||
Children = new[]
|
||||
{
|
||||
description = new MetadataSegment("Description"),
|
||||
source = new MetadataSegment("Source"),
|
||||
tags = new MetadataSegment("Tags")
|
||||
},
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Width = 0.6f,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 15),
|
||||
Padding = new MarginPadding(10) { Top = 0 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 5),
|
||||
Padding = new MarginPadding(10),
|
||||
Children = new[]
|
||||
{
|
||||
circleSize = new DifficultyRow("Circle Size", 7),
|
||||
drainRate = new DifficultyRow("HP Drain"),
|
||||
overallDifficulty = new DifficultyRow("Accuracy"),
|
||||
approachRate = new DifficultyRow("Approach Rate"),
|
||||
stars = new DifficultyRow("Star Difficulty"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ratingsContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Top = 25,
|
||||
Left = 15,
|
||||
Right = 15,
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "User Rating",
|
||||
Font = @"Exo2.0-Medium",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
},
|
||||
ratingsBar = new Bar
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 5,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new[]
|
||||
{
|
||||
negativeRatings = new OsuSpriteText
|
||||
{
|
||||
Font = @"Exo2.0-Regular",
|
||||
Text = "0",
|
||||
},
|
||||
positiveRatings = new OsuSpriteText
|
||||
{
|
||||
Font = @"Exo2.0-Regular",
|
||||
Text = "0",
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
},
|
||||
},
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "Rating Spread",
|
||||
TextSize = 14,
|
||||
Font = @"Exo2.0-Regular",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
},
|
||||
ratingsGraph = new BarGraph
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
retryFailContainer = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Alpha = 0,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "Points of Failure",
|
||||
Font = @"Exo2.0-Regular",
|
||||
},
|
||||
new Container<BarGraph>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Size = new Vector2(1 / 0.6f, 50),
|
||||
Children = new[]
|
||||
{
|
||||
retryGraph = new BarGraph
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
failGraph = new BarGraph
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
loading = new LoadingAnimation()
|
||||
};
|
||||
|
||||
loading.Hide();
|
||||
ratingsContainer.FadeOut(transition_duration);
|
||||
failRetryContainer.FadeOut(transition_duration);
|
||||
}
|
||||
|
||||
private APIAccess api;
|
||||
private readonly LoadingAnimation loading;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colour, APIAccess api)
|
||||
private class DetailBox : Container
|
||||
{
|
||||
this.api = api;
|
||||
private readonly Container content;
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
description.AccentColour = colour.GrayB;
|
||||
source.AccentColour = colour.GrayB;
|
||||
tags.AccentColour = colour.YellowLight;
|
||||
|
||||
stars.AccentColour = colour.Yellow;
|
||||
|
||||
ratingsBar.BackgroundColour = colour.Green;
|
||||
ratingsBar.AccentColour = colour.YellowDark;
|
||||
ratingsGraph.Colour = colour.BlueDark;
|
||||
|
||||
failGraph.Colour = colour.YellowDarker;
|
||||
retryGraph.Colour = colour.Yellow;
|
||||
}
|
||||
|
||||
private class DifficultyRow : Container, IHasAccentColour
|
||||
{
|
||||
private readonly OsuSpriteText name;
|
||||
private readonly Bar bar;
|
||||
private readonly OsuSpriteText valueText;
|
||||
|
||||
private readonly float maxValue;
|
||||
|
||||
private float difficultyValue;
|
||||
public float Value
|
||||
public DetailBox()
|
||||
{
|
||||
get
|
||||
{
|
||||
return difficultyValue;
|
||||
}
|
||||
set
|
||||
{
|
||||
difficultyValue = value;
|
||||
bar.Length = value / maxValue;
|
||||
valueText.Text = value.ToString("N1", CultureInfo.CurrentCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get
|
||||
{
|
||||
return bar.AccentColour;
|
||||
}
|
||||
set
|
||||
{
|
||||
bar.AccentColour = value;
|
||||
}
|
||||
}
|
||||
|
||||
public DifficultyRow(string difficultyName, float maxValue = 10)
|
||||
{
|
||||
this.maxValue = maxValue;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Children = new Drawable[]
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
name = new OsuSpriteText
|
||||
new Box
|
||||
{
|
||||
Font = @"Exo2.0-Regular",
|
||||
Text = difficultyName,
|
||||
},
|
||||
bar = new Bar
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Size = new Vector2(1, 0.35f),
|
||||
Padding = new MarginPadding { Left = 100, Right = 25 },
|
||||
Colour = Color4.Black.Opacity(0.5f),
|
||||
},
|
||||
valueText = new OsuSpriteText
|
||||
content = new Container
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Font = @"Exo2.0-Regular",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colour)
|
||||
{
|
||||
name.Colour = colour.GrayB;
|
||||
bar.BackgroundColour = colour.Gray7;
|
||||
valueText.Colour = colour.GrayB;
|
||||
}
|
||||
}
|
||||
|
||||
private class MetadataSegment : Container, IHasAccentColour
|
||||
private class MetadataSection : Container
|
||||
{
|
||||
private readonly OsuSpriteText header;
|
||||
private readonly FillFlowContainer<OsuSpriteText> content;
|
||||
private readonly TextFlowContainer textFlow;
|
||||
|
||||
public string Text
|
||||
{
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
Hide();
|
||||
else
|
||||
{
|
||||
Show();
|
||||
if (header.Text == "Tags")
|
||||
content.ChildrenEnumerable = value.Split(' ').Select(text => new OsuSpriteText
|
||||
{
|
||||
Text = text,
|
||||
Font = "Exo2.0-Regular",
|
||||
});
|
||||
else
|
||||
content.Children = new[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = value,
|
||||
Font = "Exo2.0-Regular",
|
||||
}
|
||||
};
|
||||
this.FadeOut(transition_duration);
|
||||
return;
|
||||
}
|
||||
|
||||
this.FadeIn(transition_duration);
|
||||
textFlow.Clear();
|
||||
textFlow.AddText(value, s => s.TextSize = 14);
|
||||
}
|
||||
}
|
||||
|
||||
public Color4 AccentColour
|
||||
public Color4 TextColour
|
||||
{
|
||||
get
|
||||
{
|
||||
return content.Colour;
|
||||
}
|
||||
set
|
||||
{
|
||||
content.Colour = value;
|
||||
}
|
||||
get { return textFlow.Colour; }
|
||||
set { textFlow.Colour = value; }
|
||||
}
|
||||
|
||||
public MetadataSegment(string headerText)
|
||||
public MetadataSection(string title)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Margin = new MarginPadding { Top = 10 };
|
||||
|
||||
InternalChild = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(spacing / 2),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = new OsuSpriteText
|
||||
{
|
||||
Text = title,
|
||||
Font = @"Exo2.0-Bold",
|
||||
TextSize = 14,
|
||||
},
|
||||
},
|
||||
textFlow = new TextFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private class DimmedLoadingAnimation : VisibilityContainer
|
||||
{
|
||||
private readonly LoadingAnimation loading;
|
||||
|
||||
public DimmedLoadingAnimation()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
header = new OsuSpriteText
|
||||
new Box
|
||||
{
|
||||
Font = @"Exo2.0-Bold",
|
||||
Text = headerText,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.5f),
|
||||
},
|
||||
content = new FillFlowContainer<OsuSpriteText>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Full,
|
||||
Spacing = new Vector2(5, 0),
|
||||
Margin = new MarginPadding { Top = header.TextSize }
|
||||
}
|
||||
loading = new LoadingAnimation(),
|
||||
};
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
this.FadeIn(transition_duration, Easing.OutQuint);
|
||||
loading.State = Visibility.Visible;
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
this.FadeOut(transition_duration, Easing.OutQuint);
|
||||
loading.State = Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
152
osu.Game/Screens/Select/Details/AdvancedStats.cs
Normal file
152
osu.Game/Screens/Select/Details/AdvancedStats.cs
Normal file
@ -0,0 +1,152 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using System;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Screens.Select.Details
|
||||
{
|
||||
public class AdvancedStats : Container
|
||||
{
|
||||
private readonly StatisticRow firstValue, hpDrain, accuracy, approachRate, starDifficulty;
|
||||
|
||||
private BeatmapInfo beatmap;
|
||||
public BeatmapInfo Beatmap
|
||||
{
|
||||
get { return beatmap; }
|
||||
set
|
||||
{
|
||||
if (value == beatmap) return;
|
||||
beatmap = value;
|
||||
|
||||
//mania specific
|
||||
if ((Beatmap?.Ruleset?.ID ?? 0) == 3)
|
||||
{
|
||||
firstValue.Title = "Key Amount";
|
||||
firstValue.Value = (int)Math.Round(Beatmap?.Difficulty?.CircleSize ?? 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
firstValue.Title = "Circle Size";
|
||||
firstValue.Value = Beatmap?.Difficulty?.CircleSize ?? 0;
|
||||
}
|
||||
|
||||
hpDrain.Value = beatmap.Difficulty.DrainRate;
|
||||
accuracy.Value = beatmap.Difficulty.OverallDifficulty;
|
||||
approachRate.Value = beatmap.Difficulty.ApproachRate;
|
||||
starDifficulty.Value = (float)beatmap.StarDifficulty;
|
||||
}
|
||||
}
|
||||
|
||||
public AdvancedStats()
|
||||
{
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(4f),
|
||||
Children = new[]
|
||||
{
|
||||
firstValue = new StatisticRow(), //circle size/key amount
|
||||
hpDrain = new StatisticRow { Title = "HP Drain" },
|
||||
accuracy = new StatisticRow { Title = "Accuracy" },
|
||||
approachRate = new StatisticRow { Title = "Approach Rate" },
|
||||
starDifficulty = new StatisticRow(10, true) { Title = "Star Difficulty" },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
starDifficulty.AccentColour = colours.Yellow;
|
||||
}
|
||||
|
||||
private class StatisticRow : Container, IHasAccentColour
|
||||
{
|
||||
private const float value_width = 25;
|
||||
private const float name_width = 70;
|
||||
|
||||
private readonly float maxValue;
|
||||
private readonly bool forceDecimalPlaces;
|
||||
private readonly OsuSpriteText name, value;
|
||||
private readonly Bar bar;
|
||||
|
||||
public string Title
|
||||
{
|
||||
get { return name.Text; }
|
||||
set { name.Text = value; }
|
||||
}
|
||||
|
||||
private float difficultyValue;
|
||||
public float Value
|
||||
{
|
||||
get { return difficultyValue; }
|
||||
set
|
||||
{
|
||||
difficultyValue = value;
|
||||
bar.Length = value / maxValue;
|
||||
this.value.Text = value.ToString(forceDecimalPlaces ? "0.00" : "0.##");
|
||||
}
|
||||
}
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get { return bar.AccentColour; }
|
||||
set { bar.AccentColour = value; }
|
||||
}
|
||||
|
||||
public StatisticRow(float maxValue = 10, bool forceDecimalPlaces = false)
|
||||
{
|
||||
this.maxValue = maxValue;
|
||||
this.forceDecimalPlaces = forceDecimalPlaces;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Width = name_width,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = name = new OsuSpriteText
|
||||
{
|
||||
TextSize = 13,
|
||||
},
|
||||
},
|
||||
bar = new Bar
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 5,
|
||||
BackgroundColour = Color4.White.Opacity(0.5f),
|
||||
Padding = new MarginPadding { Left = name_width + 10, Right = value_width + 10 },
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Width = value_width,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Child = value = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
TextSize = 13,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
62
osu.Game/Screens/Select/Details/FailRetryGraph.cs
Normal file
62
osu.Game/Screens/Select/Details/FailRetryGraph.cs
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Screens.Select.Details
|
||||
{
|
||||
public class FailRetryGraph : Container
|
||||
{
|
||||
private readonly BarGraph retryGraph, failGraph;
|
||||
|
||||
private BeatmapMetrics metrics;
|
||||
public BeatmapMetrics Metrics
|
||||
{
|
||||
get { return metrics; }
|
||||
set
|
||||
{
|
||||
if (value == metrics) return;
|
||||
metrics = value;
|
||||
|
||||
var retries = Metrics.Retries;
|
||||
var fails = Metrics.Fails;
|
||||
|
||||
float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max();
|
||||
failGraph.MaxValue = maxValue;
|
||||
retryGraph.MaxValue = maxValue;
|
||||
|
||||
failGraph.Values = fails.Select(f => (float)f);
|
||||
retryGraph.Values = retries.Zip(fails, (retry, fail) => retry + MathHelper.Clamp(fail, 0, maxValue));
|
||||
}
|
||||
}
|
||||
|
||||
public FailRetryGraph()
|
||||
{
|
||||
Children = new[]
|
||||
{
|
||||
retryGraph = new BarGraph
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
failGraph = new BarGraph
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
retryGraph.Colour = colours.Yellow;
|
||||
failGraph.Colour = colours.YellowDarker;
|
||||
}
|
||||
}
|
||||
}
|
122
osu.Game/Screens/Select/Details/UserRatings.cs
Normal file
122
osu.Game/Screens/Select/Details/UserRatings.cs
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Screens.Select.Details
|
||||
{
|
||||
public class UserRatings : Container
|
||||
{
|
||||
private readonly FillFlowContainer header;
|
||||
private readonly Bar ratingsBar;
|
||||
private readonly OsuSpriteText negativeRatings, positiveRatings;
|
||||
private readonly Container graphContainer;
|
||||
private readonly BarGraph graph;
|
||||
|
||||
private BeatmapMetrics metrics;
|
||||
public BeatmapMetrics Metrics
|
||||
{
|
||||
get { return metrics; }
|
||||
set
|
||||
{
|
||||
if (value == metrics) return;
|
||||
metrics = value;
|
||||
|
||||
var ratings = Metrics.Ratings.ToList();
|
||||
negativeRatings.Text = ratings.GetRange(0, ratings.Count / 2).Sum().ToString();
|
||||
positiveRatings.Text = ratings.GetRange(ratings.Count / 2, ratings.Count / 2).Sum().ToString();
|
||||
ratingsBar.Length = (float)ratings.GetRange(0, ratings.Count / 2).Sum() / ratings.Sum();
|
||||
graph.Values = Metrics.Ratings.Select(r => (float)r);
|
||||
}
|
||||
}
|
||||
|
||||
public UserRatings()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
header = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = "User Rating",
|
||||
TextSize = 13,
|
||||
},
|
||||
ratingsBar = new Bar
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 5,
|
||||
Margin = new MarginPadding { Top = 5 },
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new[]
|
||||
{
|
||||
negativeRatings = new OsuSpriteText
|
||||
{
|
||||
Text = "0",
|
||||
TextSize = 13,
|
||||
},
|
||||
positiveRatings = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Text = @"0",
|
||||
TextSize = 13,
|
||||
},
|
||||
},
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = "Rating Spread",
|
||||
TextSize = 13,
|
||||
Margin = new MarginPadding { Top = 10, Bottom = 5 },
|
||||
},
|
||||
},
|
||||
},
|
||||
graphContainer = new Container
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = graph = new BarGraph
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
ratingsBar.BackgroundColour = colours.Green;
|
||||
ratingsBar.AccentColour = colours.Yellow;
|
||||
graph.Colour = colours.BlueDark;
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
graphContainer.Padding = new MarginPadding { Top = header.DrawHeight };
|
||||
}
|
||||
}
|
||||
}
|
@ -304,7 +304,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
BlurSigma = new Vector2(4),
|
||||
CacheDrawnFrameBuffer = true,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Blending = BlendingMode.Additive,
|
||||
Size = new Vector2(3f),
|
||||
Children = new[]
|
||||
{
|
||||
|
@ -119,7 +119,7 @@ namespace osu.Game.Screens.Select.Options
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
EdgeSmoothness = new Vector2(1.5f, 0),
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Blending = BlendingMode.Additive,
|
||||
Colour = Color4.White,
|
||||
Alpha = 0,
|
||||
},
|
||||
|
@ -9,7 +9,7 @@
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>osu.Game</RootNamespace>
|
||||
<AssemblyName>osu.Game</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
<NuGetPackageImportStamp>
|
||||
@ -98,6 +98,7 @@
|
||||
<Compile Include="Input\Bindings\GlobalKeyBindingInputManager.cs" />
|
||||
<Compile Include="IO\FileStore.cs" />
|
||||
<Compile Include="IO\FileInfo.cs" />
|
||||
<Compile Include="Online\API\Requests\DownloadBeatmapSetRequest.cs" />
|
||||
<Compile Include="Online\API\Requests\GetUsersRequest.cs" />
|
||||
<Compile Include="Online\API\Requests\PostMessageRequest.cs" />
|
||||
<Compile Include="Online\Chat\ErrorMessage.cs" />
|
||||
@ -214,7 +215,6 @@
|
||||
<Compile Include="Rulesets\Objects\Types\IHasYPosition.cs" />
|
||||
<Compile Include="Rulesets\Replays\Replay.cs" />
|
||||
<Compile Include="Rulesets\Judgements\DrawableJudgement.cs" />
|
||||
<Compile Include="Rulesets\Judgements\IPartialJudgement.cs" />
|
||||
<Compile Include="Rulesets\Replays\FramedReplayInputHandler.cs" />
|
||||
<Compile Include="Rulesets\Mods\IApplicableMod.cs" />
|
||||
<Compile Include="Rulesets\Mods\ModType.cs" />
|
||||
@ -538,6 +538,9 @@
|
||||
<Compile Include="Screens\Multiplayer\ParticipantInfo.cs" />
|
||||
<Compile Include="Screens\Multiplayer\ModeTypeInfo.cs" />
|
||||
<Compile Include="Beatmaps\Drawables\BeatmapSetCover.cs" />
|
||||
<Compile Include="Screens\Select\Details\AdvancedStats.cs" />
|
||||
<Compile Include="Screens\Select\Details\FailRetryGraph.cs" />
|
||||
<Compile Include="Screens\Select\Details\UserRatings.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
|
||||
|
Reference in New Issue
Block a user