mirror of
https://github.com/ppy/osu.git
synced 2024-12-13 03:15:06 +08:00
Merge branch 'master' into slider_fix_lazyjump
This commit is contained in:
commit
60efb5ef92
osu.Desktop
osu.Game.Rulesets.Mania
osu.Game.Rulesets.Osu.Tests/Editor
osu.Game.Rulesets.Osu
osu.Game.Rulesets.Taiko/Difficulty/Evaluators
osu.Game.Tests/Visual
DailyChallenge
Editing
Online
UserInterface
osu.Game
@ -26,7 +26,7 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="System.IO.Packaging" Version="8.0.1" />
|
<PackageReference Include="System.IO.Packaging" Version="8.0.1" />
|
||||||
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||||
<PackageReference Include="Velopack" Version="0.0.630-g9c52e40" />
|
<PackageReference Include="Velopack" Version="0.0.869" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Resources">
|
<ItemGroup Label="Resources">
|
||||||
<EmbeddedResource Include="lazer.ico" />
|
<EmbeddedResource Include="lazer.ico" />
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
|
using osu.Game.Rulesets.Difficulty.Utils;
|
||||||
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
@ -73,7 +74,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
|||||||
// 0.0 +--------+-+---------------> Release Difference / ms
|
// 0.0 +--------+-+---------------> Release Difference / ms
|
||||||
// release_threshold
|
// release_threshold
|
||||||
if (isOverlapping)
|
if (isOverlapping)
|
||||||
holdAddition = 1 / (1 + Math.Exp(0.27 * (release_threshold - closestEndTime)));
|
holdAddition = DifficultyCalculationUtils.Logistic(x: closestEndTime, multiplier: 0.27, midpointOffset: release_threshold);
|
||||||
|
|
||||||
// Decay and increase individualStrains in own column
|
// Decay and increase individualStrains in own column
|
||||||
individualStrains[column] = applyDecay(individualStrains[column], startTime - startTimes[column], individual_decay_base);
|
individualStrains[column] = applyDecay(individualStrains[column], startTime - startTimes[column], individual_decay_base);
|
||||||
|
@ -54,7 +54,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
},
|
},
|
||||||
columnBackgrounds = new ColumnFlow<Drawable>(stageDefinition)
|
columnBackgrounds = new ColumnFlow<Drawable>(stageDefinition)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Masking = false,
|
||||||
},
|
},
|
||||||
new HitTargetInsetContainer
|
new HitTargetInsetContainer
|
||||||
{
|
{
|
||||||
@ -126,8 +127,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
|
X = isLastColumn ? -0.16f : 0,
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Width = rightLineWidth,
|
Width = rightLineWidth,
|
||||||
Scale = new Vector2(0.740f, 1),
|
Scale = new Vector2(0.740f, 1),
|
||||||
|
@ -28,6 +28,12 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
private readonly FillFlowContainer<Container<TContent>> columns;
|
private readonly FillFlowContainer<Container<TContent>> columns;
|
||||||
private readonly StageDefinition stageDefinition;
|
private readonly StageDefinition stageDefinition;
|
||||||
|
|
||||||
|
public new bool Masking
|
||||||
|
{
|
||||||
|
get => base.Masking;
|
||||||
|
set => base.Masking = value;
|
||||||
|
}
|
||||||
|
|
||||||
public ColumnFlow(StageDefinition stageDefinition)
|
public ColumnFlow(StageDefinition stageDefinition)
|
||||||
{
|
{
|
||||||
this.stageDefinition = stageDefinition;
|
this.stageDefinition = stageDefinition;
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Screens.Edit.Components.RadioButtons;
|
using osu.Game.Screens.Edit.Components.RadioButtons;
|
||||||
@ -24,38 +25,57 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestTouchInputAfterTouchingComposeArea()
|
public void TestTouchInputPlaceHitCircleDirectly()
|
||||||
{
|
{
|
||||||
AddStep("tap circle", () => tap(this.ChildrenOfType<EditorRadioButton>().Single(b => b.Button.Label == "HitCircle")));
|
AddStep("tap circle", () => tap(this.ChildrenOfType<EditorRadioButton>().Single(b => b.Button.Label == "HitCircle")));
|
||||||
|
|
||||||
// this input is just for interacting with compose area
|
AddStep("tap to place circle", () => tap(this.ChildrenOfType<Playfield>().Single()));
|
||||||
AddStep("tap playfield", () => tap(this.ChildrenOfType<Playfield>().Single()));
|
|
||||||
|
|
||||||
AddStep("move current time", () => InputManager.Key(Key.Right));
|
|
||||||
|
|
||||||
AddStep("tap to place circle", () => tap(this.ChildrenOfType<Playfield>().Single().ToScreenSpace(new Vector2(10, 10))));
|
|
||||||
AddAssert("circle placed correctly", () =>
|
AddAssert("circle placed correctly", () =>
|
||||||
{
|
{
|
||||||
var circle = (HitCircle)EditorBeatmap.HitObjects.Single(h => h.StartTime == EditorClock.CurrentTimeAccurate);
|
var circle = (HitCircle)EditorBeatmap.HitObjects.Single(h => h.StartTime == EditorClock.CurrentTimeAccurate);
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
Assert.That(circle.Position.X, Is.EqualTo(10f).Within(0.01f));
|
Assert.That(circle.Position.X, Is.EqualTo(256f).Within(0.01f));
|
||||||
Assert.That(circle.Position.Y, Is.EqualTo(10f).Within(0.01f));
|
Assert.That(circle.Position.Y, Is.EqualTo(192f).Within(0.01f));
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
AddStep("tap slider", () => tap(this.ChildrenOfType<EditorRadioButton>().Single(b => b.Button.Label == "Slider")));
|
[Test]
|
||||||
|
public void TestTouchInputPlaceCircleAfterTouchingComposeArea()
|
||||||
|
{
|
||||||
|
AddStep("tap circle", () => tap(this.ChildrenOfType<EditorRadioButton>().Single(b => b.Button.Label == "HitCircle")));
|
||||||
|
|
||||||
// this input is just for interacting with compose area
|
|
||||||
AddStep("tap playfield", () => tap(this.ChildrenOfType<Playfield>().Single()));
|
AddStep("tap playfield", () => tap(this.ChildrenOfType<Playfield>().Single()));
|
||||||
|
AddAssert("circle placed", () => EditorBeatmap.HitObjects.Single(h => h.StartTime == EditorClock.CurrentTimeAccurate) is HitCircle);
|
||||||
|
|
||||||
AddStep("move current time", () => InputManager.Key(Key.Right));
|
AddStep("move forward", () => InputManager.Key(Key.Right));
|
||||||
|
|
||||||
|
AddStep("tap to place circle", () => tap(this.ChildrenOfType<Playfield>().Single()));
|
||||||
|
AddAssert("circle placed correctly", () =>
|
||||||
|
{
|
||||||
|
var circle = (HitCircle)EditorBeatmap.HitObjects.Single(h => h.StartTime == EditorClock.CurrentTimeAccurate);
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(circle.Position.X, Is.EqualTo(256f).Within(0.01f));
|
||||||
|
Assert.That(circle.Position.Y, Is.EqualTo(192f).Within(0.01f));
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTouchInputPlaceSliderDirectly()
|
||||||
|
{
|
||||||
|
AddStep("tap slider", () => tap(this.ChildrenOfType<EditorRadioButton>().Single(b => b.Button.Label == "Slider")));
|
||||||
|
|
||||||
AddStep("hold to draw slider", () => InputManager.BeginTouch(new Touch(TouchSource.Touch1, this.ChildrenOfType<Playfield>().Single().ToScreenSpace(new Vector2(50, 20)))));
|
AddStep("hold to draw slider", () => InputManager.BeginTouch(new Touch(TouchSource.Touch1, this.ChildrenOfType<Playfield>().Single().ToScreenSpace(new Vector2(50, 20)))));
|
||||||
AddStep("drag to draw", () => InputManager.MoveTouchTo(new Touch(TouchSource.Touch1, this.ChildrenOfType<Playfield>().Single().ToScreenSpace(new Vector2(200, 50)))));
|
AddStep("drag to draw", () => InputManager.MoveTouchTo(new Touch(TouchSource.Touch1, this.ChildrenOfType<Playfield>().Single().ToScreenSpace(new Vector2(200, 50)))));
|
||||||
AddAssert("selection not initiated", () => this.ChildrenOfType<DragBox>().All(d => d.State == Visibility.Hidden));
|
AddAssert("selection not initiated", () => this.ChildrenOfType<DragBox>().All(d => d.State == Visibility.Hidden));
|
||||||
|
AddAssert("blueprint visible", () => this.ChildrenOfType<SliderPlacementBlueprint>().Single().Alpha > 0);
|
||||||
AddStep("end", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, InputManager.CurrentState.Touch.GetTouchPosition(TouchSource.Touch1)!.Value)));
|
AddStep("end", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, InputManager.CurrentState.Touch.GetTouchPosition(TouchSource.Touch1)!.Value)));
|
||||||
AddAssert("slider placed correctly", () =>
|
AddAssert("slider placed correctly", () =>
|
||||||
{
|
{
|
||||||
@ -76,12 +96,55 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tap(Drawable drawable) => tap(drawable.ScreenSpaceDrawQuad.Centre);
|
[Test]
|
||||||
|
public void TestTouchInputPlaceSliderAfterTouchingComposeArea()
|
||||||
|
{
|
||||||
|
AddStep("tap slider", () => tap(this.ChildrenOfType<EditorRadioButton>().Single(b => b.Button.Label == "Slider")));
|
||||||
|
|
||||||
|
AddStep("tap playfield", () => tap(this.ChildrenOfType<Playfield>().Single()));
|
||||||
|
AddStep("tap and hold another spot", () => hold(this.ChildrenOfType<Playfield>().Single(), new Vector2(50, 0)));
|
||||||
|
AddUntilStep("wait for slider placement", () => EditorBeatmap.HitObjects.SingleOrDefault(h => h.StartTime == EditorClock.CurrentTimeAccurate) is Slider);
|
||||||
|
AddStep("end", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, InputManager.CurrentState.Touch.GetTouchPosition(TouchSource.Touch1)!.Value)));
|
||||||
|
|
||||||
|
AddStep("move forward", () => InputManager.Key(Key.Right));
|
||||||
|
|
||||||
|
AddStep("hold to draw slider", () => InputManager.BeginTouch(new Touch(TouchSource.Touch1, this.ChildrenOfType<Playfield>().Single().ToScreenSpace(new Vector2(50, 20)))));
|
||||||
|
AddStep("drag to draw", () => InputManager.MoveTouchTo(new Touch(TouchSource.Touch1, this.ChildrenOfType<Playfield>().Single().ToScreenSpace(new Vector2(200, 50)))));
|
||||||
|
AddAssert("selection not initiated", () => this.ChildrenOfType<DragBox>().All(d => d.State == Visibility.Hidden));
|
||||||
|
AddAssert("blueprint visible", () => this.ChildrenOfType<SliderPlacementBlueprint>().Single().IsPresent);
|
||||||
|
AddStep("end", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, InputManager.CurrentState.Touch.GetTouchPosition(TouchSource.Touch1)!.Value)));
|
||||||
|
AddAssert("slider placed correctly", () =>
|
||||||
|
{
|
||||||
|
var slider = (Slider)EditorBeatmap.HitObjects.Single(h => h.StartTime == EditorClock.CurrentTimeAccurate);
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(slider.Position.X, Is.EqualTo(50f).Within(0.01f));
|
||||||
|
Assert.That(slider.Position.Y, Is.EqualTo(20f).Within(0.01f));
|
||||||
|
Assert.That(slider.Path.ControlPoints.Count, Is.EqualTo(2));
|
||||||
|
Assert.That(slider.Path.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
|
||||||
|
|
||||||
|
// the final position may be slightly off from the mouse position when drawing, account for that.
|
||||||
|
Assert.That(slider.Path.ControlPoints[1].Position.X, Is.EqualTo(150).Within(5));
|
||||||
|
Assert.That(slider.Path.ControlPoints[1].Position.Y, Is.EqualTo(30).Within(5));
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tap(Drawable drawable, Vector2 offset = default) => tap(drawable.ToScreenSpace(drawable.LayoutRectangle.Centre + offset));
|
||||||
|
|
||||||
private void tap(Vector2 position)
|
private void tap(Vector2 position)
|
||||||
{
|
{
|
||||||
InputManager.BeginTouch(new Touch(TouchSource.Touch1, position));
|
hold(position);
|
||||||
InputManager.EndTouch(new Touch(TouchSource.Touch1, position));
|
InputManager.EndTouch(new Touch(TouchSource.Touch1, position));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void hold(Drawable drawable, Vector2 offset = default) => hold(drawable.ToScreenSpace(drawable.LayoutRectangle.Centre + offset));
|
||||||
|
|
||||||
|
private void hold(Vector2 position)
|
||||||
|
{
|
||||||
|
InputManager.BeginTouch(new Touch(TouchSource.Touch1, position));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Difficulty.Utils;
|
||||||
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
|
||||||
@ -33,6 +34,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
|||||||
var osuLastObj = (OsuDifficultyHitObject)current.Previous(0);
|
var osuLastObj = (OsuDifficultyHitObject)current.Previous(0);
|
||||||
var osuLastLastObj = (OsuDifficultyHitObject)current.Previous(1);
|
var osuLastLastObj = (OsuDifficultyHitObject)current.Previous(1);
|
||||||
|
|
||||||
|
const int radius = OsuDifficultyHitObject.NORMALISED_RADIUS;
|
||||||
|
const int diameter = OsuDifficultyHitObject.NORMALISED_DIAMETER;
|
||||||
|
|
||||||
// Calculate the velocity to the current hitobject, which starts with a base distance / time assuming the last object is a hitcircle.
|
// Calculate the velocity to the current hitobject, which starts with a base distance / time assuming the last object is a hitcircle.
|
||||||
double currDistance = withSliderTravelDistance ? osuCurrObj.LazyJumpFromEndDistance : osuCurrObj.JumpDistance;
|
double currDistance = withSliderTravelDistance ? osuCurrObj.LazyJumpFromEndDistance : osuCurrObj.JumpDistance;
|
||||||
double currVelocity = currDistance / osuCurrObj.StrainTime;
|
double currVelocity = currDistance / osuCurrObj.StrainTime;
|
||||||
@ -79,14 +83,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
|||||||
wideAngleBonus = calcWideAngleBonus(currAngle);
|
wideAngleBonus = calcWideAngleBonus(currAngle);
|
||||||
acuteAngleBonus = calcAcuteAngleBonus(currAngle);
|
acuteAngleBonus = calcAcuteAngleBonus(currAngle);
|
||||||
|
|
||||||
if (osuCurrObj.StrainTime > 100) // Only buff deltaTime exceeding 300 bpm 1/2.
|
if (DifficultyCalculationUtils.MillisecondsToBPM(osuCurrObj.StrainTime, 2) < 300) // Only buff deltaTime exceeding 300 bpm 1/2.
|
||||||
acuteAngleBonus = 0;
|
acuteAngleBonus = 0;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
acuteAngleBonus *= calcAcuteAngleBonus(lastAngle) // Multiply by previous angle, we don't want to buff unless this is a wiggle type pattern.
|
acuteAngleBonus *= calcAcuteAngleBonus(lastAngle) // Multiply by previous angle, we don't want to buff unless this is a wiggle type pattern.
|
||||||
* Math.Min(angleBonus, 125 / osuCurrObj.StrainTime) // The maximum velocity we buff is equal to 125 / strainTime
|
* Math.Min(angleBonus, diameter * 1.25 / osuCurrObj.StrainTime) // The maximum velocity we buff is equal to 125 / strainTime
|
||||||
* Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, (100 - osuCurrObj.StrainTime) / 25)), 2) // scale buff from 150 bpm 1/4 to 200 bpm 1/4
|
* Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, (100 - osuCurrObj.StrainTime) / 25)), 2) // scale buff from 150 bpm 1/4 to 200 bpm 1/4
|
||||||
* Math.Pow(Math.Sin(Math.PI / 2 * (Math.Clamp(currDistance, 50, 100) - 50) / 50), 2); // Buff distance exceeding 50 (radius) up to 100 (diameter).
|
* Math.Pow(Math.Sin(Math.PI / 2 * (Math.Clamp(currDistance, radius, diameter) - radius) / radius), 2); // Buff distance exceeding radius up to diameter.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Penalize wide angles if they're repeated, reducing the penalty as the lastAngle gets more acute.
|
// Penalize wide angles if they're repeated, reducing the penalty as the lastAngle gets more acute.
|
||||||
@ -109,7 +113,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
|||||||
double distRatio = Math.Pow(Math.Sin(Math.PI / 2 * Math.Abs(prevVelocity - currVelocity) / Math.Max(prevVelocity, currVelocity)), 2);
|
double distRatio = Math.Pow(Math.Sin(Math.PI / 2 * Math.Abs(prevVelocity - currVelocity) / Math.Max(prevVelocity, currVelocity)), 2);
|
||||||
|
|
||||||
// Reward for % distance up to 125 / strainTime for overlaps where velocity is still changing.
|
// Reward for % distance up to 125 / strainTime for overlaps where velocity is still changing.
|
||||||
double overlapVelocityBuff = Math.Min(125 / Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime), Math.Abs(prevVelocity - currVelocity));
|
double overlapVelocityBuff = Math.Min(diameter * 1.25 / Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime), Math.Abs(prevVelocity - currVelocity));
|
||||||
|
|
||||||
velocityChangeBonus = overlapVelocityBuff * distRatio;
|
velocityChangeBonus = overlapVelocityBuff * distRatio;
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Difficulty.Utils;
|
||||||
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
|
||||||
@ -120,7 +121,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
|||||||
islandCount.Count++;
|
islandCount.Count++;
|
||||||
|
|
||||||
// repeated island (ex: triplet -> triplet)
|
// repeated island (ex: triplet -> triplet)
|
||||||
double power = logistic(island.Delta, 2.75, 0.24, 14);
|
double power = DifficultyCalculationUtils.Logistic(island.Delta, maxValue: 2.75, multiplier: 0.24, midpointOffset: 58.33);
|
||||||
effectiveRatio *= Math.Min(3.0 / islandCount.Count, Math.Pow(1.0 / islandCount.Count, power));
|
effectiveRatio *= Math.Min(3.0 / islandCount.Count, Math.Pow(1.0 / islandCount.Count, power));
|
||||||
|
|
||||||
islandCounts[countIndex] = (islandCount.Island, islandCount.Count);
|
islandCounts[countIndex] = (islandCount.Island, islandCount.Count);
|
||||||
@ -172,8 +173,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
|||||||
return Math.Sqrt(4 + rhythmComplexitySum * rhythm_overall_multiplier) / 2.0; // produces multiplier that can be applied to strain. range [1, infinity) (not really though)
|
return Math.Sqrt(4 + rhythmComplexitySum * rhythm_overall_multiplier) / 2.0; // produces multiplier that can be applied to strain. range [1, infinity) (not really though)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double logistic(double x, double maxValue, double multiplier, double offset) => (maxValue / (1 + Math.Pow(Math.E, offset - (multiplier * x))));
|
|
||||||
|
|
||||||
private class Island : IEquatable<Island>
|
private class Island : IEquatable<Island>
|
||||||
{
|
{
|
||||||
private readonly double deltaDifferenceEpsilon;
|
private readonly double deltaDifferenceEpsilon;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Difficulty.Utils;
|
||||||
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
|
||||||
@ -10,8 +11,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
|||||||
{
|
{
|
||||||
public static class SpeedEvaluator
|
public static class SpeedEvaluator
|
||||||
{
|
{
|
||||||
private const double single_spacing_threshold = 125; // 1.25 circles distance between centers
|
private const double single_spacing_threshold = OsuDifficultyHitObject.NORMALISED_DIAMETER * 1.25; // 1.25 circles distance between centers
|
||||||
private const double min_speed_bonus = 75; // ~200BPM
|
private const double min_speed_bonus = 200; // 200 BPM 1/4th
|
||||||
private const double speed_balancing_factor = 40;
|
private const double speed_balancing_factor = 40;
|
||||||
private const double distance_multiplier = 0.94;
|
private const double distance_multiplier = 0.94;
|
||||||
|
|
||||||
@ -43,8 +44,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
|||||||
double speedBonus = 0.0;
|
double speedBonus = 0.0;
|
||||||
|
|
||||||
// Add additional scaling bonus for streams/bursts higher than 200bpm
|
// Add additional scaling bonus for streams/bursts higher than 200bpm
|
||||||
if (strainTime < min_speed_bonus)
|
if (DifficultyCalculationUtils.MillisecondsToBPM(strainTime) > min_speed_bonus)
|
||||||
speedBonus = 0.75 * Math.Pow((min_speed_bonus - strainTime) / speed_balancing_factor, 2);
|
speedBonus = 0.75 * Math.Pow((DifficultyCalculationUtils.BPMToMilliseconds(min_speed_bonus) - strainTime) / speed_balancing_factor, 2);
|
||||||
|
|
||||||
double travelDistance = osuPrevObj?.TravelDistance ?? 0;
|
double travelDistance = osuPrevObj?.TravelDistance ?? 0;
|
||||||
double distance = travelDistance + osuCurrObj.MinimumJumpDistance;
|
double distance = travelDistance + osuCurrObj.MinimumJumpDistance;
|
||||||
|
@ -48,8 +48,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
double sliderFactor = aimRating > 0 ? Math.Min(aimRating, aimRatingNoSliders) / aimRating : 1;
|
double sliderFactor = aimRating > 0 ? Math.Min(aimRating, aimRatingNoSliders) / aimRating : 1;
|
||||||
|
|
||||||
double aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountDifficultStrains();
|
double aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountTopWeightedStrains();
|
||||||
double speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountDifficultStrains();
|
double speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountTopWeightedStrains();
|
||||||
|
|
||||||
if (mods.Any(m => m is OsuModTouchDevice))
|
if (mods.Any(m => m is OsuModTouchDevice))
|
||||||
{
|
{
|
||||||
|
@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const int NORMALISED_RADIUS = 50; // Change radius to 50 to make 100 the diameter. Easier for mental maths.
|
public const int NORMALISED_RADIUS = 50; // Change radius to 50 to make 100 the diameter. Easier for mental maths.
|
||||||
|
|
||||||
|
public const int NORMALISED_DIAMETER = NORMALISED_RADIUS * 2;
|
||||||
|
|
||||||
public const int MIN_DELTA_TIME = 25;
|
public const int MIN_DELTA_TIME = 25;
|
||||||
|
|
||||||
private const float maximum_slider_radius = NORMALISED_RADIUS * 2.4f;
|
private const float maximum_slider_radius = NORMALISED_RADIUS * 2.4f;
|
||||||
|
@ -34,7 +34,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
{
|
{
|
||||||
currentStrain *= strainDecay(current.DeltaTime);
|
currentStrain *= strainDecay(current.DeltaTime);
|
||||||
currentStrain += AimEvaluator.EvaluateDifficultyOf(current, withSliders) * skillMultiplier;
|
currentStrain += AimEvaluator.EvaluateDifficultyOf(current, withSliders) * skillMultiplier;
|
||||||
ObjectStrains.Add(currentStrain);
|
|
||||||
|
|
||||||
return currentStrain;
|
return currentStrain;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual double ReducedStrainBaseline => 0.75;
|
protected virtual double ReducedStrainBaseline => 0.75;
|
||||||
|
|
||||||
protected List<double> ObjectStrains = new List<double>();
|
|
||||||
protected double Difficulty;
|
protected double Difficulty;
|
||||||
|
|
||||||
protected OsuStrainSkill(Mod[] mods)
|
protected OsuStrainSkill(Mod[] mods)
|
||||||
@ -60,20 +59,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
return Difficulty;
|
return Difficulty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the number of strains weighted against the top strain.
|
|
||||||
/// The result is scaled by clock rate as it affects the total number of strains.
|
|
||||||
/// </summary>
|
|
||||||
public double CountDifficultStrains()
|
|
||||||
{
|
|
||||||
if (Difficulty == 0)
|
|
||||||
return 0.0;
|
|
||||||
|
|
||||||
double consistentTopStrain = Difficulty / 10; // What would the top strain be if all strain values were identical
|
|
||||||
// Use a weighted sum of all strains. Constants are arbitrary and give nice values
|
|
||||||
return ObjectStrains.Sum(s => 1.1 / (1 + Math.Exp(-10 * (s / consistentTopStrain - 0.88))));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double DifficultyToPerformance(double difficulty) => Math.Pow(5.0 * Math.Max(1.0, difficulty / 0.0675) - 4.0, 3.0) / 100000.0;
|
public static double DifficultyToPerformance(double difficulty) => Math.Pow(5.0 * Math.Max(1.0, difficulty / 0.0675) - 4.0, 3.0) / 100000.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current);
|
currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current);
|
||||||
|
|
||||||
double totalStrain = currentStrain * currentRhythm;
|
double totalStrain = currentStrain * currentRhythm;
|
||||||
ObjectStrains.Add(totalStrain);
|
|
||||||
|
|
||||||
return totalStrain;
|
return totalStrain;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// 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.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -31,12 +32,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints
|
|||||||
public override void EndPlacement(bool commit)
|
public override void EndPlacement(bool commit)
|
||||||
{
|
{
|
||||||
if (!commit && PlacementActive != PlacementState.Finished)
|
if (!commit && PlacementActive != PlacementState.Finished)
|
||||||
{
|
resetGridState();
|
||||||
gridToolboxGroup.StartPosition.Value = originalOrigin;
|
|
||||||
gridToolboxGroup.Spacing.Value = originalSpacing;
|
|
||||||
if (!gridToolboxGroup.GridLinesRotation.Disabled)
|
|
||||||
gridToolboxGroup.GridLinesRotation.Value = originalRotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
base.EndPlacement(commit);
|
base.EndPlacement(commit);
|
||||||
|
|
||||||
@ -103,6 +99,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints
|
|||||||
|
|
||||||
public override void UpdateTimeAndPosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
|
if (State.Value == Visibility.Hidden)
|
||||||
|
return;
|
||||||
|
|
||||||
var pos = ToLocalSpace(result.ScreenSpacePosition);
|
var pos = ToLocalSpace(result.ScreenSpacePosition);
|
||||||
|
|
||||||
if (PlacementActive != PlacementState.Active)
|
if (PlacementActive != PlacementState.Active)
|
||||||
@ -122,5 +121,19 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void PopOut()
|
||||||
|
{
|
||||||
|
base.PopOut();
|
||||||
|
resetGridState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetGridState()
|
||||||
|
{
|
||||||
|
gridToolboxGroup.StartPosition.Value = originalOrigin;
|
||||||
|
gridToolboxGroup.Spacing.Value = originalSpacing;
|
||||||
|
if (!gridToolboxGroup.GridLinesRotation.Disabled)
|
||||||
|
gridToolboxGroup.GridLinesRotation.Value = originalRotation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,6 +156,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this allows sliders to be drawn outside compose area (after starting from a point within the compose area).
|
||||||
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || PlacementActive == PlacementState.Active;
|
||||||
|
|
||||||
|
// ReceivePositionalInputAtSubTree generally always returns true when masking is disabled, but we don't want that,
|
||||||
|
// otherwise a slider path tooltip will be displayed anywhere in the editor (outside compose area).
|
||||||
|
protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
private void beginNewSegment(PathControlPoint lastPoint)
|
private void beginNewSegment(PathControlPoint lastPoint)
|
||||||
{
|
{
|
||||||
segmentStart = lastPoint;
|
segmentStart = lastPoint;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Difficulty.Utils;
|
||||||
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour;
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour;
|
||||||
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data;
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data;
|
||||||
@ -11,26 +12,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
|
|||||||
{
|
{
|
||||||
public class ColourEvaluator
|
public class ColourEvaluator
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// A sigmoid function. It gives a value between (middle - height/2) and (middle + height/2).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="val">The input value.</param>
|
|
||||||
/// <param name="center">The center of the sigmoid, where the largest gradient occurs and value is equal to middle.</param>
|
|
||||||
/// <param name="width">The radius of the sigmoid, outside of which values are near the minimum/maximum.</param>
|
|
||||||
/// <param name="middle">The middle of the sigmoid output.</param>
|
|
||||||
/// <param name="height">The height of the sigmoid output. This will be equal to max value - min value.</param>
|
|
||||||
private static double sigmoid(double val, double center, double width, double middle, double height)
|
|
||||||
{
|
|
||||||
double sigmoid = Math.Tanh(Math.E * -(val - center) / width);
|
|
||||||
return sigmoid * (height / 2) + middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Evaluate the difficulty of the first note of a <see cref="MonoStreak"/>.
|
/// Evaluate the difficulty of the first note of a <see cref="MonoStreak"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static double EvaluateDifficultyOf(MonoStreak monoStreak)
|
public static double EvaluateDifficultyOf(MonoStreak monoStreak)
|
||||||
{
|
{
|
||||||
return sigmoid(monoStreak.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(monoStreak.Parent) * 0.5;
|
return DifficultyCalculationUtils.Logistic(exponent: Math.E * monoStreak.Index - 2 * Math.E) * EvaluateDifficultyOf(monoStreak.Parent) * 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -38,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static double EvaluateDifficultyOf(AlternatingMonoPattern alternatingMonoPattern)
|
public static double EvaluateDifficultyOf(AlternatingMonoPattern alternatingMonoPattern)
|
||||||
{
|
{
|
||||||
return sigmoid(alternatingMonoPattern.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(alternatingMonoPattern.Parent);
|
return DifficultyCalculationUtils.Logistic(exponent: Math.E * alternatingMonoPattern.Index - 2 * Math.E) * EvaluateDifficultyOf(alternatingMonoPattern.Parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -46,7 +33,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static double EvaluateDifficultyOf(RepeatingHitPatterns repeatingHitPattern)
|
public static double EvaluateDifficultyOf(RepeatingHitPatterns repeatingHitPattern)
|
||||||
{
|
{
|
||||||
return 2 * (1 - sigmoid(repeatingHitPattern.RepetitionInterval, 2, 2, 0.5, 1));
|
return 2 * (1 - DifficultyCalculationUtils.Logistic(exponent: Math.E * repeatingHitPattern.RepetitionInterval - 2 * Math.E));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double EvaluateDifficultyOf(DifficultyHitObject hitObject)
|
public static double EvaluateDifficultyOf(DifficultyHitObject hitObject)
|
||||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
|||||||
Add(metadataClient);
|
Add(metadataClient);
|
||||||
|
|
||||||
// add button to observe for daily challenge changes and perform its logic.
|
// add button to observe for daily challenge changes and perform its logic.
|
||||||
Add(new DailyChallengeButton(@"button-default-select", new Color4(102, 68, 204, 255), _ => { }, 0, Key.D));
|
Add(new DailyChallengeButton(@"button-default-select", new Color4(102, 68, 204, 255), (_, _) => { }, 0, Key.D));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -19,6 +19,7 @@ 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.Rulesets.UI;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Components.RadioButtons;
|
using osu.Game.Screens.Edit.Components.RadioButtons;
|
||||||
using osu.Game.Screens.Edit.Components.TernaryButtons;
|
using osu.Game.Screens.Edit.Components.TernaryButtons;
|
||||||
@ -82,6 +83,45 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPlacementOutsideComposeScreen()
|
||||||
|
{
|
||||||
|
AddStep("clear all control points and hitobjects", () =>
|
||||||
|
{
|
||||||
|
editorBeatmap.ControlPointInfo.Clear();
|
||||||
|
editorBeatmap.Clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint()));
|
||||||
|
AddStep("select circle", () => hitObjectComposer.ChildrenOfType<EditorRadioButton>().First(d => d.Button.Label == "HitCircle").TriggerClick());
|
||||||
|
AddStep("move mouse to compose", () => InputManager.MoveMouseTo(hitObjectComposer.ChildrenOfType<HitObjectContainer>().Single()));
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("circle placed", () => editorBeatmap.HitObjects.Count == 1);
|
||||||
|
|
||||||
|
AddStep("move mouse outside compose", () => InputManager.MoveMouseTo(hitObjectComposer.ChildrenOfType<HitObjectContainer>().Single().ScreenSpaceDrawQuad.TopLeft - new Vector2(0f, 20f)));
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("no circle placed", () => editorBeatmap.HitObjects.Count == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDragSliderOutsideComposeScreen()
|
||||||
|
{
|
||||||
|
AddStep("clear all control points and hitobjects", () =>
|
||||||
|
{
|
||||||
|
editorBeatmap.ControlPointInfo.Clear();
|
||||||
|
editorBeatmap.Clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint()));
|
||||||
|
AddStep("select slider", () => hitObjectComposer.ChildrenOfType<EditorRadioButton>().First(d => d.Button.Label == "Slider").TriggerClick());
|
||||||
|
|
||||||
|
AddStep("move mouse to compose", () => InputManager.MoveMouseTo(hitObjectComposer.ChildrenOfType<HitObjectContainer>().Single()));
|
||||||
|
AddStep("hold", () => InputManager.PressButton(MouseButton.Left));
|
||||||
|
AddStep("move mouse outside compose", () => InputManager.MoveMouseTo(hitObjectComposer.ChildrenOfType<HitObjectContainer>().Single().ScreenSpaceDrawQuad.TopLeft - new Vector2(0f, 80f)));
|
||||||
|
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
|
AddAssert("slider placed", () => editorBeatmap.HitObjects.Count == 1);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestPlacementOnlyWorksWithTiming()
|
public void TestPlacementOnlyWorksWithTiming()
|
||||||
{
|
{
|
||||||
|
@ -55,21 +55,21 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Username = @"flyte",
|
Username = @"flyte",
|
||||||
Id = 3103765,
|
Id = 3103765,
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
|
CoverUrl = @"https://assets.ppy.sh/user-cover-presets/1/df28696b58541a9e67f6755918951d542d93bdf1da41720fcca2fd2c1ea8cf51.jpeg",
|
||||||
}),
|
}),
|
||||||
new UserBrickPanel(new APIUser
|
new UserBrickPanel(new APIUser
|
||||||
{
|
{
|
||||||
Username = @"peppy",
|
Username = @"peppy",
|
||||||
Id = 2,
|
Id = 2,
|
||||||
Colour = "99EB47",
|
Colour = "99EB47",
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg",
|
||||||
}),
|
}),
|
||||||
new UserGridPanel(new APIUser
|
new UserGridPanel(new APIUser
|
||||||
{
|
{
|
||||||
Username = @"flyte",
|
Username = @"flyte",
|
||||||
Id = 3103765,
|
Id = 3103765,
|
||||||
CountryCode = CountryCode.JP,
|
CountryCode = CountryCode.JP,
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg",
|
CoverUrl = @"https://assets.ppy.sh/user-cover-presets/1/df28696b58541a9e67f6755918951d542d93bdf1da41720fcca2fd2c1ea8cf51.jpeg",
|
||||||
Status = { Value = UserStatus.Online }
|
Status = { Value = UserStatus.Online }
|
||||||
}) { Width = 300 },
|
}) { Width = 300 },
|
||||||
boundPanel1 = new UserGridPanel(new APIUser
|
boundPanel1 = new UserGridPanel(new APIUser
|
||||||
@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Username = @"peppy",
|
Username = @"peppy",
|
||||||
Id = 2,
|
Id = 2,
|
||||||
CountryCode = CountryCode.AU,
|
CountryCode = CountryCode.AU,
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg",
|
||||||
IsSupporter = true,
|
IsSupporter = true,
|
||||||
SupportLevel = 3,
|
SupportLevel = 3,
|
||||||
}) { Width = 300 },
|
}) { Width = 300 },
|
||||||
@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Username = @"flyte",
|
Username = @"flyte",
|
||||||
Id = 3103765,
|
Id = 3103765,
|
||||||
CountryCode = CountryCode.JP,
|
CountryCode = CountryCode.JP,
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg",
|
CoverUrl = @"https://assets.ppy.sh/user-cover-presets/1/df28696b58541a9e67f6755918951d542d93bdf1da41720fcca2fd2c1ea8cf51.jpeg",
|
||||||
Statistics = new UserStatistics { GlobalRank = 12345, CountryRank = 1234 }
|
Statistics = new UserStatistics { GlobalRank = 12345, CountryRank = 1234 }
|
||||||
}) { Width = 300 },
|
}) { Width = 300 },
|
||||||
new UserRankPanel(new APIUser
|
new UserRankPanel(new APIUser
|
||||||
@ -104,7 +104,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Id = 2,
|
Id = 2,
|
||||||
Colour = "99EB47",
|
Colour = "99EB47",
|
||||||
CountryCode = CountryCode.AU,
|
CountryCode = CountryCode.AU,
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg",
|
||||||
Statistics = new UserStatistics { GlobalRank = null, CountryRank = null }
|
Statistics = new UserStatistics { GlobalRank = null, CountryRank = null }
|
||||||
}) { Width = 300 }
|
}) { Width = 300 }
|
||||||
}
|
}
|
||||||
@ -168,6 +168,14 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
CountryRank = RNG.Next(100000)
|
CountryRank = RNG.Next(100000)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
AddStep("set statistics to something big", () =>
|
||||||
|
{
|
||||||
|
API.UpdateStatistics(new UserStatistics
|
||||||
|
{
|
||||||
|
GlobalRank = RNG.Next(1_000_000, 100_000_000),
|
||||||
|
CountryRank = RNG.Next(1_000_000, 100_000_000)
|
||||||
|
});
|
||||||
|
});
|
||||||
AddStep("set statistics to empty", () =>
|
AddStep("set statistics to empty", () =>
|
||||||
{
|
{
|
||||||
API.UpdateStatistics(new UserStatistics());
|
API.UpdateStatistics(new UserStatistics());
|
||||||
|
@ -110,7 +110,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.Q:
|
case Key.Q:
|
||||||
buttons.OnExit = action;
|
buttons.OnExit = _ => action();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.O:
|
case Key.O:
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
public void TestStandardButton()
|
public void TestStandardButton()
|
||||||
{
|
{
|
||||||
AddStep("add button", () => Child = new MainMenuButton(
|
AddStep("add button", () => Child = new MainMenuButton(
|
||||||
ButtonSystemStrings.Solo, @"button-default-select", OsuIcon.Player, new Color4(102, 68, 204, 255), _ => { }, 0, Key.P)
|
ButtonSystemStrings.Solo, @"button-default-select", OsuIcon.Player, new Color4(102, 68, 204, 255), (_, _) => { }, 0, Key.P)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
CachedDependencies = [(typeof(INotificationOverlay), notificationOverlay)],
|
CachedDependencies = [(typeof(INotificationOverlay), notificationOverlay)],
|
||||||
Child = new DailyChallengeButton(@"button-default-select", new Color4(102, 68, 204, 255), _ => { }, 0, Key.D)
|
Child = new DailyChallengeButton(@"button-default-select", new Color4(102, 68, 204, 255), (_, _) => { }, 0, Key.D)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -161,7 +161,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
CachedDependencies = [(typeof(INotificationOverlay), notificationOverlay)],
|
CachedDependencies = [(typeof(INotificationOverlay), notificationOverlay)],
|
||||||
Child = new DailyChallengeButton(@"button-default-select", new Color4(102, 68, 204, 255), _ => { }, 0, Key.D)
|
Child = new DailyChallengeButton(@"button-default-select", new Color4(102, 68, 204, 255), (_, _) => { }, 0, Key.D)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -26,10 +26,10 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
|||||||
protected virtual int SectionLength => 400;
|
protected virtual int SectionLength => 400;
|
||||||
|
|
||||||
private double currentSectionPeak; // We also keep track of the peak strain level in the current section.
|
private double currentSectionPeak; // We also keep track of the peak strain level in the current section.
|
||||||
|
|
||||||
private double currentSectionEnd;
|
private double currentSectionEnd;
|
||||||
|
|
||||||
private readonly List<double> strainPeaks = new List<double>();
|
private readonly List<double> strainPeaks = new List<double>();
|
||||||
|
protected readonly List<double> ObjectStrains = new List<double>(); // Store individual strains
|
||||||
|
|
||||||
protected StrainSkill(Mod[] mods)
|
protected StrainSkill(Mod[] mods)
|
||||||
: base(mods)
|
: base(mods)
|
||||||
@ -57,7 +57,25 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
|||||||
currentSectionEnd += SectionLength;
|
currentSectionEnd += SectionLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSectionPeak = Math.Max(StrainValueAt(current), currentSectionPeak);
|
double strain = StrainValueAt(current);
|
||||||
|
currentSectionPeak = Math.Max(strain, currentSectionPeak);
|
||||||
|
|
||||||
|
// Store the strain value for the object
|
||||||
|
ObjectStrains.Add(strain);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the number of strains weighted against the top strain.
|
||||||
|
/// The result is scaled by clock rate as it affects the total number of strains.
|
||||||
|
/// </summary>
|
||||||
|
public virtual double CountTopWeightedStrains()
|
||||||
|
{
|
||||||
|
if (ObjectStrains.Count == 0)
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
double consistentTopStrain = DifficultyValue() / 10; // What would the top strain be if all strain values were identical
|
||||||
|
// Use a weighted sum of all strains. Constants are arbitrary and give nice values
|
||||||
|
return ObjectStrains.Sum(s => 1.1 / (1 + Math.Exp(-10 * (s / consistentTopStrain - 0.88))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Difficulty.Utils
|
||||||
|
{
|
||||||
|
public static class DifficultyCalculationUtils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts BPM value into milliseconds
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bpm">Beats per minute</param>
|
||||||
|
/// <param name="delimiter">Which rhythm delimiter to use, default is 1/4</param>
|
||||||
|
/// <returns>BPM conveted to milliseconds</returns>
|
||||||
|
public static double BPMToMilliseconds(double bpm, int delimiter = 4)
|
||||||
|
{
|
||||||
|
return 60000.0 / delimiter / bpm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts milliseconds value into a BPM value
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ms">Milliseconds</param>
|
||||||
|
/// <param name="delimiter">Which rhythm delimiter to use, default is 1/4</param>
|
||||||
|
/// <returns>Milliseconds conveted to beats per minute</returns>
|
||||||
|
public static double MillisecondsToBPM(double ms, int delimiter = 4)
|
||||||
|
{
|
||||||
|
return 60000.0 / (ms * delimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates a S-shaped logistic function (https://en.wikipedia.org/wiki/Logistic_function)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x">Value to calculate the function for</param>
|
||||||
|
/// <param name="maxValue">Maximum value returnable by the function</param>
|
||||||
|
/// <param name="multiplier">Growth rate of the function</param>
|
||||||
|
/// <param name="midpointOffset">How much the function midpoint is offset from zero <paramref name="x"/></param>
|
||||||
|
/// <returns>The output of logistic function of <paramref name="x"/></returns>
|
||||||
|
public static double Logistic(double x, double midpointOffset, double multiplier, double maxValue = 1) => maxValue / (1 + Math.Exp(multiplier * (midpointOffset - x)));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates a S-shaped logistic function (https://en.wikipedia.org/wiki/Logistic_function)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="maxValue">Maximum value returnable by the function</param>
|
||||||
|
/// <param name="exponent">Exponent</param>
|
||||||
|
/// <returns>The output of logistic function</returns>
|
||||||
|
public static double Logistic(double exponent, double maxValue = 1) => maxValue / (1 + Math.Exp(exponent));
|
||||||
|
}
|
||||||
|
}
|
@ -537,22 +537,23 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
#region IPlacementHandler
|
#region IPlacementHandler
|
||||||
|
|
||||||
public void BeginPlacement(HitObject hitObject)
|
public void ShowPlacement(HitObject hitObject)
|
||||||
{
|
{
|
||||||
EditorBeatmap.PlacementObject.Value = hitObject;
|
EditorBeatmap.PlacementObject.Value = hitObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EndPlacement(HitObject hitObject, bool commit)
|
public void HidePlacement()
|
||||||
{
|
{
|
||||||
EditorBeatmap.PlacementObject.Value = null;
|
EditorBeatmap.PlacementObject.Value = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (commit)
|
public void CommitPlacement(HitObject hitObject)
|
||||||
{
|
{
|
||||||
EditorBeatmap.Add(hitObject);
|
EditorBeatmap.PlacementObject.Value = null;
|
||||||
|
EditorBeatmap.Add(hitObject);
|
||||||
|
|
||||||
if (autoSeekOnPlacement.Value && EditorClock.CurrentTime < hitObject.StartTime)
|
if (autoSeekOnPlacement.Value && EditorClock.CurrentTime < hitObject.StartTime)
|
||||||
EditorClock.SeekSmoothlyTo(hitObject.StartTime);
|
EditorClock.SeekSmoothlyTo(hitObject.StartTime);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Delete(HitObject hitObject) => EditorBeatmap.Remove(hitObject);
|
public void Delete(HitObject hitObject) => EditorBeatmap.Remove(hitObject);
|
||||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -63,18 +64,26 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
startTimeBindable.BindValueChanged(_ => ApplyDefaultsToHitObject(), true);
|
startTimeBindable.BindValueChanged(_ => ApplyDefaultsToHitObject(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool placementBegun;
|
||||||
|
|
||||||
protected override void BeginPlacement(bool commitStart = false)
|
protected override void BeginPlacement(bool commitStart = false)
|
||||||
{
|
{
|
||||||
base.BeginPlacement(commitStart);
|
base.BeginPlacement(commitStart);
|
||||||
|
|
||||||
placementHandler.BeginPlacement(HitObject);
|
if (State.Value == Visibility.Visible)
|
||||||
|
placementHandler.ShowPlacement(HitObject);
|
||||||
|
|
||||||
|
placementBegun = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void EndPlacement(bool commit)
|
public override void EndPlacement(bool commit)
|
||||||
{
|
{
|
||||||
base.EndPlacement(commit);
|
base.EndPlacement(commit);
|
||||||
|
|
||||||
placementHandler.EndPlacement(HitObject, IsValidForPlacement && commit);
|
if (IsValidForPlacement && commit)
|
||||||
|
placementHandler.CommitPlacement(HitObject);
|
||||||
|
else
|
||||||
|
placementHandler.HidePlacement();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -127,5 +136,19 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
/// refreshing <see cref="Objects.HitObject.NestedHitObjects"/> and parameters for the <see cref="HitObject"/>.
|
/// refreshing <see cref="Objects.HitObject.NestedHitObjects"/> and parameters for the <see cref="HitObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.Difficulty);
|
protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.Difficulty);
|
||||||
|
|
||||||
|
protected override void PopIn()
|
||||||
|
{
|
||||||
|
base.PopIn();
|
||||||
|
|
||||||
|
if (placementBegun)
|
||||||
|
placementHandler.ShowPlacement(HitObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopOut()
|
||||||
|
{
|
||||||
|
base.PopOut();
|
||||||
|
placementHandler.HidePlacement();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ using osu.Framework.Input.Bindings;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osuTK;
|
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit
|
namespace osu.Game.Rulesets.Edit
|
||||||
@ -15,7 +14,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A blueprint which governs the placement of something.
|
/// A blueprint which governs the placement of something.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract partial class PlacementBlueprint : CompositeDrawable, IKeyBindingHandler<GlobalAction>
|
public abstract partial class PlacementBlueprint : VisibilityContainer, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the <see cref="HitObject"/> is currently mid-placement, but has not necessarily finished being placed.
|
/// Whether the <see cref="HitObject"/> is currently mid-placement, but has not necessarily finished being placed.
|
||||||
@ -31,12 +30,17 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
protected virtual bool IsValidForPlacement => true;
|
protected virtual bool IsValidForPlacement => true;
|
||||||
|
|
||||||
|
// the blueprint should still be considered for input even if it is hidden,
|
||||||
|
// especially when such input is the reason for making the blueprint become visible.
|
||||||
|
public override bool PropagatePositionalInputSubTree => true;
|
||||||
|
public override bool PropagateNonPositionalInputSubTree => true;
|
||||||
|
|
||||||
protected PlacementBlueprint()
|
protected PlacementBlueprint()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
// This is required to allow the blueprint's position to be updated via OnMouseMove/Handle
|
// the blueprint should still be considered for input even if it is hidden,
|
||||||
// on the same frame it is made visible via a PlacementState change.
|
// especially when such input is the reason for making the blueprint become visible.
|
||||||
AlwaysPresent = true;
|
AlwaysPresent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,8 +108,6 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
|
||||||
|
|
||||||
protected override bool Handle(UIEvent e)
|
protected override bool Handle(UIEvent e)
|
||||||
{
|
{
|
||||||
base.Handle(e);
|
base.Handle(e);
|
||||||
@ -127,6 +129,9 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void PopIn() => this.FadeIn();
|
||||||
|
protected override void PopOut() => this.FadeOut();
|
||||||
|
|
||||||
public enum PlacementState
|
public enum PlacementState
|
||||||
{
|
{
|
||||||
Waiting,
|
Waiting,
|
||||||
|
@ -316,9 +316,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Refreshes the current placement tool.
|
/// Refreshes the current placement tool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void refreshTool()
|
private void refreshPlacement()
|
||||||
{
|
{
|
||||||
removePlacement();
|
CurrentPlacement?.EndPlacement(false);
|
||||||
|
CurrentPlacement?.Expire();
|
||||||
|
CurrentPlacement = null;
|
||||||
|
|
||||||
ensurePlacementCreated();
|
ensurePlacementCreated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,21 +347,24 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
{
|
{
|
||||||
case PlacementBlueprint.PlacementState.Waiting:
|
case PlacementBlueprint.PlacementState.Waiting:
|
||||||
if (!Composer.CursorInPlacementArea)
|
if (!Composer.CursorInPlacementArea)
|
||||||
removePlacement();
|
CurrentPlacement.Hide();
|
||||||
|
else
|
||||||
|
CurrentPlacement.Show();
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PlacementBlueprint.PlacementState.Active:
|
||||||
|
CurrentPlacement.Show();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PlacementBlueprint.PlacementState.Finished:
|
case PlacementBlueprint.PlacementState.Finished:
|
||||||
removePlacement();
|
refreshPlacement();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (Composer.CursorInPlacementArea)
|
// updates the placement with the latest editor clock time.
|
||||||
ensurePlacementCreated();
|
|
||||||
|
|
||||||
// updates the placement with the latest editor clock time.
|
|
||||||
if (CurrentPlacement != null)
|
|
||||||
updatePlacementTimeAndPosition();
|
updatePlacementTimeAndPosition();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
@ -386,7 +392,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
private void hitObjectAdded(HitObject obj)
|
private void hitObjectAdded(HitObject obj)
|
||||||
{
|
{
|
||||||
// refresh the tool to handle the case of placement completing.
|
// refresh the tool to handle the case of placement completing.
|
||||||
refreshTool();
|
refreshPlacement();
|
||||||
|
|
||||||
// on successful placement, the new combo button should be reset as this is the most common user interaction.
|
// on successful placement, the new combo button should be reset as this is the most common user interaction.
|
||||||
if (Beatmap.SelectedHitObjects.Count == 0)
|
if (Beatmap.SelectedHitObjects.Count == 0)
|
||||||
@ -415,14 +421,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
public void CommitIfPlacementActive()
|
public void CommitIfPlacementActive()
|
||||||
{
|
{
|
||||||
CurrentPlacement?.EndPlacement(CurrentPlacement.PlacementActive == PlacementBlueprint.PlacementState.Active);
|
CurrentPlacement?.EndPlacement(CurrentPlacement.PlacementActive == PlacementBlueprint.PlacementState.Active);
|
||||||
removePlacement();
|
refreshPlacement();
|
||||||
}
|
|
||||||
|
|
||||||
private void removePlacement()
|
|
||||||
{
|
|
||||||
CurrentPlacement?.EndPlacement(false);
|
|
||||||
CurrentPlacement?.Expire();
|
|
||||||
CurrentPlacement = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompositionTool currentTool;
|
private CompositionTool currentTool;
|
||||||
|
@ -10,17 +10,21 @@ namespace osu.Game.Screens.Edit.Compose
|
|||||||
public interface IPlacementHandler
|
public interface IPlacementHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Notifies that a placement has begun.
|
/// Notifies that a placement blueprint became visible on the screen.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hitObject">The <see cref="HitObject"/> being placed.</param>
|
/// <param name="hitObject">The <see cref="HitObject"/> representing the placement.</param>
|
||||||
void BeginPlacement(HitObject hitObject);
|
void ShowPlacement(HitObject hitObject);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Notifies that a placement has finished.
|
/// Notifies that a visible placement blueprint has been hidden.
|
||||||
|
/// </summary>
|
||||||
|
void HidePlacement();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Notifies that a placement has been committed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hitObject">The <see cref="HitObject"/> that has been placed.</param>
|
/// <param name="hitObject">The <see cref="HitObject"/> that has been placed.</param>
|
||||||
/// <param name="commit">Whether the object should be committed.</param>
|
void CommitPlacement(HitObject hitObject);
|
||||||
void EndPlacement(HitObject hitObject, bool commit);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes a <see cref="HitObject"/>.
|
/// Deletes a <see cref="HitObject"/>.
|
||||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
public Action? OnEditBeatmap;
|
public Action? OnEditBeatmap;
|
||||||
public Action? OnEditSkin;
|
public Action? OnEditSkin;
|
||||||
public Action? OnExit;
|
public Action<UIEvent>? OnExit;
|
||||||
public Action? OnBeatmapListing;
|
public Action? OnBeatmapListing;
|
||||||
public Action? OnSolo;
|
public Action? OnSolo;
|
||||||
public Action? OnSettings;
|
public Action? OnSettings;
|
||||||
@ -104,11 +104,11 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
buttonArea.AddRange(new Drawable[]
|
buttonArea.AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
new MainMenuButton(ButtonSystemStrings.Settings, string.Empty, OsuIcon.Settings, new Color4(85, 85, 85, 255), _ => OnSettings?.Invoke(), Key.O, Key.S)
|
new MainMenuButton(ButtonSystemStrings.Settings, string.Empty, OsuIcon.Settings, new Color4(85, 85, 85, 255), (_, _) => OnSettings?.Invoke(), Key.O, Key.S)
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding { Right = WEDGE_WIDTH },
|
Padding = new MarginPadding { Right = WEDGE_WIDTH },
|
||||||
},
|
},
|
||||||
backButton = new MainMenuButton(ButtonSystemStrings.Back, @"back-to-top", OsuIcon.PrevCircle, new Color4(51, 58, 94, 255), _ => State = ButtonSystemState.TopLevel)
|
backButton = new MainMenuButton(ButtonSystemStrings.Back, @"back-to-top", OsuIcon.PrevCircle, new Color4(51, 58, 94, 255), (_, _) => State = ButtonSystemState.TopLevel)
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding { Right = WEDGE_WIDTH },
|
Padding = new MarginPadding { Right = WEDGE_WIDTH },
|
||||||
VisibleStateMin = ButtonSystemState.Play,
|
VisibleStateMin = ButtonSystemState.Play,
|
||||||
@ -132,7 +132,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio, IdleTracker? idleTracker, GameHost host)
|
private void load(AudioManager audio, IdleTracker? idleTracker, GameHost host)
|
||||||
{
|
{
|
||||||
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Solo, @"button-default-select", OsuIcon.Player, new Color4(102, 68, 204, 255), _ => OnSolo?.Invoke(), Key.P)
|
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Solo, @"button-default-select", OsuIcon.Player, new Color4(102, 68, 204, 255), (_, _) => OnSolo?.Invoke(), Key.P)
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding { Left = WEDGE_WIDTH },
|
Padding = new MarginPadding { Left = WEDGE_WIDTH },
|
||||||
});
|
});
|
||||||
@ -141,22 +141,22 @@ namespace osu.Game.Screens.Menu
|
|||||||
buttonsPlay.Add(new DailyChallengeButton(@"button-daily-select", new Color4(94, 63, 186, 255), onDailyChallenge, Key.D));
|
buttonsPlay.Add(new DailyChallengeButton(@"button-daily-select", new Color4(94, 63, 186, 255), onDailyChallenge, Key.D));
|
||||||
buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play);
|
buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play);
|
||||||
|
|
||||||
buttonsEdit.Add(new MainMenuButton(EditorStrings.BeatmapEditor.ToLower(), @"button-default-select", OsuIcon.Beatmap, new Color4(238, 170, 0, 255), _ => OnEditBeatmap?.Invoke(), Key.B, Key.E)
|
buttonsEdit.Add(new MainMenuButton(EditorStrings.BeatmapEditor.ToLower(), @"button-default-select", OsuIcon.Beatmap, new Color4(238, 170, 0, 255), (_, _) => OnEditBeatmap?.Invoke(), Key.B, Key.E)
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding { Left = WEDGE_WIDTH },
|
Padding = new MarginPadding { Left = WEDGE_WIDTH },
|
||||||
});
|
});
|
||||||
buttonsEdit.Add(new MainMenuButton(SkinEditorStrings.SkinEditor.ToLower(), @"button-default-select", OsuIcon.SkinB, new Color4(220, 160, 0, 255), _ => OnEditSkin?.Invoke(), Key.S));
|
buttonsEdit.Add(new MainMenuButton(SkinEditorStrings.SkinEditor.ToLower(), @"button-default-select", OsuIcon.SkinB, new Color4(220, 160, 0, 255), (_, _) => OnEditSkin?.Invoke(), Key.S));
|
||||||
buttonsEdit.ForEach(b => b.VisibleState = ButtonSystemState.Edit);
|
buttonsEdit.ForEach(b => b.VisibleState = ButtonSystemState.Edit);
|
||||||
|
|
||||||
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), _ => State = ButtonSystemState.Play, Key.P, Key.M, Key.L)
|
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), (_, _) => State = ButtonSystemState.Play, Key.P, Key.M, Key.L)
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding { Left = WEDGE_WIDTH },
|
Padding = new MarginPadding { Left = WEDGE_WIDTH },
|
||||||
});
|
});
|
||||||
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-play-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), _ => State = ButtonSystemState.Edit, Key.E));
|
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-play-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), (_, _) => State = ButtonSystemState.Edit, Key.E));
|
||||||
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-default-select", OsuIcon.Beatmap, new Color4(165, 204, 0, 255), _ => OnBeatmapListing?.Invoke(), Key.B, Key.D));
|
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-default-select", OsuIcon.Beatmap, new Color4(165, 204, 0, 255), (_, _) => OnBeatmapListing?.Invoke(), Key.B, Key.D));
|
||||||
|
|
||||||
if (host.CanExit)
|
if (host.CanExit)
|
||||||
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Exit, string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), _ => OnExit?.Invoke(), Key.Q));
|
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Exit, string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), (_, e) => OnExit?.Invoke(e), Key.Q));
|
||||||
|
|
||||||
buttonArea.AddRange(buttonsPlay);
|
buttonArea.AddRange(buttonsPlay);
|
||||||
buttonArea.AddRange(buttonsEdit);
|
buttonArea.AddRange(buttonsEdit);
|
||||||
@ -179,7 +179,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
sampleLogoSwoosh = audio.Samples.Get(@"Menu/osu-logo-swoosh");
|
sampleLogoSwoosh = audio.Samples.Get(@"Menu/osu-logo-swoosh");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMultiplayer(MainMenuButton _)
|
private void onMultiplayer(MainMenuButton mainMenuButton, UIEvent uiEvent)
|
||||||
{
|
{
|
||||||
if (api.State.Value != APIState.Online)
|
if (api.State.Value != APIState.Online)
|
||||||
{
|
{
|
||||||
@ -190,7 +190,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
OnMultiplayer?.Invoke();
|
OnMultiplayer?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPlaylists(MainMenuButton _)
|
private void onPlaylists(MainMenuButton mainMenuButton, UIEvent uiEvent)
|
||||||
{
|
{
|
||||||
if (api.State.Value != APIState.Online)
|
if (api.State.Value != APIState.Online)
|
||||||
{
|
{
|
||||||
@ -201,7 +201,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
OnPlaylists?.Invoke();
|
OnPlaylists?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDailyChallenge(MainMenuButton button)
|
private void onDailyChallenge(MainMenuButton button, UIEvent uiEvent)
|
||||||
{
|
{
|
||||||
if (api.State.Value != APIState.Online)
|
if (api.State.Value != APIState.Online)
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps.Drawables;
|
using osu.Game.Beatmaps.Drawables;
|
||||||
@ -50,7 +51,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private SessionStatics statics { get; set; } = null!;
|
private SessionStatics statics { get; set; } = null!;
|
||||||
|
|
||||||
public DailyChallengeButton(string sampleName, Color4 colour, Action<MainMenuButton>? clickAction = null, params Key[] triggerKeys)
|
public DailyChallengeButton(string sampleName, Color4 colour, Action<MainMenuButton, UIEvent>? clickAction = null, params Key[] triggerKeys)
|
||||||
: base(ButtonSystemStrings.DailyChallenge, sampleName, OsuIcon.DailyChallenge, colour, clickAction, triggerKeys)
|
: base(ButtonSystemStrings.DailyChallenge, sampleName, OsuIcon.DailyChallenge, colour, clickAction, triggerKeys)
|
||||||
{
|
{
|
||||||
BaseSize = new Vector2(ButtonSystem.BUTTON_WIDTH * 1.3f, ButtonArea.BUTTON_AREA_HEIGHT);
|
BaseSize = new Vector2(ButtonSystem.BUTTON_WIDTH * 1.3f, ButtonArea.BUTTON_AREA_HEIGHT);
|
||||||
|
@ -153,9 +153,9 @@ namespace osu.Game.Screens.Menu
|
|||||||
else
|
else
|
||||||
this.Push(new DailyChallengeIntro(room));
|
this.Push(new DailyChallengeIntro(room));
|
||||||
},
|
},
|
||||||
OnExit = () =>
|
OnExit = e =>
|
||||||
{
|
{
|
||||||
exitConfirmedViaHoldOrClick = true;
|
exitConfirmedViaHoldOrClick = e is MouseEvent;
|
||||||
this.Exit();
|
this.Exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
protected Vector2 BaseSize { get; init; } = new Vector2(ButtonSystem.BUTTON_WIDTH, ButtonArea.BUTTON_AREA_HEIGHT);
|
protected Vector2 BaseSize { get; init; } = new Vector2(ButtonSystem.BUTTON_WIDTH, ButtonArea.BUTTON_AREA_HEIGHT);
|
||||||
|
|
||||||
private readonly Action<MainMenuButton>? clickAction;
|
private readonly Action<MainMenuButton, UIEvent>? clickAction;
|
||||||
|
|
||||||
private readonly Container background;
|
private readonly Container background;
|
||||||
private readonly Drawable backgroundContent;
|
private readonly Drawable backgroundContent;
|
||||||
@ -84,7 +84,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => background.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => background.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
public MainMenuButton(LocalisableString text, string sampleName, IconUsage symbol, Color4 colour, Action<MainMenuButton>? clickAction = null, params Key[] triggerKeys)
|
public MainMenuButton(LocalisableString text, string sampleName, IconUsage symbol, Color4 colour, Action<MainMenuButton, UIEvent>? clickAction = null, params Key[] triggerKeys)
|
||||||
{
|
{
|
||||||
this.sampleName = sampleName;
|
this.sampleName = sampleName;
|
||||||
this.clickAction = clickAction;
|
this.clickAction = clickAction;
|
||||||
@ -263,7 +263,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
trigger();
|
trigger(e);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,19 +274,19 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
if (TriggerKeys.Contains(e.Key))
|
if (TriggerKeys.Contains(e.Key))
|
||||||
{
|
{
|
||||||
trigger();
|
trigger(e);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void trigger()
|
private void trigger(UIEvent e)
|
||||||
{
|
{
|
||||||
sampleChannel = sampleClick?.GetChannel();
|
sampleChannel = sampleClick?.GetChannel();
|
||||||
sampleChannel?.Play();
|
sampleChannel?.Play();
|
||||||
|
|
||||||
clickAction?.Invoke(this);
|
clickAction?.Invoke(this, e);
|
||||||
|
|
||||||
boxHoverLayer.ClearTransforms();
|
boxHoverLayer.ClearTransforms();
|
||||||
boxHoverLayer.Alpha = 0.9f;
|
boxHoverLayer.Alpha = 0.9f;
|
||||||
|
@ -224,7 +224,7 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
idleTracker = new IdleTracker(750),
|
idleTracker = new IdleTracker(1500),
|
||||||
sampleRestart = new SkinnableSound(new SampleInfo(@"Gameplay/restart", @"pause-retry-click"))
|
sampleRestart = new SkinnableSound(new SampleInfo(@"Gameplay/restart", @"pause-retry-click"))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -222,12 +222,6 @@ namespace osu.Game.Screens.Select
|
|||||||
InternalChild = new Container
|
InternalChild = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding
|
|
||||||
{
|
|
||||||
// Avoid clash between scrollbar and osu! logo.
|
|
||||||
Top = 10,
|
|
||||||
Bottom = 100,
|
|
||||||
},
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
setPool,
|
setPool,
|
||||||
@ -1266,6 +1260,38 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
return base.OnDragStart(e);
|
return base.OnDragStart(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override ScrollbarContainer CreateScrollbar(Direction direction)
|
||||||
|
{
|
||||||
|
return new PaddedScrollbar();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected partial class PaddedScrollbar : OsuScrollbar
|
||||||
|
{
|
||||||
|
public PaddedScrollbar()
|
||||||
|
: base(Direction.Vertical)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const float top_padding = 10;
|
||||||
|
private const float bottom_padding = 70;
|
||||||
|
|
||||||
|
protected override float ToScrollbarPosition(float scrollPosition)
|
||||||
|
{
|
||||||
|
if (Precision.AlmostEquals(0, ScrollableExtent))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return top_padding + (ScrollbarMovementExtent - (top_padding + bottom_padding)) * (scrollPosition / ScrollableExtent);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override float FromScrollbarPosition(float scrollbarPosition)
|
||||||
|
{
|
||||||
|
if (Precision.AlmostEquals(0, ScrollbarMovementExtent))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return ScrollableExtent * ((scrollbarPosition - top_padding) / (ScrollbarMovementExtent - (top_padding + bottom_padding)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
|
@ -59,20 +59,20 @@ namespace osu.Game.Tests.Visual
|
|||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
ResetPlacement();
|
ResetPlacement();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeginPlacement(HitObject hitObject)
|
public void ShowPlacement(HitObject hitObject)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EndPlacement(HitObject hitObject, bool commit)
|
public void HidePlacement()
|
||||||
{
|
{
|
||||||
if (commit)
|
}
|
||||||
AddHitObject(CreateHitObject(hitObject));
|
|
||||||
|
|
||||||
ResetPlacement();
|
public void CommitPlacement(HitObject hitObject)
|
||||||
|
{
|
||||||
|
AddHitObject(CreateHitObject(hitObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ResetPlacement()
|
protected void ResetPlacement()
|
||||||
@ -89,6 +89,10 @@ namespace osu.Game.Tests.Visual
|
|||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
|
if (CurrentBlueprint.PlacementActive == PlacementBlueprint.PlacementState.Finished)
|
||||||
|
ResetPlacement();
|
||||||
|
|
||||||
updatePlacementTimeAndPosition();
|
updatePlacementTimeAndPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ namespace osu.Game.Users
|
|||||||
Margin = new MarginPadding { Top = main_content_height },
|
Margin = new MarginPadding { Top = main_content_height },
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding { Left = 80, Vertical = padding },
|
Padding = new MarginPadding(padding),
|
||||||
ColumnDimensions = new[]
|
ColumnDimensions = new[]
|
||||||
{
|
{
|
||||||
new Dimension(),
|
new Dimension(),
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
<PackageReference Include="DiffPlex" Version="1.7.2" />
|
<PackageReference Include="DiffPlex" Version="1.7.2" />
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.67" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.11.70" />
|
||||||
<PackageReference Include="Humanizer" Version="2.14.1" />
|
<PackageReference Include="Humanizer" Version="2.14.1" />
|
||||||
<PackageReference Include="MessagePack" Version="2.5.187" />
|
<PackageReference Include="MessagePack" Version="2.5.187" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.10" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.10" />
|
||||||
@ -35,9 +35,9 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="11.5.0" />
|
<PackageReference Include="Realm" Version="11.5.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2024.1025.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2024.1111.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.1106.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.1106.0" />
|
||||||
<PackageReference Include="Sentry" Version="4.12.1" />
|
<PackageReference Include="Sentry" Version="4.13.0" />
|
||||||
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
|
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
|
||||||
<PackageReference Include="SharpCompress" Version="0.38.0" />
|
<PackageReference Include="SharpCompress" Version="0.38.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||||
|
Loading…
Reference in New Issue
Block a user