2021-07-22 14:20:33 +08:00
|
|
|
// 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.
|
|
|
|
|
2021-07-22 14:41:01 +08:00
|
|
|
using osu.Framework.Graphics;
|
2021-07-23 09:10:55 +08:00
|
|
|
using osu.Framework.Input;
|
2021-07-22 14:41:01 +08:00
|
|
|
using osu.Framework.Input.Events;
|
2023-12-20 04:20:21 +08:00
|
|
|
using osu.Framework.Utils;
|
2021-07-22 14:41:01 +08:00
|
|
|
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
2021-07-22 14:20:33 +08:00
|
|
|
using osu.Game.Rulesets.Catch.Objects;
|
2021-07-22 14:41:01 +08:00
|
|
|
using osu.Game.Rulesets.Edit;
|
|
|
|
using osuTK;
|
|
|
|
using osuTK.Input;
|
2021-07-22 14:20:33 +08:00
|
|
|
|
|
|
|
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|
|
|
{
|
|
|
|
public partial class JuiceStreamPlacementBlueprint : CatchPlacementBlueprint<JuiceStream>
|
|
|
|
{
|
2021-07-22 14:41:01 +08:00
|
|
|
private readonly ScrollingPath scrollingPath;
|
|
|
|
|
|
|
|
private readonly NestedOutlineContainer nestedOutlineContainer;
|
|
|
|
|
|
|
|
private readonly PlacementEditablePath editablePath;
|
|
|
|
|
|
|
|
private int lastEditablePathId = -1;
|
|
|
|
|
2023-01-15 15:00:34 +08:00
|
|
|
private InputManager inputManager = null!;
|
2021-07-23 09:10:55 +08:00
|
|
|
|
2023-12-20 04:20:21 +08:00
|
|
|
protected override bool IsValidForPlacement => Precision.DefinitelyBigger(HitObject.Duration, 0);
|
2023-05-12 15:00:40 +08:00
|
|
|
|
2021-07-22 14:41:01 +08:00
|
|
|
public JuiceStreamPlacementBlueprint()
|
|
|
|
{
|
|
|
|
InternalChildren = new Drawable[]
|
|
|
|
{
|
|
|
|
scrollingPath = new ScrollingPath(),
|
|
|
|
nestedOutlineContainer = new NestedOutlineContainer(),
|
2022-05-08 17:32:01 +08:00
|
|
|
editablePath = new PlacementEditablePath(positionToTime)
|
2021-07-22 14:41:01 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void Update()
|
|
|
|
{
|
|
|
|
base.Update();
|
|
|
|
|
|
|
|
if (PlacementActive == PlacementState.Active)
|
|
|
|
editablePath.UpdateFrom(HitObjectContainer, HitObject);
|
|
|
|
}
|
|
|
|
|
2021-07-23 09:10:55 +08:00
|
|
|
protected override void LoadComplete()
|
|
|
|
{
|
|
|
|
base.LoadComplete();
|
|
|
|
|
2024-05-27 17:23:32 +08:00
|
|
|
inputManager = GetContainingInputManager()!;
|
2022-05-08 14:52:14 +08:00
|
|
|
|
|
|
|
BeginPlacement();
|
2021-07-23 09:10:55 +08:00
|
|
|
}
|
|
|
|
|
2021-07-22 14:41:01 +08:00
|
|
|
protected override bool OnMouseDown(MouseDownEvent e)
|
|
|
|
{
|
|
|
|
switch (PlacementActive)
|
|
|
|
{
|
|
|
|
case PlacementState.Waiting:
|
|
|
|
if (e.Button != MouseButton.Left) break;
|
|
|
|
|
|
|
|
editablePath.AddNewVertex();
|
|
|
|
BeginPlacement(true);
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case PlacementState.Active:
|
|
|
|
switch (e.Button)
|
|
|
|
{
|
|
|
|
case MouseButton.Left:
|
|
|
|
editablePath.AddNewVertex();
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case MouseButton.Right:
|
2023-05-12 15:00:40 +08:00
|
|
|
EndPlacement(true);
|
2021-07-22 14:41:01 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return base.OnMouseDown(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void UpdateTimeAndPosition(SnapResult result)
|
|
|
|
{
|
|
|
|
switch (PlacementActive)
|
|
|
|
{
|
|
|
|
case PlacementState.Waiting:
|
|
|
|
HitObject.OriginalX = ToLocalSpace(result.ScreenSpacePosition).X;
|
Fix juice stream placement blueprint being initially visually offset
- Closes https://github.com/ppy/osu/issues/31423.
- Regressed in https://github.com/ppy/osu/pull/30411.
Admittedly, I don't completely understand all of the pieces here,
because code quality of this placement blueprint code is ALL-CAPS
ATROCIOUS, but I believe the failure mode to be something along the
lines of:
- User activates juice stream tool, blueprint gets created in initial
state. It reads in a mouse position far outside of the playfield, and
sets internal positioning appropriately.
- When the user moves the mouse into the bounds of the playfield, some
positions update (the ones inside `UpdateTimeAndPosition()`, but the
fruit markers are for *nested* objects, and
`updateHitObjectFromPath()` is responsible for updating those...
however, it only fires if the `editablePath.PathId` changes, which it
won't here, because there is only one path vertex until the user
commits the starting point of the juice stream and it's always at
(0,0).
- Therefore the position of the starting fruit marker remains bogus
until left click, at which point the path changes and everything
returns to *relative* sanity.
The solution essentially relies on inlining the broken method and only
guarding the relevant part of processing behind the path version check
(which is actually updating the path). Everything else that can touch
positions of nesteds (like default application, and the drawable piece
updates) is allowed to happen unconditionally.
2025-01-08 20:40:42 +08:00
|
|
|
if (result.Time is double snappedTime)
|
|
|
|
HitObject.StartTime = snappedTime;
|
2021-07-22 14:41:01 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PlacementState.Active:
|
2021-07-23 09:10:55 +08:00
|
|
|
Vector2 unsnappedPosition = inputManager.CurrentState.Mouse.Position;
|
2021-07-22 14:41:01 +08:00
|
|
|
editablePath.MoveLastVertex(unsnappedPosition);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the up-to-date position is used for outlines.
|
|
|
|
Vector2 startPosition = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject);
|
|
|
|
editablePath.Position = nestedOutlineContainer.Position = scrollingPath.Position = startPosition;
|
|
|
|
|
Fix juice stream placement blueprint being initially visually offset
- Closes https://github.com/ppy/osu/issues/31423.
- Regressed in https://github.com/ppy/osu/pull/30411.
Admittedly, I don't completely understand all of the pieces here,
because code quality of this placement blueprint code is ALL-CAPS
ATROCIOUS, but I believe the failure mode to be something along the
lines of:
- User activates juice stream tool, blueprint gets created in initial
state. It reads in a mouse position far outside of the playfield, and
sets internal positioning appropriately.
- When the user moves the mouse into the bounds of the playfield, some
positions update (the ones inside `UpdateTimeAndPosition()`, but the
fruit markers are for *nested* objects, and
`updateHitObjectFromPath()` is responsible for updating those...
however, it only fires if the `editablePath.PathId` changes, which it
won't here, because there is only one path vertex until the user
commits the starting point of the juice stream and it's always at
(0,0).
- Therefore the position of the starting fruit marker remains bogus
until left click, at which point the path changes and everything
returns to *relative* sanity.
The solution essentially relies on inlining the broken method and only
guarding the relevant part of processing behind the path version check
(which is actually updating the path). Everything else that can touch
positions of nesteds (like default application, and the drawable piece
updates) is allowed to happen unconditionally.
2025-01-08 20:40:42 +08:00
|
|
|
if (lastEditablePathId != editablePath.PathId)
|
|
|
|
editablePath.UpdateHitObjectFromPath(HitObject);
|
|
|
|
lastEditablePathId = editablePath.PathId;
|
2021-07-22 14:41:01 +08:00
|
|
|
|
|
|
|
ApplyDefaultsToHitObject();
|
|
|
|
scrollingPath.UpdatePathFrom(HitObjectContainer, HitObject);
|
|
|
|
nestedOutlineContainer.UpdateNestedObjectsFrom(HitObjectContainer, HitObject);
|
|
|
|
}
|
|
|
|
|
2022-05-08 17:32:01 +08:00
|
|
|
private double positionToTime(float relativeYPosition)
|
2021-07-22 14:41:01 +08:00
|
|
|
{
|
|
|
|
double time = HitObjectContainer.TimeAtPosition(relativeYPosition, HitObject.StartTime);
|
2022-05-08 17:32:01 +08:00
|
|
|
return time - HitObject.StartTime;
|
2021-07-22 14:41:01 +08:00
|
|
|
}
|
2021-07-22 14:20:33 +08:00
|
|
|
}
|
|
|
|
}
|