1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 21:07:33 +08:00

Merge branch 'master' into ArrangeMod

This commit is contained in:
miterosan 2018-08-14 21:57:10 +02:00 committed by GitHub
commit 82054cde38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
115 changed files with 1631 additions and 1003 deletions

View File

@ -27,7 +27,7 @@
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="System.IO.Packaging" Version="4.5.0" />
<PackageReference Include="ppy.squirrel.windows" Version="1.8.0.3" />
<PackageReference Include="ppy.squirrel.windows" Version="1.8.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.1" />
</ItemGroup>

View File

@ -1,6 +1,7 @@
// 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.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Judgements
@ -9,8 +10,6 @@ namespace osu.Game.Rulesets.Catch.Judgements
{
public override bool AffectsCombo => false;
public override bool ShouldExplode => true;
protected override int NumericResultFor(HitResult result)
{
switch (result)
@ -32,5 +31,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
return 8;
}
}
public override bool ShouldExplodeFor(JudgementResult result) => true;
}
}

View File

@ -23,21 +23,10 @@ namespace osu.Game.Rulesets.Catch.Judgements
}
/// <summary>
/// The base health increase for the result achieved.
/// Retrieves the numeric health increase of a <see cref="HitResult"/>.
/// </summary>
public float HealthIncrease => HealthIncreaseFor(Result);
/// <summary>
/// Whether fruit on the platter should explode or drop.
/// Note that this is only checked if the owning object is also <see cref="IHasComboInformation.LastInCombo" />
/// </summary>
public virtual bool ShouldExplode => IsHit;
/// <summary>
/// Convert a <see cref="HitResult"/> to a base health increase.
/// </summary>
/// <param name="result">The value to convert.</param>
/// <returns>The base health increase.</returns>
/// <param name="result">The <see cref="HitResult"/> to find the numeric health increase for.</param>
/// <returns>The numeric health increase of <paramref name="result"/>.</returns>
protected virtual float HealthIncreaseFor(HitResult result)
{
switch (result)
@ -48,5 +37,18 @@ namespace osu.Game.Rulesets.Catch.Judgements
return 10.2f;
}
}
/// <summary>
/// Retrieves the numeric health increase of a <see cref="JudgementResult"/>.
/// </summary>
/// <param name="result">The <see cref="JudgementResult"/> to find the numeric health increase for.</param>
/// <returns>The numeric health increase of <paramref name="result"/>.</returns>
public float HealthIncreaseFor(JudgementResult result) => HealthIncreaseFor(result.Type);
/// <summary>
/// Whether fruit on the platter should explode or drop.
/// Note that this is only checked if the owning object is also <see cref="IHasComboInformation.LastInCombo" />
/// </summary>
public virtual bool ShouldExplodeFor(JudgementResult result) => result.IsHit;
}
}

View File

@ -1,10 +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.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Catch.Objects
{
public class Banana : Fruit
{
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
public override Judgement CreateJudgement() => new CatchBananaJudgement();
}
}

View File

@ -1,8 +1,6 @@
// 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.Rulesets.Catch.Judgements;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public class DrawableBanana : DrawableFruit
@ -11,7 +9,5 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
: base(h)
{
}
protected override CatchJudgement CreateJudgement() => new CatchBananaJudgement();
}
}

View File

@ -26,8 +26,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
AddNested(getVisualRepresentation?.Invoke(b));
}
protected override bool ProvidesJudgement => false;
protected override void AddNested(DrawableHitObject h)
{
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;

View File

@ -5,7 +5,6 @@ using System;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
@ -53,20 +52,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
public Func<CatchHitObject, bool> CheckPosition;
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (CheckPosition == null) return;
if (timeOffset >= 0)
{
var judgement = CreateJudgement();
judgement.Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss;
AddJudgement(judgement);
}
if (timeOffset >= 0 && Result != null)
ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss);
}
protected virtual CatchJudgement CreateJudgement() => new CatchJudgement();
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);

View File

@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Rulesets.Catch.Judgements;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
@ -24,8 +23,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
Masking = false;
}
protected override CatchJudgement CreateJudgement() => new CatchDropletJudgement();
[BackgroundDependencyLoader]
private void load()
{

View File

@ -26,8 +26,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
AddNested(getVisualRepresentation?.Invoke(o));
}
protected override bool ProvidesJudgement => false;
protected override void AddNested(DrawableHitObject h)
{
var catchObject = (DrawableCatchHitObject)h;

View File

@ -2,18 +2,15 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Game.Rulesets.Catch.Judgements;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public class DrawableTinyDroplet : DrawableDroplet
{
public DrawableTinyDroplet(Droplet h)
public DrawableTinyDroplet(TinyDroplet h)
: base(h)
{
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 8;
}
protected override CatchJudgement CreateJudgement() => new CatchTinyDropletJudgement();
}
}

View File

@ -1,9 +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.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Catch.Objects
{
public class Droplet : CatchHitObject
{
public override Judgement CreateJudgement() => new CatchDropletJudgement();
}
}

View File

@ -1,9 +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.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Catch.Objects
{
public class Fruit : CatchHitObject
{
public override Judgement CreateJudgement() => new CatchJudgement();
}
}

View File

@ -1,9 +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.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Catch.Objects
{
public class TinyDroplet : Droplet
{
public override Judgement CreateJudgement() => new CatchTinyDropletJudgement();
}
}

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
@ -21,55 +20,28 @@ namespace osu.Game.Rulesets.Catch.Scoring
private float hpDrainRate;
protected override void SimulateAutoplay(Beatmap<CatchHitObject> beatmap)
protected override void ApplyBeatmap(Beatmap<CatchHitObject> beatmap)
{
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
base.ApplyBeatmap(beatmap);
foreach (var obj in beatmap.HitObjects)
{
switch (obj)
{
case JuiceStream stream:
foreach (var nestedObject in stream.NestedHitObjects)
switch (nestedObject)
{
case TinyDroplet _:
AddJudgement(new CatchTinyDropletJudgement { Result = HitResult.Perfect });
break;
case Droplet _:
AddJudgement(new CatchDropletJudgement { Result = HitResult.Perfect });
break;
case Fruit _:
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
break;
}
break;
case BananaShower shower:
foreach (var _ in shower.NestedHitObjects.Cast<CatchHitObject>())
AddJudgement(new CatchBananaJudgement { Result = HitResult.Perfect });
break;
case Fruit _:
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
break;
}
}
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
}
private const double harshness = 0.01;
protected override void OnNewJudgement(Judgement judgement)
protected override void ApplyResult(JudgementResult result)
{
base.OnNewJudgement(judgement);
base.ApplyResult(result);
if (judgement.Result == HitResult.Miss)
if (result.Type == HitResult.Miss)
{
if (!judgement.IsBonus)
if (!result.Judgement.IsBonus)
Health.Value -= hpDrainRate * (harshness * 2);
return;
}
if (judgement is CatchJudgement catchJudgement)
Health.Value += Math.Max(catchJudgement.HealthIncrease - hpDrainRate, 0) * harshness;
if (result.Judgement is CatchJudgement catchJudgement)
Health.Value += Math.Max(catchJudgement.HealthIncreaseFor(result) - hpDrainRate, 0) * harshness;
}
}
}

View File

@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.UI
public override void Add(DrawableHitObject h)
{
h.OnJudgement += onJudgement;
h.OnNewResult += onNewResult;
base.Add(h);
@ -67,6 +67,7 @@ namespace osu.Game.Rulesets.Catch.UI
fruit.CheckPosition = CheckIfWeCanCatch;
}
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement);
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
=> catcherArea.OnResult((DrawableCatchHitObject)judgedObject, result);
}
}

View File

