1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 14:57:52 +08:00

Merge branch 'master' into decoders

This commit is contained in:
Dean Herbert 2018-03-12 13:38:22 +09:00 committed by GitHub
commit ab4c527ebc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 424 additions and 266 deletions

View File

@ -1,9 +1,11 @@
osu!lazer is currently in early stages of development and is not yet ready for end users. Please avoid creating issues or bugs if you do not personally intend to fix them. Some acceptable topics include:
osu!lazer is currently still under heavy development!
Please ensure that you are making an issue for one of the following:
- A bug with currently implemented features (not features that don't exist)
- A feature you are considering adding, so we can collaborate on feedback and design.
- Discussions about technical design decisions
- Bugs that you have found and are personally willing and able to fix
- TODO lists of smaller tasks around larger features
Basically, issues are not a place for you to get help. They are a place for developers to collaborate on the game.
If your issue qualifies, replace this text with a detailed description of your issue with as much relevant information as you can provide.
Screenshots and log files are highly welcomed.

@ -1 +1 @@
Subproject commit 6915954abdba64e72f698aa58698b00159f3678d
Subproject commit 59004b46f2c96ac02fec712e66f9f96fe252f2fa

View File

@ -91,6 +91,7 @@ namespace osu.Game.Rulesets.Mania
},
new ManiaModRandom(),
new ManiaModDualStages(),
new ManiaModMirror(),
new MultiMod
{
Mods = new Mod[]

View File

@ -0,0 +1,28 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using System.Linq;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModMirror : Mod, IApplicableToRulesetContainer<ManiaHitObject>
{
public override string Name => "Mirror";
public override string ShortenedName => "MR";
public override ModType Type => ModType.Special;
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
{
var availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns;
rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = availableColumns - 1 - h.Column);
}
}
}

View File

