1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-18 11:02:57 +08:00

Merge branch 'master' into chart-button-rankings

This commit is contained in:
Dan Balasescu 2020-02-26 09:10:39 +09:00 committed by GitHub
commit 92bbd71e60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 277 additions and 164 deletions

View File

@ -1,8 +0,0 @@
---
name: Mobile Report
about: ⚠ Due to current development priorities we are not accepting mobile reports at this time (unless you're willing to fix them yourself!)
---
**PLEASE READ** ⚠: Due to prioritising finishing the client for desktop first we are not accepting reports related to mobile platforms for the time being, unless you're willing to fix them.
If you'd like to report a problem or suggest a feature and then work on it, feel free to open an issue and highlight that you'd like to address it yourself in the issue body; mobile pull requests are also welcome.
Otherwise, please check back in the future when the focus of development shifts towards mobile!

View File

@ -8,4 +8,7 @@ about: Issues regarding encountered bugs.
**osu!lazer version:**
**Logs:**
**Logs:**
*please attach logs here, which are located at:*
- `%AppData%/osu/logs` *(on Windows),*
- `~/.local/share/osu/logs` *(on Linux & macOS).*

View File

@ -8,6 +8,9 @@ about: Issues regarding crashes or permanent freezes.
**osu!lazer version:**
**Logs:**
**Logs:**
*please attach logs here, which are located at:*
- `%AppData%/osu/logs` *(on Windows),*
- `~/.local/share/osu/logs` *(on Linux & macOS).*
**Computer Specifications:**

View File

@ -15,7 +15,7 @@ Rhythm is just a *click* away. The future of [osu!](https://osu.ppy.sh) and the
This project is under heavy development, but is in a stable state. Users are encouraged to try it out and keep it installed alongside the stable *osu!* client. It will continue to evolve to the point of eventually replacing the existing stable client as an update.
We are accepting bug reports (please report with as much detail as possible). Feature requests are also welcome, but understand that our focus is on completing the game to feature parity before adding new features. A few resources are available as starting points to getting involved and understanding the project:
We are accepting bug reports (please report with as much detail as possible and follow the existing issue templates). Feature requests are also welcome, but understand that our focus is on completing the game to feature parity before adding new features. A few resources are available as starting points to getting involved and understanding the project:
- Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer).
- You can learn more about our approach to [project management](https://github.com/ppy/osu/wiki/Project-management).

View File

@ -54,6 +54,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.221.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.221.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.225.0" />
</ItemGroup>
</Project>

View File

@ -29,6 +29,12 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
[Test]
public void TestBananaShower()
{
AddUntilStep("player is done", () => !Player.ValidForResume);
}
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
@ -40,7 +46,7 @@ namespace osu.Game.Rulesets.Catch.Tests
}
};
beatmap.HitObjects.Add(new BananaShower { StartTime = 200, Duration = 5000, NewCombo = true });
beatmap.HitObjects.Add(new BananaShower { StartTime = 200, Duration = 3000, NewCombo = true });
return beatmap;
}

View File

@ -0,0 +1,56 @@
// 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 NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Catch.Tests
{
public class TestSceneJuiceStream : PlayerTestScene
{
public TestSceneJuiceStream()
: base(new CatchRuleset())
{
}
[Test]
public void TestJuiceStreamEndingCombo()
{
AddUntilStep("player is done", () => !Player.ValidForResume);
}
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 5, SliderMultiplier = 2 },
Ruleset = ruleset
},
HitObjects = new List<HitObject>
{
new JuiceStream
{
X = 0.5f,
Path = new SliderPath(PathType.Linear, new[]
{
Vector2.Zero,
new Vector2(0, 100)
}),
StartTime = 200
},
new Banana
{
X = 0.5f,
StartTime = 1000,
NewCombo = true
}
}
};
}
}

View File