@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Catch.UI
private DrawableCatchHitObject lastPlateableFruit;
public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement)
public void OnResult(DrawableCatchHitObject fruit, JudgementResult result)
{
void runAfterLoaded(Action action)
{
@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.UI
lastPlateableFruit.OnLoadComplete = _ => action();
}
if (judgement.IsHit && fruit.CanBePlated)
if (result.IsHit && fruit.CanBePlated)
{
var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject);
@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Catch.UI
if (fruit.HitObject.LastInCombo)
{
if (((CatchJudgement)judgement).ShouldExplode)
if (((CatchJudgement)result.Judgement).ShouldExplodeFor(result))
runAfterLoaded(() => MovableCatcher.Explode());
else
MovableCatcher.Drop();

View File

@ -14,24 +14,20 @@ namespace osu.Game.Rulesets.Mania.Tests
/// </summary>
public class ScrollingTestContainer : Container
{
private readonly ScrollingDirection direction;
[Cached(Type = typeof(IScrollingInfo))]
private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
public ScrollingTestContainer(ScrollingDirection direction)
{
this.direction = direction;
scrollingInfo.Direction.Value = direction;
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs<IScrollingInfo>(new ScrollingInfo { Direction = { Value = direction }});
return dependencies;
}
public void Flip() => scrollingInfo.Direction.Value = scrollingInfo.Direction.Value == ScrollingDirection.Up ? ScrollingDirection.Down : ScrollingDirection.Up;
}
private class ScrollingInfo : IScrollingInfo
{
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
}
public class TestScrollingInfo : IScrollingInfo
{
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests
{
[TestFixture]
public class TestCaseEditor : EditorTestCase
{
private readonly Bindable<ManiaScrollingDirection> direction = new Bindable<ManiaScrollingDirection>();
public TestCaseEditor()
: base(new ManiaRuleset())
{
AddStep("upwards scroll", () => direction.Value = ManiaScrollingDirection.Up);
AddStep("downwards scroll", () => direction.Value = ManiaScrollingDirection.Down);
}
[BackgroundDependencyLoader]
private void load(RulesetConfigCache configCache)
{
var config = (ManiaConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
config.BindWith(ManiaSetting.ScrollDirection, direction);
}
}
}

View File

@ -46,15 +46,20 @@ namespace osu.Game.Rulesets.Mania.Tests
Spacing = new Vector2(20),
Children = new[]
{
createNoteDisplay(ScrollingDirection.Down),
createNoteDisplay(ScrollingDirection.Up),
createHoldNoteDisplay(ScrollingDirection.Down),
createHoldNoteDisplay(ScrollingDirection.Up),
createNoteDisplay(ScrollingDirection.Down, 1, out var note1),
createNoteDisplay(ScrollingDirection.Up, 2, out var note2),
createHoldNoteDisplay(ScrollingDirection.Down, 1, out var holdNote1),
createHoldNoteDisplay(ScrollingDirection.Up, 2, out var holdNote2),
}
};
AddAssert("note 1 facing downwards", () => verifyAnchors(note1, Anchor.y2));
AddAssert("note 2 facing upwards", () => verifyAnchors(note2, Anchor.y0));
AddAssert("hold note 1 facing downwards", () => verifyAnchors(holdNote1, Anchor.y2));
AddAssert("hold note 2 facing upwards", () => verifyAnchors(holdNote2, Anchor.y0));
}
private Drawable createNoteDisplay(ScrollingDirection direction)
private Drawable createNoteDisplay(ScrollingDirection direction, int identifier, out DrawableNote hitObject)
{
var note = new Note { StartTime = 999999999 };
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
@ -62,24 +67,24 @@ namespace osu.Game.Rulesets.Mania.Tests
return new ScrollingTestContainer(direction)
{
AutoSizeAxes = Axes.Both,
Child = new NoteContainer(direction, $"note, scrolling {direction.ToString().ToLowerInvariant()}")
Child = new NoteContainer(direction, $"note {identifier}, scrolling {direction.ToString().ToLowerInvariant()}")
{
Child = new DrawableNote(note) { AccentColour = Color4.OrangeRed }
Child = hitObject = new DrawableNote(note) { AccentColour = Color4.OrangeRed }
}
};
}
private Drawable createHoldNoteDisplay(ScrollingDirection direction)
private Drawable createHoldNoteDisplay(ScrollingDirection direction, int identifier, out DrawableHoldNote hitObject)
{
var note = new HoldNote { StartTime = 999999999, Duration = 1000 };
var note = new HoldNote { StartTime = 999999999, Duration = 5000 };
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
return new ScrollingTestContainer(direction)
{
AutoSizeAxes = Axes.Both,
Child = new NoteContainer(direction, $"hold note, scrolling {direction.ToString().ToLowerInvariant()}")
Child = new NoteContainer(direction, $"hold note {identifier}, scrolling {direction.ToString().ToLowerInvariant()}")
{
Child = new DrawableHoldNote(note)
Child = hitObject = new DrawableHoldNote(note)
{
RelativeSizeAxes = Axes.Both,
AccentColour = Color4.OrangeRed,
@ -88,6 +93,12 @@ namespace osu.Game.Rulesets.Mania.Tests
};
}
private bool verifyAnchors(DrawableHitObject hitObject, Anchor expectedAnchor)
=> hitObject.Anchor.HasFlag(expectedAnchor) && hitObject.Origin.HasFlag(expectedAnchor);
private bool verifyAnchors(DrawableHoldNote holdNote, Anchor expectedAnchor)
=> verifyAnchors((DrawableHitObject)holdNote, expectedAnchor) && holdNote.NestedHitObjects.All(n => verifyAnchors(n, expectedAnchor));
private class NoteContainer : Container
{
private readonly Container content;

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@ -24,6 +25,8 @@ namespace osu.Game.Rulesets.Mania.Tests
private readonly List<ManiaStage> stages = new List<ManiaStage>();
private FillFlowContainer<ScrollingTestContainer> fill;
public TestCaseStage()
: base(columns)
{
@ -32,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Tests
[BackgroundDependencyLoader]
private void load()
{
Child = new FillFlowContainer
Child = fill = new FillFlowContainer<ScrollingTestContainer>
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
@ -54,8 +57,22 @@ namespace osu.Game.Rulesets.Mania.Tests
AddStep("hold note", createHoldNote);
AddStep("minor bar line", () => createBarLine(false));
AddStep("major bar line", () => createBarLine(true));
AddAssert("check note anchors", () => notesInStageAreAnchored(stages[0], Anchor.TopCentre));
AddAssert("check note anchors", () => notesInStageAreAnchored(stages[1], Anchor.BottomCentre));
AddStep("flip direction", () =>
{
foreach (var c in fill.Children)
c.Flip();
});
AddAssert("check note anchors", () => notesInStageAreAnchored(stages[0], Anchor.BottomCentre));
AddAssert("check note anchors", () => notesInStageAreAnchored(stages[1], Anchor.TopCentre));
}
private bool notesInStageAreAnchored(ManiaStage stage, Anchor anchor) => stage.Columns.SelectMany(c => c.AllHitObjects).All(o => o.Anchor == anchor);
private void createNote()
{
foreach (var stage in stages)
@ -101,7 +118,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}
}
private Drawable createStage(ScrollingDirection direction, ManiaAction action)
private ScrollingTestContainer createStage(ScrollingDirection direction, ManiaAction action)
{
var specialAction = ManiaAction.Special1;

View File

@ -0,0 +1,84 @@
// 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.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.UI.Scrolling;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
{
public class HoldNoteMask : HitObjectMask
{
public new DrawableHoldNote HitObject => (DrawableHoldNote)base.HitObject;
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private readonly BodyPiece body;
public HoldNoteMask(DrawableHoldNote hold)
: base(hold)
{
InternalChildren = new Drawable[]
{
new HoldNoteNoteMask(hold.Head),
new HoldNoteNoteMask(hold.Tail),
body = new BodyPiece
{
AccentColour = Color4.Transparent
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, IScrollingInfo scrollingInfo)
{
body.BorderColour = colours.Yellow;
direction.BindTo(scrollingInfo.Direction);
}
protected override void Update()
{
base.Update();
Size = HitObject.DrawSize + new Vector2(0, HitObject.Tail.DrawHeight);
Position = Parent.ToLocalSpace(HitObject.ScreenSpaceDrawQuad.TopLeft);
// This is a side-effect of not matching the hitobject's anchors/origins, which is kinda hard to do
// When scrolling upwards our origin is already at the top of the head note (which is the intended location),
// but when scrolling downwards our origin is at the _bottom_ of the tail note (where we need to be at the _top_ of the tail note)
if (direction.Value == ScrollingDirection.Down)
Y -= HitObject.Tail.DrawHeight;
}
private class HoldNoteNoteMask : NoteMask
{
public HoldNoteNoteMask(DrawableNote note)
: base(note)
{
Select();
}
protected override void Update()
{
base.Update();
Anchor = HitObject.Anchor;
Origin = HitObject.Origin;
Position = HitObject.DrawPosition;
}
// Todo: This is temporary, since the note masks don't do anything special yet. In the future they will handle input.
public override bool HandleMouseInput => false;
}
}
}

View File

@ -0,0 +1,39 @@
// 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.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
{
public class NoteMask : HitObjectMask
{
public NoteMask(DrawableNote note)
: base(note)
{
Scale = note.Scale;
CornerRadius = 5;
Masking = true;
AddInternal(new NotePiece());
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Colour = colours.Yellow;
}
protected override void Update()
{
base.Update();
Size = HitObject.DrawSize;
Position = Parent.ToLocalSpace(HitObject.ScreenSpaceDrawQuad.TopLeft);
}
}
}

View File

@ -0,0 +1,17 @@
// 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.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.UI;
using System.Collections.Generic;
namespace osu.Game.Rulesets.Mania.Edit
{
public class ManiaEditPlayfield : ManiaPlayfield
{
public ManiaEditPlayfield(List<StageDefinition> stages)
: base(stages)
{
}
}
}

View File

@ -0,0 +1,27 @@
// 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;
using OpenTK;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Edit
{
public class ManiaEditRulesetContainer : ManiaRulesetContainer
{
public ManiaEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
protected override Playfield CreatePlayfield() => new ManiaEditPlayfield(Beatmap.Stages)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
protected override Vector2 PlayfieldArea => Vector2.One;
}
}

View File

@ -0,0 +1,56 @@
// 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.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays;
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 System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.UI;
namespace osu.Game.Rulesets.Mania.Edit
{
public class ManiaHitObjectComposer : HitObjectComposer
{
protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config;
public ManiaHitObjectComposer(Ruleset ruleset)
: base(ruleset)
{
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs<IScrollingInfo>(new ManiaScrollingInfo(Config));
return dependencies;
}
protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new ManiaEditRulesetContainer(ruleset, beatmap);
protected override IReadOnlyList<ICompositionTool> CompositionTools => new ICompositionTool[]
{
new HitObjectCompositionTool<Note>("Note"),
new HitObjectCompositionTool<HoldNote>("Hold"),
};
public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableNote note:
return new NoteMask(note);
case DrawableHoldNote holdNote:
return new HoldNoteMask(holdNote);
}
return base.CreateMaskFor(hitObject);
}
}
}

View File

@ -1,27 +0,0 @@
// 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.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Judgements
{
public class HoldNoteTailJudgement : ManiaJudgement
{
/// <summary>
/// Whether the hold note has been released too early and shouldn't give full score for the release.
/// </summary>
public bool HasBroken;
protected override int NumericResultFor(HitResult result)
{
switch (result)
{
default:
return base.NumericResultFor(result);
case HitResult.Great:
case HitResult.Perfect:
return base.NumericResultFor(HasBroken ? HitResult.Good : result);
}
}
}
}

View File

@ -19,9 +19,11 @@ using osu.Game.Configuration;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Difficulty;
using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania
@ -32,6 +34,8 @@ namespace osu.Game.Rulesets.Mania
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, Score score) => new ManiaPerformanceCalculator(this, beatmap, score);
public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this);
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))

View File

@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using OpenTK.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling;
@ -19,10 +18,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// </summary>
public class DrawableHoldNote : DrawableManiaHitObject<HoldNote>, IKeyBindingHandler<ManiaAction>
{
public override bool DisplayJudgement => false;
public override bool DisplayResult => false;
private readonly DrawableNote head;
private readonly DrawableNote tail;
public readonly DrawableNote Head;
public readonly DrawableNote Tail;
private readonly BodyPiece bodyPiece;
@ -57,12 +56,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
HoldStartTime = () => holdStartTime
})
},
head = new DrawableHeadNote(this)
Head = new DrawableHeadNote(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
tail = new DrawableTailNote(this)
Tail = new DrawableTailNote(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
@ -72,8 +71,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
foreach (var tick in tickContainer)
AddNested(tick);
AddNested(head);
AddNested(tail);
AddNested(Head);
AddNested(Tail);
}
protected override void OnDirectionChanged(ScrollingDirection direction)
@ -91,16 +90,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
base.AccentColour = value;
bodyPiece.AccentColour = value;
head.AccentColour = value;
tail.AccentColour = value;
Head.AccentColour = value;
Tail.AccentColour = value;
tickContainer.ForEach(t => t.AccentColour = value);
}
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (tail.AllJudged)
AddJudgement(new HoldNoteJudgement { Result = HitResult.Perfect });
if (Tail.AllJudged)
ApplyResult(r => r.Type = HitResult.Perfect);
}
protected override void Update()
@ -108,8 +107,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
base.Update();
// Make the body piece not lie under the head note
bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * head.Height / 2;
bodyPiece.Height = DrawHeight - head.Height / 2 + tail.Height / 2;
bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * Head.Height / 2;
bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2;
}
public bool OnPressed(ManiaAction action)
@ -141,7 +140,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
holdStartTime = null;
// If the key has been released too early, the user should not receive full score for the release
if (!tail.IsHit)
if (!Tail.IsHit)
hasBroken = true;
return true;
@ -166,7 +165,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return false;
// If the key has been released too early, the user should not receive full score for the release
if (Judgements.Any(j => j.Result == HitResult.Miss))
if (Result.Type == HitResult.Miss)
holdNote.hasBroken = true;
// The head note also handles early hits before the body, but we want accurate early hits to count as the body being held
@ -197,7 +196,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
this.holdNote = holdNote;
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
// Factor in the release lenience
timeOffset /= release_window_lenience;
@ -205,13 +204,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
{
AddJudgement(new HoldNoteTailJudgement
{
Result = HitResult.Miss,
HasBroken = holdNote.hasBroken
});
}
ApplyResult(r => r.Type = HitResult.Miss);
return;
}
@ -220,10 +213,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (result == HitResult.None)
return;
AddJudgement(new HoldNoteTailJudgement
ApplyResult(r =>
{
Result = result,
HasBroken = holdNote.hasBroken
if (holdNote.hasBroken && (result == HitResult.Perfect || result == HitResult.Perfect))
result = HitResult.Good;
r.Type = result;
});
}
@ -238,7 +233,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (action != Action.Value)
return false;
UpdateJudgement(true);
UpdateResult(true);
// Handled by the hold note, which will set holding = false
return false;

