Merge branch 'master' into hud_visibility

This commit is contained in:
Dean Herbert 2017-04-06 13:40:10 +09:00 committed by GitHub
commit efe82ef88c
46 changed files with 511 additions and 449 deletions

@ -1 +1 @@
Subproject commit e9b388934ed77cbc1af3cdfd213eb754f71554ae Subproject commit a7c99e06ff4c3f56fad24bec170eb93f42b1e149

View File

@ -29,7 +29,6 @@ public override void Reset()
Add(new CirclePiece Add(new CirclePiece
{ {
Position = new Vector2(100, 100), Position = new Vector2(100, 100),
Width = 0,
AccentColour = Color4.DarkRed, AccentColour = Color4.DarkRed,
KiaiMode = kiai, KiaiMode = kiai,
Children = new[] Children = new[]
@ -38,10 +37,9 @@ public override void Reset()
} }
}); });
Add(new StrongCirclePiece Add(new CirclePiece(true)
{ {
Position = new Vector2(350, 100), Position = new Vector2(350, 100),
Width = 0,
AccentColour = Color4.DarkRed, AccentColour = Color4.DarkRed,
KiaiMode = kiai, KiaiMode = kiai,
Children = new[] Children = new[]
@ -53,7 +51,6 @@ public override void Reset()
Add(new CirclePiece Add(new CirclePiece
{ {
Position = new Vector2(100, 300), Position = new Vector2(100, 300),
Width = 0,
AccentColour = Color4.DarkBlue, AccentColour = Color4.DarkBlue,
KiaiMode = kiai, KiaiMode = kiai,
Children = new[] Children = new[]
@ -62,10 +59,9 @@ public override void Reset()
} }
}); });
Add(new StrongCirclePiece Add(new CirclePiece(true)
{ {
Position = new Vector2(350, 300), Position = new Vector2(350, 300),
Width = 0,
AccentColour = Color4.DarkBlue, AccentColour = Color4.DarkBlue,
KiaiMode = kiai, KiaiMode = kiai,
Children = new[] Children = new[]
@ -77,7 +73,6 @@ public override void Reset()
Add(new CirclePiece Add(new CirclePiece
{ {
Position = new Vector2(100, 500), Position = new Vector2(100, 500),
Width = 0,
AccentColour = Color4.Orange, AccentColour = Color4.Orange,
KiaiMode = kiai, KiaiMode = kiai,
Children = new[] Children = new[]
@ -86,20 +81,22 @@ public override void Reset()
} }
}); });
Add(new CirclePiece Add(new ElongatedCirclePiece
{ {
Position = new Vector2(575, 100), Position = new Vector2(575, 100),
Width = 0.25f,
AccentColour = Color4.Orange, AccentColour = Color4.Orange,
KiaiMode = kiai, KiaiMode = kiai,
Length = 0.10f,
PlayfieldLengthReference = () => DrawSize.X
}); });
Add(new StrongCirclePiece Add(new ElongatedCirclePiece(true)
{ {
Position = new Vector2(575, 300), Position = new Vector2(575, 300),
Width = 0.25f,
AccentColour = Color4.Orange, AccentColour = Color4.Orange,
KiaiMode = kiai KiaiMode = kiai,
Length = 0.10f,
PlayfieldLengthReference = () => DrawSize.X
}); });
} }

View File

@ -5,6 +5,7 @@
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects; using osu.Game.Modes.Taiko.Objects;
@ -19,7 +20,11 @@ internal class TestCaseTaikoPlayfield : TestCase
private TaikoPlayfield playfield; private TaikoPlayfield playfield;
protected override double TimePerAction => 500; protected override double TimePerAction => default_duration * 2;
private const double default_duration = 300;
private const float scroll_time = 1000;
public override void Reset() public override void Reset()
{ {
@ -29,7 +34,7 @@ public override void Reset()
AddStep("Miss :(", addMissJudgement); AddStep("Miss :(", addMissJudgement);
AddStep("DrumRoll", () => addDrumRoll(false)); AddStep("DrumRoll", () => addDrumRoll(false));
AddStep("Strong DrumRoll", () => addDrumRoll(true)); AddStep("Strong DrumRoll", () => addDrumRoll(true));
AddStep("Swell", addSwell); AddStep("Swell", () => addSwell());
AddStep("Centre", () => addCentreHit(false)); AddStep("Centre", () => addCentreHit(false));
AddStep("Strong Centre", () => addCentreHit(true)); AddStep("Strong Centre", () => addCentreHit(true));
AddStep("Rim", () => addRimHit(false)); AddStep("Rim", () => addRimHit(false));
@ -37,8 +42,12 @@ public override void Reset()
AddStep("Add bar line", () => addBarLine(false)); AddStep("Add bar line", () => addBarLine(false));
AddStep("Add major bar line", () => addBarLine(true)); AddStep("Add major bar line", () => addBarLine(true));
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
Add(new Container Add(new Container
{ {
Clock = new FramedClock(rateAdjustClock),
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Y = 200, Y = 200,
Children = new[] Children = new[]
@ -77,49 +86,53 @@ private void addMissJudgement()
}); });
} }
private void addBarLine(bool major) private void addBarLine(bool major, double delay = scroll_time)
{ {
BarLine bl = new BarLine BarLine bl = new BarLine
{ {
StartTime = Time.Current + 1000, StartTime = playfield.Time.Current + delay,
PreEmpt = 1000 ScrollTime = scroll_time
}; };
playfield.AddBarLine(major ? new DrawableMajorBarLine(bl) : new DrawableBarLine(bl)); playfield.AddBarLine(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl));
} }
private void addSwell() private void addSwell(double duration = default_duration)
{ {
playfield.Add(new DrawableSwell(new Swell playfield.Add(new DrawableSwell(new Swell
{ {
StartTime = Time.Current + 1000, StartTime = playfield.Time.Current + scroll_time,
EndTime = Time.Current + 1000, Duration = duration,
PreEmpt = 1000 ScrollTime = scroll_time
})); }));
} }
private void addDrumRoll(bool strong) private void addDrumRoll(bool strong, double duration = default_duration)
{ {
addBarLine(true);
addBarLine(true, scroll_time + duration);
var d = new DrumRoll var d = new DrumRoll
{ {
StartTime = Time.Current + 1000, StartTime = playfield.Time.Current + scroll_time,
Distance = 1000, IsStrong = strong,
PreEmpt = 1000, Duration = duration,
ScrollTime = scroll_time,
}; };
playfield.Add(strong ? new DrawableStrongDrumRoll(d) : new DrawableDrumRoll(d)); playfield.Add(new DrawableDrumRoll(d));
} }
private void addCentreHit(bool strong) private void addCentreHit(bool strong)
{ {
Hit h = new Hit Hit h = new Hit
{ {
StartTime = Time.Current + 1000, StartTime = playfield.Time.Current + scroll_time,
PreEmpt = 1000 ScrollTime = scroll_time
}; };
if (strong) if (strong)
playfield.Add(new DrawableStrongCentreHit(h)); playfield.Add(new DrawableCentreHitStrong(h));
else else
playfield.Add(new DrawableCentreHit(h)); playfield.Add(new DrawableCentreHit(h));
} }
@ -128,12 +141,12 @@ private void addRimHit(bool strong)
{ {
Hit h = new Hit Hit h = new Hit
{ {
StartTime = Time.Current + 1000, StartTime = playfield.Time.Current + scroll_time,
PreEmpt = 1000 ScrollTime = scroll_time
}; };
if (strong) if (strong)
playfield.Add(new DrawableStrongRimHit(h)); playfield.Add(new DrawableRimHitStrong(h));
else else
playfield.Add(new DrawableRimHit(h)); playfield.Add(new DrawableRimHit(h));
} }

View File

@ -21,6 +21,8 @@ public class OsuPlayfield : Playfield<OsuHitObject, OsuJudgement>
private readonly Container judgementLayer; private readonly Container judgementLayer;
private readonly ConnectionRenderer<OsuHitObject> connectionLayer; private readonly ConnectionRenderer<OsuHitObject> connectionLayer;
public override bool ProvidingUserCursor => true;
public override Vector2 Size public override Vector2 Size
{ {
get get

View File

@ -9,6 +9,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Beatmaps.Timing;
using osu.Game.Database; using osu.Game.Database;
namespace osu.Game.Modes.Taiko.Beatmaps namespace osu.Game.Modes.Taiko.Beatmaps
@ -41,6 +43,7 @@ public Beatmap<TaikoHitObject> Convert(Beatmap original)
{ {
return new Beatmap<TaikoHitObject>(original) return new Beatmap<TaikoHitObject>(original)
{ {
TimingInfo = original is LegacyBeatmap ? new LegacyTimingInfo(original.TimingInfo) : original.TimingInfo,
HitObjects = original.HitObjects.SelectMany(h => convertHitObject(h, original)).ToList() HitObjects = original.HitObjects.SelectMany(h => convertHitObject(h, original)).ToList()
}; };
} }
@ -100,7 +103,6 @@ private IEnumerable<TaikoHitObject> convertHitObject(HitObject obj, Beatmap beat
StartTime = j, StartTime = j,
Sample = obj.Sample, Sample = obj.Sample,
IsStrong = strong, IsStrong = strong,
VelocityMultiplier = legacy_velocity_multiplier
}; };
} }
} }
@ -111,9 +113,8 @@ private IEnumerable<TaikoHitObject> convertHitObject(HitObject obj, Beatmap beat
StartTime = obj.StartTime, StartTime = obj.StartTime,
Sample = obj.Sample, Sample = obj.Sample,
IsStrong = strong, IsStrong = strong,
Distance = distance, Duration = taikoDuration,
TickRate = beatmap.BeatmapInfo.Difficulty.SliderTickRate == 3 ? 3 : 4, TickRate = beatmap.BeatmapInfo.Difficulty.SliderTickRate == 3 ? 3 : 4,
VelocityMultiplier = legacy_velocity_multiplier
}; };
} }
} }
@ -126,9 +127,8 @@ private IEnumerable<TaikoHitObject> convertHitObject(HitObject obj, Beatmap beat
StartTime = obj.StartTime, StartTime = obj.StartTime,
Sample = obj.Sample, Sample = obj.Sample,
IsStrong = strong, IsStrong = strong,
EndTime = endTimeData.EndTime, Duration = endTimeData.Duration,
RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier), RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier),
VelocityMultiplier = legacy_velocity_multiplier
}; };
} }
else else
@ -142,7 +142,6 @@ private IEnumerable<TaikoHitObject> convertHitObject(HitObject obj, Beatmap beat
StartTime = obj.StartTime, StartTime = obj.StartTime,
Sample = obj.Sample, Sample = obj.Sample,
IsStrong = strong, IsStrong = strong,
VelocityMultiplier = legacy_velocity_multiplier
}; };
} }
else else
@ -152,10 +151,25 @@ private IEnumerable<TaikoHitObject> convertHitObject(HitObject obj, Beatmap beat
StartTime = obj.StartTime, StartTime = obj.StartTime,
Sample = obj.Sample, Sample = obj.Sample,
IsStrong = strong, IsStrong = strong,
VelocityMultiplier = legacy_velocity_multiplier
}; };
} }
} }
} }
private class LegacyTimingInfo : TimingInfo
{
public LegacyTimingInfo(TimingInfo original)
{
if (original is LegacyTimingInfo)
ControlPoints.AddRange(original.ControlPoints);
else
{
ControlPoints.AddRange(original.ControlPoints.Select(c => c.Clone()));
foreach (var c in ControlPoints)
c.SpeedMultiplier *= legacy_velocity_multiplier;
}
}
}
} }
} }

