Fix clashing namespace.

This commit is contained in:
Dean Herbert
2017-04-04 12:38:55 +09:00
parent 353cf2a026
commit 2df360a5e9
23 changed files with 82 additions and 82 deletions

View File

@ -0,0 +1,81 @@
// 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;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
/// <summary>
/// A line that scrolls alongside hit objects in the playfield and visualises control points.
/// </summary>
public class DrawableBarLine : Container
{
/// <summary>
/// The width of the line tracker.
/// </summary>
private const float tracker_width = 2f;
/// <summary>
/// Fade out time calibrated to a pre-empt of 1000ms.
/// </summary>
private const float base_fadeout_time = 100f;
/// <summary>
/// The visual line tracker.
/// </summary>
protected Box Tracker;
/// <summary>
/// The bar line.
/// </summary>
protected readonly BarLine BarLine;
public DrawableBarLine(BarLine barLine)
{
BarLine = barLine;
Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre;
RelativePositionAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
Width = tracker_width;
Children = new[]
{
Tracker = new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
EdgeSmoothness = new Vector2(0.5f, 0),
Alpha = 0.75f
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
LifetimeStart = BarLine.StartTime - BarLine.PreEmpt * 2;
LifetimeEnd = BarLine.StartTime + BarLine.PreEmpt;
Delay(BarLine.StartTime - Time.Current);
FadeOut(base_fadeout_time * BarLine.PreEmpt / 1000);
}
private void updateScrollPosition(double time) => MoveToX((float)((BarLine.StartTime - time) / BarLine.PreEmpt));
protected override void Update()
{
base.Update();
updateScrollPosition(Time.Current);
}
}
}

View File

@ -0,0 +1,31 @@
// 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.Allocation;
using osu.Game.Graphics;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public class DrawableCentreHit : DrawableHit
{
protected override Key[] HitKeys { get; } = { Key.F, Key.J };
public DrawableCentreHit(Hit hit)
: base(hit)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Circle.AccentColour = colours.PinkDarker;
}
protected override CirclePiece CreateCirclePiece() => new CirclePiece
{
Children = new[] { new CentreHitSymbolPiece() }
};
}
}

View File

@ -0,0 +1,116 @@
// 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.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public class DrawableDrumRoll : DrawableTaikoHitObject
{
/// <summary>
/// Number of rolling hits required to reach the dark/final accent colour.
/// </summary>
private const int rolling_hits_for_dark_accent = 5;
private readonly DrumRoll drumRoll;
private readonly CirclePiece circle;
private Color4 accentDarkColour;
/// <summary>
/// Rolling number of tick hits. This increases for hits and decreases for misses.
/// </summary>
private int rollingHits;
public DrawableDrumRoll(DrumRoll 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)
{
var newTick = new DrawableDrumRollTick(tick)
{
X = (float)((tick.StartTime - HitObject.StartTime) / drumRoll.Duration)
};
newTick.OnJudgement += onTickJudgement;
AddNested(newTick);
Add(newTick);
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
circle.AccentColour = AccentColour = colours.YellowDark;
accentDarkColour = colours.YellowDarker;
}
protected override void LoadComplete()
{
base.LoadComplete();
// This is naive, however it's based on the reasoning that the hit target
// 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.
// Thus, using PreEmpt here is enough for the drum roll to completely scroll out.
LifetimeEnd = drumRoll.EndTime + drumRoll.PreEmpt;
}
private void onTickJudgement(DrawableHitObject<TaikoHitObject, TaikoJudgement> obj)
{
if (obj.Judgement.Result == HitResult.Hit)
rollingHits++;
else
rollingHits--;
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);
circle.FadeAccent(newAccent, 100);
}
protected override void CheckJudgement(bool userTriggered)
{
if (userTriggered)
return;
if (Judgement.TimeOffset < 0)
return;
int countHit = NestedHitObjects.Count(o => o.Judgement.Result == HitResult.Hit);
if (countHit > drumRoll.RequiredGoodHits)
{
Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = countHit >= drumRoll.RequiredGreatHits ? TaikoHitResult.Great : TaikoHitResult.Good;
}
else
Judgement.Result = HitResult.Miss;
}
protected override void UpdateState(ArmedState state)
{
}
protected virtual CirclePiece CreateCirclePiece() => new CirclePiece();
}
}

