mirror of
https://github.com/ppy/osu.git
synced 2025-01-13 09:23:06 +08:00
Merge pull request #13617 from ekrctb/catch-editor
Add "placeholder" (pre-MVP) implementation of osu!catch editor
This commit is contained in:
commit
f62b4f2d24
14
osu.Game.Rulesets.Catch.Tests/Editor/TestSceneEditor.cs
Normal file
14
osu.Game.Rulesets.Catch.Tests/Editor/TestSceneEditor.cs
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests.Editor
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneEditor : EditorTestScene
|
||||
{
|
||||
protected override Ruleset CreateEditorRuleset() => new CatchRuleset();
|
||||
}
|
||||
}
|
@ -22,7 +22,9 @@ using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using System;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Game.Rulesets.Catch.Edit;
|
||||
using osu.Game.Rulesets.Catch.Skinning.Legacy;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch
|
||||
@ -182,5 +184,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
public int LegacyID => 2;
|
||||
|
||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
|
||||
|
||||
public override HitObjectComposer CreateHitObjectComposer() => new CatchHitObjectComposer(this);
|
||||
}
|
||||
}
|
||||
|
24
osu.Game.Rulesets.Catch/Edit/BananaShowerCompositionTool.cs
Normal file
24
osu.Game.Rulesets.Catch/Edit/BananaShowerCompositionTool.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Edit.Blueprints;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit
|
||||
{
|
||||
public class BananaShowerCompositionTool : HitObjectCompositionTool
|
||||
{
|
||||
public BananaShowerCompositionTool()
|
||||
: base(nameof(BananaShower))
|
||||
{
|
||||
}
|
||||
|
||||
public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners);
|
||||
|
||||
public override PlacementBlueprint CreatePlacementBlueprint() => new BananaShowerPlacementBlueprint();
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
||||
{
|
||||
public class BananaShowerPlacementBlueprint : CatchPlacementBlueprint<BananaShower>
|
||||
{
|
||||
private readonly TimeSpanOutline outline;
|
||||
|
||||
public BananaShowerPlacementBlueprint()
|
||||
{
|
||||
InternalChild = outline = new TimeSpanOutline();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
outline.UpdateFrom(HitObjectContainer, HitObject);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
switch (PlacementActive)
|
||||
{
|
||||
case PlacementState.Waiting:
|
||||
if (e.Button != MouseButton.Left) break;
|
||||
|
||||
BeginPlacement(true);
|
||||
return true;
|
||||
|
||||
case PlacementState.Active:
|
||||
if (e.Button != MouseButton.Right) break;
|
||||
|
||||
// If the duration is negative, swap the start and the end time to make the duration positive.
|
||||
if (HitObject.Duration < 0)
|
||||
{
|
||||
HitObject.StartTime = HitObject.EndTime;
|
||||
HitObject.Duration = -HitObject.Duration;
|
||||
}
|
||||
|
||||
EndPlacement(HitObject.Duration > 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseDown(e);
|
||||
}
|
||||
|
||||
public override void UpdateTimeAndPosition(SnapResult result)
|
||||
{
|
||||
base.UpdateTimeAndPosition(result);
|
||||
|
||||
if (!(result.Time is double time)) return;
|
||||
|
||||
switch (PlacementActive)
|
||||
{
|
||||
case PlacementState.Waiting:
|
||||
HitObject.StartTime = time;
|
||||
break;
|
||||
|
||||
case PlacementState.Active:
|
||||
HitObject.EndTime = time;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
||||
{
|
||||
public class BananaShowerSelectionBlueprint : CatchSelectionBlueprint<BananaShower>
|
||||
{
|
||||
public BananaShowerSelectionBlueprint(BananaShower hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
||||
{
|
||||
public class CatchPlacementBlueprint<THitObject> : PlacementBlueprint
|
||||
where THitObject : CatchHitObject, new()
|
||||
{
|
||||
protected new THitObject HitObject => (THitObject)base.HitObject;
|
||||
|
||||
protected ScrollingHitObjectContainer HitObjectContainer => (ScrollingHitObjectContainer)playfield.HitObjectContainer;
|
||||
|
||||
[Resolved]
|
||||
private Playfield playfield { get; set; }
|
||||
|
||||
public CatchPlacementBlueprint()
|
||||
: base(new THitObject())
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
||||
{
|
||||
public abstract class CatchSelectionBlueprint<THitObject> : HitObjectSelectionBlueprint<THitObject>
|
||||
where THitObject : CatchHitObject
|
||||
{
|
||||
public override Vector2 ScreenSpaceSelectionPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
float x = HitObject.OriginalX;
|
||||
float y = HitObjectContainer.PositionAtTime(HitObject.StartTime);
|
||||
return HitObjectContainer.ToScreenSpace(new Vector2(x, y + HitObjectContainer.DrawHeight));
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => SelectionQuad.Contains(screenSpacePos);
|
||||
|
||||
protected ScrollingHitObjectContainer HitObjectContainer => (ScrollingHitObjectContainer)playfield.HitObjectContainer;
|
||||
|
||||
[Resolved]
|
||||
private Playfield playfield { get; set; }
|
||||
|
||||
protected CatchSelectionBlueprint(THitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Skinning.Default;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
||||
{
|
||||
public class FruitOutline : CompositeDrawable
|
||||
{
|
||||
public FruitOutline()
|
||||
{
|
||||
Anchor = Anchor.BottomLeft;
|
||||
Origin = Anchor.Centre;
|
||||
Size = new Vector2(2 * CatchHitObject.OBJECT_RADIUS);
|
||||
InternalChild = new BorderPiece();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour osuColour)
|
||||
{
|
||||
Colour = osuColour.Yellow;
|
||||
}
|
||||
|
||||
public void UpdateFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject)
|
||||
{
|
||||
X = hitObject.EffectiveX;
|
||||
Y = hitObjectContainer.PositionAtTime(hitObject.StartTime);
|
||||
Scale = new Vector2(hitObject.Scale);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
||||
{
|
||||
public class TimeSpanOutline : CompositeDrawable
|
||||
{
|
||||
private const float border_width = 4;
|
||||
|
||||
private const float opacity_when_empty = 0.5f;
|
||||
|
||||
private bool isEmpty = true;
|
||||
|
||||
public TimeSpanOutline()
|
||||
{
|
||||
Anchor = Origin = Anchor.BottomLeft;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
Masking = true;
|
||||
BorderThickness = border_width;
|
||||
Alpha = opacity_when_empty;
|
||||
|
||||
// A box is needed to make the border visible.
|
||||
InternalChild = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Transparent
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour osuColour)
|
||||
{
|
||||
BorderColour = osuColour.Yellow;
|
||||
}
|
||||
|
||||
public void UpdateFrom(ScrollingHitObjectContainer hitObjectContainer, BananaShower hitObject)
|
||||
{
|
||||
float startY = hitObjectContainer.PositionAtTime(hitObject.StartTime);
|
||||
float endY = hitObjectContainer.PositionAtTime(hitObject.EndTime);
|
||||
|
||||
Y = Math.Max(startY, endY);
|
||||
float height = Math.Abs(startY - endY);
|
||||
|
||||
bool wasEmpty = isEmpty;
|
||||
isEmpty = height == 0;
|
||||
if (wasEmpty != isEmpty)
|
||||
this.FadeTo(isEmpty ? opacity_when_empty : 1f, 150);
|
||||
|
||||
Height = Math.Max(height, border_width);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
||||
{
|
||||
public class FruitPlacementBlueprint : CatchPlacementBlueprint<Fruit>
|
||||
{
|
||||
private readonly FruitOutline outline;
|
||||
|
||||
public FruitPlacementBlueprint()
|
||||
{
|
||||
InternalChild = outline = new FruitOutline();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
BeginPlacement();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
outline.UpdateFrom(HitObjectContainer, HitObject);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
if (e.Button != MouseButton.Left) return base.OnMouseDown(e);
|
||||
|
||||
EndPlacement(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void UpdateTimeAndPosition(SnapResult result)
|
||||
{
|
||||
base.UpdateTimeAndPosition(result);
|
||||
|
||||
HitObject.X = ToLocalSpace(result.ScreenSpacePosition).X;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
||||
{
|
||||
public class FruitSelectionBlueprint : CatchSelectionBlueprint<Fruit>
|
||||
{
|
||||
private readonly FruitOutline outline;
|
||||
|
||||
public FruitSelectionBlueprint(Fruit hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
InternalChild = outline = new FruitOutline();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (IsSelected)
|
||||
outline.UpdateFrom(HitObjectContainer, HitObject);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
||||
{
|
||||
public class JuiceStreamSelectionBlueprint : CatchSelectionBlueprint<JuiceStream>
|
||||
{
|
||||
public override Quad SelectionQuad => HitObjectContainer.ToScreenSpace(getBoundingBox().Offset(new Vector2(0, HitObjectContainer.DrawHeight)));
|
||||
|
||||
private float minNestedX;
|
||||
private float maxNestedX;
|
||||
|
||||
public JuiceStreamSelectionBlueprint(JuiceStream hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
HitObject.DefaultsApplied += onDefaultsApplied;
|
||||
computeObjectBounds();
|
||||
}
|
||||
|
||||
private void onDefaultsApplied(HitObject _) => computeObjectBounds();
|
||||
|
||||
private void computeObjectBounds()
|
||||
{
|
||||
minNestedX = HitObject.NestedHitObjects.OfType<CatchHitObject>().Min(nested => nested.OriginalX) - HitObject.OriginalX;
|
||||
maxNestedX = HitObject.NestedHitObjects.OfType<CatchHitObject>().Max(nested => nested.OriginalX) - HitObject.OriginalX;
|
||||
}
|
||||
|
||||
private RectangleF getBoundingBox()
|
||||
{
|
||||
float left = HitObject.OriginalX + minNestedX;
|
||||
float right = HitObject.OriginalX + maxNestedX;
|
||||
float top = HitObjectContainer.PositionAtTime(HitObject.EndTime);
|
||||
float bottom = HitObjectContainer.PositionAtTime(HitObject.StartTime);
|
||||
float objectRadius = CatchHitObject.OBJECT_RADIUS * HitObject.Scale;
|
||||
return new RectangleF(left, top, right - left, bottom - top).Inflate(objectRadius);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
HitObject.DefaultsApplied -= onDefaultsApplied;
|
||||
}
|
||||
}
|
||||
}
|
38
osu.Game.Rulesets.Catch/Edit/CatchBlueprintContainer.cs
Normal file
38
osu.Game.Rulesets.Catch/Edit/CatchBlueprintContainer.cs
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Catch.Edit.Blueprints;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit
|
||||
{
|
||||
public class CatchBlueprintContainer : ComposeBlueprintContainer
|
||||
{
|
||||
public CatchBlueprintContainer(CatchHitObjectComposer composer)
|
||||
: base(composer)
|
||||
{
|
||||
}
|
||||
|
||||
protected override SelectionHandler<HitObject> CreateSelectionHandler() => new CatchSelectionHandler();
|
||||
|
||||
public override HitObjectSelectionBlueprint CreateHitObjectBlueprintFor(HitObject hitObject)
|
||||
{
|
||||
switch (hitObject)
|
||||
{
|
||||
case Fruit fruit:
|
||||
return new FruitSelectionBlueprint(fruit);
|
||||
|
||||
case JuiceStream juiceStream:
|
||||
return new JuiceStreamSelectionBlueprint(juiceStream);
|
||||
|
||||
case BananaShower bananaShower:
|
||||
return new BananaShowerSelectionBlueprint(bananaShower);
|
||||
}
|
||||
|
||||
return base.CreateHitObjectBlueprintFor(hitObject);
|
||||
}
|
||||
}
|
||||
}
|
27
osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs
Normal file
27
osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit
|
||||
{
|
||||
public class CatchEditorPlayfield : CatchPlayfield
|
||||
{
|
||||
// TODO fixme: the size of the catcher is not changed when circle size is changed in setup screen.
|
||||
public CatchEditorPlayfield(BeatmapDifficulty difficulty)
|
||||
: base(difficulty)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
// TODO: honor "hit animation" setting?
|
||||
CatcherArea.MovableCatcher.CatchFruitOnPlate = false;
|
||||
|
||||
// TODO: disable hit lighting as well
|
||||
}
|
||||
}
|
||||
}
|
42
osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs
Normal file
42
osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit
|
||||
{
|
||||
public class CatchHitObjectComposer : HitObjectComposer<CatchHitObject>
|
||||
{
|
||||
public CatchHitObjectComposer(CatchRuleset ruleset)
|
||||
: base(ruleset)
|
||||
{
|
||||
}
|
||||
|
||||
protected override DrawableRuleset<CatchHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null) =>
|
||||
new DrawableCatchEditorRuleset(ruleset, beatmap, mods);
|
||||
|
||||
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
|
||||
{
|
||||
new FruitCompositionTool(),
|
||||
new BananaShowerCompositionTool()
|
||||
};
|
||||
|
||||
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
|
||||
{
|
||||
var result = base.SnapScreenSpacePositionToValidTime(screenSpacePosition);
|
||||
// TODO: implement position snap
|
||||
result.ScreenSpacePosition.X = screenSpacePosition.X;
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override ComposeBlueprintContainer CreateBlueprintContainer() => new CatchBlueprintContainer(this);
|
||||
}
|
||||
}
|
46
osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs
Normal file
46
osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit
|
||||
{
|
||||
public class CatchSelectionHandler : EditorSelectionHandler
|
||||
{
|
||||
protected ScrollingHitObjectContainer HitObjectContainer => (ScrollingHitObjectContainer)playfield.HitObjectContainer;
|
||||
|
||||
[Resolved]
|
||||
private Playfield playfield { get; set; }
|
||||
|
||||
public override bool HandleMovement(MoveSelectionEvent<HitObject> moveEvent)
|
||||
{
|
||||
var blueprint = moveEvent.Blueprint;
|
||||
Vector2 originalPosition = HitObjectContainer.ToLocalSpace(blueprint.ScreenSpaceSelectionPoint);
|
||||
Vector2 targetPosition = HitObjectContainer.ToLocalSpace(blueprint.ScreenSpaceSelectionPoint + moveEvent.ScreenSpaceDelta);
|
||||
float deltaX = targetPosition.X - originalPosition.X;
|
||||
|
||||
EditorBeatmap.PerformOnSelection(h =>
|
||||
{
|
||||
if (!(h is CatchHitObject hitObject)) return;
|
||||
|
||||
if (hitObject is BananaShower) return;
|
||||
|
||||
// TODO: confine in bounds
|
||||
hitObject.OriginalXBindable.Value += deltaX;
|
||||
|
||||
// Move the nested hit objects to give an instant result before nested objects are recreated.
|
||||
foreach (var nested in hitObject.NestedHitObjects.OfType<CatchHitObject>())
|
||||
nested.OriginalXBindable.Value += deltaX;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
21
osu.Game.Rulesets.Catch/Edit/DrawableCatchEditorRuleset.cs
Normal file
21
osu.Game.Rulesets.Catch/Edit/DrawableCatchEditorRuleset.cs
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit
|
||||
{
|
||||
public class DrawableCatchEditorRuleset : DrawableCatchRuleset
|
||||
{
|
||||
public DrawableCatchEditorRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Playfield CreatePlayfield() => new CatchEditorPlayfield(Beatmap.BeatmapInfo.BaseDifficulty);
|
||||
}
|
||||
}
|
24
osu.Game.Rulesets.Catch/Edit/FruitCompositionTool.cs
Normal file
24
osu.Game.Rulesets.Catch/Edit/FruitCompositionTool.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Edit.Blueprints;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit
|
||||
{
|
||||
public class FruitCompositionTool : HitObjectCompositionTool
|
||||
{
|
||||
public FruitCompositionTool()
|
||||
: base(nameof(Fruit))
|
||||
{
|
||||
}
|
||||
|
||||
public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles);
|
||||
|
||||
public override PlacementBlueprint CreatePlacementBlueprint() => new FruitPlacementBlueprint();
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -94,6 +95,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// Creates a <see cref="SelectionBlueprint{T}"/> for a specific item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to create the overlay for.</param>
|
||||
[CanBeNull]
|
||||
protected virtual SelectionBlueprint<T> CreateBlueprintFor(T item) => null;
|
||||
|
||||
protected virtual DragBox CreateDragBox(Action<RectangleF> performSelect) => new DragBox(performSelect);
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Humanizer;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -256,9 +257,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
if (drawable == null)
|
||||
return null;
|
||||
|
||||
return CreateHitObjectBlueprintFor(item).With(b => b.DrawableObject = drawable);
|
||||
return CreateHitObjectBlueprintFor(item)?.With(b => b.DrawableObject = drawable);
|
||||
}
|
||||
|
||||
[CanBeNull]
|
||||
public virtual HitObjectSelectionBlueprint CreateHitObjectBlueprintFor(HitObject hitObject) => null;
|
||||
|
||||
private void hitObjectAdded(HitObject obj)
|
||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -89,7 +90,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
}
|
||||
else
|
||||
{
|
||||
placementBlueprint = CreateBlueprintFor(obj.NewValue);
|
||||
placementBlueprint = CreateBlueprintFor(obj.NewValue).AsNonNull();
|
||||
|
||||
placementBlueprint.Colour = Color4.MediumPurple;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user