View File

@ -1,26 +1,9 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps.Timing;
using osu.Game.Database;
namespace osu.Game.Modes.Taiko.Objects namespace osu.Game.Modes.Taiko.Objects
{ {
public class BarLine public class BarLine : TaikoHitObject
{ {
/// <summary>
/// The start time of the control point this bar line represents.
/// </summary>
public double StartTime;
/// <summary>
/// The time to scroll in the bar line.
/// </summary>
public double PreEmpt;
public void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{
PreEmpt = TaikoHitObject.BASE_SCROLL_TIME / difficulty.SliderMultiplier * timing.BeatLengthAt(StartTime) * timing.SpeedMultiplierAt(StartTime) / 1000;
}
} }
} }

View File

@ -56,20 +56,19 @@ public DrawableBarLine(BarLine barLine)
Alpha = 0.75f Alpha = 0.75f
} }
}; };
LifetimeStart = BarLine.StartTime - BarLine.ScrollTime * 2;
LifetimeEnd = BarLine.StartTime + BarLine.ScrollTime;
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
LifetimeStart = BarLine.StartTime - BarLine.PreEmpt * 2;
LifetimeEnd = BarLine.StartTime + BarLine.PreEmpt;
Delay(BarLine.StartTime - Time.Current); Delay(BarLine.StartTime - Time.Current);
FadeOut(base_fadeout_time * BarLine.PreEmpt / 1000); FadeOut(base_fadeout_time * BarLine.ScrollTime / 1000);
} }
private void updateScrollPosition(double time) => MoveToX((float)((BarLine.StartTime - time) / BarLine.PreEmpt)); private void updateScrollPosition(double time) => MoveToX((float)((BarLine.StartTime - time) / BarLine.ScrollTime));
protected override void Update() protected override void Update()
{ {

View File

@ -8,7 +8,7 @@
namespace osu.Game.Modes.Taiko.Objects.Drawables namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public class DrawableMajorBarLine : DrawableBarLine public class DrawableBarLineMajor : DrawableBarLine
{ {
/// <summary> /// <summary>
/// The vertical offset of the triangles from the line tracker. /// The vertical offset of the triangles from the line tracker.
@ -20,7 +20,7 @@ public class DrawableMajorBarLine : DrawableBarLine
/// </summary> /// </summary>
private const float triangle_size = 20f; private const float triangle_size = 20f;
public DrawableMajorBarLine(BarLine barLine) public DrawableBarLineMajor(BarLine barLine)
: base(barLine) : base(barLine)
{ {
Add(new Container Add(new Container

View File

@ -15,17 +15,13 @@ public class DrawableCentreHit : DrawableHit
public DrawableCentreHit(Hit hit) public DrawableCentreHit(Hit hit)
: base(hit) : base(hit)
{ {
MainPiece.Add(new CentreHitSymbolPiece());
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
Circle.AccentColour = colours.PinkDarker; MainPiece.AccentColour = colours.PinkDarker;
} }
protected override CirclePiece CreateCirclePiece() => new CirclePiece
{
Children = new[] { new CentreHitSymbolPiece() }
};
} }
} }

View File

@ -8,24 +8,20 @@
namespace osu.Game.Modes.Taiko.Objects.Drawables namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public class DrawableStrongCentreHit : DrawableStrongHit public class DrawableCentreHitStrong : DrawableHitStrong
{ {
protected override Key[] HitKeys { get; } = { Key.F, Key.J }; protected override Key[] HitKeys { get; } = { Key.F, Key.J };
public DrawableStrongCentreHit(Hit hit) public DrawableCentreHitStrong(Hit hit)
: base(hit) : base(hit)
{ {
MainPiece.Add(new CentreHitSymbolPiece());
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
Circle.AccentColour = colours.PinkDarker; MainPiece.AccentColour = colours.PinkDarker;
} }
protected override CirclePiece CreateCirclePiece() => new StrongCirclePiece
{
Children = new[] { new CentreHitSymbolPiece() }
};
} }
} }

View File

@ -3,28 +3,23 @@
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawables namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public class DrawableDrumRoll : DrawableTaikoHitObject public class DrawableDrumRoll : DrawableTaikoHitObject<DrumRoll>
{ {
/// <summary> /// <summary>
/// Number of rolling hits required to reach the dark/final accent colour. /// Number of rolling hits required to reach the dark/final accent colour.
/// </summary> /// </summary>
private const int rolling_hits_for_dark_accent = 5; private const int rolling_hits_for_dark_accent = 5;
private readonly DrumRoll drumRoll;
private readonly CirclePiece circle;
private Color4 accentDarkColour; private Color4 accentDarkColour;
/// <summary> /// <summary>
@ -35,32 +30,32 @@ public class DrawableDrumRoll : DrawableTaikoHitObject
public DrawableDrumRoll(DrumRoll drumRoll) public DrawableDrumRoll(DrumRoll drumRoll)
: base(drumRoll) : base(drumRoll)
{ {
this.drumRoll = drumRoll;
RelativeSizeAxes = Axes.X;
Width = (float)(drumRoll.Duration / drumRoll.PreEmpt);
Add(circle = CreateCirclePiece());
circle.KiaiMode = HitObject.Kiai;
foreach (var tick in drumRoll.Ticks) foreach (var tick in drumRoll.Ticks)
{ {
var newTick = new DrawableDrumRollTick(tick) var newTick = new DrawableDrumRollTick(tick)
{ {
X = (float)((tick.StartTime - HitObject.StartTime) / drumRoll.Duration) X = (float)((tick.StartTime - HitObject.StartTime) / HitObject.Duration)
}; };
newTick.OnJudgement += onTickJudgement; newTick.OnJudgement += onTickJudgement;
AddNested(newTick); AddNested(newTick);
Add(newTick); MainPiece.Add(newTick);
} }
} }
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement { SecondHit = HitObject.IsStrong };
protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece(HitObject.IsStrong)
{
Length = (float)(HitObject.Duration / HitObject.ScrollTime),
PlayfieldLengthReference = () => Parent.DrawSize.X
};
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
circle.AccentColour = AccentColour = colours.YellowDark; MainPiece.AccentColour = AccentColour = colours.YellowDark;
accentDarkColour = colours.YellowDarker; accentDarkColour = colours.YellowDarker;
} }
@ -72,7 +67,7 @@ protected override void LoadComplete()
// is further than mid point of the play field, so the time taken to scroll in should always // is further than mid point of the play field, so the time taken to scroll in should always
// be greater than the time taken to scroll out to the left of the screen. // be greater than the time taken to scroll out to the left of the screen.
// Thus, using PreEmpt here is enough for the drum roll to completely scroll out. // Thus, using PreEmpt here is enough for the drum roll to completely scroll out.
LifetimeEnd = drumRoll.EndTime + drumRoll.PreEmpt; LifetimeEnd = HitObject.EndTime + HitObject.ScrollTime;
} }
private void onTickJudgement(DrawableHitObject<TaikoHitObject, TaikoJudgement> obj) private void onTickJudgement(DrawableHitObject<TaikoHitObject, TaikoJudgement> obj)
@ -85,7 +80,7 @@ private void onTickJudgement(DrawableHitObject<TaikoHitObject, TaikoJudgement> o
rollingHits = MathHelper.Clamp(rollingHits, 0, rolling_hits_for_dark_accent); rollingHits = MathHelper.Clamp(rollingHits, 0, rolling_hits_for_dark_accent);
Color4 newAccent = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_dark_accent, AccentColour, accentDarkColour, 0, 1); Color4 newAccent = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_dark_accent, AccentColour, accentDarkColour, 0, 1);
circle.FadeAccent(newAccent, 100); MainPiece.FadeAccent(newAccent, 100);
} }
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
@ -98,10 +93,10 @@ protected override void CheckJudgement(bool userTriggered)
int countHit = NestedHitObjects.Count(o => o.Judgement.Result == HitResult.Hit); int countHit = NestedHitObjects.Count(o => o.Judgement.Result == HitResult.Hit);
if (countHit > drumRoll.RequiredGoodHits) if (countHit > HitObject.RequiredGoodHits)
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = countHit >= drumRoll.RequiredGreatHits ? TaikoHitResult.Great : TaikoHitResult.Good; Judgement.TaikoResult = countHit >= HitObject.RequiredGreatHits ? TaikoHitResult.Great : TaikoHitResult.Good;
} }
else else
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
@ -110,7 +105,5 @@ protected override void CheckJudgement(bool userTriggered)
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {
} }
protected virtual CirclePiece CreateCirclePiece() => new CirclePiece();
} }
} }