View File

@ -7,7 +7,6 @@ using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Scoring;
@ -72,29 +71,17 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered)
return;
if (Time.Current < HitObject.StartTime)
return;
if (HoldStartTime?.Invoke() > HitObject.StartTime)
return;
var startTime = HoldStartTime?.Invoke();
AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
}
protected override void Update()
{
if (AllJudged)
return;
if (HoldStartTime?.Invoke() == null)
return;
UpdateJudgement(true);
if (startTime == null || startTime > HitObject.StartTime)
ApplyResult(r => r.Type = HitResult.Miss);
else
ApplyResult(r => r.Type = HitResult.Perfect);
}
}
}

View File

@ -6,7 +6,6 @@ using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling;
@ -56,12 +55,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
AddJudgement(new ManiaJudgement { Result = HitResult.Miss });
ApplyResult(r => r.Type = HitResult.Miss);
return;
}
@ -69,7 +68,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (result == HitResult.None)
return;
AddJudgement(new ManiaJudgement { Result = result });
ApplyResult(r => r.Type = result);
}
public virtual bool OnPressed(ManiaAction action)
@ -77,7 +76,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (action != Action.Value)
return false;
return UpdateJudgement(true);
return UpdateResult(true);
}
public virtual bool OnReleased(ManiaAction action) => false;

View File

@ -3,6 +3,8 @@
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;
namespace osu.Game.Rulesets.Mania.Objects
@ -55,7 +57,7 @@ namespace osu.Game.Rulesets.Mania.Objects
/// <summary>
/// The tail note of the hold.
/// </summary>
public readonly Note Tail = new Note();
public readonly TailNote Tail = new TailNote();
/// <summary>
/// The time between ticks of this hold.
@ -94,5 +96,7 @@ namespace osu.Game.Rulesets.Mania.Objects
});
}
}
public override Judgement CreateJudgement() => new HoldNoteJudgement();
}
}

View File

@ -1,6 +1,9 @@
// 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.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
namespace osu.Game.Rulesets.Mania.Objects
{
/// <summary>
@ -8,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Objects
/// </summary>
public class HoldNoteTick : ManiaHitObject
{
public override Judgement CreateJudgement() => new HoldNoteTickJudgement();
}
}

View File

@ -1,6 +1,9 @@
// 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.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
namespace osu.Game.Rulesets.Mania.Objects
{
/// <summary>
@ -8,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Objects
/// </summary>
public class Note : ManiaHitObject
{
public override Judgement CreateJudgement() => new ManiaJudgement();
}
}

View File

@ -0,0 +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.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
namespace osu.Game.Rulesets.Mania.Objects
{
public class TailNote : Note
{
public override Judgement CreateJudgement() => new ManiaJudgement();
}
}

View File

@ -1,7 +1,6 @@
// 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.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
@ -97,31 +96,20 @@ namespace osu.Game.Rulesets.Mania.Scoring
{
}
protected override void SimulateAutoplay(Beatmap<ManiaHitObject> beatmap)
protected override void ApplyBeatmap(Beatmap<ManiaHitObject> beatmap)
{
base.ApplyBeatmap(beatmap);
BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty;
hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
}
protected override void SimulateAutoplay(Beatmap<ManiaHitObject> beatmap)
{
while (true)
{
foreach (var obj in beatmap.HitObjects)
{
var holdNote = obj as HoldNote;
if (holdNote != null)
{
// Head
AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
// Ticks
int tickCount = holdNote.NestedHitObjects.OfType<HoldNoteTick>().Count();
for (int i = 0; i < tickCount; i++)
AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
}
AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
}
base.SimulateAutoplay(beatmap);
if (!HasFailed)
break;
@ -133,20 +121,20 @@ namespace osu.Game.Rulesets.Mania.Scoring
}
}
protected override void OnNewJudgement(Judgement judgement)
protected override void ApplyResult(JudgementResult result)
{
base.OnNewJudgement(judgement);
base.ApplyResult(result);
bool isTick = judgement is HoldNoteTickJudgement;
bool isTick = result.Judgement is HoldNoteTickJudgement;
if (isTick)
{
if (judgement.IsHit)
if (result.IsHit)
Health.Value += hpMultiplier * hp_increase_tick;
}
else
{
switch (judgement.Result)
switch (result.Type)
{
case HitResult.Miss:
Health.Value += hpMissMultiplier * hp_increase_miss;

View File

@ -131,14 +131,14 @@ namespace osu.Game.Rulesets.Mania.UI
public override void Add(DrawableHitObject hitObject)
{
hitObject.AccentColour = AccentColour;
hitObject.OnJudgement += OnJudgement;
hitObject.OnNewResult += OnNewResult;
HitObjects.Add(hitObject);
}
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
if (!judgement.IsHit || !judgedObject.DisplayJudgement || !DisplayJudgements)
if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements)
return;
explosionContainer.Add(new HitExplosion(judgedObject)

View File

@ -8,10 +8,10 @@ using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.UI
{
internal class DrawableManiaJudgement : DrawableJudgement
public class DrawableManiaJudgement : DrawableJudgement
{
public DrawableManiaJudgement(Judgement judgement, DrawableHitObject judgedObject)
: base(judgement, judgedObject)
public DrawableManiaJudgement(JudgementResult result, DrawableHitObject judgedObject)
: base(result, judgedObject)
{
}
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.UI
this.FadeInFromZero(50, Easing.OutQuint);
if (Judgement.IsHit)
if (Result.IsHit)
{
this.ScaleTo(0.8f);
this.ScaleTo(1, 250, Easing.OutElastic);

View File

@ -1,22 +1,20 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects;
using osu.Framework.Graphics.Containers;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.UI
{
public class ManiaPlayfield : ManiaScrollingPlayfield
{
public List<Column> Columns => stages.SelectMany(x => x.Columns).ToList();
private readonly List<ManiaStage> stages = new List<ManiaStage>();
public ManiaPlayfield(List<StageDefinition> stageDefinitions)

View File

@ -4,7 +4,6 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Input;
@ -35,8 +34,7 @@ namespace osu.Game.Rulesets.Mania.UI
public IEnumerable<BarLine> BarLines;
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
private ScrollingInfo scrollingInfo;
protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config;
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
@ -73,9 +71,6 @@ namespace osu.Game.Rulesets.Mania.UI
private void load()
{
BarLines.ForEach(Playfield.Add);
((ManiaConfigManager)Config).BindWith(ManiaSetting.ScrollDirection, configDirection);
configDirection.BindValueChanged(d => scrollingInfo.Direction.Value = (ScrollingDirection)d, true);
}
private DependencyContainer dependencies;
@ -83,11 +78,14 @@ namespace osu.Game.Rulesets.Mania.UI
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs<IScrollingInfo>(scrollingInfo = new ScrollingInfo());
if (dependencies.Get<ManiaScrollingInfo>() == null)
dependencies.CacheAs<IScrollingInfo>(new ManiaScrollingInfo(Config));
return dependencies;
}
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages)
protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -115,11 +113,5 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f);
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
private class ScrollingInfo : IScrollingInfo
{
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
}
}
}

View File

@ -0,0 +1,23 @@
// 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.Configuration;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Mania.UI
{
public class ManiaScrollingInfo : IScrollingInfo
{
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
public ManiaScrollingInfo(ManiaConfigManager config)
{
config.BindWith(ManiaSetting.ScrollDirection, configDirection);
configDirection.BindValueChanged(v => Direction.Value = (ScrollingDirection)v, true);
}
}
}

View File

@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.UI
/// <summary>
/// A collection of <see cref="Column"/>s.
/// </summary>
internal class ManiaStage : ManiaScrollingPlayfield
public class ManiaStage : ManiaScrollingPlayfield
{
public const float HIT_TARGET_POSITION = 50;
@ -156,18 +156,18 @@ namespace osu.Game.Rulesets.Mania.UI
var maniaObject = (ManiaHitObject)h.HitObject;
int columnIndex = maniaObject.Column - firstColumnIndex;
Columns.ElementAt(columnIndex).Add(h);
h.OnJudgement += OnJudgement;
h.OnNewResult += OnNewResult;
}
public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline));
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
if (!judgedObject.DisplayJudgement || !DisplayJudgements)
if (!judgedObject.DisplayResult || !DisplayJudgements)
return;
judgements.Clear();
judgements.Add(new DrawableManiaJudgement(judgement, judgedObject)
judgements.Add(new DrawableManiaJudgement(result, judgedObject)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -5,12 +5,10 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Tests.Visual;
using OpenTK;
using osu.Game.Rulesets.Osu.Judgements;
using System.Collections.Generic;
using System;
using osu.Game.Rulesets.Mods;
@ -96,19 +94,15 @@ namespace osu.Game.Rulesets.Osu.Tests
this.auto = auto;
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (auto && !userTriggered && timeOffset > 0)
{
// force success
AddJudgement(new OsuJudgement
{
Result = HitResult.Great
});
State.Value = ArmedState.Hit;
ApplyResult(r => r.Type = HitResult.Great);
}
else
base.CheckForJudgements(userTriggered, timeOffset);
base.CheckForResult(userTriggered, timeOffset);
}
}
}

View File

@ -304,13 +304,13 @@ namespace osu.Game.Rulesets.Osu.Tests
foreach (var mod in Mods.OfType<IApplicableToDrawableHitObjects>())
mod.ApplyToDrawableHitObjects(new[] { drawable });
drawable.OnJudgement += onJudgement;
drawable.OnNewResult += onNewResult;
Add(drawable);
}
private float judgementOffsetDirection = 1;
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
var osuObject = judgedObject as DrawableOsuHitObject;
if (osuObject == null)
@ -321,8 +321,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = judgement.IsHit ? "Hit!" : "Miss!",
Colour = judgement.IsHit ? Color4.Green : Color4.Red,
Text = result.IsHit ? "Hit!" : "Miss!",
Colour = result.IsHit ? Color4.Green : Color4.Red,
TextSize = 30,
Position = osuObject.HitObject.StackedEndPosition + judgementOffsetDirection * new Vector2(0, 45)
});

View File

@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Tests
this.auto = auto;
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (auto && !userTriggered && Time.Current > Spinner.StartTime + Spinner.Duration / 2 && Progress < 1)
{
@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Osu.Tests
auto = false;
}
base.CheckForJudgements(userTriggered, timeOffset);
base.CheckForResult(userTriggered, timeOffset);
}
}
}

View File

@ -0,0 +1,17 @@
// 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.ComponentModel;
namespace osu.Game.Rulesets.Osu.Judgements
{
public enum ComboResult
{
[Description(@"")]
None,
[Description(@"Good")]
Good,
[Description(@"Amazing")]
Perfect
}
}

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Judgements
@ -25,7 +24,5 @@ namespace osu.Game.Rulesets.Osu.Judgements
return 300;
}
}
public ComboResult Combo;
}
}

