// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable disable using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osuTK; namespace osu.Game.Rulesets.Catch.UI { public partial class CatchPlayfield : ScrollingPlayfield { /// /// The width of the playfield. /// The horizontal movement of the catcher is confined in the area of this width. /// public const float WIDTH = 512; /// /// The height of the playfield. /// This doesn't include the catcher area. /// public const float HEIGHT = 384; /// /// The center position of the playfield. /// public const float CENTER_X = WIDTH / 2; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => // only check the X position; handle all vertical space. base.ReceivePositionalInputAt(new Vector2(screenSpacePos.X, ScreenSpaceDrawQuad.Centre.Y)); internal Catcher Catcher { get; private set; } internal CatcherArea CatcherArea { get; private set; } private readonly IBeatmapDifficultyInfo difficulty; public CatchPlayfield(IBeatmapDifficultyInfo difficulty) { this.difficulty = difficulty; } protected override GameplayCursorContainer CreateCursor() => new CatchCursorContainer(); [BackgroundDependencyLoader] private void load() { var droppedObjectContainer = new DroppedObjectContainer(); Catcher = new Catcher(droppedObjectContainer, difficulty) { X = CENTER_X }; AddRangeInternal(new[] { droppedObjectContainer, Catcher.CreateProxiedContent(), HitObjectContainer.CreateProxy(), // This ordering (`CatcherArea` before `HitObjectContainer`) is important to // make sure the up-to-date catcher position is used for the catcher catching logic of hit objects. CatcherArea = new CatcherArea { Anchor = Anchor.BottomLeft, Origin = Anchor.TopLeft, Catcher = Catcher, }, HitObjectContainer, }); RegisterPool(50); RegisterPool(50); RegisterPool(100); RegisterPool(100); RegisterPool(10); RegisterPool(2); } protected override void LoadComplete() { base.LoadComplete(); // these subscriptions need to be done post constructor to ensure externally bound components have a chance to populate required fields (ScoreProcessor / ComboAtJudgement in this case). NewResult += onNewResult; RevertResult += onRevertResult; } protected override void OnNewDrawableHitObject(DrawableHitObject d) { ((DrawableCatchHitObject)d).CheckPosition = checkIfWeCanCatch; } private bool checkIfWeCanCatch(CatchHitObject obj) => Catcher.CanCatch(obj); private void onNewResult(DrawableHitObject judgedObject, JudgementResult result) => CatcherArea.OnNewResult((DrawableCatchHitObject)judgedObject, result); private void onRevertResult(DrawableHitObject judgedObject, JudgementResult result) => CatcherArea.OnRevertResult((DrawableCatchHitObject)judgedObject, result); } }