View File

@ -0,0 +1,105 @@
// 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;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public class DrawableDrumRollTick : DrawableTaikoHitObject
{
/// <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)
: 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 void CheckJudgement(bool userTriggered)
{
if (!userTriggered)
{
if (Judgement.TimeOffset > tick.HitWindow)
Judgement.Result = HitResult.Miss;
return;
}
if (Math.Abs(Judgement.TimeOffset) < tick.HitWindow)
{
Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Great;
}
}
protected override void UpdateState(ArmedState state)
{
switch (state)
{
case ArmedState.Hit:
bodyContainer.ScaleTo(0, 100, EasingTypes.OutQuint);
break;
}
}
protected override void UpdateScrollPosition(double time)
{
// Ticks don't move
}
protected override bool HandleKeyPress(Key key)
{
return Judgement.Result == HitResult.None && UpdateJudgement(true);
}
}
}

View File

@ -0,0 +1,115 @@
// 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 System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public abstract class DrawableHit : DrawableTaikoHitObject
{
/// <summary>
/// A list of keys which can result in hits for this HitObject.
/// </summary>
protected abstract Key[] HitKeys { get; }
protected override Container<Drawable> Content => bodyContainer;
protected readonly CirclePiece Circle;
private readonly Hit hit;
private readonly Container bodyContainer;
/// <summary>
/// Whether the last key pressed is a valid hit key.
/// </summary>
private bool validKeyPressed;
protected DrawableHit(Hit 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)
{
if (!userTriggered)
{
if (Judgement.TimeOffset > hit.HitWindowGood)
Judgement.Result = HitResult.Miss;
return;
}
double hitOffset = Math.Abs(Judgement.TimeOffset);
if (hitOffset > hit.HitWindowMiss)
return;
if (!validKeyPressed)
Judgement.Result = HitResult.Miss;
else if (hitOffset < hit.HitWindowGood)
{
Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = hitOffset < hit.HitWindowGreat ? TaikoHitResult.Great : TaikoHitResult.Good;
}
else
Judgement.Result = HitResult.Miss;
}
protected override bool HandleKeyPress(Key key)
{
if (Judgement.Result != HitResult.None)
return false;
validKeyPressed = HitKeys.Contains(key);
return UpdateJudgement(true);
}
protected override void UpdateState(ArmedState state)
{
Delay(HitObject.StartTime - Time.Current + Judgement.TimeOffset, true);
switch (State)
{
case ArmedState.Idle:
Delay(hit.HitWindowMiss);
break;
case ArmedState.Miss:
FadeOut(100);
break;
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);
break;
}
Expire();
}
protected abstract CirclePiece CreateCirclePiece();
}
}

View File

@ -0,0 +1,57 @@
// 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;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public class DrawableMajorBarLine : DrawableBarLine
{
/// <summary>
/// The vertical offset of the triangles from the line tracker.
/// </summary>
private const float triangle_offfset = 10f;
/// <summary>
/// The size of the triangles.
/// </summary>
private const float triangle_size = 20f;
public DrawableMajorBarLine(BarLine barLine)
: base(barLine)
{
Add(new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Children = new[]
{
new EquilateralTriangle
{
Name = "Top",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Position = new Vector2(0, -triangle_offfset),
Size = new Vector2(-triangle_size),
EdgeSmoothness = new Vector2(1),
},
new EquilateralTriangle
{
Name = "Bottom",
Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre,
Position = new Vector2(0, triangle_offfset),
Size = new Vector2(triangle_size),
EdgeSmoothness = new Vector2(1),
}
}
});
Tracker.Alpha = 1f;
}
}
}