@ -1,6 +1,7 @@
// 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 osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Catch.Objects
@ -11,6 +12,8 @@ namespace osu.Game.Rulesets.Catch.Objects
public override bool LastInCombo => true;
public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override void CreateNestedHitObjects()
{
base.CreateNestedHitObjects();

View File

@ -7,6 +7,7 @@ using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
@ -19,6 +20,8 @@ namespace osu.Game.Rulesets.Catch.Objects
/// </summary>
private const float base_scoring_distance = 100;
public override Judgement CreateJudgement() => new IgnoreJudgement();
public int RepeatCount { get; set; }
public double Velocity;

View File

@ -89,7 +89,7 @@ namespace osu.Game.Rulesets.Catch.UI
if (fruit.HitObject.LastInCombo)
{
if (((CatchJudgement)result.Judgement).ShouldExplodeFor(result))
if (result.Judgement is CatchJudgement catchJudgement && catchJudgement.ShouldExplodeFor(result))
runAfterLoaded(() => MovableCatcher.Explode());
else
MovableCatcher.Drop();

View File

@ -1,14 +0,0 @@
// 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 osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Judgements
{
public class HoldNoteJudgement : ManiaJudgement
{
public override bool AffectsCombo => false;
protected override int NumericResultFor(HitResult result) => 0;
}
}

View File

@ -1,6 +1,7 @@
// 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 osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Objects
@ -8,5 +9,7 @@ namespace osu.Game.Rulesets.Mania.Objects
public class BarLine : ManiaHitObject, IBarLine
{
public bool Major { get; set; }
public override Judgement CreateJudgement() => new IgnoreJudgement();
}
}

View File

@ -71,8 +71,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
}
protected override void UpdateStateTransforms(ArmedState state)
{
}
protected override void UpdateStateTransforms(ArmedState state) => this.FadeOut(150);
}
}

View File

@ -4,7 +4,6 @@
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
@ -103,7 +102,7 @@ namespace osu.Game.Rulesets.Mania.Objects
}
}
public override Judgement CreateJudgement() => new HoldNoteJudgement();
public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}

View File

@ -1,14 +0,0 @@
// 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 osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Judgements
{
public class OsuSliderTailJudgement : OsuJudgement
{
public override bool AffectsCombo => false;
protected override int NumericResultFor(HitResult result) => 0;
}
}

View File

@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
private const int spacing = 32;
private const double preempt = 800;
public override bool RemoveWhenNotAlive => false;
/// <summary>
/// The start time of <see cref="Start"/>.
/// </summary>
@ -79,27 +81,31 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
drawableObject.HitObject.DefaultsApplied += scheduleRefresh;
}
private void scheduleRefresh() => Scheduler.AddOnce(refresh);
private void scheduleRefresh()
{
Scheduler.AddOnce(refresh);
}
private void refresh()
{
ClearInternal();
if (End == null)
return;
OsuHitObject osuStart = Start.HitObject;
OsuHitObject osuEnd = End.HitObject;
double startTime = osuStart.GetEndTime();
if (osuEnd.NewCombo)
return;
LifetimeStart = startTime;
if (osuStart is Spinner || osuEnd is Spinner)
OsuHitObject osuEnd = End?.HitObject;
if (osuEnd == null || osuEnd.NewCombo || osuStart is Spinner || osuEnd is Spinner)
{
// ensure we always set a lifetime for full LifetimeManagementContainer benefits
LifetimeEnd = LifetimeStart;
return;
}
Vector2 startPosition = osuStart.EndPosition;
Vector2 endPosition = osuEnd.Position;
double startTime = osuStart.GetEndTime();
double endTime = osuEnd.StartTime;
Vector2 distanceVector = endPosition - startPosition;
@ -107,6 +113,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
float rotation = (float)(Math.Atan2(distanceVector.Y, distanceVector.X) * (180 / Math.PI));
double duration = endTime - startTime;
double? firstTransformStartTime = null;
double finalTransformEndTime = startTime;
for (int d = (int)(spacing * 1.5); d < distance - spacing; d += spacing)
{
float fraction = (float)d / distance;
@ -125,16 +134,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
Scale = new Vector2(1.5f * osuEnd.Scale),
});
if (firstTransformStartTime == null)
firstTransformStartTime = fadeInTime;
using (fp.BeginAbsoluteSequence(fadeInTime))
{
fp.FadeIn(osuEnd.TimeFadeIn);
fp.ScaleTo(osuEnd.Scale, osuEnd.TimeFadeIn, Easing.Out);
fp.MoveTo(pointEndPosition, osuEnd.TimeFadeIn, Easing.Out);
fp.Delay(fadeOutTime - fadeInTime).FadeOut(osuEnd.TimeFadeIn);
}
fp.Expire(true);
finalTransformEndTime = fadeOutTime + osuEnd.TimeFadeIn;
}
}
// todo: use Expire() on FollowPoints and take lifetime from them when https://github.com/ppy/osu-framework/issues/3300 is fixed.
LifetimeStart = firstTransformStartTime ?? startTime;
LifetimeEnd = finalTransformEndTime;
}
}
}

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
/// <summary>
/// Visualises connections between <see cref="DrawableOsuHitObject"/>s.
/// </summary>
public class FollowPointRenderer : CompositeDrawable
public class FollowPointRenderer : LifetimeManagementContainer
{
/// <summary>
/// All the <see cref="FollowPointConnection"/>s contained by this <see cref="FollowPointRenderer"/>.
@ -45,8 +45,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
/// <returns>The index of <paramref name="connection"/> in <see cref="connections"/>.</returns>
private void addConnection(FollowPointConnection connection)
{
AddInternal(connection);
// Groups are sorted by their start time when added such that the index can be used to post-process other surrounding connections
int index = connections.AddInPlace(connection, Comparer<FollowPointConnection>.Create((g1, g2) => g1.StartTime.Value.CompareTo(g2.StartTime.Value)));
@ -74,6 +72,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
FollowPointConnection previousConnection = connections[index - 1];
previousConnection.End = connection.Start;
}
AddInternal(connection);
}
/// <summary>

