mirror of
https://github.com/ppy/osu.git
synced 2025-02-06 23:12:54 +08:00
18f1d62182
- 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.
125 lines
4.1 KiB
C#
125 lines
4.1 KiB
C#
// 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.Framework.Input;
|
|
using osu.Framework.Input.Events;
|
|
using osu.Framework.Utils;
|
|
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
|
using osu.Game.Rulesets.Catch.Objects;
|
|
using osu.Game.Rulesets.Edit;
|
|
using osuTK;
|
|
using osuTK.Input;
|
|
|
|
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|
{
|
|
public partial class JuiceStreamPlacementBlueprint : CatchPlacementBlueprint<JuiceStream>
|
|
{
|
|
private readonly ScrollingPath scrollingPath;
|
|
|
|
private readonly NestedOutlineContainer nestedOutlineContainer;
|
|
|
|
private readonly PlacementEditablePath editablePath;
|
|
|
|
private int lastEditablePathId = -1;
|
|
|
|
private InputManager inputManager = null!;
|
|
|
|
protected override bool IsValidForPlacement => Precision.DefinitelyBigger(HitObject.Duration, 0);
|
|
|
|
public JuiceStreamPlacementBlueprint()
|
|
{
|
|
InternalChildren = new Drawable[]
|
|
{
|
|
scrollingPath = new ScrollingPath(),
|
|
nestedOutlineContainer = new NestedOutlineContainer(),
|
|
editablePath = new PlacementEditablePath(positionToTime)
|
|
};
|
|
}
|
|
|
|
protected override void Update()
|
|
{
|
|
base.Update();
|
|
|
|
if (PlacementActive == PlacementState.Active)
|
|
editablePath.UpdateFrom(HitObjectContainer, HitObject);
|
|
}
|
|
|
|
protected override void LoadComplete()
|
|
{
|
|
base.LoadComplete();
|
|
|
|
inputManager = GetContainingInputManager()!;
|
|
|
|
BeginPlacement();
|
|
}
|
|
|
|
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:
|
|
EndPlacement(true);
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return base.OnMouseDown(e);
|
|
}
|
|
|
|
public override void UpdateTimeAndPosition(SnapResult result)
|
|
{
|
|
switch (PlacementActive)
|
|
{
|
|
case PlacementState.Waiting:
|
|
HitObject.OriginalX = ToLocalSpace(result.ScreenSpacePosition).X;
|
|
if (result.Time is double snappedTime)
|
|
HitObject.StartTime = snappedTime;
|
|
break;
|
|
|
|
case PlacementState.Active:
|
|
Vector2 unsnappedPosition = inputManager.CurrentState.Mouse.Position;
|
|
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;
|
|
|
|
if (lastEditablePathId != editablePath.PathId)
|
|
editablePath.UpdateHitObjectFromPath(HitObject);
|
|
lastEditablePathId = editablePath.PathId;
|
|
|
|
ApplyDefaultsToHitObject();
|
|
scrollingPath.UpdatePathFrom(HitObjectContainer, HitObject);
|
|
nestedOutlineContainer.UpdateNestedObjectsFrom(HitObjectContainer, HitObject);
|
|
}
|
|
|
|
private double positionToTime(float relativeYPosition)
|
|
{
|
|
double time = HitObjectContainer.TimeAtPosition(relativeYPosition, HitObject.StartTime);
|
|
return time - HitObject.StartTime;
|
|
}
|
|
}
|
|
}
|