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

Merge branch 'master' into fix-texture-loader-usages

This commit is contained in:
Bartłomiej Dach 2020-12-21 18:35:34 +01:00 committed by GitHub
commit f96d2f4ba4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 378 additions and 152 deletions

View File

@ -25,16 +25,22 @@ namespace osu.Game.Rulesets.Taiko.Tests
private ScrollingHitObjectContainer hitObjectContainer; private ScrollingHitObjectContainer hitObjectContainer;
[SetUpSteps] [BackgroundDependencyLoader]
public void SetUp() private void load()
=> AddStep("create SHOC", () => Child = hitObjectContainer = new ScrollingHitObjectContainer {
Child = hitObjectContainer = new ScrollingHitObjectContainer
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = 200, Height = 200,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Clock = new FramedClock(new StopwatchClock()) Clock = new FramedClock(new StopwatchClock())
}); };
}
[SetUpSteps]
public void SetUp()
=> AddStep("clear SHOC", () => hitObjectContainer.Clear(false));
protected void AddHitObject(DrawableHitObject hitObject) protected void AddHitObject(DrawableHitObject hitObject)
=> AddStep("add to SHOC", () => hitObjectContainer.Add(hitObject)); => AddStep("add to SHOC", () => hitObjectContainer.Add(hitObject));

View File

