mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 09:27:29 +08:00
Merge remote-tracking branch 'upstream/master' into peppy-net471
This commit is contained in:
commit
e076107a19
@ -1,4 +1,3 @@
|
|||||||
# 2017-09-14
|
|
||||||
clone_depth: 1
|
clone_depth: 1
|
||||||
version: '{branch}-{build}'
|
version: '{branch}-{build}'
|
||||||
image: Visual Studio 2017
|
image: Visual Studio 2017
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
# 2017-09-14
|
branches:
|
||||||
|
only:
|
||||||
|
- release
|
||||||
|
skip_tags: true
|
||||||
|
skip_branch_with_pr: true
|
||||||
clone_depth: 1
|
clone_depth: 1
|
||||||
version: '{branch}-{build}'
|
version: '{branch}-{build}'
|
||||||
image: Visual Studio 2017
|
image: Visual Studio 2017
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit a5e5b64a9270df6704e7d78126e7b1541064f209
|
Subproject commit ac2b966644982a35ba96d315a7bb5367e0bedb99
|
@ -7,6 +7,7 @@ using System.Configuration;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Management.Automation;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using osu.Framework.IO.Network;
|
using osu.Framework.IO.Network;
|
||||||
using FileWebRequest = osu.Framework.IO.Network.FileWebRequest;
|
using FileWebRequest = osu.Framework.IO.Network.FileWebRequest;
|
||||||
@ -99,6 +100,7 @@ namespace osu.Desktop.Deploy
|
|||||||
|
|
||||||
write("Updating AssemblyInfo...");
|
write("Updating AssemblyInfo...");
|
||||||
updateCsprojVersion(version);
|
updateCsprojVersion(version);
|
||||||
|
updateAppveyorVersion(version);
|
||||||
|
|
||||||
write("Running build process...");
|
write("Running build process...");
|
||||||
foreach (string targetName in TargetNames.Split(','))
|
foreach (string targetName in TargetNames.Split(','))
|
||||||
@ -404,6 +406,25 @@ namespace osu.Desktop.Deploy
|
|||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool updateAppveyorVersion(string version)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (PowerShell ps = PowerShell.Create())
|
||||||
|
{
|
||||||
|
ps.AddScript($"Update-AppveyorBuild -Version \"{version}\"");
|
||||||
|
ps.Invoke();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// we don't have appveyor and don't care
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static void write(string message, ConsoleColor col = ConsoleColor.Gray)
|
private static void write(string message, ConsoleColor col = ConsoleColor.Gray)
|
||||||
{
|
{
|
||||||
if (sw.ElapsedMilliseconds > 0)
|
if (sw.ElapsedMilliseconds > 0)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.Game.props" />
|
<Import Project="..\osu.Game.props" />
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFrameworks>net471</TargetFrameworks>
|
<TargetFrameworks>net471</TargetFrameworks>
|
||||||
@ -14,5 +14,6 @@
|
|||||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||||
<PackageReference Include="squirrel.windows" Version="1.7.8" Condition="'$(TargetFramework)' == 'net471'" />
|
<PackageReference Include="squirrel.windows" Version="1.7.8" Condition="'$(TargetFramework)' == 'net471'" />
|
||||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.4.0" />
|
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.4.0" />
|
||||||
|
<PackageReference Include="System.Management.Automation.dll" Version="10.0.10586" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -7,7 +7,6 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
public class OsuEditPlayfield : OsuPlayfield
|
public class OsuEditPlayfield : OsuPlayfield
|
||||||
{
|
{
|
||||||
protected override bool ProxyApproachCircles => false;
|
|
||||||
protected override bool DisplayJudgements => false;
|
protected override bool DisplayJudgements => false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Children = new Drawable[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
Background = new SpinnerBackground
|
Background = new SpinnerBackground
|
||||||
{
|
{
|
||||||
|
@ -21,9 +21,6 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
private readonly JudgementContainer<DrawableOsuJudgement> judgementLayer;
|
private readonly JudgementContainer<DrawableOsuJudgement> judgementLayer;
|
||||||
private readonly ConnectionRenderer<OsuHitObject> connectionLayer;
|
private readonly ConnectionRenderer<OsuHitObject> connectionLayer;
|
||||||
|
|
||||||
// Todo: This should not be a thing, but is currently required for the editor
|
|
||||||
// https://github.com/ppy/osu-framework/issues/1283
|
|
||||||
protected virtual bool ProxyApproachCircles => true;
|
|
||||||
protected virtual bool DisplayJudgements => true;
|
protected virtual bool DisplayJudgements => true;
|
||||||
|
|
||||||
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
|
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
|
||||||
@ -59,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
h.OnJudgement += onJudgement;
|
h.OnJudgement += onJudgement;
|
||||||
|
|
||||||
var c = h as IDrawableHitObjectWithProxiedApproach;
|
var c = h as IDrawableHitObjectWithProxiedApproach;
|
||||||
if (c != null && ProxyApproachCircles)
|
if (c != null)
|
||||||
approachCircles.Add(c.ProxiedLayer.CreateProxy());
|
approachCircles.Add(c.ProxiedLayer.CreateProxy());
|
||||||
|
|
||||||
base.Add(h);
|
base.Add(h);
|
||||||
|
@ -71,9 +71,9 @@ namespace osu.Game.Graphics.Containers
|
|||||||
switch (linkType)
|
switch (linkType)
|
||||||
{
|
{
|
||||||
case LinkAction.OpenBeatmap:
|
case LinkAction.OpenBeatmap:
|
||||||
// todo: replace this with overlay.ShowBeatmap(id) once an appropriate API call is implemented.
|
// TODO: proper query params handling
|
||||||
if (int.TryParse(linkArgument, out int beatmapId))
|
if (linkArgument != null && int.TryParse(linkArgument.Contains('?') ? linkArgument.Split('?')[0] : linkArgument, out int beatmapId))
|
||||||
Process.Start($"https://osu.ppy.sh/b/{beatmapId}");
|
game?.ShowBeatmap(beatmapId);
|
||||||
break;
|
break;
|
||||||
case LinkAction.OpenBeatmapSet:
|
case LinkAction.OpenBeatmapSet:
|
||||||
if (int.TryParse(linkArgument, out int setId))
|
if (int.TryParse(linkArgument, out int setId))
|
||||||
|
@ -5,13 +5,21 @@ namespace osu.Game.Online.API.Requests
|
|||||||
{
|
{
|
||||||
public class GetBeatmapSetRequest : APIRequest<APIResponseBeatmapSet>
|
public class GetBeatmapSetRequest : APIRequest<APIResponseBeatmapSet>
|
||||||
{
|
{
|
||||||
private readonly int beatmapSetId;
|
private readonly int id;
|
||||||
|
private readonly BeatmapSetLookupType type;
|
||||||
|
|
||||||
public GetBeatmapSetRequest(int beatmapSetId)
|
public GetBeatmapSetRequest(int id, BeatmapSetLookupType type = BeatmapSetLookupType.SetId)
|
||||||
{
|
{
|
||||||
this.beatmapSetId = beatmapSetId;
|
this.id = id;
|
||||||
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string Target => $@"beatmapsets/{beatmapSetId}";
|
protected override string Target => type == BeatmapSetLookupType.SetId ? $@"beatmapsets/{id}" : $@"beatmapsets/lookup?beatmap_id={id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BeatmapSetLookupType
|
||||||
|
{
|
||||||
|
SetId,
|
||||||
|
BeatmapId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,6 +166,12 @@ namespace osu.Game
|
|||||||
/// <param name="userId">The user to display.</param>
|
/// <param name="userId">The user to display.</param>
|
||||||
public void ShowUser(long userId) => userProfile.ShowUser(userId);
|
public void ShowUser(long userId) => userProfile.ShowUser(userId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Show a beatmap's set as an overlay, displaying the given beatmap.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beatmapId">The beatmap to show.</param>
|
||||||
|
public void ShowBeatmap(int beatmapId) => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId);
|
||||||
|
|
||||||
protected void LoadScore(Score s)
|
protected void LoadScore(Score s)
|
||||||
{
|
{
|
||||||
scoreLoad?.Cancel();
|
scoreLoad?.Cancel();
|
||||||
|
@ -17,21 +17,39 @@ using osu.Game.Online.API.Requests;
|
|||||||
using osu.Game.Overlays.BeatmapSet;
|
using osu.Game.Overlays.BeatmapSet;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Overlays.BeatmapSet.Scores;
|
using osu.Game.Overlays.BeatmapSet.Scores;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
public class BeatmapSetOverlay : WaveOverlayContainer
|
public class BeatmapSetOverlay : WaveOverlayContainer
|
||||||
{
|
{
|
||||||
|
private const int fade_duration = 300;
|
||||||
|
|
||||||
public const float X_PADDING = 40;
|
public const float X_PADDING = 40;
|
||||||
public const float RIGHT_WIDTH = 275;
|
public const float RIGHT_WIDTH = 275;
|
||||||
|
|
||||||
private readonly Header header;
|
private readonly Header header;
|
||||||
private readonly Info info;
|
private readonly Info info;
|
||||||
|
|
||||||
private APIAccess api;
|
private APIAccess api;
|
||||||
private RulesetStore rulesets;
|
private RulesetStore rulesets;
|
||||||
|
|
||||||
private readonly ScrollContainer scroll;
|
private readonly ScrollContainer scroll;
|
||||||
|
|
||||||
|
private BeatmapSetInfo beatmapSet;
|
||||||
|
|
||||||
|
public BeatmapSetInfo BeatmapSet
|
||||||
|
{
|
||||||
|
get => beatmapSet;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == beatmapSet)
|
||||||
|
return;
|
||||||
|
|
||||||
|
header.BeatmapSet = info.BeatmapSet = beatmapSet = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// receive input outside our bounds so we can trigger a close event on ourselves.
|
// receive input outside our bounds so we can trigger a close event on ourselves.
|
||||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
|
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
|
||||||
|
|
||||||
@ -107,7 +125,8 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
base.PopOut();
|
base.PopOut();
|
||||||
header.Details.StopPreview();
|
header.Details.StopPreview();
|
||||||
FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out);
|
|
||||||
|
FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out).OnComplete(_ => BeatmapSet = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnClick(InputState state)
|
protected override bool OnClick(InputState state)
|
||||||
@ -116,17 +135,31 @@ namespace osu.Game.Overlays
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void FetchAndShowBeatmap(int beatmapId)
|
||||||
|
{
|
||||||
|
BeatmapSet = null;
|
||||||
|
var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId);
|
||||||
|
req.Success += res =>
|
||||||
|
{
|
||||||
|
ShowBeatmapSet(res.ToBeatmapSet(rulesets));
|
||||||
|
header.Picker.Beatmap.Value = header.BeatmapSet.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId);
|
||||||
|
};
|
||||||
|
api.Queue(req);
|
||||||
|
Show();
|
||||||
|
}
|
||||||
|
|
||||||
public void FetchAndShowBeatmapSet(int beatmapSetId)
|
public void FetchAndShowBeatmapSet(int beatmapSetId)
|
||||||
{
|
{
|
||||||
// todo: display the overlay while we are loading here. we need to support setting BeatmapSet to null for this to work.
|
BeatmapSet = null;
|
||||||
var req = new GetBeatmapSetRequest(beatmapSetId);
|
var req = new GetBeatmapSetRequest(beatmapSetId);
|
||||||
req.Success += res => ShowBeatmapSet(res.ToBeatmapSet(rulesets));
|
req.Success += res => ShowBeatmapSet(res.ToBeatmapSet(rulesets));
|
||||||
api.Queue(req);
|
api.Queue(req);
|
||||||
|
Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowBeatmapSet(BeatmapSetInfo set)
|
public void ShowBeatmapSet(BeatmapSetInfo set)
|
||||||
{
|
{
|
||||||
header.BeatmapSet = info.BeatmapSet = set;
|
BeatmapSet = set;
|
||||||
Show();
|
Show();
|
||||||
scroll.ScrollTo(0);
|
scroll.ScrollTo(0);
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
base.UpdateAfterChildrenLife();
|
base.UpdateAfterChildrenLife();
|
||||||
|
|
||||||
// We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions
|
// We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions
|
||||||
speedChangeVisualiser.ComputePositions(AliveObjects, direction, Time.Current, TimeRange, DrawSize);
|
speedChangeVisualiser.UpdatePositions(AliveObjects, direction, Time.Current, TimeRange, DrawSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,24 +10,23 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
|||||||
public interface ISpeedChangeVisualiser
|
public interface ISpeedChangeVisualiser
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Computes the states of <see cref="DrawableHitObject"/>s that are constant, such as lifetime and spatial length.
|
/// Computes the states of <see cref="DrawableHitObject"/>s that remain constant while scrolling, such as lifetime and spatial length.
|
||||||
/// This is invoked once whenever <paramref name="timeRange"/> or <paramref name="length"/> changes.
|
/// This is invoked once whenever <paramref name="timeRange"/> or <paramref name="length"/> changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose states should be computed.</param>
|
/// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose states should be computed.</param>
|
||||||
/// <param name="direction">The scrolling direction.</param>
|
/// <param name="direction">The scrolling direction.</param>
|
||||||
/// <param name="timeRange">The duration required to scroll through one length of the screen before any control point adjustments.</param>
|
/// <param name="timeRange">The duration required to scroll through one length of the screen before any speed adjustments.</param>
|
||||||
/// <param name="length">The length of the screen that is scrolled through.</param>
|
/// <param name="length">The length of the screen that is scrolled through.</param>
|
||||||
void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length);
|
void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Computes the states of <see cref="DrawableHitObject"/>s that change depending on <paramref name="currentTime"/>, such as position.
|
/// Updates the positions of <see cref="DrawableHitObject"/>s, depending on the current time. This is invoked once per frame.
|
||||||
/// This is invoked once per frame.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose states should be computed.</param>
|
/// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose positions should be computed.</param>
|
||||||
/// <param name="direction">The scrolling direction.</param>
|
/// <param name="direction">The scrolling direction.</param>
|
||||||
/// <param name="currentTime">The current time.</param>
|
/// <param name="currentTime">The current time.</param>
|
||||||
/// <param name="timeRange">The duration required to scroll through one length of the screen before any control point adjustments.</param>
|
/// <param name="timeRange">The duration required to scroll through one length of the screen before any speed adjustments.</param>
|
||||||
/// <param name="length">The length of the screen that is scrolled through.</param>
|
/// <param name="length">The length of the screen that is scrolled through.</param>
|
||||||
void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length);
|
void UpdatePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Lists;
|
using osu.Framework.Lists;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Timing;
|
using osu.Game.Rulesets.Timing;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
|
||||||
@ -22,24 +23,45 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
|||||||
{
|
{
|
||||||
foreach (var obj in hitObjects)
|
foreach (var obj in hitObjects)
|
||||||
{
|
{
|
||||||
var controlPoint = controlPointAt(obj.HitObject.StartTime);
|
// The total amount of time that the hitobject will remain visible within the timeRange, which decreases as the speed multiplier increases
|
||||||
obj.LifetimeStart = obj.HitObject.StartTime - timeRange / controlPoint.Multiplier;
|
double visibleDuration = timeRange / controlPointAt(obj.HitObject.StartTime).Multiplier;
|
||||||
|
|
||||||
|
obj.LifetimeStart = obj.HitObject.StartTime - visibleDuration;
|
||||||
|
|
||||||
|
if (obj.HitObject is IHasEndTime endTime)
|
||||||
|
{
|
||||||
|
// At the hitobject's end time, the hitobject will be positioned such that its end rests at the origin.
|
||||||
|
// This results in a negative-position value, and the absolute of it indicates the length of the hitobject.
|
||||||
|
var hitObjectLength = -hitObjectPositionAt(obj, endTime.EndTime, timeRange);
|
||||||
|
|
||||||
|
switch (direction)
|
||||||
|
{
|
||||||
|
case ScrollingDirection.Up:
|
||||||
|
case ScrollingDirection.Down:
|
||||||
|
obj.Height = (float)(hitObjectLength * length.Y);
|
||||||
|
break;
|
||||||
|
case ScrollingDirection.Left:
|
||||||
|
case ScrollingDirection.Right:
|
||||||
|
obj.Width = (float)(hitObjectLength * length.X);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (obj.HasNestedHitObjects)
|
if (obj.HasNestedHitObjects)
|
||||||
{
|
{
|
||||||
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
|
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
|
||||||
ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
|
|
||||||
|
// Nested hitobjects don't need to scroll, but they do need accurate positions
|
||||||
|
UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length)
|
public void UpdatePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length)
|
||||||
{
|
{
|
||||||
foreach (var obj in hitObjects)
|
foreach (var obj in hitObjects)
|
||||||
{
|
{
|
||||||
var controlPoint = controlPointAt(obj.HitObject.StartTime);
|
var position = hitObjectPositionAt(obj, currentTime, timeRange);
|
||||||
|
|
||||||
var position = (obj.HitObject.StartTime - currentTime) * controlPoint.Multiplier / timeRange;
|
|
||||||
|
|
||||||
switch (direction)
|
switch (direction)
|
||||||
{
|
{
|
||||||
@ -59,7 +81,28 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the position of a <see cref="DrawableHitObject"/> at a point in time.
|
||||||
|
/// <para>
|
||||||
|
/// At t < startTime, position > 0. <br />
|
||||||
|
/// At t = startTime, position = 0. <br />
|
||||||
|
/// At t > startTime, position < 0.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The <see cref="DrawableHitObject"/>.</param>
|
||||||
|
/// <param name="time">The time to find the position of <paramref name="obj"/> at.</param>
|
||||||
|
/// <param name="timeRange">The amount of time visualised by the scrolling area.</param>
|
||||||
|
/// <returns>The position of <paramref name="obj"/> in the scrolling area at time = <paramref name="time"/>.</returns>
|
||||||
|
private double hitObjectPositionAt(DrawableHitObject obj, double time, double timeRange)
|
||||||
|
=> (obj.HitObject.StartTime - time) / timeRange * controlPointAt(obj.HitObject.StartTime).Multiplier;
|
||||||
|
|
||||||
private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint();
|
private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the <see cref="MultiplierControlPoint"/> which affects the speed of hitobjects at a specific time.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="time">The time which the <see cref="MultiplierControlPoint"/> should affect.</param>
|
||||||
|
/// <returns>The <see cref="MultiplierControlPoint"/>.</returns>
|
||||||
private MultiplierControlPoint controlPointAt(double time)
|
private MultiplierControlPoint controlPointAt(double time)
|
||||||
{
|
{
|
||||||
if (controlPoints.Count == 0)
|
if (controlPoints.Count == 0)
|
||||||
|
@ -25,23 +25,25 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
|||||||
{
|
{
|
||||||
foreach (var obj in hitObjects)
|
foreach (var obj in hitObjects)
|
||||||
{
|
{
|
||||||
|
// To reduce iterations when updating hitobject positions later on, their initial positions are cached
|
||||||
var startPosition = hitObjectPositions[obj] = positionAt(obj.HitObject.StartTime, timeRange);
|
var startPosition = hitObjectPositions[obj] = positionAt(obj.HitObject.StartTime, timeRange);
|
||||||
|
|
||||||
|
// Todo: This is approximate and will be incorrect in the case of extreme speed changes
|
||||||
obj.LifetimeStart = obj.HitObject.StartTime - timeRange - 1000;
|
obj.LifetimeStart = obj.HitObject.StartTime - timeRange - 1000;
|
||||||
|
|
||||||
if (obj.HitObject is IHasEndTime endTime)
|
if (obj.HitObject is IHasEndTime endTime)
|
||||||
{
|
{
|
||||||
var diff = positionAt(endTime.EndTime, timeRange) - startPosition;
|
var hitObjectLength = positionAt(endTime.EndTime, timeRange) - startPosition;
|
||||||
|
|
||||||
switch (direction)
|
switch (direction)
|
||||||
{
|
{
|
||||||
case ScrollingDirection.Up:
|
case ScrollingDirection.Up:
|
||||||
case ScrollingDirection.Down:
|
case ScrollingDirection.Down:
|
||||||
obj.Height = (float)(diff * length.Y);
|
obj.Height = (float)(hitObjectLength * length.Y);
|
||||||
break;
|
break;
|
||||||
case ScrollingDirection.Left:
|
case ScrollingDirection.Left:
|
||||||
case ScrollingDirection.Right:
|
case ScrollingDirection.Right:
|
||||||
obj.Width = (float)(diff * length.X);
|
obj.Width = (float)(hitObjectLength * length.X);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,12 +51,14 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
|||||||
if (obj.HasNestedHitObjects)
|
if (obj.HasNestedHitObjects)
|
||||||
{
|
{
|
||||||
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
|
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
|
||||||
ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
|
|
||||||
|
// Nested hitobjects don't need to scroll, but they do need accurate positions
|
||||||
|
UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length)
|
public void UpdatePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length)
|
||||||
{
|
{
|
||||||
var timelinePosition = positionAt(currentTime, timeRange);
|
var timelinePosition = positionAt(currentTime, timeRange);
|
||||||
|
|
||||||
@ -80,21 +84,38 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the position which corresponds to a point in time.
|
||||||
|
/// This is a non-linear operation that depends on all the control points up to and including the one active at the time value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="time">The time to find the position at.</param>
|
||||||
|
/// <param name="timeRange">The amount of time visualised by the scrolling area.</param>
|
||||||
|
/// <returns>A positive value indicating the position at <paramref name="time"/>.</returns>
|
||||||
private double positionAt(double time, double timeRange)
|
private double positionAt(double time, double timeRange)
|
||||||
{
|
{
|
||||||
double length = 0;
|
double length = 0;
|
||||||
|
|
||||||
|
// We need to consider all timing points until the specified time and not just the currently-active one,
|
||||||
|
// since each timing point individually affects the positions of _all_ hitobjects after its start time
|
||||||
for (int i = 0; i < controlPoints.Count; i++)
|
for (int i = 0; i < controlPoints.Count; i++)
|
||||||
{
|
{
|
||||||
var current = controlPoints[i];
|
var current = controlPoints[i];
|
||||||
var next = i < controlPoints.Count - 1 ? controlPoints[i + 1] : null;
|
var next = i < controlPoints.Count - 1 ? controlPoints[i + 1] : null;
|
||||||
|
|
||||||
|
// We don't need to consider any control points beyond the current time, since it will not yet
|
||||||
|
// affect any hitobjects
|
||||||
if (i > 0 && current.StartTime > time)
|
if (i > 0 && current.StartTime > time)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Duration of the current control point
|
// Duration of the current control point
|
||||||
var currentDuration = (next?.StartTime ?? double.PositiveInfinity) - current.StartTime;
|
var currentDuration = (next?.StartTime ?? double.PositiveInfinity) - current.StartTime;
|
||||||
|
|
||||||
length += Math.Min(currentDuration, time - current.StartTime) * current.Multiplier / timeRange;
|
// We want to consider the minimal amount of time that this control point has affected,
|
||||||
|
// which may be either its duration, or the amount of time that has passed within it
|
||||||
|
var durationInCurrent = Math.Min(currentDuration, time - current.StartTime);
|
||||||
|
|
||||||
|
// Figure out how much of the time range the duration represents, and adjust it by the speed multiplier
|
||||||
|
length += durationInCurrent / timeRange * current.Multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
|
@ -46,7 +46,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
|||||||
var dragLayer = new DragLayer(maskContainer.Select);
|
var dragLayer = new DragLayer(maskContainer.Select);
|
||||||
dragLayer.DragEnd += () => maskSelection.UpdateVisibility();
|
dragLayer.DragEnd += () => maskSelection.UpdateVisibility();
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
dragLayer,
|
dragLayer,
|
||||||
maskSelection,
|
maskSelection,
|
||||||
|
@ -17,6 +17,7 @@ using osu.Game.Graphics;
|
|||||||
using osu.Game.Graphics.Backgrounds;
|
using osu.Game.Graphics.Backgrounds;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
@ -35,6 +36,8 @@ namespace osu.Game.Screens.Select.Carousel
|
|||||||
private Triangles triangles;
|
private Triangles triangles;
|
||||||
private StarCounter starCounter;
|
private StarCounter starCounter;
|
||||||
|
|
||||||
|
private BeatmapSetOverlay beatmapOverlay;
|
||||||
|
|
||||||
public DrawableCarouselBeatmap(CarouselBeatmap panel) : base(panel)
|
public DrawableCarouselBeatmap(CarouselBeatmap panel) : base(panel)
|
||||||
{
|
{
|
||||||
beatmap = panel.Beatmap;
|
beatmap = panel.Beatmap;
|
||||||
@ -42,8 +45,10 @@ namespace osu.Game.Screens.Select.Carousel
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(SongSelect songSelect, BeatmapManager manager)
|
private void load(SongSelect songSelect, BeatmapManager manager, BeatmapSetOverlay beatmapOverlay)
|
||||||
{
|
{
|
||||||
|
this.beatmapOverlay = beatmapOverlay;
|
||||||
|
|
||||||
if (songSelect != null)
|
if (songSelect != null)
|
||||||
{
|
{
|
||||||
startRequested = songSelect.FinaliseSelection;
|
startRequested = songSelect.FinaliseSelection;
|
||||||
@ -171,6 +176,10 @@ namespace osu.Game.Screens.Select.Carousel
|
|||||||
new OsuMenuItem("Play", MenuItemType.Highlighted, () => startRequested?.Invoke(beatmap)),
|
new OsuMenuItem("Play", MenuItemType.Highlighted, () => startRequested?.Invoke(beatmap)),
|
||||||
new OsuMenuItem("Edit", MenuItemType.Standard, () => editRequested?.Invoke(beatmap)),
|
new OsuMenuItem("Edit", MenuItemType.Standard, () => editRequested?.Invoke(beatmap)),
|
||||||
new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested?.Invoke(beatmap)),
|
new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested?.Invoke(beatmap)),
|
||||||
|
new OsuMenuItem("Details", MenuItemType.Standard, () =>
|
||||||
|
{
|
||||||
|
if (beatmap.OnlineBeatmapID.HasValue) beatmapOverlay?.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value);
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user