1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 19:22:54 +08:00

Merge branch 'master' into fix-switching-ruleset-unpauses-beatmap

This commit is contained in:
Dan Balasescu 2019-09-02 15:24:01 +09:00 committed by GitHub
commit daa2007b41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 236 additions and 67 deletions

View File

@ -0,0 +1,74 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests
{
public class TestSceneSwellJudgements : PlayerTestScene
{
protected new TestPlayer Player => (TestPlayer)base.Player;
public TestSceneSwellJudgements()
: base(new TaikoRuleset())
{
}
[Test]
public void TestZeroTickTimeOffsets()
{
AddUntilStep("gameplay finished", () => Player.ScoreProcessor.HasCompleted);
AddAssert("all tick offsets are 0", () => Player.Results.Where(r => r.Judgement is TaikoSwellTickJudgement).All(r => r.TimeOffset == 0));
}
protected override bool Autoplay => true;
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap<TaikoHitObject>
{
BeatmapInfo = { Ruleset = new TaikoRuleset().RulesetInfo },
HitObjects =
{
new Swell
{
StartTime = 1000,
Duration = 1000,
}
}
};
return beatmap;
}
protected override Player CreatePlayer(Ruleset ruleset) => new TestPlayer();
protected class TestPlayer : Player
{
public readonly List<JudgementResult> Results = new List<JudgementResult>();
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public TestPlayer()
: base(false, false)
{
}
[BackgroundDependencyLoader]
private void load()
{
ScoreProcessor.NewJudgement += r => Results.Add(r);
}
}
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables namespace osu.Game.Rulesets.Taiko.Objects.Drawables
@ -14,7 +15,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{ {
} }
public void TriggerResult(HitResult type) => ApplyResult(r => r.Type = type); protected override void UpdateInitialTransforms() => this.FadeOut();
public void TriggerResult(HitResult type)
{
HitObject.StartTime = Time.Current;
ApplyResult(r => r.Type = type);
}
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {

View File

@ -176,6 +176,8 @@ namespace osu.Game.Tests.Visual.Online
HasVideo = true, HasVideo = true,
HasStoryboard = true, HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(), Covers = new BeatmapSetOnlineCovers(),
Language = new BeatmapSetOnlineLanguage { Id = 3, Name = "English" },
Genre = new BeatmapSetOnlineGenre { Id = 4, Name = "Rock" },
}, },
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() },
Beatmaps = new List<BeatmapInfo> Beatmaps = new List<BeatmapInfo>

View File

@ -75,6 +75,28 @@ namespace osu.Game.Beatmaps
/// The availability of this beatmap set. /// The availability of this beatmap set.
/// </summary> /// </summary>
public BeatmapSetOnlineAvailability Availability { get; set; } public BeatmapSetOnlineAvailability Availability { get; set; }
/// <summary>
/// The song genre of this beatmap set.
/// </summary>
public BeatmapSetOnlineGenre Genre { get; set; }
/// <summary>
/// The song language of this beatmap set.
/// </summary>
public BeatmapSetOnlineLanguage Language { get; set; }
}
public class BeatmapSetOnlineGenre
{
public int Id { get; set; }
public string Name { get; set; }
}
public class BeatmapSetOnlineLanguage
{
public int Id { get; set; }
public string Name { get; set; }
} }
public class BeatmapSetOnlineCovers public class BeatmapSetOnlineCovers

View File

@ -69,6 +69,12 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"availability")] [JsonProperty(@"availability")]
private BeatmapSetOnlineAvailability availability { get; set; } private BeatmapSetOnlineAvailability availability { get; set; }
[JsonProperty(@"genre")]
private BeatmapSetOnlineGenre genre { get; set; }
[JsonProperty(@"language")]
private BeatmapSetOnlineLanguage language { get; set; }
[JsonProperty(@"beatmaps")] [JsonProperty(@"beatmaps")]
private IEnumerable<APIBeatmap> beatmaps { get; set; } private IEnumerable<APIBeatmap> beatmaps { get; set; }
@ -95,6 +101,8 @@ namespace osu.Game.Online.API.Requests.Responses
LastUpdated = lastUpdated, LastUpdated = lastUpdated,
Availability = availability, Availability = availability,
HasFavourited = hasFavourited, HasFavourited = hasFavourited,
Genre = genre,
Language = language
}, },
Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(), Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(),
}; };