View File

@ -0,0 +1,31 @@
// 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.Allocation;
using osu.Game.Graphics;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public class DrawableRimHit : DrawableHit
{
protected override Key[] HitKeys { get; } = { Key.D, Key.K };
public DrawableRimHit(Hit hit)
: base(hit)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Circle.AccentColour = colours.BlueDarker;
}
protected override CirclePiece CreateCirclePiece() => new CirclePiece
{
Children = new[] { new RimHitSymbolPiece() }
};
}
}

View File

@ -0,0 +1,31 @@
// 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.Allocation;
using osu.Game.Graphics;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public class DrawableStrongCentreHit : DrawableStrongHit
{
protected override Key[] HitKeys { get; } = { Key.F, Key.J };
public DrawableStrongCentreHit(Hit hit)
: base(hit)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Circle.AccentColour = colours.PinkDarker;
}
protected override CirclePiece CreateCirclePiece() => new StrongCirclePiece
{
Children = new[] { new CentreHitSymbolPiece() }
};
}
}

View File

@ -0,0 +1,20 @@
// 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

@ -0,0 +1,89 @@
// 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 System.Linq;
using osu.Framework.Input;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public abstract class DrawableStrongHit : 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 Key firstHitKey;
protected DrawableStrongHit(Hit hit)
: base(hit)
{
}
protected override TaikoJudgement CreateJudgement() => new TaikoStrongHitJudgement();
protected override void CheckJudgement(bool userTriggered)
{
if (Judgement.Result == HitResult.None)
{
base.CheckJudgement(userTriggered);
return;
}
if (!userTriggered)
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)
Judgement.SecondHit = true;
}
protected override bool HandleKeyPress(Key key)
{
// Check if we've handled the first key
if (Judgement.Result == HitResult.None)
{
// First key hasn't been handled yet, attempt to handle it
bool handled = base.HandleKeyPress(key);
if (handled)
{
firstHitTime = Time.Current;
firstHitKey = key;
}
return handled;
}
// If we've already hit the second key, don't handle this object any further
if (Judgement.SecondHit)
return false;
// Don't handle represses of the first key
if (firstHitKey == key)
return false;
// Don't handle invalid hit key presses
if (!HitKeys.Contains(key))
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);
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
firstKeyHeld = state.Keyboard.Keys.Contains(firstHitKey);
return base.OnKeyDown(state, args);
}
}
}

View File

@ -0,0 +1,31 @@
// 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.Allocation;
using osu.Game.Graphics;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public class DrawableStrongRimHit : DrawableStrongHit
{
protected override Key[] HitKeys { get; } = { Key.D, Key.K };
public DrawableStrongRimHit(Hit hit)
: base(hit)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Circle.AccentColour = colours.BlueDarker;
}
protected override CirclePiece CreateCirclePiece() => new StrongCirclePiece
{
Children = new[] { new RimHitSymbolPiece() }
};
}
}

View File