View File

@ -0,0 +1,17 @@
// 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.Rulesets.Judgements;
namespace osu.Game.Rulesets.Osu.Judgements
{
public class OsuJudgementResult : JudgementResult
{
public ComboResult ComboType;
public OsuJudgementResult(Judgement judgement)
: base(judgement)
{
}
}
}

View File

@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using OpenTK.Graphics;
@ -40,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (AllJudged)
return false;
UpdateJudgement(true);
UpdateResult(true);
return true;
},
},
@ -77,12 +76,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
AddJudgement(new OsuJudgement { Result = HitResult.Miss });
ApplyResult(r => r.Type = HitResult.Miss);
return;
}
@ -90,10 +90,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (result == HitResult.None)
return;
AddJudgement(new OsuJudgement
{
Result = result,
});
ApplyResult(r => r.Type = result);
}
protected override void UpdatePreemptState()

View File

@ -2,11 +2,11 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.ComponentModel;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics;
using System.Linq;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using OpenTK.Graphics;
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
UpdatePreemptState();
var judgementOffset = Math.Min(HitObject.HitWindows.HalfWindowFor(HitResult.Miss), Judgements.FirstOrDefault()?.TimeOffset ?? 0);
var judgementOffset = Math.Min(HitObject.HitWindows.HalfWindowFor(HitResult.Miss), Result?.TimeOffset ?? 0);
using (BeginDelayedSequence(HitObject.TimePreempt + judgementOffset, true))
UpdateCurrentState(state);
@ -57,20 +57,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
// Todo: At some point we need to move these to DrawableHitObject after ensuring that all other Rulesets apply
// transforms in the same way and don't rely on them not being cleared
public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { }
public override void ApplyTransformsAt(double time, bool propagateChildren = false) { }
public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null)
{
}
public override void ApplyTransformsAt(double time, bool propagateChildren = false)
{
}
private OsuInputManager osuActionInputManager;
internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager);
}
public enum ComboResult
{
[Description(@"")]
None,
[Description(@"Good")]
Good,
[Description(@"Amazing")]
Perfect
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement);
}
}

View File

@ -11,14 +11,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableOsuJudgement : DrawableJudgement
{
public DrawableOsuJudgement(Judgement judgement, DrawableHitObject judgedObject)
: base(judgement, judgedObject)
public DrawableOsuJudgement(JudgementResult result, DrawableHitObject judgedObject)
: base(result, judgedObject)
{
}
protected override void LoadComplete()
{
if (Judgement.Result != HitResult.Miss)
if (Result.Type != HitResult.Miss)
JudgementText?.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint);
base.LoadComplete();

View File

@ -8,7 +8,6 @@ using osu.Framework.MathUtils;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
using osu.Game.Graphics;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
@ -42,10 +41,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
};
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (repeatPoint.StartTime <= Time.Current)
AddJudgement(new OsuJudgement { Result = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss });
ApplyResult(r => r.Type = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss);
}
protected override void UpdatePreemptState()

View File

@ -9,7 +9,6 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Configuration;
using osu.Game.Rulesets.Scoring;
using OpenTK.Graphics;
@ -132,23 +131,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered && Time.Current >= slider.EndTime)
if (userTriggered || Time.Current < slider.EndTime)
return;
ApplyResult(r =>
{
var judgementsCount = NestedHitObjects.Count();
var judgementsHit = NestedHitObjects.Count(h => h.IsHit);
var hitFraction = (double)judgementsHit / judgementsCount;
if (hitFraction == 1 && HeadCircle.Judgements.Any(j => j.Result == HitResult.Great))
AddJudgement(new OsuJudgement { Result = HitResult.Great });
else if (hitFraction >= 0.5 && HeadCircle.Judgements.Any(j => j.Result >= HitResult.Good))
AddJudgement(new OsuJudgement { Result = HitResult.Good });
if (hitFraction == 1 && HeadCircle.Result.Type == HitResult.Great)
r.Type = HitResult.Great;
else if (hitFraction >= 0.5 && HeadCircle.Result.Type >= HitResult.Good)
r.Type = HitResult.Good;
else if (hitFraction > 0)
AddJudgement(new OsuJudgement { Result = HitResult.Meh });
r.Type = HitResult.Meh;
else
AddJudgement(new OsuJudgement { Result = HitResult.Miss });
}
r.Type = HitResult.Miss;
});
}
protected override void UpdateCurrentState(ArmedState state)

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
@ -12,11 +11,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
/// <summary>
/// The judgement text is provided by the <see cref="DrawableSlider"/>.
/// </summary>
public override bool DisplayJudgement => false;
public override bool DisplayResult => false;
public bool Tracking { get; set; }
public DrawableSliderTail(Slider slider, HitCircle hitCircle)
public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle)
: base(hitCircle)
{
Origin = Anchor.Centre;
@ -29,10 +28,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Position = HitObject.Position - slider.Position;
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered && timeOffset >= 0)
AddJudgement(new OsuSliderTailJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss });
ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
}
}
}

View File

@ -6,7 +6,6 @@ using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osu.Framework.Graphics.Containers;
@ -19,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public bool Tracking { get; set; }
public override bool DisplayJudgement => false;
public override bool DisplayResult => false;
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
{
@ -48,10 +47,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
};
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (timeOffset >= 0)
AddJudgement(new OsuJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss });
ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
}
protected override void UpdatePreemptState()

View File

@ -11,7 +11,6 @@ using OpenTK.Graphics;
using osu.Game.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Screens.Ranking;
using osu.Game.Rulesets.Scoring;
@ -117,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public float Progress => MathHelper.Clamp(Disc.RotationAbsolute / 360 / Spinner.SpinsRequired, 0, 1);
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (Time.Current < HitObject.StartTime) return;
@ -136,17 +135,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
glow.FadeColour(completeColour, duration);
}
if (!userTriggered && Time.Current >= Spinner.EndTime)
if (userTriggered || Time.Current < Spinner.EndTime)
return;
ApplyResult(r =>
{
if (Progress >= 1)
AddJudgement(new OsuJudgement { Result = HitResult.Great });
r.Type = HitResult.Great;
else if (Progress > .9)
AddJudgement(new OsuJudgement { Result = HitResult.Good });
r.Type = HitResult.Good;
else if (Progress > .75)
AddJudgement(new OsuJudgement { Result = HitResult.Meh });
r.Type = HitResult.Meh;
else if (Time.Current >= Spinner.EndTime)
AddJudgement(new OsuJudgement { Result = HitResult.Miss });
}
r.Type = HitResult.Miss;
});
}
[BackgroundDependencyLoader]

View File

@ -1,9 +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.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
public class HitCircle : OsuHitObject
{
public override Judgement CreateJudgement() => new OsuJudgement();
}
}

View File

@ -4,6 +4,8 @@
using System;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
@ -24,5 +26,7 @@ namespace osu.Game.Rulesets.Osu.Objects
if (RepeatIndex > 0)
TimePreempt = Math.Min(SpanDuration * 2, TimePreempt);
}
public override Judgement CreateJudgement() => new OsuJudgement();
}
}

View File

@ -10,6 +10,8 @@ using System.Linq;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
@ -94,7 +96,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public double TickDistance;
public HitCircle HeadCircle;
public HitCircle TailCircle;
public SliderTailCircle TailCircle;
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
@ -133,7 +135,7 @@ namespace osu.Game.Rulesets.Osu.Objects
ComboIndex = ComboIndex,
};
TailCircle = new SliderCircle(this)
TailCircle = new SliderTailCircle(this)
{
StartTime = EndTime,
Position = EndPosition,
@ -211,5 +213,7 @@ namespace osu.Game.Rulesets.Osu.Objects
});
}
}
public override Judgement CreateJudgement() => new OsuJudgement();
}
}

View File

@ -0,0 +1,18 @@
// 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.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
public class SliderTailCircle : SliderCircle
{
public SliderTailCircle(Slider slider)
: base(slider)
{
}
public override Judgement CreateJudgement() => new OsuSliderTailJudgement();
}
}

View File

@ -3,6 +3,8 @@
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
@ -26,5 +28,7 @@ namespace osu.Game.Rulesets.Osu.Objects
TimePreempt = (StartTime - SpanStartTime) / 2 + offset;
}
public override Judgement CreateJudgement() => new OsuJudgement();
}
}

View File

@ -5,6 +5,8 @@ using System;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
@ -29,5 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects
// spinning doesn't match 1:1 with stable, so let's fudge them easier for the time being.
SpinsRequired = (int)Math.Max(1, SpinsRequired * 0.6);
}
public override Judgement CreateJudgement() => new OsuJudgement();
}
}

View File

@ -2,13 +2,11 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
@ -26,28 +24,11 @@ namespace osu.Game.Rulesets.Osu.Scoring
private readonly Dictionary<HitResult, int> scoreResultCounts = new Dictionary<HitResult, int>();
private readonly Dictionary<ComboResult, int> comboResultCounts = new Dictionary<ComboResult, int>();
protected override void SimulateAutoplay(Beatmap<OsuHitObject> beatmap)
protected override void ApplyBeatmap(Beatmap<OsuHitObject> beatmap)
{
base.ApplyBeatmap(beatmap);
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
foreach (var obj in beatmap.HitObjects)
{
if (obj is Slider slider)
{
// Head
AddJudgement(new OsuJudgement { Result = HitResult.Great });
// Ticks
foreach (var unused in slider.NestedHitObjects.OfType<SliderTick>())
AddJudgement(new OsuJudgement { Result = HitResult.Great });
//Repeats
foreach (var unused in slider.NestedHitObjects.OfType<RepeatPoint>())
AddJudgement(new OsuJudgement { Result = HitResult.Great });
}
AddJudgement(new OsuJudgement { Result = HitResult.Great });
}
}
protected override void Reset(bool storeResults)
@ -70,19 +51,19 @@ namespace osu.Game.Rulesets.Osu.Scoring
private const double harshness = 0.01;
protected override void OnNewJudgement(Judgement judgement)
protected override void ApplyResult(JudgementResult result)
{
base.OnNewJudgement(judgement);
base.ApplyResult(result);
var osuJudgement = (OsuJudgement)judgement;
var osuResult = (OsuJudgementResult)result;
if (judgement.Result != HitResult.None)
if (result.Type != HitResult.None)
{
scoreResultCounts[judgement.Result] = scoreResultCounts.GetOrDefault(judgement.Result) + 1;
comboResultCounts[osuJudgement.Combo] = comboResultCounts.GetOrDefault(osuJudgement.Combo) + 1;
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1;
comboResultCounts[osuResult.ComboType] = comboResultCounts.GetOrDefault(osuResult.ComboType) + 1;
}
switch (judgement.Result)
switch (result.Type)
{
case HitResult.Great:
Health.Value += (10.2 - hpDrainRate) * harshness;
@ -105,5 +86,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
break;
}
}
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement);
}
}

View File