@ -3,13 +3,14 @@
using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.UI
{
internal class DrawableManiaJudgement : DrawableJudgement
{
public DrawableManiaJudgement(Judgement judgement)
: base(judgement)
public DrawableManiaJudgement(Judgement judgement, DrawableHitObject judgedObject)
: base(judgement, judgedObject)
{
JudgementText.TextSize = 25;
}

View File

@ -15,6 +15,7 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using OpenTK;
using OpenTK.Graphics;
@ -40,7 +41,7 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly Container<Drawable> content;
public Container<DrawableManiaJudgement> Judgements => judgements;
private readonly Container<DrawableManiaJudgement> judgements;
private readonly JudgementContainer<DrawableManiaJudgement> judgements;
private readonly Container topLevelContainer;
@ -114,7 +115,7 @@ namespace osu.Game.Rulesets.Mania.UI
Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
}
},
judgements = new Container<DrawableManiaJudgement>
judgements = new JudgementContainer<DrawableManiaJudgement>
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
@ -171,7 +172,7 @@ namespace osu.Game.Rulesets.Mania.UI
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
judgements.Clear();
judgements.Add(new DrawableManiaJudgement(judgement)
judgements.Add(new DrawableManiaJudgement(judgement, judgedObject)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -81,6 +81,7 @@
<Compile Include="Judgements\ManiaJudgement.cs" />
<Compile Include="ManiaDifficultyCalculator.cs" />
<Compile Include="Mods\IPlayfieldTypeMod.cs" />
<Compile Include="Mods\ManiaModMirror.cs" />
<Compile Include="Mods\ManiaKeyMod.cs" />
<Compile Include="Mods\ManiaModAutoplay.cs" />
<Compile Include="Mods\ManiaModDaycore.cs" />

View File

@ -2,17 +2,17 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Rulesets.Osu.Judgements;
using OpenTK;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableOsuJudgement : DrawableJudgement
{
public DrawableOsuJudgement(OsuJudgement judgement)
: base(judgement)
public DrawableOsuJudgement(Judgement judgement, DrawableHitObject judgedObject)
: base(judgement, judgedObject)
{
}

View File

@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.UI
public class OsuPlayfield : Playfield
{
private readonly Container approachCircles;
private readonly Container judgementLayer;
private readonly JudgementContainer<DrawableOsuJudgement> judgementLayer;
private readonly ConnectionRenderer<OsuHitObject> connectionLayer;
// Todo: This should not be a thing, but is currently required for the editor
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.UI
RelativeSizeAxes = Axes.Both,
Depth = 2,
},
judgementLayer = new Container
judgementLayer = new JudgementContainer<DrawableOsuJudgement>
{
RelativeSizeAxes = Axes.Both,
Depth = 1,
@ -75,16 +75,13 @@ namespace osu.Game.Rulesets.Osu.UI
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
var osuJudgement = (OsuJudgement)judgement;
var osuObject = (OsuHitObject)judgedObject.HitObject;
if (!judgedObject.DisplayJudgement)
return;
DrawableOsuJudgement explosion = new DrawableOsuJudgement(osuJudgement)
DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgement, judgedObject)
{
Origin = Anchor.Centre,
Position = osuObject.StackedEndPosition + osuJudgement.PositionOffset
Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition + ((OsuJudgement)judgement).PositionOffset
};
judgementLayer.Add(explosion);

View File

@ -15,17 +15,14 @@ namespace osu.Game.Rulesets.Taiko.UI
/// </summary>
public class DrawableTaikoJudgement : DrawableJudgement
{
public readonly DrawableHitObject JudgedObject;
/// <summary>
/// Creates a new judgement text.
/// </summary>
/// <param name="judgedObject">The object which is being judged.</param>
/// <param name="judgement">The judgement to visualise.</param>
public DrawableTaikoJudgement(DrawableHitObject judgedObject, Judgement judgement)
: base(judgement)
public DrawableTaikoJudgement(Judgement judgement, DrawableHitObject judgedObject)
: base(judgement, judgedObject)
{
JudgedObject = judgedObject;
}
[BackgroundDependencyLoader]

View File

@ -16,6 +16,7 @@ using System.Linq;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Taiko.UI
@ -41,7 +42,7 @@ namespace osu.Game.Rulesets.Taiko.UI
private readonly Container<HitExplosion> hitExplosionContainer;
private readonly Container<KiaiHitExplosion> kiaiExplosionContainer;
private readonly Container<DrawableTaikoJudgement> judgementContainer;
private readonly JudgementContainer<DrawableTaikoJudgement> judgementContainer;
protected override Container<Drawable> Content => content;
private readonly Container content;
@ -131,7 +132,7 @@ namespace osu.Game.Rulesets.Taiko.UI
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
Blending = BlendingMode.Additive
},
judgementContainer = new Container<DrawableTaikoJudgement>
judgementContainer = new JudgementContainer<DrawableTaikoJudgement>
{
Name = "Judgements",
RelativeSizeAxes = Axes.Y,
@ -227,7 +228,7 @@ namespace osu.Game.Rulesets.Taiko.UI
{
if (judgedObject.DisplayJudgement && judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null)
{
judgementContainer.Add(new DrawableTaikoJudgement(judgedObject, judgement)
judgementContainer.Add(new DrawableTaikoJudgement(judgement, judgedObject)
{
Anchor = judgement.IsHit ? Anchor.TopLeft : Anchor.CentreLeft,
Origin = judgement.IsHit ? Anchor.BottomCentre : Anchor.Centre,

View File

@ -12,6 +12,7 @@ using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
@ -22,6 +23,7 @@ namespace osu.Game.Tests.Visual
public class TestCaseBeatmapCarousel : OsuTestCase
{
private TestBeatmapCarousel carousel;
private RulesetStore rulesets;
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@ -46,8 +48,10 @@ namespace osu.Game.Tests.Visual
private const int set_count = 5;
[BackgroundDependencyLoader]
private void load()
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
Add(carousel = new TestBeatmapCarousel
{
RelativeSizeAxes = Axes.Both,
@ -75,6 +79,7 @@ namespace osu.Game.Tests.Visual
testRemoveAll();
testEmptyTraversal();
testHiding();
testSelectingFilteredRuleset();
}
private void ensureRandomFetchSuccess() =>
@ -363,6 +368,41 @@ namespace osu.Game.Tests.Visual
}
}
private void testSelectingFilteredRuleset()
{
var testMixed = createTestBeatmapSet(set_count + 1);
AddStep("add mixed ruleset beatmapset", () =>
{
for (int i = 0; i <= 2; i++)
{
testMixed.Beatmaps[i].Ruleset = rulesets.AvailableRulesets.ElementAt(i);
testMixed.Beatmaps[i].RulesetID = i;
}
carousel.UpdateBeatmapSet(testMixed);
});
AddStep("filter to ruleset 0", () =>
carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) }, false));
AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testMixed.Beatmaps[1], false));
AddAssert("unfiltered beatmap selected", () => carousel.SelectedBeatmap.Equals(testMixed.Beatmaps[0]));
AddStep("remove mixed set", () =>
{
carousel.RemoveBeatmapSet(testMixed);
testMixed = null;
});
var testSingle = createTestBeatmapSet(set_count + 2);
testSingle.Beatmaps.ForEach(b =>
{
b.Ruleset = rulesets.AvailableRulesets.ElementAt(1);
b.RulesetID = b.Ruleset.ID ?? 1;
});
AddStep("add single ruleset beatmapset", () => carousel.UpdateBeatmapSet(testSingle));
AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testSingle.Beatmaps[0], false));
checkNoSelection();
AddStep("remove single ruleset set", () => carousel.RemoveBeatmapSet(testSingle));
}
private BeatmapSetInfo createTestBeatmapSet(int id)
{
return new BeatmapSetInfo

View File

@ -3,9 +3,9 @@
using osu.Framework.Timing;
using osu.Game.Beatmaps.Timing;
using osu.Game.Screens.Play.BreaksOverlay;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{

View File

@ -58,6 +58,12 @@ namespace osu.Game.Tests.Visual
checkSupporterTag(false);
AddStep("Show null dummy", () => profile.ShowUser(new User
{
Username = @"Null",
Id = 1,
}, false));
AddStep("Show ppy", () => profile.ShowUser(new User
{
Username = @"peppy",

View File

@ -66,8 +66,10 @@ namespace osu.Game.Graphics.Containers
{
Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount;
content.Position = Interpolation.ValueAt(MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000), content.Position, offset, 0, 1000, Easing.OutQuint);
content.Scale = new Vector2(1 + ParallaxAmount);
double elapsed = MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000);
content.Position = Interpolation.ValueAt(elapsed, content.Position, offset, 0, 1000, Easing.OutQuint);
content.Scale = Interpolation.ValueAt(elapsed, content.Scale, new Vector2(1 + ParallaxAmount), 0, 1000, Easing.OutQuint);
}
firstUpdate = false;

View File

@ -0,0 +1,66 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using Humanizer;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Threading;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Graphics
{
public class DrawableDate : OsuSpriteText, IHasTooltip
{
private readonly DateTimeOffset date;
private ScheduledDelegate updateTask;
public DrawableDate(DateTimeOffset date)
{
AutoSizeAxes = Axes.Both;
Font = "Exo2.0-RegularItalic";
this.date = date.ToLocalTime();
}
[BackgroundDependencyLoader]
private void load()
{
updateTime();
}
protected override void LoadComplete()
{
base.LoadComplete();
Scheduler.Add(updateTimeWithReschedule);
}
private void updateTimeWithReschedule()
{
updateTime();
var diffToNow = DateTimeOffset.Now.Subtract(date);
double timeUntilNextUpdate = 1000;
if (diffToNow.TotalSeconds > 60)
{
timeUntilNextUpdate *= 60;
if (diffToNow.TotalMinutes > 60)
{
timeUntilNextUpdate *= 60;
if (diffToNow.TotalHours > 24)
timeUntilNextUpdate *= 24;
}
}
Scheduler.AddDelayed(updateTimeWithReschedule, timeUntilNextUpdate);
}
public override bool HandleMouseInput => true;
private void updateTime() => Text = date.Humanize();
public string TooltipText => date.ToString();
}
}

View File

@ -130,11 +130,7 @@ namespace osu.Game.Overlays.Profile
}
}
},
infoTextLeft = new OsuTextFlowContainer(t =>
{
t.TextSize = 14;
t.Alpha = 0.8f;
})
infoTextLeft = new OsuTextFlowContainer(t => t.TextSize = 14)
{
X = UserProfileOverlay.CONTENT_X_MARGIN,
Y = cover_height + 20,
@ -318,11 +314,23 @@ namespace osu.Game.Overlays.Profile
colourBar.Show();
}
void boldItalic(SpriteText t)
void boldItalic(SpriteText t) => t.Font = @"Exo2.0-BoldItalic";
void lightText(SpriteText t) => t.Alpha = 0.8f;
OsuSpriteText createScoreText(string text) => new OsuSpriteText
{
t.Font = @"Exo2.0-BoldItalic";
t.Alpha = 1;
}
TextSize = 14,
Text = text
};
OsuSpriteText createScoreNumberText(string text) => new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-Bold",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Text = text
};
if (user.Age != null)
{
@ -331,7 +339,7 @@ namespace osu.Game.Overlays.Profile
if (user.Country != null)
{
infoTextLeft.AddText("from ");
infoTextLeft.AddText("from ", lightText);
infoTextLeft.AddText(user.Country.FullName, boldItalic);
countryFlag.Country = user.Country;
}
@ -344,18 +352,18 @@ namespace osu.Game.Overlays.Profile
}
else
{
infoTextLeft.AddText("Joined ");
infoTextLeft.AddText(user.JoinDate.LocalDateTime.ToShortDateString(), boldItalic);
infoTextLeft.AddText("Joined ", lightText);
infoTextLeft.AddText(new DrawableDate(user.JoinDate), boldItalic);
}
infoTextLeft.NewLine();
infoTextLeft.AddText("Last seen ");
infoTextLeft.AddText(user.LastVisit.LocalDateTime.ToShortDateString(), boldItalic);
infoTextLeft.AddText("Last seen ", lightText);
infoTextLeft.AddText(new DrawableDate(user.LastVisit), boldItalic);
infoTextLeft.NewParagraph();
if (user.PlayStyle?.Length > 0)
{
infoTextLeft.AddText("Plays with ");
infoTextLeft.AddText("Plays with ", lightText);
infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic);
}
@ -411,23 +419,6 @@ namespace osu.Game.Overlays.Profile
}
}
// These could be local functions when C# 7 enabled
private OsuSpriteText createScoreText(string text) => new OsuSpriteText
{
TextSize = 14,
Text = text
};
private OsuSpriteText createScoreNumberText(string text) => new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-Bold",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Text = text
};
private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null)
{
if (string.IsNullOrEmpty(str)) return;
@ -436,10 +427,12 @@ namespace osu.Game.Overlays.Profile
if (url != null)
{
infoTextRight.AddLink(" " + str, url);
} else
}
else
{
infoTextRight.AddText(" " + str);
}
infoTextRight.NewLine();
}