@ -0,0 +1,244 @@
// 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 System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public class DrawableSwell : DrawableTaikoHitObject
{
/// <summary>
/// Invoked when the swell has reached the hit target, i.e. when CurrentTime >= StartTime.
/// This is only ever invoked once.
/// </summary>
public event Action OnStart;
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 Swell swell;
private readonly Container bodyContainer;
private readonly CircularContainer targetRing;
private readonly CircularContainer expandingRing;
private readonly CirclePiece circlePiece;
private readonly Key[] rimKeys = { Key.D, Key.K };
private readonly Key[] centreKeys = { Key.F, Key.J };
private Key[] lastKeySet;
/// <summary>
/// The amount of times the user has hit this swell.
/// </summary>
private int userHits;
private bool hasStarted;
private readonly SwellSymbolPiece symbol;
public DrawableSwell(Swell swell)
: base(swell)
{
this.swell = swell;
Children = new Drawable[]
{
bodyContainer = new Container
{
Children = new Drawable[]
{
expandingRing = new CircularContainer
{
Name = "Expanding ring",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Alpha = 0,
Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2),
BlendingMode = BlendingMode.Additive,
Masking = true,
Children = new []
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = inner_ring_alpha,
}
}
},
targetRing = new CircularContainer
{
Name = "Target ring (thick border)",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2),
Masking = true,
BorderThickness = target_ring_thick_border,
BlendingMode = BlendingMode.Additive,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
},
new CircularContainer
{
Name = "Target ring (thin border)",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = target_ring_thin_border,
BorderColour = Color4.White,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
}
}
},
circlePiece = new CirclePiece
{
Children = new []
{
symbol = new SwellSymbolPiece()
}
}
}
}
};
circlePiece.KiaiMode = HitObject.Kiai;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
circlePiece.AccentColour = colours.YellowDark;
expandingRing.Colour = colours.YellowLight;
targetRing.BorderColour = colours.YellowDark.Opacity(0.25f);
}
protected override void CheckJudgement(bool userTriggered)
{
if (userTriggered)
{
userHits++;
var completion = (float)userHits / swell.RequiredHits;
expandingRing.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50);
expandingRing.Delay(50);
expandingRing.FadeTo(completion / 8, 2000, EasingTypes.OutQuint);
expandingRing.DelayReset();
symbol.RotateTo((float)(completion * swell.Duration / 8), 4000, 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)
{
Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Great;
}
}
else
{
if (Judgement.TimeOffset < 0)
return;
//TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP
if (userHits > swell.RequiredHits / 2)
{
Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Good;
}
else
Judgement.Result = HitResult.Miss;
}
}
protected override void UpdateState(ArmedState state)
{
const float preempt = 100;
Delay(HitObject.StartTime - Time.Current - preempt, true);
targetRing.ScaleTo(target_ring_scale, preempt * 4, EasingTypes.OutQuint);
Delay(preempt, true);
Delay(Judgement.TimeOffset + swell.Duration, true);
const float out_transition_time = 300;
switch (state)
{
case ArmedState.Hit:
bodyContainer.ScaleTo(1.4f, out_transition_time);
break;
}
FadeOut(out_transition_time, EasingTypes.Out);
Expire();
}
protected override void UpdateScrollPosition(double time)
{
// Make the swell stop at the hit target
double t = Math.Min(HitObject.StartTime, time);
if (t == HitObject.StartTime && !hasStarted)
{
OnStart?.Invoke();
hasStarted = true;
}
base.UpdateScrollPosition(t);
}
protected override bool HandleKeyPress(Key key)
{
if (Judgement.Result != HitResult.None)
return false;
// Don't handle keys before the swell starts
if (Time.Current < HitObject.StartTime)
return false;
// Find the keyset which this key corresponds to
var keySet = rimKeys.Contains(key) ? rimKeys : centreKeys;
// Ensure alternating keysets
if (keySet == lastKeySet)
return false;
lastKeySet = keySet;
UpdateJudgement(true);
return true;
}
}
}

View File

@ -0,0 +1,70 @@
// 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.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public abstract class DrawableTaikoHitObject : DrawableHitObject<TaikoHitObject, TaikoJudgement>
{
/// <summary>
/// A list of keys which this hit object will accept. These are the standard Taiko keys for now.
/// These should be moved to bindings later.
/// </summary>
private readonly List<Key> validKeys = new List<Key>(new[] { Key.D, Key.F, Key.J, Key.K });
protected DrawableTaikoHitObject(TaikoHitObject hitObject)
: base(hitObject)
{
Anchor = Anchor.CentreLeft;
Origin = Anchor.CentreLeft;
RelativePositionAxes = Axes.X;
}
protected override void LoadComplete()
{
LifetimeStart = HitObject.StartTime - HitObject.PreEmpt * 2;
base.LoadComplete();
}
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
/// <summary>
/// Sets the scroll position of the DrawableHitObject relative to the offset between
/// a time value and the HitObject's StartTime.
/// </summary>
/// <param name="time"></param>
protected virtual void UpdateScrollPosition(double time)
{
MoveToX((float)((HitObject.StartTime - time) / HitObject.PreEmpt));
}
protected override void Update()
{
UpdateScrollPosition(Time.Current);
}
protected virtual bool HandleKeyPress(Key key) => false;
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
// Make sure we don't handle held-down keys
if (args.Repeat)
return false;
// Check if we've pressed a valid taiko key
if (!validKeys.Contains(args.Key))
return false;
// Handle it!
return HandleKeyPress(args.Key);
}
}
}