@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.UI
public override void Add(DrawableHitObject h)
{
h.OnJudgement += onJudgement;
h.OnNewResult += onNewResult;
var c = h as IDrawableHitObjectWithProxiedApproach;
if (c != null)
@ -64,12 +64,12 @@ namespace osu.Game.Rulesets.Osu.UI
connectionLayer.HitObjects = HitObjects.Objects.Select(d => d.HitObject).OfType<OsuHitObject>();
}
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
if (!judgedObject.DisplayJudgement || !DisplayJudgements)
if (!judgedObject.DisplayResult || !DisplayJudgements)
return;
DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgement, judgedObject)
DrawableOsuJudgement explosion = new DrawableOsuJudgement(result, judgedObject)
{
Origin = Anchor.Centre,
Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition

View File

@ -8,9 +8,9 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
@ -39,8 +39,10 @@ namespace osu.Game.Rulesets.Taiko.Tests
[BackgroundDependencyLoader]
private void load()
{
AddStep("Hit!", () => addHitJudgement(false));
AddStep("Hit", () => addHitJudgement(false));
AddStep("Strong hit", () => addStrongHitJudgement(false));
AddStep("Kiai hit", () => addHitJudgement(true));
AddStep("Strong kiai hit", () => addStrongHitJudgement(true));
AddStep("Miss :(", addMissJudgement);
AddStep("DrumRoll", () => addDrumRoll(false));
AddStep("Strong DrumRoll", () => addDrumRoll(true));
@ -78,15 +80,12 @@ namespace osu.Game.Rulesets.Taiko.Tests
ControlPointInfo = controlPointInfo
});
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
Add(playfieldContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = 768,
Clock = new FramedClock(rateAdjustClock),
Children = new[] { rulesetContainer = new TaikoRulesetContainer(new TaikoRuleset(), beatmap) }
});
}
@ -133,28 +132,35 @@ namespace osu.Game.Rulesets.Taiko.Tests
HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Good : HitResult.Great;
var cpi = new ControlPointInfo();
cpi.EffectPoints.Add(new EffectControlPoint
{
KiaiMode = kiai
});
cpi.EffectPoints.Add(new EffectControlPoint { KiaiMode = kiai });
Hit hit = new Hit();
hit.ApplyDefaults(cpi, new BeatmapDifficulty());
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoJudgement { Result = hitResult });
((TaikoPlayfield)rulesetContainer.Playfield).OnNewResult(h, new JudgementResult(new TaikoJudgement()) { Type = hitResult });
}
if (RNG.Next(10) == 0)
{
((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoJudgement { Result = hitResult });
((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoStrongHitJudgement());
}
private void addStrongHitJudgement(bool kiai)
{
HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Good : HitResult.Great;
var cpi = new ControlPointInfo();
cpi.EffectPoints.Add(new EffectControlPoint { KiaiMode = kiai });
Hit hit = new Hit();
hit.ApplyDefaults(cpi, new BeatmapDifficulty());
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
((TaikoPlayfield)rulesetContainer.Playfield).OnNewResult(h, new JudgementResult(new TaikoJudgement()) { Type = hitResult });
((TaikoPlayfield)rulesetContainer.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new TaikoStrongJudgement()) { Type = HitResult.Great });
}
private void addMissJudgement()
{
((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(new DrawableTestHit(new Hit()), new TaikoJudgement { Result = HitResult.Miss });
((TaikoPlayfield)rulesetContainer.Playfield).OnNewResult(new DrawableTestHit(new Hit()), new JudgementResult(new TaikoJudgement()) { Type = HitResult.Miss });
}
private void addBarLine(bool major, double delay = scroll_time)
@ -204,10 +210,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
if (strong)
rulesetContainer.Playfield.Add(new DrawableCentreHitStrong(h));
else
rulesetContainer.Playfield.Add(new DrawableCentreHit(h));
rulesetContainer.Playfield.Add(new DrawableCentreHit(h));
}
private void addRimHit(bool strong)
@ -220,10 +223,17 @@ namespace osu.Game.Rulesets.Taiko.Tests
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
if (strong)
rulesetContainer.Playfield.Add(new DrawableRimHitStrong(h));
else
rulesetContainer.Playfield.Add(new DrawableRimHit(h));
rulesetContainer.Playfield.Add(new DrawableRimHit(h));
}
private class TestStrongNestedHit : DrawableStrongNestedHit
{
public TestStrongNestedHit(DrawableHitObject mainObject)
: base(null, mainObject)
{
}
public override bool OnPressed(TaikoAction action) => false;
}
private class DrawableTestHit : DrawableHitObject<TaikoHitObject>

View File

@ -7,15 +7,10 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{
public class TaikoIntermediateSwellJudgement : TaikoJudgement
{
public override HitResult MaxResult => HitResult.Perfect;
public override HitResult MaxResult => HitResult.Great;
public override bool AffectsCombo => false;
public TaikoIntermediateSwellJudgement()
{
Final = false;
}
/// <summary>
/// Computes the numeric result value for the combo portion of the score.
/// </summary>

View File

@ -3,13 +3,8 @@
namespace osu.Game.Rulesets.Taiko.Judgements
{
public class TaikoStrongHitJudgement : TaikoJudgement
public class TaikoStrongJudgement : TaikoJudgement
{
public override bool AffectsCombo => false;
public TaikoStrongHitJudgement()
{
Final = true;
}
}
}

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableCentreHit : DrawableHit
{
protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
public override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
public DrawableCentreHit(Hit hit)
: base(hit)

View File

@ -1,26 +0,0 @@
// 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.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableCentreHitStrong : DrawableHitStrong
{
protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
public DrawableCentreHitStrong(Hit hit)
: base(hit)
{
MainPiece.Add(new CentreHitSymbolPiece());
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
MainPiece.AccentColour = colours.PinkDarker;
}
}
}

View File

@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
@ -40,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
foreach (var tick in drumRoll.NestedHitObjects.OfType<DrumRollTick>())
{
var newTick = new DrawableDrumRollTick(tick);
newTick.OnJudgement += onTickJudgement;
newTick.OnNewResult += onNewTickResult;
AddNested(newTick);
tickContainer.Add(newTick);
@ -61,9 +60,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
colourEngaged = colours.YellowDarker;
}
private void onTickJudgement(DrawableHitObject obj, Judgement judgement)
private void onNewTickResult(DrawableHitObject obj, JudgementResult result)
{
if (judgement.Result > HitResult.Miss)
if (result.Type > HitResult.Miss)
rollingHits++;
else
rollingHits--;
@ -74,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
MainPiece.FadeAccent(newColour, 100);
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (userTriggered)
return;
@ -84,13 +83,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
int countHit = NestedHitObjects.Count(o => o.IsHit);
if (countHit >= HitObject.RequiredGoodHits)
{
AddJudgement(new TaikoJudgement { Result = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good });
if (HitObject.IsStrong)
AddJudgement(new TaikoStrongHitJudgement());
}
ApplyResult(r => r.Type = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good);
else
AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
ApplyResult(r => r.Type = HitResult.Miss);
}
protected override void UpdateState(ArmedState state)
@ -103,5 +98,25 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
break;
}
}
protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this);
private class StrongNestedHit : DrawableStrongNestedHit
{
public StrongNestedHit(StrongHitObject strong, DrawableDrumRoll drumRoll)
: base(strong, drumRoll)
{
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!MainObject.Judged)
return;
ApplyResult(r => r.Type = MainObject.IsHit ? HitResult.Great : HitResult.Miss);
}
public override bool OnPressed(TaikoAction action) => false;
}
}
}

View File

@ -5,7 +5,6 @@ using System;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
@ -18,24 +17,26 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
FillMode = FillMode.Fit;
}
public override bool DisplayJudgement => false;
public override bool DisplayResult => false;
protected override TaikoPiece CreateMainPiece() => new TickPiece
{
Filled = HitObject.FirstTick
};
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
if (timeOffset > HitObject.HitWindow)
ApplyResult(r => r.Type = HitResult.Miss);
return;
}
if (Math.Abs(timeOffset) > HitObject.HitWindow)
return;
if (!(Math.Abs(timeOffset) < HitObject.HitWindow))
return;
AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great });
if (HitObject.IsStrong)
AddJudgement(new TaikoStrongHitJudgement());
ApplyResult(r => r.Type = HitResult.Great);
}
protected override void UpdateState(ArmedState state)
@ -48,6 +49,26 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
}
}
public override bool OnPressed(TaikoAction action) => UpdateJudgement(true);
public override bool OnPressed(TaikoAction action) => UpdateResult(true);
protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this);
private class StrongNestedHit : DrawableStrongNestedHit
{
public StrongNestedHit(StrongHitObject strong, DrawableDrumRollTick tick)
: base(strong, tick)
{
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!MainObject.Judged)
return;
ApplyResult(r => r.Type = MainObject.IsHit ? HitResult.Great : HitResult.Miss);
}
public override bool OnPressed(TaikoAction action) => false;
}
}
}

View File

@ -1,11 +1,11 @@
// 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 System.Linq;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
@ -15,17 +15,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
/// <summary>
/// A list of keys which can result in hits for this HitObject.
/// </summary>
protected abstract TaikoAction[] HitActions { get; }
public abstract TaikoAction[] HitActions { get; }
/// <summary>
/// Whether a second hit is allowed to be processed. This occurs once this hit object has been hit successfully.
/// The action that caused this <see cref="DrawableHit"/> to be hit.
/// </summary>
protected bool SecondHitAllowed { get; private set; }
public TaikoAction? HitAction { get; private set; }
/// <summary>
/// Whether the last key pressed is a valid hit key.
/// </summary>
private bool validKeyPressed;
private bool validActionPressed;
protected DrawableHit(Hit hit)
: base(hit)
@ -33,12 +30,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
FillMode = FillMode.Fit;
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
ApplyResult(r => r.Type = HitResult.Miss);
return;
}
@ -46,26 +43,33 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (result == HitResult.None)
return;
if (!validKeyPressed || result == HitResult.Miss)
AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
if (!validActionPressed)
ApplyResult(r => r.Type = HitResult.Miss);
else
{
AddJudgement(new TaikoJudgement
{
Result = result,
Final = !HitObject.IsStrong
});
SecondHitAllowed = true;
}
ApplyResult(r => r.Type = result);
}
public override bool OnPressed(TaikoAction action)
{
validKeyPressed = HitActions.Contains(action);
if (Judged)
return false;
validActionPressed = HitActions.Contains(action);
// Only count this as handled if the new judgement is a hit
return UpdateJudgement(true);
var result = UpdateResult(true);
if (IsHit)
HitAction = action;
return result;
}
public override bool OnReleased(TaikoAction action)
{
if (action == HitAction)
HitAction = null;
return base.OnReleased(action);
}
protected override void Update()
@ -86,8 +90,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
switch (State.Value)
{
case ArmedState.Idle:
SecondHitAllowed = false;
validKeyPressed = false;
validActionPressed = false;
UnproxyContent();
this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire();
@ -123,5 +126,65 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
}
}
}
protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this);
private class StrongNestedHit : DrawableStrongNestedHit
{
/// <summary>
/// The lenience for the second key press.
/// This does not adjust by map difficulty in ScoreV2 yet.
/// </summary>
private const double second_hit_window = 30;
public new DrawableHit MainObject => (DrawableHit)base.MainObject;
public StrongNestedHit(StrongHitObject strong, DrawableHit hit)
: base(strong, hit)
{
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!MainObject.Result.HasResult)
{
base.CheckForResult(userTriggered, timeOffset);
return;
}
if (!MainObject.Result.IsHit)
{
ApplyResult(r => r.Type = HitResult.Miss);
return;
}
if (!userTriggered)
{
if (timeOffset > second_hit_window)
ApplyResult(r => r.Type = HitResult.Miss);
return;
}
if (Math.Abs(MainObject.Result.TimeOffset - timeOffset) < second_hit_window)
ApplyResult(r => r.Type = HitResult.Great);
}
public override bool OnPressed(TaikoAction action)
{
// Don't process actions until the main hitobject is hit
if (!MainObject.IsHit)
return false;
// Don't process actions if the pressed button was released
if (MainObject.HitAction == null)
return false;
// Don't handle invalid hit action presses
if (!MainObject.HitActions.Contains(action))
return false;
return UpdateResult(true);
}
}
}
}