View File

@ -95,7 +95,7 @@ namespace osu.Game.Overlays.Profile
{
placeholder.FadeIn(fade_duration, Easing.Out);
if (user == null)
if (user?.Statistics?.Ranks.Global == null)
{
rankText.Text = string.Empty;
performanceText.Text = string.Empty;
@ -105,7 +105,7 @@ namespace osu.Game.Overlays.Profile
return;
}
int[] userRanks = user.RankHistory?.Data ?? new[] { user.Statistics.Ranks.Global };
int[] userRanks = user.RankHistory?.Data ?? new[] { user.Statistics.Ranks.Global.Value };
ranks = userRanks.Select((x, index) => new KeyValuePair<int, int>(index, x)).Where(x => x.Value != 0).ToArray();
if (ranks.Length > 1)

View File

@ -54,12 +54,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
RightFlowContainer.SetLayoutPosition(text, 1);
LeftFlowContainer.Add(new BeatmapMetadataContainer(Score.Beatmap));
LeftFlowContainer.Add(new OsuSpriteText
{
Text = Score.Date.LocalDateTime.ToShortDateString(),
TextSize = 11,
Colour = OsuColour.Gray(0xAA),
});
LeftFlowContainer.Add(new DrawableDate(Score.Date));
foreach (Mod mod in Score.Mods)
modsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.5f) });