View File

@ -4,7 +4,6 @@
using osu.Framework.Bindables;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects
@ -23,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects
pathVersion.BindValueChanged(_ => Position = slider.EndPosition);
}
public override Judgement CreateJudgement() => new OsuSliderTailJudgement();
public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}

View File

@ -8,7 +8,6 @@ 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;
@ -28,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
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));
AddAssert("all tick offsets are 0", () => Player.Results.Where(r => r.HitObject is SwellTick).All(r => r.TimeOffset == 0));
}
protected override bool Autoplay => true;

View File

@ -1,6 +1,7 @@
// 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 osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Taiko.Objects
@ -8,5 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
public class BarLine : TaikoHitObject, IBarLine
{
public bool Major { get; set; }
public override Judgement CreateJudgement() => new IgnoreJudgement();
}
}

View File

@ -54,5 +54,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Alpha = 0.75f
});
}
protected override void UpdateStateTransforms(ArmedState state) => this.FadeOut(150);
}
}

View File

@ -3,13 +3,12 @@
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects
{
public class SwellTick : TaikoHitObject
{
public override Judgement CreateJudgement() => new TaikoSwellTickJudgement();
public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}

View File

@ -154,6 +154,7 @@ namespace osu.Game.Tests.Gameplay
private class JudgeableHitObject : HitObject
{
public override Judgement CreateJudgement() => new Judgement();
protected override HitWindows CreateHitWindows() => new HitWindows();
}
}
}

View File