View File

@ -1,103 +0,0 @@
// 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 System.Linq;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public abstract class DrawableHitStrong : DrawableHit
{
/// <summary>
/// The lenience for the second key press.
/// This does not adjust by map difficulty in ScoreV2 yet.
/// </summary>
private const double second_hit_window = 30;
private double firstHitTime;
private bool firstKeyHeld;
private TaikoAction firstHitAction;
protected DrawableHitStrong(Hit hit)
: base(hit)
{
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!SecondHitAllowed)
{
base.CheckForJudgements(userTriggered, timeOffset);
return;
}
if (!userTriggered)
{
if (timeOffset > second_hit_window)
AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.None });
return;
}
// If we get here, we're assured that the key pressed is the correct secondary key
if (Math.Abs(firstHitTime - Time.Current) < second_hit_window)
AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Great });
}
protected override void UpdateState(ArmedState state)
{
base.UpdateState(state);
switch (state)
{
case ArmedState.Idle:
firstHitTime = 0;
firstKeyHeld = false;
break;
}
}
public override bool OnReleased(TaikoAction action)
{
if (action == firstHitAction)
firstKeyHeld = false;
return base.OnReleased(action);
}
public override bool OnPressed(TaikoAction action)
{
if (AllJudged)
return false;
// Check if we've handled the first key
if (!SecondHitAllowed)
{
// First key hasn't been handled yet, attempt to handle it
bool handled = base.OnPressed(action);
if (handled)
{
firstHitTime = Time.Current;
firstHitAction = action;
firstKeyHeld = true;
}
return handled;
}
// Don't handle represses of the first key
if (firstHitAction == action)
return false;
// Don't handle invalid hit action presses
if (!HitActions.Contains(action))
return false;
// Assume the intention was to hit the strong hit with both keys only if the first key is still being held down
return firstKeyHeld && UpdateJudgement(true);
}
}
}

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableRimHit : DrawableHit
{
protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim };
public override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim };
public DrawableRimHit(Hit hit)
: base(hit)

View File

@ -1,26 +0,0 @@
// 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.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableRimHitStrong : DrawableHitStrong
{
protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim };
public DrawableRimHitStrong(Hit hit)
: base(hit)
{
MainPiece.Add(new RimHitSymbolPiece());
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
MainPiece.AccentColour = colours.BlueDarker;
}
}
}

View File

@ -0,0 +1,26 @@
// 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.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
/// <summary>
/// Used as a nested hitobject to provide <see cref="TaikoStrongJudgement"/>s for <see cref="DrawableTaikoHitObject"/>s.
/// </summary>
public abstract class DrawableStrongNestedHit : DrawableTaikoHitObject
{
public readonly DrawableHitObject MainObject;
protected DrawableStrongNestedHit(StrongHitObject strong, DrawableHitObject mainObject)
: base(strong)
{
MainObject = mainObject;
}
protected override void UpdateState(ArmedState state)
{
}
}
}

View File

@ -2,6 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@ -12,23 +14,19 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableSwell : DrawableTaikoHitObject<Swell>
{
/// <summary>
/// A judgement is only displayed when the user has complete the swell (either a hit or miss).
/// </summary>
public override bool DisplayJudgement => AllJudged;
private const float target_ring_thick_border = 1.4f;
private const float target_ring_thin_border = 1f;
private const float target_ring_scale = 5f;
private const float inner_ring_alpha = 0.65f;
private readonly List<DrawableSwellTick> ticks = new List<DrawableSwellTick>();
private readonly Container bodyContainer;
private readonly CircularContainer targetRing;
private readonly CircularContainer expandingRing;
@ -106,6 +104,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
});
MainPiece.Add(symbol = new SwellSymbolPiece());
foreach (var tick in HitObject.NestedHitObjects.OfType<SwellTick>())
{
var vis = new DrawableSwellTick(tick);
ticks.Add(vis);
AddInternal(vis);
AddNested(vis);
}
}
[BackgroundDependencyLoader]
@ -124,13 +131,17 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Width *= Parent.RelativeChildSize.X;
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (userTriggered)
{
AddJudgement(new TaikoIntermediateSwellJudgement());
var nextTick = ticks.FirstOrDefault(j => !j.IsHit);
var completion = (float)Judgements.Count / HitObject.RequiredHits;
nextTick?.TriggerResult(HitResult.Great);
var numHits = ticks.Count(r => r.IsHit);
var completion = (float)numHits / HitObject.RequiredHits;
expandingRing
.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50)
@ -141,18 +152,30 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint);
if (Judgements.Count == HitObject.RequiredHits)
AddJudgement(new TaikoJudgement { Result = HitResult.Great });
if (numHits == HitObject.RequiredHits)
ApplyResult(r => r.Type = HitResult.Great);
}
else
{
if (timeOffset < 0)
return;
//TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP
AddJudgement(Judgements.Count > HitObject.RequiredHits / 2
? new TaikoJudgement { Result = HitResult.Good }
: new TaikoJudgement { Result = HitResult.Miss });
int numHits = 0;
foreach (var tick in ticks)
{
if (tick.IsHit)
{
numHits++;
continue;
}
tick.TriggerResult(HitResult.Miss);
}
var hitResult = numHits > HitObject.RequiredHits / 2 ? HitResult.Good : HitResult.Miss;
ApplyResult(r => r.Type = hitResult);
}
}
@ -208,7 +231,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
return false;
lastWasCentre = isCentre;
UpdateJudgement(true);
UpdateResult(true);
return true;
}

View File

@ -0,0 +1,30 @@
// 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.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableSwellTick : DrawableTaikoHitObject
{
public override bool DisplayResult => false;
public DrawableSwellTick(TaikoHitObject hitObject)
: base(hitObject)
{
}
public void TriggerResult(HitResult type) => ApplyResult(r => r.Type = type);
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
}
protected override void UpdateState(ArmedState state)
{
}
public override bool OnPressed(TaikoAction action) => false;
}
}

View File

@ -101,6 +101,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Content.Add(MainPiece = CreateMainPiece());
MainPiece.KiaiMode = HitObject.Kiai;
var strongObject = HitObject.NestedHitObjects.OfType<StrongHitObject>().FirstOrDefault();
if (strongObject != null)
{
var strongHit = CreateStrongHit(strongObject);
AddNested(strongHit);
AddInternal(strongHit);
}
}
// Normal and clap samples are handled by the drum
@ -109,5 +118,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override string SampleNamespace => "Taiko";
protected virtual TaikoPiece CreateMainPiece() => new CirclePiece();
/// <summary>
/// Creates the handler for this <see cref="DrawableHitObject"/>'s <see cref="StrongHitObject"/>.
/// This is only invoked if <see cref="TaikoHitObject.IsStrong"/> is true for <see cref="HitObject"/>.
/// </summary>
/// <param name="hitObject">The strong hitobject.</param>
/// <returns>The strong hitobject handler.</returns>
protected virtual DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => null;
}
}

View File

@ -54,12 +54,12 @@ namespace osu.Game.Rulesets.Taiko.Objects
protected override void CreateNestedHitObjects()
{
base.CreateNestedHitObjects();
createTicks();
RequiredGoodHits = NestedHitObjects.Count * Math.Min(0.15, 0.05 + 0.10 / 6 * overallDifficulty);
RequiredGreatHits = NestedHitObjects.Count * Math.Min(0.30, 0.10 + 0.20 / 6 * overallDifficulty);
base.CreateNestedHitObjects();
}
private void createTicks()

View File

@ -1,6 +1,9 @@
// 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.Rulesets.Judgements;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects
{
public class DrumRollTick : TaikoHitObject
@ -20,5 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// The time allowed to hit this tick.
/// </summary>
public double HitWindow => TickSpacing / 2;
public override Judgement CreateJudgement() => new TaikoDrumRollTickJudgement();
}
}

View File

@ -0,0 +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.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects
{
public class StrongHitObject : TaikoHitObject
{
public override Judgement CreateJudgement() => new TaikoStrongJudgement();
}
}

View File

@ -15,5 +15,13 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// The number of hits required to complete the swell successfully.
/// </summary>
public int RequiredHits = 10;
protected override void CreateNestedHitObjects()
{
base.CreateNestedHitObjects();
for (int i = 0; i < RequiredHits; i++)
AddNested(new SwellTick());
}
}
}

View File

@ -0,0 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Rulesets.Taiko.Objects
{
public class SwellTick : TaikoHitObject
{
}
}

View File

@ -1,7 +1,10 @@
// 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.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects
{
@ -28,6 +31,16 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// </summary>
public bool IsStrong;
protected override void CreateNestedHitObjects()
{
base.CreateNestedHitObjects();
if (IsStrong)
AddNested(new StrongHitObject { StartTime = (this as IHasEndTime)?.EndTime ?? StartTime });
}
public override Judgement CreateJudgement() => new TaikoJudgement();
protected override HitWindows CreateHitWindows() => new TaikoHitWindows();
}
}

View File