View File

@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Chat;
@ -40,14 +39,12 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
RelativeSizeAxes = Axes.X,
});
RightFlowContainer.Add(new OsuSpriteText
RightFlowContainer.Add(new DrawableDate(activity.CreatedAt)
{
Text = activity.CreatedAt.LocalDateTime.ToShortDateString(),
TextSize = 13,
Colour = OsuColour.Gray(0xAA),
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Font = "Exo2.0-RegularItalic",
TextSize = 12,
Colour = OsuColour.Gray(0xAA),
});
var formatted = createMessage();

View File

@ -169,6 +169,8 @@ namespace osu.Game.Overlays
{
Header.User = user;
if (user.ProfileOrder != null)
{
foreach (string id in user.ProfileOrder)
{
var sec = sections.FirstOrDefault(s => s.Identifier == id);
@ -181,6 +183,7 @@ namespace osu.Game.Overlays
}
}
}
}
private class ProfileTabControl : PageTabControl<ProfileSection>
{

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Judgements
@ -20,15 +21,18 @@ namespace osu.Game.Rulesets.Judgements
{
protected readonly Judgement Judgement;
public readonly DrawableHitObject JudgedObject;
protected readonly SpriteText JudgementText;
/// <summary>
/// Creates a drawable which visualises a <see cref="Judgements.Judgement"/>.
/// </summary>
/// <param name="judgement">The judgement to visualise.</param>
public DrawableJudgement(Judgement judgement)
public DrawableJudgement(Judgement judgement, DrawableHitObject judgedObject)
{
Judgement = judgement;
JudgedObject = judgedObject;
AutoSizeAxes = Axes.Both;

View File

@ -63,7 +63,7 @@ namespace osu.Game.Rulesets
var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, (RulesetInfo)null)).ToList();
//add all legacy modes in correct order
foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID))
foreach (var r in instances.Where(r => r.LegacyID != null).OrderBy(r => r.LegacyID))
{
if (context.RulesetInfo.SingleOrDefault(rsi => rsi.ID == r.RulesetInfo.ID) == null)
context.RulesetInfo.Add(r.RulesetInfo);
@ -72,7 +72,7 @@ namespace osu.Game.Rulesets
context.SaveChanges();
//add any other modes
foreach (var r in instances.Where(r => r.LegacyID < 0))
foreach (var r in instances.Where(r => r.LegacyID == null))
if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == r.RulesetInfo.InstantiationInfo) == null)
context.RulesetInfo.Add(r.RulesetInfo);

View File

@ -0,0 +1,24 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.UI
{
public class JudgementContainer<T> : Container<T>
where T : DrawableJudgement
{
public override void Add(T judgement)
{
if (judgement == null) throw new ArgumentNullException(nameof(judgement));
// remove any existing judgements for the judged object.
// this can be the case when rewinding.
RemoveAll(c => c.JudgedObject == judgement.JudgedObject);
base.Add(judgement);
}
}
}

