mirror of
https://github.com/osukey/osukey.git
synced 2025-08-04 15:16:38 +09:00
Mode -> Ruleset.
This commit is contained in:
9
osu.Game.Rulesets.Taiko/Objects/BarLine.cs
Normal file
9
osu.Game.Rulesets.Taiko/Objects/BarLine.cs
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects
|
||||
{
|
||||
public class BarLine : TaikoHitObject
|
||||
{
|
||||
}
|
||||
}
|
9
osu.Game.Rulesets.Taiko/Objects/CentreHit.cs
Normal file
9
osu.Game.Rulesets.Taiko/Objects/CentreHit.cs
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects
|
||||
{
|
||||
public class CentreHit : Hit
|
||||
{
|
||||
}
|
||||
}
|
80
osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
Normal file
80
osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
Normal file
@ -0,0 +1,80 @@
|
||||
// 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.Rulesets.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
|
||||
}
|
||||
};
|
||||
|
||||
LifetimeStart = BarLine.StartTime - BarLine.ScrollTime * 2;
|
||||
LifetimeEnd = BarLine.StartTime + BarLine.ScrollTime;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Delay(BarLine.StartTime - Time.Current);
|
||||
FadeOut(base_fadeout_time * BarLine.ScrollTime / 1000);
|
||||
}
|
||||
|
||||
private void updateScrollPosition(double time) => MoveToX((float)((BarLine.StartTime - time) / BarLine.ScrollTime));
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
updateScrollPosition(Time.Current);
|
||||
}
|
||||
}
|
||||
}
|
@ -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.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
public class DrawableBarLineMajor : 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 DrawableBarLineMajor(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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
// 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.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
public class DrawableCentreHit : DrawableHit
|
||||
{
|
||||
protected override Key[] HitKeys { get; } = { Key.F, Key.J };
|
||||
|
||||
public DrawableCentreHit(Hit hit)
|
||||
: base(hit)
|
||||
{
|
||||
MainPiece.Add(new CentreHitSymbolPiece());
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
MainPiece.AccentColour = colours.PinkDarker;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
// 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.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
public class DrawableCentreHitStrong : DrawableHitStrong
|
||||
{
|
||||
protected override Key[] HitKeys { get; } = { Key.F, Key.J };
|
||||
|
||||
public DrawableCentreHitStrong(Hit hit)
|
||||
: base(hit)
|
||||
{
|
||||
MainPiece.Add(new CentreHitSymbolPiece());
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
MainPiece.AccentColour = colours.PinkDarker;
|
||||
}
|
||||
}
|
||||
}
|
109
osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
Normal file
109
osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
Normal file
@ -0,0 +1,109 @@
|
||||
// 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.MathUtils;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
public class DrawableDrumRoll : DrawableTaikoHitObject<DrumRoll>
|
||||
{
|
||||
/// <summary>
|
||||
/// Number of rolling hits required to reach the dark/final accent colour.
|
||||
/// </summary>
|
||||
private const int rolling_hits_for_dark_accent = 5;
|
||||
|
||||
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)
|
||||
{
|
||||
foreach (var tick in drumRoll.Ticks)
|
||||
{
|
||||
var newTick = new DrawableDrumRollTick(tick)
|
||||
{
|
||||
X = (float)((tick.StartTime - HitObject.StartTime) / HitObject.Duration)
|
||||
};
|
||||
|
||||
newTick.OnJudgement += onTickJudgement;
|
||||
|
||||
AddNested(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]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
MainPiece.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 = HitObject.EndTime + HitObject.ScrollTime;
|
||||
}
|
||||
|
||||
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);
|
||||
MainPiece.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 > HitObject.RequiredGoodHits)
|
||||
{
|
||||
Judgement.Result = HitResult.Hit;
|
||||
Judgement.TaikoResult = countHit >= HitObject.RequiredGreatHits ? TaikoHitResult.Great : TaikoHitResult.Good;
|
||||
}
|
||||
else
|
||||
Judgement.Result = HitResult.Miss;
|
||||
}
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
// 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.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
using OpenTK.Input;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
public class DrawableDrumRollTick : DrawableTaikoHitObject<DrumRollTick>
|
||||
{
|
||||
public DrawableDrumRollTick(DrumRollTick tick)
|
||||
: base(tick)
|
||||
{
|
||||
}
|
||||
|
||||
protected override TaikoPiece CreateMainPiece() => new TickPiece
|
||||
{
|
||||
Filled = HitObject.FirstTick
|
||||
};
|
||||
|
||||
protected override TaikoJudgement CreateJudgement() => new TaikoDrumRollTickJudgement { SecondHit = HitObject.IsStrong };
|
||||
|
||||
protected override void CheckJudgement(bool userTriggered)
|
||||
{
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (Judgement.TimeOffset > HitObject.HitWindow)
|
||||
Judgement.Result = HitResult.Miss;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.Abs(Judgement.TimeOffset) < HitObject.HitWindow)
|
||||
{
|
||||
Judgement.Result = HitResult.Hit;
|
||||
Judgement.TaikoResult = TaikoHitResult.Great;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
Content.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);
|
||||
}
|
||||
}
|
||||
}
|
109
osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
Normal file
109
osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
Normal file
@ -0,0 +1,109 @@
|
||||
// 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.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
public abstract class DrawableHit : DrawableTaikoHitObject<Hit>
|
||||
{
|
||||
/// <summary>
|
||||
/// A list of keys which can result in hits for this HitObject.
|
||||
/// </summary>
|
||||
protected abstract Key[] HitKeys { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the last key pressed is a valid hit key.
|
||||
/// </summary>
|
||||
private bool validKeyPressed;
|
||||
|
||||
protected DrawableHit(Hit hit)
|
||||
: base(hit)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void CheckJudgement(bool userTriggered)
|
||||
{
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (Judgement.TimeOffset > HitObject.HitWindowGood)
|
||||
Judgement.Result = HitResult.Miss;
|
||||
return;
|
||||
}
|
||||
|
||||
double hitOffset = Math.Abs(Judgement.TimeOffset);
|
||||
|
||||
if (hitOffset > HitObject.HitWindowMiss)
|
||||
return;
|
||||
|
||||
if (!validKeyPressed)
|
||||
Judgement.Result = HitResult.Miss;
|
||||
else if (hitOffset < HitObject.HitWindowGood)
|
||||
{
|
||||
Judgement.Result = HitResult.Hit;
|
||||
Judgement.TaikoResult = hitOffset < HitObject.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);
|
||||
|
||||
var circlePiece = MainPiece as CirclePiece;
|
||||
|
||||
circlePiece?.FlashBox.Flush();
|
||||
|
||||
switch (State)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
Delay(HitObject.HitWindowMiss);
|
||||
break;
|
||||
case ArmedState.Miss:
|
||||
FadeOut(100);
|
||||
break;
|
||||
case ArmedState.Hit:
|
||||
FadeOut(600);
|
||||
|
||||
var flash = circlePiece?.FlashBox;
|
||||
if (flash != null)
|
||||
{
|
||||
flash.FadeTo(0.9f);
|
||||
flash.FadeOut(300);
|
||||
}
|
||||
|
||||
|
||||
FadeOut(800);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Expire();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
// 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.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
using OpenTK.Input;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
public abstract class DrawableHitStrong : DrawableHit
|
||||
{
|
||||
/// <summary>
|
||||
/// The lenience for the second key press.
|
||||
/// This does not adjust by map difficulty in ScoreV2 yet.
|
||||
/// </summary>
|
||||
private const double second_hit_window = 30;
|
||||
|
||||
private double firstHitTime;
|
||||
private bool firstKeyHeld;
|
||||
private Key firstHitKey;
|
||||
|
||||
protected DrawableHitStrong(Hit hit)
|
||||
: base(hit)
|
||||
{
|
||||
}
|
||||
|
||||
protected override TaikoPiece CreateMainPiece() => new CirclePiece(true);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
27
osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs
Normal file
27
osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs
Normal file
@ -0,0 +1,27 @@
|
||||
// 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.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
public class DrawableRimHit : DrawableHit
|
||||
{
|
||||
protected override Key[] HitKeys { get; } = { Key.D, Key.K };
|
||||
|
||||
public DrawableRimHit(Hit hit)
|
||||
: base(hit)
|
||||
{
|
||||
MainPiece.Add(new RimHitSymbolPiece());
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
MainPiece.AccentColour = colours.BlueDarker;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
// 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.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
public class DrawableRimHitStrong : DrawableHitStrong
|
||||
{
|
||||
protected override Key[] HitKeys { get; } = { Key.D, Key.K };
|
||||
|
||||
public DrawableRimHitStrong(Hit hit)
|
||||
: base(hit)
|
||||
{
|
||||
MainPiece.Add(new RimHitSymbolPiece());
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
MainPiece.AccentColour = colours.BlueDarker;
|
||||
}
|
||||
}
|
||||
}
|
243
osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
Normal file
243
osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
Normal file
@ -0,0 +1,243 @@
|
||||
// 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.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
public class DrawableSwell : DrawableTaikoHitObject<Swell>
|
||||
{
|
||||
/// <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 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)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
bodyContainer = new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
expandingRing = new CircularContainer
|
||||
{
|
||||
Name = "Expanding ring",
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Alpha = 0,
|
||||
Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER),
|
||||
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.DEFAULT_CIRCLE_DIAMETER),
|
||||
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
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
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 / HitObject.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 * 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);
|
||||
|
||||
if (userHits == HitObject.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 > HitObject.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 + HitObject.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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
// 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.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||
using OpenTK;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
public abstract class DrawableTaikoHitObject<TaikoHitType> : DrawableHitObject<TaikoHitObject, TaikoJudgement>
|
||||
where TaikoHitType : TaikoHitObject
|
||||
{
|
||||
/// <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 });
|
||||
|
||||
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)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
|
||||
Anchor = Anchor.CentreLeft;
|
||||
Origin = Anchor.Custom;
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
RelativePositionAxes = Axes.X;
|
||||
|
||||
AddInternal(bodyContainer = new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new[]
|
||||
{
|
||||
MainPiece = CreateMainPiece()
|
||||
}
|
||||
});
|
||||
|
||||
MainPiece.KiaiMode = HitObject.Kiai;
|
||||
|
||||
LifetimeStart = HitObject.StartTime - HitObject.ScrollTime * 2;
|
||||
}
|
||||
|
||||
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
|
||||
|
||||
protected virtual TaikoPiece CreateMainPiece() => new CirclePiece(HitObject.IsStrong);
|
||||
|
||||
/// <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) => X = (float)((HitObject.StartTime - time) / HitObject.ScrollTime);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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.Rulesets.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
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
152
osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs
Normal file
152
osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs
Normal file
@ -0,0 +1,152 @@
|
||||
// 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.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
|
||||
{
|
||||
/// <summary>
|
||||
/// A circle piece which is used uniformly through osu!taiko to visualise hitobjects.
|
||||
/// <para>
|
||||
/// Note that this can actually be non-circle if the width is changed. See <see cref="ElongatedCirclePiece"/>
|
||||
/// for a usage example.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class CirclePiece : TaikoPiece
|
||||
{
|
||||
public const float SYMBOL_SIZE = TaikoHitObject.DEFAULT_CIRCLE_DIAMETER * 0.45f;
|
||||
public const float SYMBOL_BORDER = 8;
|
||||
public const float SYMBOL_INNER_SIZE = SYMBOL_SIZE - 2 * SYMBOL_BORDER;
|
||||
|
||||
/// <summary>
|
||||
/// The colour of the inner circle and outer glows.
|
||||
/// </summary>
|
||||
public override Color4 AccentColour
|
||||
{
|
||||
get { return base.AccentColour; }
|
||||
set
|
||||
{
|
||||
base.AccentColour = value;
|
||||
|
||||
background.Colour = AccentColour;
|
||||
|
||||
resetEdgeEffects();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether Kiai mode effects are enabled for this circle piece.
|
||||
/// </summary>
|
||||
public override bool KiaiMode
|
||||
{
|
||||
get { return base.KiaiMode; }
|
||||
set
|
||||
{
|
||||
base.KiaiMode = value;
|
||||
|
||||
resetEdgeEffects();
|
||||
}
|
||||
}
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
private readonly Container content;
|
||||
|
||||
private readonly Container background;
|
||||
|
||||
public Box FlashBox;
|
||||
|
||||
public CirclePiece(bool isStrong = false)
|
||||
{
|
||||
AddInternal(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[]
|
||||
{
|
||||
FlashBox = new Box
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.White,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true
|
||||
}
|
||||
}
|
||||
},
|
||||
content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Name = "Content",
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
});
|
||||
|
||||
if (isStrong)
|
||||
{
|
||||
Size *= TaikoHitObject.STRONG_CIRCLE_DIAMETER_SCALE;
|
||||
|
||||
//default for symbols etc.
|
||||
Content.Scale *= TaikoHitObject.STRONG_CIRCLE_DIAMETER_SCALE;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
//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()
|
||||
{
|
||||
background.EdgeEffect = new EdgeEffect
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = AccentColour,
|
||||
Radius = KiaiMode ? 50 : 8
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -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.Rulesets.Taiko.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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.Rulesets.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
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -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.Rulesets.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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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.Rulesets.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.DEFAULT_CIRCLE_DIAMETER);
|
||||
}
|
||||
}
|
||||
}
|
@ -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.Rulesets.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.DEFAULT_CIRCLE_DIAMETER / 16;
|
||||
|
||||
/// <summary>
|
||||
/// The size of a tick.
|
||||
/// </summary>
|
||||
private const float tick_size = TaikoHitObject.DEFAULT_CIRCLE_DIAMETER / 4;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
99
osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
Normal file
99
osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
Normal file
@ -0,0 +1,99 @@
|
||||
// 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.Rulesets.Objects.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Audio;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects
|
||||
{
|
||||
public class DrumRoll : TaikoHitObject, IHasEndTime
|
||||
{
|
||||
/// <summary>
|
||||
/// Drum roll distance that results in a duration of 1 speed-adjusted beat length.
|
||||
/// </summary>
|
||||
private const float base_distance = 100;
|
||||
|
||||
public double EndTime => StartTime + Duration;
|
||||
|
||||
public double Duration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Numer of ticks per beat length.
|
||||
/// </summary>
|
||||
public int TickRate = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Number of drum roll ticks required for a "Good" hit.
|
||||
/// </summary>
|
||||
public double RequiredGoodHits { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of drum roll ticks required for a "Great" hit.
|
||||
/// </summary>
|
||||
public double RequiredGreatHits { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Total number of drum roll ticks.
|
||||
/// </summary>
|
||||
public int TotalTicks => Ticks.Count();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the drum roll ticks if not initialized and returns them.
|
||||
/// </summary>
|
||||
public IEnumerable<DrumRollTick> Ticks => ticks ?? (ticks = createTicks());
|
||||
|
||||
private List<DrumRollTick> ticks;
|
||||
|
||||
/// <summary>
|
||||
/// The length (in milliseconds) between ticks of this drumroll.
|
||||
/// <para>Half of this value is the hit window of the ticks.</para>
|
||||
/// </summary>
|
||||
private double tickSpacing = 100;
|
||||
|
||||
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(timing, difficulty);
|
||||
|
||||
tickSpacing = timing.BeatLengthAt(StartTime) / TickRate;
|
||||
|
||||
RequiredGoodHits = TotalTicks * Math.Min(0.15, 0.05 + 0.10 / 6 * difficulty.OverallDifficulty);
|
||||
RequiredGreatHits = TotalTicks * Math.Min(0.30, 0.10 + 0.20 / 6 * difficulty.OverallDifficulty);
|
||||
}
|
||||
|
||||
private List<DrumRollTick> createTicks()
|
||||
{
|
||||
var ret = new List<DrumRollTick>();
|
||||
|
||||
if (tickSpacing == 0)
|
||||
return ret;
|
||||
|
||||
bool first = true;
|
||||
for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing)
|
||||
{
|
||||
ret.Add(new DrumRollTick
|
||||
{
|
||||
FirstTick = first,
|
||||
ScrollTime = ScrollTime,
|
||||
TickSpacing = tickSpacing,
|
||||
StartTime = t,
|
||||
IsStrong = IsStrong,
|
||||
Samples = Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}).ToList()
|
||||
});
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
24
osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs
Normal file
24
osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs
Normal 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
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects
|
||||
{
|
||||
public class DrumRollTick : TaikoHitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether this is the first (initial) tick of the slider.
|
||||
/// </summary>
|
||||
public bool FirstTick;
|
||||
|
||||
/// <summary>
|
||||
/// The length (in milliseconds) between this tick and the next.
|
||||
/// <para>Half of this value is the hit window of the tick.</para>
|
||||
/// </summary>
|
||||
public double TickSpacing;
|
||||
|
||||
/// <summary>
|
||||
/// The time allowed to hit this tick.
|
||||
/// </summary>
|
||||
public double HitWindow => TickSpacing / 2;
|
||||
}
|
||||
}
|
35
osu.Game.Rulesets.Taiko/Objects/Hit.cs
Normal file
35
osu.Game.Rulesets.Taiko/Objects/Hit.cs
Normal file
@ -0,0 +1,35 @@
|
||||
// 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.Beatmaps.Timing;
|
||||
using osu.Game.Database;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects
|
||||
{
|
||||
public class Hit : TaikoHitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The hit window that results in a "GREAT" hit.
|
||||
/// </summary>
|
||||
public double HitWindowGreat = 35;
|
||||
|
||||
/// <summary>
|
||||
/// The hit window that results in a "GOOD" hit.
|
||||
/// </summary>
|
||||
public double HitWindowGood = 80;
|
||||
|
||||
/// <summary>
|
||||
/// The hit window that results in a "MISS".
|
||||
/// </summary>
|
||||
public double HitWindowMiss = 95;
|
||||
|
||||
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(timing, difficulty);
|
||||
|
||||
HitWindowGreat = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 50, 35, 20);
|
||||
HitWindowGood = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 120, 80, 50);
|
||||
HitWindowMiss = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 135, 95, 70);
|
||||
}
|
||||
}
|
||||
}
|
9
osu.Game.Rulesets.Taiko/Objects/RimHit.cs
Normal file
9
osu.Game.Rulesets.Taiko/Objects/RimHit.cs
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects
|
||||
{
|
||||
public class RimHit : Hit
|
||||
{
|
||||
}
|
||||
}
|
19
osu.Game.Rulesets.Taiko/Objects/Swell.cs
Normal file
19
osu.Game.Rulesets.Taiko/Objects/Swell.cs
Normal file
@ -0,0 +1,19 @@
|
||||
// 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.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects
|
||||
{
|
||||
public class Swell : TaikoHitObject, IHasEndTime
|
||||
{
|
||||
public double EndTime => StartTime + Duration;
|
||||
|
||||
public double Duration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of hits required to complete the swell successfully.
|
||||
/// </summary>
|
||||
public int RequiredHits = 10;
|
||||
}
|
||||
}
|
67
osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
Normal file
67
osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
Normal file
@ -0,0 +1,67 @@
|
||||
// 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.Beatmaps.Timing;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects
|
||||
{
|
||||
public abstract class TaikoHitObject : HitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Diameter of a circle relative to the size of the <see cref="TaikoPlayfield"/>.
|
||||
/// </summary>
|
||||
public const float PLAYFIELD_RELATIVE_DIAMETER = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Scale multiplier for a strong circle.
|
||||
/// </summary>
|
||||
public const float STRONG_CIRCLE_DIAMETER_SCALE = 1.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Default circle diameter.
|
||||
/// </summary>
|
||||
public const float DEFAULT_CIRCLE_DIAMETER = TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT * PLAYFIELD_RELATIVE_DIAMETER;
|
||||
|
||||
/// <summary>
|
||||
/// Default strong circle diameter.
|
||||
/// </summary>
|
||||
public const float DEFAULT_STRONG_CIRCLE_DIAMETER = DEFAULT_CIRCLE_DIAMETER * STRONG_CIRCLE_DIAMETER_SCALE;
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
private const double scroll_time = 6000;
|
||||
|
||||
/// <summary>
|
||||
/// Our adjusted <see cref="scroll_time"/> taking into consideration local <see cref="ControlPoint.BeatLength"/> and other speed multipliers.
|
||||
/// </summary>
|
||||
public double ScrollTime;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this HitObject is a "strong" type.
|
||||
/// Strong hit objects give more points for hitting the hit object with both keys.
|
||||
/// </summary>
|
||||
public bool IsStrong;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this HitObject is in Kiai time.
|
||||
/// </summary>
|
||||
public bool Kiai { get; protected set; }
|
||||
|
||||
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(timing, difficulty);
|
||||
|
||||
ScrollTime = scroll_time * (timing.BeatLengthAt(StartTime) * timing.SpeedMultiplierAt(StartTime) / 1000) / difficulty.SliderMultiplier;
|
||||
|
||||
ControlPoint overridePoint;
|
||||
Kiai = timing.TimingPointAt(StartTime, out overridePoint).KiaiMode;
|
||||
|
||||
if (overridePoint != null)
|
||||
Kiai |= overridePoint.KiaiMode;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user