@ -12,12 +12,13 @@ namespace osu.Game.Rulesets.Taiko.Tests
[Test] [Test]
public void TestApplyNewBarLine() public void TestApplyNewBarLine()
{ {
DrawableBarLine barLine = new DrawableBarLine(PrepareObject(new BarLine DrawableBarLine barLine = new DrawableBarLine();
AddStep("apply new bar line", () => barLine.Apply(PrepareObject(new BarLine
{ {
StartTime = 400, StartTime = 400,
Major = true Major = true
})); }), null));
AddHitObject(barLine); AddHitObject(barLine);
RemoveHitObject(barLine); RemoveHitObject(barLine);

View File

@ -0,0 +1,39 @@
// 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 NUnit.Framework;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.Tests
{
public class TestSceneDrumRollApplication : HitObjectApplicationTestScene
{
[Test]
public void TestApplyNewDrumRoll()
{
var drumRoll = new DrawableDrumRoll();
AddStep("apply new drum roll", () => drumRoll.Apply(PrepareObject(new DrumRoll
{
StartTime = 300,
Duration = 500,
IsStrong = false,
TickRate = 2
}), null));
AddHitObject(drumRoll);
RemoveHitObject(drumRoll);
AddStep("apply new drum roll", () => drumRoll.Apply(PrepareObject(new DrumRoll
{
StartTime = 150,
Duration = 400,
IsStrong = true,
TickRate = 16
}), null));
AddHitObject(drumRoll);
}
}
}

View File

@ -0,0 +1,37 @@
// 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 NUnit.Framework;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.Tests
{
public class TestSceneHitApplication : HitObjectApplicationTestScene
{
[Test]
public void TestApplyNewHit()
{
var hit = new DrawableHit();
AddStep("apply new hit", () => hit.Apply(PrepareObject(new Hit
{
Type = HitType.Rim,
IsStrong = false,
StartTime = 300
}), null));
AddHitObject(hit);
RemoveHitObject(hit);
AddStep("apply new hit", () => hit.Apply(PrepareObject(new Hit
{
Type = HitType.Centre,
IsStrong = true,
StartTime = 500
}), null));
AddHitObject(hit);
}
}
}

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 System.Linq; using System.Linq;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Audio; using osu.Game.Audio;
@ -18,24 +19,33 @@ namespace osu.Game.Rulesets.Taiko.Tests
public override void SetUpSteps() public override void SetUpSteps()
{ {
base.SetUpSteps(); base.SetUpSteps();
AddAssert("has correct samples", () =>
var expectedSampleNames = new[]
{ {
var names = Player.DrawableRuleset.Playfield.AllHitObjects.OfType<DrawableHit>().Select(h => string.Join(',', h.GetSamples().Select(s => s.Name))); string.Empty,
string.Empty,
string.Empty,
string.Empty,
HitSampleInfo.HIT_FINISH,
HitSampleInfo.HIT_WHISTLE,
HitSampleInfo.HIT_WHISTLE,
HitSampleInfo.HIT_WHISTLE,
};
var actualSampleNames = new List<string>();
var expected = new[] // due to pooling we can't access all samples right away due to object re-use,
{ // so we need to collect as we go.
string.Empty, AddStep("collect sample names", () => Player.DrawableRuleset.Playfield.NewResult += (dho, _) =>
string.Empty, {
string.Empty, if (!(dho is DrawableHit h))
string.Empty, return;
HitSampleInfo.HIT_FINISH,
HitSampleInfo.HIT_WHISTLE,
HitSampleInfo.HIT_WHISTLE,
HitSampleInfo.HIT_WHISTLE,
};
return names.SequenceEqual(expected); actualSampleNames.Add(string.Join(',', h.GetSamples().Select(s => s.Name)));
}); });
AddUntilStep("all samples collected", () => actualSampleNames.Count == expectedSampleNames.Length);
AddAssert("samples are correct", () => actualSampleNames.SequenceEqual(expectedSampleNames));
} }
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TaikoBeatmapConversionTest().GetBeatmap("sample-to-type-conversions"); protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TaikoBeatmapConversionTest().GetBeatmap("sample-to-type-conversions");

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Linq; using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Graphics; using osu.Game.Graphics;
@ -31,15 +32,26 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
/// </summary> /// </summary>
private int rollingHits; private int rollingHits;
private Container tickContainer; private readonly Container tickContainer;
private Color4 colourIdle; private Color4 colourIdle;
private Color4 colourEngaged; private Color4 colourEngaged;
public DrawableDrumRoll(DrumRoll drumRoll) public DrawableDrumRoll()
: this(null)
{
}
public DrawableDrumRoll([CanBeNull] DrumRoll drumRoll)
: base(drumRoll) : base(drumRoll)
{ {
RelativeSizeAxes = Axes.Y; RelativeSizeAxes = Axes.Y;
Content.Add(tickContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Depth = float.MinValue
});
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -47,12 +59,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{ {
colourIdle = colours.YellowDark; colourIdle = colours.YellowDark;
colourEngaged = colours.YellowDarker; colourEngaged = colours.YellowDarker;
Content.Add(tickContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Depth = float.MinValue
});
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -68,6 +74,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
updateColour(); updateColour();
} }
protected override void OnFree()
{
base.OnFree();
rollingHits = 0;
}
protected override void AddNestedHitObject(DrawableHitObject hitObject) protected override void AddNestedHitObject(DrawableHitObject hitObject)
{ {
base.AddNestedHitObject(hitObject); base.AddNestedHitObject(hitObject);
@ -83,7 +95,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override void ClearNestedHitObjects() protected override void ClearNestedHitObjects()
{ {
base.ClearNestedHitObjects(); base.ClearNestedHitObjects();
tickContainer.Clear(); tickContainer.Clear(false);
} }
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)
@ -114,7 +126,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
rollingHits = Math.Clamp(rollingHits, 0, rolling_hits_for_engaged_colour); rollingHits = Math.Clamp(rollingHits, 0, rolling_hits_for_engaged_colour);
updateColour(); updateColour(100);
} }
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
@ -154,27 +166,34 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Content.X = DrawHeight / 2; Content.X = DrawHeight / 2;
} }
protected override DrawableStrongNestedHit CreateStrongNestedHit(DrumRoll.StrongNestedHit hitObject) => new StrongNestedHit(hitObject, this); protected override DrawableStrongNestedHit CreateStrongNestedHit(DrumRoll.StrongNestedHit hitObject) => new StrongNestedHit(hitObject);
private void updateColour() private void updateColour(double fadeDuration = 0)
{ {
Color4 newColour = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_engaged_colour, colourIdle, colourEngaged, 0, 1); Color4 newColour = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_engaged_colour, colourIdle, colourEngaged, 0, 1);
(MainPiece.Drawable as IHasAccentColour)?.FadeAccent(newColour, 100); (MainPiece.Drawable as IHasAccentColour)?.FadeAccent(newColour, fadeDuration);
} }
private class StrongNestedHit : DrawableStrongNestedHit public class StrongNestedHit : DrawableStrongNestedHit
{ {
public StrongNestedHit(DrumRoll.StrongNestedHit nestedHit, DrawableDrumRoll drumRoll) public new DrawableDrumRoll ParentHitObject => (DrawableDrumRoll)base.ParentHitObject;
: base(nestedHit, drumRoll)
public StrongNestedHit()
: this(null)
{
}
public StrongNestedHit([CanBeNull] DrumRoll.StrongNestedHit nestedHit)
: base(nestedHit)
{ {
} }
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (!MainObject.Judged) if (!ParentHitObject.Judged)
return; return;
ApplyResult(r => r.Type = MainObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult); ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
} }
public override bool OnPressed(TaikoAction action) => false; public override bool OnPressed(TaikoAction action) => false;

View File