View File

@ -1,13 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using OpenTK;
namespace osu.Game.Screens.Play.BreaksOverlay
namespace osu.Game.Screens.Play.Break
{
public class BlurredIcon : BufferedContainer
{

View File

@ -1,18 +1,15 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using OpenTK;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
using osu.Game.Beatmaps.Timing;
using OpenTK;
namespace osu.Game.Screens.Play.BreaksOverlay
namespace osu.Game.Screens.Play.Break
{
public class ArrowsOverlay : VisibilityContainer
public class BreakArrows : CompositeDrawable
{
private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
private const int glow_icon_size = 60;
private const int glow_icon_blur_sigma = 10;
private const float glow_icon_final_offset = 0.22f;
@ -29,10 +26,10 @@ namespace osu.Game.Screens.Play.BreaksOverlay
private readonly BlurredIcon leftBlurredIcon;
private readonly BlurredIcon rightBlurredIcon;
public ArrowsOverlay()
public BreakArrows()
{
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
leftGlowIcon = new GlowIcon
{
@ -82,22 +79,22 @@ namespace osu.Game.Screens.Play.BreaksOverlay
};
}
protected override void PopIn()
public void Show(double duration)
{
leftGlowIcon.MoveToX(-glow_icon_final_offset, fade_duration, Easing.OutQuint);
rightGlowIcon.MoveToX(glow_icon_final_offset, fade_duration, Easing.OutQuint);
leftGlowIcon.MoveToX(-glow_icon_final_offset, duration, Easing.OutQuint);
rightGlowIcon.MoveToX(glow_icon_final_offset, duration, Easing.OutQuint);
leftBlurredIcon.MoveToX(-blurred_icon_final_offset, fade_duration, Easing.OutQuint);
rightBlurredIcon.MoveToX(blurred_icon_final_offset, fade_duration, Easing.OutQuint);
leftBlurredIcon.MoveToX(-blurred_icon_final_offset, duration, Easing.OutQuint);
rightBlurredIcon.MoveToX(blurred_icon_final_offset, duration, Easing.OutQuint);
}
protected override void PopOut()
public void Hide(double duration)
{
leftGlowIcon.MoveToX(-glow_icon_offscreen_offset, fade_duration, Easing.OutQuint);
rightGlowIcon.MoveToX(glow_icon_offscreen_offset, fade_duration, Easing.OutQuint);
leftGlowIcon.MoveToX(-glow_icon_offscreen_offset, duration, Easing.OutQuint);
rightGlowIcon.MoveToX(glow_icon_offscreen_offset, duration, Easing.OutQuint);
leftBlurredIcon.MoveToX(-blurred_icon_offscreen_offset, fade_duration, Easing.OutQuint);
rightBlurredIcon.MoveToX(blurred_icon_offscreen_offset, fade_duration, Easing.OutQuint);
leftBlurredIcon.MoveToX(-blurred_icon_offscreen_offset, duration, Easing.OutQuint);
rightBlurredIcon.MoveToX(blurred_icon_offscreen_offset, duration, Easing.OutQuint);
}
}
}

View File

@ -1,24 +1,21 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Scoring;
using osu.Game.Beatmaps.Timing;
using OpenTK;
namespace osu.Game.Screens.Play.BreaksOverlay
namespace osu.Game.Screens.Play.Break
{
public class InfoContainer : VisibilityContainer
public class BreakInfo : Container
{
private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
public PercentageBreakInfoLine AccuracyDisplay;
public BreakInfoLine<int> RankDisplay;
public BreakInfoLine<ScoreRank> GradeDisplay;
public PercentageInfoLine AccuracyDisplay;
public InfoLine<int> RankDisplay;
public InfoLine<ScoreRank> GradeDisplay;
public InfoContainer()
public BreakInfo()
{
AutoSizeAxes = Axes.Both;
Child = new FillFlowContainer
@ -43,16 +40,13 @@ namespace osu.Game.Screens.Play.BreaksOverlay
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
AccuracyDisplay = new PercentageInfoLine("Accuracy"),
RankDisplay = new InfoLine<int>("Rank"),
GradeDisplay = new InfoLine<ScoreRank>("Grade"),
AccuracyDisplay = new PercentageBreakInfoLine("Accuracy"),
RankDisplay = new BreakInfoLine<int>("Rank"),
GradeDisplay = new BreakInfoLine<ScoreRank>("Grade"),
},
}
},
};
}
protected override void PopIn() => this.FadeIn(fade_duration);
protected override void PopOut() => this.FadeOut(fade_duration);
}
}

View File