View File

@ -36,7 +36,7 @@ namespace osu.Game.Overlays.BeatmapSet
public Info() public Info()
{ {
MetadataSection source, tags; MetadataSection source, tags, genre, language;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = 220; Height = 220;
Masking = true; Masking = true;
@ -83,11 +83,12 @@ namespace osu.Game.Overlays.BeatmapSet
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical, Direction = FillDirection.Full,
LayoutDuration = transition_duration,
Children = new[] Children = new[]
{ {
source = new MetadataSection("Source"), source = new MetadataSection("Source"),
genre = new MetadataSection("Genre") { Width = 0.5f },
language = new MetadataSection("Language") { Width = 0.5f },
tags = new MetadataSection("Tags"), tags = new MetadataSection("Tags"),
}, },
}, },
@ -119,6 +120,8 @@ namespace osu.Game.Overlays.BeatmapSet
{ {
source.Text = b.NewValue?.Metadata.Source ?? string.Empty; source.Text = b.NewValue?.Metadata.Source ?? string.Empty;
tags.Text = b.NewValue?.Metadata.Tags ?? string.Empty; tags.Text = b.NewValue?.Metadata.Tags ?? string.Empty;
genre.Text = b.NewValue?.OnlineInfo?.Genre?.Name ?? string.Empty;
language.Text = b.NewValue?.OnlineInfo?.Language?.Name ?? string.Empty;
}; };
} }
@ -139,7 +142,7 @@ namespace osu.Game.Overlays.BeatmapSet
{ {
if (string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(value))
{ {
this.FadeOut(transition_duration); Hide();
return; return;
} }
@ -149,12 +152,6 @@ namespace osu.Game.Overlays.BeatmapSet
} }
} }
public Color4 TextColour
{
get => textFlow.Colour;
set => textFlow.Colour = value;
}
public MetadataSection(string title) public MetadataSection(string title)
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;

View File

@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Chat.Selection
private Color4 topicColour; private Color4 topicColour;
private Color4 hoverColour; private Color4 hoverColour;
public IEnumerable<string> FilterTerms => new[] { channel.Name }; public IEnumerable<string> FilterTerms => new[] { channel.Name, channel.Topic };
public bool MatchingFilter public bool MatchingFilter
{ {

View File

@ -296,7 +296,11 @@ namespace osu.Game.Overlays.Profile.Header.Components
this.MoveTo(pos, 200, Easing.OutQuint); this.MoveTo(pos, 200, Easing.OutQuint);
} }
protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); protected override void PopIn()
{
instantMove |= !IsPresent;
this.FadeIn(200, Easing.OutQuint);
}
protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); protected override void PopOut() => this.FadeOut(200, Easing.OutQuint);
} }

View File

@ -9,6 +9,7 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Extensions.TypeExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Framework.Threading;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
@ -278,6 +279,14 @@ namespace osu.Game.Rulesets.Objects.Drawables
UpdateResult(false); UpdateResult(false);
} }
/// <summary>
/// Schedules an <see cref="Action"/> to this <see cref="DrawableHitObject"/>.
/// </summary>
/// <remarks>
/// Only provided temporarily until hitobject pooling is implemented.
/// </remarks>
protected internal new ScheduledDelegate Schedule(Action action) => base.Schedule(action);
private double? lifetimeStart; private double? lifetimeStart;
public override double LifetimeStart public override double LifetimeStart

View File