View File

@ -0,0 +1,31 @@
// 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;
namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{
/// <summary>
/// The symbol used for centre hit pieces.
/// </summary>
public class CentreHitSymbolPiece : CircularContainer
{
public CentreHitSymbolPiece()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Size = new Vector2(CirclePiece.SYMBOL_INNER_SIZE);
Masking = true;
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both
}
};
}
}
}

View File

@ -0,0 +1,158 @@
// 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.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using OpenTK.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{
/// <summary>
/// A circle piece which is used uniformly through osu!taiko to visualise hitobjects.
/// <para>
/// The body of this piece will overshoot its parent by <see cref="CirclePiece.Height"/> to form
/// a rounded (_[-Width-]_) figure such that a regular "circle" is the result of a parent with Width = 0.
/// </para>
/// </summary>
public class CirclePiece : Container, IHasAccentColour
{
public const float SYMBOL_SIZE = TaikoHitObject.CIRCLE_RADIUS * 2f * 0.45f;
public const float SYMBOL_BORDER = 8;
public const float SYMBOL_INNER_SIZE = SYMBOL_SIZE - 2 * SYMBOL_BORDER;
private Color4 accentColour;
/// <summary>
/// The colour of the inner circle and outer glows.
/// </summary>
public Color4 AccentColour
{
get { return accentColour; }
set
{
accentColour = value;
background.Colour = AccentColour;
resetEdgeEffects();
}
}
private bool kiaiMode;
/// <summary>
/// Whether Kiai mode effects are enabled for this circle piece.
/// </summary>
public bool KiaiMode
{
get { return kiaiMode; }
set
{
kiaiMode = value;
resetEdgeEffects();
}
}
public override Anchor Origin
{
get { return Anchor.CentreLeft; }
set { throw new InvalidOperationException($"{nameof(CirclePiece)} must always use CentreLeft origin."); }
}
protected override Container<Drawable> Content => SymbolContainer;
protected readonly Container SymbolContainer;
private readonly Container background;
private readonly Container innerLayer;
public CirclePiece()
{
RelativeSizeAxes = Axes.X;
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",
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[]
{
new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
},
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,
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
},
SymbolContainer = new Container
{
Name = "Symbol",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
}
});
}
protected override void Update()
{
// Add the overshoot to compensate for corner radius
innerLayer.Width = DrawWidth + DrawHeight;
}
private void resetEdgeEffects()
{
background.EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Colour = AccentColour,
Radius = KiaiMode ? 50 : 8
};
}
}
}

View File

@ -0,0 +1,36 @@
// 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
{
/// <summary>
/// The symbol used for rim hit pieces.
/// </summary>
public class RimHitSymbolPiece : CircularContainer
{
public RimHitSymbolPiece()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Size = new Vector2(CirclePiece.SYMBOL_SIZE);
BorderThickness = CirclePiece.SYMBOL_BORDER;
BorderColour = Color4.White;
Masking = true;
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
};
}
}
}

View File

@ -0,0 +1,25 @@
// 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,24 @@
// 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.Game.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{
/// <summary>
/// The symbol used for swell pieces.
/// </summary>
public class SwellSymbolPiece : TextAwesome
{
public SwellSymbolPiece()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
UseFullGlyphHeight = true;
TextSize = CirclePiece.SYMBOL_INNER_SIZE;
Icon = FontAwesome.fa_asterisk;
Shadow = false;
}
}
}