@ -1,7 +1,6 @@
// 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.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
@ -60,63 +59,31 @@ namespace osu.Game.Rulesets.Taiko.Scoring
private double hpIncreaseGood;
private double hpIncreaseMiss;
public TaikoScoreProcessor()
{
}
public TaikoScoreProcessor(RulesetContainer<TaikoHitObject> rulesetContainer)
: base(rulesetContainer)
{
}
protected override void SimulateAutoplay(Beatmap<TaikoHitObject> beatmap)
protected override void ApplyBeatmap(Beatmap<TaikoHitObject> beatmap)
{
base.ApplyBeatmap(beatmap);
double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
hpIncreaseTick = hp_hit_tick;
hpIncreaseGreat = hpMultiplierNormal * hp_hit_great;
hpIncreaseGood = hpMultiplierNormal * hp_hit_good;
hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max);
foreach (var obj in beatmap.HitObjects)
{
switch (obj)
{
case Hit _:
AddJudgement(new TaikoJudgement { Result = HitResult.Great });
if (obj.IsStrong)
AddJudgement(new TaikoStrongHitJudgement());
break;
case DrumRoll drumRoll:
var count = drumRoll.NestedHitObjects.OfType<DrumRollTick>().Count();
for (int i = 0; i < count; i++)
{
AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great });
if (obj.IsStrong)
AddJudgement(new TaikoStrongHitJudgement());
}
AddJudgement(new TaikoJudgement { Result = HitResult.Great });
if (obj.IsStrong)
AddJudgement(new TaikoStrongHitJudgement());
break;
case Swell _:
AddJudgement(new TaikoJudgement { Result = HitResult.Great });
break;
}
}
}
protected override void OnNewJudgement(Judgement judgement)
protected override void ApplyResult(JudgementResult result)
{
base.OnNewJudgement(judgement);
base.ApplyResult(result);
bool isTick = judgement is TaikoDrumRollTickJudgement;
bool isTick = result.Judgement is TaikoDrumRollTickJudgement;
// Apply HP changes
switch (judgement.Result)
switch (result.Type)
{
case HitResult.Miss:
// Missing ticks shouldn't drop HP

View File

@ -19,16 +19,16 @@ namespace osu.Game.Rulesets.Taiko.UI
/// 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(Judgement judgement, DrawableHitObject judgedObject)
: base(judgement, judgedObject)
/// <param name="result">The judgement to visualise.</param>
public DrawableTaikoJudgement(JudgementResult result, DrawableHitObject judgedObject)
: base(result, judgedObject)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
switch (Judgement.Result)
switch (Result.Type)
{
case HitResult.Good:
Colour = colours.GreenLight;
@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Taiko.UI
protected override void LoadComplete()
{
if (Judgement.IsHit)
if (Result.IsHit)
this.MoveToY(-100, 500);
base.LoadComplete();

View File

@ -209,7 +209,7 @@ namespace osu.Game.Rulesets.Taiko.UI
public override void Add(DrawableHitObject h)
{
h.OnJudgement += OnJudgement;
h.OnNewResult += OnNewResult;
base.Add(h);
@ -224,35 +224,40 @@ namespace osu.Game.Rulesets.Taiko.UI
}
}
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
if (!DisplayJudgements)
return;
if (judgedObject.DisplayJudgement && judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null)
{
judgementContainer.Add(new DrawableTaikoJudgement(judgement, judgedObject)
{
Anchor = judgement.IsHit ? Anchor.TopLeft : Anchor.CentreLeft,
Origin = judgement.IsHit ? Anchor.BottomCentre : Anchor.Centre,
RelativePositionAxes = Axes.X,
X = judgement.IsHit ? judgedObject.Position.X : 0,
});
}
if (!judgement.IsHit)
if (!judgedObject.DisplayResult)
return;
bool isRim = judgedObject.HitObject is RimHit;
if (judgement is TaikoStrongHitJudgement)
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == judgedObject)?.VisualiseSecondHit();
else
switch (result.Judgement)
{
hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
case TaikoStrongJudgement _:
if (result.IsHit)
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).MainObject)?.VisualiseSecondHit();
break;
default:
judgementContainer.Add(new DrawableTaikoJudgement(result, judgedObject)
{
Anchor = result.IsHit ? Anchor.TopLeft : Anchor.CentreLeft,
Origin = result.IsHit ? Anchor.BottomCentre : Anchor.Centre,
RelativePositionAxes = Axes.X,
X = result.IsHit ? judgedObject.Position.X : 0,
});
if (judgedObject.HitObject.Kiai)
kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim));
if (!result.IsHit)
break;
bool isRim = judgedObject.HitObject is RimHit;
hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
if (judgedObject.HitObject.Kiai)
kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim));
break;
}
}
}

View File

@ -100,12 +100,8 @@ namespace osu.Game.Rulesets.Taiko.UI
{
switch (h)
{
case CentreHit centreHit when h.IsStrong:
return new DrawableCentreHitStrong(centreHit);
case CentreHit centreHit:
return new DrawableCentreHit(centreHit);
case RimHit rimHit when h.IsStrong:
return new DrawableRimHitStrong(rimHit);
case RimHit rimHit:
return new DrawableRimHit(rimHit);
case DrumRoll drumRoll:

View File

@ -86,5 +86,19 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(78993, animation.StartTime);
}
}
[Test]
public void TestDecodeVariableWithSuffix()
{
var decoder = new LegacyStoryboardDecoder();
using (var resStream = Resource.OpenResource("variable-with-suffix.osb"))
using (var stream = new StreamReader(resStream))
{
var storyboard = decoder.Decode(stream);
StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
Assert.AreEqual(123456, ((StoryboardSprite)background.Elements.Single()).InitialPosition.X);
}
}
}
}

View File

@ -0,0 +1,5 @@
[Variables]
$var=1234
[Events]
Sprite,Background,TopCentre,"img.jpg",$var56,240

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.ComponentModel;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Overlays.Mods;
@ -13,11 +12,11 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using System.Linq;
using System.Collections.Generic;
using osu.Game.Rulesets.Osu;
using NUnit.Framework;
using osu.Framework.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Mods.Sections;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.UI;
using OpenTK.Graphics;
@ -50,11 +49,6 @@ namespace osu.Game.Tests.Visual
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}
protected override void LoadComplete()
{
base.LoadComplete();
Add(modSelect = new TestModSelectOverlay
{
@ -71,34 +65,25 @@ namespace osu.Game.Tests.Visual
Position = new Vector2(0, 25),
});
modDisplay.Current.UnbindBindings();
modDisplay.Current.BindTo(modSelect.SelectedMods);
AddStep("Toggle", modSelect.ToggleVisibility);
AddStep("Hide", modSelect.Hide);
AddStep("Show", modSelect.Show);
foreach (var rulesetInfo in rulesets.AvailableRulesets)
{
Ruleset ruleset = rulesetInfo.CreateInstance();
AddStep($"switch to {ruleset.Description}", () => Ruleset.Value = rulesetInfo);
switch (ruleset)
{
case OsuRuleset or:
testOsuMods(or);
break;
case ManiaRuleset mr:
testManiaMods(mr);
break;
}
}
AddStep("Toggle", modSelect.ToggleVisibility);
AddStep("Toggle", modSelect.ToggleVisibility);
}
private void testOsuMods(OsuRuleset ruleset)
[Test]
public void TestOsuMods()
{
var easierMods = ruleset.GetModsFor(ModType.DifficultyReduction);
var harderMods = ruleset.GetModsFor(ModType.DifficultyIncrease);
var assistMods = ruleset.GetModsFor(ModType.Automation);
var ruleset = rulesets.AvailableRulesets.First(r => r.ID == 0);
AddStep("change ruleset", () => { Ruleset.Value = ruleset; });
var instance = ruleset.CreateInstance();
var easierMods = instance.GetModsFor(ModType.DifficultyReduction);
var harderMods = instance.GetModsFor(ModType.DifficultyIncrease);
var assistMods = instance.GetModsFor(ModType.Automation);
var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail);
var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden);
@ -120,9 +105,40 @@ namespace osu.Game.Tests.Visual
testUnimplementedMod(autoPilotMod);
}
private void testManiaMods(ManiaRuleset ruleset)
[Test]
public void TestManiaMods()
{
testRankedText(ruleset.GetModsFor(ModType.Conversion).First(m => m is ManiaModRandom));
var ruleset = rulesets.AvailableRulesets.First(r => r.ID == 3);
AddStep("change ruleset", () => { Ruleset.Value = ruleset; });
testRankedText(ruleset.CreateInstance().GetModsFor(ModType.Conversion).First(m => m is ManiaModRandom));
}
[Test]
public void TestRulesetChanges()
{
var rulesetOsu = rulesets.AvailableRulesets.First(r => r.ID == 0);
var rulesetMania = rulesets.AvailableRulesets.First(r => r.ID == 3);
AddStep("change ruleset to null", () => { Ruleset.Value = null; });
var instance = rulesetOsu.CreateInstance();
var easierMods = instance.GetModsFor(ModType.DifficultyReduction);
var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail);
AddStep("set mods externally", () => { modDisplay.Current.Value = new[] { noFailMod }; });
AddStep("change ruleset to osu", () => { Ruleset.Value = rulesetOsu; });
AddAssert("ensure mods still selected", () => modDisplay.Current.Value.Single(m => m is OsuModNoFail) != null);
AddStep("change ruleset to mania", () => { Ruleset.Value = rulesetMania; });
AddAssert("ensure mods not selected", () => !modDisplay.Current.Value.Any(m => m is OsuModNoFail));
AddStep("change ruleset to osu", () => { Ruleset.Value = rulesetOsu; });
AddAssert("ensure mods not selected", () => !modDisplay.Current.Value.Any());
}
private void testSingleMod(Mod mod)
@ -237,6 +253,8 @@ namespace osu.Game.Tests.Visual
private class TestModSelectOverlay : ModSelectOverlay
{
public new Bindable<IEnumerable<Mod>> SelectedMods => base.SelectedMods;
public ModButton GetModButton(Mod mod)
{
var section = ModSectionsContainer.Children.Single(s => s.ModType == mod.Type);

View File

@ -8,11 +8,15 @@ using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
@ -29,6 +33,10 @@ namespace osu.Game.Tests.Visual
private WorkingBeatmap defaultBeatmap;
private DatabaseContextFactory factory;
[Cached]
[Cached(Type = typeof(IBindable<IEnumerable<Mod>>))]
private readonly Bindable<IEnumerable<Mod>> selectedMods = new Bindable<IEnumerable<Mod>>(new Mod[] { });
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(SongSelect),
@ -49,6 +57,8 @@ namespace osu.Game.Tests.Visual
private class TestSongSelect : PlaySongSelect
{
public new Bindable<RulesetInfo> Ruleset => base.Ruleset;
public WorkingBeatmap CurrentBeatmap => Beatmap.Value;
public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap;
public new BeatmapCarousel Carousel => base.Carousel;
@ -121,7 +131,7 @@ namespace osu.Game.Tests.Visual
[Test]
[Ignore("needs fixing")]
public void ImportUnderDifferentRuleset()
public void TestImportUnderDifferentRuleset()
{
changeRuleset(2);
importForRuleset(0);
@ -129,7 +139,7 @@ namespace osu.Game.Tests.Visual
}
[Test]
public void ImportUnderCurrentRuleset()
public void TestImportUnderCurrentRuleset()
{
changeRuleset(2);
importForRuleset(2);
@ -143,11 +153,42 @@ namespace osu.Game.Tests.Visual
AddUntilStep(() => songSelect.Carousel.SelectedBeatmap == null, "no selection");
}
[Test]
public void TestRulesetChangeResetsMods()
{
changeRuleset(0);
changeMods(new OsuModHardRock());
int actionIndex = 0;
int modChangeIndex = 0;
int rulesetChangeIndex = 0;
AddStep("change ruleset", () =>
{
songSelect.CurrentBeatmap.Mods.ValueChanged += onModChange;
songSelect.Ruleset.ValueChanged += onRulesetChange;
Ruleset.Value = new TaikoRuleset().RulesetInfo;
songSelect.CurrentBeatmap.Mods.ValueChanged -= onModChange;
songSelect.Ruleset.ValueChanged -= onRulesetChange;
});
AddAssert("mods changed before ruleset", () => modChangeIndex < rulesetChangeIndex);
AddAssert("empty mods", () => !selectedMods.Value.Any());
void onModChange(IEnumerable<Mod> mods) => modChangeIndex = actionIndex++;
void onRulesetChange(RulesetInfo ruleset) => rulesetChangeIndex = actionIndex--;
}
private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())));
private static int importId;
private int getImportId() => ++importId;
private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.ShortenedName))}", () => selectedMods.Value = mods);
private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id));
private void addManyTestMaps()