@ -2,6 +2,7 @@
// 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; using System;
using JetBrains.Annotations;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Skinning.Default; using osu.Game.Rulesets.Taiko.Skinning.Default;
@ -16,7 +17,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
/// </summary> /// </summary>
public HitType JudgementType; public HitType JudgementType;
public DrawableDrumRollTick(DrumRollTick tick) public DrawableDrumRollTick()
: this(null)
{
}
public DrawableDrumRollTick([CanBeNull] DrumRollTick tick)
: base(tick) : base(tick)
{ {
FillMode = FillMode.Fit; FillMode = FillMode.Fit;
@ -61,21 +67,28 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
return UpdateResult(true); return UpdateResult(true);
} }
protected override DrawableStrongNestedHit CreateStrongNestedHit(DrumRollTick.StrongNestedHit hitObject) => new StrongNestedHit(hitObject, this); protected override DrawableStrongNestedHit CreateStrongNestedHit(DrumRollTick.StrongNestedHit hitObject) => new StrongNestedHit(hitObject);
private class StrongNestedHit : DrawableStrongNestedHit public class StrongNestedHit : DrawableStrongNestedHit
{ {
public StrongNestedHit(DrumRollTick.StrongNestedHit nestedHit, DrawableDrumRollTick tick) public new DrawableDrumRollTick ParentHitObject => (DrawableDrumRollTick)base.ParentHitObject;
: base(nestedHit, tick)
public StrongNestedHit()
: this(null)
{
}
public StrongNestedHit([CanBeNull] DrumRollTick.StrongNestedHit nestedHit)
: base(nestedHit)
{ {
} }
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (!MainObject.Judged) if (!ParentHitObject.Judged)
return; return;
ApplyResult(r => r.Type = MainObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult); ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
} }
public override bool OnPressed(TaikoAction action) => false; public override bool OnPressed(TaikoAction action) => false;

