diff --git a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs index 31a1bc369e..0569fcf3c3 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs @@ -12,6 +12,7 @@ using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Osu.Objects; using osu.Game.Modes.Osu.Objects.Drawables; using osu.Framework.Graphics.Containers; +using osu.Game.Modes; namespace osu.Desktop.VisualTests.Tests { @@ -50,7 +51,7 @@ namespace osu.Desktop.VisualTests.Tests Anchor = Anchor.Centre, Origin = Anchor.Centre, Depth = -i, - State = ArmedState.Armed, + State = ArmedState.Hit, }; approachContainer.Add(d.ApproachCircle.CreateProxy()); diff --git a/osu.Game.Mode.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Mode.Osu/Objects/Drawables/DrawableHitCircle.cs index 8c73955ca9..dc5d5bf2a6 100644 --- a/osu.Game.Mode.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Mode.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -2,6 +2,7 @@ //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.ComponentModel; using osu.Framework.Graphics; using osu.Framework.Graphics.Transformations; using osu.Game.Modes.Objects.Drawables; @@ -10,7 +11,7 @@ using OpenTK; namespace osu.Game.Modes.Osu.Objects.Drawables { - public class DrawableHitCircle : DrawableHitObject + public class DrawableHitCircle : DrawableOsuHitObject { private OsuHitObject osuObject; @@ -39,7 +40,12 @@ namespace osu.Game.Modes.Osu.Objects.Drawables circle = new CirclePiece { Colour = osuObject.Colour, - Hit = Hit, + Hit = () => + { + ((PositionalJudgementInfo)Judgement).PositionOffset = Vector2.Zero; //todo: set to correct value + UpdateJudgement(true); + return true; + }, }, number = new NumberPiece(), ring = new RingPiece(), @@ -66,6 +72,38 @@ namespace osu.Game.Modes.Osu.Objects.Drawables UpdateState(State); } + double hit50 = 150; + double hit100 = 80; + double hit300 = 30; + + protected override void CheckJudgement(bool userTriggered) + { + if (!userTriggered) + { + if (Judgement.TimeOffset > hit50) + Judgement.Result = HitResult.Miss; + return; + } + + double hitOffset = Math.Abs(Judgement.TimeOffset); + + if (hitOffset < hit50) + { + Judgement.Result = HitResult.Hit; + + OsuJudgementInfo osuInfo = Judgement as OsuJudgementInfo; + + if (hitOffset < hit300) + osuInfo.Score = OsuScoreResult.Hit300; + else if (hitOffset < hit100) + osuInfo.Score = OsuScoreResult.Hit100; + else if (hitOffset < hit50) + osuInfo.Score = OsuScoreResult.Hit50; + } + else + Judgement.Result = HitResult.Miss; + } + protected override void UpdateState(ArmedState state) { if (!IsLoaded) return; @@ -73,7 +111,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables Flush(true); //move to DrawableHitObject ApproachCircle.Flush(true); - double t = HitTime ?? osuObject.StartTime; + double t = osuObject.EndTime + Judgement.TimeOffset; Alpha = 0; @@ -103,14 +141,27 @@ namespace osu.Game.Modes.Osu.Objects.Drawables switch (state) { - case ArmedState.Disarmed: - Delay(osuObject.Duration + 200); - FadeOut(200); + case ArmedState.Idle: + Delay(osuObject.Duration + 500); + FadeOut(500); explosion?.Expire(); explosion = null; break; - case ArmedState.Armed: + case ArmedState.Miss: + ring.FadeOut(); + circle.FadeOut(); + number.FadeOut(); + glow.FadeOut(); + + explosion?.Expire(); + explosion = null; + + Schedule(() => Add(explosion = new HitExplosion((OsuJudgementInfo)Judgement))); + + FadeOut(800); + break; + case ArmedState.Hit: const double flash_in = 30; flash.FadeTo(0.8f, flash_in); @@ -119,7 +170,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables explode.FadeIn(flash_in); - Schedule(() => Add(explosion = new HitExplosion(Judgement.Hit300))); + Schedule(() => Add(explosion = new HitExplosion((OsuJudgementInfo)Judgement))); Delay(flash_in, true); diff --git a/osu.Game.Mode.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Mode.Osu/Objects/Drawables/DrawableOsuHitObject.cs new file mode 100644 index 0000000000..d416732327 --- /dev/null +++ b/osu.Game.Mode.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using osu.Game.Modes.Objects; +using osu.Game.Modes.Objects.Drawables; + +namespace osu.Game.Modes.Osu.Objects.Drawables +{ + public class DrawableOsuHitObject : DrawableHitObject + { + public DrawableOsuHitObject(OsuHitObject hitObject) + : base(hitObject) + { + } + + public override JudgementInfo CreateJudgementInfo() => new OsuJudgementInfo(); + + protected override void UpdateState(ArmedState state) + { + throw new NotImplementedException(); + } + } + + public class OsuJudgementInfo : PositionalJudgementInfo + { + public OsuScoreResult Score; + public ComboResult Combo; + } + + public enum ComboResult + { + [Description(@"")] + None, + [Description(@"Good")] + Good, + [Description(@"Amazing")] + Perfect + } + + public enum OsuScoreResult + { + [Description(@"Miss")] + Miss, + [Description(@"50")] + Hit50, + [Description(@"100")] + Hit100, + [Description(@"300")] + Hit300, + [Description(@"500")] + Hit500 + } +} diff --git a/osu.Game.Mode.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Mode.Osu/Objects/Drawables/DrawableSlider.cs index 693f05cd1f..55e4781cd3 100644 --- a/osu.Game.Mode.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Mode.Osu/Objects/Drawables/DrawableSlider.cs @@ -5,7 +5,7 @@ using OpenTK; namespace osu.Game.Modes.Osu.Objects.Drawables { - class DrawableSlider : DrawableHitObject + class DrawableSlider : DrawableOsuHitObject { public DrawableSlider(Slider h) : base(h) { @@ -18,7 +18,6 @@ namespace osu.Game.Modes.Osu.Objects.Drawables Add(new CirclePiece { Colour = h.Colour, - Hit = Hit, Position = h.Curve.PositionAt(i) - h.Position //non-relative? }); } diff --git a/osu.Game.Mode.Osu/Objects/Drawables/HitExplosion.cs b/osu.Game.Mode.Osu/Objects/Drawables/HitExplosion.cs index a3067a1eab..a0ab68fa99 100644 --- a/osu.Game.Mode.Osu/Objects/Drawables/HitExplosion.cs +++ b/osu.Game.Mode.Osu/Objects/Drawables/HitExplosion.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Transformations; +using osu.Game.Modes.Objects.Drawables; using OpenTK; namespace osu.Game.Modes.Osu.Objects.Drawables @@ -12,7 +13,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables private SpriteText line1; private SpriteText line2; - public HitExplosion(Judgement judgement, ComboJudgement comboJudgement = ComboJudgement.None) + public HitExplosion(OsuJudgementInfo judgement) { AutoSizeAxes = Axes.Both; Anchor = Anchor.Centre; @@ -27,13 +28,13 @@ namespace osu.Game.Modes.Osu.Objects.Drawables { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Text = judgement.GetDescription(), + Text = judgement.Score.GetDescription(), Font = @"Venera", TextSize = 20, }, line2 = new SpriteText { - Text = comboJudgement.GetDescription(), + Text = judgement.Combo.GetDescription(), Font = @"Venera", TextSize = 14, } diff --git a/osu.Game.Mode.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Mode.Osu/Objects/Drawables/Pieces/CirclePiece.cs index c326c46553..fd55a8315a 100644 --- a/osu.Game.Mode.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Mode.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -49,8 +49,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - Hit?.Invoke(); - return true; + return Hit?.Invoke() ?? false; } } } \ No newline at end of file diff --git a/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj index e7efa413c4..97b4ebcb99 100644 --- a/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Mode.Osu/osu.Game.Modes.Osu.csproj @@ -41,6 +41,7 @@ + diff --git a/osu.Game/Modes/HitJudgementResolver.cs b/osu.Game/Modes/HitJudgementResolver.cs deleted file mode 100644 index 5f2afec369..0000000000 --- a/osu.Game/Modes/HitJudgementResolver.cs +++ /dev/null @@ -1,51 +0,0 @@ -//Copyright (c) 2007-2016 ppy Pty Ltd . -//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using osu.Game.Modes.Objects; -using OpenTK; - -namespace osu.Game.Modes -{ - public class HitJudgementResolver - { - public JudgementResult CheckJudgement(HitObject h) => new JudgementResult { Combo = ComboJudgement.None, Judgement = Judgement.Hit300 }; - } - - public struct JudgementResult - { - public ComboJudgement Combo; - public Judgement Judgement; - public float TimeOffset; - public Vector2 PositionOffset; - } - - public enum ComboJudgement - { - [Description(@"")] - None, - [Description(@"Good")] - Good, - [Description(@"Amazing")] - Perfect - } - - public enum Judgement - { - [Description(@"Miss")] - Miss, - [Description(@"50")] - Hit50, - [Description(@"100")] - Hit100, - [Description(@"300")] - Hit300, - [Description(@"500")] - Hit500 - } -} diff --git a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs index 387d5d061a..3508d6c3a6 100644 --- a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs @@ -2,22 +2,26 @@ //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.ComponentModel; +using System.Diagnostics; using osu.Framework; using osu.Framework.Graphics.Containers; +using OpenTK; +using Container = osu.Framework.Graphics.Containers.Container; namespace osu.Game.Modes.Objects.Drawables { public abstract class DrawableHitObject : Container, IStateful { //todo: move to a more central implementation. this logic should not be at a drawable level. - public Action OnHit; - public Action OnMiss; - - public Func AllowHit; + public Action OnHit; + public Action OnMiss; public Container ChildObjects; - public JudgementResult Result; + protected JudgementInfo Judgement; + + public abstract JudgementInfo CreateJudgementInfo(); public HitObject HitObject; @@ -41,36 +45,55 @@ namespace osu.Game.Modes.Objects.Drawables } } - protected double? HitTime; - - protected virtual bool Hit() + protected override void LoadComplete() { - if (State != ArmedState.Disarmed) + base.LoadComplete(); + + Judgement = CreateJudgementInfo(); + } + + /// + /// Process a hit of this hitobject. Carries out judgement. + /// + /// Preliminary judgement information provided by the hit source. + /// Whether a hit was processed. + protected bool UpdateJudgement(bool userTriggered) + { + if (Judgement.Result != null) return false; - if (AllowHit?.Invoke(this) == false) + Judgement.TimeOffset = Time.Current - HitObject.EndTime; + + CheckJudgement(userTriggered); + + if (Judgement.Result == null) return false; - HitTime = Time.Current; + switch (Judgement.Result) + { + default: + State = ArmedState.Hit; + OnHit?.Invoke(this, Judgement); + break; + case HitResult.Miss: + State = ArmedState.Miss; + OnMiss?.Invoke(this, Judgement); + break; + } - State = ArmedState.Armed; return true; } - private bool counted; + protected virtual void CheckJudgement(bool userTriggered) + { + + } protected override void Update() { base.Update(); - if (Time.Current >= HitObject.EndTime && !counted) - { - counted = true; - if (state == ArmedState.Armed) - OnHit?.Invoke(this); - else - OnMiss?.Invoke(this); - } + UpdateJudgement(false); } protected abstract void UpdateState(ArmedState state); @@ -78,7 +101,27 @@ namespace osu.Game.Modes.Objects.Drawables public enum ArmedState { - Disarmed, - Armed + Idle, + Hit, + Miss + } + + public class PositionalJudgementInfo : JudgementInfo + { + public Vector2 PositionOffset; + } + + public class JudgementInfo + { + public HitResult? Result; + public double TimeOffset; + } + + public enum HitResult + { + [Description(@"Miss")] + Miss, + [Description(@"Hit")] + Hit, } } diff --git a/osu.Game/Modes/Ruleset.cs b/osu.Game/Modes/Ruleset.cs index 6576159f62..d35aab6568 100644 --- a/osu.Game/Modes/Ruleset.cs +++ b/osu.Game/Modes/Ruleset.cs @@ -22,8 +22,6 @@ namespace osu.Game.Modes public abstract HitObjectParser CreateHitObjectParser(); - public virtual HitJudgementResolver CreateHitJudgement() => new HitJudgementResolver(); - public static void Register(Ruleset ruleset) => availableRulesets.TryAdd(ruleset.PlayMode, ruleset.GetType()); protected abstract PlayMode PlayMode { get; } diff --git a/osu.Game/Modes/UI/HitRenderer.cs b/osu.Game/Modes/UI/HitRenderer.cs index f160356cb4..d37cf545a8 100644 --- a/osu.Game/Modes/UI/HitRenderer.cs +++ b/osu.Game/Modes/UI/HitRenderer.cs @@ -75,12 +75,12 @@ namespace osu.Game.Modes.UI } } - private void onMiss(DrawableHitObject obj) + private void onMiss(DrawableHitObject obj, JudgementInfo judgement) { OnMiss?.Invoke(obj.HitObject); } - private void onHit(DrawableHitObject obj) + private void onHit(DrawableHitObject obj, JudgementInfo judgement) { OnHit?.Invoke(obj.HitObject); } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 8691097ebf..66cec7405e 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -90,7 +90,7 @@ namespace osu.Game.Screens.Play hitRenderer.OnMiss += delegate (HitObject h) { scoreOverlay.OnMiss(h); }; if (Autoplay) - hitRenderer.Schedule(() => hitRenderer.DrawableObjects.ForEach(h => h.State = ArmedState.Armed)); + hitRenderer.Schedule(() => hitRenderer.DrawableObjects.ForEach(h => h.State = ArmedState.Hit)); Children = new Drawable[] { diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9c2bbf086a..00c46a7ce3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -64,7 +64,6 @@ -