2020-01-02 10:46:18 +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.
|
|
|
|
|
2020-01-15 18:09:49 +08:00
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
|
|
|
using osu.Framework.Allocation;
|
2020-09-25 13:10:30 +08:00
|
|
|
using osu.Framework.Bindables;
|
2020-01-15 18:09:49 +08:00
|
|
|
using osu.Framework.Graphics;
|
|
|
|
using osu.Framework.Graphics.Containers;
|
|
|
|
using osu.Framework.Input;
|
|
|
|
using osu.Game.Rulesets.Edit;
|
|
|
|
using osu.Game.Rulesets.Edit.Tools;
|
|
|
|
using osu.Game.Rulesets.Objects;
|
|
|
|
using osu.Game.Rulesets.Objects.Drawables;
|
2020-09-25 13:10:30 +08:00
|
|
|
using osu.Game.Rulesets.Objects.Types;
|
2020-06-14 12:20:58 +08:00
|
|
|
using osuTK;
|
2020-01-15 18:09:49 +08:00
|
|
|
|
2020-01-02 10:46:18 +08:00
|
|
|
namespace osu.Game.Screens.Edit.Compose.Components
|
|
|
|
{
|
|
|
|
/// <summary>
|
|
|
|
/// A blueprint container generally displayed as an overlay to a ruleset's playfield.
|
|
|
|
/// </summary>
|
|
|
|
public class ComposeBlueprintContainer : BlueprintContainer
|
|
|
|
{
|
2020-01-15 18:09:49 +08:00
|
|
|
[Resolved]
|
|
|
|
private HitObjectComposer composer { get; set; }
|
|
|
|
|
|
|
|
private PlacementBlueprint currentPlacement;
|
|
|
|
|
|
|
|
private readonly Container<PlacementBlueprint> placementBlueprintContainer;
|
|
|
|
|
2020-06-14 12:20:58 +08:00
|
|
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
|
|
|
|
2020-01-15 18:09:49 +08:00
|
|
|
private InputManager inputManager;
|
|
|
|
|
|
|
|
private readonly IEnumerable<DrawableHitObject> drawableHitObjects;
|
|
|
|
|
|
|
|
public ComposeBlueprintContainer(IEnumerable<DrawableHitObject> drawableHitObjects)
|
|
|
|
{
|
|
|
|
this.drawableHitObjects = drawableHitObjects;
|
|
|
|
|
|
|
|
placementBlueprintContainer = new Container<PlacementBlueprint>
|
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
[BackgroundDependencyLoader]
|
|
|
|
private void load()
|
|
|
|
{
|
|
|
|
AddInternal(placementBlueprintContainer);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void LoadComplete()
|
|
|
|
{
|
|
|
|
base.LoadComplete();
|
|
|
|
|
|
|
|
inputManager = GetContainingInputManager();
|
2020-09-25 13:10:30 +08:00
|
|
|
|
|
|
|
Beatmap.SelectedHitObjects.CollectionChanged += (_, __) => updateTogglesFromSelection();
|
|
|
|
|
|
|
|
// the updated object may be in the selection
|
|
|
|
Beatmap.HitObjectUpdated += _ => updateTogglesFromSelection();
|
|
|
|
|
|
|
|
NewCombo.ValueChanged += combo =>
|
|
|
|
{
|
|
|
|
if (Beatmap.SelectedHitObjects.Count > 0)
|
|
|
|
{
|
|
|
|
foreach (var h in Beatmap.SelectedHitObjects)
|
|
|
|
{
|
|
|
|
if (h is IHasComboInformation c)
|
|
|
|
{
|
|
|
|
c.NewCombo = combo.NewValue;
|
|
|
|
Beatmap.UpdateHitObject(h);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (currentPlacement != null)
|
|
|
|
{
|
|
|
|
// update placement object from toggle
|
|
|
|
if (currentPlacement.HitObject is IHasComboInformation c)
|
|
|
|
c.NewCombo = combo.NewValue;
|
|
|
|
}
|
|
|
|
};
|
2020-01-15 18:09:49 +08:00
|
|
|
}
|
|
|
|
|
2020-09-25 13:10:30 +08:00
|
|
|
private void updateTogglesFromSelection() =>
|
|
|
|
NewCombo.Value = Beatmap.SelectedHitObjects.OfType<IHasComboInformation>().All(c => c.NewCombo);
|
|
|
|
|
|
|
|
public readonly Bindable<bool> NewCombo = new Bindable<bool> { Description = "New Combo" };
|
|
|
|
|
|
|
|
public virtual IEnumerable<Bindable<bool>> Toggles => new[]
|
|
|
|
{
|
|
|
|
//TODO: this should only be enabled (visible?) for rulesets that provide combo-supporting HitObjects.
|
|
|
|
NewCombo
|
|
|
|
};
|
|
|
|
|
2020-01-15 18:09:49 +08:00
|
|
|
#region Placement
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Refreshes the current placement tool.
|
|
|
|
/// </summary>
|
|
|
|
private void refreshTool()
|
|
|
|
{
|
2020-02-13 09:00:09 +08:00
|
|
|
removePlacement();
|
2020-02-13 09:05:50 +08:00
|
|
|
createPlacement();
|
2020-01-15 18:09:49 +08:00
|
|
|
}
|
|
|
|
|
2020-05-20 17:40:55 +08:00
|
|
|
private void updatePlacementPosition()
|
2020-01-15 18:09:49 +08:00
|
|
|
{
|
2020-05-20 17:40:55 +08:00
|
|
|
var snapResult = composer.SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position);
|
2020-01-15 18:09:49 +08:00
|
|
|
|
2020-05-20 18:05:03 +08:00
|
|
|
currentPlacement.UpdatePosition(snapResult);
|
2020-01-15 18:09:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
protected override void Update()
|
|
|
|
{
|
|
|
|
base.Update();
|
|
|
|
|
2020-02-13 09:05:50 +08:00
|
|
|
if (composer.CursorInPlacementArea)
|
|
|
|
createPlacement();
|
|
|
|
else if (currentPlacement?.PlacementActive == false)
|
2020-02-13 09:00:09 +08:00
|
|
|
removePlacement();
|
2020-02-13 09:45:16 +08:00
|
|
|
|
|
|
|
if (currentPlacement != null)
|
2020-09-25 13:10:30 +08:00
|
|
|
{
|
2020-05-20 17:40:55 +08:00
|
|
|
updatePlacementPosition();
|
2020-09-25 13:10:30 +08:00
|
|
|
}
|
2020-01-15 18:09:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
protected sealed override SelectionBlueprint CreateBlueprintFor(HitObject hitObject)
|
|
|
|
{
|
|
|
|
var drawable = drawableHitObjects.FirstOrDefault(d => d.HitObject == hitObject);
|
2020-02-13 09:00:09 +08:00
|
|
|
|
2020-01-15 18:09:49 +08:00
|
|
|
if (drawable == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
return CreateBlueprintFor(drawable);
|
|
|
|
}
|
|
|
|
|
2020-01-20 23:53:59 +08:00
|
|
|
public virtual OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => null;
|
2020-01-15 18:09:49 +08:00
|
|
|
|
|
|
|
protected override void AddBlueprintFor(HitObject hitObject)
|
|
|
|
{
|
|
|
|
refreshTool();
|
|
|
|
base.AddBlueprintFor(hitObject);
|
|
|
|
}
|
|
|
|
|
2020-02-13 09:05:50 +08:00
|
|
|
private void createPlacement()
|
|
|
|
{
|
|
|
|
if (currentPlacement != null) return;
|
|
|
|
|
|
|
|
var blueprint = CurrentTool?.CreatePlacementBlueprint();
|
|
|
|
|
|
|
|
if (blueprint != null)
|
|
|
|
{
|
|
|
|
placementBlueprintContainer.Child = currentPlacement = blueprint;
|
|
|
|
|
|
|
|
// Fixes a 1-frame position discrepancy due to the first mouse move event happening in the next frame
|
2020-05-20 17:40:55 +08:00
|
|
|
updatePlacementPosition();
|
2020-02-13 09:05:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-13 09:00:09 +08:00
|
|
|
private void removePlacement()
|
|
|
|
{
|
|
|
|
if (currentPlacement == null) return;
|
|
|
|
|
|
|
|
currentPlacement.EndPlacement(false);
|
2020-02-13 09:05:50 +08:00
|
|
|
currentPlacement.Expire();
|
2020-02-13 09:00:09 +08:00
|
|
|
currentPlacement = null;
|
|
|
|
}
|
|
|
|
|
2020-01-15 18:09:49 +08:00
|
|
|
private HitObjectCompositionTool currentTool;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// The current placement tool.
|
|
|
|
/// </summary>
|
|
|
|
public HitObjectCompositionTool CurrentTool
|
|
|
|
{
|
|
|
|
get => currentTool;
|
2020-02-13 09:00:09 +08:00
|
|
|
|
2020-01-15 18:09:49 +08:00
|
|
|
set
|
|
|
|
{
|
|
|
|
if (currentTool == value)
|
|
|
|
return;
|
|
|
|
|
|
|
|
currentTool = value;
|
|
|
|
|
|
|
|
refreshTool();
|
|
|
|
}
|
|
|
|
}
|
2020-01-02 10:46:18 +08:00
|
|
|
}
|
|
|
|
}
|