View File

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using JetBrains.Annotations;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Audio; using osu.Game.Audio;
@ -36,29 +36,51 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
private bool pressHandledThisFrame; private bool pressHandledThisFrame;
private readonly Bindable<HitType> type; private readonly Bindable<HitType> type = new Bindable<HitType>();
public DrawableHit(Hit hit) public DrawableHit()
: base(hit) : this(null)
{ {
type = HitObject.TypeBindable.GetBoundCopy();
FillMode = FillMode.Fit;
updateActionsFromType();
} }
[BackgroundDependencyLoader] public DrawableHit([CanBeNull] Hit hit)
private void load() : base(hit)
{ {
FillMode = FillMode.Fit;
}
protected override void OnApply()
{
type.BindTo(HitObject.TypeBindable);
type.BindValueChanged(_ => type.BindValueChanged(_ =>
{ {
updateActionsFromType(); updateActionsFromType();
// will overwrite samples, should only be called on change. // will overwrite samples, should only be called on subsequent changes
// after the initial application.
updateSamplesFromTypeChange(); updateSamplesFromTypeChange();
RecreatePieces(); RecreatePieces();
}); });
// action update also has to happen immediately on application.
updateActionsFromType();
base.OnApply();
}
protected override void OnFree()
{
base.OnFree();
type.UnbindFrom(HitObject.TypeBindable);
type.UnbindEvents();
UnproxyContent();
HitActions = null;
HitAction = null;
validActionPressed = pressHandledThisFrame = false;
} }
private HitSampleInfo[] getRimSamples() => HitObject.Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray(); private HitSampleInfo[] getRimSamples() => HitObject.Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray();
@ -228,32 +250,37 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
} }
} }
protected override DrawableStrongNestedHit CreateStrongNestedHit(Hit.StrongNestedHit hitObject) => new StrongNestedHit(hitObject, this); protected override DrawableStrongNestedHit CreateStrongNestedHit(Hit.StrongNestedHit hitObject) => new StrongNestedHit(hitObject);
private class StrongNestedHit : DrawableStrongNestedHit public class StrongNestedHit : DrawableStrongNestedHit
{ {
public new DrawableHit ParentHitObject => (DrawableHit)base.ParentHitObject;
/// <summary> /// <summary>
/// The lenience for the second key press. /// The lenience for the second key press.
/// This does not adjust by map difficulty in ScoreV2 yet. /// This does not adjust by map difficulty in ScoreV2 yet.
/// </summary> /// </summary>
private const double second_hit_window = 30; private const double second_hit_window = 30;
public new DrawableHit MainObject => (DrawableHit)base.MainObject; public StrongNestedHit()
: this(null)
{
}
public StrongNestedHit(Hit.StrongNestedHit nestedHit, DrawableHit hit) public StrongNestedHit([CanBeNull] Hit.StrongNestedHit nestedHit)
: base(nestedHit, hit) : base(nestedHit)
{ {
} }
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (!MainObject.Result.HasResult) if (!ParentHitObject.Result.HasResult)
{ {
base.CheckForResult(userTriggered, timeOffset); base.CheckForResult(userTriggered, timeOffset);
return; return;
} }
if (!MainObject.Result.IsHit) if (!ParentHitObject.Result.IsHit)
{ {
ApplyResult(r => r.Type = r.Judgement.MinResult); ApplyResult(r => r.Type = r.Judgement.MinResult);
return; return;
@ -261,27 +288,27 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (!userTriggered) if (!userTriggered)
{ {
if (timeOffset - MainObject.Result.TimeOffset > second_hit_window) if (timeOffset - ParentHitObject.Result.TimeOffset > second_hit_window)
ApplyResult(r => r.Type = r.Judgement.MinResult); ApplyResult(r => r.Type = r.Judgement.MinResult);
return; return;
} }
if (Math.Abs(timeOffset - MainObject.Result.TimeOffset) <= second_hit_window) if (Math.Abs(timeOffset - ParentHitObject.Result.TimeOffset) <= second_hit_window)
ApplyResult(r => r.Type = r.Judgement.MaxResult); ApplyResult(r => r.Type = r.Judgement.MaxResult);
} }
public override bool OnPressed(TaikoAction action) public override bool OnPressed(TaikoAction action)
{ {
// Don't process actions until the main hitobject is hit // Don't process actions until the main hitobject is hit
if (!MainObject.IsHit) if (!ParentHitObject.IsHit)
return false; return false;
// Don't process actions if the pressed button was released // Don't process actions if the pressed button was released
if (MainObject.HitAction == null) if (ParentHitObject.HitAction == null)
return false; return false;
// Don't handle invalid hit action presses // Don't handle invalid hit action presses
if (!MainObject.HitActions.Contains(action)) if (!ParentHitObject.HitActions.Contains(action))
return false; return false;
return UpdateResult(true); return UpdateResult(true);

View File

@ -1,7 +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.Game.Rulesets.Objects.Drawables; using JetBrains.Annotations;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables namespace osu.Game.Rulesets.Taiko.Objects.Drawables
@ -11,12 +11,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
/// </summary> /// </summary>
public abstract class DrawableStrongNestedHit : DrawableTaikoHitObject public abstract class DrawableStrongNestedHit : DrawableTaikoHitObject
{ {
public readonly DrawableHitObject MainObject; public new DrawableTaikoHitObject ParentHitObject => (DrawableTaikoHitObject)base.ParentHitObject;
protected DrawableStrongNestedHit(StrongNestedHitObject nestedHit, DrawableHitObject mainObject) protected DrawableStrongNestedHit([CanBeNull] StrongNestedHitObject nestedHit)
: base(nestedHit) : base(nestedHit)
{ {
MainObject = mainObject;
} }
} }
} }

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Linq; using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -35,7 +36,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
private readonly CircularContainer targetRing; private readonly CircularContainer targetRing;
private readonly CircularContainer expandingRing; private readonly CircularContainer expandingRing;
public DrawableSwell(Swell swell) public DrawableSwell()
: this(null)
{
}
public DrawableSwell([CanBeNull] Swell swell)
: base(swell) : base(swell)
{ {
FillMode = FillMode.Fit; FillMode = FillMode.Fit;
@ -123,12 +129,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Origin = Anchor.Centre, Origin = Anchor.Centre,
}); });
protected override void LoadComplete() protected override void OnFree()
{ {
base.LoadComplete(); base.OnFree();
// We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize UnproxyContent();
Width *= Parent.RelativeChildSize.X;
lastWasCentre = null;
} }
protected override void AddNestedHitObject(DrawableHitObject hitObject) protected override void AddNestedHitObject(DrawableHitObject hitObject)
@ -146,7 +153,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override void ClearNestedHitObjects() protected override void ClearNestedHitObjects()
{ {
base.ClearNestedHitObjects(); base.ClearNestedHitObjects();
ticks.Clear(); ticks.Clear(false);
} }
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)

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 JetBrains.Annotations;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Taiko.Skinning.Default; using osu.Game.Rulesets.Taiko.Skinning.Default;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -11,7 +12,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{ {
public override bool DisplayResult => false; public override bool DisplayResult => false;
public DrawableSwellTick(SwellTick hitObject) public DrawableSwellTick()
: this(null)
{
}
public DrawableSwellTick([CanBeNull] SwellTick hitObject)
: base(hitObject) : base(hitObject)
{ {
} }

View File

@ -3,7 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using JetBrains.Annotations;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
private readonly Container nonProxiedContent; private readonly Container nonProxiedContent;
protected DrawableTaikoHitObject(TaikoHitObject hitObject) protected DrawableTaikoHitObject([CanBeNull] TaikoHitObject hitObject)
: base(hitObject) : base(hitObject)
{ {
AddRangeInternal(new[] AddRangeInternal(new[]
@ -113,25 +113,23 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{ {
public override Vector2 OriginPosition => new Vector2(DrawHeight / 2); public override Vector2 OriginPosition => new Vector2(DrawHeight / 2);
public new TObject HitObject; public new TObject HitObject => (TObject)base.HitObject;
protected Vector2 BaseSize; protected Vector2 BaseSize;
protected SkinnableDrawable MainPiece; protected SkinnableDrawable MainPiece;
protected DrawableTaikoHitObject(TObject hitObject) protected DrawableTaikoHitObject([CanBeNull] TObject hitObject)
: base(hitObject) : base(hitObject)
{ {
HitObject = hitObject;
Anchor = Anchor.CentreLeft; Anchor = Anchor.CentreLeft;
Origin = Anchor.Custom; Origin = Anchor.Custom;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
} }
[BackgroundDependencyLoader] protected override void OnApply()
private void load()
{ {
base.OnApply();
RecreatePieces(); RecreatePieces();
} }

View File

@ -2,7 +2,7 @@
// 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.Linq; using System.Linq;
using osu.Framework.Allocation; using JetBrains.Annotations;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Audio; using osu.Game.Audio;
@ -16,28 +16,38 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
where TObject : TaikoStrongableHitObject where TObject : TaikoStrongableHitObject
where TStrongNestedObject : StrongNestedHitObject where TStrongNestedObject : StrongNestedHitObject
{ {
private readonly Bindable<bool> isStrong; private readonly Bindable<bool> isStrong = new BindableBool();
private readonly Container<DrawableStrongNestedHit> strongHitContainer; private readonly Container<DrawableStrongNestedHit> strongHitContainer;
protected DrawableTaikoStrongableHitObject(TObject hitObject) protected DrawableTaikoStrongableHitObject([CanBeNull] TObject hitObject)
: base(hitObject) : base(hitObject)
{ {
isStrong = HitObject.IsStrongBindable.GetBoundCopy();
AddInternal(strongHitContainer = new Container<DrawableStrongNestedHit>()); AddInternal(strongHitContainer = new Container<DrawableStrongNestedHit>());
} }
[BackgroundDependencyLoader] protected override void OnApply()
private void load()
{ {
isStrong.BindTo(HitObject.IsStrongBindable);
isStrong.BindValueChanged(_ => isStrong.BindValueChanged(_ =>
{ {
// will overwrite samples, should only be called on change. // will overwrite samples, should only be called on subsequent changes
// after the initial application.
updateSamplesFromStrong(); updateSamplesFromStrong();
RecreatePieces(); RecreatePieces();
}); });
base.OnApply();
}
protected override void OnFree()
{
base.OnFree();
isStrong.UnbindFrom(HitObject.IsStrongBindable);
// ensure the next application does not accidentally overwrite samples.
isStrong.UnbindEvents();
} }
private HitSampleInfo[] getStrongSamples() => HitObject.Samples.Where(s => s.Name == HitSampleInfo.HIT_FINISH).ToArray(); private HitSampleInfo[] getStrongSamples() => HitObject.Samples.Where(s => s.Name == HitSampleInfo.HIT_FINISH).ToArray();
@ -86,7 +96,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override void ClearNestedHitObjects() protected override void ClearNestedHitObjects()
{ {
base.ClearNestedHitObjects(); base.ClearNestedHitObjects();
strongHitContainer.Clear(); strongHitContainer.Clear(false);
} }
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)

View File

@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Taiko.Replays; using osu.Game.Rulesets.Taiko.Replays;
using osu.Framework.Input; using osu.Framework.Input;
@ -64,22 +63,7 @@ namespace osu.Game.Rulesets.Taiko.UI
protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo); protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo);
public override DrawableHitObject<TaikoHitObject> CreateDrawableRepresentation(TaikoHitObject h) public override DrawableHitObject<TaikoHitObject> CreateDrawableRepresentation(TaikoHitObject h) => null;
{
switch (h)
{
case Hit hit:
return new DrawableHit(hit);
case DrumRoll drumRoll:
return new DrawableDrumRoll(drumRoll);
case Swell swell:
return new DrawableSwell(swell);
}
return null;
}
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay); protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay);

View File

@ -147,6 +147,32 @@ namespace osu.Game.Rulesets.Taiko.UI
}, },
drumRollHitContainer.CreateProxy(), drumRollHitContainer.CreateProxy(),
}; };
RegisterPool<Hit, DrawableHit>(50);
RegisterPool<Hit.StrongNestedHit, DrawableHit.StrongNestedHit>(50);
RegisterPool<DrumRoll, DrawableDrumRoll>(5);
RegisterPool<DrumRoll.StrongNestedHit, DrawableDrumRoll.StrongNestedHit>(5);
RegisterPool<DrumRollTick, DrawableDrumRollTick>(100);
RegisterPool<DrumRollTick.StrongNestedHit, DrawableDrumRollTick.StrongNestedHit>(100);
RegisterPool<Swell, DrawableSwell>(5);
RegisterPool<SwellTick, DrawableSwellTick>(100);
}
protected override void LoadComplete()
{
base.LoadComplete();
NewResult += OnNewResult;
}
protected override void OnNewDrawableHitObject(DrawableHitObject drawableHitObject)
{
base.OnNewDrawableHitObject(drawableHitObject);
var taikoObject = (DrawableTaikoHitObject)drawableHitObject;
topLevelHitContainer.Add(taikoObject.CreateProxiedContent());
} }
protected override void Update() protected override void Update()
@ -207,9 +233,7 @@ namespace osu.Game.Rulesets.Taiko.UI
barLinePlayfield.Add(barLine); barLinePlayfield.Add(barLine);
break; break;
case DrawableTaikoHitObject taikoObject: case DrawableTaikoHitObject _:
h.OnNewResult += OnNewResult;
topLevelHitContainer.Add(taikoObject.CreateProxiedContent());
base.Add(h); base.Add(h);
break; break;
@ -226,8 +250,6 @@ namespace osu.Game.Rulesets.Taiko.UI
return barLinePlayfield.Remove(barLine); return barLinePlayfield.Remove(barLine);
case DrawableTaikoHitObject _: case DrawableTaikoHitObject _:
h.OnNewResult -= OnNewResult;
// todo: consider tidying of proxied content if required.
return base.Remove(h); return base.Remove(h);
default: default:
@ -248,7 +270,7 @@ namespace osu.Game.Rulesets.Taiko.UI
{ {
case TaikoStrongJudgement _: case TaikoStrongJudgement _:
if (result.IsHit) if (result.IsHit)
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).MainObject)?.VisualiseSecondHit(); hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).ParentHitObject)?.VisualiseSecondHit();
break; break;
case TaikoDrumRollTickJudgement _: case TaikoDrumRollTickJudgement _:

