mirror of
https://github.com/ppy/osu.git
synced 2026-05-24 18:20:12 +08:00
Merge branch 'master' into diffcalc-optimisations
This commit is contained in:
@@ -74,16 +74,25 @@ jobs:
|
||||
mkdir -p $GITHUB_WORKSPACE/master/
|
||||
mkdir -p $GITHUB_WORKSPACE/pr/
|
||||
|
||||
- name: Get upstream branch # https://akaimo.hatenablog.jp/entry/2020/05/16/101251
|
||||
id: upstreambranch
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
echo "::set-output name=branchname::$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.ref' | sed 's/\"//g')"
|
||||
echo "::set-output name=repo::$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.repo.full_name' | sed 's/\"//g')"
|
||||
|
||||
# Checkout osu
|
||||
- name: Checkout osu (master)
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: ppy/osu
|
||||
path: 'master/osu'
|
||||
- name: Checkout osu (pr)
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: 'pr/osu'
|
||||
repository: ${{ steps.upstreambranch.outputs.repo }}
|
||||
ref: ${{ steps.upstreambranch.outputs.branchname }}
|
||||
|
||||
- name: Checkout osu-difficulty-calculator (master)
|
||||
uses: actions/checkout@v2
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.918.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.920.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.924.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. -->
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Osu.Edit;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
public class TestSceneOsuEditorGrids : EditorTestScene
|
||||
{
|
||||
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
|
||||
|
||||
[Test]
|
||||
public void TestGridExclusivity()
|
||||
{
|
||||
AddStep("enable distance snap grid", () => InputManager.Key(Key.T));
|
||||
AddStep("select second object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.ElementAt(1)));
|
||||
AddUntilStep("distance snap grid visible", () => this.ChildrenOfType<OsuDistanceSnapGrid>().Any());
|
||||
rectangularGridActive(false);
|
||||
|
||||
AddStep("enable rectangular grid", () => InputManager.Key(Key.Y));
|
||||
AddUntilStep("distance snap grid hidden", () => !this.ChildrenOfType<OsuDistanceSnapGrid>().Any());
|
||||
rectangularGridActive(true);
|
||||
|
||||
AddStep("enable distance snap grid", () => InputManager.Key(Key.T));
|
||||
AddStep("select second object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.ElementAt(1)));
|
||||
AddUntilStep("distance snap grid visible", () => this.ChildrenOfType<OsuDistanceSnapGrid>().Any());
|
||||
rectangularGridActive(false);
|
||||
}
|
||||
|
||||
private void rectangularGridActive(bool active)
|
||||
{
|
||||
AddStep("choose placement tool", () => InputManager.Key(Key.Number2));
|
||||
AddStep("move cursor to (1, 1)", () =>
|
||||
{
|
||||
var composer = Editor.ChildrenOfType<OsuRectangularPositionSnapGrid>().Single();
|
||||
InputManager.MoveMouseTo(composer.ToScreenSpace(new Vector2(1, 1)));
|
||||
});
|
||||
|
||||
if (active)
|
||||
AddAssert("placement blueprint at (0, 0)", () => Precision.AlmostEquals(Editor.ChildrenOfType<HitCirclePlacementBlueprint>().Single().HitObject.Position, new Vector2(0, 0)));
|
||||
else
|
||||
AddAssert("placement blueprint at (1, 1)", () => Precision.AlmostEquals(Editor.ChildrenOfType<HitCirclePlacementBlueprint>().Single().HitObject.Position, new Vector2(1, 1)));
|
||||
|
||||
AddStep("choose selection tool", () => InputManager.Key(Key.Number1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGridSizeToggling()
|
||||
{
|
||||
AddStep("enable rectangular grid", () => InputManager.Key(Key.Y));
|
||||
AddUntilStep("rectangular grid visible", () => this.ChildrenOfType<OsuRectangularPositionSnapGrid>().Any());
|
||||
gridSizeIs(4);
|
||||
|
||||
nextGridSizeIs(8);
|
||||
nextGridSizeIs(16);
|
||||
nextGridSizeIs(32);
|
||||
nextGridSizeIs(4);
|
||||
}
|
||||
|
||||
private void nextGridSizeIs(int size)
|
||||
{
|
||||
AddStep("toggle to next grid size", () => InputManager.Key(Key.G));
|
||||
gridSizeIs(size);
|
||||
}
|
||||
|
||||
private void gridSizeIs(int size)
|
||||
=> AddAssert($"grid size is {size}", () => this.ChildrenOfType<OsuRectangularPositionSnapGrid>().Single().Spacing == new Vector2(size)
|
||||
&& EditorBeatmap.BeatmapInfo.GridSize == size);
|
||||
}
|
||||
}
|
||||
@@ -98,6 +98,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
var controlPointInfo = new ControlPointInfo();
|
||||
controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true });
|
||||
controlPointInfo.Add(5000, new EffectControlPoint { KiaiMode = false });
|
||||
|
||||
return new Beatmap
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
public class OsuDifficultyCalculator : DifficultyCalculator
|
||||
{
|
||||
private const double difficulty_multiplier = 0.0675;
|
||||
private double hitWindowGreat;
|
||||
|
||||
public OsuDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
@@ -52,11 +53,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0;
|
||||
|
||||
HitWindows hitWindows = new OsuHitWindows();
|
||||
hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
|
||||
|
||||
// Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
|
||||
double hitWindowGreat = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate;
|
||||
double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||
|
||||
int maxCombo = beatmap.HitObjects.Count;
|
||||
@@ -96,12 +92,21 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
}
|
||||
}
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
|
||||
{
|
||||
new Aim(mods),
|
||||
new Speed(mods),
|
||||
new Flashlight(mods)
|
||||
};
|
||||
HitWindows hitWindows = new OsuHitWindows();
|
||||
hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
|
||||
|
||||
// Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
|
||||
hitWindowGreat = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate;
|
||||
|
||||
return new Skill[]
|
||||
{
|
||||
new Aim(mods),
|
||||
new Speed(mods, hitWindowGreat),
|
||||
new Flashlight(mods)
|
||||
};
|
||||
}
|
||||
|
||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||
{
|
||||
|
||||
@@ -16,6 +16,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
||||
|
||||
protected new OsuHitObject BaseObject => (OsuHitObject)base.BaseObject;
|
||||
|
||||
/// <summary>
|
||||
/// Milliseconds elapsed since the start time of the previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 25ms to account for simultaneous <see cref="OsuDifficultyHitObject"/>s.
|
||||
/// </summary>
|
||||
public double StrainTime { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Normalized distance from the end position of the previous <see cref="OsuDifficultyHitObject"/> to the start position of this <see cref="OsuDifficultyHitObject"/>.
|
||||
/// </summary>
|
||||
@@ -32,11 +37,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
||||
/// </summary>
|
||||
public double? Angle { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Milliseconds elapsed since the start time of the previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 50ms.
|
||||
/// </summary>
|
||||
public readonly double StrainTime;
|
||||
|
||||
private readonly OsuHitObject lastLastObject;
|
||||
private readonly OsuHitObject lastObject;
|
||||
|
||||
@@ -48,8 +48,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
||||
|
||||
setDistances();
|
||||
|
||||
// Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
|
||||
StrainTime = Math.Max(50, DeltaTime);
|
||||
// Capped to 25ms to prevent difficulty calculation breaking from simulatenous objects.
|
||||
StrainTime = Math.Max(DeltaTime, 25);
|
||||
}
|
||||
|
||||
private void setDistances()
|
||||
|
||||
@@ -6,6 +6,7 @@ using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Framework.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||
{
|
||||
@@ -26,12 +27,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||
protected override double DifficultyMultiplier => 1.04;
|
||||
|
||||
private const double min_speed_bonus = 75; // ~200BPM
|
||||
private const double max_speed_bonus = 45; // ~330BPM
|
||||
private const double speed_balancing_factor = 40;
|
||||
|
||||
public Speed(Mod[] mods)
|
||||
private readonly double greatWindow;
|
||||
|
||||
public Speed(Mod[] mods, double hitWindowGreat)
|
||||
: base(mods)
|
||||
{
|
||||
greatWindow = hitWindowGreat;
|
||||
}
|
||||
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
@@ -40,13 +43,25 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||
return 0;
|
||||
|
||||
var osuCurrent = (OsuDifficultyHitObject)current;
|
||||
var osuPrevious = Previous.Count > 0 ? (OsuDifficultyHitObject)Previous[0] : null;
|
||||
|
||||
double distance = Math.Min(single_spacing_threshold, osuCurrent.TravelDistance + osuCurrent.JumpDistance);
|
||||
double deltaTime = Math.Max(max_speed_bonus, current.DeltaTime);
|
||||
double strainTime = osuCurrent.StrainTime;
|
||||
|
||||
double greatWindowFull = greatWindow * 2;
|
||||
double speedWindowRatio = strainTime / greatWindowFull;
|
||||
|
||||
// Aim to nerf cheesy rhythms (Very fast consecutive doubles with large deltatimes between)
|
||||
if (osuPrevious != null && strainTime < greatWindowFull && osuPrevious.StrainTime > strainTime)
|
||||
strainTime = Interpolation.Lerp(osuPrevious.StrainTime, strainTime, speedWindowRatio);
|
||||
|
||||
// Cap deltatime to the OD 300 hitwindow.
|
||||
// 0.93 is derived from making sure 260bpm OD8 streams aren't nerfed harshly, whilst 0.92 limits the effect of the cap.
|
||||
strainTime /= Math.Clamp((strainTime / greatWindowFull) / 0.93, 0.92, 1);
|
||||
|
||||
double speedBonus = 1.0;
|
||||
if (deltaTime < min_speed_bonus)
|
||||
speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2);
|
||||
if (strainTime < min_speed_bonus)
|
||||
speedBonus = 1 + Math.Pow((min_speed_bonus - strainTime) / speed_balancing_factor, 2);
|
||||
|
||||
double angleBonus = 1.0;
|
||||
|
||||
@@ -64,7 +79,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||
}
|
||||
}
|
||||
|
||||
return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrent.StrainTime;
|
||||
return (1 + (speedBonus - 1) * 0.75)
|
||||
* angleBonus
|
||||
* (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5))
|
||||
/ strainTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,10 +42,12 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
};
|
||||
|
||||
private readonly Bindable<TernaryState> distanceSnapToggle = new Bindable<TernaryState>();
|
||||
private readonly Bindable<TernaryState> rectangularGridSnapToggle = new Bindable<TernaryState>();
|
||||
|
||||
protected override IEnumerable<TernaryButton> CreateTernaryButtons() => base.CreateTernaryButtons().Concat(new[]
|
||||
{
|
||||
new TernaryButton(distanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler })
|
||||
new TernaryButton(distanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler }),
|
||||
new TernaryButton(rectangularGridSnapToggle, "Grid Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Th })
|
||||
});
|
||||
|
||||
private BindableList<HitObject> selectedHitObjects;
|
||||
@@ -63,6 +65,10 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
PlayfieldBorderStyle = { Value = PlayfieldBorderStyle.Corners }
|
||||
},
|
||||
distanceSnapGridContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
rectangularPositionSnapGrid = new OsuRectangularPositionSnapGrid
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
@@ -73,7 +79,19 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
placementObject = EditorBeatmap.PlacementObject.GetBoundCopy();
|
||||
placementObject.ValueChanged += _ => updateDistanceSnapGrid();
|
||||
distanceSnapToggle.ValueChanged += _ => updateDistanceSnapGrid();
|
||||
distanceSnapToggle.ValueChanged += _ =>
|
||||
{
|
||||
updateDistanceSnapGrid();
|
||||
|
||||
if (distanceSnapToggle.Value == TernaryState.True)
|
||||
rectangularGridSnapToggle.Value = TernaryState.False;
|
||||
};
|
||||
|
||||
rectangularGridSnapToggle.ValueChanged += _ =>
|
||||
{
|
||||
if (rectangularGridSnapToggle.Value == TernaryState.True)
|
||||
distanceSnapToggle.Value = TernaryState.False;
|
||||
};
|
||||
|
||||
// we may be entering the screen with a selection already active
|
||||
updateDistanceSnapGrid();
|
||||
@@ -91,6 +109,8 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
private readonly Cached distanceSnapGridCache = new Cached();
|
||||
private double? lastDistanceSnapGridTime;
|
||||
|
||||
private RectangularPositionSnapGrid rectangularPositionSnapGrid;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@@ -122,13 +142,19 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
if (positionSnap.ScreenSpacePosition != screenSpacePosition)
|
||||
return positionSnap;
|
||||
|
||||
// will be null if distance snap is disabled or not feasible for the current time value.
|
||||
if (distanceSnapGrid == null)
|
||||
return base.SnapScreenSpacePositionToValidTime(screenSpacePosition);
|
||||
if (distanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null)
|
||||
{
|
||||
(Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition));
|
||||
return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time, PlayfieldAtScreenSpacePosition(screenSpacePosition));
|
||||
}
|
||||
|
||||
(Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition));
|
||||
if (rectangularGridSnapToggle.Value == TernaryState.True)
|
||||
{
|
||||
Vector2 pos = rectangularPositionSnapGrid.GetSnappedPosition(rectangularPositionSnapGrid.ToLocalSpace(screenSpacePosition));
|
||||
return new SnapResult(rectangularPositionSnapGrid.ToScreenSpace(pos), null, PlayfieldAtScreenSpacePosition(screenSpacePosition));
|
||||
}
|
||||
|
||||
return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time, PlayfieldAtScreenSpacePosition(screenSpacePosition));
|
||||
return base.SnapScreenSpacePositionToValidTime(screenSpacePosition);
|
||||
}
|
||||
|
||||
private bool snapToVisibleBlueprints(Vector2 screenSpacePosition, out SnapResult snapResult)
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public class OsuRectangularPositionSnapGrid : RectangularPositionSnapGrid, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
private static readonly int[] grid_sizes = { 4, 8, 16, 32 };
|
||||
|
||||
private int currentGridSizeIndex = grid_sizes.Length - 1;
|
||||
|
||||
[Resolved]
|
||||
private EditorBeatmap editorBeatmap { get; set; }
|
||||
|
||||
public OsuRectangularPositionSnapGrid()
|
||||
: base(OsuPlayfield.BASE_SIZE / 2)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var gridSizeIndex = Array.IndexOf(grid_sizes, editorBeatmap.BeatmapInfo.GridSize);
|
||||
if (gridSizeIndex >= 0)
|
||||
currentGridSizeIndex = gridSizeIndex;
|
||||
updateSpacing();
|
||||
}
|
||||
|
||||
private void nextGridSize()
|
||||
{
|
||||
currentGridSizeIndex = (currentGridSizeIndex + 1) % grid_sizes.Length;
|
||||
updateSpacing();
|
||||
}
|
||||
|
||||
private void updateSpacing()
|
||||
{
|
||||
int gridSize = grid_sizes[currentGridSizeIndex];
|
||||
|
||||
editorBeatmap.BeatmapInfo.GridSize = gridSize;
|
||||
Spacing = new Vector2(gridSize);
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.EditorCycleGridDisplayMode:
|
||||
nextGridSize();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,9 +39,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
[Resolved(canBeNull: true)]
|
||||
private GameplayBeatmap gameplayBeatmap { get; set; }
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private GameplayClock gameplayClock { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin, OsuColour colours)
|
||||
{
|
||||
@@ -83,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
DrawableHitObject kiaiHitObject = null;
|
||||
|
||||
// Check whether currently in a kiai section first. This is only done as an optimisation to avoid enumerating AliveObjects when not necessary.
|
||||
if (gameplayBeatmap.ControlPointInfo.EffectPointAt(gameplayBeatmap.Time.Current).KiaiMode)
|
||||
if (gameplayBeatmap.ControlPointInfo.EffectPointAt(Time.Current).KiaiMode)
|
||||
kiaiHitObject = playfield.HitObjectContainer.AliveObjects.FirstOrDefault(isTracking);
|
||||
|
||||
kiaiSpewer.Active.Value = kiaiHitObject != null;
|
||||
|
||||
@@ -2,45 +2,55 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Mods
|
||||
{
|
||||
public class TaikoModHidden : ModHidden
|
||||
public class TaikoModHidden : ModHidden, IApplicableToDrawableRuleset<TaikoHitObject>
|
||||
{
|
||||
public override string Description => @"Beats fade out before you hit them!";
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
|
||||
private ControlPointInfo controlPointInfo;
|
||||
/// <summary>
|
||||
/// How far away from the hit target should hitobjects start to fade out.
|
||||
/// Range: [0, 1]
|
||||
/// </summary>
|
||||
private const float fade_out_start_time = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// How long hitobjects take to fade out, in terms of the scrolling length.
|
||||
/// Range: [0, 1]
|
||||
/// </summary>
|
||||
private const float fade_out_duration = 0.375f;
|
||||
|
||||
private DrawableTaikoRuleset drawableRuleset;
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<TaikoHitObject> drawableRuleset)
|
||||
{
|
||||
this.drawableRuleset = (DrawableTaikoRuleset)drawableRuleset;
|
||||
}
|
||||
|
||||
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
{
|
||||
ApplyNormalVisibilityState(hitObject, state);
|
||||
}
|
||||
|
||||
protected double MultiplierAt(double position)
|
||||
{
|
||||
double beatLength = controlPointInfo.TimingPointAt(position).BeatLength;
|
||||
double speedMultiplier = controlPointInfo.DifficultyPointAt(position).SpeedMultiplier;
|
||||
|
||||
return speedMultiplier * TimingControlPoint.DEFAULT_BEAT_LENGTH / beatLength;
|
||||
}
|
||||
|
||||
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
{
|
||||
switch (hitObject)
|
||||
{
|
||||
case DrawableDrumRollTick _:
|
||||
case DrawableHit _:
|
||||
double preempt = 10000 / MultiplierAt(hitObject.HitObject.StartTime);
|
||||
double start = hitObject.HitObject.StartTime - preempt * 0.6;
|
||||
double duration = preempt * 0.3;
|
||||
double preempt = drawableRuleset.TimeRange.Value / drawableRuleset.ControlPointAt(hitObject.HitObject.StartTime).Multiplier;
|
||||
double start = hitObject.HitObject.StartTime - preempt * fade_out_start_time;
|
||||
double duration = preempt * fade_out_duration;
|
||||
|
||||
using (hitObject.BeginAbsoluteSequence(start))
|
||||
{
|
||||
@@ -56,10 +66,5 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ApplyToBeatmap(IBeatmap beatmap)
|
||||
{
|
||||
controlPointInfo = beatmap.ControlPointInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@@ -16,6 +17,7 @@ using osu.Game.Input.Handlers;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
@@ -60,6 +62,14 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
scroller.Height = ToLocalSpace(playfieldScreen.TopLeft + new Vector2(0, playfieldScreen.Height / 20)).Y;
|
||||
}
|
||||
|
||||
public MultiplierControlPoint ControlPointAt(double time)
|
||||
{
|
||||
int result = ControlPoints.BinarySearch(new MultiplierControlPoint(time));
|
||||
if (result < 0)
|
||||
result = Math.Clamp(~result - 1, 0, ControlPoints.Count);
|
||||
return ControlPoints[result];
|
||||
}
|
||||
|
||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer();
|
||||
|
||||
protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
public class TestSceneRectangularPositionSnapGrid : OsuManualInputManagerTestScene
|
||||
{
|
||||
private Container content;
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
base.Content.AddRange(new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Colour4.Gray
|
||||
},
|
||||
content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static readonly object[][] test_cases =
|
||||
{
|
||||
new object[] { new Vector2(0, 0), new Vector2(10, 10) },
|
||||
new object[] { new Vector2(240, 180), new Vector2(10, 15) },
|
||||
new object[] { new Vector2(160, 120), new Vector2(30, 20) },
|
||||
new object[] { new Vector2(480, 360), new Vector2(100, 100) },
|
||||
};
|
||||
|
||||
[TestCaseSource(nameof(test_cases))]
|
||||
public void TestRectangularGrid(Vector2 position, Vector2 spacing)
|
||||
{
|
||||
RectangularPositionSnapGrid grid = null;
|
||||
|
||||
AddStep("create grid", () => Child = grid = new RectangularPositionSnapGrid(position)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Spacing = spacing
|
||||
});
|
||||
|
||||
AddStep("add snapping cursor", () => Add(new SnappingCursorContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
GetSnapPosition = pos => grid.GetSnappedPosition(grid.ToLocalSpace(pos))
|
||||
}));
|
||||
}
|
||||
|
||||
private class SnappingCursorContainer : CompositeDrawable
|
||||
{
|
||||
public Func<Vector2, Vector2> GetSnapPosition;
|
||||
|
||||
private readonly Drawable cursor;
|
||||
|
||||
public SnappingCursorContainer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChild = cursor = new Circle
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(50),
|
||||
Colour = Color4.Red
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
updatePosition(GetContainingInputManager().CurrentState.Mouse.Position);
|
||||
}
|
||||
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
base.OnMouseMove(e);
|
||||
|
||||
updatePosition(e.ScreenSpaceMousePosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updatePosition(Vector2 screenSpacePosition)
|
||||
{
|
||||
cursor.Position = GetSnapPosition.Invoke(screenSpacePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,9 +24,10 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
[Test]
|
||||
public void TestBasic()
|
||||
{
|
||||
TestPopupDialog dialog = null;
|
||||
TestPopupDialog firstDialog = null;
|
||||
TestPopupDialog secondDialog = null;
|
||||
|
||||
AddStep("dialog #1", () => overlay.Push(dialog = new TestPopupDialog
|
||||
AddStep("dialog #1", () => overlay.Push(firstDialog = new TestPopupDialog
|
||||
{
|
||||
Icon = FontAwesome.Regular.TrashAlt,
|
||||
HeaderText = @"Confirm deletion of",
|
||||
@@ -46,9 +47,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
},
|
||||
}));
|
||||
|
||||
AddAssert("first dialog displayed", () => overlay.CurrentDialog == dialog);
|
||||
AddAssert("first dialog displayed", () => overlay.CurrentDialog == firstDialog);
|
||||
|
||||
AddStep("dialog #2", () => overlay.Push(dialog = new TestPopupDialog
|
||||
AddStep("dialog #2", () => overlay.Push(secondDialog = new TestPopupDialog
|
||||
{
|
||||
Icon = FontAwesome.Solid.Cog,
|
||||
HeaderText = @"What do you want to do with",
|
||||
@@ -82,30 +83,33 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
},
|
||||
}));
|
||||
|
||||
AddAssert("second dialog displayed", () => overlay.CurrentDialog == dialog);
|
||||
AddAssert("second dialog displayed", () => overlay.CurrentDialog == secondDialog);
|
||||
AddAssert("first dialog is not part of hierarchy", () => firstDialog.Parent == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDismissBeforePush()
|
||||
{
|
||||
TestPopupDialog testDialog = null;
|
||||
AddStep("dismissed dialog push", () =>
|
||||
{
|
||||
overlay.Push(new TestPopupDialog
|
||||
overlay.Push(testDialog = new TestPopupDialog
|
||||
{
|
||||
State = { Value = Visibility.Hidden }
|
||||
});
|
||||
});
|
||||
|
||||
AddAssert("no dialog pushed", () => overlay.CurrentDialog == null);
|
||||
AddAssert("dialog is not part of hierarchy", () => testDialog.Parent == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDismissBeforePushViaButtonPress()
|
||||
{
|
||||
TestPopupDialog testDialog = null;
|
||||
AddStep("dismissed dialog push", () =>
|
||||
{
|
||||
TestPopupDialog dialog;
|
||||
overlay.Push(dialog = new TestPopupDialog
|
||||
overlay.Push(testDialog = new TestPopupDialog
|
||||
{
|
||||
Buttons = new PopupDialogButton[]
|
||||
{
|
||||
@@ -113,10 +117,11 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
},
|
||||
});
|
||||
|
||||
dialog.PerformOkAction();
|
||||
testDialog.PerformOkAction();
|
||||
});
|
||||
|
||||
AddAssert("no dialog pushed", () => overlay.CurrentDialog == null);
|
||||
AddAssert("dialog is not part of hierarchy", () => testDialog.Parent == null);
|
||||
}
|
||||
|
||||
private class TestPopupDialog : PopupDialog
|
||||
|
||||
@@ -115,6 +115,8 @@ namespace osu.Game.Graphics
|
||||
null, TextureCoords);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool CanDrawOpaqueInterior => false;
|
||||
}
|
||||
|
||||
private readonly struct ParticlePart
|
||||
|
||||
@@ -164,6 +164,8 @@ namespace osu.Game.Graphics
|
||||
|
||||
return Vector2Extensions.Transform(new Vector2(x, y), DrawInfo.Matrix);
|
||||
}
|
||||
|
||||
protected override bool CanDrawOpaqueInterior => false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -274,21 +274,40 @@ namespace osu.Game.Graphics.UserInterface
|
||||
CornerRadius = corner_radius;
|
||||
Height = 40;
|
||||
|
||||
Foreground.Children = new Drawable[]
|
||||
Foreground.Child = new GridContainer
|
||||
{
|
||||
Text = new OsuSpriteText
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RowDimensions = new[]
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
},
|
||||
Icon = new SpriteIcon
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
Icon = FontAwesome.Solid.ChevronDown,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Margin = new MarginPadding { Right = 5 },
|
||||
Size = new Vector2(12),
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
Text = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Truncate = true,
|
||||
},
|
||||
Icon = new SpriteIcon
|
||||
{
|
||||
Icon = FontAwesome.Solid.ChevronDown,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Margin = new MarginPadding { Horizontal = 5 },
|
||||
Size = new Vector2(12),
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddInternal(new HoverClickSounds());
|
||||
|
||||
@@ -75,6 +75,7 @@ namespace osu.Game.Input.Bindings
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.A }, GlobalAction.EditorVerifyMode),
|
||||
new KeyBinding(new[] { InputKey.J }, GlobalAction.EditorNudgeLeft),
|
||||
new KeyBinding(new[] { InputKey.K }, GlobalAction.EditorNudgeRight),
|
||||
new KeyBinding(new[] { InputKey.G }, GlobalAction.EditorCycleGridDisplayMode),
|
||||
};
|
||||
|
||||
public IEnumerable<KeyBinding> InGameKeyBindings => new[]
|
||||
@@ -284,6 +285,9 @@ namespace osu.Game.Input.Bindings
|
||||
SeekReplayBackward,
|
||||
|
||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleChatFocus))]
|
||||
ToggleChatFocus
|
||||
ToggleChatFocus,
|
||||
|
||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorCycleGridDisplayMode))]
|
||||
EditorCycleGridDisplayMode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +164,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString EditorTimingMode => new TranslatableString(getKey(@"editor_timing_mode"), @"Timing mode");
|
||||
|
||||
/// <summary>
|
||||
/// "Cycle grid display mode"
|
||||
/// </summary>
|
||||
public static LocalisableString EditorCycleGridDisplayMode => new TranslatableString(getKey(@"editor_cycle_grid_display_mode"), @"Cycle grid display mode");
|
||||
|
||||
/// <summary>
|
||||
/// "Hold for HUD"
|
||||
/// </summary>
|
||||
|
||||
@@ -49,6 +49,8 @@ namespace osu.Game.Overlays
|
||||
Show();
|
||||
}
|
||||
|
||||
public override bool IsPresent => dialogContainer.Children.Count > 0;
|
||||
|
||||
protected override bool BlockNonPositionalInput => true;
|
||||
|
||||
private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v)
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Layout;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Compose.Components
|
||||
{
|
||||
public class RectangularPositionSnapGrid : CompositeDrawable
|
||||
{
|
||||
/// <summary>
|
||||
/// The position of the origin of this <see cref="RectangularPositionSnapGrid"/> in local coordinates.
|
||||
/// </summary>
|
||||
public Vector2 StartPosition { get; }
|
||||
|
||||
private Vector2 spacing = Vector2.One;
|
||||
|
||||
/// <summary>
|
||||
/// The spacing between grid lines of this <see cref="RectangularPositionSnapGrid"/>.
|
||||
/// </summary>
|
||||
public Vector2 Spacing
|
||||
{
|
||||
get => spacing;
|
||||
set
|
||||
{
|
||||
if (spacing.X <= 0 || spacing.Y <= 0)
|
||||
throw new ArgumentException("Grid spacing must be positive.");
|
||||
|
||||
spacing = value;
|
||||
gridCache.Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
|
||||
|
||||
public RectangularPositionSnapGrid(Vector2 startPosition)
|
||||
{
|
||||
StartPosition = startPosition;
|
||||
|
||||
AddLayout(gridCache);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!gridCache.IsValid)
|
||||
{
|
||||
ClearInternal();
|
||||
createContent();
|
||||
gridCache.Validate();
|
||||
}
|
||||
}
|
||||
|
||||
private void createContent()
|
||||
{
|
||||
var drawSize = DrawSize;
|
||||
|
||||
generateGridLines(Direction.Horizontal, StartPosition.Y, 0, -Spacing.Y);
|
||||
generateGridLines(Direction.Horizontal, StartPosition.Y, drawSize.Y, Spacing.Y);
|
||||
|
||||
generateGridLines(Direction.Vertical, StartPosition.X, 0, -Spacing.X);
|
||||
generateGridLines(Direction.Vertical, StartPosition.X, drawSize.X, Spacing.X);
|
||||
}
|
||||
|
||||
private void generateGridLines(Direction direction, float startPosition, float endPosition, float step)
|
||||
{
|
||||
int index = 0;
|
||||
float currentPosition = startPosition;
|
||||
|
||||
while ((endPosition - currentPosition) * Math.Sign(step) > 0)
|
||||
{
|
||||
var gridLine = new Box
|
||||
{
|
||||
Colour = Colour4.White,
|
||||
Alpha = index == 0 ? 0.3f : 0.1f,
|
||||
EdgeSmoothness = new Vector2(0.2f)
|
||||
};
|
||||
|
||||
if (direction == Direction.Horizontal)
|
||||
{
|
||||
gridLine.RelativeSizeAxes = Axes.X;
|
||||
gridLine.Height = 1;
|
||||
gridLine.Y = currentPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
gridLine.RelativeSizeAxes = Axes.Y;
|
||||
gridLine.Width = 1;
|
||||
gridLine.X = currentPosition;
|
||||
}
|
||||
|
||||
AddInternal(gridLine);
|
||||
|
||||
index += 1;
|
||||
currentPosition = startPosition + index * step;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 GetSnappedPosition(Vector2 original)
|
||||
{
|
||||
Vector2 relativeToStart = original - StartPosition;
|
||||
Vector2 offset = Vector2.Divide(relativeToStart, Spacing);
|
||||
Vector2 roundedOffset = new Vector2(MathF.Round(offset.X), MathF.Round(offset.Y));
|
||||
|
||||
return StartPosition + Vector2.Multiply(roundedOffset, Spacing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,8 +33,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public KeyCounterDisplay()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
InternalChild = KeyFlow = new FillFlowContainer<KeyCounter>
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
@@ -42,6 +40,15 @@ namespace osu.Game.Screens.Play
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
// Don't use autosize as it will shrink to zero when KeyFlow is hidden.
|
||||
// In turn this can cause the display to be masked off screen and never become visible again.
|
||||
Size = KeyFlow.Size;
|
||||
}
|
||||
|
||||
public override void Add(KeyCounter key)
|
||||
{
|
||||
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.5.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.920.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.924.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.918.0" />
|
||||
<PackageReference Include="Sentry" Version="3.9.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.29.0" />
|
||||
|
||||
+2
-2
@@ -70,7 +70,7 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.920.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.924.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.918.0" />
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||
@@ -93,7 +93,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.920.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.924.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.28.3" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
||||
Reference in New Issue
Block a user