View File

@ -3,79 +3,37 @@
using System; using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input; using OpenTK.Input;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawables namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public class DrawableDrumRollTick : DrawableTaikoHitObject public class DrawableDrumRollTick : DrawableTaikoHitObject<DrumRollTick>
{ {
/// <summary>
/// The size of a tick.
/// </summary>
private const float tick_size = TaikoHitObject.CIRCLE_RADIUS / 2;
/// <summary>
/// Any tick that is not the first for a drumroll is not filled, but is instead displayed
/// as a hollow circle. This is what controls the border width of that circle.
/// </summary>
private const float tick_border_width = tick_size / 4;
private readonly DrumRollTick tick;
private readonly CircularContainer bodyContainer;
public DrawableDrumRollTick(DrumRollTick tick) public DrawableDrumRollTick(DrumRollTick tick)
: base(tick) : base(tick)
{ {
this.tick = tick;
Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre;
RelativePositionAxes = Axes.X;
Size = new Vector2(tick_size);
Children = new[]
{
bodyContainer = new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = tick_border_width,
BorderColour = Color4.White,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = tick.FirstTick ? 1 : 0,
AlwaysPresent = true
}
}
}
};
} }
protected override TaikoJudgement CreateJudgement() => new TaikoDrumRollTickJudgement { SecondHit = tick.IsStrong }; protected override TaikoPiece CreateMainPiece() => new TickPiece
{
Filled = HitObject.FirstTick
};
protected override TaikoJudgement CreateJudgement() => new TaikoDrumRollTickJudgement { SecondHit = HitObject.IsStrong };
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
{ {
if (!userTriggered) if (!userTriggered)
{ {
if (Judgement.TimeOffset > tick.HitWindow) if (Judgement.TimeOffset > HitObject.HitWindow)
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
return; return;
} }
if (Math.Abs(Judgement.TimeOffset) < tick.HitWindow) if (Math.Abs(Judgement.TimeOffset) < HitObject.HitWindow)
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Great; Judgement.TaikoResult = TaikoHitResult.Great;
@ -87,7 +45,7 @@ protected override void UpdateState(ArmedState state)
switch (state) switch (state)
{ {
case ArmedState.Hit: case ArmedState.Hit:
bodyContainer.ScaleTo(0, 100, EasingTypes.OutQuint); Content.ScaleTo(0, 100, EasingTypes.OutQuint);
break; break;
} }
} }

View File

@ -4,29 +4,19 @@
using System; using System;
using System.Linq; using System.Linq;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK.Input; using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public abstract class DrawableHit : DrawableTaikoHitObject public abstract class DrawableHit : DrawableTaikoHitObject<Hit>
{ {
/// <summary> /// <summary>
/// A list of keys which can result in hits for this HitObject. /// A list of keys which can result in hits for this HitObject.
/// </summary> /// </summary>
protected abstract Key[] HitKeys { get; } protected abstract Key[] HitKeys { get; }
protected override Container<Drawable> Content => bodyContainer;
protected readonly CirclePiece Circle;
private readonly Hit hit;
private readonly Container bodyContainer;
/// <summary> /// <summary>
/// Whether the last key pressed is a valid hit key. /// Whether the last key pressed is a valid hit key.
/// </summary> /// </summary>
@ -35,41 +25,28 @@ public abstract class DrawableHit : DrawableTaikoHitObject
protected DrawableHit(Hit hit) protected DrawableHit(Hit hit)
: base(hit) : base(hit)
{ {
this.hit = hit;
AddInternal(bodyContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new[]
{
Circle = CreateCirclePiece()
}
});
Circle.KiaiMode = HitObject.Kiai;
} }
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
{ {
if (!userTriggered) if (!userTriggered)
{ {
if (Judgement.TimeOffset > hit.HitWindowGood) if (Judgement.TimeOffset > HitObject.HitWindowGood)
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
return; return;
} }
double hitOffset = Math.Abs(Judgement.TimeOffset); double hitOffset = Math.Abs(Judgement.TimeOffset);
if (hitOffset > hit.HitWindowMiss) if (hitOffset > HitObject.HitWindowMiss)
return; return;
if (!validKeyPressed) if (!validKeyPressed)
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
else if (hitOffset < hit.HitWindowGood) else if (hitOffset < HitObject.HitWindowGood)
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = hitOffset < hit.HitWindowGreat ? TaikoHitResult.Great : TaikoHitResult.Good; Judgement.TaikoResult = hitOffset < HitObject.HitWindowGreat ? TaikoHitResult.Great : TaikoHitResult.Good;
} }
else else
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
@ -92,24 +69,26 @@ protected override void UpdateState(ArmedState state)
switch (State) switch (State)
{ {
case ArmedState.Idle: case ArmedState.Idle:
Delay(hit.HitWindowMiss); Delay(HitObject.HitWindowMiss);
break; break;
case ArmedState.Miss: case ArmedState.Miss:
FadeOut(100); FadeOut(100);
break; break;
case ArmedState.Hit: case ArmedState.Hit:
bodyContainer.ScaleTo(0.8f, 400, EasingTypes.OutQuad);
bodyContainer.MoveToY(-200, 250, EasingTypes.Out);
bodyContainer.Delay(250);
bodyContainer.MoveToY(0, 500, EasingTypes.In);
FadeOut(600); FadeOut(600);
const float gravity_time = 300;
const float gravity_travel_height = 200;
Content.ScaleTo(0.8f, gravity_time * 2, EasingTypes.OutQuad);
MoveToY(-gravity_travel_height, gravity_time, EasingTypes.Out);
Delay(gravity_time, true);
MoveToY(gravity_travel_height * 2, gravity_time * 2, EasingTypes.In);
break; break;
} }
Expire(); Expire();
} }
protected abstract CirclePiece CreateCirclePiece();
} }
} }