View File

@ -22,22 +22,28 @@ namespace osu.Game.Tests.Visual.Multiplayer
{ {
new DrawableRoom(new Room new DrawableRoom(new Room
{ {
Name = { Value = "Room 1" }, Name = { Value = "Open - ending in 1 day" },
Status = { Value = new RoomStatusOpen() }, Status = { Value = new RoomStatusOpen() },
EndDate = { Value = DateTimeOffset.Now.AddDays(1) } EndDate = { Value = DateTimeOffset.Now.AddDays(1) }
}) { MatchingFilter = true }, }) { MatchingFilter = true },
new DrawableRoom(new Room new DrawableRoom(new Room
{ {
Name = { Value = "Room 2" }, Name = { Value = "Playing - ending in 1 day" },
Status = { Value = new RoomStatusPlaying() }, Status = { Value = new RoomStatusPlaying() },
EndDate = { Value = DateTimeOffset.Now.AddDays(1) } EndDate = { Value = DateTimeOffset.Now.AddDays(1) }
}) { MatchingFilter = true }, }) { MatchingFilter = true },
new DrawableRoom(new Room new DrawableRoom(new Room
{ {
Name = { Value = "Room 3" }, Name = { Value = "Ended" },
Status = { Value = new RoomStatusEnded() }, Status = { Value = new RoomStatusEnded() },
EndDate = { Value = DateTimeOffset.Now } EndDate = { Value = DateTimeOffset.Now }
}) { MatchingFilter = true }, }) { MatchingFilter = true },
new DrawableRoom(new Room
{
Name = { Value = "Open (realtime)" },
Status = { Value = new RoomStatusOpen() },
Category = { Value = RoomCategory.Realtime }
}) { MatchingFilter = true },
} }
}; };
} }

