mirror of
https://github.com/ppy/osu.git
synced 2025-01-14 17:52:56 +08:00
Merge branch 'master' into master
This commit is contained in:
commit
1d6d047a69
@ -52,7 +52,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1008.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1005.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1011.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
@ -36,5 +36,7 @@ namespace osu.Game.Rulesets.Catch.Edit
|
||||
|
||||
return base.CreateHitObjectBlueprintFor(hitObject);
|
||||
}
|
||||
|
||||
protected sealed override DragBox CreateDragBox() => new ScrollingDragBox(Composer.Playfield);
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
this.playfield = playfield;
|
||||
|
||||
FlashlightSize = new Vector2(0, GetSizeFor(0));
|
||||
FlashlightSize = new Vector2(0, GetSize());
|
||||
FlashlightSmoothness = 1.4f;
|
||||
}
|
||||
|
||||
@ -66,9 +66,9 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
FlashlightPosition = playfield.CatcherArea.ToSpaceOfOtherDrawable(playfield.Catcher.DrawPosition, this);
|
||||
}
|
||||
|
||||
protected override void OnComboChange(ValueChangedEvent<int> e)
|
||||
protected override void UpdateFlashlightSize(float size)
|
||||
{
|
||||
this.TransformTo(nameof(FlashlightSize), new Vector2(0, GetSizeFor(e.NewValue)), FLASHLIGHT_FADE_DURATION);
|
||||
this.TransformTo(nameof(FlashlightSize), new Vector2(0, size), FLASHLIGHT_FADE_DURATION);
|
||||
}
|
||||
|
||||
protected override string FragmentShader => "CircularFlashlight";
|
||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
|
||||
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
|
||||
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
|
||||
scoreAccuracy = customAccuracy;
|
||||
scoreAccuracy = calculateCustomAccuracy();
|
||||
|
||||
// Arbitrary initial value for scaling pp in order to standardize distributions across game modes.
|
||||
// The specific number has no intrinsic meaning and can be adjusted as needed.
|
||||
@ -73,6 +73,12 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
/// <summary>
|
||||
/// Accuracy used to weight judgements independently from the score's actual accuracy.
|
||||
/// </summary>
|
||||
private double customAccuracy => (countPerfect * 320 + countGreat * 300 + countGood * 200 + countOk * 100 + countMeh * 50) / (totalHits * 320);
|
||||
private double calculateCustomAccuracy()
|
||||
{
|
||||
if (totalHits == 0)
|
||||
return 0;
|
||||
|
||||
return (countPerfect * 320 + countGreat * 300 + countGood * 200 + countOk * 100 + countMeh * 50) / (totalHits * 320);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,5 +33,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
}
|
||||
|
||||
protected override SelectionHandler<HitObject> CreateSelectionHandler() => new ManiaSelectionHandler();
|
||||
|
||||
protected sealed override DragBox CreateDragBox() => new ScrollingDragBox(Composer.Playfield);
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public ManiaFlashlight(ManiaModFlashlight modFlashlight)
|
||||
: base(modFlashlight)
|
||||
{
|
||||
FlashlightSize = new Vector2(DrawWidth, GetSizeFor(0));
|
||||
FlashlightSize = new Vector2(DrawWidth, GetSize());
|
||||
|
||||
AddLayout(flashlightProperties);
|
||||
}
|
||||
@ -54,9 +54,9 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnComboChange(ValueChangedEvent<int> e)
|
||||
protected override void UpdateFlashlightSize(float size)
|
||||
{
|
||||
this.TransformTo(nameof(FlashlightSize), new Vector2(DrawWidth, GetSizeFor(e.NewValue)), FLASHLIGHT_FADE_DURATION);
|
||||
this.TransformTo(nameof(FlashlightSize), new Vector2(DrawWidth, size), FLASHLIGHT_FADE_DURATION);
|
||||
}
|
||||
|
||||
protected override string FragmentShader => "RectangularFlashlight";
|
||||
|
@ -4,12 +4,14 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Mania.Skinning.Default;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@ -38,6 +40,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
private Container<DrawableHoldNoteTail> tailContainer;
|
||||
private Container<DrawableHoldNoteTick> tickContainer;
|
||||
|
||||
private PausableSkinnableSound slidingSample;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the size of the hold note covering the whole head/tail bounds. The size of this container changes as the hold note is being pressed.
|
||||
/// </summary>
|
||||
@ -108,6 +112,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
},
|
||||
tickContainer = new Container<DrawableHoldNoteTick> { RelativeSizeAxes = Axes.Both },
|
||||
tailContainer = new Container<DrawableHoldNoteTail> { RelativeSizeAxes = Axes.Both },
|
||||
slidingSample = new PausableSkinnableSound { Looping = true }
|
||||
});
|
||||
|
||||
maskedContents.AddRange(new[]
|
||||
@ -118,6 +123,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
isHitting.BindValueChanged(updateSlidingSample, true);
|
||||
}
|
||||
|
||||
protected override void OnApply()
|
||||
{
|
||||
base.OnApply();
|
||||
@ -322,5 +334,38 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
HoldStartTime = null;
|
||||
isHitting.Value = false;
|
||||
}
|
||||
|
||||
protected override void LoadSamples()
|
||||
{
|
||||
// Note: base.LoadSamples() isn't called since the slider plays the tail's hitsounds for the time being.
|
||||
|
||||
if (HitObject.SampleControlPoint == null)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}."
|
||||
+ $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}.");
|
||||
}
|
||||
|
||||
slidingSample.Samples = HitObject.CreateSlidingSamples().Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
||||
}
|
||||
|
||||
public override void StopAllSamples()
|
||||
{
|
||||
base.StopAllSamples();
|
||||
slidingSample?.Stop();
|
||||
}
|
||||
|
||||
private void updateSlidingSample(ValueChangedEvent<bool> tracking)
|
||||
{
|
||||
if (tracking.NewValue)
|
||||
slidingSample?.Play();
|
||||
else
|
||||
slidingSample?.Stop();
|
||||
}
|
||||
|
||||
protected override void OnFree()
|
||||
{
|
||||
slidingSample.Samples = null;
|
||||
base.OnFree();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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 osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -89,13 +90,15 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
|
||||
Color4 colour;
|
||||
|
||||
const int total_colours = 7;
|
||||
|
||||
if (stage.IsSpecialColumn(column))
|
||||
colour = new Color4(159, 101, 255, 255);
|
||||
else
|
||||
{
|
||||
switch (column % 8)
|
||||
switch (column % total_colours)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
colour = new Color4(240, 216, 0, 255);
|
||||
break;
|
||||
|
||||
@ -112,20 +115,19 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
break;
|
||||
|
||||
case 4:
|
||||
colour = new Color4(178, 0, 240, 255);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
colour = new Color4(0, 96, 240, 255);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
case 5:
|
||||
colour = new Color4(0, 226, 240, 255);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
case 6:
|
||||
colour = new Color4(0, 240, 96, 255);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
followDelay = modFlashlight.FollowDelay.Value;
|
||||
|
||||
FlashlightSize = new Vector2(0, GetSizeFor(0));
|
||||
FlashlightSize = new Vector2(0, GetSize());
|
||||
FlashlightSmoothness = 1.4f;
|
||||
}
|
||||
|
||||
@ -83,9 +83,9 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
protected override void OnComboChange(ValueChangedEvent<int> e)
|
||||
protected override void UpdateFlashlightSize(float size)
|
||||
{
|
||||
this.TransformTo(nameof(FlashlightSize), new Vector2(0, GetSizeFor(e.NewValue)), FLASHLIGHT_FADE_DURATION);
|
||||
this.TransformTo(nameof(FlashlightSize), new Vector2(0, size), FLASHLIGHT_FADE_DURATION);
|
||||
}
|
||||
|
||||
protected override string FragmentShader => "CircularFlashlight";
|
||||
|
@ -186,17 +186,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
private Vector2? lastPosition;
|
||||
|
||||
private bool rewinding;
|
||||
|
||||
public void UpdateProgress(double completionProgress)
|
||||
{
|
||||
Position = drawableSlider.HitObject.CurvePositionAt(completionProgress);
|
||||
|
||||
var diff = lastPosition.HasValue ? lastPosition.Value - Position : Position - drawableSlider.HitObject.CurvePositionAt(completionProgress + 0.01f);
|
||||
|
||||
if (Clock.ElapsedFrameTime != 0)
|
||||
rewinding = Clock.ElapsedFrameTime < 0;
|
||||
|
||||
// Ensure the value is substantially high enough to allow for Atan2 to get a valid angle.
|
||||
if (diff.LengthFast < 0.01f)
|
||||
return;
|
||||
|
||||
ball.Rotation = -90 + (float)(-Math.Atan2(diff.X, diff.Y) * 180 / Math.PI);
|
||||
ball.Rotation = -90 + (float)(-Math.Atan2(diff.X, diff.Y) * 180 / Math.PI) + (rewinding ? 180 : 0);
|
||||
lastPosition = Position;
|
||||
}
|
||||
}
|
||||
|
@ -34,21 +34,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
public override IList<HitSampleInfo> AuxiliarySamples => CreateSlidingSamples().Concat(TailSamples).ToArray();
|
||||
|
||||
public IList<HitSampleInfo> CreateSlidingSamples()
|
||||
{
|
||||
var slidingSamples = new List<HitSampleInfo>();
|
||||
|
||||
var normalSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL);
|
||||
if (normalSample != null)
|
||||
slidingSamples.Add(normalSample.With("sliderslide"));
|
||||
|
||||
var whistleSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_WHISTLE);
|
||||
if (whistleSample != null)
|
||||
slidingSamples.Add(whistleSample.With("sliderwhistle"));
|
||||
|
||||
return slidingSamples;
|
||||
}
|
||||
|
||||
private readonly Cached<Vector2> endPositionCache = new Cached<Vector2>();
|
||||
|
||||
public override Vector2 EndPosition => endPositionCache.IsValid ? endPositionCache.Value : endPositionCache.Value = Position + this.CurvePositionAt(1);
|
||||
|
@ -47,21 +47,21 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
{
|
||||
this.taikoPlayfield = taikoPlayfield;
|
||||
|
||||
FlashlightSize = getSizeFor(0);
|
||||
FlashlightSize = adjustSize(GetSize());
|
||||
FlashlightSmoothness = 1.4f;
|
||||
|
||||
AddLayout(flashlightProperties);
|
||||
}
|
||||
|
||||
private Vector2 getSizeFor(int combo)
|
||||
private Vector2 adjustSize(float size)
|
||||
{
|
||||
// Preserve flashlight size through the playfield's aspect adjustment.
|
||||
return new Vector2(0, GetSizeFor(combo) * taikoPlayfield.DrawHeight / TaikoPlayfield.DEFAULT_HEIGHT);
|
||||
return new Vector2(0, size * taikoPlayfield.DrawHeight / TaikoPlayfield.DEFAULT_HEIGHT);
|
||||
}
|
||||
|
||||
protected override void OnComboChange(ValueChangedEvent<int> e)
|
||||
protected override void UpdateFlashlightSize(float size)
|
||||
{
|
||||
this.TransformTo(nameof(FlashlightSize), getSizeFor(e.NewValue), FLASHLIGHT_FADE_DURATION);
|
||||
this.TransformTo(nameof(FlashlightSize), adjustSize(size), FLASHLIGHT_FADE_DURATION);
|
||||
}
|
||||
|
||||
protected override string FragmentShader => "CircularFlashlight";
|
||||
@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
FlashlightPosition = ToLocalSpace(taikoPlayfield.HitTarget.ScreenSpaceDrawQuad.Centre);
|
||||
|
||||
ClearTransforms(targetMember: nameof(FlashlightSize));
|
||||
FlashlightSize = getSizeFor(Combo.Value);
|
||||
FlashlightSize = adjustSize(Combo.Value);
|
||||
|
||||
flashlightProperties.Validate();
|
||||
}
|
||||
|
@ -29,16 +29,18 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
private TimelineBlueprintContainer blueprintContainer
|
||||
=> Editor.ChildrenOfType<TimelineBlueprintContainer>().First();
|
||||
|
||||
private Vector2 getPosition(HitObject hitObject) =>
|
||||
blueprintContainer.SelectionBlueprints.First(s => s.Item == hitObject).ScreenSpaceDrawQuad.Centre;
|
||||
|
||||
private Vector2 getMiddlePosition(HitObject hitObject1, HitObject hitObject2) =>
|
||||
(getPosition(hitObject1) + getPosition(hitObject2)) / 2;
|
||||
|
||||
private void moveMouseToObject(Func<HitObject> targetFunc)
|
||||
{
|
||||
AddStep("move mouse to object", () =>
|
||||
{
|
||||
var pos = blueprintContainer.SelectionBlueprints
|
||||
.First(s => s.Item == targetFunc())
|
||||
.ChildrenOfType<TimelineHitObjectBlueprint>()
|
||||
.First().ScreenSpaceDrawQuad.Centre;
|
||||
|
||||
InputManager.MoveMouseTo(pos);
|
||||
var hitObject = targetFunc();
|
||||
InputManager.MoveMouseTo(getPosition(hitObject));
|
||||
});
|
||||
}
|
||||
|
||||
@ -262,6 +264,56 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBasicDragSelection()
|
||||
{
|
||||
var addedObjects = new[]
|
||||
{
|
||||
new HitCircle { StartTime = 0 },
|
||||
new HitCircle { StartTime = 500, Position = new Vector2(100) },
|
||||
new HitCircle { StartTime = 1000, Position = new Vector2(200) },
|
||||
new HitCircle { StartTime = 1500, Position = new Vector2(300) },
|
||||
};
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects));
|
||||
|
||||
AddStep("move mouse", () => InputManager.MoveMouseTo(getMiddlePosition(addedObjects[0], addedObjects[1])));
|
||||
AddStep("mouse down", () => InputManager.PressButton(MouseButton.Left));
|
||||
|
||||
AddStep("drag to select", () => InputManager.MoveMouseTo(getMiddlePosition(addedObjects[2], addedObjects[3])));
|
||||
assertSelectionIs(new[] { addedObjects[1], addedObjects[2] });
|
||||
|
||||
AddStep("drag to deselect", () => InputManager.MoveMouseTo(getMiddlePosition(addedObjects[1], addedObjects[2])));
|
||||
assertSelectionIs(new[] { addedObjects[1] });
|
||||
|
||||
AddStep("mouse up", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
assertSelectionIs(new[] { addedObjects[1] });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFastDragSelection()
|
||||
{
|
||||
var addedObjects = new[]
|
||||
{
|
||||
new HitCircle { StartTime = 0 },
|
||||
new HitCircle { StartTime = 500 },
|
||||
new HitCircle { StartTime = 20000, Position = new Vector2(100) },
|
||||
new HitCircle { StartTime = 31000, Position = new Vector2(200) },
|
||||
new HitCircle { StartTime = 60000, Position = new Vector2(300) },
|
||||
};
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects));
|
||||
|
||||
AddStep("move mouse", () => InputManager.MoveMouseTo(getMiddlePosition(addedObjects[0], addedObjects[1])));
|
||||
AddStep("mouse down", () => InputManager.PressButton(MouseButton.Left));
|
||||
AddStep("start drag", () => InputManager.MoveMouseTo(getPosition(addedObjects[1])));
|
||||
|
||||
AddStep("jump editor clock", () => EditorClock.Seek(30000));
|
||||
AddStep("jump editor clock", () => EditorClock.Seek(60000));
|
||||
AddStep("end drag", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
assertSelectionIs(addedObjects.Skip(1));
|
||||
AddAssert("all blueprints are present", () => blueprintContainer.SelectionBlueprints.Count == EditorBeatmap.SelectedHitObjects.Count);
|
||||
}
|
||||
|
||||
private void assertSelectionIs(IEnumerable<HitObject> hitObjects)
|
||||
=> AddAssert("correct hitobjects selected", () => EditorBeatmap.SelectedHitObjects.OrderBy(h => h.StartTime).SequenceEqual(hitObjects));
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Gameplay;
|
||||
@ -148,6 +149,42 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("key counters still hidden", () => !keyCounterFlow.IsPresent);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInputDoesntWorkWhenHUDHidden()
|
||||
{
|
||||
SongProgressBar getSongProgress() => hudOverlay.ChildrenOfType<SongProgressBar>().Single();
|
||||
|
||||
bool seeked = false;
|
||||
|
||||
createNew();
|
||||
|
||||
AddStep("bind seek", () =>
|
||||
{
|
||||
seeked = false;
|
||||
|
||||
var progress = getSongProgress();
|
||||
|
||||
progress.ShowHandle = true;
|
||||
progress.OnSeek += _ => seeked = true;
|
||||
});
|
||||
|
||||
AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
|
||||
AddUntilStep("hidetarget is hidden", () => !hideTarget.IsPresent);
|
||||
|
||||
AddStep("attempt seek", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(getSongProgress());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddAssert("seek not performed", () => !seeked);
|
||||
|
||||
AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true);
|
||||
|
||||
AddStep("attempt seek", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("seek performed", () => seeked);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHiddenHUDDoesntBlockComponentUpdates()
|
||||
{
|
||||
|
@ -189,6 +189,16 @@ Line after image";
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFlag()
|
||||
{
|
||||
AddStep("Add flag", () =>
|
||||
{
|
||||
markdownContainer.CurrentPath = @"https://dev.ppy.sh";
|
||||
markdownContainer.Text = "::{flag=\"AU\"}:: ::{flag=\"ZZ\"}::";
|
||||
});
|
||||
}
|
||||
|
||||
private class TestMarkdownContainer : WikiMarkdownContainer
|
||||
{
|
||||
public LinkInline Link;
|
||||
|
File diff suppressed because one or more lines are too long
@ -45,7 +45,7 @@ namespace osu.Game.Database
|
||||
|
||||
public bool Download(T model, bool minimiseDownloadSize = false) => Download(model, minimiseDownloadSize, null);
|
||||
|
||||
public void DownloadAsUpdate(TModel originalModel) => Download(originalModel, false, originalModel);
|
||||
public void DownloadAsUpdate(TModel originalModel, bool minimiseDownloadSize) => Download(originalModel, minimiseDownloadSize, originalModel);
|
||||
|
||||
protected bool Download(T model, bool minimiseDownloadSize, TModel? originalModel)
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
using Markdig;
|
||||
using Markdig.Extensions.AutoLinks;
|
||||
using Markdig.Extensions.CustomContainers;
|
||||
using Markdig.Extensions.EmphasisExtras;
|
||||
using Markdig.Extensions.Footnotes;
|
||||
using Markdig.Extensions.Tables;
|
||||
@ -32,6 +33,12 @@ namespace osu.Game.Graphics.Containers.Markdown
|
||||
/// <seealso cref="AutoLinkExtension"/>
|
||||
protected virtual bool Autolinks => false;
|
||||
|
||||
/// <summary>
|
||||
/// Allows this markdown container to parse custom containers (used for flags and infoboxes).
|
||||
/// </summary>
|
||||
/// <seealso cref="CustomContainerExtension"/>
|
||||
protected virtual bool CustomContainers => false;
|
||||
|
||||
public OsuMarkdownContainer()
|
||||
{
|
||||
LineSpacing = 21;
|
||||
@ -107,6 +114,9 @@ namespace osu.Game.Graphics.Containers.Markdown
|
||||
if (Autolinks)
|
||||
pipeline = pipeline.UseAutoLinks();
|
||||
|
||||
if (CustomContainers)
|
||||
pipeline.UseCustomContainers();
|
||||
|
||||
return pipeline.Build();
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,9 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Markdig.Extensions.CustomContainers;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
@ -11,6 +14,9 @@ using osu.Framework.Graphics.Containers.Markdown;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Users.Drawables;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Graphics.Containers.Markdown
|
||||
{
|
||||
@ -33,6 +39,31 @@ namespace osu.Game.Graphics.Containers.Markdown
|
||||
protected override SpriteText CreateEmphasisedSpriteText(bool bold, bool italic)
|
||||
=> CreateSpriteText().With(t => t.Font = t.Font.With(weight: bold ? FontWeight.Bold : FontWeight.Regular, italics: italic));
|
||||
|
||||
protected override void AddCustomComponent(CustomContainerInline inline)
|
||||
{
|
||||
if (!(inline.FirstChild is LiteralInline literal))
|
||||
{
|
||||
base.AddCustomComponent(inline);
|
||||
return;
|
||||
}
|
||||
|
||||
string[] attributes = literal.Content.ToString().Trim(' ', '{', '}').Split();
|
||||
string flagAttribute = attributes.SingleOrDefault(a => a.StartsWith(@"flag", StringComparison.Ordinal));
|
||||
|
||||
if (flagAttribute == null)
|
||||
{
|
||||
base.AddCustomComponent(inline);
|
||||
return;
|
||||
}
|
||||
|
||||
string flag = flagAttribute.Split('=').Last().Trim('"');
|
||||
|
||||
if (!Enum.TryParse<CountryCode>(flag, out var countryCode))
|
||||
countryCode = CountryCode.Unknown;
|
||||
|
||||
AddDrawable(new DrawableFlag(countryCode) { Size = new Vector2(20, 15) });
|
||||
}
|
||||
|
||||
private class OsuMarkdownInlineCode : Container
|
||||
{
|
||||
[Resolved]
|
||||
|
@ -563,6 +563,15 @@ namespace osu.Game
|
||||
|
||||
// This should be able to be performed from song select, but that is disabled for now
|
||||
// due to the weird decoupled ruleset logic (which can cause a crash in certain filter scenarios).
|
||||
//
|
||||
// As a special case, if the beatmap and ruleset already match, allow immediately displaying the score from song select.
|
||||
// This is guaranteed to not crash, and feels better from a user's perspective (ie. if they are clicking a score in the
|
||||
// song select leaderboard).
|
||||
IEnumerable<Type> validScreens =
|
||||
Beatmap.Value.BeatmapInfo.Equals(databasedBeatmap) && Ruleset.Value.Equals(databasedScore.ScoreInfo.Ruleset)
|
||||
? new[] { typeof(SongSelect) }
|
||||
: Array.Empty<Type>();
|
||||
|
||||
PerformFromScreen(screen =>
|
||||
{
|
||||
Logger.Log($"{nameof(PresentScore)} updating beatmap ({databasedBeatmap}) and ruleset ({databasedScore.ScoreInfo.Ruleset}) to match score");
|
||||
@ -580,7 +589,7 @@ namespace osu.Game
|
||||
screen.Push(new SoloResultsScreen(databasedScore.ScoreInfo, false));
|
||||
break;
|
||||
}
|
||||
});
|
||||
}, validScreens: validScreens);
|
||||
}
|
||||
|
||||
public override Task Import(params ImportTask[] imports)
|
||||
|
@ -387,14 +387,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
if (bindTarget != null) bindTarget.IsBinding = true;
|
||||
}
|
||||
|
||||
private void updateStoreFromButton(KeyButton button)
|
||||
{
|
||||
realm.Run(r =>
|
||||
{
|
||||
var binding = r.Find<RealmKeyBinding>(((IHasGuidPrimaryKey)button.KeyBinding).ID);
|
||||
r.Write(() => binding.KeyCombinationString = button.KeyBinding.KeyCombinationString);
|
||||
});
|
||||
}
|
||||
private void updateStoreFromButton(KeyButton button) =>
|
||||
realm.WriteAsync(r => r.Find<RealmKeyBinding>(button.KeyBinding.ID).KeyCombinationString = button.KeyBinding.KeyCombinationString);
|
||||
|
||||
private void updateIsDefaultValue()
|
||||
{
|
||||
|
@ -4,6 +4,7 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Linq;
|
||||
using Markdig.Extensions.CustomContainers;
|
||||
using Markdig.Extensions.Yaml;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
@ -16,6 +17,7 @@ namespace osu.Game.Overlays.Wiki.Markdown
|
||||
public class WikiMarkdownContainer : OsuMarkdownContainer
|
||||
{
|
||||
protected override bool Footnotes => true;
|
||||
protected override bool CustomContainers => true;
|
||||
|
||||
public string CurrentPath
|
||||
{
|
||||
@ -26,6 +28,11 @@ namespace osu.Game.Overlays.Wiki.Markdown
|
||||
{
|
||||
switch (markdownObject)
|
||||
{
|
||||
case CustomContainer:
|
||||
// infoboxes are parsed into CustomContainer objects, but we don't have support for infoboxes yet.
|
||||
// todo: add support for infobox.
|
||||
break;
|
||||
|
||||
case YamlFrontMatterBlock yamlFrontMatterBlock:
|
||||
container.Add(new WikiNoticeContainer(yamlFrontMatterBlock));
|
||||
break;
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -12,7 +11,6 @@ using osu.Framework.Graphics.Rendering.Vertices;
|
||||
using osu.Framework.Graphics.Shaders;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.OpenGL.Vertices;
|
||||
@ -20,6 +18,7 @@ using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -84,8 +83,6 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
flashlight.Combo.BindTo(Combo);
|
||||
drawableRuleset.KeyBindingInputManager.Add(flashlight);
|
||||
|
||||
flashlight.Breaks = drawableRuleset.Beatmap.Breaks;
|
||||
}
|
||||
|
||||
protected abstract Flashlight CreateFlashlight();
|
||||
@ -100,8 +97,6 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
public override bool RemoveCompletedTransforms => false;
|
||||
|
||||
public List<BreakPeriod> Breaks = new List<BreakPeriod>();
|
||||
|
||||
private readonly float defaultFlashlightSize;
|
||||
private readonly float sizeMultiplier;
|
||||
private readonly bool comboBasedSize;
|
||||
@ -119,37 +114,36 @@ namespace osu.Game.Rulesets.Mods
|
||||
shader = shaderManager.Load("PositionAndColour", FragmentShader);
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private Player? player { get; set; }
|
||||
|
||||
private readonly IBindable<bool> isBreakTime = new BindableBool();
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Combo.ValueChanged += OnComboChange;
|
||||
Combo.ValueChanged += _ => UpdateFlashlightSize(GetSize());
|
||||
|
||||
using (BeginAbsoluteSequence(0))
|
||||
if (player != null)
|
||||
{
|
||||
foreach (var breakPeriod in Breaks)
|
||||
{
|
||||
if (!breakPeriod.HasEffect)
|
||||
continue;
|
||||
|
||||
if (breakPeriod.Duration < FLASHLIGHT_FADE_DURATION * 2) continue;
|
||||
|
||||
this.Delay(breakPeriod.StartTime + FLASHLIGHT_FADE_DURATION).FadeOutFromOne(FLASHLIGHT_FADE_DURATION);
|
||||
this.Delay(breakPeriod.EndTime - FLASHLIGHT_FADE_DURATION).FadeInFromZero(FLASHLIGHT_FADE_DURATION);
|
||||
}
|
||||
isBreakTime.BindTo(player.IsBreakTime);
|
||||
isBreakTime.BindValueChanged(_ => UpdateFlashlightSize(GetSize()), true);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void OnComboChange(ValueChangedEvent<int> e);
|
||||
protected abstract void UpdateFlashlightSize(float size);
|
||||
|
||||
protected abstract string FragmentShader { get; }
|
||||
|
||||
protected float GetSizeFor(int combo)
|
||||
protected float GetSize()
|
||||
{
|
||||
float size = defaultFlashlightSize * sizeMultiplier;
|
||||
|
||||
if (comboBasedSize)
|
||||
size *= GetComboScaleFor(combo);
|
||||
if (isBreakTime.Value)
|
||||
size *= 2.5f;
|
||||
else if (comboBasedSize)
|
||||
size *= GetComboScaleFor(Combo.Value);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
@ -198,6 +199,21 @@ namespace osu.Game.Rulesets.Objects
|
||||
/// </summary>
|
||||
[NotNull]
|
||||
protected virtual HitWindows CreateHitWindows() => new HitWindows();
|
||||
|
||||
public IList<HitSampleInfo> CreateSlidingSamples()
|
||||
{
|
||||
var slidingSamples = new List<HitSampleInfo>();
|
||||
|
||||
var normalSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL);
|
||||
if (normalSample != null)
|
||||
slidingSamples.Add(normalSample.With("sliderslide"));
|
||||
|
||||
var whistleSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_WHISTLE);
|
||||
if (whistleSample != null)
|
||||
slidingSamples.Add(whistleSample.With("sliderwhistle"));
|
||||
|
||||
return slidingSamples;
|
||||
}
|
||||
}
|
||||
|
||||
public static class HitObjectExtensions
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
@ -13,7 +12,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
@ -61,25 +59,31 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
foreach (object o in args.NewItems)
|
||||
SelectionBlueprints.FirstOrDefault(b => b.Item == o)?.Select();
|
||||
{
|
||||
if (blueprintMap.TryGetValue((T)o, out var blueprint))
|
||||
blueprint.Select();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
foreach (object o in args.OldItems)
|
||||
SelectionBlueprints.FirstOrDefault(b => b.Item == o)?.Deselect();
|
||||
{
|
||||
if (blueprintMap.TryGetValue((T)o, out var blueprint))
|
||||
blueprint.Deselect();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
SelectionHandler = CreateSelectionHandler();
|
||||
SelectionHandler.DeselectAll = deselectAll;
|
||||
SelectionHandler.DeselectAll = DeselectAll;
|
||||
SelectionHandler.SelectedItems.BindTo(SelectedItems);
|
||||
|
||||
AddRangeInternal(new[]
|
||||
{
|
||||
DragBox = CreateDragBox(selectBlueprintsFromDragRectangle),
|
||||
DragBox = CreateDragBox(),
|
||||
SelectionHandler,
|
||||
SelectionBlueprints = CreateSelectionBlueprintContainer(),
|
||||
SelectionHandler.CreateProxy(),
|
||||
@ -101,12 +105,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
[CanBeNull]
|
||||
protected virtual SelectionBlueprint<T> CreateBlueprintFor(T item) => null;
|
||||
|
||||
protected virtual DragBox CreateDragBox(Action<RectangleF> performSelect) => new DragBox(performSelect);
|
||||
|
||||
/// <summary>
|
||||
/// Whether this component is in a state where items outside a drag selection should be deselected. If false, selection will only be added to.
|
||||
/// </summary>
|
||||
protected virtual bool AllowDeselectionDuringDrag => true;
|
||||
protected virtual DragBox CreateDragBox() => new DragBox();
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
@ -142,7 +141,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
if (endClickSelection(e) || ClickedBlueprint != null)
|
||||
return true;
|
||||
|
||||
deselectAll();
|
||||
DeselectAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -171,11 +170,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
finishSelectionMovement();
|
||||
}
|
||||
|
||||
private MouseButtonEvent lastDragEvent;
|
||||
|
||||
protected override bool OnDragStart(DragStartEvent e)
|
||||
{
|
||||
if (e.Button == MouseButton.Right)
|
||||
return false;
|
||||
|
||||
lastDragEvent = e;
|
||||
|
||||
if (movementBlueprints != null)
|
||||
{
|
||||
isDraggingBlueprint = true;
|
||||
@ -183,30 +186,21 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DragBox.HandleDrag(e))
|
||||
{
|
||||
DragBox.Show();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
DragBox.HandleDrag(e);
|
||||
DragBox.Show();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnDrag(DragEvent e)
|
||||
{
|
||||
if (e.Button == MouseButton.Right)
|
||||
return;
|
||||
|
||||
if (DragBox.State == Visibility.Visible)
|
||||
DragBox.HandleDrag(e);
|
||||
lastDragEvent = e;
|
||||
|
||||
moveCurrentSelection(e);
|
||||
}
|
||||
|
||||
protected override void OnDragEnd(DragEndEvent e)
|
||||
{
|
||||
if (e.Button == MouseButton.Right)
|
||||
return;
|
||||
lastDragEvent = null;
|
||||
|
||||
if (isDraggingBlueprint)
|
||||
{
|
||||
@ -214,8 +208,19 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
changeHandler?.EndChange();
|
||||
}
|
||||
|
||||
if (DragBox.State == Visibility.Visible)
|
||||
DragBox.Hide();
|
||||
DragBox.Hide();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (lastDragEvent != null && DragBox.State == Visibility.Visible)
|
||||
{
|
||||
lastDragEvent.Target = this;
|
||||
DragBox.HandleDrag(lastDragEvent);
|
||||
UpdateSelectionFromDragBox();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -233,7 +238,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
if (!SelectionHandler.SelectedBlueprints.Any())
|
||||
return false;
|
||||
|
||||
deselectAll();
|
||||
DeselectAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -380,44 +385,39 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Select all masks in a given rectangle selection area.
|
||||
/// Select all blueprints in a selection area specified by <see cref="DragBox"/>.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle to perform a selection on in screen-space coordinates.</param>
|
||||
private void selectBlueprintsFromDragRectangle(RectangleF rect)
|
||||
protected virtual void UpdateSelectionFromDragBox()
|
||||
{
|
||||
var quad = DragBox.Box.ScreenSpaceDrawQuad;
|
||||
|
||||
foreach (var blueprint in SelectionBlueprints)
|
||||
{
|
||||
// only run when utmost necessary to avoid unnecessary rect computations.
|
||||
bool isValidForSelection() => blueprint.IsAlive && blueprint.IsPresent && rect.Contains(blueprint.ScreenSpaceSelectionPoint);
|
||||
|
||||
switch (blueprint.State)
|
||||
{
|
||||
case SelectionState.NotSelected:
|
||||
if (isValidForSelection())
|
||||
blueprint.Select();
|
||||
case SelectionState.Selected:
|
||||
// Selection is preserved even after blueprint becomes dead.
|
||||
if (!quad.Contains(blueprint.ScreenSpaceSelectionPoint))
|
||||
blueprint.Deselect();
|
||||
break;
|
||||
|
||||
case SelectionState.Selected:
|
||||
if (AllowDeselectionDuringDrag && !isValidForSelection())
|
||||
blueprint.Deselect();
|
||||
case SelectionState.NotSelected:
|
||||
if (blueprint.IsAlive && blueprint.IsPresent && quad.Contains(blueprint.ScreenSpaceSelectionPoint))
|
||||
blueprint.Select();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects all <see cref="SelectionBlueprint{T}"/>s.
|
||||
/// Select all currently-present items.
|
||||
/// </summary>
|
||||
protected virtual void SelectAll()
|
||||
{
|
||||
// Scheduled to allow the change in lifetime to take place.
|
||||
Schedule(() => SelectionBlueprints.ToList().ForEach(m => m.Select()));
|
||||
}
|
||||
protected abstract void SelectAll();
|
||||
|
||||
/// <summary>
|
||||
/// Deselects all selected <see cref="SelectionBlueprint{T}"/>s.
|
||||
/// Deselect all selected items.
|
||||
/// </summary>
|
||||
private void deselectAll() => SelectionHandler.SelectedBlueprints.ToList().ForEach(m => m.Deselect());
|
||||
protected void DeselectAll() => SelectedItems.Clear();
|
||||
|
||||
protected virtual void OnBlueprintSelected(SelectionBlueprint<T> blueprint)
|
||||
{
|
||||
|
@ -12,7 +12,6 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
@ -37,7 +36,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
protected new EditorSelectionHandler SelectionHandler => (EditorSelectionHandler)base.SelectionHandler;
|
||||
|
||||
private PlacementBlueprint currentPlacement;
|
||||
private InputManager inputManager;
|
||||
|
||||
/// <remarks>
|
||||
/// Positional input must be received outside the container's bounds,
|
||||
@ -66,8 +64,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
inputManager = GetContainingInputManager();
|
||||
|
||||
Beatmap.HitObjectAdded += hitObjectAdded;
|
||||
|
||||
// updates to selected are handled for us by SelectionHandler.
|
||||
@ -220,7 +216,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
|
||||
private void updatePlacementPosition()
|
||||
{
|
||||
var snapResult = Composer.FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position);
|
||||
var snapResult = Composer.FindSnappedPositionAndTime(InputManager.CurrentState.Mouse.Position);
|
||||
|
||||
// if no time was found from positional snapping, we should still quantize to the beat.
|
||||
snapResult.Time ??= Beatmap.SnapTime(EditorClock.CurrentTime, null);
|
||||
|
@ -8,7 +8,6 @@ using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Layout;
|
||||
@ -21,18 +20,13 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// </summary>
|
||||
public class DragBox : CompositeDrawable, IStateful<Visibility>
|
||||
{
|
||||
protected readonly Action<RectangleF> PerformSelection;
|
||||
|
||||
protected Drawable Box;
|
||||
public Drawable Box { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="DragBox"/>.
|
||||
/// </summary>
|
||||
/// <param name="performSelection">A delegate that performs drag selection.</param>
|
||||
public DragBox(Action<RectangleF> performSelection)
|
||||
public DragBox()
|
||||
{
|
||||
PerformSelection = performSelection;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
AlwaysPresent = true;
|
||||
Alpha = 0;
|
||||
@ -46,30 +40,14 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
|
||||
protected virtual Drawable CreateBox() => new BoxWithBorders();
|
||||
|
||||
private RectangleF? dragRectangle;
|
||||
|
||||
/// <summary>
|
||||
/// Handle a forwarded mouse event.
|
||||
/// </summary>
|
||||
/// <param name="e">The mouse event.</param>
|
||||
/// <returns>Whether the event should be handled and blocking.</returns>
|
||||
public virtual bool HandleDrag(MouseButtonEvent e)
|
||||
public virtual void HandleDrag(MouseButtonEvent e)
|
||||
{
|
||||
var dragPosition = e.ScreenSpaceMousePosition;
|
||||
var dragStartPosition = e.ScreenSpaceMouseDownPosition;
|
||||
|
||||
var dragQuad = new Quad(dragStartPosition.X, dragStartPosition.Y, dragPosition.X - dragStartPosition.X, dragPosition.Y - dragStartPosition.Y);
|
||||
|
||||
// We use AABBFloat instead of RectangleF since it handles negative sizes for us
|
||||
var rec = dragQuad.AABBFloat;
|
||||
dragRectangle = rec;
|
||||
|
||||
var topLeft = ToLocalSpace(rec.TopLeft);
|
||||
var bottomRight = ToLocalSpace(rec.BottomRight);
|
||||
|
||||
Box.Position = topLeft;
|
||||
Box.Size = bottomRight - topLeft;
|
||||
return true;
|
||||
Box.Position = Vector2.ComponentMin(e.MouseDownPosition, e.MousePosition);
|
||||
Box.Size = Vector2.ComponentMax(e.MouseDownPosition, e.MousePosition) - Box.Position;
|
||||
}
|
||||
|
||||
private Visibility state;
|
||||
@ -87,19 +65,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (dragRectangle != null)
|
||||
PerformSelection?.Invoke(dragRectangle.Value);
|
||||
}
|
||||
|
||||
public override void Hide()
|
||||
{
|
||||
State = Visibility.Hidden;
|
||||
dragRectangle = null;
|
||||
}
|
||||
public override void Hide() => State = Visibility.Hidden;
|
||||
|
||||
public override void Show() => State = Visibility.Visible;
|
||||
|
||||
|
@ -8,6 +8,7 @@ using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -27,6 +28,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
|
||||
private HitObjectUsageEventBuffer usageEventBuffer;
|
||||
|
||||
protected InputManager InputManager { get; private set; }
|
||||
|
||||
protected EditorBlueprintContainer(HitObjectComposer composer)
|
||||
{
|
||||
Composer = composer;
|
||||
@ -42,6 +45,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
InputManager = GetContainingInputManager();
|
||||
|
||||
Beatmap.HitObjectAdded += AddBlueprintFor;
|
||||
Beatmap.HitObjectRemoved += RemoveBlueprintFor;
|
||||
|
||||
@ -66,8 +71,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
protected override IEnumerable<SelectionBlueprint<HitObject>> SortForMovement(IReadOnlyList<SelectionBlueprint<HitObject>> blueprints)
|
||||
=> blueprints.OrderBy(b => b.Item.StartTime);
|
||||
|
||||
protected override bool AllowDeselectionDuringDrag => !EditorClock.IsRunning;
|
||||
|
||||
protected override bool ApplySnapResult(SelectionBlueprint<HitObject>[] blueprints, SnapResult result)
|
||||
{
|
||||
if (!base.ApplySnapResult(blueprints, result))
|
||||
@ -133,8 +136,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
protected override void SelectAll()
|
||||
{
|
||||
Composer.Playfield.KeepAllAlive();
|
||||
|
||||
base.SelectAll();
|
||||
SelectedItems.AddRange(Beatmap.HitObjects.Except(SelectedItems).ToArray());
|
||||
}
|
||||
|
||||
protected override void OnBlueprintSelected(SelectionBlueprint<HitObject> blueprint)
|
||||
|
64
osu.Game/Screens/Edit/Compose/Components/ScrollingDragBox.cs
Normal file
64
osu.Game/Screens/Edit/Compose/Components/ScrollingDragBox.cs
Normal file
@ -0,0 +1,64 @@
|
||||
// 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 osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Compose.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="DragBox"/> that scrolls along with the scrolling playfield.
|
||||
/// </summary>
|
||||
public class ScrollingDragBox : DragBox
|
||||
{
|
||||
public double MinTime { get; private set; }
|
||||
|
||||
public double MaxTime { get; private set; }
|
||||
|
||||
private double? startTime;
|
||||
|
||||
private readonly ScrollingPlayfield playfield;
|
||||
|
||||
public ScrollingDragBox(Playfield playfield)
|
||||
{
|
||||
this.playfield = playfield as ScrollingPlayfield ?? throw new ArgumentException("Playfield must be of type {nameof(ScrollingPlayfield)} to use this class.", nameof(playfield));
|
||||
}
|
||||
|
||||
public override void HandleDrag(MouseButtonEvent e)
|
||||
{
|
||||
base.HandleDrag(e);
|
||||
|
||||
startTime ??= playfield.TimeAtScreenSpacePosition(e.ScreenSpaceMouseDownPosition);
|
||||
double endTime = playfield.TimeAtScreenSpacePosition(e.ScreenSpaceMousePosition);
|
||||
|
||||
MinTime = Math.Min(startTime.Value, endTime);
|
||||
MaxTime = Math.Max(startTime.Value, endTime);
|
||||
|
||||
var startPos = ToLocalSpace(playfield.ScreenSpacePositionAtTime(startTime.Value));
|
||||
var endPos = ToLocalSpace(playfield.ScreenSpacePositionAtTime(endTime));
|
||||
|
||||
switch (playfield.ScrollingInfo.Direction.Value)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
case ScrollingDirection.Down:
|
||||
Box.Y = Math.Min(startPos.Y, endPos.Y);
|
||||
Box.Height = Math.Max(startPos.Y, endPos.Y) - Box.Y;
|
||||
break;
|
||||
|
||||
case ScrollingDirection.Left:
|
||||
case ScrollingDirection.Right:
|
||||
Box.X = Math.Min(startPos.X, endPos.X);
|
||||
Box.Width = Math.Max(startPos.X, endPos.X) - Box.X;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Hide()
|
||||
{
|
||||
base.Hide();
|
||||
startTime = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -305,7 +305,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
|
||||
protected void DeleteSelected()
|
||||
{
|
||||
DeleteItems(selectedBlueprints.Select(b => b.Item));
|
||||
DeleteItems(SelectedItems.ToArray());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -304,10 +304,20 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
/// </summary>
|
||||
public double VisibleRange => editorClock.TrackLength / Zoom;
|
||||
|
||||
public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All) =>
|
||||
new SnapResult(screenSpacePosition, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(screenSpacePosition))));
|
||||
public double TimeAtPosition(float x)
|
||||
{
|
||||
return x / Content.DrawWidth * editorClock.TrackLength;
|
||||
}
|
||||
|
||||
private double getTimeFromPosition(Vector2 localPosition) =>
|
||||
(localPosition.X / Content.DrawWidth) * editorClock.TrackLength;
|
||||
public float PositionAtTime(double time)
|
||||
{
|
||||
return (float)(time / editorClock.TrackLength * Content.DrawWidth);
|
||||
}
|
||||
|
||||
public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All)
|
||||
{
|
||||
double time = TimeAtPosition(Content.ToLocalSpace(screenSpacePosition).X);
|
||||
return new SnapResult(screenSpacePosition, beatSnapProvider.SnapTime(time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
@ -13,7 +12,6 @@ using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Utils;
|
||||
@ -31,10 +29,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
[Resolved(CanBeNull = true)]
|
||||
private Timeline timeline { get; set; }
|
||||
|
||||
private DragEvent lastDragEvent;
|
||||
private Bindable<HitObject> placement;
|
||||
private SelectionBlueprint<HitObject> placementBlueprint;
|
||||
|
||||
private bool hitObjectDragged;
|
||||
|
||||
/// <remarks>
|
||||
/// Positional input must be received outside the container's bounds,
|
||||
/// in order to handle timeline blueprints which are stacked offscreen.
|
||||
@ -65,7 +64,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
DragBox.Alpha = 0;
|
||||
|
||||
placement = Beatmap.PlacementObject.GetBoundCopy();
|
||||
placement.ValueChanged += placementChanged;
|
||||
@ -93,24 +91,18 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
|
||||
protected override Container<SelectionBlueprint<HitObject>> CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
protected override void OnDrag(DragEvent e)
|
||||
protected override bool OnDragStart(DragStartEvent e)
|
||||
{
|
||||
handleScrollViaDrag(e);
|
||||
if (!base.ReceivePositionalInputAt(e.ScreenSpaceMouseDownPosition))
|
||||
return false;
|
||||
|
||||
base.OnDrag(e);
|
||||
}
|
||||
|
||||
protected override void OnDragEnd(DragEndEvent e)
|
||||
{
|
||||
base.OnDragEnd(e);
|
||||
lastDragEvent = null;
|
||||
return base.OnDragStart(e);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
// trigger every frame so drags continue to update selection while playback is scrolling the timeline.
|
||||
if (lastDragEvent != null)
|
||||
OnDrag(lastDragEvent);
|
||||
if (IsDragged || hitObjectDragged)
|
||||
handleScrollViaDrag();
|
||||
|
||||
if (Composer != null && timeline != null)
|
||||
{
|
||||
@ -165,30 +157,45 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
return new TimelineHitObjectBlueprint(item)
|
||||
{
|
||||
OnDragHandled = handleScrollViaDrag,
|
||||
OnDragHandled = e => hitObjectDragged = e != null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override DragBox CreateDragBox(Action<RectangleF> performSelect) => new TimelineDragBox(performSelect);
|
||||
protected sealed override DragBox CreateDragBox() => new TimelineDragBox();
|
||||
|
||||
private void handleScrollViaDrag(DragEvent e)
|
||||
protected override void UpdateSelectionFromDragBox()
|
||||
{
|
||||
lastDragEvent = e;
|
||||
var dragBox = (TimelineDragBox)DragBox;
|
||||
double minTime = dragBox.MinTime;
|
||||
double maxTime = dragBox.MaxTime;
|
||||
|
||||
if (lastDragEvent == null)
|
||||
return;
|
||||
SelectedItems.RemoveAll(hitObject => !shouldBeSelected(hitObject));
|
||||
|
||||
if (timeline != null)
|
||||
foreach (var hitObject in Beatmap.HitObjects.Except(SelectedItems).Where(shouldBeSelected))
|
||||
{
|
||||
var timelineQuad = timeline.ScreenSpaceDrawQuad;
|
||||
float mouseX = e.ScreenSpaceMousePosition.X;
|
||||
|
||||
// scroll if in a drag and dragging outside visible extents
|
||||
if (mouseX > timelineQuad.TopRight.X)
|
||||
timeline.ScrollBy((float)((mouseX - timelineQuad.TopRight.X) / 10 * Clock.ElapsedFrameTime));
|
||||
else if (mouseX < timelineQuad.TopLeft.X)
|
||||
timeline.ScrollBy((float)((mouseX - timelineQuad.TopLeft.X) / 10 * Clock.ElapsedFrameTime));
|
||||
Composer.Playfield.SetKeepAlive(hitObject, true);
|
||||
SelectedItems.Add(hitObject);
|
||||
}
|
||||
|
||||
bool shouldBeSelected(HitObject hitObject)
|
||||
{
|
||||
double midTime = (hitObject.StartTime + hitObject.GetEndTime()) / 2;
|
||||
return minTime <= midTime && midTime <= maxTime;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleScrollViaDrag()
|
||||
{
|
||||
if (timeline == null) return;
|
||||
|
||||
var timelineQuad = timeline.ScreenSpaceDrawQuad;
|
||||
float mouseX = InputManager.CurrentState.Mouse.Position.X;
|
||||
|
||||
// scroll if in a drag and dragging outside visible extents
|
||||
if (mouseX > timelineQuad.TopRight.X)
|
||||
timeline.ScrollBy((float)((mouseX - timelineQuad.TopRight.X) / 10 * Clock.ElapsedFrameTime));
|
||||
else if (mouseX < timelineQuad.TopLeft.X)
|
||||
timeline.ScrollBy((float)((mouseX - timelineQuad.TopLeft.X) / 10 * Clock.ElapsedFrameTime));
|
||||
}
|
||||
|
||||
private class SelectableAreaBackground : CompositeDrawable
|
||||
|
@ -6,76 +6,44 @@
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Utils;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
public class TimelineDragBox : DragBox
|
||||
{
|
||||
// the following values hold the start and end X positions of the drag box in the timeline's local space,
|
||||
// but with zoom unapplied in order to be able to compensate for positional changes
|
||||
// while the timeline is being zoomed in/out.
|
||||
private float? selectionStart;
|
||||
private float selectionEnd;
|
||||
public double MinTime { get; private set; }
|
||||
|
||||
public double MaxTime { get; private set; }
|
||||
|
||||
private double? startTime;
|
||||
|
||||
[Resolved]
|
||||
private Timeline timeline { get; set; }
|
||||
|
||||
public TimelineDragBox(Action<RectangleF> performSelect)
|
||||
: base(performSelect)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Drawable CreateBox() => new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Alpha = 0.3f
|
||||
};
|
||||
|
||||
public override bool HandleDrag(MouseButtonEvent e)
|
||||
public override void HandleDrag(MouseButtonEvent e)
|
||||
{
|
||||
// The dragbox should only be active if the mouseDownPosition.Y is within this drawable's bounds.
|
||||
float localY = ToLocalSpace(e.ScreenSpaceMouseDownPosition).Y;
|
||||
if (DrawRectangle.Top > localY || DrawRectangle.Bottom < localY)
|
||||
return false;
|
||||
startTime ??= timeline.TimeAtPosition(e.MouseDownPosition.X);
|
||||
double endTime = timeline.TimeAtPosition(e.MousePosition.X);
|
||||
|
||||
selectionStart ??= e.MouseDownPosition.X / timeline.CurrentZoom;
|
||||
MinTime = Math.Min(startTime.Value, endTime);
|
||||
MaxTime = Math.Max(startTime.Value, endTime);
|
||||
|
||||
// only calculate end when a transition is not in progress to avoid bouncing.
|
||||
if (Precision.AlmostEquals(timeline.CurrentZoom, timeline.Zoom))
|
||||
selectionEnd = e.MousePosition.X / timeline.CurrentZoom;
|
||||
|
||||
updateDragBoxPosition();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateDragBoxPosition()
|
||||
{
|
||||
if (selectionStart == null)
|
||||
return;
|
||||
|
||||
float rescaledStart = selectionStart.Value * timeline.CurrentZoom;
|
||||
float rescaledEnd = selectionEnd * timeline.CurrentZoom;
|
||||
|
||||
Box.X = Math.Min(rescaledStart, rescaledEnd);
|
||||
Box.Width = Math.Abs(rescaledStart - rescaledEnd);
|
||||
|
||||
var boxScreenRect = Box.ScreenSpaceDrawQuad.AABBFloat;
|
||||
|
||||
// we don't care about where the hitobjects are vertically. in cases like stacking display, they may be outside the box without this adjustment.
|
||||
boxScreenRect.Y -= boxScreenRect.Height;
|
||||
boxScreenRect.Height *= 2;
|
||||
|
||||
PerformSelection?.Invoke(boxScreenRect);
|
||||
Box.X = timeline.PositionAtTime(MinTime);
|
||||
Box.Width = timeline.PositionAtTime(MaxTime) - Box.X;
|
||||
}
|
||||
|
||||
public override void Hide()
|
||||
{
|
||||
base.Hide();
|
||||
selectionStart = null;
|
||||
startTime = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -352,6 +352,8 @@ namespace osu.Game.Screens.Edit
|
||||
var updates = batchPendingUpdates.ToArray();
|
||||
batchPendingUpdates.Clear();
|
||||
|
||||
foreach (var h in deletes) SelectedHitObjects.Remove(h);
|
||||
|
||||
foreach (var h in deletes) HitObjectRemoved?.Invoke(h);
|
||||
foreach (var h in inserts) HitObjectAdded?.Invoke(h);
|
||||
foreach (var h in updates) HitObjectUpdated?.Invoke(h);
|
||||
|
@ -39,6 +39,10 @@ namespace osu.Game.Screens.Play
|
||||
/// </summary>
|
||||
public float BottomScoringElementsHeight { get; private set; }
|
||||
|
||||
// HUD uses AlwaysVisible on child components so they can be in an updated state for next display.
|
||||
// Without blocking input, this would also allow them to be interacted with in such a state.
|
||||
public override bool PropagatePositionalInputSubTree => ShowHud.Value;
|
||||
|
||||
public readonly KeyCounterDisplay KeyCounter;
|
||||
public readonly ModDisplay ModDisplay;
|
||||
public readonly HoldForMenuButton HoldToQuit;
|
||||
|
@ -2,12 +2,14 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
@ -43,11 +45,15 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
Origin = Anchor.CentreLeft;
|
||||
}
|
||||
|
||||
private Bindable<bool> preferNoVideo = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
const float icon_size = 14;
|
||||
|
||||
preferNoVideo = config.GetBindable<bool>(OsuSetting.PreferNoVideo);
|
||||
|
||||
Content.Anchor = Anchor.CentreLeft;
|
||||
Content.Origin = Anchor.CentreLeft;
|
||||
|
||||
@ -104,7 +110,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
return;
|
||||
}
|
||||
|
||||
beatmapDownloader.DownloadAsUpdate(beatmapSetInfo);
|
||||
beatmapDownloader.DownloadAsUpdate(beatmapSetInfo, preferNoVideo.Value);
|
||||
attachExistingDownload();
|
||||
};
|
||||
}
|
||||
|
@ -117,6 +117,11 @@ namespace osu.Game.Skinning.Editor
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void SelectAll()
|
||||
{
|
||||
SelectedItems.AddRange(targetComponents.SelectMany(list => list).Except(SelectedItems).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move the current selection spatially by the specified delta, in screen coordinates (ie. the same coordinates as the blueprints).
|
||||
/// </summary>
|
||||
|
@ -27,7 +27,10 @@ namespace osu.Game.Storyboards
|
||||
public void Add(Easing easing, double startTime, double endTime, T startValue, T endValue)
|
||||
{
|
||||
if (endTime < startTime)
|
||||
return;
|
||||
{
|
||||
(startTime, endTime) = (endTime, startTime);
|
||||
(startValue, endValue) = (endValue, startValue);
|
||||
}
|
||||
|
||||
commands.Add(new TypedCommand { Easing = easing, StartTime = startTime, EndTime = endTime, StartValue = startValue, EndValue = endValue });
|
||||
|
||||
|
@ -35,7 +35,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.17.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.1005.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.1011.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1008.0" />
|
||||
<PackageReference Include="Sentry" Version="3.22.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
|
@ -62,7 +62,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1008.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.1005.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.1011.0" />
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
|
||||
<PropertyGroup>
|
||||
@ -82,7 +82,7 @@
|
||||
<PackageReference Include="DiffPlex" Version="1.7.1" />
|
||||
<PackageReference Include="Humanizer" Version="2.14.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.1005.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.1011.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user