@ -8,9 +8,9 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Screens.Play.BreaksOverlay
namespace osu.Game.Screens.Play.Break
{
public class InfoLine<T> : Container
public class BreakInfoLine<T> : Container
where T : struct
{
private const int margin = 2;
@ -22,7 +22,7 @@ namespace osu.Game.Screens.Play.BreaksOverlay
private readonly string prefix;
public InfoLine(string name, string prefix = @"")
public BreakInfoLine(string name, string prefix = @"")
{
this.prefix = prefix;
@ -71,9 +71,9 @@ namespace osu.Game.Screens.Play.BreaksOverlay
}
}
public class PercentageInfoLine : InfoLine<double>
public class PercentageBreakInfoLine : BreakInfoLine<double>
{
public PercentageInfoLine(string name, string prefix = "") : base(name, prefix)
public PercentageBreakInfoLine(string name, string prefix = "") : base(name, prefix)
{
}

View File

@ -1,13 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using OpenTK;
using osu.Framework.Allocation;
namespace osu.Game.Screens.Play.BreaksOverlay
namespace osu.Game.Screens.Play.Break
{
public class GlowIcon : Container
{
@ -16,24 +16,24 @@ namespace osu.Game.Screens.Play.BreaksOverlay
public override Vector2 Size
{
get { return base.Size; }
set
{
blurredIcon.Size = spriteIcon.Size = value;
blurredIcon.ForceRedraw();
}
get { return base.Size; }
}
public Vector2 BlurSigma
{
set { blurredIcon.BlurSigma = value; }
get { return blurredIcon.BlurSigma; }
set { blurredIcon.BlurSigma = value; }
}
public FontAwesome Icon
{
set { spriteIcon.Icon = blurredIcon.Icon = value; }
get { return spriteIcon.Icon; }
set { spriteIcon.Icon = blurredIcon.Icon = value; }
}
public GlowIcon()

View File

@ -1,18 +1,16 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps.Timing;
using OpenTK.Graphics;
namespace osu.Game.Screens.Play.BreaksOverlay
namespace osu.Game.Screens.Play.Break
{
public class LetterboxOverlay : VisibilityContainer
public class LetterboxOverlay : CompositeDrawable
{
private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
private const int height = 350;
private static readonly Color4 transparent_black = new Color4(0, 0, 0, 0);
@ -20,7 +18,7 @@ namespace osu.Game.Screens.Play.BreaksOverlay
public LetterboxOverlay()
{
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
new Container
{
@ -48,8 +46,5 @@ namespace osu.Game.Screens.Play.BreaksOverlay
}
};
}
protected override void PopIn() => this.FadeIn(fade_duration);
protected override void PopOut() => this.FadeOut(fade_duration);
}
}

View File

@ -1,18 +1,15 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics;
using System;
using osu.Game.Beatmaps.Timing;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Screens.Play.BreaksOverlay
namespace osu.Game.Screens.Play.Break
{
public class RemainingTimeCounter : Counter
{
private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
private readonly OsuSpriteText counter;
public RemainingTimeCounter()
@ -25,13 +22,8 @@ namespace osu.Game.Screens.Play.BreaksOverlay
TextSize = 33,
Font = "Venera",
};
Alpha = 0;
}
protected override void OnCountChanged(double count) => counter.Text = ((int)Math.Ceiling(count / 1000)).ToString();
public override void Show() => this.FadeIn(fade_duration);
public override void Hide() => this.FadeOut(fade_duration);
}
}

View File

@ -1,15 +1,16 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Scoring;
using System.Collections.Generic;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Screens.Play.Break;
namespace osu.Game.Screens.Play.BreaksOverlay
namespace osu.Game.Screens.Play
{
public class BreakOverlay : Container
{
@ -18,28 +19,26 @@ namespace osu.Game.Screens.Play.BreaksOverlay
private const int vertical_margin = 25;
private List<BreakPeriod> breaks;
private readonly Container fadeContainer;
public List<BreakPeriod> Breaks
{
get => breaks;
set
{
breaks = value;
initializeBreaks();
}
get
{
return breaks;
}
}
public override bool RemoveCompletedTransforms => false;
private readonly bool letterboxing;
private readonly LetterboxOverlay letterboxOverlay;
private readonly Container remainingTimeAdjustmentBox;
private readonly Container remainingTimeBox;
private readonly RemainingTimeCounter remainingTimeCounter;
private readonly InfoContainer info;
private readonly ArrowsOverlay arrowsOverlay;
private readonly BreakInfo info;
private readonly BreakArrows breakArrows;
public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor)
: this(letterboxing)
@ -49,13 +48,16 @@ namespace osu.Game.Screens.Play.BreaksOverlay
public BreakOverlay(bool letterboxing)
{
this.letterboxing = letterboxing;
RelativeSizeAxes = Axes.Both;
Child = fadeContainer = new Container
{
Alpha = 0,
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
letterboxOverlay = new LetterboxOverlay
new LetterboxOverlay
{
Alpha = letterboxing ? 1 : 0,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
@ -83,27 +85,35 @@ namespace osu.Game.Screens.Play.BreaksOverlay
Origin = Anchor.BottomCentre,
Margin = new MarginPadding { Bottom = vertical_margin },
},
info = new InfoContainer
info = new BreakInfo
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
Margin = new MarginPadding { Top = vertical_margin },
},
arrowsOverlay = new ArrowsOverlay
breakArrows = new BreakArrows
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
initializeBreaks();
}
private void initializeBreaks()
{
if (!IsLoaded) return; // we need a clock.
FinishTransforms(true);
Scheduler.CancelDelayedTasks();
if (breaks == null)
return;
if (breaks == null) return; //we need breaks.
foreach (var b in breaks)
{
@ -112,6 +122,9 @@ namespace osu.Game.Screens.Play.BreaksOverlay
using (BeginAbsoluteSequence(b.StartTime, true))
{
fadeContainer.FadeIn(fade_duration);
breakArrows.Show(fade_duration);
remainingTimeAdjustmentBox
.ResizeWidthTo(remaining_time_container_max_size, fade_duration, Easing.OutQuint)
.Delay(b.Duration - fade_duration)
@ -123,35 +136,14 @@ namespace osu.Game.Screens.Play.BreaksOverlay
.ResizeWidthTo(1);
remainingTimeCounter.CountTo(b.Duration).CountTo(0, b.Duration);
}
using (BeginAbsoluteSequence(b.StartTime))
using (BeginDelayedSequence(b.Duration - fade_duration, true))
{
Schedule(showBreak);
using (BeginDelayedSequence(b.Duration - fade_duration))
Schedule(hideBreak);
fadeContainer.FadeOut(fade_duration);
breakArrows.Hide(fade_duration);
}
}
}
private void showBreak()
{
if (letterboxing)
letterboxOverlay.Show();
remainingTimeCounter.Show();
info.Show();
arrowsOverlay.Show();
}
private void hideBreak()
{
if (letterboxing)
letterboxOverlay.Hide();
remainingTimeCounter.Hide();
info.Hide();
arrowsOverlay.Hide();
}
private void bindProcessor(ScoreProcessor processor)

View File

@ -25,7 +25,6 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play.BreaksOverlay;
using osu.Game.Screens.Ranking;
using osu.Game.Storyboards.Drawables;

View File

@ -169,20 +169,43 @@ namespace osu.Game.Screens.Select
});
}
public void SelectBeatmap(BeatmapInfo beatmap)
/// <summary>
/// Selects a given beatmap on the carousel.
///
/// If bypassFilters is false, we will try to select another unfiltered beatmap in the same set. If the
/// entire set is filtered, no selection is made.
/// </summary>
/// <param name="beatmap">The beatmap to select.</param>
/// <param name="bypassFilters">Whether to select the beatmap even if it is filtered (i.e., not visible on carousel).</param>
/// <returns>True if a selection was made, False if it wasn't.</returns>
public bool SelectBeatmap(BeatmapInfo beatmap, bool bypassFilters = true)
{
if (beatmap?.Hidden != false)
return;
return false;
foreach (CarouselBeatmapSet group in beatmapSets)
foreach (CarouselBeatmapSet set in beatmapSets)
{
var item = group.Beatmaps.FirstOrDefault(p => p.Beatmap.Equals(beatmap));
if (!bypassFilters && set.Filtered)
continue;
var item = set.Beatmaps.FirstOrDefault(p => p.Beatmap.Equals(beatmap));
if (item == null)
// The beatmap that needs to be selected doesn't exist in this set
continue;
if (!bypassFilters && item.Filtered)
// The beatmap exists in this set but is filtered, so look for the first unfiltered map in the set
item = set.Beatmaps.FirstOrDefault(b => !b.Filtered);
if (item != null)
{
select(item);
return;
return true;
}
}
return false;
}
/// <summary>