View File

@ -0,0 +1,16 @@
// 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 Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
namespace osu.Game.IO.Serialization.Converters
{
public class SnakeCaseStringEnumConverter : StringEnumConverter
{
public SnakeCaseStringEnumConverter()
{
NamingStrategy = new SnakeCaseNamingStrategy();
}
}
}

View File

@ -80,7 +80,7 @@ namespace osu.Game.Online.API.Requests.Responses
public BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) public BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets)
{ {
return new BeatmapSetInfo var beatmapSet = new BeatmapSetInfo
{ {
OnlineBeatmapSetID = OnlineBeatmapSetID, OnlineBeatmapSetID = OnlineBeatmapSetID,
Metadata = this, Metadata = this,
@ -104,8 +104,17 @@ namespace osu.Game.Online.API.Requests.Responses
Genre = genre, Genre = genre,
Language = language Language = language
}, },
Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(),
}; };
beatmapSet.Beatmaps = beatmaps?.Select(b =>
{
var beatmap = b.ToBeatmap(rulesets);
beatmap.BeatmapSet = beatmapSet;
beatmap.Metadata = beatmapSet.Metadata;
return beatmap;
}).ToList();
return beatmapSet;
} }
} }
} }

View File

@ -6,6 +6,7 @@ using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Game.IO.Serialization.Converters;
using osu.Game.Online.Multiplayer.GameTypes; using osu.Game.Online.Multiplayer.GameTypes;
using osu.Game.Online.Multiplayer.RoomStatuses; using osu.Game.Online.Multiplayer.RoomStatuses;
using osu.Game.Users; using osu.Game.Users;
@ -35,12 +36,21 @@ namespace osu.Game.Online.Multiplayer
public readonly Bindable<int> ChannelId = new Bindable<int>(); public readonly Bindable<int> ChannelId = new Bindable<int>();
[Cached] [Cached]
[JsonProperty("category")] [JsonIgnore]
public readonly Bindable<RoomCategory> Category = new Bindable<RoomCategory>(); public readonly Bindable<RoomCategory> Category = new Bindable<RoomCategory>();
// Todo: osu-framework bug (https://github.com/ppy/osu-framework/issues/4106)
[JsonProperty("category")]
[JsonConverter(typeof(SnakeCaseStringEnumConverter))]
private RoomCategory category
{
get => Category.Value;
set => Category.Value = value;
}
[Cached] [Cached]
[JsonIgnore] [JsonIgnore]
public readonly Bindable<TimeSpan> Duration = new Bindable<TimeSpan>(TimeSpan.FromMinutes(30)); public readonly Bindable<TimeSpan?> Duration = new Bindable<TimeSpan?>();
[Cached] [Cached]
[JsonIgnore] [JsonIgnore]
@ -67,27 +77,26 @@ namespace osu.Game.Online.Multiplayer
public readonly BindableList<User> RecentParticipants = new BindableList<User>(); public readonly BindableList<User> RecentParticipants = new BindableList<User>();
[Cached] [Cached]
[JsonProperty("participant_count")]
public readonly Bindable<int> ParticipantCount = new Bindable<int>(); public readonly Bindable<int> ParticipantCount = new Bindable<int>();
// todo: TEMPORARY
[JsonProperty("participant_count")]
private int? participantCount
{
get => ParticipantCount.Value;
set => ParticipantCount.Value = value ?? 0;
}
[JsonProperty("duration")] [JsonProperty("duration")]
private int duration private int? duration
{ {
get => (int)Duration.Value.TotalMinutes; get => (int?)Duration.Value?.TotalMinutes;
set => Duration.Value = TimeSpan.FromMinutes(value); set
{
if (value == null)
Duration.Value = null;
else
Duration.Value = TimeSpan.FromMinutes(value.Value);
}
} }
// Only supports retrieval for now // Only supports retrieval for now
[Cached] [Cached]
[JsonProperty("ends_at")] [JsonProperty("ends_at")]
public readonly Bindable<DateTimeOffset> EndDate = new Bindable<DateTimeOffset>(); public readonly Bindable<DateTimeOffset?> EndDate = new Bindable<DateTimeOffset?>();
// Todo: Find a better way to do this (https://github.com/ppy/osu-framework/issues/1930) // Todo: Find a better way to do this (https://github.com/ppy/osu-framework/issues/1930)
[JsonProperty("max_attempts", DefaultValueHandling = DefaultValueHandling.Ignore)] [JsonProperty("max_attempts", DefaultValueHandling = DefaultValueHandling.Ignore)]
@ -133,7 +142,7 @@ namespace osu.Game.Online.Multiplayer
ParticipantCount.Value = other.ParticipantCount.Value; ParticipantCount.Value = other.ParticipantCount.Value;
EndDate.Value = other.EndDate.Value; EndDate.Value = other.EndDate.Value;
if (DateTimeOffset.Now >= EndDate.Value) if (EndDate.Value != null && DateTimeOffset.Now >= EndDate.Value)
Status.Value = new RoomStatusEnded(); Status.Value = new RoomStatusEnded();
if (!Playlist.SequenceEqual(other.Playlist)) if (!Playlist.SequenceEqual(other.Playlist))