View File

@ -7,10 +7,11 @@
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using OpenTK.Input; using OpenTK.Input;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawables namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public abstract class DrawableStrongHit : DrawableHit public abstract class DrawableHitStrong : DrawableHit
{ {
/// <summary> /// <summary>
/// The lenience for the second key press. /// The lenience for the second key press.
@ -22,11 +23,13 @@ public abstract class DrawableStrongHit : DrawableHit
private bool firstKeyHeld; private bool firstKeyHeld;
private Key firstHitKey; private Key firstHitKey;
protected DrawableStrongHit(Hit hit) protected DrawableHitStrong(Hit hit)
: base(hit) : base(hit)
{ {
} }
protected override TaikoPiece CreateMainPiece() => new CirclePiece(true);
protected override TaikoJudgement CreateJudgement() => new TaikoStrongHitJudgement(); protected override TaikoJudgement CreateJudgement() => new TaikoStrongHitJudgement();
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)

View File

@ -15,17 +15,13 @@ public class DrawableRimHit : DrawableHit
public DrawableRimHit(Hit hit) public DrawableRimHit(Hit hit)
: base(hit) : base(hit)
{ {
MainPiece.Add(new RimHitSymbolPiece());
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
Circle.AccentColour = colours.BlueDarker; MainPiece.AccentColour = colours.BlueDarker;
} }
protected override CirclePiece CreateCirclePiece() => new CirclePiece
{
Children = new[] { new RimHitSymbolPiece() }
};
} }
} }

View File

@ -8,24 +8,20 @@
namespace osu.Game.Modes.Taiko.Objects.Drawables namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public class DrawableStrongRimHit : DrawableStrongHit public class DrawableRimHitStrong : DrawableHitStrong
{ {
protected override Key[] HitKeys { get; } = { Key.D, Key.K }; protected override Key[] HitKeys { get; } = { Key.D, Key.K };
public DrawableStrongRimHit(Hit hit) public DrawableRimHitStrong(Hit hit)
: base(hit) : base(hit)
{ {
MainPiece.Add(new RimHitSymbolPiece());
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
Circle.AccentColour = colours.BlueDarker; MainPiece.AccentColour = colours.BlueDarker;
} }
protected override CirclePiece CreateCirclePiece() => new StrongCirclePiece
{
Children = new[] { new RimHitSymbolPiece() }
};
} }
} }

View File

@ -1,20 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public class DrawableStrongDrumRoll : DrawableDrumRoll
{
public DrawableStrongDrumRoll(DrumRoll drumRoll)
: base(drumRoll)
{
}
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement { SecondHit = true };
protected override CirclePiece CreateCirclePiece() => new StrongCirclePiece();
}
}

View File