@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Scoring
/// <summary> /// <summary>
/// Whether all <see cref="Judgement"/>s have been processed. /// Whether all <see cref="Judgement"/>s have been processed.
/// </summary> /// </summary>
protected virtual bool HasCompleted => false; public virtual bool HasCompleted => false;
/// <summary> /// <summary>
/// Whether this ScoreProcessor has already triggered the failed state. /// Whether this ScoreProcessor has already triggered the failed state.
@ -205,7 +205,7 @@ namespace osu.Game.Rulesets.Scoring
private const double combo_portion = 0.7; private const double combo_portion = 0.7;
private const double max_score = 1000000; private const double max_score = 1000000;
protected sealed override bool HasCompleted => JudgedHits == MaxHits; public sealed override bool HasCompleted => JudgedHits == MaxHits;
protected int MaxHits { get; private set; } protected int MaxHits { get; private set; }
protected int JudgedHits { get; private set; } protected int JudgedHits { get; private set; }

View File

@ -215,10 +215,6 @@ namespace osu.Game.Rulesets.UI
continueResume(); continueResume();
} }
public ResumeOverlay ResumeOverlay { get; private set; }
protected virtual ResumeOverlay CreateResumeOverlay() => null;
/// <summary> /// <summary>
/// Creates and adds the visual representation of a <see cref="TObject"/> to this <see cref="DrawableRuleset{TObject}"/>. /// Creates and adds the visual representation of a <see cref="TObject"/> to this <see cref="DrawableRuleset{TObject}"/>.
/// </summary> /// </summary>
@ -389,6 +385,13 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
public abstract GameplayCursorContainer Cursor { get; } public abstract GameplayCursorContainer Cursor { get; }
/// <summary>
/// An optional overlay used when resuming gameplay from a paused state.
/// </summary>
public ResumeOverlay ResumeOverlay { get; protected set; }
protected virtual ResumeOverlay CreateResumeOverlay() => null;
/// <summary> /// <summary>
/// Sets a replay to be used, overriding local input. /// Sets a replay to be used, overriding local input.
/// </summary> /// </summary>

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Caching; using osu.Framework.Caching;
@ -50,8 +51,13 @@ namespace osu.Game.Rulesets.UI.Scrolling
public override bool Remove(DrawableHitObject hitObject) public override bool Remove(DrawableHitObject hitObject)
{ {
var result = base.Remove(hitObject); var result = base.Remove(hitObject);
if (result) if (result)
{
initialStateCache.Invalidate(); initialStateCache.Invalidate();
hitObjectInitialStateCache.Remove(hitObject);
}
return result; return result;
} }
@ -86,13 +92,34 @@ namespace osu.Game.Rulesets.UI.Scrolling
scrollingInfo.Algorithm.Reset(); scrollingInfo.Algorithm.Reset();
foreach (var obj in Objects) foreach (var obj in Objects)
{
computeLifetimeStartRecursive(obj);
computeInitialStateRecursive(obj); computeInitialStateRecursive(obj);
}
initialStateCache.Validate(); initialStateCache.Validate();
} }
} }
private void computeInitialStateRecursive(DrawableHitObject hitObject) private void computeLifetimeStartRecursive(DrawableHitObject hitObject)
{ {
hitObject.LifetimeStart = scrollingInfo.Algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, timeRange.Value);
foreach (var obj in hitObject.NestedHitObjects)
computeLifetimeStartRecursive(obj);
}
private readonly Dictionary<DrawableHitObject, Cached> hitObjectInitialStateCache = new Dictionary<DrawableHitObject, Cached>();
// Cant use AddOnce() since the delegate is re-constructed every invocation
private void computeInitialStateRecursive(DrawableHitObject hitObject) => hitObject.Schedule(() =>
{
if (!hitObjectInitialStateCache.TryGetValue(hitObject, out var cached))
cached = hitObjectInitialStateCache[hitObject] = new Cached();
if (cached.IsValid)
return;
double endTime = hitObject.HitObject.StartTime; double endTime = hitObject.HitObject.StartTime;
if (hitObject.HitObject is IHasEndTime e) if (hitObject.HitObject is IHasEndTime e)
@ -113,7 +140,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
} }
} }
hitObject.LifetimeStart = scrollingInfo.Algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, timeRange.Value);
hitObject.LifetimeEnd = scrollingInfo.Algorithm.TimeAt(scrollLength * safe_lifetime_end_multiplier, endTime, timeRange.Value, scrollLength); hitObject.LifetimeEnd = scrollingInfo.Algorithm.TimeAt(scrollLength * safe_lifetime_end_multiplier, endTime, timeRange.Value, scrollLength);
foreach (var obj in hitObject.NestedHitObjects) foreach (var obj in hitObject.NestedHitObjects)
@ -123,7 +149,9 @@ namespace osu.Game.Rulesets.UI.Scrolling
// Nested hitobjects don't need to scroll, but they do need accurate positions // Nested hitobjects don't need to scroll, but they do need accurate positions
updatePosition(obj, hitObject.HitObject.StartTime); updatePosition(obj, hitObject.HitObject.StartTime);
} }
}
cached.Validate();
});
protected override void UpdateAfterChildrenLife() protected override void UpdateAfterChildrenLife()
{ {

View File

@ -4,6 +4,9 @@
using System; using System;
using System.Linq; using System.Linq;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Colour;
using osu.Game.Graphics;
using osuTK.Graphics;
namespace osu.Game.Screens.Edit namespace osu.Game.Screens.Edit
{ {
@ -35,5 +38,41 @@ namespace osu.Game.Screens.Edit
protected override int DefaultMinValue => VALID_DIVISORS.First(); protected override int DefaultMinValue => VALID_DIVISORS.First();
protected override int DefaultMaxValue => VALID_DIVISORS.Last(); protected override int DefaultMaxValue => VALID_DIVISORS.Last();
protected override int DefaultPrecision => 1; protected override int DefaultPrecision => 1;
/// <summary>
/// Retrieves the appropriate colour for a beat divisor.
/// </summary>
/// <param name="beatDivisor">The beat divisor.</param>
/// <param name="colours">The set of colours.</param>
/// <returns>The applicable colour from <paramref name="colours"/> for <paramref name="beatDivisor"/>.</returns>
public static ColourInfo GetColourFor(int beatDivisor, OsuColour colours)
{
switch (beatDivisor)
{
case 2:
return colours.BlueLight;
case 4:
return colours.Blue;
case 8:
return colours.BlueDarker;
case 16:
return colours.PurpleDark;
case 3:
return colours.YellowLight;
case 6:
return colours.Yellow;
case 12:
return colours.YellowDarker;
default:
return Color4.White;
}
}
} }
} }