View File

@ -303,7 +303,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
samplesBindable.CollectionChanged -= onSamplesChanged; samplesBindable.CollectionChanged -= onSamplesChanged;
// Release the samples for other hitobjects to use. // Release the samples for other hitobjects to use.
Samples.Samples = null; if (Samples != null)
Samples.Samples = null;
if (nestedHitObjects.IsValueCreated) if (nestedHitObjects.IsValueCreated)
{ {

View File

@ -112,7 +112,7 @@ namespace osu.Game.Screens.Multi.Components
{ {
currentJoinRoomRequest?.Cancel(); currentJoinRoomRequest?.Cancel();
if (JoinedRoom == null) if (JoinedRoom.Value == null)
return; return;
api.Queue(new PartRoomRequest(joinedRoom.Value)); api.Queue(new PartRoomRequest(joinedRoom.Value));

View File

@ -48,16 +48,23 @@ namespace osu.Game.Screens.Multi.Components
private class EndDatePart : DrawableDate private class EndDatePart : DrawableDate
{ {
public readonly IBindable<DateTimeOffset> EndDate = new Bindable<DateTimeOffset>(); public readonly IBindable<DateTimeOffset?> EndDate = new Bindable<DateTimeOffset?>();
public EndDatePart() public EndDatePart()
: base(DateTimeOffset.UtcNow) : base(DateTimeOffset.UtcNow)
{ {
EndDate.BindValueChanged(date => Date = date.NewValue); EndDate.BindValueChanged(date =>
{
// If null, set a very large future date to prevent unnecessary schedules.
Date = date.NewValue ?? DateTimeOffset.Now.AddYears(1);
}, true);
} }
protected override string Format() protected override string Format()
{ {
if (EndDate.Value == null)
return string.Empty;
var diffToNow = Date.Subtract(DateTimeOffset.Now); var diffToNow = Date.Subtract(DateTimeOffset.Now);
if (diffToNow.TotalSeconds < -5) if (diffToNow.TotalSeconds < -5)

View File

@ -325,7 +325,7 @@ namespace osu.Game.Screens.Multi.Match.Components
Availability.BindValueChanged(availability => AvailabilityPicker.Current.Value = availability.NewValue, true); Availability.BindValueChanged(availability => AvailabilityPicker.Current.Value = availability.NewValue, true);
Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true); Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true);
MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true);
Duration.BindValueChanged(duration => DurationField.Current.Value = duration.NewValue, true); Duration.BindValueChanged(duration => DurationField.Current.Value = duration.NewValue ?? TimeSpan.FromMinutes(30), true);
playlist.Items.BindTo(Playlist); playlist.Items.BindTo(Playlist);
Playlist.BindCollectionChanged(onPlaylistChanged, true); Playlist.BindCollectionChanged(onPlaylistChanged, true);

View File

@ -40,12 +40,12 @@ namespace osu.Game.Screens.Multi
protected Bindable<int?> MaxParticipants { get; private set; } protected Bindable<int?> MaxParticipants { get; private set; }
[Resolved(typeof(Room))] [Resolved(typeof(Room))]
protected Bindable<DateTimeOffset> EndDate { get; private set; } protected Bindable<DateTimeOffset?> EndDate { get; private set; }
[Resolved(typeof(Room))] [Resolved(typeof(Room))]
protected Bindable<RoomAvailability> Availability { get; private set; } protected Bindable<RoomAvailability> Availability { get; private set; }
[Resolved(typeof(Room))] [Resolved(typeof(Room))]
protected Bindable<TimeSpan> Duration { get; private set; } protected Bindable<TimeSpan?> Duration { get; private set; }
} }
} }

View File

@ -45,7 +45,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer
public override void PartRoom() public override void PartRoom()
{ {
if (JoinedRoom == null) if (JoinedRoom.Value == null)
return; return;
var joinedRoom = JoinedRoom.Value; var joinedRoom = JoinedRoom.Value;

View File

@ -13,7 +13,7 @@ namespace osu.Game.Screens.Multi.Timeshift
public class TimeshiftReadyButton : ReadyButton public class TimeshiftReadyButton : ReadyButton
{ {
[Resolved(typeof(Room), nameof(Room.EndDate))] [Resolved(typeof(Room), nameof(Room.EndDate))]
private Bindable<DateTimeOffset> endDate { get; set; } private Bindable<DateTimeOffset?> endDate { get; set; }
public TimeshiftReadyButton() public TimeshiftReadyButton()
{ {
@ -32,7 +32,7 @@ namespace osu.Game.Screens.Multi.Timeshift
{ {
base.Update(); base.Update();
Enabled.Value = DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(GameBeatmap.Value.Track.Length) < endDate.Value; Enabled.Value = endDate.Value != null && DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(GameBeatmap.Value.Track.Length) < endDate.Value;
} }
} }
} }

View File

@ -12,7 +12,7 @@ using osu.Game.Screens.Multi.RealtimeMultiplayer;
namespace osu.Game.Tests.Visual.RealtimeMultiplayer namespace osu.Game.Tests.Visual.RealtimeMultiplayer
{ {
public class RealtimeMultiplayerTestScene : MultiplayerTestScene public abstract class RealtimeMultiplayerTestScene : MultiplayerTestScene
{ {
[Cached(typeof(StatefulMultiplayerClient))] [Cached(typeof(StatefulMultiplayerClient))]
public TestRealtimeMultiplayerClient Client { get; } public TestRealtimeMultiplayerClient Client { get; }
@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.RealtimeMultiplayer
private readonly bool joinRoom; private readonly bool joinRoom;
public RealtimeMultiplayerTestScene(bool joinRoom = true) protected RealtimeMultiplayerTestScene(bool joinRoom = true)
{ {
this.joinRoom = joinRoom; this.joinRoom = joinRoom;
base.Content.Add(content = new TestRealtimeRoomContainer { RelativeSizeAxes = Axes.Both }); base.Content.Add(content = new TestRealtimeRoomContainer { RelativeSizeAxes = Axes.Both });