View File

@ -214,11 +214,7 @@ namespace osu.Game.Screens.Select
Beatmap.DisabledChanged += disabled => Carousel.AllowSelection = !disabled;
Beatmap.TriggerChange();
Beatmap.ValueChanged += b =>
{
if (IsCurrentScreen)
Carousel.SelectBeatmap(b?.BeatmapInfo);
};
Beatmap.ValueChanged += workingBeatmapChanged;
}
public void Edit(BeatmapInfo beatmap)
@ -261,6 +257,17 @@ namespace osu.Game.Screens.Select
// We need to keep track of the last selected beatmap ignoring debounce to play the correct selection sounds.
private BeatmapInfo beatmapNoDebounce;
private void workingBeatmapChanged(WorkingBeatmap beatmap)
{
if (IsCurrentScreen && !Carousel.SelectBeatmap(beatmap?.BeatmapInfo, false))
// If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch
if (beatmap?.BeatmapInfo?.Ruleset != null && beatmap.BeatmapInfo.Ruleset != Ruleset.Value)
{
Ruleset.Value = beatmap.BeatmapInfo.Ruleset;
Carousel.SelectBeatmap(beatmap.BeatmapInfo);
}
}
/// <summary>
/// selection has been changed as the result of interaction with the carousel.
/// </summary>
@ -450,13 +457,11 @@ namespace osu.Game.Screens.Select
private void carouselBeatmapsLoaded()
{
if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false && Beatmap.Value.BeatmapSetInfo?.Protected == false)
if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false && Beatmap.Value.BeatmapSetInfo?.Protected == false && Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo, false))
return;
if (Carousel.SelectedBeatmapSet == null && !Carousel.SelectNextRandom())
{
Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo);
}
else if (Carousel.SelectedBeatmapSet == null)
{
if (!Carousel.SelectNextRandom())
// in the case random selection failed, we want to trigger selectionChanged
// to show the dummy beatmap (we have nothing else to display).
carouselSelectionChanged(null);

View File

@ -128,7 +128,7 @@ namespace osu.Game.Tests.Visual
private void load(BeatmapManager beatmaps)
{
var sets = beatmaps.GetAllUsableBeatmapSets();
var allBeatmaps = sets.SelectMany(s => s.Beatmaps).Where(b => ruleset.LegacyID < 0 || b.RulesetID == ruleset.LegacyID);
var allBeatmaps = sets.SelectMany(s => s.Beatmaps).Where(b => ruleset.LegacyID == null || b.RulesetID == ruleset.LegacyID);
allBeatmaps.ForEach(b => beatmapDisplays.Add(new BeatmapDisplay(b)));
}

View File

@ -73,10 +73,10 @@ namespace osu.Game.Users
public struct UserRanks
{
[JsonProperty(@"global")]
public int Global;
public int? Global;
[JsonProperty(@"country")]
public int Country;
public int? Country;
}
}

