mirror of
https://github.com/ppy/osu.git
synced 2026-05-20 14:40:19 +08:00
e05b6f44b9
(partially) Closes: #36233 Surpasses: #36244 This PR meant to be one of the last steps that finally make editor use the new forms. Initially it meant to only change one SliderWithTextBoxInput in "Effects section" in timing screen, however soon after it was obvious that there's many other places that still using it. This currently won't affect IndeterminateSliderWithTextBoxInput that is being used in hitsounds, for example, since I think it needs more consideration. Anyways, with this PR, SliderWithTextBoxInput, will no longer be used at all, as it's going to be replaced with modern FormSliderBar Comparison: |master|this PR| |:---:|:---:| |<img width="510" height="316" alt="532203751-eb965923-d3a8-441d-a7c8-5c364a6328ad" src="https://github.com/user-attachments/assets/268b45b8-e235-494f-91a5-d00db057dba8" />|<img width="540" height="321" alt="535466527-3a700a8b-bc3c-4610-998f-a4e55ee03eed" src="https://github.com/user-attachments/assets/20cd4b58-b0bd-49bc-8c48-7de5cf8556b3" />| |<img width="694" height="639" alt="534509844-f00e4da4-53c4-45e8-80ea-1be62da6c83b" src="https://github.com/user-attachments/assets/398c4484-a867-4df1-9de3-0940aa748a01" />|<img width="720" height="433" alt="изображение" src="https://github.com/user-attachments/assets/b6359443-a224-4a55-b171-07e8f013cf46" />| |<img width="715" height="353" alt="534509421-a6ac950f-16e8-4a16-bca6-1a781f82135f" src="https://github.com/user-attachments/assets/4854312b-772f-4b81-a800-89e58d4c715d" />|<img width="710" height="296" alt="изображение" src="https://github.com/user-attachments/assets/a7fed53e-e006-4285-92c9-bb84cb603f60" />| |<img width="717" height="374" alt="534509478-80222623-7766-481d-8682-088276d415ee" src="https://github.com/user-attachments/assets/8143b6dc-4599-45d5-bd3b-f059caf3d93d" />|<img width="718" height="328" alt="изображение" src="https://github.com/user-attachments/assets/bffa04de-983c-45ae-a1ec-373701ea0e49" />| |<img width="702" height="446" alt="534509935-58954060-7ac1-4392-8754-a58f909e86aa" src="https://github.com/user-attachments/assets/2bb67a2d-3f57-42a1-96ce-b30b4891e1a4" />|<img width="722" height="386" alt="изображение" src="https://github.com/user-attachments/assets/01b7fff4-7f31-4aac-90c9-353b15f4964e" />|
236 lines
9.2 KiB
C#
236 lines
9.2 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 System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using osu.Framework.Allocation;
|
|
using osu.Framework.Bindables;
|
|
using osu.Framework.Graphics;
|
|
using osu.Framework.Graphics.Containers;
|
|
using osu.Game.Beatmaps.ControlPoints;
|
|
using osu.Game.Graphics.UserInterface;
|
|
using osu.Game.Graphics.UserInterfaceV2;
|
|
using osu.Game.Rulesets.Edit;
|
|
using osu.Game.Rulesets.Objects;
|
|
using osu.Game.Rulesets.Objects.Legacy;
|
|
using osu.Game.Rulesets.Objects.Types;
|
|
using osu.Game.Rulesets.Osu.Objects;
|
|
using osu.Game.Rulesets.Osu.UI;
|
|
using osu.Game.Screens.Edit;
|
|
using osu.Game.Screens.Edit.Compose.Components;
|
|
using osuTK;
|
|
|
|
namespace osu.Game.Rulesets.Osu.Edit
|
|
{
|
|
public partial class PolygonGenerationPopover : OsuPopover
|
|
{
|
|
private FormSliderBar<double> distanceSnapInput { get; set; } = null!;
|
|
private FormSliderBar<int> offsetAngleInput { get; set; } = null!;
|
|
private FormSliderBar<int> repeatCountInput { get; set; } = null!;
|
|
private FormSliderBar<int> pointInput { get; set; } = null!;
|
|
private RoundedButton commitButton = null!;
|
|
|
|
private readonly List<HitCircle> insertedCircles = new List<HitCircle>();
|
|
private bool began;
|
|
private bool committed;
|
|
|
|
[Resolved]
|
|
private IBeatSnapProvider beatSnapProvider { get; set; } = null!;
|
|
|
|
[Resolved]
|
|
private EditorClock editorClock { get; set; } = null!;
|
|
|
|
[Resolved]
|
|
private EditorBeatmap editorBeatmap { get; set; } = null!;
|
|
|
|
[Resolved]
|
|
private IEditorChangeHandler? changeHandler { get; set; }
|
|
|
|
[Resolved]
|
|
private HitObjectComposer composer { get; set; } = null!;
|
|
|
|
private Bindable<TernaryState> newComboState = null!;
|
|
|
|
[BackgroundDependencyLoader]
|
|
private void load()
|
|
{
|
|
var selectionHandler = (EditorSelectionHandler)composer.BlueprintContainer.SelectionHandler;
|
|
newComboState = selectionHandler.SelectionNewComboState.GetBoundCopy();
|
|
|
|
AllowableAnchors = new[] { Anchor.CentreLeft, Anchor.CentreRight };
|
|
|
|
Child = new FillFlowContainer
|
|
{
|
|
Width = 220,
|
|
AutoSizeAxes = Axes.Y,
|
|
Spacing = new Vector2(5),
|
|
Children = new Drawable[]
|
|
{
|
|
distanceSnapInput = new FormSliderBar<double>
|
|
{
|
|
Caption = "Distance snap",
|
|
Current = new BindableNumber<double>(1)
|
|
{
|
|
MinValue = 0.1,
|
|
MaxValue = 6,
|
|
Precision = 0.1,
|
|
Value = ((OsuHitObjectComposer)composer).DistanceSnapProvider.DistanceSpacingMultiplier.Value,
|
|
},
|
|
TabbableContentContainer = this
|
|
},
|
|
offsetAngleInput = new FormSliderBar<int>
|
|
{
|
|
Caption = "Offset angle",
|
|
Current = new BindableNumber<int>
|
|
{
|
|
MinValue = 0,
|
|
MaxValue = 180,
|
|
Precision = 1
|
|
},
|
|
TabbableContentContainer = this
|
|
},
|
|
repeatCountInput = new FormSliderBar<int>
|
|
{
|
|
Caption = "Repeats",
|
|
Current = new BindableNumber<int>(1)
|
|
{
|
|
MinValue = 1,
|
|
MaxValue = 10,
|
|
Precision = 1
|
|
},
|
|
TabbableContentContainer = this
|
|
},
|
|
pointInput = new FormSliderBar<int>
|
|
{
|
|
Caption = "Vertices",
|
|
Current = new BindableNumber<int>(3)
|
|
{
|
|
MinValue = 3,
|
|
MaxValue = 32,
|
|
Precision = 1,
|
|
},
|
|
TabbableContentContainer = this
|
|
},
|
|
commitButton = new RoundedButton
|
|
{
|
|
RelativeSizeAxes = Axes.X,
|
|
Text = "Create",
|
|
Action = commit
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
protected override void LoadComplete()
|
|
{
|
|
base.LoadComplete();
|
|
|
|
changeHandler?.BeginChange();
|
|
began = true;
|
|
|
|
distanceSnapInput.Current.BindValueChanged(_ => Scheduler.AddOnce(tryCreatePolygon));
|
|
offsetAngleInput.Current.BindValueChanged(_ => Scheduler.AddOnce(tryCreatePolygon));
|
|
repeatCountInput.Current.BindValueChanged(_ => Scheduler.AddOnce(tryCreatePolygon));
|
|
pointInput.Current.BindValueChanged(_ => Scheduler.AddOnce(tryCreatePolygon));
|
|
newComboState.BindValueChanged(_ => Scheduler.AddOnce(tryCreatePolygon));
|
|
tryCreatePolygon();
|
|
}
|
|
|
|
private void tryCreatePolygon()
|
|
{
|
|
double startTime = beatSnapProvider.SnapTime(editorClock.CurrentTime);
|
|
TimingControlPoint timingPoint = editorBeatmap.ControlPointInfo.TimingPointAt(startTime);
|
|
double timeSpacing = timingPoint.BeatLength / editorBeatmap.BeatDivisor;
|
|
IHasSliderVelocity lastWithSliderVelocity = editorBeatmap.HitObjects.Where(ho => ho.GetEndTime() <= startTime).OfType<IHasSliderVelocity>().LastOrDefault() ?? new Slider();
|
|
double velocity = OsuHitObject.BASE_SCORING_DISTANCE * editorBeatmap.Difficulty.SliderMultiplier
|
|
/ LegacyRulesetExtensions.GetPrecisionAdjustedBeatLength(lastWithSliderVelocity, timingPoint, OsuRuleset.SHORT_NAME);
|
|
double length = distanceSnapInput.Current.Value * velocity * timeSpacing;
|
|
float polygonRadius = (float)(length / (2 * Math.Sin(double.Pi / pointInput.Current.Value)));
|
|
|
|
int totalPoints = pointInput.Current.Value * repeatCountInput.Current.Value;
|
|
|
|
if (insertedCircles.Count > totalPoints)
|
|
{
|
|
editorBeatmap.RemoveRange(insertedCircles.GetRange(totalPoints, insertedCircles.Count - totalPoints));
|
|
insertedCircles.RemoveRange(totalPoints, insertedCircles.Count - totalPoints);
|
|
}
|
|
|
|
var newlyAdded = new List<HitCircle>();
|
|
|
|
for (int i = 0; i < totalPoints; ++i)
|
|
{
|
|
float angle = float.DegreesToRadians(offsetAngleInput.Current.Value) + (i + 1) * (2 * float.Pi / pointInput.Current.Value);
|
|
var position = OsuPlayfield.BASE_SIZE / 2 + new Vector2(polygonRadius * float.Cos(angle), polygonRadius * float.Sin(angle));
|
|
bool newCombo = i == 0 && newComboState.Value == TernaryState.True;
|
|
|
|
HitCircle circle;
|
|
|
|
if (i < insertedCircles.Count)
|
|
{
|
|
circle = insertedCircles[i];
|
|
|
|
circle.Position = position;
|
|
circle.StartTime = startTime;
|
|
circle.NewCombo = newCombo;
|
|
|
|
editorBeatmap.Update(circle);
|
|
}
|
|
else
|
|
{
|
|
circle = new HitCircle
|
|
{
|
|
Position = position,
|
|
StartTime = startTime,
|
|
NewCombo = newCombo,
|
|
};
|
|
|
|
newlyAdded.Add(circle);
|
|
|
|
// TODO: probably ensure samples also follow current ternary status (not trivial)
|
|
circle.Samples.Add(circle.CreateHitSampleInfo());
|
|
}
|
|
|
|
if (position.X < 0 || position.Y < 0 || position.X > OsuPlayfield.BASE_SIZE.X || position.Y > OsuPlayfield.BASE_SIZE.Y)
|
|
{
|
|
commitButton.Enabled.Value = false;
|
|
editorBeatmap.RemoveRange(insertedCircles);
|
|
insertedCircles.Clear();
|
|
return;
|
|
}
|
|
|
|
startTime = beatSnapProvider.SnapTime(startTime + timeSpacing);
|
|
}
|
|
|
|
var previousNewComboState = newComboState.Value;
|
|
|
|
insertedCircles.AddRange(newlyAdded);
|
|
editorBeatmap.AddRange(newlyAdded);
|
|
|
|
// When adding new hitObjects, newCombo state will get reset to false when no objects are selected.
|
|
// Since this is the case when this popover is showing, we need to restore the previous newCombo state
|
|
newComboState.Value = previousNewComboState;
|
|
|
|
commitButton.Enabled.Value = true;
|
|
}
|
|
|
|
private void commit()
|
|
{
|
|
changeHandler?.EndChange();
|
|
committed = true;
|
|
Hide();
|
|
}
|
|
|
|
protected override void PopOut()
|
|
{
|
|
base.PopOut();
|
|
|
|
if (began && !committed)
|
|
{
|
|
editorBeatmap.RemoveRange(insertedCircles);
|
|
changeHandler?.EndChange();
|
|
}
|
|
}
|
|
}
|
|
}
|