View File

@ -188,6 +188,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
{ {
private Marker marker; private Marker marker;
[Resolved]
private OsuColour colours { get; set; }
private readonly BindableBeatDivisor beatDivisor; private readonly BindableBeatDivisor beatDivisor;
private readonly int[] availableDivisors; private readonly int[] availableDivisors;
@ -204,11 +207,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
{ {
foreach (var t in availableDivisors) foreach (var t in availableDivisors)
{ {
AddInternal(new Tick(t) AddInternal(new Tick
{ {
Anchor = Anchor.TopLeft, Anchor = Anchor.TopLeft,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
RelativePositionAxes = Axes.X, RelativePositionAxes = Axes.X,
Colour = BindableBeatDivisor.GetColourFor(t, colours),
X = getMappedPosition(t) X = getMappedPosition(t)
}); });
} }
@ -284,11 +288,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
private class Tick : CompositeDrawable private class Tick : CompositeDrawable
{ {
private readonly int divisor; public Tick()
public Tick(int divisor)
{ {
this.divisor = divisor;
Size = new Vector2(2.5f, 10); Size = new Vector2(2.5f, 10);
InternalChild = new Box { RelativeSizeAxes = Axes.Both }; InternalChild = new Box { RelativeSizeAxes = Axes.Both };
@ -296,42 +297,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
CornerRadius = 0.5f; CornerRadius = 0.5f;
Masking = true; Masking = true;
} }
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Colour = getColourForDivisor(divisor, colours);
}
private ColourInfo getColourForDivisor(int divisor, OsuColour colours)
{
switch (divisor)
{
case 2:
return colours.BlueLight;
case 4:
return colours.Blue;
case 8:
return colours.BlueDarker;
case 16:
return colours.PurpleDark;
case 3:
return colours.YellowLight;
case 6:
return colours.Yellow;
case 12:
return colours.YellowDarker;
default:
return Color4.White;
}
}
} }
private class Marker : CompositeDrawable private class Marker : CompositeDrawable

View File

@ -77,7 +77,7 @@ namespace osu.Game.Screens.Menu
Scheduler.AddDelayed(delegate Scheduler.AddDelayed(delegate
{ {
// Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu. // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu.
if (menuMusic.Value) if (menuMusic.Value)
{ {
track.Restart(); track.Restart();

View File

@ -47,7 +47,7 @@ namespace osu.Game.Screens.Menu
private const float visualiser_rounds = 5; private const float visualiser_rounds = 5;
/// <summary> /// <summary>
/// How much should each bar go down each milisecond (based on a full bar). /// How much should each bar go down each millisecond (based on a full bar).
/// </summary> /// </summary>
private const float decay_per_milisecond = 0.0024f; private const float decay_per_milisecond = 0.0024f;
@ -161,7 +161,7 @@ namespace osu.Game.Screens.Menu
private IShader shader; private IShader shader;
private Texture texture; private Texture texture;
//Asuming the logo is a circle, we don't need a second dimension. //Assuming the logo is a circle, we don't need a second dimension.
private float size; private float size;
private Color4 colour; private Color4 colour;

View File

@ -229,7 +229,7 @@ namespace osu.Game.Screens.Menu
} }
/// <summary> /// <summary>
/// Schedule a new extenral animation. Handled queueing and finishing previous animations in a sane way. /// Schedule a new external animation. Handled queueing and finishing previous animations in a sane way.
/// </summary> /// </summary>
/// <param name="action">The animation to be performed</param> /// <param name="action">The animation to be performed</param>
/// <param name="waitForPrevious">If true, the new animation is delayed until all previous transforms finish. If false, existing transformed are cleared.</param> /// <param name="waitForPrevious">If true, the new animation is delayed until all previous transforms finish. If false, existing transformed are cleared.</param>

View File

@ -178,6 +178,7 @@ namespace osu.Game.Screens.Play
}, },
// display the cursor above some HUD elements. // display the cursor above some HUD elements.
DrawableRuleset.Cursor?.CreateProxy() ?? new Container(), DrawableRuleset.Cursor?.CreateProxy() ?? new Container(),
DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(),
HUDOverlay = new HUDOverlay(ScoreProcessor, DrawableRuleset, Mods.Value) HUDOverlay = new HUDOverlay(ScoreProcessor, DrawableRuleset, Mods.Value)
{ {
HoldToQuit = HoldToQuit =

View File

@ -3,6 +3,7 @@
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets; using osu.Game.Rulesets;
@ -40,6 +41,8 @@ namespace osu.Game.Tests.Visual
protected virtual bool AllowFail => false; protected virtual bool AllowFail => false;
protected virtual bool Autoplay => false;
private void loadPlayer() private void loadPlayer()
{ {
var beatmap = CreateBeatmap(ruleset.RulesetInfo); var beatmap = CreateBeatmap(ruleset.RulesetInfo);
@ -49,6 +52,13 @@ namespace osu.Game.Tests.Visual
if (!AllowFail) if (!AllowFail)
Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) }; Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) };
if (Autoplay)
{
var mod = ruleset.GetAutoplayMod();
if (mod != null)
Mods.Value = Mods.Value.Concat(mod.Yield()).ToArray();
}
Player = CreatePlayer(ruleset); Player = CreatePlayer(ruleset);
LoadScreen(Player); LoadScreen(Player);
} }