View File

@ -287,6 +287,7 @@
<Compile Include="Database\MutableDatabaseBackedStore.cs" />
<Compile Include="Database\SingletonContextFactory.cs" />
<Compile Include="Graphics\Containers\LinkFlowContainer.cs" />
<Compile Include="Graphics\DrawableDate.cs" />
<Compile Include="Graphics\Textures\LargeTextureStore.cs" />
<Compile Include="IO\Archives\ArchiveReader.cs" />
<Compile Include="IO\Archives\LegacyFilesystemReader.cs" />
@ -378,6 +379,7 @@
<Compile Include="Rulesets\Replays\ReplayFrame.cs" />
<Compile Include="Rulesets\Replays\Types\IConvertibleReplayFrame.cs" />
<Compile Include="Rulesets\Scoring\Legacy\LegacyScoreParser.cs" />
<Compile Include="Rulesets\UI\JudgementContainer.cs" />
<Compile Include="Screens\Play\ScreenWithBeatmapBackground.cs" />
<Compile Include="Screens\Play\PlayerSettings\VisualSettings.cs" />
<Compile Include="Rulesets\Objects\CatmullApproximator.cs" />
@ -409,14 +411,14 @@
<Compile Include="Screens\Edit\Screens\Compose\RadioButtons\RadioButtonCollection.cs" />
<Compile Include="Screens\Edit\Screens\Compose\Timeline\BeatmapWaveformGraph.cs" />
<Compile Include="Screens\Edit\Screens\Compose\Timeline\TimelineButton.cs" />
<Compile Include="Screens\Play\BreaksOverlay\ArrowsOverlay.cs" />
<Compile Include="Screens\Play\BreaksOverlay\BlurredIcon.cs" />
<Compile Include="Screens\Play\BreaksOverlay\BreakOverlay.cs" />
<Compile Include="Screens\Play\BreaksOverlay\GlowIcon.cs" />
<Compile Include="Screens\Play\BreaksOverlay\InfoContainer.cs" />
<Compile Include="Screens\Play\BreaksOverlay\InfoLine.cs" />
<Compile Include="Screens\Play\BreaksOverlay\LetterboxOverlay.cs" />
<Compile Include="Screens\Play\BreaksOverlay\RemainingTimeCounter.cs" />
<Compile Include="Screens\Play\Break\BreakArrows.cs" />
<Compile Include="Screens\Play\Break\BlurredIcon.cs" />
<Compile Include="Screens\Play\BreakOverlay.cs" />
<Compile Include="Screens\Play\Break\GlowIcon.cs" />
<Compile Include="Screens\Play\Break\BreakInfo.cs" />
<Compile Include="Screens\Play\Break\BreakInfoLine.cs" />
<Compile Include="Screens\Play\Break\LetterboxOverlay.cs" />
<Compile Include="Screens\Play\Break\RemainingTimeCounter.cs" />
<Compile Include="Beatmaps\Legacy\LegacyBeatmap.cs" />
<Compile Include="Beatmaps\RankStatus.cs" />
<Compile Include="Beatmaps\Timing\BreakPeriod.cs" />