mirror of
https://github.com/osukey/osukey.git
synced 2025-05-26 07:57:32 +09:00
Merge branch 'master' into fix-replay-date
This commit is contained in:
commit
5ef1fe6948
@ -23,7 +23,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
|
|
||||||
protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap, CancellationToken cancellationToken)
|
protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var positionData = obj as IHasXPosition;
|
var xPositionData = obj as IHasXPosition;
|
||||||
|
var yPositionData = obj as IHasYPosition;
|
||||||
var comboData = obj as IHasCombo;
|
var comboData = obj as IHasCombo;
|
||||||
|
|
||||||
switch (obj)
|
switch (obj)
|
||||||
@ -36,10 +37,11 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
Path = curveData.Path,
|
Path = curveData.Path,
|
||||||
NodeSamples = curveData.NodeSamples,
|
NodeSamples = curveData.NodeSamples,
|
||||||
RepeatCount = curveData.RepeatCount,
|
RepeatCount = curveData.RepeatCount,
|
||||||
X = positionData?.X ?? 0,
|
X = xPositionData?.X ?? 0,
|
||||||
NewCombo = comboData?.NewCombo ?? false,
|
NewCombo = comboData?.NewCombo ?? false,
|
||||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||||
LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0
|
LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0,
|
||||||
|
LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y
|
||||||
}.Yield();
|
}.Yield();
|
||||||
|
|
||||||
case IHasDuration endTime:
|
case IHasDuration endTime:
|
||||||
@ -59,7 +61,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
Samples = obj.Samples,
|
Samples = obj.Samples,
|
||||||
NewCombo = comboData?.NewCombo ?? false,
|
NewCombo = comboData?.NewCombo ?? false,
|
||||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||||
X = positionData?.X ?? 0
|
X = xPositionData?.X ?? 0,
|
||||||
|
LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y
|
||||||
}.Yield();
|
}.Yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
float x = HitObject.OriginalX;
|
Vector2 position = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject);
|
||||||
float y = HitObjectContainer.PositionAtTime(HitObject.StartTime);
|
return HitObjectContainer.ToScreenSpace(position + new Vector2(0, HitObjectContainer.DrawHeight));
|
||||||
return HitObjectContainer.ToScreenSpace(new Vector2(x, y + HitObjectContainer.DrawHeight));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Skinning.Default;
|
using osu.Game.Rulesets.Catch.Skinning.Default;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
||||||
@ -28,10 +26,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
|||||||
Colour = osuColour.Yellow;
|
Colour = osuColour.Yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject, [CanBeNull] CatchHitObject parent = null)
|
public void UpdateFrom(CatchHitObject hitObject)
|
||||||
{
|
{
|
||||||
X = hitObject.EffectiveX - (parent?.OriginalX ?? 0);
|
|
||||||
Y = hitObjectContainer.PositionAtTime(hitObject.StartTime, parent?.StartTime ?? hitObjectContainer.Time.Current);
|
|
||||||
Scale = new Vector2(hitObject.Scale);
|
Scale = new Vector2(hitObject.Scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,6 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
|||||||
Anchor = Anchor.BottomLeft;
|
Anchor = Anchor.BottomLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdatePositionFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject parentHitObject)
|
|
||||||
{
|
|
||||||
X = parentHitObject.OriginalX;
|
|
||||||
Y = hitObjectContainer.PositionAtTime(parentHitObject.StartTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateNestedObjectsFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject parentHitObject)
|
public void UpdateNestedObjectsFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject parentHitObject)
|
||||||
{
|
{
|
||||||
nestedHitObjects.Clear();
|
nestedHitObjects.Clear();
|
||||||
@ -43,7 +37,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
|||||||
{
|
{
|
||||||
var hitObject = nestedHitObjects[i];
|
var hitObject = nestedHitObjects[i];
|
||||||
var outline = (FruitOutline)InternalChildren[i];
|
var outline = (FruitOutline)InternalChildren[i];
|
||||||
outline.UpdateFrom(hitObjectContainer, hitObject, parentHitObject);
|
outline.Position = CatchHitObjectUtils.GetStartPosition(hitObjectContainer, hitObject) - Position;
|
||||||
|
outline.UpdateFrom(hitObject);
|
||||||
outline.Scale *= hitObject is Droplet ? 0.5f : 1;
|
outline.Scale *= hitObject is Droplet ? 0.5f : 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,12 +33,6 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdatePositionFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject)
|
|
||||||
{
|
|
||||||
X = hitObject.OriginalX;
|
|
||||||
Y = hitObjectContainer.PositionAtTime(hitObject.StartTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdatePathFrom(ScrollingHitObjectContainer hitObjectContainer, JuiceStream hitObject)
|
public void UpdatePathFrom(ScrollingHitObjectContainer hitObjectContainer, JuiceStream hitObject)
|
||||||
{
|
{
|
||||||
double distanceToYFactor = -hitObjectContainer.LengthAtTime(hitObject.StartTime, hitObject.StartTime + 1 / hitObject.Velocity);
|
double distanceToYFactor = -hitObjectContainer.LengthAtTime(hitObject.StartTime, hitObject.StartTime + 1 / hitObject.Velocity);
|
||||||
|
@ -29,7 +29,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
outline.UpdateFrom(HitObjectContainer, HitObject);
|
outline.Position = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject);
|
||||||
|
outline.UpdateFrom(HitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
|
@ -20,8 +20,10 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
if (IsSelected)
|
if (!IsSelected) return;
|
||||||
outline.UpdateFrom(HitObjectContainer, HitObject);
|
|
||||||
|
outline.Position = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject);
|
||||||
|
outline.UpdateFrom(HitObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,8 +49,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
|
|
||||||
if (!IsSelected) return;
|
if (!IsSelected) return;
|
||||||
|
|
||||||
scrollingPath.UpdatePositionFrom(HitObjectContainer, HitObject);
|
nestedOutlineContainer.Position = scrollingPath.Position = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject);
|
||||||
nestedOutlineContainer.UpdatePositionFrom(HitObjectContainer, HitObject);
|
|
||||||
|
|
||||||
if (pathCache.IsValid) return;
|
if (pathCache.IsValid) return;
|
||||||
|
|
||||||
|
24
osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs
Normal file
24
osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.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.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Edit
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Utility functions used by the editor.
|
||||||
|
/// </summary>
|
||||||
|
public static class CatchHitObjectUtils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get the position of the hit object in the playfield based on <see cref="CatchHitObject.OriginalX"/> and <see cref="HitObject.StartTime"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static Vector2 GetStartPosition(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject)
|
||||||
|
{
|
||||||
|
return new Vector2(hitObject.OriginalX, hitObjectContainer.PositionAtTime(hitObject.StartTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,10 +9,11 @@ using osu.Game.Rulesets.Catch.UI;
|
|||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation
|
public abstract class CatchHitObject : HitObject, IHasPosition, IHasComboInformation
|
||||||
{
|
{
|
||||||
public const float OBJECT_RADIUS = 64;
|
public const float OBJECT_RADIUS = 64;
|
||||||
|
|
||||||
@ -31,8 +32,6 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
set => OriginalXBindable.Value = value;
|
set => OriginalXBindable.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
float IHasXPosition.X => OriginalXBindable.Value;
|
|
||||||
|
|
||||||
public readonly Bindable<float> XOffsetBindable = new Bindable<float>();
|
public readonly Bindable<float> XOffsetBindable = new Bindable<float>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -131,5 +130,24 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||||
|
|
||||||
|
#region Hit object conversion
|
||||||
|
|
||||||
|
// The half of the height of the osu! playfield.
|
||||||
|
public const float DEFAULT_LEGACY_CONVERT_Y = 192;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Y position of the hit object is not used in the normal osu!catch gameplay.
|
||||||
|
/// It is preserved to maximize the backward compatibility with the legacy editor, in which the mappers use the Y position to organize the patterns.
|
||||||
|
/// </summary>
|
||||||
|
public float LegacyConvertedY { get; set; } = DEFAULT_LEGACY_CONVERT_Y;
|
||||||
|
|
||||||
|
float IHasXPosition.X => OriginalX;
|
||||||
|
|
||||||
|
float IHasYPosition.Y => LegacyConvertedY;
|
||||||
|
|
||||||
|
Vector2 IHasPosition.Position => new Vector2(OriginalX, LegacyConvertedY);
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,8 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
public TestSceneEditorComposeRadioButtons()
|
public TestSceneEditorComposeRadioButtons()
|
||||||
{
|
{
|
||||||
RadioButtonCollection collection;
|
EditorRadioButtonCollection collection;
|
||||||
Add(collection = new RadioButtonCollection
|
Add(collection = new EditorRadioButtonCollection
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -2,17 +2,24 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osu.Game.Rulesets.Edit.Tools;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Edit;
|
using osu.Game.Rulesets.Osu.Edit;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
|
using osu.Game.Screens.Edit.Components.RadioButtons;
|
||||||
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editing
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
@ -20,37 +27,89 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneHitObjectComposer : EditorClockTestScene
|
public class TestSceneHitObjectComposer : EditorClockTestScene
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
private OsuHitObjectComposer hitObjectComposer;
|
||||||
private void load()
|
private EditorBeatmapContainer editorBeatmapContainer;
|
||||||
|
|
||||||
|
private EditorBeatmap editorBeatmap => editorBeatmapContainer.EditorBeatmap;
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
{
|
{
|
||||||
Beatmap.Value = CreateWorkingBeatmap(new Beatmap
|
AddStep("create beatmap", () =>
|
||||||
{
|
{
|
||||||
HitObjects = new List<HitObject>
|
Beatmap.Value = CreateWorkingBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f },
|
HitObjects = new List<HitObject>
|
||||||
new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f },
|
|
||||||
new Slider
|
|
||||||
{
|
{
|
||||||
Position = new Vector2(128, 256),
|
new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f },
|
||||||
Path = new SliderPath(PathType.Linear, new[]
|
new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f },
|
||||||
|
new Slider
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Position = new Vector2(128, 256),
|
||||||
new Vector2(216, 0),
|
Path = new SliderPath(PathType.Linear, new[]
|
||||||
}),
|
{
|
||||||
Scale = 0.5f,
|
Vector2.Zero,
|
||||||
}
|
new Vector2(216, 0),
|
||||||
},
|
}),
|
||||||
|
Scale = 0.5f,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var editorBeatmap = new EditorBeatmap(Beatmap.Value.GetPlayableBeatmap(new OsuRuleset().RulesetInfo));
|
AddStep("Create composer", () =>
|
||||||
|
{
|
||||||
|
Child = editorBeatmapContainer = new EditorBeatmapContainer(Beatmap.Value)
|
||||||
|
{
|
||||||
|
Child = hitObjectComposer = new OsuHitObjectComposer(new OsuRuleset())
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
[Test]
|
||||||
Dependencies.CacheAs<IAdjustableClock>(clock);
|
public void TestPlacementOnlyWorksWithTiming()
|
||||||
Dependencies.CacheAs<IFrameBasedClock>(clock);
|
{
|
||||||
Dependencies.CacheAs(editorBeatmap);
|
AddStep("clear all control points", () => editorBeatmap.ControlPointInfo.Clear());
|
||||||
Dependencies.CacheAs<IBeatSnapProvider>(editorBeatmap);
|
|
||||||
|
|
||||||
Child = new OsuHitObjectComposer(new OsuRuleset());
|
AddAssert("Tool is selection", () => hitObjectComposer.ChildrenOfType<ComposeBlueprintContainer>().First().CurrentTool is SelectTool);
|
||||||
|
AddAssert("Hitcircle button not clickable", () => !hitObjectComposer.ChildrenOfType<EditorRadioButton>().First(d => d.Button.Label == "HitCircle").Enabled.Value);
|
||||||
|
AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint()));
|
||||||
|
AddAssert("Hitcircle button is clickable", () => hitObjectComposer.ChildrenOfType<EditorRadioButton>().First(d => d.Button.Label == "HitCircle").Enabled.Value);
|
||||||
|
AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType<EditorRadioButton>().First(d => d.Button.Label == "HitCircle").Click());
|
||||||
|
AddAssert("Tool changed", () => hitObjectComposer.ChildrenOfType<ComposeBlueprintContainer>().First().CurrentTool is HitCircleCompositionTool);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EditorBeatmapContainer : Container
|
||||||
|
{
|
||||||
|
private readonly WorkingBeatmap working;
|
||||||
|
|
||||||
|
public EditorBeatmap EditorBeatmap { get; private set; }
|
||||||
|
|
||||||
|
public EditorBeatmapContainer(WorkingBeatmap working)
|
||||||
|
{
|
||||||
|
this.working = working;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
|
{
|
||||||
|
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||||
|
|
||||||
|
EditorBeatmap = new EditorBeatmap(working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo));
|
||||||
|
|
||||||
|
dependencies.CacheAs(EditorBeatmap);
|
||||||
|
dependencies.CacheAs<IBeatSnapProvider>(EditorBeatmap);
|
||||||
|
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Add(EditorBeatmap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,11 +251,8 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
switch (beatmap.BeatmapInfo.RulesetID)
|
switch (beatmap.BeatmapInfo.RulesetID)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
position = ((IHasPosition)hitObject).Position;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
position.X = ((IHasXPosition)hitObject).X;
|
position = ((IHasPosition)hitObject).Position;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
@ -63,10 +64,12 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
private InputManager inputManager;
|
private InputManager inputManager;
|
||||||
|
|
||||||
private RadioButtonCollection toolboxCollection;
|
private EditorRadioButtonCollection toolboxCollection;
|
||||||
|
|
||||||
private FillFlowContainer togglesCollection;
|
private FillFlowContainer togglesCollection;
|
||||||
|
|
||||||
|
private IBindable<bool> hasTiming;
|
||||||
|
|
||||||
protected HitObjectComposer(Ruleset ruleset)
|
protected HitObjectComposer(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
Ruleset = ruleset;
|
Ruleset = ruleset;
|
||||||
@ -126,7 +129,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
{
|
{
|
||||||
new ToolboxGroup("toolbox (1-9)")
|
new ToolboxGroup("toolbox (1-9)")
|
||||||
{
|
{
|
||||||
Child = toolboxCollection = new RadioButtonCollection { RelativeSizeAxes = Axes.X }
|
Child = toolboxCollection = new EditorRadioButtonCollection { RelativeSizeAxes = Axes.X }
|
||||||
},
|
},
|
||||||
new ToolboxGroup("toggles (Q~P)")
|
new ToolboxGroup("toggles (Q~P)")
|
||||||
{
|
{
|
||||||
@ -160,6 +163,14 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
inputManager = GetContainingInputManager();
|
inputManager = GetContainingInputManager();
|
||||||
|
|
||||||
|
hasTiming = EditorBeatmap.HasTiming.GetBoundCopy();
|
||||||
|
hasTiming.BindValueChanged(timing =>
|
||||||
|
{
|
||||||
|
// it's important this is performed before the similar code in EditorRadioButton disables the button.
|
||||||
|
if (!timing.NewValue)
|
||||||
|
setSelectTool();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Playfield Playfield => drawableRulesetWrapper.Playfield;
|
public override Playfield Playfield => drawableRulesetWrapper.Playfield;
|
||||||
@ -219,7 +230,8 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
if (item != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
item.Select();
|
if (!item.Selected.Disabled)
|
||||||
|
item.Select();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,20 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects.Legacy.Catch
|
namespace osu.Game.Rulesets.Objects.Legacy.Catch
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Legacy osu!catch Hit-type, used for parsing Beatmaps.
|
/// Legacy osu!catch Hit-type, used for parsing Beatmaps.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class ConvertHit : ConvertHitObject, IHasCombo, IHasXPosition
|
internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo
|
||||||
{
|
{
|
||||||
public float X { get; set; }
|
public float X => Position.X;
|
||||||
|
|
||||||
|
public float Y => Position.Y;
|
||||||
|
|
||||||
|
public Vector2 Position { get; set; }
|
||||||
|
|
||||||
public bool NewCombo { get; set; }
|
public bool NewCombo { get; set; }
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
|
|||||||
|
|
||||||
return new ConvertHit
|
return new ConvertHit
|
||||||
{
|
{
|
||||||
X = position.X,
|
Position = position,
|
||||||
NewCombo = newCombo,
|
NewCombo = newCombo,
|
||||||
ComboOffset = comboOffset
|
ComboOffset = comboOffset
|
||||||
};
|
};
|
||||||
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
|
|||||||
|
|
||||||
return new ConvertSlider
|
return new ConvertSlider
|
||||||
{
|
{
|
||||||
X = position.X,
|
Position = position,
|
||||||
NewCombo = FirstObject || newCombo,
|
NewCombo = FirstObject || newCombo,
|
||||||
ComboOffset = comboOffset,
|
ComboOffset = comboOffset,
|
||||||
Path = new SliderPath(controlPoints, length),
|
Path = new SliderPath(controlPoints, length),
|
||||||
|
@ -2,15 +2,20 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects.Legacy.Catch
|
namespace osu.Game.Rulesets.Objects.Legacy.Catch
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Legacy osu!catch Slider-type, used for parsing Beatmaps.
|
/// Legacy osu!catch Slider-type, used for parsing Beatmaps.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition, IHasCombo
|
internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo
|
||||||
{
|
{
|
||||||
public float X { get; set; }
|
public float X => Position.X;
|
||||||
|
|
||||||
|
public float Y => Position.Y;
|
||||||
|
|
||||||
|
public Vector2 Position { get; set; }
|
||||||
|
|
||||||
public bool NewCombo { get; set; }
|
public bool NewCombo { get; set; }
|
||||||
|
|
||||||
|
@ -5,9 +5,11 @@ using System;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -16,26 +18,30 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Components.RadioButtons
|
namespace osu.Game.Screens.Edit.Components.RadioButtons
|
||||||
{
|
{
|
||||||
public class DrawableRadioButton : OsuButton
|
public class EditorRadioButton : OsuButton, IHasTooltip
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked when this <see cref="DrawableRadioButton"/> has been selected.
|
/// Invoked when this <see cref="EditorRadioButton"/> has been selected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action<RadioButton> Selected;
|
public Action<RadioButton> Selected;
|
||||||
|
|
||||||
|
public readonly RadioButton Button;
|
||||||
|
|
||||||
private Color4 defaultBackgroundColour;
|
private Color4 defaultBackgroundColour;
|
||||||
private Color4 defaultBubbleColour;
|
private Color4 defaultBubbleColour;
|
||||||
private Color4 selectedBackgroundColour;
|
private Color4 selectedBackgroundColour;
|
||||||
private Color4 selectedBubbleColour;
|
private Color4 selectedBubbleColour;
|
||||||
|
|
||||||
private Drawable icon;
|
private Drawable icon;
|
||||||
private readonly RadioButton button;
|
|
||||||
|
|
||||||
public DrawableRadioButton(RadioButton button)
|
[Resolved(canBeNull: true)]
|
||||||
|
private EditorBeatmap editorBeatmap { get; set; }
|
||||||
|
|
||||||
|
public EditorRadioButton(RadioButton button)
|
||||||
{
|
{
|
||||||
this.button = button;
|
Button = button;
|
||||||
|
|
||||||
Text = button.Item.ToString();
|
Text = button.Label;
|
||||||
Action = button.Select;
|
Action = button.Select;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
@ -57,7 +63,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
|
|||||||
Colour = Color4.Black.Opacity(0.5f)
|
Colour = Color4.Black.Opacity(0.5f)
|
||||||
};
|
};
|
||||||
|
|
||||||
Add(icon = (button.CreateIcon?.Invoke() ?? new Circle()).With(b =>
|
Add(icon = (Button.CreateIcon?.Invoke() ?? new Circle()).With(b =>
|
||||||
{
|
{
|
||||||
b.Blending = BlendingParameters.Additive;
|
b.Blending = BlendingParameters.Additive;
|
||||||
b.Anchor = Anchor.CentreLeft;
|
b.Anchor = Anchor.CentreLeft;
|
||||||
@ -71,13 +77,16 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
button.Selected.ValueChanged += selected =>
|
Button.Selected.ValueChanged += selected =>
|
||||||
{
|
{
|
||||||
updateSelectionState();
|
updateSelectionState();
|
||||||
if (selected.NewValue)
|
if (selected.NewValue)
|
||||||
Selected?.Invoke(button);
|
Selected?.Invoke(Button);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
editorBeatmap?.HasTiming.BindValueChanged(hasTiming => Button.Selected.Disabled = !hasTiming.NewValue, true);
|
||||||
|
|
||||||
|
Button.Selected.BindDisabledChanged(disabled => Enabled.Value = !disabled, true);
|
||||||
updateSelectionState();
|
updateSelectionState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,8 +95,8 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
|
|||||||
if (!IsLoaded)
|
if (!IsLoaded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
BackgroundColour = button.Selected.Value ? selectedBackgroundColour : defaultBackgroundColour;
|
BackgroundColour = Button.Selected.Value ? selectedBackgroundColour : defaultBackgroundColour;
|
||||||
icon.Colour = button.Selected.Value ? selectedBubbleColour : defaultBubbleColour;
|
icon.Colour = Button.Selected.Value ? selectedBubbleColour : defaultBubbleColour;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override SpriteText CreateText() => new OsuSpriteText
|
protected override SpriteText CreateText() => new OsuSpriteText
|
||||||
@ -97,5 +106,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
|
|||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
X = 40f
|
X = 40f
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public LocalisableString TooltipText => Enabled.Value ? string.Empty : "Add at least one timing point first!";
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,7 +9,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Components.RadioButtons
|
namespace osu.Game.Screens.Edit.Components.RadioButtons
|
||||||
{
|
{
|
||||||
public class RadioButtonCollection : CompositeDrawable
|
public class EditorRadioButtonCollection : CompositeDrawable
|
||||||
{
|
{
|
||||||
private IReadOnlyList<RadioButton> items;
|
private IReadOnlyList<RadioButton> items;
|
||||||
|
|
||||||
@ -28,13 +28,13 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly FlowContainer<DrawableRadioButton> buttonContainer;
|
private readonly FlowContainer<EditorRadioButton> buttonContainer;
|
||||||
|
|
||||||
public RadioButtonCollection()
|
public EditorRadioButtonCollection()
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
InternalChild = buttonContainer = new FillFlowContainer<DrawableRadioButton>
|
InternalChild = buttonContainer = new FillFlowContainer<EditorRadioButton>
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
@ -58,7 +58,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
|
|||||||
currentlySelected = null;
|
currentlySelected = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
buttonContainer.Add(new DrawableRadioButton(button));
|
buttonContainer.Add(new EditorRadioButton(button));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,7 +17,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The item related to this button.
|
/// The item related to this button.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object Item;
|
public string Label;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A function which creates a drawable icon to represent this item. If null, a sane default should be used.
|
/// A function which creates a drawable icon to represent this item. If null, a sane default should be used.
|
||||||
@ -26,21 +26,14 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
|
|||||||
|
|
||||||
private readonly Action action;
|
private readonly Action action;
|
||||||
|
|
||||||
public RadioButton(object item, Action action, Func<Drawable> createIcon = null)
|
public RadioButton(string label, Action action, Func<Drawable> createIcon = null)
|
||||||
{
|
{
|
||||||
Item = item;
|
Label = label;
|
||||||
CreateIcon = createIcon;
|
CreateIcon = createIcon;
|
||||||
this.action = action;
|
this.action = action;
|
||||||
Selected = new BindableBool();
|
Selected = new BindableBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
public RadioButton(string item)
|
|
||||||
: this(item, null)
|
|
||||||
{
|
|
||||||
Item = item;
|
|
||||||
action = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Selects this <see cref="RadioButton"/>.
|
/// Selects this <see cref="RadioButton"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -46,12 +46,22 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
public readonly IBeatmap PlayableBeatmap;
|
public readonly IBeatmap PlayableBeatmap;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether at least one timing control point is present and providing timing information.
|
||||||
|
/// </summary>
|
||||||
|
public IBindable<bool> HasTiming => hasTiming;
|
||||||
|
|
||||||
|
private readonly Bindable<bool> hasTiming = new Bindable<bool>();
|
||||||
|
|
||||||
[CanBeNull]
|
[CanBeNull]
|
||||||
public readonly ISkin BeatmapSkin;
|
public readonly ISkin BeatmapSkin;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private BindableBeatDivisor beatDivisor { get; set; }
|
private BindableBeatDivisor beatDivisor { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private EditorClock editorClock { get; set; }
|
||||||
|
|
||||||
private readonly IBeatmapProcessor beatmapProcessor;
|
private readonly IBeatmapProcessor beatmapProcessor;
|
||||||
|
|
||||||
private readonly Dictionary<HitObject, Bindable<double>> startTimeBindables = new Dictionary<HitObject, Bindable<double>>();
|
private readonly Dictionary<HitObject, Bindable<double>> startTimeBindables = new Dictionary<HitObject, Bindable<double>>();
|
||||||
@ -238,6 +248,8 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
if (batchPendingUpdates.Count > 0)
|
if (batchPendingUpdates.Count > 0)
|
||||||
UpdateState();
|
UpdateState();
|
||||||
|
|
||||||
|
hasTiming.Value = !ReferenceEquals(ControlPointInfo.TimingPointAt(editorClock.CurrentTime), TimingControlPoint.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateState()
|
protected override void UpdateState()
|
||||||
|
@ -955,7 +955,11 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
// if arriving here and the results screen preparation task hasn't run, it's safe to say the user has not completed the beatmap.
|
// if arriving here and the results screen preparation task hasn't run, it's safe to say the user has not completed the beatmap.
|
||||||
if (prepareScoreForDisplayTask == null)
|
if (prepareScoreForDisplayTask == null)
|
||||||
|
{
|
||||||
Score.ScoreInfo.Passed = false;
|
Score.ScoreInfo.Passed = false;
|
||||||
|
// potentially should be ScoreRank.F instead? this is the best alternative for now.
|
||||||
|
Score.ScoreInfo.Rank = ScoreRank.D;
|
||||||
|
}
|
||||||
|
|
||||||
// EndPlaying() is typically called from ReplayRecorder.Dispose(). Disposal is currently asynchronous.
|
// EndPlaying() is typically called from ReplayRecorder.Dispose(). Disposal is currently asynchronous.
|
||||||
// To resolve test failures, forcefully end playing synchronously when this screen exits.
|
// To resolve test failures, forcefully end playing synchronously when this screen exits.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user