@ -18,7 +18,7 @@
namespace osu.Game.Modes.Taiko.Objects.Drawables namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public class DrawableSwell : DrawableTaikoHitObject public class DrawableSwell : DrawableTaikoHitObject<Swell>
{ {
/// <summary> /// <summary>
/// Invoked when the swell has reached the hit target, i.e. when CurrentTime >= StartTime. /// Invoked when the swell has reached the hit target, i.e. when CurrentTime >= StartTime.
@ -31,8 +31,6 @@ public class DrawableSwell : DrawableTaikoHitObject
private const float target_ring_scale = 5f; private const float target_ring_scale = 5f;
private const float inner_ring_alpha = 0.65f; private const float inner_ring_alpha = 0.65f;
private readonly Swell swell;
private readonly Container bodyContainer; private readonly Container bodyContainer;
private readonly CircularContainer targetRing; private readonly CircularContainer targetRing;
private readonly CircularContainer expandingRing; private readonly CircularContainer expandingRing;
@ -54,12 +52,11 @@ public class DrawableSwell : DrawableTaikoHitObject
public DrawableSwell(Swell swell) public DrawableSwell(Swell swell)
: base(swell) : base(swell)
{ {
this.swell = swell;
Children = new Drawable[] Children = new Drawable[]
{ {
bodyContainer = new Container bodyContainer = new Container
{ {
AutoSizeAxes = Axes.Both,
Children = new Drawable[] Children = new Drawable[]
{ {
expandingRing = new CircularContainer expandingRing = new CircularContainer
@ -120,6 +117,8 @@ public DrawableSwell(Swell swell)
}, },
circlePiece = new CirclePiece circlePiece = new CirclePiece
{ {
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new [] Children = new []
{ {
symbol = new SwellSymbolPiece() symbol = new SwellSymbolPiece()
@ -146,18 +145,18 @@ protected override void CheckJudgement(bool userTriggered)
{ {
userHits++; userHits++;
var completion = (float)userHits / swell.RequiredHits; var completion = (float)userHits / HitObject.RequiredHits;
expandingRing.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50); expandingRing.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50);
expandingRing.Delay(50); expandingRing.Delay(50);
expandingRing.FadeTo(completion / 8, 2000, EasingTypes.OutQuint); expandingRing.FadeTo(completion / 8, 2000, EasingTypes.OutQuint);
expandingRing.DelayReset(); expandingRing.DelayReset();
symbol.RotateTo((float)(completion * swell.Duration / 8), 4000, EasingTypes.OutQuint); symbol.RotateTo((float)(completion * HitObject.Duration / 8), 4000, EasingTypes.OutQuint);
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, EasingTypes.OutQuint); expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, EasingTypes.OutQuint);
if (userHits == swell.RequiredHits) if (userHits == HitObject.RequiredHits)
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Great; Judgement.TaikoResult = TaikoHitResult.Great;
@ -169,7 +168,7 @@ protected override void CheckJudgement(bool userTriggered)
return; return;
//TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP //TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP
if (userHits > swell.RequiredHits / 2) if (userHits > HitObject.RequiredHits / 2)
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Good; Judgement.TaikoResult = TaikoHitResult.Good;
@ -189,7 +188,7 @@ protected override void UpdateState(ArmedState state)
Delay(preempt, true); Delay(preempt, true);
Delay(Judgement.TimeOffset + swell.Duration, true); Delay(Judgement.TimeOffset + HitObject.Duration, true);
const float out_transition_time = 300; const float out_transition_time = 300;

View File

@ -3,14 +3,18 @@
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK;
using OpenTK.Input; using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public abstract class DrawableTaikoHitObject : DrawableHitObject<TaikoHitObject, TaikoJudgement> public abstract class DrawableTaikoHitObject<TaikoHitType> : DrawableHitObject<TaikoHitObject, TaikoJudgement>
where TaikoHitType : TaikoHitObject
{ {
/// <summary> /// <summary>
/// A list of keys which this hit object will accept. These are the standard Taiko keys for now. /// A list of keys which this hit object will accept. These are the standard Taiko keys for now.
@ -18,33 +22,52 @@ public abstract class DrawableTaikoHitObject : DrawableHitObject<TaikoHitObject,
/// </summary> /// </summary>
private readonly List<Key> validKeys = new List<Key>(new[] { Key.D, Key.F, Key.J, Key.K }); private readonly List<Key> validKeys = new List<Key>(new[] { Key.D, Key.F, Key.J, Key.K });
protected DrawableTaikoHitObject(TaikoHitObject hitObject) public override Vector2 OriginPosition => new Vector2(DrawHeight / 2);
protected override Container<Drawable> Content => bodyContainer;
protected readonly TaikoPiece MainPiece;
private readonly Container bodyContainer;
public new TaikoHitType HitObject;
protected DrawableTaikoHitObject(TaikoHitType hitObject)
: base(hitObject) : base(hitObject)
{ {
HitObject = hitObject;
Anchor = Anchor.CentreLeft; Anchor = Anchor.CentreLeft;
Origin = Anchor.CentreLeft; Origin = Anchor.Custom;
AutoSizeAxes = Axes.Both;
RelativePositionAxes = Axes.X; RelativePositionAxes = Axes.X;
}
protected override void LoadComplete() AddInternal(bodyContainer = new Container
{ {
LifetimeStart = HitObject.StartTime - HitObject.PreEmpt * 2; AutoSizeAxes = Axes.Both,
Children = new[]
{
MainPiece = CreateMainPiece()
}
});
base.LoadComplete(); MainPiece.KiaiMode = HitObject.Kiai;
LifetimeStart = HitObject.StartTime - HitObject.ScrollTime * 2;
} }
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement(); protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
protected virtual TaikoPiece CreateMainPiece() => new CirclePiece(HitObject.IsStrong);
/// <summary> /// <summary>
/// Sets the scroll position of the DrawableHitObject relative to the offset between /// Sets the scroll position of the DrawableHitObject relative to the offset between
/// a time value and the HitObject's StartTime. /// a time value and the HitObject's StartTime.
/// </summary> /// </summary>
/// <param name="time"></param> /// <param name="time"></param>
protected virtual void UpdateScrollPosition(double time) protected virtual void UpdateScrollPosition(double time) => X = (float)((HitObject.StartTime - time) / HitObject.ScrollTime);
{
MoveToX((float)((HitObject.StartTime - time) / HitObject.PreEmpt));
}
protected override void Update() protected override void Update()
{ {

View File

@ -1,12 +1,10 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -15,26 +13,30 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
/// <summary> /// <summary>
/// A circle piece which is used uniformly through osu!taiko to visualise hitobjects. /// A circle piece which is used uniformly through osu!taiko to visualise hitobjects.
/// <para> /// <para>
/// The body of this piece will overshoot its parent by <see cref="CirclePiece.Height"/> to form /// Note that this can actually be non-circle if the width is changed. See <see cref="ElongatedCirclePiece"/>
/// a rounded (_[-Width-]_) figure such that a regular "circle" is the result of a parent with Width = 0. /// for a usage example.
/// </para> /// </para>
/// </summary> /// </summary>
public class CirclePiece : Container, IHasAccentColour public class CirclePiece : TaikoPiece
{ {
public const float SYMBOL_SIZE = TaikoHitObject.CIRCLE_RADIUS * 2f * 0.45f; public const float SYMBOL_SIZE = TaikoHitObject.CIRCLE_RADIUS * 2f * 0.45f;
public const float SYMBOL_BORDER = 8; public const float SYMBOL_BORDER = 8;
public const float SYMBOL_INNER_SIZE = SYMBOL_SIZE - 2 * SYMBOL_BORDER; public const float SYMBOL_INNER_SIZE = SYMBOL_SIZE - 2 * SYMBOL_BORDER;
private Color4 accentColour; /// <summary>
/// The amount to scale up the base circle to show it as a "strong" piece.
/// </summary>
private const float strong_scale = 1.5f;
/// <summary> /// <summary>
/// The colour of the inner circle and outer glows. /// The colour of the inner circle and outer glows.
/// </summary> /// </summary>
public Color4 AccentColour public override Color4 AccentColour
{ {
get { return accentColour; } get { return base.AccentColour; }
set set
{ {
accentColour = value; base.AccentColour = value;
background.Colour = AccentColour; background.Colour = AccentColour;
@ -42,107 +44,100 @@ public Color4 AccentColour
} }
} }
private bool kiaiMode;
/// <summary> /// <summary>
/// Whether Kiai mode effects are enabled for this circle piece. /// Whether Kiai mode effects are enabled for this circle piece.
/// </summary> /// </summary>
public bool KiaiMode public override bool KiaiMode
{ {
get { return kiaiMode; } get { return base.KiaiMode; }
set set
{ {
kiaiMode = value; base.KiaiMode = value;
resetEdgeEffects(); resetEdgeEffects();
} }
} }
public override Anchor Origin protected override Container<Drawable> Content => content;
{
get { return Anchor.CentreLeft; }
set { throw new InvalidOperationException($"{nameof(CirclePiece)} must always use CentreLeft origin."); }
}
protected override Container<Drawable> Content => SymbolContainer; private readonly Container content;
protected readonly Container SymbolContainer;
private readonly Container background; private readonly Container background;
private readonly Container innerLayer;
public CirclePiece() public CirclePiece(bool isStrong = false)
{ {
RelativeSizeAxes = Axes.X; AddInternal(new Drawable[]
Height = TaikoHitObject.CIRCLE_RADIUS * 2;
// The "inner layer" is the body of the CirclePiece that overshoots it by Height/2 px on both sides
AddInternal(innerLayer = new Container
{ {
Name = "Inner Layer", background = new CircularContainer
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Children = new Drawable[]
{ {
background = new CircularContainer Name = "Background",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{ {
Name = "Background", new Box
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{ {
new Box Anchor = Anchor.Centre,
{ Origin = Anchor.Centre,
Anchor = Anchor.Centre, RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre, },
RelativeSizeAxes = Axes.Both, new Triangles
},
new Triangles
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
ColourLight = Color4.White,
ColourDark = Color4.White.Darken(0.1f)
}
}
},
new CircularContainer
{
Name = "Ring",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
BorderThickness = 8,
BorderColour = Color4.White,
Masking = true,
Children = new[]
{ {
new Box Anchor = Anchor.Centre,
{ Origin = Anchor.Centre,
Anchor = Anchor.Centre, RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre, ColourLight = Color4.White,
RelativeSizeAxes = Axes.Both, ColourDark = Color4.White.Darken(0.1f)
Alpha = 0,
AlwaysPresent = true
}
} }
},
SymbolContainer = new Container
{
Name = "Symbol",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
} }
},
new CircularContainer
{
Name = "Ring",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
BorderThickness = 8,
BorderColour = Color4.White,
Masking = true,
Children = new[]
{
new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
},
content = new Container
{
RelativeSizeAxes = Axes.Both,
Name = "Content",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
} }
}); });
if (isStrong)
{
Size *= strong_scale;
//default for symbols etc.
Content.Scale *= strong_scale;
}
} }
protected override void Update() protected override void Update()
{ {
// Add the overshoot to compensate for corner radius base.Update();
innerLayer.Width = DrawWidth + DrawHeight;
//we want to allow for width of content to remain mapped to the area inside us, regardless of the scale applied above.
Content.Width = 1 / Content.Scale.X;
} }
private void resetEdgeEffects() private void resetEdgeEffects()

View File

@ -0,0 +1,41 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics.Primitives;
using osu.Game.Modes.Taiko.UI;
namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{
public class ElongatedCirclePiece : CirclePiece
{
/// <summary>
/// As we are being used to define the absolute size of hits, we need to be given a relative reference of our containing <see cref="TaikoPlayfield"/>.
/// </summary>
public Func<float> PlayfieldLengthReference;
/// <summary>
/// The length of this piece as a multiple of the value returned by <see cref="PlayfieldLengthReference"/>
/// </summary>
public float Length;
public ElongatedCirclePiece(bool isStrong = false) : base(isStrong)
{
}
protected override void Update()
{
base.Update();
var padding = Content.DrawHeight * Content.Width / 2;
Content.Padding = new MarginPadding
{
Left = padding,
Right = padding,
};
Width = (PlayfieldLengthReference?.Invoke() ?? 0) * Length + DrawHeight;
}
}
}

View File

@ -1,25 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{
/// <summary>
/// A type of circle piece which is drawn at a higher scale to represent a "strong" piece.
/// </summary>
public class StrongCirclePiece : CirclePiece
{
/// <summary>
/// The amount to scale up the base circle to show it as a "strong" piece.
/// </summary>
private const float strong_scale = 1.5f;
public StrongCirclePiece()
{
SymbolContainer.Scale = new Vector2(strong_scale);
}
public override Vector2 Size => new Vector2(base.Size.X, base.Size.Y * strong_scale);
}
}

View File

@ -0,0 +1,45 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{
public class TaikoPiece : Container, IHasAccentColour
{
private Color4 accentColour;
/// <summary>
/// The colour of the inner circle and outer glows.
/// </summary>
public virtual Color4 AccentColour
{
get { return accentColour; }
set
{
accentColour = value;
}
}
private bool kiaiMode;
/// <summary>
/// Whether Kiai mode effects are enabled for this circle piece.
/// </summary>
public virtual bool KiaiMode
{
get { return kiaiMode; }
set
{
kiaiMode = value;
}
}
public TaikoPiece()
{
//just a default
Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2);
}
}
}

View File

@ -0,0 +1,60 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{
public class TickPiece : TaikoPiece
{
/// <summary>
/// Any tick that is not the first for a drumroll is not filled, but is instead displayed
/// as a hollow circle. This is what controls the border width of that circle.
/// </summary>
private const float tick_border_width = TaikoHitObject.CIRCLE_RADIUS / 2 / 4;
/// <summary>
/// The size of a tick.
/// </summary>
private const float tick_size = TaikoHitObject.CIRCLE_RADIUS / 2;
private bool filled;
public bool Filled
{
get { return filled; }
set
{
filled = value;
fillBox.Alpha = filled ? 1 : 0;
}
}
private readonly Box fillBox;
public TickPiece()
{
Size = new Vector2(tick_size);
Add(new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = tick_border_width,
BorderColour = Color4.White,
Children = new[]
{
fillBox = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
});
}
}
}

View File

@ -11,26 +11,16 @@
namespace osu.Game.Modes.Taiko.Objects namespace osu.Game.Modes.Taiko.Objects
{ {
public class DrumRoll : TaikoHitObject, IHasDistance public class DrumRoll : TaikoHitObject, IHasEndTime
{ {
/// <summary> /// <summary>
/// Drum roll distance that results in a duration of 1 speed-adjusted beat length. /// Drum roll distance that results in a duration of 1 speed-adjusted beat length.
/// </summary> /// </summary>
private const float base_distance = 100; private const float base_distance = 100;
public double EndTime => StartTime + Distance / Velocity; public double EndTime => StartTime + Duration;
public double Duration => EndTime - StartTime; public double Duration { get; set; }
/// <summary>
/// Raw length of the drum roll in positional length units.
/// </summary>
public double Distance { get; set; }
/// <summary>
/// Velocity of the drum roll in positional length units per millisecond.
/// </summary>
public double Velocity { get; protected set; } = 5;
/// <summary> /// <summary>
/// Numer of ticks per beat length. /// Numer of ticks per beat length.
@ -69,9 +59,6 @@ public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficul
{ {
base.ApplyDefaults(timing, difficulty); base.ApplyDefaults(timing, difficulty);
double speedAdjutedBeatLength = timing.SpeedMultiplierAt(StartTime) * timing.BeatLengthAt(StartTime);
Velocity = base_distance * difficulty.SliderMultiplier / speedAdjutedBeatLength * VelocityMultiplier;
tickSpacing = timing.BeatLengthAt(StartTime) / TickRate; tickSpacing = timing.BeatLengthAt(StartTime) / TickRate;
RequiredGoodHits = TotalTicks * Math.Min(0.15, 0.05 + 0.10 / 6 * difficulty.OverallDifficulty); RequiredGoodHits = TotalTicks * Math.Min(0.15, 0.05 + 0.10 / 6 * difficulty.OverallDifficulty);
@ -86,12 +73,12 @@ private List<DrumRollTick> createTicks()
return ret; return ret;
bool first = true; bool first = true;
for (double t = StartTime; t < EndTime + (int)tickSpacing; t += tickSpacing) for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing)
{ {
ret.Add(new DrumRollTick ret.Add(new DrumRollTick
{ {
FirstTick = first, FirstTick = first,
PreEmpt = PreEmpt, ScrollTime = ScrollTime,
TickSpacing = tickSpacing, TickSpacing = tickSpacing,
StartTime = t, StartTime = t,
IsStrong = IsStrong, IsStrong = IsStrong,

View File

@ -7,9 +7,9 @@ namespace osu.Game.Modes.Taiko.Objects
{ {
public class Swell : TaikoHitObject, IHasEndTime public class Swell : TaikoHitObject, IHasEndTime
{ {
public double EndTime { get; set; } public double EndTime => StartTime + Duration;
public double Duration => EndTime - StartTime; public double Duration { get; set; }
/// <summary> /// <summary>
/// The number of hits required to complete the swell successfully. /// The number of hits required to complete the swell successfully.

View File

@ -15,19 +15,14 @@ public abstract class TaikoHitObject : HitObject
public const float CIRCLE_RADIUS = 42f; public const float CIRCLE_RADIUS = 42f;
/// <summary> /// <summary>
/// Time (in milliseconds) to scroll in the hit object with a speed-adjusted beat length of 1 second. /// The time taken from the initial (off-screen) spawn position to the centre of the hit target for a <see cref="ControlPoint.BeatLength"/> of 1000ms.
/// </summary> /// </summary>
public const double BASE_SCROLL_TIME = 6000; private const double scroll_time = 6000;
/// <summary> /// <summary>
/// The velocity multiplier applied to this hit object. /// Our adjusted <see cref="scroll_time"/> taking into consideration local <see cref="ControlPoint.BeatLength"/> and other speed multipliers.
/// </summary> /// </summary>
public float VelocityMultiplier = 1; public double ScrollTime;
/// <summary>
/// The time to scroll in the HitObject.
/// </summary>
public double PreEmpt;
/// <summary> /// <summary>
/// Whether this HitObject is a "strong" type. /// Whether this HitObject is a "strong" type.
@ -44,7 +39,7 @@ public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficul
{ {
base.ApplyDefaults(timing, difficulty); base.ApplyDefaults(timing, difficulty);
PreEmpt = BASE_SCROLL_TIME / difficulty.SliderMultiplier * timing.BeatLengthAt(StartTime) * timing.SpeedMultiplierAt(StartTime) / VelocityMultiplier / 1000; ScrollTime = scroll_time * (timing.BeatLengthAt(StartTime) / 1000) / (difficulty.SliderMultiplier * timing.SpeedMultiplierAt(StartTime));
ControlPoint overridePoint; ControlPoint overridePoint;
Kiai = timing.TimingPointAt(StartTime, out overridePoint).KiaiMode; Kiai = timing.TimingPointAt(StartTime, out overridePoint).KiaiMode;

View File

@ -128,17 +128,36 @@ protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
return false; return false;
Drawable target = null; Drawable target = null;
Drawable back = null;
if (args.Key == CentreKey) if (args.Key == CentreKey)
{
target = centreHit; target = centreHit;
back = centre;
}
else if (args.Key == RimKey) else if (args.Key == RimKey)
{
target = rimHit; target = rimHit;
back = rim;
}
if (target != null) if (target != null)
{ {
target.FadeTo(Math.Min(target.Alpha + 0.4f, 1), 40, EasingTypes.OutQuint); const float scale_amount = 0.05f;
target.Delay(40); const float alpha_amount = 0.5f;
target.FadeOut(1000, EasingTypes.OutQuint);
const float down_time = 40;
const float up_time = 1000;
back.ScaleTo(target.Scale.X - scale_amount, down_time, EasingTypes.OutQuint);
back.Delay(down_time);
back.ScaleTo(1, up_time, EasingTypes.OutQuint);
target.ScaleTo(target.Scale.X - scale_amount, down_time, EasingTypes.OutQuint);
target.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, EasingTypes.OutQuint);
target.Delay(down_time);
target.ScaleTo(1, up_time, EasingTypes.OutQuint);
target.FadeOut(up_time, EasingTypes.OutQuint);
} }
return false; return false;

View File

@ -79,7 +79,7 @@ private void loadBarLines()
barLine.ApplyDefaults(Beatmap.TimingInfo, Beatmap.BeatmapInfo.Difficulty); barLine.ApplyDefaults(Beatmap.TimingInfo, Beatmap.BeatmapInfo.Difficulty);
taikoPlayfield.AddBarLine(isMajor ? new DrawableMajorBarLine(barLine) : new DrawableBarLine(barLine)); taikoPlayfield.AddBarLine(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine));
currentBeat++; currentBeat++;
} }
@ -118,7 +118,7 @@ protected override DrawableHitObject<TaikoHitObject, TaikoJudgement> GetVisualRe
if (centreHit != null) if (centreHit != null)
{ {
if (h.IsStrong) if (h.IsStrong)
return new DrawableStrongCentreHit(centreHit); return new DrawableCentreHitStrong(centreHit);
return new DrawableCentreHit(centreHit); return new DrawableCentreHit(centreHit);
} }
@ -126,15 +126,13 @@ protected override DrawableHitObject<TaikoHitObject, TaikoJudgement> GetVisualRe
if (rimHit != null) if (rimHit != null)
{ {
if (h.IsStrong) if (h.IsStrong)
return new DrawableStrongRimHit(rimHit); return new DrawableRimHitStrong(rimHit);
return new DrawableRimHit(rimHit); return new DrawableRimHit(rimHit);
} }
var drumRoll = h as DrumRoll; var drumRoll = h as DrumRoll;
if (drumRoll != null) if (drumRoll != null)
{ {
if (h.IsStrong)
return new DrawableStrongDrumRoll(drumRoll);
return new DrawableDrumRoll(drumRoll); return new DrawableDrumRoll(drumRoll);
} }

View File

@ -85,7 +85,7 @@ public TaikoPlayfield()
{ {
new Container new Container
{ {
Padding = new MarginPadding { Left = hit_target_offset }, X = hit_target_offset,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Children = new Drawable[] Children = new Drawable[]
{ {

View File

@ -55,24 +55,25 @@
<Compile Include="Judgements\TaikoHitResult.cs" /> <Compile Include="Judgements\TaikoHitResult.cs" />
<Compile Include="Objects\BarLine.cs" /> <Compile Include="Objects\BarLine.cs" />
<Compile Include="Objects\Drawables\DrawableBarLine.cs" /> <Compile Include="Objects\Drawables\DrawableBarLine.cs" />
<Compile Include="Objects\Drawables\DrawableMajorBarLine.cs" /> <Compile Include="Objects\Drawables\DrawableBarLineMajor.cs" />
<Compile Include="Objects\CentreHit.cs" /> <Compile Include="Objects\CentreHit.cs" />
<Compile Include="Objects\Drawables\DrawableRimHit.cs" /> <Compile Include="Objects\Drawables\DrawableRimHit.cs" />
<Compile Include="Objects\Drawables\DrawableStrongRimHit.cs" /> <Compile Include="Objects\Drawables\DrawableRimHitStrong.cs" />
<Compile Include="Objects\Drawables\DrawableCentreHit.cs" /> <Compile Include="Objects\Drawables\DrawableCentreHit.cs" />
<Compile Include="Objects\Drawables\DrawableHit.cs" /> <Compile Include="Objects\Drawables\DrawableHit.cs" />
<Compile Include="Objects\Drawables\DrawableStrongDrumRoll.cs" /> <Compile Include="Objects\Drawables\DrawableCentreHitStrong.cs" />
<Compile Include="Objects\Drawables\DrawableStrongCentreHit.cs" /> <Compile Include="Objects\Drawables\DrawableHitStrong.cs" />
<Compile Include="Objects\Drawables\DrawableStrongHit.cs" /> <Compile Include="Objects\Drawables\Pieces\ElongatedCirclePiece.cs" />
<Compile Include="Objects\Drawables\Pieces\CentreHitSymbolPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\CentreHitSymbolPiece.cs" />
<Compile Include="Objects\Drawables\DrawableDrumRoll.cs" /> <Compile Include="Objects\Drawables\DrawableDrumRoll.cs" />
<Compile Include="Objects\Drawables\DrawableDrumRollTick.cs" /> <Compile Include="Objects\Drawables\DrawableDrumRollTick.cs" />
<Compile Include="Objects\Drawables\DrawableSwell.cs" /> <Compile Include="Objects\Drawables\DrawableSwell.cs" />
<Compile Include="Objects\Drawables\DrawableTaikoHitObject.cs" /> <Compile Include="Objects\Drawables\DrawableTaikoHitObject.cs" />
<Compile Include="Objects\Drawables\Pieces\RimHitSymbolPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\RimHitSymbolPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\StrongCirclePiece.cs" />
<Compile Include="Objects\Drawables\Pieces\CirclePiece.cs" /> <Compile Include="Objects\Drawables\Pieces\CirclePiece.cs" />
<Compile Include="Objects\Drawables\Pieces\SwellSymbolPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\SwellSymbolPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\TaikoPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\TickPiece.cs" />
<Compile Include="Objects\DrumRoll.cs" /> <Compile Include="Objects\DrumRoll.cs" />
<Compile Include="Objects\DrumRollTick.cs" /> <Compile Include="Objects\DrumRollTick.cs" />
<Compile Include="Objects\Hit.cs" /> <Compile Include="Objects\Hit.cs" />

View File

@ -215,7 +215,7 @@ private void handleTimingPoints(Beatmap beatmap, string val)
{ {
Time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo), Time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo),
BeatLength = beatLength > 0 ? beatLength : 0, BeatLength = beatLength > 0 ? beatLength : 0,
VelocityAdjustment = beatLength < 0 ? -beatLength / 100.0 : 1, SpeedMultiplier = beatLength < 0 ? -beatLength / 100.0 : 1,
TimingChange = split.Length <= 6 || split[6][0] == '1', TimingChange = split.Length <= 6 || split[6][0] == '1',
KiaiMode = (effectFlags & 1) > 0, KiaiMode = (effectFlags & 1) > 0,
OmitFirstBarLine = (effectFlags & 8) > 0, OmitFirstBarLine = (effectFlags & 8) > 0,

View File

@ -5,18 +5,14 @@ namespace osu.Game.Beatmaps.Timing
{ {
public class ControlPoint public class ControlPoint
{ {
public static ControlPoint Default = new ControlPoint
{
BeatLength = 500,
TimingChange = true,
};
public TimeSignatures TimeSignature; public TimeSignatures TimeSignature;
public double Time; public double Time;
public double BeatLength; public double BeatLength = 500;
public double VelocityAdjustment; public double SpeedMultiplier = 1;
public bool TimingChange; public bool TimingChange = true;
public bool KiaiMode; public bool KiaiMode;
public bool OmitFirstBarLine; public bool OmitFirstBarLine;
public ControlPoint Clone() => (ControlPoint)MemberwiseClone();
} }
} }

View File

@ -10,8 +10,8 @@ public class TimingInfo
{ {
public readonly List<ControlPoint> ControlPoints = new List<ControlPoint>(); public readonly List<ControlPoint> ControlPoints = new List<ControlPoint>();
public double BPMMaximum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderBy(c => c.BeatLength).FirstOrDefault() ?? ControlPoint.Default).BeatLength; public double BPMMaximum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderBy(c => c.BeatLength).FirstOrDefault() ?? new ControlPoint()).BeatLength;
public double BPMMinimum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? ControlPoint.Default).BeatLength; public double BPMMinimum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? new ControlPoint()).BeatLength;
public double BPMMode => BPMAt(ControlPoints.Where(c => c.BeatLength != 0).GroupBy(c => c.BeatLength).OrderByDescending(grp => grp.Count()).First().First().Time); public double BPMMode => BPMAt(ControlPoints.Where(c => c.BeatLength != 0).GroupBy(c => c.BeatLength).OrderByDescending(grp => grp.Count()).First().First().Time);
public double BPMAt(double time) public double BPMAt(double time)
@ -29,7 +29,7 @@ public double SpeedMultiplierAt(double time)
ControlPoint overridePoint; ControlPoint overridePoint;
ControlPoint timingPoint = TimingPointAt(time, out overridePoint); ControlPoint timingPoint = TimingPointAt(time, out overridePoint);
return overridePoint?.VelocityAdjustment ?? timingPoint?.VelocityAdjustment ?? 1; return overridePoint?.SpeedMultiplier ?? timingPoint?.SpeedMultiplier ?? 1;
} }
/// <summary> /// <summary>
@ -74,7 +74,7 @@ public ControlPoint TimingPointAt(double time, out ControlPoint overridePoint)
else break; else break;
} }
return timingPoint ?? ControlPoint.Default; return timingPoint ?? new ControlPoint();
} }
} }
} }

View File

@ -9,12 +9,12 @@ public class BeatmapDifficulty
{ {
[PrimaryKey, AutoIncrement] [PrimaryKey, AutoIncrement]
public int ID { get; set; } public int ID { get; set; }
public float DrainRate { get; set; } public float DrainRate { get; set; } = 5;
public float CircleSize { get; set; } public float CircleSize { get; set; } = 5;
public float OverallDifficulty { get; set; } public float OverallDifficulty { get; set; } = 5;
public float ApproachRate { get; set; } public float ApproachRate { get; set; } = 5;
public float SliderMultiplier { get; set; } public float SliderMultiplier { get; set; } = 1;
public float SliderTickRate { get; set; } public float SliderTickRate { get; set; } = 1;
/// <summary> /// <summary>
/// Maps a difficulty value [0, 10] to a two-piece linear range of values. /// Maps a difficulty value [0, 10] to a two-piece linear range of values.

View File

@ -15,6 +15,7 @@ public class BeatmapInfo : IEquatable<BeatmapInfo>
[PrimaryKey, AutoIncrement] [PrimaryKey, AutoIncrement]
public int ID { get; set; } public int ID { get; set; }
//TODO: should be in database
public int BeatmapVersion; public int BeatmapVersion;
public int? OnlineBeatmapID { get; set; } public int? OnlineBeatmapID { get; set; }

View File

@ -60,8 +60,8 @@ public float TriangleScale
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
for (int i = 0; i < aimTriangleCount; i++)
addTriangle(true); addTriangles(true);
} }
private int aimTriangleCount => (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio); private int aimTriangleCount => (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio);
@ -83,8 +83,8 @@ protected override void Update()
t.Expire(); t.Expire();
} }
while (CreateNewTriangles && Children.Count() < aimTriangleCount) if (CreateNewTriangles)
addTriangle(false); addTriangles(false);
} }
protected virtual Triangle CreateTriangle() protected virtual Triangle CreateTriangle()
@ -113,12 +113,16 @@ protected virtual Triangle CreateTriangle()
protected virtual Color4 GetTriangleShade() => Interpolation.ValueAt(RNG.NextSingle(), ColourDark, ColourLight, 0, 1); protected virtual Color4 GetTriangleShade() => Interpolation.ValueAt(RNG.NextSingle(), ColourDark, ColourLight, 0, 1);
private void addTriangle(bool randomY) private void addTriangles(bool randomY)
{ {
var sprite = CreateTriangle(); int addCount = aimTriangleCount - Children.Count();
float triangleHeight = sprite.DrawHeight / DrawHeight; for (int i = 0; i < addCount; i++)
sprite.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() * (1 + triangleHeight) - triangleHeight : 1); {
Add(sprite); var sprite = CreateTriangle();
float triangleHeight = sprite.DrawHeight / DrawHeight;
sprite.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() * (1 + triangleHeight) - triangleHeight : 1);
Add(sprite);
}
} }
} }
} }

View File

@ -56,13 +56,17 @@ protected virtual void PlaySample()
Sample?.Play(); Sample?.Play();
} }
protected override void LoadComplete() [BackgroundDependencyLoader]
private void load()
{ {
base.LoadComplete();
//we may be setting a custom judgement in test cases or what not. //we may be setting a custom judgement in test cases or what not.
if (Judgement == null) if (Judgement == null)
Judgement = CreateJudgement(); Judgement = CreateJudgement();
}
protected override void LoadComplete()
{
base.LoadComplete();
//force application of the state that was set before we loaded. //force application of the state that was set before we loaded.
UpdateState(State); UpdateState(State);

View File

@ -42,6 +42,16 @@ public abstract class HitRenderer : Container
/// </summary> /// </summary>
protected readonly KeyConversionInputManager KeyConversionInputManager; protected readonly KeyConversionInputManager KeyConversionInputManager;
/// <summary>
/// Whether we are currently providing the local user a gameplay cursor.
/// </summary>
public virtual bool ProvidingUserCursor => false;
/// <summary>
/// Whether we have a replay loaded currently.
/// </summary>
public bool HasReplayLoaded => InputManager.ReplayInputHandler != null;
/// <summary> /// <summary>
/// Whether all the HitObjects have been judged. /// Whether all the HitObjects have been judged.
/// </summary> /// </summary>
@ -157,6 +167,8 @@ public abstract class HitRenderer<TObject, TJudgement> : HitRenderer<TObject>
{ {
public event Action<TJudgement> OnJudgement; public event Action<TJudgement> OnJudgement;
public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor;
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
protected override bool AllObjectsJudged => Playfield.HitObjects.Children.All(h => h.Judgement.Result != HitResult.None); protected override bool AllObjectsJudged => Playfield.HitObjects.Children.All(h => h.Judgement.Result != HitResult.None);

View File

@ -22,6 +22,11 @@ public abstract class Playfield<TObject, TJudgement> : Container
internal Container<Drawable> ScaledContent; internal Container<Drawable> ScaledContent;
/// <summary>
/// Whether we are currently providing the local user a gameplay cursor.
/// </summary>
public virtual bool ProvidingUserCursor => false;
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
private readonly Container<Drawable> content; private readonly Container<Drawable> content;

View File

@ -314,7 +314,7 @@ protected override void UpdateAfterChildren()
if (intro?.ChildScreen != null) if (intro?.ChildScreen != null)
intro.ChildScreen.Padding = new MarginPadding { Top = Toolbar.Position.Y + Toolbar.DrawHeight }; intro.ChildScreen.Padding = new MarginPadding { Top = Toolbar.Position.Y + Toolbar.DrawHeight };
Cursor.State = currentScreen == null || currentScreen.HasLocalCursorDisplayed ? Visibility.Hidden : Visibility.Visible; Cursor.State = currentScreen?.HasLocalCursorDisplayed == false ? Visibility.Visible : Visibility.Hidden;
} }
private void screenAdded(Screen newScreen) private void screenAdded(Screen newScreen)

View File

@ -155,7 +155,7 @@ public string Text
public void MarkAllRead() public void MarkAllRead()
{ {
notifications.Children.ForEach(n => n.Read = true); notifications?.Children.ForEach(n => n.Read = true);
} }
} }
} }

View File

@ -31,9 +31,7 @@ public class Player : OsuScreen
internal override bool ShowOverlays => false; internal override bool ShowOverlays => false;
internal override bool HasLocalCursorDisplayed => !hasReplayLoaded && !IsPaused; internal override bool HasLocalCursorDisplayed => !IsPaused && HitRenderer.ProvidingUserCursor;
private bool hasReplayLoaded => HitRenderer.InputManager.ReplayInputHandler != null;
public BeatmapInfo BeatmapInfo; public BeatmapInfo BeatmapInfo;
@ -305,7 +303,7 @@ protected override bool OnExiting(Screen next)
{ {
if (pauseOverlay == null) return false; if (pauseOverlay == null) return false;
if (hasReplayLoaded) if (HitRenderer.HasReplayLoaded)
return false; return false;
if (pauseOverlay.State != Visibility.Visible && !canPause) return true; if (pauseOverlay.State != Visibility.Visible && !canPause) return true;

View File

@ -179,11 +179,11 @@ public void SelectRandom()
public void Filter(FilterCriteria newCriteria = null, bool debounce = true) public void Filter(FilterCriteria newCriteria = null, bool debounce = true)
{ {
if (!IsLoaded) return;
if (newCriteria != null) if (newCriteria != null)
criteria = newCriteria; criteria = newCriteria;
if (!IsLoaded) return;
Action perform = delegate Action perform = delegate
{ {
filterTask = null; filterTask = null;

View File

@ -173,9 +173,9 @@ private void load(OsuColour colours, OsuGame osu)
{ {
sortTabs.AccentColour = colours.GreenLight; sortTabs.AccentColour = colours.GreenLight;
if (osu != null) if (osu != null) playMode.BindTo(osu.PlayMode);
playMode.BindTo(osu.PlayMode);
playMode.ValueChanged += val => FilterChanged?.Invoke(CreateCriteria()); playMode.ValueChanged += val => FilterChanged?.Invoke(CreateCriteria());
playMode.TriggerChange();
} }
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;

View File

@ -167,13 +167,12 @@ private void load(BeatmapDatabase beatmaps, AudioManager audio, DialogOverlay di
BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, promptDelete, Key.Number4, float.MaxValue); BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, promptDelete, Key.Number4, float.MaxValue);
} }
if (osu != null)
playMode.BindTo(osu.PlayMode);
playMode.ValueChanged += val => Beatmap.PreferredPlayMode = val;
if (database == null) if (database == null)
database = beatmaps; database = beatmaps;
playMode.ValueChanged += val => { if (Beatmap != null) Beatmap.PreferredPlayMode = val; };
if (osu != null) playMode.BindTo(osu.PlayMode);
database.BeatmapSetAdded += onBeatmapSetAdded; database.BeatmapSetAdded += onBeatmapSetAdded;
database.BeatmapSetRemoved += onBeatmapSetRemoved; database.BeatmapSetRemoved += onBeatmapSetRemoved;