@ -11,8 +11,8 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures;
using osu.Framework.Testing;
using osu.Game.Audio;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
@ -78,7 +78,7 @@ namespace osu.Game.Tests.Gameplay
}
}
private class TestHitObjectWithCombo : HitObject, IHasComboInformation
private class TestHitObjectWithCombo : ConvertHitObject, IHasComboInformation
{
public bool NewCombo { get; } = false;
public int ComboOffset { get; } = 0;

View File

@ -20,6 +20,7 @@ using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.UI;
@ -289,7 +290,7 @@ namespace osu.Game.Tests.Visual.Gameplay
#region HitObject
private class TestHitObject : HitObject, IHasEndTime
private class TestHitObject : ConvertHitObject, IHasEndTime
{
public double EndTime { get; set; }

View File

@ -2,16 +2,17 @@
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Game.Rulesets.Objects;
using System;
using System.Collections.Generic;
using osu.Game.Rulesets.Judgements;
using osu.Framework.Utils;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Threading;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Scoring;
@ -43,6 +44,22 @@ namespace osu.Game.Tests.Visual.Gameplay
AddRepeatStep("New max negative", () => newJudgement(-hitWindows.WindowFor(HitResult.Meh)), 20);
AddRepeatStep("New max positive", () => newJudgement(hitWindows.WindowFor(HitResult.Meh)), 20);
AddStep("New fixed judgement (50ms)", () => newJudgement(50));
AddStep("Judgement barrage", () =>
{
int runCount = 0;
ScheduledDelegate del = null;
del = Scheduler.AddDelayed(() =>
{
newJudgement(runCount++ / 10f);
if (runCount == 500)
// ReSharper disable once AccessToModifiedClosure
del?.Cancel();
}, 10, true);
});
}
[Test]

View File

@ -13,6 +13,8 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osuTK;
using JetBrains.Annotations;
using NUnit.Framework;
namespace osu.Game.Tests.Visual.Online
{
@ -30,6 +32,8 @@ namespace osu.Game.Tests.Visual.Online
private readonly BindableBool showDeleted = new BindableBool();
private readonly Container content;
private TestCommentsPage commentsPage;
public TestSceneCommentsPage()
{
Add(new FillFlowContainer
@ -57,15 +61,29 @@ namespace osu.Game.Tests.Visual.Online
}
}
});
}
AddStep("load comments", () => createPage(getCommentBundle()));
AddStep("load empty comments", () => createPage(getEmptyCommentBundle()));
[Test]
public void TestAppendDuplicatedComment()
{
AddStep("Create page", () => createPage(getCommentBundle()));
AddAssert("Dictionary length is 10", () => commentsPage?.DictionaryLength == 10);
AddStep("Append existing comment", () => commentsPage?.AppendComments(getCommentSubBundle()));
AddAssert("Dictionary length is 10", () => commentsPage?.DictionaryLength == 10);
}
[Test]
public void TestEmptyBundle()
{
AddStep("Create page", () => createPage(getEmptyCommentBundle()));
AddAssert("Dictionary length is 0", () => commentsPage?.DictionaryLength == 0);
}
private void createPage(CommentBundle commentBundle)
{
commentsPage = null;
content.Clear();
content.Add(new CommentsPage(commentBundle)
content.Add(commentsPage = new TestCommentsPage(commentBundle)
{
ShowDeleted = { BindTarget = showDeleted }
});
@ -182,5 +200,33 @@ namespace osu.Game.Tests.Visual.Online
}
},
};
private CommentBundle getCommentSubBundle() => new CommentBundle
{
Comments = new List<Comment>
{
new Comment
{
Id = 1,
Message = "Simple test comment",
LegacyName = "TestUser1",
CreatedAt = DateTimeOffset.Now,
VotesCount = 5
},
},
IncludedComments = new List<Comment>(),
};
private class TestCommentsPage : CommentsPage
{
public TestCommentsPage(CommentBundle commentBundle)
: base(commentBundle)
{
}
public new void AppendComments([NotNull] CommentBundle bundle) => base.AppendComments(bundle);
public int DictionaryLength => CommentDictionary.Count;
}
}
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Overlays.Direct;
using osu.Game.Rulesets;
@ -14,7 +15,8 @@ using osuTK;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneDirectPanel : OsuTestScene
[Cached(typeof(IPreviewTrackOwner))]
public class TestSceneDirectPanel : OsuTestScene, IPreviewTrackOwner
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{

View File

@ -13,6 +13,7 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
@ -194,7 +195,7 @@ namespace osu.Game.Tests.Visual.SongSelect
public new BufferedWedgeInfo Info => base.Info;
}
private class TestHitObject : HitObject, IHasPosition
private class TestHitObject : ConvertHitObject, IHasPosition
{
public float X { get; } = 0;
public float Y { get; } = 0;

View File

@ -64,7 +64,7 @@ namespace osu.Game.Graphics.UserInterface
protected override void Update()
{
base.Update();
MainContents.Size = new Vector2(Math.Min(100, Math.Min(DrawWidth, DrawHeight) * 0.25f));
MainContents.Size = new Vector2(Math.Clamp(Math.Min(DrawWidth, DrawHeight) * 0.25f, 30, 100));
}
protected override void Dispose(bool isDisposing)

View File

@ -93,7 +93,7 @@ namespace osu.Game.Graphics.UserInterface
private void rotate()
{
spinner.Spin(spin_duration * 4, RotationDirection.Clockwise);
spinner.Spin(spin_duration * 3.5f, RotationDirection.Clockwise);
MainContents.RotateTo(0).Then()
.RotateTo(90, spin_duration, Easing.InOutQuart).Then()

View File

@ -61,15 +61,15 @@ namespace osu.Game.Overlays.Comments
return;
}
appendComments(commentBundle);
AppendComments(commentBundle);
}
private DrawableComment getDrawableComment(Comment comment)
{
if (commentDictionary.TryGetValue(comment.Id, out var existing))
if (CommentDictionary.TryGetValue(comment.Id, out var existing))
return existing;
return commentDictionary[comment.Id] = new DrawableComment(comment)
return CommentDictionary[comment.Id] = new DrawableComment(comment)
{
ShowDeleted = { BindTarget = ShowDeleted },
Sort = { BindTarget = Sort },
@ -81,31 +81,28 @@ namespace osu.Game.Overlays.Comments
{
var request = new GetCommentsRequest(CommentableId.Value, Type.Value, Sort.Value, page, drawableComment.Comment.Id);
request.Success += response => Schedule(() => appendComments(response));
request.Success += response => Schedule(() => AppendComments(response));
api.PerformAsync(request);
}
private readonly Dictionary<long, DrawableComment> commentDictionary = new Dictionary<long, DrawableComment>();
protected readonly Dictionary<long, DrawableComment> CommentDictionary = new Dictionary<long, DrawableComment>();
/// <summary>
/// Appends retrieved comments to the subtree rooted of comments in this page.
/// </summary>
/// <param name="bundle">The bundle of comments to add.</param>
private void appendComments([NotNull] CommentBundle bundle)
protected void AppendComments([NotNull] CommentBundle bundle)
{
var orphaned = new List<Comment>();
foreach (var topLevel in bundle.Comments)
addNewComment(topLevel);
foreach (var child in bundle.IncludedComments)
foreach (var comment in bundle.Comments.Concat(bundle.IncludedComments))
{
// Included comments can contain the parent comment, which already exists in the hierarchy.
if (commentDictionary.ContainsKey(child.Id))
// Exclude possible duplicated comments.
if (CommentDictionary.ContainsKey(comment.Id))
continue;
addNewComment(child);
addNewComment(comment);
}
// Comments whose parents were seen later than themselves can now be added.
@ -121,7 +118,7 @@ namespace osu.Game.Overlays.Comments
// Comments that have no parent are added as top-level comments to the flow.
flow.Add(drawableComment);
}
else if (commentDictionary.TryGetValue(comment.ParentId.Value, out var parentDrawable))
else if (CommentDictionary.TryGetValue(comment.ParentId.Value, out var parentDrawable))
{
// The comment's parent has already been seen, so the parent<-> child links can be added.
comment.ParentComment = parentDrawable.Comment;

View File

@ -1,11 +1,11 @@
// 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.
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Taiko.Judgements
namespace osu.Game.Rulesets.Judgements
{
public class TaikoSwellTickJudgement : TaikoJudgement
public class IgnoreJudgement : Judgement
{
public override bool AffectsCombo => false;

View File

@ -99,12 +99,9 @@ namespace osu.Game.Rulesets.Objects.Drawables
{
var judgement = HitObject.CreateJudgement();
if (judgement != null)
{
Result = CreateResult(judgement);
if (Result == null)
throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}.");
}
Result = CreateResult(judgement);
if (Result == null)
throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}.");
loadSamples();
}
@ -265,7 +262,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
}
}
if (state.Value != ArmedState.Idle && LifetimeEnd == double.MaxValue)
if (state.Value != ArmedState.Idle && LifetimeEnd == double.MaxValue || HitObject.HitWindows == null)
Expire();
}
else

