diff --git a/osu-framework b/osu-framework index d29abdf0d0..67d89a3601 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit d29abdf0d042d832f0849c013d14762db04730e7 +Subproject commit 67d89a36016f98c0ede576b859a2ccafe114fce8 diff --git a/osu.Desktop.Tests/Visual/TestCaseCatcher.cs b/osu.Desktop.Tests/Visual/TestCaseCatcher.cs index 0e92b5114b..3f57b5eeb9 100644 --- a/osu.Desktop.Tests/Visual/TestCaseCatcher.cs +++ b/osu.Desktop.Tests/Visual/TestCaseCatcher.cs @@ -1,7 +1,10 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Catch.UI; using OpenTK; @@ -9,20 +12,23 @@ namespace osu.Desktop.Tests.Visual { internal class TestCaseCatcher : OsuTestCase { - protected override void LoadComplete() + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) { - base.LoadComplete(); - Children = new Drawable[] { - new CatcherArea + new CatchInputManager(rulesets.GetRuleset(2)) { - RelativePositionAxes = Axes.Both, RelativeSizeAxes = Axes.Both, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Size = new Vector2(1, 0.2f), - } + Child = new CatcherArea + { + RelativePositionAxes = Axes.Both, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Size = new Vector2(1, 0.2f), + } + }, }; } } diff --git a/osu.Desktop.Tests/Visual/TestCaseGamefield.cs b/osu.Desktop.Tests/Visual/TestCaseGamefield.cs index 9d61c9ab0c..73c61f5669 100644 --- a/osu.Desktop.Tests/Visual/TestCaseGamefield.cs +++ b/osu.Desktop.Tests/Visual/TestCaseGamefield.cs @@ -11,11 +11,15 @@ using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Taiko; using osu.Game.Rulesets.Taiko.UI; using OpenTK; @@ -84,25 +88,25 @@ namespace osu.Desktop.Tests.Visual Clock = new FramedClock(), Children = new Drawable[] { - new OsuHitRenderer(beatmap, false) + new OsuRulesetContainer(new OsuRuleset(new RulesetInfo()), beatmap, false) { Scale = new Vector2(0.5f), Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft }, - new TaikoHitRenderer(beatmap, false) + new TaikoRulesetContainer(new TaikoRuleset(new RulesetInfo()),beatmap, false) { Scale = new Vector2(0.5f), Anchor = Anchor.TopRight, Origin = Anchor.TopRight }, - new CatchHitRenderer(beatmap, false) + new CatchRulesetContainer(new CatchRuleset(new RulesetInfo()),beatmap, false) { Scale = new Vector2(0.5f), Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft }, - new ManiaHitRenderer(beatmap, false) + new ManiaRulesetContainer(new ManiaRuleset(new RulesetInfo()),beatmap, false) { Scale = new Vector2(0.5f), Anchor = Anchor.BottomRight, diff --git a/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs b/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs index 79a9fab91c..43bd921cfc 100644 --- a/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs +++ b/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs @@ -50,25 +50,25 @@ namespace osu.Desktop.Tests.Visual WorkingBeatmap beatmap = new TestWorkingBeatmap(b); - TestHitRenderer horizontalHitRenderer; - Add(horizontalHitRenderer = new TestHitRenderer(Axes.X, beatmap, true)); + TestRulesetContainer horizontalRulesetContainer; + Add(horizontalRulesetContainer = new TestRulesetContainer(Axes.X, beatmap, true)); - TestHitRenderer verticalHitRenderer; - Add(verticalHitRenderer = new TestHitRenderer(Axes.Y, beatmap, true)); + TestRulesetContainer verticalRulesetContainer; + Add(verticalRulesetContainer = new TestRulesetContainer(Axes.Y, beatmap, true)); AddStep("Reverse direction", () => { - horizontalHitRenderer.Playfield.Reversed.Toggle(); - verticalHitRenderer.Playfield.Reversed.Toggle(); + horizontalRulesetContainer.Playfield.Reversed.Toggle(); + verticalRulesetContainer.Playfield.Reversed.Toggle(); }); } - private class TestHitRenderer : ScrollingHitRenderer + private class TestRulesetContainer : ScrollingRulesetContainer { private readonly Axes scrollingAxes; - public TestHitRenderer(Axes scrollingAxes, WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(beatmap, isForCurrentRuleset) + public TestRulesetContainer(Axes scrollingAxes, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(null, beatmap, isForCurrentRuleset) { this.scrollingAxes = scrollingAxes; } diff --git a/osu.Game.Rulesets.Catch/CatchInputManager.cs b/osu.Game.Rulesets.Catch/CatchInputManager.cs new file mode 100644 index 0000000000..eada5cf532 --- /dev/null +++ b/osu.Game.Rulesets.Catch/CatchInputManager.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Input; +using OpenTK.Input; + +namespace osu.Game.Rulesets.Catch +{ + public class CatchInputManager : ActionMappingInputManager + { + public CatchInputManager(RulesetInfo ruleset) : base(ruleset) + { + Mappings = new Dictionary + { + { Key.Z, CatchAction.MoveLeft }, + { Key.Left, CatchAction.MoveLeft }, + { Key.X, CatchAction.MoveRight }, + { Key.Right, CatchAction.MoveRight }, + { Key.LShift, CatchAction.Dash }, + { Key.RShift, CatchAction.Dash }, + }; + } + } + + public enum CatchAction + { + MoveLeft, + MoveRight, + Dash + } +} diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index dc13329bde..8d45ea8fa2 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch { public class CatchRuleset : Ruleset { - public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new CatchHitRenderer(beatmap, isForCurrentRuleset); + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new CatchRulesetContainer(this, beatmap, isForCurrentRuleset); public override IEnumerable GetModsFor(ModType type) { @@ -102,5 +102,10 @@ namespace osu.Game.Rulesets.Catch public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(); public override int LegacyID => 2; + + public CatchRuleset(RulesetInfo rulesetInfo) + : base(rulesetInfo) + { + } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs index aa92f36d23..62e5c094be 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -15,12 +16,16 @@ using OpenTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawable { - internal class DrawableFruit : DrawableHitObject + public class DrawableFruit : DrawableScrollingHitObject { + private const float pulp_size = 30; + private class Pulp : Circle, IHasAccentColour { public Pulp() { + Size = new Vector2(pulp_size); + EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, @@ -33,84 +38,91 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable } - public DrawableFruit(CatchBaseHit h) : base(h) + public DrawableFruit(CatchBaseHit h) + : base(h) { Origin = Anchor.Centre; - Size = new Vector2(50); - RelativePositionAxes = Axes.Both; - Position = new Vector2(h.Position, -0.1f); - Rotation = (float)(RNG.NextDouble() - 0.5f) * 40; + Size = new Vector2(pulp_size * 2, pulp_size * 2.6f); - Alpha = 0; + RelativePositionAxes = Axes.Both; + X = h.Position; + + Colour = new Color4(RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), 1); + + Rotation = (float)(RNG.NextDouble() - 0.5f) * 40; } + public Func CheckPosition; + [BackgroundDependencyLoader] private void load() { Children = new Framework.Graphics.Drawable[] { - new Box + //todo: share this more + new BufferedContainer { RelativeSizeAxes = Axes.Both, - Colour = Color4.Red, - }, - new Pulp - { - RelativePositionAxes = Axes.Both, - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - Scale = new Vector2(0.12f), - Y = 0.08f, - }, - new Pulp - { - RelativePositionAxes = Axes.Both, - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - Scale = new Vector2(0.32f), - Position = new Vector2(-0.16f, 0.3f), - }, - new Pulp - { - RelativePositionAxes = Axes.Both, - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - Scale = new Vector2(0.32f), - Position = new Vector2(0.16f, 0.3f), - }, - new Pulp - { - RelativePositionAxes = Axes.Both, - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - Scale = new Vector2(0.32f), - Position = new Vector2(0, 0.6f), - }, + CacheDrawnFrameBuffer = true, + Children = new Framework.Graphics.Drawable[] + { + new Pulp + { + RelativePositionAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Scale = new Vector2(0.6f), + }, + new Pulp + { + RelativePositionAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Y = -0.08f + }, + new Pulp + { + RelativePositionAxes = Axes.Both, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Y = -0.08f + }, + new Pulp + { + RelativePositionAxes = Axes.Both, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }, + } + } }; - - Alpha = 0; } protected override CatchJudgement CreateJudgement() => new CatchJudgement(); private const float preempt = 1000; + protected override void CheckJudgement(bool userTriggered) + { + if (Judgement.TimeOffset > 0) + Judgement.Result = CheckPosition?.Invoke(HitObject) ?? false ? HitResult.Hit : HitResult.Miss; + } + protected override void UpdateState(ArmedState state) { using (BeginAbsoluteSequence(HitObject.StartTime - preempt)) { - // default state - this.MoveToY(-0.1f).FadeOut(); - // animation - this.FadeIn(200).MoveToY(1, preempt); + this.FadeIn(200); } - Expire(true); + switch (state) + { + case ArmedState.Miss: + using (BeginAbsoluteSequence(HitObject.StartTime, true)) + this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out); + break; + } } } } diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 3f29547e46..33c1355823 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -14,11 +14,19 @@ namespace osu.Game.Rulesets.Catch.Scoring { } - public CatchScoreProcessor(HitRenderer hitRenderer) - : base(hitRenderer) + public CatchScoreProcessor(RulesetContainer rulesetContainer) + : base(rulesetContainer) { } + protected override void Reset() + { + base.Reset(); + + Health.Value = 1; + Accuracy.Value = 1; + } + protected override void OnNewJudgement(CatchJudgement judgement) { } diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 03aabc5632..4ea6d180f3 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -6,21 +6,35 @@ using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.UI; using OpenTK; using osu.Game.Rulesets.Catch.Judgements; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Catch.Objects.Drawable; +using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Catch.UI { - public class CatchPlayfield : Playfield + public class CatchPlayfield : ScrollingPlayfield { + protected override Container Content => content; + private readonly Container content; + private readonly CatcherArea catcherArea; + public CatchPlayfield() + : base(Axes.Y) { + Reversed.Value = true; + Size = new Vector2(1); Anchor = Anchor.TopCentre; Origin = Anchor.TopCentre; - Children = new Drawable[] + InternalChildren = new Drawable[] { - new CatcherArea + content = new Container + { + RelativeSizeAxes = Axes.Both, + }, + catcherArea = new CatcherArea { RelativeSizeAxes = Axes.Both, Anchor = Anchor.BottomLeft, @@ -29,5 +43,24 @@ namespace osu.Game.Rulesets.Catch.UI } }; } + + public override void Add(DrawableHitObject h) + { + base.Add(h); + + var fruit = (DrawableFruit)h; + fruit.CheckPosition = catcherArea.CheckIfWeCanCatch; + fruit.OnJudgement += Fruit_OnJudgement; + } + + private void Fruit_OnJudgement(DrawableHitObject obj) + { + if (obj.Judgement.Result == HitResult.Hit) + { + Vector2 screenPosition = obj.ScreenSpaceDrawQuad.Centre; + Remove(obj); + catcherArea.Add(obj, screenPosition); + } + } } -} \ No newline at end of file +} diff --git a/osu.Game.Rulesets.Catch/UI/CatchHitRenderer.cs b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs similarity index 70% rename from osu.Game.Rulesets.Catch/UI/CatchHitRenderer.cs rename to osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs index cd6adfeea9..27cc05c47a 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchHitRenderer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Rulesets.Beatmaps; using osu.Game.Rulesets.Catch.Beatmaps; @@ -14,10 +15,10 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Catch.UI { - public class CatchHitRenderer : HitRenderer + public class CatchRulesetContainer : ScrollingRulesetContainer { - public CatchHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(beatmap, isForCurrentRuleset) + public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) { } @@ -27,6 +28,8 @@ namespace osu.Game.Rulesets.Catch.UI protected override Playfield CreatePlayfield() => new CatchPlayfield(); + protected override PassThroughInputManager CreateActionMappingInputManager() => new CatchInputManager(Ruleset.RulesetInfo); + protected override DrawableHitObject GetVisualRepresentation(CatchBaseHit h) { if (h is Fruit) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 153302c1d1..776f2119ab 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -2,145 +2,185 @@ // 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.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input; +using osu.Framework.MathUtils; +using osu.Game.Rulesets.Catch.Judgements; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawable; +using osu.Game.Rulesets.Objects.Drawables; using OpenTK; -using OpenTK.Input; namespace osu.Game.Rulesets.Catch.UI { public class CatcherArea : Container { - public override bool HandleInput => true; + private Catcher catcher; - private Sprite catcher; + public void Add(DrawableHitObject fruit, Vector2 screenPosition) => catcher.AddToStack(fruit, screenPosition); - private Drawable createAdditiveFrame() => new Sprite - { - RelativePositionAxes = Axes.Both, - Anchor = Anchor.TopLeft, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - - Texture = catcher.Texture, - BlendingMode = BlendingMode.Additive, - Position = catcher.Position, - Scale = catcher.Scale, - }; + public bool CheckIfWeCanCatch(CatchBaseHit obj) => Math.Abs(catcher.Position.X - obj.Position) < catcher.DrawSize.X / DrawSize.X / 2; [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load() { Children = new Drawable[] { - catcher = new Sprite + catcher = new Catcher { RelativePositionAxes = Axes.Both, Anchor = Anchor.TopLeft, Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - X = 0.5f, - Texture = textures.Get(@"Play/Catch/fruit-catcher-idle"), - }, + } }; } - private bool leftPressed; - private bool rightPressed; - - private int currentDirection; - - private bool dashing; - - protected bool Dashing - { - get - { - return dashing; - } - set - { - if (value == dashing) return; - - dashing = value; - - if (dashing) - Schedule(addAdditiveSprite); - } - } - - private void addAdditiveSprite() - { - if (!dashing) return; - - var additive = createAdditiveFrame(); - - Add(additive); - - additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire(); - - Scheduler.AddDelayed(addAdditiveSprite, 50); - } - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - if (args.Repeat) return true; - - switch (args.Key) - { - case Key.Left: - currentDirection = -1; - leftPressed = true; - return true; - case Key.Right: - currentDirection = 1; - rightPressed = true; - return true; - case Key.ShiftLeft: - Dashing = true; - return true; - } - - return base.OnKeyDown(state, args); - } - - protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) - { - switch (args.Key) - { - case Key.Left: - currentDirection = rightPressed ? 1 : 0; - leftPressed = false; - return true; - case Key.Right: - currentDirection = leftPressed ? -1 : 0; - rightPressed = false; - return true; - case Key.ShiftLeft: - Dashing = false; - return true; - } - - return base.OnKeyUp(state, args); - } - protected override void Update() { base.Update(); - if (currentDirection == 0) return; + catcher.Size = new Vector2(DrawSize.Y); + } - float speed = Dashing ? 1.5f : 1; + private class Catcher : Container + { + private Texture texture; - catcher.Scale = new Vector2(Math.Sign(currentDirection), 1); - catcher.X = (float)MathHelper.Clamp(catcher.X + currentDirection * Clock.ElapsedFrameTime / 1800 * speed, 0, 1); + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + texture = textures.Get(@"Play/Catch/fruit-catcher-idle"); + + Child = createCatcherSprite(); + } + + private int currentDirection; + + private bool dashing; + + protected bool Dashing + { + get { return dashing; } + set + { + if (value == dashing) return; + + dashing = value; + + if (dashing) + Schedule(addAdditiveSprite); + } + } + + private void addAdditiveSprite() + { + if (!dashing) return; + + var additive = createCatcherSprite(); + + additive.RelativePositionAxes = Axes.Both; + additive.BlendingMode = BlendingMode.Additive; + additive.Position = Position; + additive.Scale = Scale; + + ((CatcherArea)Parent).Add(additive); + + additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire(); + + Scheduler.AddDelayed(addAdditiveSprite, 50); + } + + private Sprite createCatcherSprite() => new Sprite + { + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Texture = texture, + OriginPosition = new Vector2(DrawWidth / 2, 10) //temporary until the sprite is aligned correctly. + }; + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (args.Repeat) return true; + + if (state.Data is CatchAction) + { + switch ((CatchAction)state.Data) + { + case CatchAction.MoveLeft: + currentDirection--; + return true; + case CatchAction.MoveRight: + currentDirection++; + return true; + case CatchAction.Dash: + Dashing = true; + return true; + } + } + + return base.OnKeyDown(state, args); + } + + protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) + { + if (state.Data is CatchAction) + { + switch ((CatchAction)state.Data) + { + case CatchAction.MoveLeft: + currentDirection++; + return true; + case CatchAction.MoveRight: + currentDirection--; + return true; + case CatchAction.Dash: + Dashing = false; + return true; + } + } + + return base.OnKeyUp(state, args); + } + + protected override void Update() + { + base.Update(); + + if (currentDirection == 0) return; + + float speed = Dashing ? 1.5f : 1; + + Scale = new Vector2(Math.Sign(currentDirection), 1); + X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime / 1800 * speed, 0, 1); + } + + public void AddToStack(DrawableHitObject fruit, Vector2 absolutePosition) + { + fruit.RelativePositionAxes = Axes.None; + fruit.Position = new Vector2(ToLocalSpace(absolutePosition).X - DrawSize.X / 2, 0); + + fruit.Anchor = Anchor.TopCentre; + fruit.Origin = Anchor.BottomCentre; + fruit.Scale *= 0.7f; + fruit.LifetimeEnd = double.MaxValue; + fruit.Depth = (float)Time.Current; + + float distance = fruit.DrawSize.X / 2 * fruit.Scale.X; + + while (Children.OfType().Any(f => Vector2.DistanceSquared(f.Position, fruit.Position) < distance * distance)) + { + fruit.X += RNG.Next(-5, 5); + fruit.Y -= RNG.Next(0, 5); + } + + Add(fruit); + } } } } diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 003fe2763e..79ef5f4ba8 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -38,6 +38,7 @@ True + @@ -50,6 +51,7 @@ + @@ -58,7 +60,7 @@ - + diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index a8a89a57e0..50ad6960ae 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania { public class ManiaRuleset : Ruleset { - public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaHitRenderer(beatmap, isForCurrentRuleset); + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaRulesetContainer(this, beatmap, isForCurrentRuleset); public override IEnumerable GetModsFor(ModType type) { @@ -118,5 +118,10 @@ namespace osu.Game.Rulesets.Mania public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(); public override int LegacyID => 3; + + public ManiaRuleset(RulesetInfo rulesetInfo) + : base(rulesetInfo) + { + } } } diff --git a/osu.Game.Rulesets.Mania/Mods/IGenerateSpeedAdjustments.cs b/osu.Game.Rulesets.Mania/Mods/IGenerateSpeedAdjustments.cs index f179aa2ff8..954ee3f481 100644 --- a/osu.Game.Rulesets.Mania/Mods/IGenerateSpeedAdjustments.cs +++ b/osu.Game.Rulesets.Mania/Mods/IGenerateSpeedAdjustments.cs @@ -15,9 +15,9 @@ namespace osu.Game.Rulesets.Mania.Mods /// /// Applies this mod to a hit renderer. /// - /// The hit renderer to apply to. + /// The hit renderer to apply to. /// The per-column list of speed adjustments for hit objects. /// The list of speed adjustments for bar lines. - void ApplyToHitRenderer(ManiaHitRenderer hitRenderer, ref List[] hitObjectTimingChanges, ref List barlineTimingChanges); + void ApplyToRulesetContainer(ManiaRulesetContainer rulesetContainer, ref List[] hitObjectTimingChanges, ref List barlineTimingChanges); } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs index 1ba8ac4710..a054e0db56 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs @@ -20,12 +20,12 @@ namespace osu.Game.Rulesets.Mania.Mods public override FontAwesome Icon => FontAwesome.fa_sort_desc; - public void ApplyToHitRenderer(ManiaHitRenderer hitRenderer, ref List[] hitObjectTimingChanges, ref List barlineTimingChanges) + public void ApplyToRulesetContainer(ManiaRulesetContainer rulesetContainer, ref List[] hitObjectTimingChanges, ref List barlineTimingChanges) { // We have to generate one speed adjustment per hit object for gravity - foreach (ManiaHitObject obj in hitRenderer.Objects) + foreach (ManiaHitObject obj in rulesetContainer.Objects) { - MultiplierControlPoint controlPoint = hitRenderer.CreateControlPointAt(obj.StartTime); + MultiplierControlPoint controlPoint = rulesetContainer.CreateControlPointAt(obj.StartTime); // Beat length has too large of an effect for gravity, so we'll force it to a constant value for now controlPoint.TimingPoint.BeatLength = 1000; @@ -33,9 +33,9 @@ namespace osu.Game.Rulesets.Mania.Mods } // Like with hit objects, we need to generate one speed adjustment per bar line - foreach (DrawableBarLine barLine in hitRenderer.BarLines) + foreach (DrawableBarLine barLine in rulesetContainer.BarLines) { - var controlPoint = hitRenderer.CreateControlPointAt(barLine.HitObject.StartTime); + var controlPoint = rulesetContainer.CreateControlPointAt(barLine.HitObject.StartTime); // Beat length has too large of an effect for gravity, so we'll force it to a constant value for now controlPoint.TimingPoint.BeatLength = 1000; diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index f575342486..63b443319f 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -155,8 +155,8 @@ namespace osu.Game.Rulesets.Mania.Scoring { } - public ManiaScoreProcessor(HitRenderer hitRenderer) - : base(hitRenderer) + public ManiaScoreProcessor(RulesetContainer rulesetContainer) + : base(rulesetContainer) { } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs similarity index 91% rename from osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs rename to osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs index 0b8ad10b16..7f6b19f3bd 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs @@ -29,7 +29,7 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Mania.UI { - public class ManiaHitRenderer : ScrollingHitRenderer + public class ManiaRulesetContainer : ScrollingRulesetContainer { /// /// Preferred column count. This will only have an effect during the initialization of the play field. @@ -38,8 +38,8 @@ namespace osu.Game.Rulesets.Mania.UI public IEnumerable BarLines; - public ManiaHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(beatmap, isForCurrentRuleset) + public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) { // Generate the bar lines double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 3832f9ace5..fb0b2d907f 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -82,7 +82,7 @@ - + diff --git a/osu.Game.Rulesets.Osu/OsuKeyConversionInputManager.cs b/osu.Game.Rulesets.Osu/OsuKeyConversionInputManager.cs index d60aab90fb..10adca4e43 100644 --- a/osu.Game.Rulesets.Osu/OsuKeyConversionInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuKeyConversionInputManager.cs @@ -3,14 +3,13 @@ using System.Linq; using osu.Framework.Input; -using osu.Game.Screens.Play; using OpenTK.Input; using KeyboardState = osu.Framework.Input.KeyboardState; using MouseState = osu.Framework.Input.MouseState; namespace osu.Game.Rulesets.Osu { - public class OsuKeyConversionInputManager : KeyConversionInputManager + public class OsuKeyConversionInputManager : PassThroughInputManager { private bool leftViaKeyboard; private bool rightViaKeyboard; diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 9a1971d791..212c634aaf 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu { public class OsuRuleset : Ruleset { - public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new OsuHitRenderer(beatmap, isForCurrentRuleset); + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new OsuRulesetContainer(this, beatmap, isForCurrentRuleset); public override IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) => new[] { @@ -126,5 +126,10 @@ namespace osu.Game.Rulesets.Osu public override SettingsSubsection CreateSettings() => new OsuSettings(); public override int LegacyID => 0; + + public OsuRuleset(RulesetInfo rulesetInfo) + : base(rulesetInfo) + { + } } } diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index 079ee928af..856ca0c98d 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -18,8 +18,8 @@ namespace osu.Game.Rulesets.Osu.Scoring { } - public OsuScoreProcessor(HitRenderer hitRenderer) - : base(hitRenderer) + public OsuScoreProcessor(RulesetContainer rulesetContainer) + : base(rulesetContainer) { } diff --git a/osu.Game.Rulesets.Osu/UI/OsuHitRenderer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs similarity index 78% rename from osu.Game.Rulesets.Osu/UI/OsuHitRenderer.cs rename to osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs index e582d2fcd3..d029524a32 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuHitRenderer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Input; using OpenTK; using osu.Game.Beatmaps; using osu.Game.Rulesets.Beatmaps; @@ -12,14 +13,13 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; -using osu.Game.Screens.Play; namespace osu.Game.Rulesets.Osu.UI { - public class OsuHitRenderer : HitRenderer + public class OsuRulesetContainer : RulesetContainer { - public OsuHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(beatmap, isForCurrentRuleset) + public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) { } @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.UI protected override Playfield CreatePlayfield() => new OsuPlayfield(); - protected override KeyConversionInputManager CreateKeyConversionInputManager() => new OsuKeyConversionInputManager(); + protected override PassThroughInputManager CreateActionMappingInputManager() => new OsuKeyConversionInputManager(); protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h) { diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 684060c981..b24d5b3a4a 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -80,7 +80,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index 70df3d8317..647a1381c6 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -113,8 +113,8 @@ namespace osu.Game.Rulesets.Taiko.Scoring { } - public TaikoScoreProcessor(HitRenderer hitRenderer) - : base(hitRenderer) + public TaikoScoreProcessor(RulesetContainer rulesetContainer) + : base(rulesetContainer) { } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 83db9b35af..4d4bbb7bc5 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Taiko { public class TaikoRuleset : Ruleset { - public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new TaikoHitRenderer(beatmap, isForCurrentRuleset); + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new TaikoRulesetContainer(this, beatmap, isForCurrentRuleset); public override IEnumerable GetModsFor(ModType type) { @@ -103,5 +103,10 @@ namespace osu.Game.Rulesets.Taiko public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(); public override int LegacyID => 1; + + public TaikoRuleset(RulesetInfo rulesetInfo) + : base(rulesetInfo) + { + } } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index dca339f734..119cd64fef 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Taiko.UI public class TaikoPlayfield : Playfield { /// - /// Default height of a when inside a . + /// Default height of a when inside a . /// public const float DEFAULT_HEIGHT = 178; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs similarity index 92% rename from osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs rename to osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs index 570b4be488..29f563b735 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoHitRenderer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs @@ -21,10 +21,10 @@ using System.Linq; namespace osu.Game.Rulesets.Taiko.UI { - public class TaikoHitRenderer : HitRenderer + public class TaikoRulesetContainer : RulesetContainer { - public TaikoHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(beatmap, isForCurrentRuleset) + public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) { } @@ -93,7 +93,6 @@ namespace osu.Game.Rulesets.Taiko.UI return new Vector2(1, default_relative_height * aspectAdjust); } - public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this); protected override BeatmapConverter CreateBeatmapConverter() => new TaikoBeatmapConverter(); diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index f719f2002b..59c6f4db0a 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -91,7 +91,7 @@ - + diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index bd53a80555..2fb9a568f8 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -54,7 +54,7 @@ namespace osu.Game.Beatmaps private class DummyRulesetInfo : RulesetInfo { - public override Ruleset CreateInstance() => new DummyRuleset(); + public override Ruleset CreateInstance() => new DummyRuleset(this); private class DummyRuleset : Ruleset { @@ -62,7 +62,7 @@ namespace osu.Game.Beatmaps public override Mod GetAutoplayMod() => new ModAutoplay(); - public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) { throw new NotImplementedException(); } @@ -77,6 +77,11 @@ namespace osu.Game.Beatmaps public override string Description => "dummy"; public override IEnumerable CreateGameplayKeys() => new List(); + + public DummyRuleset(RulesetInfo rulesetInfo) + : base(rulesetInfo) + { + } } } } diff --git a/osu.Game/Input/ActionMappingInputManager.cs b/osu.Game/Input/ActionMappingInputManager.cs new file mode 100644 index 0000000000..c918982fab --- /dev/null +++ b/osu.Game/Input/ActionMappingInputManager.cs @@ -0,0 +1,74 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Input; +using osu.Game.Rulesets; +using OpenTK.Input; + +namespace osu.Game.Input +{ + /// + /// Maps custom action data of type and stores to . + /// + /// The type of the custom action. + public class ActionMappingInputManager : PassThroughInputManager + where T : struct + { + private readonly RulesetInfo ruleset; + + private readonly int? variant; + + /// + /// Create a new instance. + /// + /// A reference to identify the current . Used to lookup mappings. Null for global mappings. + /// An optional variant for the specified . Used when a ruleset has more than one possible keyboard layouts. + protected ActionMappingInputManager(RulesetInfo ruleset = null, int? variant = null) + { + this.ruleset = ruleset; + this.variant = variant; + } + + protected IDictionary Mappings { get; set; } + + [BackgroundDependencyLoader] + private void load(BindingStore bindings) + { + var rulesetId = ruleset?.ID; + foreach (var b in bindings.Query(b => b.RulesetID == rulesetId && b.Variant == variant)) + Mappings[b.Key] = (T)(object)b.Action; + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + mapKey(state, args.Key); + return base.OnKeyDown(state, args); + } + + protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) + { + mapKey(state, args.Key); + return base.OnKeyUp(state, args); + } + + private void mapKey(InputState state, Key key) + { + T mappedData; + if (Mappings.TryGetValue(key, out mappedData)) + state.Data = mappedData; + } + + private T parseStringRepresentation(string str) + { + T res; + + if (Enum.TryParse(str, out res)) + return res; + + return default(T); + } + } +} diff --git a/osu.Game/Input/Binding.cs b/osu.Game/Input/Binding.cs new file mode 100644 index 0000000000..e887d15a65 --- /dev/null +++ b/osu.Game/Input/Binding.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets; +using OpenTK.Input; +using SQLite.Net.Attributes; +using SQLiteNetExtensions.Attributes; + +namespace osu.Game.Input +{ + public class Binding + { + [ForeignKey(typeof(RulesetInfo))] + public int? RulesetID { get; set; } + + [Indexed] + public int? Variant { get; set; } + + public Key Key { get; set; } + + public int Action { get; set; } + } +} \ No newline at end of file diff --git a/osu.Game/Input/BindingStore.cs b/osu.Game/Input/BindingStore.cs new file mode 100644 index 0000000000..aa47bee068 --- /dev/null +++ b/osu.Game/Input/BindingStore.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Platform; +using osu.Game.Database; +using SQLite.Net; + +namespace osu.Game.Input +{ + public class BindingStore : DatabaseBackedStore + { + public BindingStore(SQLiteConnection connection, Storage storage = null) + : base(connection, storage) + { + } + + protected override void Prepare(bool reset = false) + { + Connection.CreateTable(); + } + + protected override Type[] ValidTypes => new[] + { + typeof(Binding) + }; + + } +} \ No newline at end of file diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 0dec4228de..b76235f3f4 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -19,6 +19,7 @@ using osu.Game.Online.API; using SQLite.Net; using osu.Framework.Graphics.Performance; using osu.Game.Database; +using osu.Game.Input; using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Rulesets.Scoring; @@ -37,6 +38,8 @@ namespace osu.Game protected ScoreStore ScoreStore; + protected BindingStore BindingStore; + protected override string MainResourceFile => @"osu.Game.Resources.dll"; public APIAccess API; @@ -104,6 +107,7 @@ namespace osu.Game dependencies.Cache(FileStore = new FileStore(connection, Host.Storage)); dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, connection, RulesetStore, Host)); dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, connection, Host, BeatmapManager)); + dependencies.Cache(BindingStore = new BindingStore(connection)); dependencies.Cache(new OsuColour()); //this completely overrides the framework default. will need to change once we make a proper FontStore. diff --git a/osu.Game/Rulesets/Mods/IApplicableMod.cs b/osu.Game/Rulesets/Mods/IApplicableMod.cs index 18e1ae4b3d..1957952720 100644 --- a/osu.Game/Rulesets/Mods/IApplicableMod.cs +++ b/osu.Game/Rulesets/Mods/IApplicableMod.cs @@ -7,16 +7,16 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Mods { /// - /// An interface for mods that are applied to a HitRenderer. + /// An interface for mods that are applied to a RulesetContainer. /// - /// The type of HitObject the HitRenderer contains. + /// The type of HitObject the RulesetContainer contains. public interface IApplicableMod where TObject : HitObject { /// - /// Applies the mod to a HitRenderer. + /// Applies the mod to a RulesetContainer. /// - /// The HitRenderer to apply the mod to. - void ApplyToHitRenderer(HitRenderer hitRenderer); + /// The RulesetContainer to apply the mod to. + void ApplyToRulesetContainer(RulesetContainer rulesetContainer); } } diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index 1217bf835f..ca120e446e 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -15,9 +15,9 @@ namespace osu.Game.Rulesets.Mods { protected abstract Score CreateReplayScore(Beatmap beatmap); - public void ApplyToHitRenderer(HitRenderer hitRenderer) + public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) { - hitRenderer.SetReplay(CreateReplayScore(hitRenderer.Beatmap)?.Replay); + rulesetContainer.SetReplay(CreateReplayScore(rulesetContainer.Beatmap)?.Replay); } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index b3be36a983..ba040403ba 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -15,12 +15,19 @@ namespace osu.Game.Rulesets { public abstract class Ruleset { + public readonly RulesetInfo RulesetInfo; + public virtual IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) => new BeatmapStatistic[] { }; public abstract IEnumerable GetModsFor(ModType type); public abstract Mod GetAutoplayMod(); + protected Ruleset(RulesetInfo rulesetInfo) + { + RulesetInfo = rulesetInfo; + } + /// /// Attempt to create a hit renderer for a beatmap /// @@ -28,7 +35,7 @@ namespace osu.Game.Rulesets /// Whether the hit renderer should assume the beatmap is for the current ruleset. /// Unable to successfully load the beatmap to be usable with this ruleset. /// - public abstract HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset); + public abstract RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset); public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap); diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index 84ecb7718e..bc23649ede 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -20,6 +20,6 @@ namespace osu.Game.Rulesets [Indexed] public bool Available { get; set; } - public virtual Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo)); + public virtual Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this); } } \ No newline at end of file diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 1564df1366..2956c11d7f 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets continue; foreach (Type rulesetType in rulesets) - instances.Add((Ruleset)Activator.CreateInstance(rulesetType)); + instances.Add((Ruleset)Activator.CreateInstance(rulesetType, new RulesetInfo())); } catch (Exception) { } } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index cd9089c859..5ccac2db32 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -150,13 +150,13 @@ namespace osu.Game.Rulesets.Scoring { } - protected ScoreProcessor(HitRenderer hitRenderer) + protected ScoreProcessor(RulesetContainer rulesetContainer) { - Judgements.Capacity = hitRenderer.Beatmap.HitObjects.Count; + Judgements.Capacity = rulesetContainer.Beatmap.HitObjects.Count; - hitRenderer.OnJudgement += AddJudgement; + rulesetContainer.OnJudgement += AddJudgement; - ComputeTargets(hitRenderer.Beatmap); + ComputeTargets(rulesetContainer.Beatmap); Reset(); } diff --git a/osu.Game/Rulesets/UI/HitRenderer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs similarity index 78% rename from osu.Game/Rulesets/UI/HitRenderer.cs rename to osu.Game/Rulesets/UI/RulesetContainer.cs index 13ea83dc48..387fa15191 100644 --- a/osu.Game/Rulesets/UI/HitRenderer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -14,6 +14,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using osu.Framework.Input; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using OpenTK; @@ -22,12 +23,12 @@ using osu.Game.Rulesets.Beatmaps; namespace osu.Game.Rulesets.UI { /// - /// Base HitRenderer. Doesn't hold objects. + /// Base RulesetContainer. Doesn't hold objects. /// - /// Should not be derived - derive instead. + /// Should not be derived - derive instead. /// /// - public abstract class HitRenderer : Container + public abstract class RulesetContainer : Container { /// /// Invoked when all the judgeable HitObjects have been judged. @@ -40,14 +41,14 @@ namespace osu.Game.Rulesets.UI public bool AspectAdjust = true; /// - /// The input manager for this HitRenderer. + /// The input manager for this RulesetContainer. /// internal readonly PlayerInputManager InputManager = new PlayerInputManager(); /// - /// The key conversion input manager for this HitRenderer. + /// The key conversion input manager for this RulesetContainer. /// - protected readonly KeyConversionInputManager KeyConversionInputManager; + protected readonly PassThroughInputManager KeyConversionInputManager; /// /// Whether we are currently providing the local user a gameplay cursor. @@ -66,9 +67,16 @@ namespace osu.Game.Rulesets.UI /// protected abstract bool AllObjectsJudged { get; } - internal HitRenderer() + protected readonly Ruleset Ruleset; + + /// + /// A visual representation of a . + /// + /// The ruleset being repesented. + internal RulesetContainer(Ruleset ruleset) { - KeyConversionInputManager = CreateKeyConversionInputManager(); + Ruleset = ruleset; + KeyConversionInputManager = CreateActionMappingInputManager(); KeyConversionInputManager.RelativeSizeAxes = Axes.Both; } @@ -87,7 +95,7 @@ namespace osu.Game.Rulesets.UI /// Creates a key conversion input manager. /// /// The input manager. - protected virtual KeyConversionInputManager CreateKeyConversionInputManager() => new KeyConversionInputManager(); + protected virtual PassThroughInputManager CreateActionMappingInputManager() => new PassThroughInputManager(); protected virtual FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new FramedReplayInputHandler(replay); @@ -105,14 +113,14 @@ namespace osu.Game.Rulesets.UI } /// - /// HitRenderer that applies conversion to Beatmaps. Does not contain a Playfield + /// RulesetContainer that applies conversion to Beatmaps. Does not contain a Playfield /// and does not load drawable hit objects. /// - /// Should not be derived - derive instead. + /// Should not be derived - derive instead. /// /// - /// The type of HitObject contained by this HitRenderer. - public abstract class HitRenderer : HitRenderer + /// The type of HitObject contained by this RulesetContainer. + public abstract class RulesetContainer : RulesetContainer where TObject : HitObject { /// @@ -133,11 +141,12 @@ namespace osu.Game.Rulesets.UI /// /// Creates a hit renderer for a beatmap. /// + /// The ruleset being repesented. /// The beatmap to create the hit renderer for. /// Whether to assume the beatmap is for the current ruleset. - internal HitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) + internal RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) : base(ruleset) { - Debug.Assert(beatmap != null, "HitRenderer initialized with a null beatmap."); + Debug.Assert(beatmap != null, "RulesetContainer initialized with a null beatmap."); Mods = beatmap.Mods.Value; @@ -171,7 +180,7 @@ namespace osu.Game.Rulesets.UI } /// - /// Applies the active mods to this HitRenderer. + /// Applies the active mods to this RulesetContainer. /// /// private void applyMods(IEnumerable mods) @@ -180,7 +189,7 @@ namespace osu.Game.Rulesets.UI return; foreach (var mod in mods.OfType>()) - mod.ApplyToHitRenderer(this); + mod.ApplyToRulesetContainer(this); } /// @@ -203,11 +212,11 @@ namespace osu.Game.Rulesets.UI } /// - /// A derivable HitRenderer that manages the Playfield and HitObjects. + /// A derivable RulesetContainer that manages the Playfield and HitObjects. /// - /// The type of HitObject contained by this HitRenderer. - /// The type of Judgement of DrawableHitObjects contained by this HitRenderer. - public abstract class HitRenderer : HitRenderer + /// The type of HitObject contained by this RulesetContainer. + /// The type of Judgement of DrawableHitObjects contained by this RulesetContainer. + public abstract class RulesetContainer : RulesetContainer where TObject : HitObject where TJudgement : Judgement { @@ -235,10 +244,11 @@ namespace osu.Game.Rulesets.UI /// /// Creates a hit renderer for a beatmap. /// + /// The ruleset being repesented. /// The beatmap to create the hit renderer for. /// Whether to assume the beatmap is for the current ruleset. - protected HitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(beatmap, isForCurrentRuleset) + protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) { InputManager.Add(content = new Container { @@ -324,12 +334,12 @@ namespace osu.Game.Rulesets.UI } /// - /// A derivable HitRenderer that manages the Playfield and HitObjects. + /// A derivable RulesetContainer that manages the Playfield and HitObjects. /// - /// The type of Playfield contained by this HitRenderer. - /// The type of HitObject contained by this HitRenderer. - /// The type of Judgement of DrawableHitObjects contained by this HitRenderer. - public abstract class HitRenderer : HitRenderer + /// The type of Playfield contained by this RulesetContainer. + /// The type of HitObject contained by this RulesetContainer. + /// The type of Judgement of DrawableHitObjects contained by this RulesetContainer. + public abstract class RulesetContainer : RulesetContainer where TObject : HitObject where TJudgement : Judgement where TPlayfield : Playfield @@ -342,10 +352,11 @@ namespace osu.Game.Rulesets.UI /// /// Creates a hit renderer for a beatmap. /// + /// The ruleset being repesented. /// The beatmap to create the hit renderer for. /// Whether to assume the beatmap is for the current ruleset. - protected HitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(beatmap, isForCurrentRuleset) + protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) { } } diff --git a/osu.Game/Rulesets/UI/ScrollingHitRenderer.cs b/osu.Game/Rulesets/UI/ScrollingRulesetContainer.cs similarity index 86% rename from osu.Game/Rulesets/UI/ScrollingHitRenderer.cs rename to osu.Game/Rulesets/UI/ScrollingRulesetContainer.cs index da4340ac7a..64e865473d 100644 --- a/osu.Game/Rulesets/UI/ScrollingHitRenderer.cs +++ b/osu.Game/Rulesets/UI/ScrollingRulesetContainer.cs @@ -17,23 +17,23 @@ using osu.Game.Rulesets.Timing; namespace osu.Game.Rulesets.UI { /// - /// A type of that supports a . - /// s inside this will scroll within the playfield. + /// A type of that supports a . + /// s inside this will scroll within the playfield. /// - public abstract class ScrollingHitRenderer : HitRenderer + public abstract class ScrollingRulesetContainer : RulesetContainer where TObject : HitObject where TJudgement : Judgement where TPlayfield : ScrollingPlayfield { /// /// Provides the default s that adjust the scrolling rate of s - /// inside this . + /// inside this . /// /// protected readonly SortedList DefaultControlPoints = new SortedList(Comparer.Default); - protected ScrollingHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(beatmap, isForCurrentRuleset) + protected ScrollingRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) { } diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index ea75c140db..cf6bc03c60 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -90,11 +90,11 @@ namespace osu.Game.Screens.Play } } - public virtual void BindHitRenderer(HitRenderer hitRenderer) + public virtual void BindRulesetContainer(RulesetContainer rulesetContainer) { - hitRenderer.InputManager.Add(KeyCounter.GetReceptor()); + rulesetContainer.InputManager.Add(KeyCounter.GetReceptor()); - replayLoaded = hitRenderer.HasReplayLoaded; + replayLoaded = rulesetContainer.HasReplayLoaded; // in the case a replay isn't loaded, we want some elements to only appear briefly. if (!replayLoaded) diff --git a/osu.Game/Screens/Play/KeyConversionInputManager.cs b/osu.Game/Screens/Play/KeyConversionInputManager.cs deleted file mode 100644 index f3ca764bd7..0000000000 --- a/osu.Game/Screens/Play/KeyConversionInputManager.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Input; - -namespace osu.Game.Screens.Play -{ - /// - /// An InputManager primarily used to map keys to new functions. - /// By default this does nothing; override TransformState to make alterations. - /// - public class KeyConversionInputManager : PassThroughInputManager - { - - } -} \ No newline at end of file diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a13e9ed369..00f1bd1b6b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Play internal override bool ShowOverlays => false; - internal override bool HasLocalCursorDisplayed => !pauseContainer.IsPaused && !HasFailed && HitRenderer.ProvidingUserCursor; + internal override bool HasLocalCursorDisplayed => !pauseContainer.IsPaused && !HasFailed && RulesetContainer.ProvidingUserCursor; public Action RestartRequested; @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Play private RulesetInfo ruleset; private ScoreProcessor scoreProcessor; - protected HitRenderer HitRenderer; + protected RulesetContainer RulesetContainer; #region User Settings @@ -66,7 +66,7 @@ namespace osu.Game.Screens.Play private HUDOverlay hudOverlay; private FailOverlay failOverlay; - private bool loadedSuccessfully => HitRenderer?.Objects.Any() == true; + private bool loadedSuccessfully => RulesetContainer?.Objects.Any() == true; [BackgroundDependencyLoader(permitNulls: true)] private void load(AudioManager audio, OsuConfigManager config, OsuGame osu) @@ -93,18 +93,18 @@ namespace osu.Game.Screens.Play try { - HitRenderer = rulesetInstance.CreateHitRendererWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID); + RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID); } catch (BeatmapInvalidForRulesetException) { - // we may fail to create a HitRenderer if the beatmap cannot be loaded with the user's preferred ruleset + // we may fail to create a RulesetContainer if the beatmap cannot be loaded with the user's preferred ruleset // let's try again forcing the beatmap's ruleset. ruleset = beatmap.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); - HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, true); + RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap, true); } - if (!HitRenderer.Objects.Any()) + if (!RulesetContainer.Objects.Any()) throw new InvalidOperationException("Beatmap contains no hit objects!"); } catch (Exception e) @@ -119,7 +119,7 @@ namespace osu.Game.Screens.Play adjustableSourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock(); decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; - var firstObjectTime = HitRenderer.Objects.First().StartTime; + var firstObjectTime = RulesetContainer.Objects.First().StartTime; decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn))); decoupledClock.ProcessFrame(); @@ -147,7 +147,7 @@ namespace osu.Game.Screens.Play FramedClock = offsetClock, OnRetry = Restart, OnQuit = Exit, - CheckCanPause = () => ValidForResume && !HasFailed && !HitRenderer.HasReplayLoaded, + CheckCanPause = () => ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded, Retries = RestartCount, OnPause = () => { hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused; @@ -164,7 +164,7 @@ namespace osu.Game.Screens.Play Clock = offsetClock, Children = new Drawable[] { - HitRenderer, + RulesetContainer, } }, hudOverlay = new HUDOverlay @@ -184,27 +184,27 @@ namespace osu.Game.Screens.Play Action = () => { //we want to hide the hitrenderer immediately (looks better). //we may be able to remove this once the mouse cursor trail is improved. - HitRenderer?.Hide(); + RulesetContainer?.Hide(); Restart(); }, } }; - scoreProcessor = HitRenderer.CreateScoreProcessor(); + scoreProcessor = RulesetContainer.CreateScoreProcessor(); hudOverlay.KeyCounter.AddRange(rulesetInstance.CreateGameplayKeys()); hudOverlay.BindProcessor(scoreProcessor); - hudOverlay.BindHitRenderer(HitRenderer); + hudOverlay.BindRulesetContainer(RulesetContainer); - hudOverlay.Progress.Objects = HitRenderer.Objects; + hudOverlay.Progress.Objects = RulesetContainer.Objects; hudOverlay.Progress.AudioClock = decoupledClock; - hudOverlay.Progress.AllowSeeking = HitRenderer.HasReplayLoaded; + hudOverlay.Progress.AllowSeeking = RulesetContainer.HasReplayLoaded; hudOverlay.Progress.OnSeek = pos => decoupledClock.Seek(pos); hudOverlay.ModDisplay.Current.BindTo(working.Mods); - //bind HitRenderer to ScoreProcessor and ourselves (for a pass situation) - HitRenderer.OnAllJudged += onCompletion; + //bind RulesetContainer to ScoreProcessor and ourselves (for a pass situation) + RulesetContainer.OnAllJudged += onCompletion; //bind ScoreProcessor to ourselves (for a fail situation) scoreProcessor.Failed += onFail; @@ -238,7 +238,7 @@ namespace osu.Game.Screens.Play Ruleset = ruleset }; scoreProcessor.PopulateScore(score); - score.User = HitRenderer.Replay?.User ?? (Game as OsuGame)?.API?.LocalUser?.Value; + score.User = RulesetContainer.Replay?.User ?? (Game as OsuGame)?.API?.LocalUser?.Value; Push(new Results(score)); }); } @@ -295,7 +295,7 @@ namespace osu.Game.Screens.Play protected override bool OnExiting(Screen next) { - if (HasFailed || !ValidForResume || pauseContainer?.AllowExit != false || HitRenderer?.HasReplayLoaded != false) + if (HasFailed || !ValidForResume || pauseContainer?.AllowExit != false || RulesetContainer?.HasReplayLoaded != false) { fadeOut(); return base.OnExiting(next); @@ -313,7 +313,7 @@ namespace osu.Game.Screens.Play { const float fade_out_duration = 250; - HitRenderer?.FadeOut(fade_out_duration); + RulesetContainer?.FadeOut(fade_out_duration); Content.FadeOut(fade_out_duration); hudOverlay?.ScaleTo(0.7f, fade_out_duration * 3, Easing.In); diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 860675b62a..4748ddfd71 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Play protected override void LoadComplete() { base.LoadComplete(); - HitRenderer.SetReplay(Replay); + RulesetContainer.SetReplay(Replay); } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 17596f0f49..aba4384b59 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -92,6 +92,8 @@ + + @@ -118,6 +120,7 @@ + @@ -309,7 +312,6 @@ - @@ -338,9 +340,9 @@ - + - +