View File

@ -1,25 +1,61 @@
// 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.Threading;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{
public class TestCasePlayerLoader : OsuTestCase
public class TestCasePlayerLoader : ManualInputManagerTestCase
{
private PlayerLoader loader;
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
Beatmap.Value = new DummyWorkingBeatmap(game);
AddStep("load dummy beatmap", () => Add(new PlayerLoader(new Player
AddStep("load dummy beatmap", () => Add(loader = new PlayerLoader(new Player
{
AllowPause = false,
AllowLeadIn = false,
AllowResults = false,
})));
AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre));
AddUntilStep(() => !loader.IsCurrentScreen, "wait for no longer current");
AddStep("load slow dummy beatmap", () =>
{
SlowLoadPlayer slow;
Add(loader = new PlayerLoader(slow = new SlowLoadPlayer
{
AllowPause = false,
AllowLeadIn = false,
AllowResults = false,
}));
Scheduler.AddDelayed(() => slow.Ready = true, 5000);
});
AddUntilStep(() => !loader.IsCurrentScreen, "wait for no longer current");
}
protected class SlowLoadPlayer : Player
{
public bool Ready;
[BackgroundDependencyLoader]
private void load()
{
while (!Ready)
Thread.Sleep(1);
}
}
}
}

View File

@ -354,6 +354,11 @@ namespace osu.Game.Beatmaps.Formats
private void handleTimingControlPoint(TimingControlPoint newPoint)
{
var existing = beatmap.ControlPointInfo.TimingPointAt(newPoint.Time);
if (existing.Time == newPoint.Time)
beatmap.ControlPointInfo.TimingPoints.Remove(existing);
beatmap.ControlPointInfo.TimingPoints.Add(newPoint);
}
@ -364,7 +369,9 @@ namespace osu.Game.Beatmaps.Formats
if (newPoint.EquivalentTo(existing))
return;
beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == newPoint.Time);
if (existing.Time == newPoint.Time)
beatmap.ControlPointInfo.DifficultyPoints.Remove(existing);
beatmap.ControlPointInfo.DifficultyPoints.Add(newPoint);
}
@ -375,6 +382,9 @@ namespace osu.Game.Beatmaps.Formats
if (newPoint.EquivalentTo(existing))
return;
if (existing.Time == newPoint.Time)
beatmap.ControlPointInfo.EffectPoints.Remove(existing);
beatmap.ControlPointInfo.EffectPoints.Add(newPoint);
}
@ -385,6 +395,9 @@ namespace osu.Game.Beatmaps.Formats
if (newPoint.EquivalentTo(existing))
return;
if (existing.Time == newPoint.Time)
beatmap.ControlPointInfo.SamplePoints.Remove(existing);
beatmap.ControlPointInfo.SamplePoints.Add(newPoint);
}

View File

@ -289,15 +289,10 @@ namespace osu.Game.Beatmaps.Formats
while (line.IndexOf('$') >= 0)
{
string origLine = line;
string[] split = line.Split(',');
for (int i = 0; i < split.Length; i++)
{
var item = split[i];
if (item.StartsWith("$") && variables.ContainsKey(item))
split[i] = variables[item];
}
line = string.Join(",", split);
foreach (var v in variables)
line = line.Replace(v.Key, v.Value);
if (line == origLine)
break;
}

View File

@ -99,7 +99,9 @@ namespace osu.Game
private readonly List<OverlayContainer> overlays = new List<OverlayContainer>();
// todo: move this to SongSelect once Screen has the ability to unsuspend.
public readonly Bindable<IEnumerable<Mod>> SelectedMods = new Bindable<IEnumerable<Mod>>(new List<Mod>());
[Cached]
[Cached(Type = typeof(IBindable<IEnumerable<Mod>>))]
private readonly Bindable<IEnumerable<Mod>> selectedMods = new Bindable<IEnumerable<Mod>>(new Mod[] { });
public OsuGame(string[] args = null)
{

View File

@ -12,7 +12,6 @@ using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation;
using osu.Framework.Localisation;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.States;
using osu.Game.Beatmaps;
namespace osu.Game.Overlays.Direct
@ -26,12 +25,14 @@ namespace osu.Game.Overlays.Direct
private PlayButton playButton;
private Box progressBar;
private Container downloadContainer;
protected override bool FadePlayButton => false;
protected override PlayButton PlayButton => playButton;
protected override Box PreviewBar => progressBar;
public DirectListPanel(BeatmapSetInfo beatmap) : base(beatmap)
public DirectListPanel(BeatmapSetInfo beatmap)
: base(beatmap)
{
RelativeSizeAxes = Axes.X;
Height = height;
@ -66,30 +67,45 @@ namespace osu.Game.Overlays.Direct
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
playButton = new PlayButton(SetInfo)
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Size = new Vector2(height / 2),
FillMode = FillMode.Fit,
Alpha = 0,
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
new FillFlowContainer
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
TextSize = 18,
Font = @"Exo2.0-BoldItalic",
},
new OsuSpriteText
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
Font = @"Exo2.0-BoldItalic",
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
playButton = new PlayButton(SetInfo)
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Size = new Vector2(height / 2),
FillMode = FillMode.Fit,
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
TextSize = 18,
Font = @"Exo2.0-BoldItalic",
},
new OsuSpriteText
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
Font = @"Exo2.0-BoldItalic",
},
}
},
}
},
new FillFlowContainer
{
@ -108,16 +124,13 @@ namespace osu.Game.Overlays.Direct
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
LayoutEasing = Easing.OutQuint,
LayoutDuration = transition_duration,
Children = new Drawable[]
{
downloadContainer = new Container
new Container
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.Both,
Alpha = 0,
Child = new DownloadButton(SetInfo)
{
Size = new Vector2(height - vertical_padding * 3),
@ -184,17 +197,5 @@ namespace osu.Game.Overlays.Direct
},
});
}
protected override bool OnHover(InputState state)
{
downloadContainer.FadeIn(transition_duration, Easing.InOutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
downloadContainer.FadeOut(transition_duration, Easing.InOutQuint);
base.OnHoverLost(state);
}
}
}

View File

@ -40,6 +40,8 @@ namespace osu.Game.Overlays.Direct
protected abstract PlayButton PlayButton { get; }
protected abstract Box PreviewBar { get; }
protected virtual bool FadePlayButton => true;
protected override Container<Drawable> Content => content;
protected DirectPanel(BeatmapSetInfo setInfo)
@ -125,7 +127,8 @@ namespace osu.Game.Overlays.Direct
{
content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint);
content.MoveToY(-4, hover_transition_time, Easing.OutQuint);
PlayButton.FadeIn(120, Easing.InOutQuint);
if (FadePlayButton)
PlayButton.FadeIn(120, Easing.InOutQuint);
return base.OnHover(state);
}
@ -134,7 +137,7 @@ namespace osu.Game.Overlays.Direct
{
content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
content.MoveToY(0, hover_transition_time, Easing.OutQuint);
if (!PreviewPlaying)
if (FadePlayButton && !PreviewPlaying)
PlayButton.FadeOut(120, Easing.InOutQuint);
base.OnHoverLost(state);

View File

@ -39,9 +39,39 @@ namespace osu.Game.Overlays.Mods
protected readonly FillFlowContainer<ModSection> ModSectionsContainer;
public readonly Bindable<IEnumerable<Mod>> SelectedMods = new Bindable<IEnumerable<Mod>>();
protected readonly Bindable<IEnumerable<Mod>> SelectedMods = new Bindable<IEnumerable<Mod>>(new Mod[] { });
public readonly IBindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
protected readonly IBindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
[BackgroundDependencyLoader(true)]
private void load(OsuColour colours, IBindable<RulesetInfo> ruleset, AudioManager audio, Bindable<IEnumerable<Mod>> selectedMods)
{
LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green;
UnrankedLabel.Colour = colours.Blue;
Ruleset.BindTo(ruleset);
if (selectedMods != null) SelectedMods.BindTo(selectedMods);
sampleOn = audio.Sample.Get(@"UI/check-on");
sampleOff = audio.Sample.Get(@"UI/check-off");
}
protected override void LoadComplete()
{
base.LoadComplete();
Ruleset.BindValueChanged(rulesetChanged, true);
SelectedMods.BindValueChanged(selectedModsChanged, true);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
Ruleset.UnbindAll();
SelectedMods.UnbindAll();
}
private void rulesetChanged(RulesetInfo newRuleset)
{
@ -51,33 +81,16 @@ namespace osu.Game.Overlays.Mods
foreach (ModSection section in ModSectionsContainer.Children)
section.Mods = instance.GetModsFor(section.ModType);
// attempt to re-select any already selected mods.
// this may be the first time we are receiving the ruleset, in which case they will still match.
selectedModsChanged(SelectedMods.Value);
// write the mods back to the SelectedMods bindable in the case a change was not applicable.
// this generally isn't required as the previous line will perform deselection; just here for safety.
refreshSelectedMods();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, IBindable<RulesetInfo> ruleset, AudioManager audio)
{
SelectedMods.ValueChanged += selectedModsChanged;
LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green;
UnrankedLabel.Colour = colours.Blue;
Ruleset.BindTo(ruleset);
Ruleset.BindValueChanged(rulesetChanged, true);
sampleOn = audio.Sample.Get(@"UI/check-on");
sampleOff = audio.Sample.Get(@"UI/check-off");
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
Ruleset.UnbindAll();
SelectedMods.UnbindAll();
}
private void selectedModsChanged(IEnumerable<Mod> obj)
{
foreach (ModSection section in ModSectionsContainer.Children)
@ -176,10 +189,7 @@ namespace osu.Game.Overlays.Mods
refreshSelectedMods();
}
private void refreshSelectedMods()
{
SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
}
private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
public ModSelectOverlay()
{

View File

@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
@ -23,12 +24,15 @@ namespace osu.Game.Rulesets.Edit
{
private readonly Ruleset ruleset;
public IEnumerable<DrawableHitObject> HitObjects => rulesetContainer.Playfield.AllHitObjects;
protected ICompositionTool CurrentTool { get; private set; }
protected IRulesetConfigManager Config { get; private set; }
private readonly List<Container> layerContainers = new List<Container>();
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
private RulesetContainer rulesetContainer;
private readonly List<Container> layerContainers = new List<Container>();
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
protected HitObjectComposer(Ruleset ruleset)
{
@ -60,7 +64,7 @@ namespace osu.Game.Rulesets.Edit
};
var layerAboveRuleset = CreateLayerContainer();
layerAboveRuleset.Child = new HitObjectMaskLayer(rulesetContainer.Playfield, this);
layerAboveRuleset.Child = new HitObjectMaskLayer();
layerContainers.Add(layerBelowRuleset);
layerContainers.Add(layerAboveRuleset);
@ -110,6 +114,16 @@ namespace osu.Game.Rulesets.Edit
toolboxCollection.Items[0].Select();
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs(this);
Config = dependencies.Get<RulesetConfigCache>().GetConfigFor(ruleset);
return dependencies;
}
protected override void LoadComplete()
{
base.LoadComplete();

Some files were not shown because too many files have changed in this diff Show More