View File

@ -144,9 +144,9 @@ namespace osu.Game.Rulesets.Objects
/// <summary>
/// Creates the <see cref="Judgement"/> that represents the scoring information for this <see cref="HitObject"/>.
/// May be null.
/// </summary>
public virtual Judgement CreateJudgement() => null;
[NotNull]
public virtual Judgement CreateJudgement() => new Judgement();
/// <summary>
/// Creates the <see cref="HitWindows"/> for this <see cref="HitObject"/>.

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
/// <summary>
/// Legacy osu!catch Hit-type, used for parsing Beatmaps.
/// </summary>
internal sealed class ConvertHit : HitObject, IHasCombo, IHasXPosition
internal sealed class ConvertHit : ConvertHitObject, IHasCombo, IHasXPosition
{
public float X { get; set; }

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
/// <summary>
/// Legacy osu!catch Spinner-type, used for parsing Beatmaps.
/// </summary>
internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasXPosition, IHasCombo
internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasXPosition, IHasCombo
{
public double EndTime { get; set; }

View File

@ -0,0 +1,18 @@
// 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 osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Objects.Legacy
{
/// <summary>
/// A hit object only used for conversion, not actual gameplay.
/// </summary>
internal abstract class ConvertHitObject : HitObject
{
public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -9,7 +9,7 @@ using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Objects.Legacy
{
internal abstract class ConvertSlider : HitObject, IHasCurve, IHasLegacyLastTickOffset
internal abstract class ConvertSlider : ConvertHitObject, IHasCurve, IHasLegacyLastTickOffset
{
/// <summary>
/// Scoring distance with a speed-adjusted beat length of 1 second.

View File

@ -2,17 +2,14 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Objects.Legacy.Mania
{
/// <summary>
/// Legacy osu!mania Hit-type, used for parsing Beatmaps.
/// </summary>
internal sealed class ConvertHit : HitObject, IHasXPosition
internal sealed class ConvertHit : ConvertHitObject, IHasXPosition
{
public float X { get; set; }
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -2,18 +2,15 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Objects.Legacy.Mania
{
internal sealed class ConvertHold : HitObject, IHasXPosition, IHasEndTime
internal sealed class ConvertHold : ConvertHitObject, IHasXPosition, IHasEndTime
{
public float X { get; set; }
public double EndTime { get; set; }
public double Duration => EndTime - StartTime;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Objects.Legacy.Mania
{
@ -12,7 +11,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition
{
public float X { get; set; }
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -2,21 +2,18 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Objects.Legacy.Mania
{
/// <summary>
/// Legacy osu!mania Spinner-type, used for parsing Beatmaps.
/// </summary>
internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasXPosition
internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasXPosition
{
public double EndTime { get; set; }
public double Duration => EndTime - StartTime;
public float X { get; set; }
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osuTK;
namespace osu.Game.Rulesets.Objects.Legacy.Osu
@ -10,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
/// <summary>
/// Legacy osu! Hit-type, used for parsing Beatmaps.
/// </summary>
internal sealed class ConvertHit : HitObject, IHasPosition, IHasCombo
internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo
{
public Vector2 Position { get; set; }
@ -21,7 +20,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
public bool NewCombo { get; set; }
public int ComboOffset { get; set; }
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osuTK;
namespace osu.Game.Rulesets.Objects.Legacy.Osu
@ -21,7 +20,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
public bool NewCombo { get; set; }
public int ComboOffset { get; set; }
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osuTK;
namespace osu.Game.Rulesets.Objects.Legacy.Osu
@ -10,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
/// <summary>
/// Legacy osu! Spinner-type, used for parsing Beatmaps.
/// </summary>
internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasPosition, IHasCombo
internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime, IHasPosition, IHasCombo
{
public double EndTime { get; set; }
@ -22,8 +21,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
public float Y => Position.Y;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
public bool NewCombo { get; set; }
public int ComboOffset { get; set; }

View File

@ -1,15 +1,12 @@
// 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 osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Objects.Legacy.Taiko
{
/// <summary>
/// Legacy osu!taiko Hit-type, used for parsing Beatmaps.
/// </summary>
internal sealed class ConvertHit : HitObject
internal sealed class ConvertHit : ConvertHitObject
{
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -1,8 +1,6 @@
// 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 osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Objects.Legacy.Taiko
{
/// <summary>
@ -10,6 +8,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
/// </summary>
internal sealed class ConvertSlider : Legacy.ConvertSlider
{
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -2,19 +2,16 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Objects.Legacy.Taiko
{
/// <summary>
/// Legacy osu!taiko Spinner-type, used for parsing Beatmaps.
/// </summary>
internal sealed class ConvertSpinner : HitObject, IHasEndTime
internal sealed class ConvertSpinner : ConvertHitObject, IHasEndTime
{
public double EndTime { get; set; }
public double Duration => EndTime - StartTime;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -125,8 +125,6 @@ namespace osu.Game.Rulesets.Scoring
simulate(nested);
var judgement = obj.CreateJudgement();
if (judgement == null)
return;
var result = CreateResult(obj, judgement);
if (result == null)

View File

@ -93,15 +93,15 @@ namespace osu.Game.Screens.Edit
EditorMenuBar menuBar;
var fileMenuItems = new List<MenuItem>();
var fileMenuItems = new List<MenuItem>
{
new EditorMenuItem("Save", MenuItemType.Standard, saveBeatmap)
};
if (RuntimeInfo.IsDesktop)
{
fileMenuItems.Add(new EditorMenuItem("Save", MenuItemType.Standard, saveBeatmap));
fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, exportBeatmap));
fileMenuItems.Add(new EditorMenuItemSpacer());
}
fileMenuItems.Add(new EditorMenuItemSpacer());
fileMenuItems.Add(new EditorMenuItem("Exit", MenuItemType.Standard, this.Exit));
AddInternal(new OsuContextMenuContainer

View File

@ -15,7 +15,6 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play.HUD;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Play
{
@ -63,15 +62,9 @@ namespace osu.Game.Screens.Play
set
{
if (value)
{
loading.Show();
backgroundSprite.FadeColour(OsuColour.Gray(0.5f), 400, Easing.OutQuint);
}
else
{
loading.Hide();
backgroundSprite.FadeColour(Color4.White, 400, Easing.OutQuint);
}
}
}
@ -138,7 +131,7 @@ namespace osu.Game.Screens.Play
Anchor = Anchor.Centre,
FillMode = FillMode.Fill,
},
loading = new LoadingSpinner { Scale = new Vector2(1.3f) }
loading = new LoadingLayer(backgroundSprite)
}
},
new OsuSpriteText

View File

@ -207,11 +207,27 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
private double floatingAverage;
private Container colourBars;
private const int max_concurrent_judgements = 50;
public override void OnNewJudgement(JudgementResult judgement)
{
if (!judgement.IsHit)
return;
if (judgementsContainer.Count > max_concurrent_judgements)
{
const double quick_fade_time = 100;
// check with a bit of lenience to avoid precision error in comparison.
var old = judgementsContainer.FirstOrDefault(j => j.LifetimeEnd > Clock.CurrentTime + quick_fade_time * 1.1);
if (old != null)
{
old.ClearTransforms();
old.FadeOut(quick_fade_time).Expire();
}
}
judgementsContainer.Add(new JudgementLine
{
Y = getRelativeJudgementPosition(judgement.TimeOffset),
@ -228,7 +244,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
private class JudgementLine : CompositeDrawable
{
private const int judgement_fade_duration = 10000;
private const int judgement_fade_duration = 5000;
public JudgementLine()
{
@ -255,7 +271,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
Width = 0;
this.ResizeWidthTo(1, 200, Easing.OutElasticHalf);
this.FadeTo(0.8f, 150).Then().FadeOut(judgement_fade_duration, Easing.OutQuint).Expire();
this.FadeTo(0.8f, 150).Then().FadeOut(judgement_fade_duration).Expire();
}
}
}

View File

@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.221.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.221.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.225.0" />
<PackageReference Include="Sentry" Version="2.0.3" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />

View File

@ -74,7 +74,7 @@
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.221.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.221.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.225.0" />
</ItemGroup>
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
<ItemGroup Label="Transitive Dependencies">
@ -82,7 +82,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2020.221.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.225.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />