mirror of
https://github.com/ppy/osu.git
synced 2025-01-26 23:43:03 +08:00
Merge https://github.com/ppy/osu into medal-splash
This commit is contained in:
commit
e09625e49d
@ -20,4 +20,4 @@ build:
|
||||
verbosity: minimal
|
||||
after_build:
|
||||
- cmd: inspectcode /o="inspectcodereport.xml" /caches-home="inspectcode" osu.sln
|
||||
- cmd: NVika parsereport "inspectcodereport.xml"
|
||||
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
|
@ -1 +1 @@
|
||||
Subproject commit e5f0cf73c1e0bbcbd04194bf175d73af47fc850a
|
||||
Subproject commit 97ff3376d1bdac3703d442e62f5ee6a36eb3b73f
|
@ -1 +1 @@
|
||||
Subproject commit 37e9e96d011ba7faeb4a302e65fb7bdb4dc16690
|
||||
Subproject commit 10fda22522ffadbdbc43fa0f3683a065e536f7d1
|
@ -31,6 +31,10 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="SharpCompress" publicKeyToken="afb0a02973931d96" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-0.17.1.0" newVersion="0.17.1.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
@ -49,10 +49,6 @@
|
||||
<HintPath>$(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.PatchApi.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
@ -73,15 +69,19 @@
|
||||
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\NuGet.Squirrel.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\NuGet.Squirrel.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SharpCompress, Version=0.17.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Squirrel, Version=1.5.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\Squirrel.dll</HintPath>
|
||||
<Reference Include="Squirrel, Version=1.7.5.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\Squirrel.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
|
@ -7,7 +7,8 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
|
||||
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net452" />
|
||||
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net452" />
|
||||
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net452" />
|
||||
<package id="NuGet.CommandLine" version="3.5.0" targetFramework="net452" developmentDependency="true" />
|
||||
<package id="NuGet.CommandLine" version="4.1.0" targetFramework="net452" developmentDependency="true" />
|
||||
<package id="SharpCompress" version="0.17.1" targetFramework="net452" />
|
||||
<package id="Splat" version="2.0.0" targetFramework="net452" />
|
||||
<package id="squirrel.windows" version="1.5.2" targetFramework="net452" />
|
||||
<package id="squirrel.windows" version="1.7.5" targetFramework="net452" />
|
||||
</packages>
|
11
osu.Desktop.Tests/app.config
Normal file
11
osu.Desktop.Tests/app.config
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
@ -37,8 +37,9 @@
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.6.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.6.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Reference Include="nunit.framework, Version=3.7.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
@ -100,6 +101,7 @@
|
||||
<None Include="..\osu.licenseheader">
|
||||
<Link>osu.licenseheader</Link>
|
||||
</None>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -5,7 +5,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
|
||||
-->
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net45" />
|
||||
<package id="NUnit" version="3.6.1" targetFramework="net45" />
|
||||
<package id="NUnit" version="3.7.1" targetFramework="net45" />
|
||||
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" />
|
||||
<package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" />
|
||||
<package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" />
|
||||
|
@ -41,7 +41,7 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
StarDifficulty = 5.3f,
|
||||
Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = Enumerable.Range(0,10),
|
||||
Ratings = Enumerable.Range(0, 10),
|
||||
Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6),
|
||||
Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6),
|
||||
},
|
||||
|
125
osu.Desktop.VisualTests/Tests/TestCaseContextMenu.cs
Normal file
125
osu.Desktop.VisualTests/Tests/TestCaseContextMenu.cs
Normal file
@ -0,0 +1,125 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
internal class TestCaseContextMenu : TestCase
|
||||
{
|
||||
public override string Description => @"Menu visible on right click";
|
||||
|
||||
private const int start_time = 0;
|
||||
private const int duration = 1000;
|
||||
|
||||
private MyContextMenuContainer container;
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
Add(container = new MyContextMenuContainer
|
||||
{
|
||||
Size = new Vector2(200),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Green,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Add(new AnotherContextMenuContainer
|
||||
{
|
||||
Size = new Vector2(200),
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Red,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
container.Transforms.Add(new TransformPosition
|
||||
{
|
||||
StartValue = Vector2.Zero,
|
||||
EndValue = new Vector2(0, 100),
|
||||
StartTime = start_time,
|
||||
EndTime = start_time + duration,
|
||||
LoopCount = -1,
|
||||
LoopDelay = duration * 3
|
||||
});
|
||||
container.Transforms.Add(new TransformPosition
|
||||
{
|
||||
StartValue = new Vector2(0, 100),
|
||||
EndValue = new Vector2(100, 100),
|
||||
StartTime = start_time + duration,
|
||||
EndTime = start_time + duration * 2,
|
||||
LoopCount = -1,
|
||||
LoopDelay = duration * 3
|
||||
});
|
||||
container.Transforms.Add(new TransformPosition
|
||||
{
|
||||
StartValue = new Vector2(100, 100),
|
||||
EndValue = new Vector2(100, 0),
|
||||
StartTime = start_time + duration * 2,
|
||||
EndTime = start_time + duration * 3,
|
||||
LoopCount = -1,
|
||||
LoopDelay = duration * 3
|
||||
});
|
||||
container.Transforms.Add(new TransformPosition
|
||||
{
|
||||
StartValue = new Vector2(100, 0),
|
||||
EndValue = Vector2.Zero,
|
||||
StartTime = start_time + duration * 3,
|
||||
EndTime = start_time + duration * 4,
|
||||
LoopCount = -1,
|
||||
LoopDelay = duration * 3
|
||||
});
|
||||
}
|
||||
|
||||
private class MyContextMenuContainer : Container, IHasContextMenu
|
||||
{
|
||||
public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[]
|
||||
{
|
||||
new OsuContextMenuItem(@"Some option"),
|
||||
new OsuContextMenuItem(@"Highlighted option", MenuItemType.Highlighted),
|
||||
new OsuContextMenuItem(@"Another option"),
|
||||
new OsuContextMenuItem(@"Choose me please"),
|
||||
new OsuContextMenuItem(@"And me too"),
|
||||
new OsuContextMenuItem(@"Trying to fill"),
|
||||
new OsuContextMenuItem(@"Destructive option", MenuItemType.Destructive),
|
||||
};
|
||||
}
|
||||
|
||||
private class AnotherContextMenuContainer : Container, IHasContextMenu
|
||||
{
|
||||
public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[]
|
||||
{
|
||||
new OsuContextMenuItem(@"Simple option"),
|
||||
new OsuContextMenuItem(@"Simple very very long option"),
|
||||
new OsuContextMenuItem(@"Change width", MenuItemType.Highlighted) { Action = () => ResizeWidthTo(Width * 2, 100, EasingTypes.OutQuint) },
|
||||
new OsuContextMenuItem(@"Change height", MenuItemType.Highlighted) { Action = () => ResizeHeightTo(Height * 2, 100, EasingTypes.OutQuint) },
|
||||
new OsuContextMenuItem(@"Change width back", MenuItemType.Destructive) { Action = () => ResizeWidthTo(Width / 2, 100, EasingTypes.OutQuint) },
|
||||
new OsuContextMenuItem(@"Change height back", MenuItemType.Destructive) { Action = () => ResizeHeightTo(Height / 2, 100, EasingTypes.OutQuint) },
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -36,14 +36,28 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
});
|
||||
|
||||
first.Room.Name.Value = @"Great Room Right Here";
|
||||
first.Room.Host.Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" }};
|
||||
first.Room.Host.Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" } };
|
||||
first.Room.Status.Value = new RoomStatusOpen();
|
||||
first.Room.Beatmap.Value = new BeatmapMetadata { Title = @"Seiryu", Artist = @"Critical Crystal" };
|
||||
first.Room.Beatmap.Value = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata
|
||||
{
|
||||
Title = @"Seiryu",
|
||||
Artist = @"Critical Crystal",
|
||||
},
|
||||
};
|
||||
|
||||
second.Room.Name.Value = @"Relax It's The Weekend";
|
||||
second.Room.Host.Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" }};
|
||||
second.Room.Host.Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" } };
|
||||
second.Room.Status.Value = new RoomStatusPlaying();
|
||||
second.Room.Beatmap.Value = new BeatmapMetadata { Title = @"ZAQ", Artist = @"Serendipity" };
|
||||
second.Room.Beatmap.Value = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata
|
||||
{
|
||||
Title = @"Serendipity",
|
||||
Artist = @"ZAQ",
|
||||
},
|
||||
};
|
||||
|
||||
AddStep(@"change state", () =>
|
||||
{
|
||||
|
@ -30,7 +30,7 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
},
|
||||
};
|
||||
|
||||
AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1,10).Select(i => (float)i));
|
||||
AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Select(i => (float)i));
|
||||
AddStep("values from 1-100", () => graph.Values = Enumerable.Range(1, 100).Select(i => (float)i));
|
||||
AddStep("reversed values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Reverse().Select(i => (float)i));
|
||||
AddStep("Bottom to top", () => graph.Direction = BarDirection.BottomToTop);
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
@ -54,7 +55,7 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteText { Text = "FadeTime" },
|
||||
sliderBar =new TestSliderBar<int>
|
||||
sliderBar = new TestSliderBar<int>
|
||||
{
|
||||
Width = 150,
|
||||
Height = 10,
|
||||
|
@ -40,7 +40,7 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
Name = "Timing section",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativeCoordinateSpace = new Vector2(1, 10000),
|
||||
RelativeChildSize = new Vector2(1, 10000),
|
||||
Children = new[]
|
||||
{
|
||||
new DrawableNote(new Note { StartTime = 5000 }) { AccentColour = Color4.Red },
|
||||
@ -62,7 +62,7 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
Name = "Timing section",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativeCoordinateSpace = new Vector2(1, 10000),
|
||||
RelativeChildSize = new Vector2(1, 10000),
|
||||
Children = new[]
|
||||
{
|
||||
new DrawableHoldNote(new HoldNote
|
||||
|
@ -6,14 +6,16 @@ using osu.Framework.Testing;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Timing;
|
||||
using osu.Framework.Configuration;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Mania.Timing;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
@ -30,7 +32,7 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
Action<int, SpecialColumnPosition> createPlayfield = (cols, pos) =>
|
||||
{
|
||||
Clear();
|
||||
Add(new ManiaPlayfield(cols, new List<TimingChange>())
|
||||
Add(new ManiaPlayfield(cols)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -39,37 +41,22 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
});
|
||||
};
|
||||
|
||||
Action<int, SpecialColumnPosition> createPlayfieldWithNotes = (cols, pos) =>
|
||||
const double start_time = 500;
|
||||
const double duration = 500;
|
||||
|
||||
Func<double, bool, SpeedAdjustmentContainer> createTimingChange = (time, gravity) => new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(time)
|
||||
{
|
||||
TimingPoint = { BeatLength = 1000 }
|
||||
}, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic);
|
||||
|
||||
Action<bool> createPlayfieldWithNotes = gravity =>
|
||||
{
|
||||
Clear();
|
||||
|
||||
ManiaPlayfield playField;
|
||||
Add(playField = new ManiaPlayfield(cols, new List<TimingChange> { new TimingChange { BeatLength = 200 } })
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
SpecialColumnPosition = pos,
|
||||
Scale = new Vector2(1, -1)
|
||||
});
|
||||
|
||||
for (int i = 0; i < cols; i++)
|
||||
{
|
||||
playField.Add(new DrawableNote(new Note
|
||||
{
|
||||
StartTime = Time.Current + 1000,
|
||||
Column = i
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
Action createPlayfieldWithNotesAcceptingInput = () =>
|
||||
{
|
||||
Clear();
|
||||
|
||||
var rateAdjustClock = new StopwatchClock(true) { Rate = 0.5 };
|
||||
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
|
||||
|
||||
ManiaPlayfield playField;
|
||||
Add(playField = new ManiaPlayfield(4, new List<TimingChange> { new TimingChange { BeatLength = 200 } })
|
||||
Add(playField = new ManiaPlayfield(4)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -77,14 +64,23 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
Clock = new FramedClock(rateAdjustClock)
|
||||
});
|
||||
|
||||
for (int t = 1000; t <= 2000; t += 100)
|
||||
if (!gravity)
|
||||
playField.Columns.ForEach(c => c.Add(createTimingChange(0, false)));
|
||||
|
||||
for (double t = start_time; t <= start_time + duration; t += 100)
|
||||
{
|
||||
if (gravity)
|
||||
playField.Columns.ElementAt(0).Add(createTimingChange(t, true));
|
||||
|
||||
playField.Add(new DrawableNote(new Note
|
||||
{
|
||||
StartTime = t,
|
||||
Column = 0
|
||||
}, new Bindable<Key>(Key.D)));
|
||||
|
||||
if (gravity)
|
||||
playField.Columns.ElementAt(3).Add(createTimingChange(t, true));
|
||||
|
||||
playField.Add(new DrawableNote(new Note
|
||||
{
|
||||
StartTime = t,
|
||||
@ -92,17 +88,23 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
}, new Bindable<Key>(Key.K)));
|
||||
}
|
||||
|
||||
playField.Add(new DrawableHoldNote(new HoldNote
|
||||
{
|
||||
StartTime = 1000,
|
||||
Duration = 1000,
|
||||
Column = 1
|
||||
}, new Bindable<Key>(Key.F)));
|
||||
if (gravity)
|
||||
playField.Columns.ElementAt(1).Add(createTimingChange(start_time, true));
|
||||
|
||||
playField.Add(new DrawableHoldNote(new HoldNote
|
||||
{
|
||||
StartTime = 1000,
|
||||
Duration = 1000,
|
||||
StartTime = start_time,
|
||||
Duration = duration,
|
||||
Column = 1
|
||||
}, new Bindable<Key>(Key.F)));
|
||||
|
||||
if (gravity)
|
||||
playField.Columns.ElementAt(2).Add(createTimingChange(start_time, true));
|
||||
|
||||
playField.Add(new DrawableHoldNote(new HoldNote
|
||||
{
|
||||
StartTime = start_time,
|
||||
Duration = duration,
|
||||
Column = 2
|
||||
}, new Bindable<Key>(Key.J)));
|
||||
};
|
||||
@ -116,16 +118,11 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
AddStep("Left special style", () => createPlayfield(8, SpecialColumnPosition.Left));
|
||||
AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right));
|
||||
|
||||
AddStep("Normal special style", () => createPlayfield(4, SpecialColumnPosition.Normal));
|
||||
AddStep("Notes with input", () => createPlayfieldWithNotes(false));
|
||||
AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction));
|
||||
|
||||
AddStep("Notes", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Normal));
|
||||
AddWaitStep(10);
|
||||
AddStep("Left special style", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Left));
|
||||
AddWaitStep(10);
|
||||
AddStep("Right special style", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Right));
|
||||
AddWaitStep(10);
|
||||
|
||||
AddStep("Notes with input", () => createPlayfieldWithNotesAcceptingInput());
|
||||
AddStep("Notes with gravity", () => createPlayfieldWithNotes(true));
|
||||
AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction));
|
||||
}
|
||||
|
||||
private void triggerKeyDown(Column column)
|
||||
|
@ -3,9 +3,9 @@
|
||||
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Screens.Menu;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
|
@ -35,7 +35,7 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
});
|
||||
|
||||
AddStep(@"Pause", delegate {
|
||||
if(failOverlay.State == Visibility.Visible)
|
||||
if (failOverlay.State == Visibility.Visible)
|
||||
{
|
||||
failOverlay.Hide();
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@ -15,6 +14,7 @@ using osu.Game.Screens.Play;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Desktop.VisualTests.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
|
@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.ReplaySettings;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
internal class TestCaseReplaySettingsOverlay : TestCase
|
||||
{
|
||||
public override string Description => @"Settings visible in replay/auto";
|
||||
|
||||
private ExampleContainer container;
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
Add(new ReplaySettingsOverlay()
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
});
|
||||
|
||||
Add(container = new ExampleContainer());
|
||||
|
||||
AddStep(@"Add button", () => container.Add(new OsuButton
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Text = @"Button",
|
||||
}));
|
||||
|
||||
AddStep(@"Add checkbox", () => container.Add(new ReplayCheckbox
|
||||
{
|
||||
LabelText = "Checkbox",
|
||||
}));
|
||||
|
||||
AddStep(@"Add textbox", () => container.Add(new FocusedTextBox
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 30,
|
||||
PlaceholderText = "Textbox",
|
||||
HoldFocus = false,
|
||||
}));
|
||||
}
|
||||
|
||||
private class ExampleContainer : ReplayGroup
|
||||
{
|
||||
protected override string Title => @"example";
|
||||
}
|
||||
}
|
||||
}
|
131
osu.Desktop.VisualTests/Tests/TestCaseRoomInspector.cs
Normal file
131
osu.Desktop.VisualTests/Tests/TestCaseRoomInspector.cs
Normal file
@ -0,0 +1,131 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Screens.Multiplayer;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Users;
|
||||
using osu.Framework.Allocation;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
internal class TestCaseRoomInspector : TestCase
|
||||
{
|
||||
public override string Description => @"from the multiplayer lobby";
|
||||
|
||||
private RulesetDatabase rulesets;
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
var room = new Room
|
||||
{
|
||||
Name = { Value = @"My Awesome Room" },
|
||||
Host = { Value = new User { Username = @"flyte", Id = 3103765, Country = new Country { FlagName = @"JP" } } },
|
||||
Status = { Value = new RoomStatusOpen() },
|
||||
Type = { Value = new GameTypeTeamVersus() },
|
||||
Beatmap =
|
||||
{
|
||||
Value = new BeatmapInfo
|
||||
{
|
||||
StarDifficulty = 3.7,
|
||||
Ruleset = rulesets.GetRuleset(3),
|
||||
Metadata = new BeatmapMetadata
|
||||
{
|
||||
Title = @"Platina",
|
||||
Artist = @"Maaya Sakamoto",
|
||||
Author = @"uwutm8",
|
||||
},
|
||||
OnlineInfo = new BeatmapOnlineInfo
|
||||
{
|
||||
Covers = new[] { @"https://assets.ppy.sh//beatmaps/560573/covers/cover.jpg?1492722343" },
|
||||
},
|
||||
}
|
||||
},
|
||||
MaxParticipants = { Value = 200 },
|
||||
Participants =
|
||||
{
|
||||
Value = new[]
|
||||
{
|
||||
new User { Username = @"flyte", Id = 3103765, GlobalRank = 1425 },
|
||||
new User { Username = @"Cookiezi", Id = 124493, GlobalRank = 5466 },
|
||||
new User { Username = @"Angelsim", Id = 1777162, GlobalRank = 2873 },
|
||||
new User { Username = @"Rafis", Id = 2558286, GlobalRank = 4687 },
|
||||
new User { Username = @"hvick225", Id = 50265, GlobalRank = 3258 },
|
||||
new User { Username = @"peppy", Id = 2, GlobalRank = 6251 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
RoomInspector inspector;
|
||||
Add(inspector = new RoomInspector
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Room = room,
|
||||
});
|
||||
|
||||
AddStep(@"change title", () => room.Name.Value = @"A Better Room Than The Above");
|
||||
AddStep(@"change host", () => room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } });
|
||||
AddStep(@"change status", () => room.Status.Value = new RoomStatusPlaying());
|
||||
AddStep(@"change type", () => room.Type.Value = new GameTypeTag());
|
||||
AddStep(@"change beatmap", () => room.Beatmap.Value = null);
|
||||
AddStep(@"change max participants", () => room.MaxParticipants.Value = null);
|
||||
AddStep(@"change participants", () => room.Participants.Value = new[]
|
||||
{
|
||||
new User { Username = @"filsdelama", Id = 2831793, GlobalRank = 8542 },
|
||||
new User { Username = @"_index", Id = 652457, GlobalRank = 15024 }
|
||||
});
|
||||
|
||||
AddStep(@"change room", () =>
|
||||
{
|
||||
var newRoom = new Room
|
||||
{
|
||||
Name = { Value = @"My New, Better Than Ever Room" },
|
||||
Host = { Value = new User { Username = @"Angelsim", Id = 1777162, Country = new Country { FlagName = @"KR" } } },
|
||||
Status = { Value = new RoomStatusOpen() },
|
||||
Type = { Value = new GameTypeTagTeam() },
|
||||
Beatmap =
|
||||
{
|
||||
Value = new BeatmapInfo
|
||||
{
|
||||
StarDifficulty = 7.07,
|
||||
Ruleset = rulesets.GetRuleset(0),
|
||||
Metadata = new BeatmapMetadata
|
||||
{
|
||||
Title = @"xi",
|
||||
Artist = @"FREEDOM DIVE",
|
||||
Author = @"Nakagawa-Kanon",
|
||||
},
|
||||
OnlineInfo = new BeatmapOnlineInfo
|
||||
{
|
||||
Covers = new[] { @"https://assets.ppy.sh//beatmaps/39804/covers/cover.jpg?1456506845" },
|
||||
},
|
||||
}
|
||||
},
|
||||
MaxParticipants = { Value = 10 },
|
||||
Participants =
|
||||
{
|
||||
Value = new[]
|
||||
{
|
||||
new User { Username = @"Angelsim", Id = 1777162, GlobalRank = 4 },
|
||||
new User { Username = @"HappyStick", Id = 256802, GlobalRank = 752 },
|
||||
new User { Username = @"-Konpaku-", Id = 2258797, GlobalRank = 571 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inspector.Room = newRoom;
|
||||
});
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetDatabase rulesets)
|
||||
{
|
||||
this.rulesets = rulesets;
|
||||
}
|
||||
}
|
||||
}
|
@ -79,7 +79,8 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
score.Current.Value += 300 + (ulong)(300.0 * (comboCounter.Current > 0 ? comboCounter.Current - 1 : 0) / 25.0);
|
||||
comboCounter.Increment();
|
||||
numerator++; denominator++;
|
||||
numerator++;
|
||||
denominator++;
|
||||
accuracyCounter.SetFraction(numerator, denominator);
|
||||
});
|
||||
|
||||
|
211
osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs
Normal file
211
osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs
Normal file
@ -0,0 +1,211 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
public class TestCaseScrollingHitObjects : TestCase
|
||||
{
|
||||
public override string Description => "SpeedAdjustmentContainer/DrawableTimingSection";
|
||||
|
||||
private SpeedAdjustmentCollection adjustmentCollection;
|
||||
|
||||
private BindableDouble timeRangeBindable;
|
||||
private OsuSpriteText timeRangeText;
|
||||
private OsuSpriteText bottomLabel;
|
||||
private SpriteText topTime, bottomTime;
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
timeRangeBindable = new BindableDouble(2000)
|
||||
{
|
||||
MinValue = 200,
|
||||
MaxValue = 4000,
|
||||
};
|
||||
|
||||
SliderBar<double> timeRange;
|
||||
Add(timeRange = new BasicSliderBar<double>
|
||||
{
|
||||
Size = new Vector2(200, 20),
|
||||
SelectionColor = Color4.Pink,
|
||||
KeyboardStep = 100
|
||||
});
|
||||
|
||||
Add(timeRangeText = new OsuSpriteText
|
||||
{
|
||||
X = 210,
|
||||
TextSize = 16,
|
||||
});
|
||||
|
||||
timeRange.Current.BindTo(timeRangeBindable);
|
||||
timeRangeBindable.ValueChanged += v => timeRangeText.Text = $"Visible Range: {v:#,#.#}";
|
||||
timeRangeBindable.ValueChanged += v => bottomLabel.Text = $"t minus {v:#,#}";
|
||||
|
||||
Add(new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(100, 500),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.25f
|
||||
},
|
||||
adjustmentCollection = new SpeedAdjustmentCollection(Axes.Y)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
VisibleTimeRange = timeRangeBindable,
|
||||
Masking = true,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "t minus 0",
|
||||
Margin = new MarginPadding(2),
|
||||
TextSize = 14,
|
||||
Anchor = Anchor.TopRight,
|
||||
},
|
||||
bottomLabel = new OsuSpriteText
|
||||
{
|
||||
Text = "t minus x",
|
||||
Margin = new MarginPadding(2),
|
||||
TextSize = 14,
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomLeft,
|
||||
},
|
||||
topTime = new OsuSpriteText
|
||||
{
|
||||
Margin = new MarginPadding(2),
|
||||
TextSize = 14,
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopRight,
|
||||
},
|
||||
bottomTime = new OsuSpriteText
|
||||
{
|
||||
Margin = new MarginPadding(2),
|
||||
TextSize = 14,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomRight,
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
timeRangeBindable.TriggerChange();
|
||||
|
||||
adjustmentCollection.Add(new TestSpeedAdjustmentContainer(new MultiplierControlPoint()));
|
||||
|
||||
AddStep("Add hit object", () => adjustmentCollection.Add(new TestDrawableHitObject(new HitObject { StartTime = Time.Current + 2000 })));
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
topTime.Text = Time.Current.ToString("#,#");
|
||||
bottomTime.Text = (Time.Current + timeRangeBindable.Value).ToString("#,#");
|
||||
}
|
||||
|
||||
private class TestSpeedAdjustmentContainer : SpeedAdjustmentContainer
|
||||
{
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
|
||||
public TestSpeedAdjustmentContainer(MultiplierControlPoint controlPoint)
|
||||
: base(controlPoint)
|
||||
{
|
||||
}
|
||||
|
||||
protected override DrawableTimingSection CreateTimingSection() => new TestDrawableTimingSection(ControlPoint);
|
||||
|
||||
private class TestDrawableTimingSection : DrawableTimingSection
|
||||
{
|
||||
private readonly MultiplierControlPoint controlPoint;
|
||||
|
||||
public TestDrawableTimingSection(MultiplierControlPoint controlPoint)
|
||||
{
|
||||
this.controlPoint = controlPoint;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Y = (float)(controlPoint.StartTime - Time.Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TestDrawableHitObject : DrawableHitObject, IScrollingHitObject
|
||||
{
|
||||
private readonly Box background;
|
||||
private const float height = 14;
|
||||
|
||||
public BindableDouble LifetimeOffset { get; } = new BindableDouble();
|
||||
|
||||
public TestDrawableHitObject(HitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
AutoSizeAxes = Axes.Y;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
RelativePositionAxes = Axes.Y;
|
||||
|
||||
Y = (float)hitObject.StartTime;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = height,
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Colour = Color4.Cyan,
|
||||
Height = 1,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = Color4.Black,
|
||||
TextSize = height,
|
||||
Font = @"Exo2.0-BoldItalic",
|
||||
Text = $"{hitObject.StartTime:#,#}"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
FadeInFromZero(250, EasingTypes.OutQuint);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
if (Time.Current >= HitObject.StartTime)
|
||||
background.Colour = Color4.Red;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
osu.Desktop.VisualTests/Tests/TestCaseSkipButton.cs
Normal file
19
osu.Desktop.VisualTests/Tests/TestCaseSkipButton.cs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
internal class TestCaseSkipButton : TestCase
|
||||
{
|
||||
public override string Description => @"Skip skip skippediskip";
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
Add(new SkipButton(Clock.CurrentTime + 5000));
|
||||
}
|
||||
}
|
||||
}
|
85
osu.Desktop.VisualTests/Tests/TestCaseSocial.cs
Normal file
85
osu.Desktop.VisualTests/Tests/TestCaseSocial.cs
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
public class TestCaseSocial : TestCase
|
||||
{
|
||||
public override string Description => @"social browser overlay";
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
SocialOverlay s = new SocialOverlay
|
||||
{
|
||||
Users = new[]
|
||||
{
|
||||
new User
|
||||
{
|
||||
Username = @"flyte",
|
||||
Id = 3103765,
|
||||
Country = new Country { FlagName = @"JP" },
|
||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg",
|
||||
},
|
||||
new User
|
||||
{
|
||||
Username = @"Cookiezi",
|
||||
Id = 124493,
|
||||
Country = new Country { FlagName = @"KR" },
|
||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg",
|
||||
},
|
||||
new User
|
||||
{
|
||||
Username = @"Angelsim",
|
||||
Id = 1777162,
|
||||
Country = new Country { FlagName = @"KR" },
|
||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||
},
|
||||
new User
|
||||
{
|
||||
Username = @"Rafis",
|
||||
Id = 2558286,
|
||||
Country = new Country { FlagName = @"PL" },
|
||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c4.jpg",
|
||||
},
|
||||
new User
|
||||
{
|
||||
Username = @"hvick225",
|
||||
Id = 50265,
|
||||
Country = new Country { FlagName = @"TW" },
|
||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c5.jpg",
|
||||
},
|
||||
new User
|
||||
{
|
||||
Username = @"peppy",
|
||||
Id = 2,
|
||||
Country = new Country { FlagName = @"AU" },
|
||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
|
||||
},
|
||||
new User
|
||||
{
|
||||
Username = @"filsdelama",
|
||||
Id = 2831793,
|
||||
Country = new Country { FlagName = @"FR" },
|
||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c7.jpg"
|
||||
},
|
||||
new User
|
||||
{
|
||||
Username = @"_index",
|
||||
Id = 652457,
|
||||
Country = new Country { FlagName = @"RU" },
|
||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c8.jpg"
|
||||
},
|
||||
},
|
||||
};
|
||||
Add(s);
|
||||
|
||||
AddStep(@"toggle", s.ToggleVisibility);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Framework.Configuration;
|
||||
using OpenTK;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
internal class TestCaseTooltip : TestCase
|
||||
{
|
||||
public override string Description => "tests tooltips on various elements";
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
OsuSliderBar<int> slider;
|
||||
OsuSliderBar<double> sliderDouble;
|
||||
|
||||
const float width = 400;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new TooltipTextContainer("text with a tooltip"),
|
||||
new TooltipTextContainer("more text with another tooltip"),
|
||||
new TooltipTextbox
|
||||
{
|
||||
Text = "a textbox with a tooltip",
|
||||
Size = new Vector2(width,30),
|
||||
},
|
||||
slider = new OsuSliderBar<int>
|
||||
{
|
||||
Width = width,
|
||||
},
|
||||
sliderDouble = new OsuSliderBar<double>
|
||||
{
|
||||
Width = width,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
slider.Current.BindTo(new BindableInt(5)
|
||||
{
|
||||
MaxValue = 10,
|
||||
MinValue = 0
|
||||
});
|
||||
|
||||
sliderDouble.Current.BindTo(new BindableDouble(0.5)
|
||||
{
|
||||
MaxValue = 1,
|
||||
MinValue = 0
|
||||
});
|
||||
}
|
||||
|
||||
private class TooltipTextContainer : Container, IHasTooltip
|
||||
{
|
||||
private readonly OsuSpriteText text;
|
||||
|
||||
public string TooltipText => text.Text;
|
||||
|
||||
public TooltipTextContainer(string tooltipText)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Children = new[]
|
||||
{
|
||||
text = new OsuSpriteText
|
||||
{
|
||||
Text = tooltipText,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private class TooltipTextbox : OsuTextBox, IHasTooltip
|
||||
{
|
||||
public string TooltipText => Text;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,20 +3,18 @@
|
||||
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
internal class TestCaseTwoLayerButton : TestCase
|
||||
{
|
||||
public override string Description => @"Back and skip and what not";
|
||||
public override string Description => @"Mostly back button";
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
Add(new BackButton());
|
||||
Add(new SkipButton(Clock.CurrentTime + 5000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,11 +87,13 @@
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SharpCompress, Version=0.15.2.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SharpCompress.0.15.2\lib\net45\SharpCompress.dll</HintPath>
|
||||
<Reference Include="SharpCompress, Version=0.17.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
@ -187,6 +189,7 @@
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Tests\TestCaseChatDisplay.cs" />
|
||||
<Compile Include="Tests\TestCaseBeatmapDetails.cs" />
|
||||
<Compile Include="Tests\TestCaseContextMenu.cs" />
|
||||
<Compile Include="Tests\TestCaseDrawings.cs" />
|
||||
<Compile Include="Tests\TestCaseGamefield.cs" />
|
||||
<Compile Include="Tests\TestCaseGraph.cs" />
|
||||
@ -196,6 +199,7 @@
|
||||
<Compile Include="Tests\TestCaseMusicController.cs" />
|
||||
<Compile Include="Tests\TestCaseNotificationManager.cs" />
|
||||
<Compile Include="Tests\TestCaseOnScreenDisplay.cs" />
|
||||
<Compile Include="Tests\TestCaseReplaySettingsOverlay.cs" />
|
||||
<Compile Include="Tests\TestCasePlayer.cs" />
|
||||
<Compile Include="Tests\TestCaseHitObjects.cs" />
|
||||
<Compile Include="Tests\TestCaseKeyCounter.cs" />
|
||||
@ -203,12 +207,13 @@
|
||||
<Compile Include="Tests\TestCaseReplay.cs" />
|
||||
<Compile Include="Tests\TestCaseResults.cs" />
|
||||
<Compile Include="Tests\TestCaseScoreCounter.cs" />
|
||||
<Compile Include="Tests\TestCaseScrollingHitObjects.cs" />
|
||||
<Compile Include="Tests\TestCaseSkipButton.cs" />
|
||||
<Compile Include="Tests\TestCaseTabControl.cs" />
|
||||
<Compile Include="Tests\TestCaseTaikoHitObjects.cs" />
|
||||
<Compile Include="Tests\TestCaseTaikoPlayfield.cs" />
|
||||
<Compile Include="Tests\TestCaseTextAwesome.cs" />
|
||||
<Compile Include="Tests\TestCasePlaySongSelect.cs" />
|
||||
<Compile Include="Tests\TestCaseTooltip.cs" />
|
||||
<Compile Include="Tests\TestCaseTwoLayerButton.cs" />
|
||||
<Compile Include="VisualTestGame.cs" />
|
||||
<Compile Include="Platform\TestStorage.cs" />
|
||||
@ -223,8 +228,10 @@
|
||||
<Compile Include="Tests\TestCaseDrawableRoom.cs" />
|
||||
<Compile Include="Tests\TestCaseUserPanel.cs" />
|
||||
<Compile Include="Tests\TestCaseDirect.cs" />
|
||||
<Compile Include="Tests\TestCaseSocial.cs" />
|
||||
<Compile Include="Tests\TestCaseBreadcrumbs.cs" />
|
||||
<Compile Include="Tests\TestCaseMedalOverlay.cs" />
|
||||
<Compile Include="Tests\TestCaseRoomInspector.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup />
|
||||
|
@ -5,8 +5,8 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
|
||||
-->
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net45" />
|
||||
<package id="ppy.OpenTK" version="2.0.50727.1341" targetFramework="net45" />
|
||||
<package id="SharpCompress" version="0.15.2" targetFramework="net45" />
|
||||
<package id="ppy.OpenTK" version="3.0" targetFramework="net45" />
|
||||
<package id="SharpCompress" version="0.17.1" targetFramework="net45" />
|
||||
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" />
|
||||
<package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" />
|
||||
<package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" />
|
||||
|
@ -10,6 +10,7 @@ using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using Squirrel;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Graphics;
|
||||
|
@ -121,18 +121,23 @@
|
||||
</Reference>
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\NuGet.Squirrel.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\NuGet.Squirrel.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SharpCompress, Version=0.17.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Squirrel, Version=1.5.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\Squirrel.dll</HintPath>
|
||||
<Reference Include="Squirrel, Version=1.7.5.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\Squirrel.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
|
@ -7,7 +7,8 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
|
||||
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" />
|
||||
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
|
||||
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" />
|
||||
<package id="ppy.OpenTK" version="2.0.50727.1341" targetFramework="net45" />
|
||||
<package id="ppy.OpenTK" version="3.0" targetFramework="net45" />
|
||||
<package id="SharpCompress" version="0.17.1" targetFramework="net45" />
|
||||
<package id="Splat" version="2.0.0" targetFramework="net45" />
|
||||
<package id="squirrel.windows" version="1.5.2" targetFramework="net45" />
|
||||
<package id="squirrel.windows" version="1.7.5" targetFramework="net45" />
|
||||
</packages>
|
@ -28,7 +28,14 @@ namespace osu.Game.Rulesets.Catch
|
||||
{
|
||||
new CatchModEasy(),
|
||||
new CatchModNoFail(),
|
||||
new CatchModHalfTime(),
|
||||
new MultiMod
|
||||
{
|
||||
Mods = new Mod[]
|
||||
{
|
||||
new CatchModHalfTime(),
|
||||
new CatchModDaycore(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
case ModType.DifficultyIncrease:
|
||||
|
@ -32,6 +32,11 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
|
||||
}
|
||||
|
||||
public class CatchModDaycore : ModDaycore
|
||||
{
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
}
|
||||
|
||||
public class CatchModDoubleTime : ModDoubleTime
|
||||
{
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using OpenTK;
|
||||
|
@ -33,8 +33,9 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
|
@ -5,5 +5,5 @@ Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
-->
|
||||
<packages>
|
||||
<package id="ppy.OpenTK" version="2.0.50727.1341" targetFramework="net45" />
|
||||
<package id="ppy.OpenTK" version="3.0" targetFramework="net45" />
|
||||
</packages>
|
@ -448,7 +448,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
return curveData.RepeatSamples[index];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs and adds a note to a pattern.
|
||||
/// </summary>
|
||||
@ -480,7 +479,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
Tail = { Samples = sampleInfoListAt(endTime) }
|
||||
};
|
||||
|
||||
|
||||
newObject = holdNote;
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,14 @@ namespace osu.Game.Rulesets.Mania
|
||||
{
|
||||
new ManiaModEasy(),
|
||||
new ManiaModNoFail(),
|
||||
new ManiaModHalfTime(),
|
||||
new MultiMod
|
||||
{
|
||||
Mods = new Mod[]
|
||||
{
|
||||
new ManiaModHalfTime(),
|
||||
new ManiaModDaycore(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
case ModType.DifficultyIncrease:
|
||||
@ -89,6 +96,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
new ModCinema(),
|
||||
},
|
||||
},
|
||||
new ManiaModGravity()
|
||||
};
|
||||
|
||||
default:
|
||||
|
@ -87,6 +87,5 @@ namespace osu.Game.Rulesets.Mania.MathUtils
|
||||
bitIndex++;
|
||||
return ((bitBuffer >>= 1) & 1) == 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
23
osu.Game.Rulesets.Mania/Mods/IGenerateSpeedAdjustments.cs
Normal file
23
osu.Game.Rulesets.Mania/Mods/IGenerateSpeedAdjustments.cs
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of mod which generates speed adjustments that scroll the hit objects and bar lines.
|
||||
/// </summary>
|
||||
internal interface IGenerateSpeedAdjustments
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies this mod to a hit renderer.
|
||||
/// </summary>
|
||||
/// <param name="hitRenderer">The hit renderer to apply to.</param>
|
||||
/// <param name="hitObjectTimingChanges">The per-column list of speed adjustments for hit objects.</param>
|
||||
/// <param name="barlineTimingChanges">The list of speed adjustments for bar lines.</param>
|
||||
void ApplyToHitRenderer(ManiaHitRenderer hitRenderer, ref List<SpeedAdjustmentContainer>[] hitObjectTimingChanges, ref List<SpeedAdjustmentContainer> barlineTimingChanges);
|
||||
}
|
||||
}
|
@ -34,6 +34,11 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
|
||||
}
|
||||
|
||||
public class ManiaModDaycore : ModDaycore
|
||||
{
|
||||
public override double ScoreMultiplier => 0.3;
|
||||
}
|
||||
|
||||
public class ManiaModDoubleTime : ModDoubleTime
|
||||
{
|
||||
public override double ScoreMultiplier => 1.0;
|
||||
|
46
osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs
Normal file
46
osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Timing;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModGravity : Mod, IGenerateSpeedAdjustments
|
||||
{
|
||||
public override string Name => "Gravity";
|
||||
|
||||
public override double ScoreMultiplier => 0;
|
||||
|
||||
public override FontAwesome Icon => FontAwesome.fa_sort_desc;
|
||||
|
||||
public void ApplyToHitRenderer(ManiaHitRenderer hitRenderer, ref List<SpeedAdjustmentContainer>[] hitObjectTimingChanges, ref List<SpeedAdjustmentContainer> barlineTimingChanges)
|
||||
{
|
||||
// We have to generate one speed adjustment per hit object for gravity
|
||||
foreach (ManiaHitObject obj in hitRenderer.Objects)
|
||||
{
|
||||
MultiplierControlPoint controlPoint = hitRenderer.CreateControlPointAt(obj.StartTime);
|
||||
// Beat length has too large of an effect for gravity, so we'll force it to a constant value for now
|
||||
controlPoint.TimingPoint.BeatLength = 1000;
|
||||
|
||||
hitObjectTimingChanges[obj.Column].Add(new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Gravity));
|
||||
}
|
||||
|
||||
// Like with hit objects, we need to generate one speed adjustment per bar line
|
||||
foreach (DrawableBarLine barLine in hitRenderer.BarLines)
|
||||
{
|
||||
var controlPoint = hitRenderer.CreateControlPointAt(barLine.HitObject.StartTime);
|
||||
// Beat length has too large of an effect for gravity, so we'll force it to a constant value for now
|
||||
controlPoint.TimingPoint.BeatLength = 1000;
|
||||
|
||||
barlineTimingChanges.Add(new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Gravity));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
@ -55,7 +55,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
tickContainer = new Container<DrawableHoldNoteTick>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativeCoordinateSpace = new Vector2(1, (float)HitObject.Duration)
|
||||
RelativeChildOffset = new Vector2(0, (float)HitObject.StartTime),
|
||||
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
|
||||
},
|
||||
head = new DrawableHeadNote(this, key)
|
||||
{
|
||||
@ -76,9 +77,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
HoldStartTime = () => holdStartTime
|
||||
};
|
||||
|
||||
// To make the ticks relative to ourselves we need to offset them backwards
|
||||
drawableTick.Y -= (float)HitObject.StartTime;
|
||||
|
||||
tickContainer.Add(drawableTick);
|
||||
AddNested(drawableTick);
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
base.AccentColour = value;
|
||||
|
||||
glowContainer.EdgeEffect = new EdgeEffect
|
||||
glowContainer.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Radius = 2f,
|
||||
|
@ -10,7 +10,7 @@ using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
public abstract class DrawableManiaHitObject<TObject> : DrawableHitObject<ManiaHitObject, ManiaJudgement>
|
||||
public abstract class DrawableManiaHitObject<TObject> : DrawableScrollingHitObject<ManiaHitObject, ManiaJudgement>
|
||||
where TObject : ManiaHitObject
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
public DrawableNote(Note hitObject, Bindable<Key> key = null)
|
||||
: base(hitObject, key)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 100;
|
||||
|
||||
Add(headPiece = new NotePiece
|
||||
|
@ -4,7 +4,7 @@
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||
|
@ -5,7 +5,7 @@ using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||
|
@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="DrawableTimingSection"/> which scrolls relative to the control point start time.
|
||||
/// </summary>
|
||||
internal class BasicScrollingDrawableTimingSection : DrawableTimingSection
|
||||
{
|
||||
private readonly MultiplierControlPoint controlPoint;
|
||||
|
||||
public BasicScrollingDrawableTimingSection(MultiplierControlPoint controlPoint)
|
||||
{
|
||||
this.controlPoint = controlPoint;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Y = (float)(controlPoint.StartTime - Time.Current);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,156 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using OpenTK;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// A container in which added drawables are put into a relative coordinate space spanned by a length of time.
|
||||
/// <para>
|
||||
/// This container contains <see cref="ControlPoint"/>s which scroll inside this container.
|
||||
/// Drawables added to this container are moved inside the relevant <see cref="ControlPoint"/>,
|
||||
/// and as such, will scroll along with the <see cref="ControlPoint"/>s.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class ControlPointContainer : Container<Drawable>
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount of time which this container spans.
|
||||
/// </summary>
|
||||
public double TimeSpan { get; set; }
|
||||
|
||||
private readonly List<DrawableControlPoint> drawableControlPoints;
|
||||
|
||||
public ControlPointContainer(IEnumerable<TimingChange> timingChanges)
|
||||
{
|
||||
drawableControlPoints = timingChanges.Select(t => new DrawableControlPoint(t)).ToList();
|
||||
Children = drawableControlPoints;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a drawable to this container. Note that the drawable added must have its Y-position be
|
||||
/// an absolute unit of time that is _not_ relative to <see cref="TimeSpan"/>.
|
||||
/// </summary>
|
||||
/// <param name="drawable">The drawable to add.</param>
|
||||
public override void Add(Drawable drawable)
|
||||
{
|
||||
// Always add timing sections to ourselves
|
||||
if (drawable is DrawableControlPoint)
|
||||
{
|
||||
base.Add(drawable);
|
||||
return;
|
||||
}
|
||||
|
||||
var controlPoint = drawableControlPoints.LastOrDefault(t => t.CanContain(drawable)) ?? drawableControlPoints.FirstOrDefault();
|
||||
|
||||
if (controlPoint == null)
|
||||
throw new InvalidOperationException("Could not find suitable timing section to add object to.");
|
||||
|
||||
controlPoint.Add(drawable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A container that contains drawables within the time span of a timing section.
|
||||
/// <para>
|
||||
/// The content of this container will scroll relative to the current time.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
private class DrawableControlPoint : Container
|
||||
{
|
||||
private readonly TimingChange timingChange;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container content;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a drawable control point. The height of this container will be proportional
|
||||
/// to the beat length of the control point it is initialized with such that, e.g. a beat length
|
||||
/// of 500ms results in this container being twice as high as its parent, which further means that
|
||||
/// the content container will scroll at twice the normal rate.
|
||||
/// </summary>
|
||||
/// <param name="timingChange">The control point to create the drawable control point for.</param>
|
||||
public DrawableControlPoint(TimingChange timingChange)
|
||||
{
|
||||
this.timingChange = timingChange;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
AddInternal(content = new AutoTimeRelativeContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Y = (float)timingChange.Time
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
var parent = (ControlPointContainer)Parent;
|
||||
|
||||
// Adjust our height to account for the speed changes
|
||||
Height = (float)(1000 / timingChange.BeatLength / timingChange.SpeedMultiplier);
|
||||
RelativeCoordinateSpace = new Vector2(1, (float)parent.TimeSpan);
|
||||
|
||||
// Scroll the content
|
||||
content.Y = (float)(timingChange.Time - Time.Current);
|
||||
}
|
||||
|
||||
public override void Add(Drawable drawable)
|
||||
{
|
||||
// The previously relatively-positioned drawable will now become relative to content, but since the drawable has no knowledge of content,
|
||||
// we need to offset it back by content's position position so that it becomes correctly relatively-positioned to content
|
||||
// This can be removed if hit objects were stored such that either their StartTime or their "beat offset" was relative to the timing change
|
||||
// they belonged to, but this requires a radical change to the beatmap format which we're not ready to do just yet
|
||||
drawable.Y -= (float)timingChange.Time;
|
||||
|
||||
base.Add(drawable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this control point can contain a drawable. This control point can contain a drawable if the drawable is positioned "after" this control point.
|
||||
/// </summary>
|
||||
/// <param name="drawable">The drawable to check.</param>
|
||||
public bool CanContain(Drawable drawable) => content.Y <= drawable.Y;
|
||||
|
||||
/// <summary>
|
||||
/// A container which always keeps its height and relative coordinate space "auto-sized" to its children.
|
||||
/// <para>
|
||||
/// This is used in the case where children are relatively positioned/sized to time values (e.g. notes/bar lines) to keep
|
||||
/// such children wrapped inside a container, otherwise they would disappear due to container flattening.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
private class AutoTimeRelativeContainer : Container
|
||||
{
|
||||
protected override IComparer<Drawable> DepthComparer => new HitObjectReverseStartTimeComparer();
|
||||
|
||||
public override void InvalidateFromChild(Invalidation invalidation)
|
||||
{
|
||||
// We only want to re-compute our size when a child's size or position has changed
|
||||
if ((invalidation & Invalidation.Geometry) == 0)
|
||||
{
|
||||
base.InvalidateFromChild(invalidation);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Children.Any())
|
||||
return;
|
||||
|
||||
float height = Children.Select(child => child.Y + child.Height).Max();
|
||||
|
||||
Height = height;
|
||||
RelativeCoordinateSpace = new Vector2(1, height);
|
||||
|
||||
base.InvalidateFromChild(invalidation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="DrawableTimingSection"/> that emulates a form of gravity where hit objects speed up over time.
|
||||
/// </summary>
|
||||
internal class GravityScrollingDrawableTimingSection : DrawableTimingSection
|
||||
{
|
||||
private readonly MultiplierControlPoint controlPoint;
|
||||
|
||||
public GravityScrollingDrawableTimingSection(MultiplierControlPoint controlPoint)
|
||||
{
|
||||
this.controlPoint = controlPoint;
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
// The gravity-adjusted start position
|
||||
float startPos = (float)computeGravityTime(controlPoint.StartTime);
|
||||
// The gravity-adjusted end position
|
||||
float endPos = (float)computeGravityTime(controlPoint.StartTime + RelativeChildSize.Y);
|
||||
|
||||
Y = startPos;
|
||||
Height = endPos - startPos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies gravity to a time value based on the current time.
|
||||
/// </summary>
|
||||
/// <param name="time">The time value gravity should be applied to.</param>
|
||||
/// <returns>The time after gravity is applied to <paramref name="time"/>.</returns>
|
||||
private double computeGravityTime(double time)
|
||||
{
|
||||
double relativeTime = relativeTimeAt(time);
|
||||
|
||||
// The sign of the relative time, this is used to apply backwards acceleration leading into startTime
|
||||
double sign = relativeTime < 0 ? -1 : 1;
|
||||
|
||||
return VisibleTimeRange - acceleration * relativeTime * relativeTime * sign;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The acceleration due to "gravity" of the content of this container.
|
||||
/// </summary>
|
||||
private double acceleration => 1 / VisibleTimeRange;
|
||||
|
||||
/// <summary>
|
||||
/// Computes the current time relative to <paramref name="time"/>, accounting for <see cref="DrawableTimingSection.VisibleTimeRange"/>.
|
||||
/// </summary>
|
||||
/// <param name="time">The non-offset time.</param>
|
||||
/// <returns>The current time relative to <paramref name="time"/> - <see cref="DrawableTimingSection.VisibleTimeRange"/>. </returns>
|
||||
private double relativeTimeAt(double time) => Time.Current - time + VisibleTimeRange;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Timing
|
||||
{
|
||||
public class ManiaSpeedAdjustmentContainer : SpeedAdjustmentContainer
|
||||
{
|
||||
private readonly ScrollingAlgorithm scrollingAlgorithm;
|
||||
|
||||
public ManiaSpeedAdjustmentContainer(MultiplierControlPoint timingSection, ScrollingAlgorithm scrollingAlgorithm)
|
||||
: base(timingSection)
|
||||
{
|
||||
this.scrollingAlgorithm = scrollingAlgorithm;
|
||||
}
|
||||
|
||||
protected override DrawableTimingSection CreateTimingSection()
|
||||
{
|
||||
switch (scrollingAlgorithm)
|
||||
{
|
||||
default:
|
||||
case ScrollingAlgorithm.Basic:
|
||||
return new BasicScrollingDrawableTimingSection(ControlPoint);
|
||||
case ScrollingAlgorithm.Gravity:
|
||||
return new GravityScrollingDrawableTimingSection(ControlPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
osu.Game.Rulesets.Mania/Timing/ScrollingAlgorithm.cs
Normal file
17
osu.Game.Rulesets.Mania/Timing/ScrollingAlgorithm.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Timing
|
||||
{
|
||||
public enum ScrollingAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic scrolling algorithm based on the timing section time. This is the default algorithm.
|
||||
/// </summary>
|
||||
Basic,
|
||||
/// <summary>
|
||||
/// Emulating a form of gravity where hit objects speed up over time.
|
||||
/// </summary>
|
||||
Gravity
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Timing
|
||||
{
|
||||
public class TimingChange
|
||||
{
|
||||
/// <summary>
|
||||
/// The time at which this timing change happened.
|
||||
/// </summary>
|
||||
public double Time;
|
||||
|
||||
/// <summary>
|
||||
/// The beat length.
|
||||
/// </summary>
|
||||
public double BeatLength = 500;
|
||||
|
||||
/// <summary>
|
||||
/// The speed multiplier.
|
||||
/// </summary>
|
||||
public double SpeedMultiplier = 1;
|
||||
}
|
||||
}
|
@ -7,17 +7,14 @@ using OpenTK.Input;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Timing;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using System;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
@ -33,6 +30,13 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
private const float column_width = 45;
|
||||
private const float special_column_width = 70;
|
||||
|
||||
private readonly BindableDouble visibleTimeRange = new BindableDouble();
|
||||
public BindableDouble VisibleTimeRange
|
||||
{
|
||||
get { return visibleTimeRange; }
|
||||
set { visibleTimeRange.BindTo(value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The key that will trigger input actions for this column and hit objects contained inside it.
|
||||
/// </summary>
|
||||
@ -42,9 +46,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
private readonly Container hitTargetBar;
|
||||
private readonly Container keyIcon;
|
||||
|
||||
public readonly ControlPointContainer ControlPointContainer;
|
||||
private readonly SpeedAdjustmentCollection speedAdjustments;
|
||||
|
||||
public Column(IEnumerable<TimingChange> timingChanges)
|
||||
public Column()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
Width = column_width;
|
||||
@ -61,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
Name = "Hit target + hit objects",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = ManiaPlayfield.HIT_TARGET_POSITION},
|
||||
Padding = new MarginPadding { Top = ManiaPlayfield.HIT_TARGET_POSITION },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
@ -93,10 +97,11 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
}
|
||||
}
|
||||
},
|
||||
ControlPointContainer = new ControlPointContainer(timingChanges)
|
||||
speedAdjustments = new SpeedAdjustmentCollection(Axes.Y)
|
||||
{
|
||||
Name = "Hit objects",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
VisibleTimeRange = VisibleTimeRange
|
||||
},
|
||||
// For column lighting, we need to capture input events before the notes
|
||||
new InputTarget
|
||||
@ -171,14 +176,14 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
background.Colour = accentColour;
|
||||
|
||||
hitTargetBar.EdgeEffect = new EdgeEffect
|
||||
hitTargetBar.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Radius = 5,
|
||||
Colour = accentColour.Opacity(0.5f),
|
||||
};
|
||||
|
||||
keyIcon.EdgeEffect = new EdgeEffect
|
||||
keyIcon.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Radius = 5,
|
||||
@ -187,10 +192,11 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> hitObject)
|
||||
public void Add(SpeedAdjustmentContainer speedAdjustment) => speedAdjustments.Add(speedAdjustment);
|
||||
public void Add(DrawableHitObject hitObject)
|
||||
{
|
||||
hitObject.AccentColour = AccentColour;
|
||||
ControlPointContainer.Add(hitObject);
|
||||
speedAdjustments.Add(hitObject);
|
||||
}
|
||||
|
||||
private bool onKeyDown(InputState state, KeyDownEventArgs args)
|
||||
|
@ -8,6 +8,7 @@ using OpenTK;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Framework.MathUtils;
|
||||
@ -16,6 +17,7 @@ using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Scoring;
|
||||
@ -23,78 +25,44 @@ using osu.Game.Rulesets.Mania.Timing;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
public class ManiaHitRenderer : HitRenderer<ManiaHitObject, ManiaJudgement>
|
||||
public class ManiaHitRenderer : SpeedAdjustedHitRenderer<ManiaHitObject, ManiaJudgement>
|
||||
{
|
||||
public int? Columns;
|
||||
/// <summary>
|
||||
/// Preferred column count. This will only have an effect during the initialization of the play field.
|
||||
/// </summary>
|
||||
public int PreferredColumns;
|
||||
|
||||
public IEnumerable<DrawableBarLine> BarLines;
|
||||
|
||||
/// <summary>
|
||||
/// Per-column timing changes.
|
||||
/// </summary>
|
||||
private readonly List<SpeedAdjustmentContainer>[] hitObjectSpeedAdjustments;
|
||||
|
||||
/// <summary>
|
||||
/// Bar line timing changes.
|
||||
/// </summary>
|
||||
private readonly List<SpeedAdjustmentContainer> barLineSpeedAdjustments = new List<SpeedAdjustmentContainer>();
|
||||
|
||||
public ManiaHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||
: base(beatmap, isForCurrentRuleset)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Playfield<ManiaHitObject, ManiaJudgement> CreatePlayfield()
|
||||
{
|
||||
double lastSpeedMultiplier = 1;
|
||||
double lastBeatLength = 500;
|
||||
|
||||
// Merge timing + difficulty points
|
||||
var allPoints = new SortedList<ControlPoint>(Comparer<ControlPoint>.Default);
|
||||
allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints);
|
||||
allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints);
|
||||
|
||||
// Generate the timing points, making non-timing changes use the previous timing change
|
||||
var timingChanges = allPoints.Select(c =>
|
||||
{
|
||||
var timingPoint = c as TimingControlPoint;
|
||||
var difficultyPoint = c as DifficultyControlPoint;
|
||||
|
||||
if (timingPoint != null)
|
||||
lastBeatLength = timingPoint.BeatLength;
|
||||
|
||||
if (difficultyPoint != null)
|
||||
lastSpeedMultiplier = difficultyPoint.SpeedMultiplier;
|
||||
|
||||
return new TimingChange
|
||||
{
|
||||
Time = c.Time,
|
||||
BeatLength = lastBeatLength,
|
||||
SpeedMultiplier = lastSpeedMultiplier
|
||||
};
|
||||
});
|
||||
|
||||
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
|
||||
|
||||
// Perform some post processing of the timing changes
|
||||
timingChanges = timingChanges
|
||||
// Collapse sections after the last hit object
|
||||
.Where(s => s.Time <= lastObjectTime)
|
||||
// Collapse sections with the same start time
|
||||
.GroupBy(s => s.Time).Select(g => g.Last()).OrderBy(s => s.Time)
|
||||
// Collapse sections with the same beat length
|
||||
.GroupBy(s => s.BeatLength * s.SpeedMultiplier).Select(g => g.First())
|
||||
.ToList();
|
||||
|
||||
return new ManiaPlayfield(Columns ?? (int)Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize), timingChanges)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
// Invert by default for now (should be moved to config/skin later)
|
||||
Scale = new Vector2(1, -1)
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var maniaPlayfield = (ManiaPlayfield)Playfield;
|
||||
// Generate the speed adjustment container lists
|
||||
hitObjectSpeedAdjustments = new List<SpeedAdjustmentContainer>[PreferredColumns];
|
||||
for (int i = 0; i < PreferredColumns; i++)
|
||||
hitObjectSpeedAdjustments[i] = new List<SpeedAdjustmentContainer>();
|
||||
|
||||
// Generate the bar lines
|
||||
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
|
||||
|
||||
SortedList<TimingControlPoint> timingPoints = Beatmap.ControlPointInfo.TimingPoints;
|
||||
var barLines = new List<DrawableBarLine>();
|
||||
|
||||
for (int i = 0; i < timingPoints.Count; i++)
|
||||
{
|
||||
TimingControlPoint point = timingPoints[i];
|
||||
@ -105,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
int index = 0;
|
||||
for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
|
||||
{
|
||||
maniaPlayfield.Add(new DrawableBarLine(new BarLine
|
||||
barLines.Add(new DrawableBarLine(new BarLine
|
||||
{
|
||||
StartTime = t,
|
||||
ControlPoint = point,
|
||||
@ -113,17 +81,78 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
BarLines = barLines;
|
||||
|
||||
// Generate speed adjustments from mods first
|
||||
bool useDefaultSpeedAdjustments = true;
|
||||
|
||||
if (Mods != null)
|
||||
{
|
||||
foreach (var speedAdjustmentMod in Mods.OfType<IGenerateSpeedAdjustments>())
|
||||
{
|
||||
useDefaultSpeedAdjustments = false;
|
||||
speedAdjustmentMod.ApplyToHitRenderer(this, ref hitObjectSpeedAdjustments, ref barLineSpeedAdjustments);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the default speed adjustments
|
||||
if (useDefaultSpeedAdjustments)
|
||||
generateDefaultSpeedAdjustments();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var maniaPlayfield = (ManiaPlayfield)Playfield;
|
||||
|
||||
BarLines.ForEach(maniaPlayfield.Add);
|
||||
}
|
||||
|
||||
protected override void ApplyBeatmap()
|
||||
{
|
||||
base.ApplyBeatmap();
|
||||
|
||||
PreferredColumns = (int)Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize);
|
||||
}
|
||||
|
||||
protected override void ApplySpeedAdjustments()
|
||||
{
|
||||
var maniaPlayfield = (ManiaPlayfield)Playfield;
|
||||
|
||||
for (int i = 0; i < PreferredColumns; i++)
|
||||
foreach (var change in hitObjectSpeedAdjustments[i])
|
||||
maniaPlayfield.Columns.ElementAt(i).Add(change);
|
||||
|
||||
foreach (var change in barLineSpeedAdjustments)
|
||||
maniaPlayfield.Add(change);
|
||||
}
|
||||
|
||||
private void generateDefaultSpeedAdjustments()
|
||||
{
|
||||
DefaultControlPoints.ForEach(c =>
|
||||
{
|
||||
foreach (List<SpeedAdjustmentContainer> t in hitObjectSpeedAdjustments)
|
||||
t.Add(new ManiaSpeedAdjustmentContainer(c, ScrollingAlgorithm.Basic));
|
||||
barLineSpeedAdjustments.Add(new ManiaSpeedAdjustmentContainer(c, ScrollingAlgorithm.Basic));
|
||||
});
|
||||
}
|
||||
|
||||
protected sealed override Playfield<ManiaHitObject, ManiaJudgement> CreatePlayfield() => new ManiaPlayfield(PreferredColumns)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
// Invert by default for now (should be moved to config/skin later)
|
||||
Scale = new Vector2(1, -1)
|
||||
};
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
||||
|
||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter();
|
||||
|
||||
protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h)
|
||||
{
|
||||
var maniaPlayfield = Playfield as ManiaPlayfield;
|
||||
if (maniaPlayfield == null)
|
||||
return null;
|
||||
var maniaPlayfield = (ManiaPlayfield)Playfield;
|
||||
|
||||
Bindable<Key> key = maniaPlayfield.Columns.ElementAt(h.Column).Key;
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using OpenTK;
|
||||
@ -15,13 +14,14 @@ using osu.Framework.Allocation;
|
||||
using OpenTK.Input;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Timing;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
@ -29,10 +29,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
public const float HIT_TARGET_POSITION = 50;
|
||||
|
||||
private const float time_span_default = 5000;
|
||||
private const float time_span_min = 10;
|
||||
private const float time_span_max = 50000;
|
||||
private const float time_span_step = 200;
|
||||
private const double time_span_default = 1500;
|
||||
private const double time_span_min = 50;
|
||||
private const double time_span_max = 10000;
|
||||
private const double time_span_step = 50;
|
||||
|
||||
/// <summary>
|
||||
/// Default column keys, expanding outwards from the middle as more column are added.
|
||||
@ -58,14 +58,20 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
private readonly FlowContainer<Column> columns;
|
||||
public IEnumerable<Column> Columns => columns.Children;
|
||||
|
||||
private readonly ControlPointContainer barLineContainer;
|
||||
private readonly BindableDouble visibleTimeRange = new BindableDouble(time_span_default)
|
||||
{
|
||||
MinValue = time_span_min,
|
||||
MaxValue = time_span_max
|
||||
};
|
||||
|
||||
private readonly SpeedAdjustmentCollection barLineContainer;
|
||||
|
||||
private List<Color4> normalColumnColours = new List<Color4>();
|
||||
private Color4 specialColumnColour;
|
||||
|
||||
private readonly int columnCount;
|
||||
|
||||
public ManiaPlayfield(int columnCount, IEnumerable<TimingChange> timingChanges)
|
||||
public ManiaPlayfield(int columnCount)
|
||||
{
|
||||
this.columnCount = columnCount;
|
||||
|
||||
@ -116,12 +122,13 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
Padding = new MarginPadding { Top = HIT_TARGET_POSITION },
|
||||
Children = new[]
|
||||
{
|
||||
barLineContainer = new ControlPointContainer(timingChanges)
|
||||
barLineContainer = new SpeedAdjustmentCollection(Axes.Y)
|
||||
{
|
||||
Name = "Bar lines",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
VisibleTimeRange = visibleTimeRange
|
||||
// Width is set in the Update method
|
||||
}
|
||||
}
|
||||
@ -131,9 +138,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
};
|
||||
|
||||
for (int i = 0; i < columnCount; i++)
|
||||
columns.Add(new Column(timingChanges));
|
||||
|
||||
TimeSpan = time_span_default;
|
||||
columns.Add(new Column { VisibleTimeRange = visibleTimeRange });
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -208,6 +213,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
public override void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> h) => Columns.ElementAt(h.HitObject.Column).Add(h);
|
||||
public void Add(DrawableBarLine barline) => barLineContainer.Add(barline);
|
||||
public void Add(SpeedAdjustmentContainer speedAdjustment) => barLineContainer.Add(speedAdjustment);
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
@ -216,10 +222,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
switch (args.Key)
|
||||
{
|
||||
case Key.Minus:
|
||||
transformTimeSpanTo(TimeSpan + time_span_step, 200, EasingTypes.OutQuint);
|
||||
transformVisibleTimeRangeTo(visibleTimeRange + time_span_step, 200, EasingTypes.OutQuint);
|
||||
break;
|
||||
case Key.Plus:
|
||||
transformTimeSpanTo(TimeSpan - time_span_step, 200, EasingTypes.OutQuint);
|
||||
transformVisibleTimeRangeTo(visibleTimeRange - time_span_step, 200, EasingTypes.OutQuint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -227,29 +233,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
return false;
|
||||
}
|
||||
|
||||
private double timeSpan;
|
||||
/// <summary>
|
||||
/// The amount of time which the length of the playfield spans.
|
||||
/// </summary>
|
||||
public double TimeSpan
|
||||
private void transformVisibleTimeRangeTo(double newTimeRange, double duration = 0, EasingTypes easing = EasingTypes.None)
|
||||
{
|
||||
get { return timeSpan; }
|
||||
set
|
||||
{
|
||||
if (timeSpan == value)
|
||||
return;
|
||||
timeSpan = value;
|
||||
|
||||
timeSpan = MathHelper.Clamp(timeSpan, time_span_min, time_span_max);
|
||||
|
||||
barLineContainer.TimeSpan = value;
|
||||
Columns.ForEach(c => c.ControlPointContainer.TimeSpan = value);
|
||||
}
|
||||
}
|
||||
|
||||
private void transformTimeSpanTo(double newTimeSpan, double duration = 0, EasingTypes easing = EasingTypes.None)
|
||||
{
|
||||
TransformTo(() => TimeSpan, newTimeSpan, duration, easing, new TransformTimeSpan());
|
||||
TransformTo(() => visibleTimeRange.Value, newTimeRange, duration, easing, new TransformTimeSpan());
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
@ -259,7 +245,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
barLineContainer.Width = columns.Width;
|
||||
}
|
||||
|
||||
private class TransformTimeSpan : Transform<double>
|
||||
private class TransformTimeSpan : Transform<double, Drawable>
|
||||
{
|
||||
public override double CurrentValue
|
||||
{
|
||||
@ -278,7 +264,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
base.Apply(d);
|
||||
|
||||
var p = (ManiaPlayfield)d;
|
||||
p.TimeSpan = CurrentValue;
|
||||
p.visibleTimeRange.Value = (float)CurrentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +33,9 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
@ -62,6 +63,7 @@
|
||||
<Compile Include="Judgements\ManiaHitResult.cs" />
|
||||
<Compile Include="Judgements\ManiaJudgement.cs" />
|
||||
<Compile Include="ManiaDifficultyCalculator.cs" />
|
||||
<Compile Include="Mods\IGenerateSpeedAdjustments.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableBarLine.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableHoldNote.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableHoldNoteTick.cs" />
|
||||
@ -77,14 +79,17 @@
|
||||
<Compile Include="Objects\ManiaHitObject.cs" />
|
||||
<Compile Include="Objects\Note.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Timing\BasicScrollingDrawableTimingSection.cs" />
|
||||
<Compile Include="Timing\GravityScrollingDrawableTimingSection.cs" />
|
||||
<Compile Include="Timing\ScrollingAlgorithm.cs" />
|
||||
<Compile Include="UI\Column.cs" />
|
||||
<Compile Include="UI\ManiaHitRenderer.cs" />
|
||||
<Compile Include="UI\ManiaPlayfield.cs" />
|
||||
<Compile Include="ManiaRuleset.cs" />
|
||||
<Compile Include="Mods\ManiaMod.cs" />
|
||||
<Compile Include="Mods\ManiaModGravity.cs" />
|
||||
<Compile Include="UI\SpecialColumnPosition.cs" />
|
||||
<Compile Include="Timing\ControlPointContainer.cs" />
|
||||
<Compile Include="Timing\TimingChange.cs" />
|
||||
<Compile Include="Timing\ManiaSpeedAdjustmentContainer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
|
||||
|
@ -1,9 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
-->
|
||||
<packages>
|
||||
<package id="ppy.OpenTK" version="2.0.50727.1341" targetFramework="net45" />
|
||||
<package id="ppy.OpenTK" version="3.0" targetFramework="net45" />
|
||||
</packages>
|
@ -39,6 +39,11 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
|
||||
}
|
||||
|
||||
public class OsuModDaycore : ModDaycore
|
||||
{
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
}
|
||||
|
||||
public class OsuModDoubleTime : ModDoubleTime
|
||||
{
|
||||
public override double ScoreMultiplier => 1.12;
|
||||
|
@ -6,7 +6,7 @@ using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
{
|
||||
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
Masking = true;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
CornerRadius = width / 2;
|
||||
EdgeEffect = new EdgeEffect
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Color4.White.Opacity(0.2f),
|
||||
|
@ -3,11 +3,11 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
|
@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
@ -31,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
Masking = true,
|
||||
Origin = Anchor.Centre,
|
||||
EdgeEffect = new EdgeEffect
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Radius = 60,
|
||||
|
@ -3,9 +3,9 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
Disc.Colour = value;
|
||||
|
||||
EdgeEffect = new EdgeEffect
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Hollow = true,
|
||||
Type = EdgeEffectType.Glow,
|
||||
|
@ -5,9 +5,9 @@ using System;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.4f,
|
||||
EdgeEffect = new EdgeEffect
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Radius = 10,
|
||||
|
@ -1,201 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
internal class OsuHitObjectDifficulty
|
||||
{
|
||||
/// <summary>
|
||||
/// Factor by how much speed / aim strain decays per second.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These values are results of tweaking a lot and taking into account general feedback.
|
||||
/// Opinionated observation: Speed is easier to maintain than accurate jumps.
|
||||
/// </remarks>
|
||||
internal static readonly double[] DECAY_BASE = { 0.3, 0.15 };
|
||||
|
||||
/// <summary>
|
||||
/// Pseudo threshold values to distinguish between "singles" and "streams"
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Of course the border can not be defined clearly, therefore the algorithm has a smooth transition between those values.
|
||||
/// They also are based on tweaking and general feedback.
|
||||
/// </remarks>
|
||||
private const double stream_spacing_threshold = 110,
|
||||
single_spacing_threshold = 125;
|
||||
|
||||
/// <summary>
|
||||
/// Scaling values for weightings to keep aim and speed difficulty in balance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Found from testing a very large map pool (containing all ranked maps) and keeping the average values the same.
|
||||
/// </remarks>
|
||||
private static readonly double[] spacing_weight_scaling = { 1400, 26.25 };
|
||||
|
||||
/// <summary>
|
||||
/// Almost the normed diameter of a circle (104 osu pixel). That is -after- position transforming.
|
||||
/// </summary>
|
||||
private const double almost_diameter = 90;
|
||||
|
||||
internal OsuHitObject BaseHitObject;
|
||||
internal double[] Strains = { 1, 1 };
|
||||
|
||||
internal int MaxCombo = 1;
|
||||
|
||||
private readonly float scalingFactor;
|
||||
private float lazySliderLength;
|
||||
|
||||
private readonly Vector2 startPosition;
|
||||
private readonly Vector2 endPosition;
|
||||
|
||||
internal OsuHitObjectDifficulty(OsuHitObject baseHitObject)
|
||||
{
|
||||
BaseHitObject = baseHitObject;
|
||||
float circleRadius = baseHitObject.Scale * 64;
|
||||
|
||||
Slider slider = BaseHitObject as Slider;
|
||||
if (slider != null)
|
||||
MaxCombo += slider.Ticks.Count();
|
||||
|
||||
// We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
|
||||
scalingFactor = 52.0f / circleRadius;
|
||||
if (circleRadius < 30)
|
||||
{
|
||||
float smallCircleBonus = Math.Min(30.0f - circleRadius, 5.0f) / 50.0f;
|
||||
scalingFactor *= 1.0f + smallCircleBonus;
|
||||
}
|
||||
|
||||
lazySliderLength = 0;
|
||||
startPosition = baseHitObject.StackedPosition;
|
||||
|
||||
// Calculate approximation of lazy movement on the slider
|
||||
if (slider != null)
|
||||
{
|
||||
float sliderFollowCircleRadius = circleRadius * 3; // Not sure if this is correct, but here we do not need 100% exact values. This comes pretty darn close in my tests.
|
||||
|
||||
// For simplifying this step we use actual osu! coordinates and simply scale the length, that we obtain by the ScalingFactor later
|
||||
Vector2 cursorPos = startPosition;
|
||||
|
||||
Action<Vector2> addSliderVertex = delegate (Vector2 pos)
|
||||
{
|
||||
Vector2 difference = pos - cursorPos;
|
||||
float distance = difference.Length;
|
||||
|
||||
// Did we move away too far?
|
||||
if (distance > sliderFollowCircleRadius)
|
||||
{
|
||||
// Yep, we need to move the cursor
|
||||
difference.Normalize(); // Obtain the direction of difference. We do no longer need the actual difference
|
||||
distance -= sliderFollowCircleRadius;
|
||||
cursorPos += difference * distance; // We move the cursor just as far as needed to stay in the follow circle
|
||||
lazySliderLength += distance;
|
||||
}
|
||||
};
|
||||
|
||||
// Actual computation of the first lazy curve
|
||||
foreach (var tick in slider.Ticks)
|
||||
addSliderVertex(tick.StackedPosition);
|
||||
|
||||
addSliderVertex(baseHitObject.StackedEndPosition);
|
||||
|
||||
lazySliderLength *= scalingFactor;
|
||||
endPosition = cursorPos;
|
||||
}
|
||||
// We have a normal HitCircle or a spinner
|
||||
else
|
||||
endPosition = startPosition;
|
||||
}
|
||||
|
||||
internal void CalculateStrains(OsuHitObjectDifficulty previousHitObject, double timeRate)
|
||||
{
|
||||
calculateSpecificStrain(previousHitObject, OsuDifficultyCalculator.DifficultyType.Speed, timeRate);
|
||||
calculateSpecificStrain(previousHitObject, OsuDifficultyCalculator.DifficultyType.Aim, timeRate);
|
||||
}
|
||||
|
||||
// Caution: The subjective values are strong with this one
|
||||
private static double spacingWeight(double distance, OsuDifficultyCalculator.DifficultyType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case OsuDifficultyCalculator.DifficultyType.Speed:
|
||||
if (distance > single_spacing_threshold)
|
||||
return 2.5;
|
||||
else if (distance > stream_spacing_threshold)
|
||||
return 1.6 + 0.9 * (distance - stream_spacing_threshold) / (single_spacing_threshold - stream_spacing_threshold);
|
||||
else if (distance > almost_diameter)
|
||||
return 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter);
|
||||
else if (distance > almost_diameter / 2)
|
||||
return 0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2);
|
||||
else
|
||||
return 0.95;
|
||||
|
||||
case OsuDifficultyCalculator.DifficultyType.Aim:
|
||||
return Math.Pow(distance, 0.99);
|
||||
}
|
||||
|
||||
Debug.Assert(false, "Invalid osu difficulty hit object type.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void calculateSpecificStrain(OsuHitObjectDifficulty previousHitObject, OsuDifficultyCalculator.DifficultyType type, double timeRate)
|
||||
{
|
||||
double addition = 0;
|
||||
double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
|
||||
double decay = Math.Pow(DECAY_BASE[(int)type], timeElapsed / 1000);
|
||||
|
||||
if (BaseHitObject is Spinner)
|
||||
{
|
||||
// Do nothing for spinners
|
||||
}
|
||||
else if (BaseHitObject is Slider)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case OsuDifficultyCalculator.DifficultyType.Speed:
|
||||
|
||||
// For speed strain we treat the whole slider as a single spacing entity, since "Speed" is about how hard it is to click buttons fast.
|
||||
// The spacing weight exists to differentiate between being able to easily alternate or having to single.
|
||||
addition =
|
||||
spacingWeight(previousHitObject.lazySliderLength +
|
||||
DistanceTo(previousHitObject), type) *
|
||||
spacing_weight_scaling[(int)type];
|
||||
|
||||
break;
|
||||
case OsuDifficultyCalculator.DifficultyType.Aim:
|
||||
|
||||
// For Aim strain we treat each slider segment and the jump after the end of the slider as separate jumps, since movement-wise there is no difference
|
||||
// to multiple jumps.
|
||||
addition =
|
||||
(
|
||||
spacingWeight(previousHitObject.lazySliderLength, type) +
|
||||
spacingWeight(DistanceTo(previousHitObject), type)
|
||||
) *
|
||||
spacing_weight_scaling[(int)type];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (BaseHitObject is HitCircle)
|
||||
{
|
||||
addition = spacingWeight(DistanceTo(previousHitObject), type) * spacing_weight_scaling[(int)type];
|
||||
}
|
||||
|
||||
// Scale addition by the time, that elapsed. Filter out HitObjects that are too close to be played anyway to avoid crazy values by division through close to zero.
|
||||
// You will never find maps that require this amongst ranked maps.
|
||||
addition /= Math.Max(timeElapsed, 50);
|
||||
|
||||
Strains[(int)type] = previousHitObject.Strains[(int)type] * decay + addition;
|
||||
}
|
||||
|
||||
internal double DistanceTo(OsuHitObjectDifficulty other)
|
||||
{
|
||||
// Scale the distance by circle size.
|
||||
return (startPosition - other.endPosition).Length * scalingFactor;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Osu.OsuDifficulty.Skills;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.OsuDifficulty
|
||||
{
|
||||
public class OsuDifficultyCalculator : DifficultyCalculator<OsuHitObject>
|
||||
{
|
||||
private const int section_length = 400;
|
||||
private const double difficulty_multiplier = 0.0675;
|
||||
|
||||
public OsuDifficultyCalculator(Beatmap beatmap) : base(beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void PreprocessHitObjects()
|
||||
{
|
||||
foreach (OsuHitObject h in Objects)
|
||||
(h as Slider)?.Curve?.Calculate();
|
||||
}
|
||||
|
||||
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
|
||||
{
|
||||
OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Objects);
|
||||
Skill[] skills =
|
||||
{
|
||||
new Aim(),
|
||||
new Speed()
|
||||
};
|
||||
|
||||
double sectionEnd = section_length / TimeRate;
|
||||
foreach (OsuDifficultyHitObject h in beatmap)
|
||||
{
|
||||
while (h.BaseObject.StartTime > sectionEnd)
|
||||
{
|
||||
foreach (Skill s in skills)
|
||||
{
|
||||
s.SaveCurrentPeak();
|
||||
s.StartNewSectionFrom(sectionEnd);
|
||||
}
|
||||
|
||||
sectionEnd += section_length;
|
||||
}
|
||||
|
||||
foreach (Skill s in skills)
|
||||
s.Process(h);
|
||||
}
|
||||
|
||||
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
|
||||
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
||||
|
||||
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
|
||||
|
||||
if (categoryDifficulty != null)
|
||||
{
|
||||
categoryDifficulty.Add("Aim", aimRating.ToString("0.00"));
|
||||
categoryDifficulty.Add("Speed", speedRating.ToString("0.00"));
|
||||
}
|
||||
|
||||
return starRating;
|
||||
}
|
||||
|
||||
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerable container wrapping <see cref="OsuHitObject"/> input as <see cref="OsuDifficultyHitObject"/>
|
||||
/// which contains extra data required for difficulty calculation.
|
||||
/// </summary>
|
||||
public class OsuDifficultyBeatmap : IEnumerable<OsuDifficultyHitObject>
|
||||
{
|
||||
private readonly IEnumerator<OsuDifficultyHitObject> difficultyObjects;
|
||||
private readonly Queue<OsuDifficultyHitObject> onScreen = new Queue<OsuDifficultyHitObject>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates an enumerator, which preprocesses a list of <see cref="OsuHitObject"/>s recieved as input, wrapping them as
|
||||
/// <see cref="OsuDifficultyHitObject"/> which contains extra data required for difficulty calculation.
|
||||
/// </summary>
|
||||
public OsuDifficultyBeatmap(List<OsuHitObject> objects)
|
||||
{
|
||||
// Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases.
|
||||
// This should probably happen before the objects reach the difficulty calculator.
|
||||
objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime));
|
||||
difficultyObjects = createDifficultyObjectEnumerator(objects);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that enumerates all <see cref="OsuDifficultyHitObject"/>s in the <see cref="OsuDifficultyBeatmap"/>.
|
||||
/// The inner loop adds objects that appear on screen into a queue until we need to hit the next object.
|
||||
/// The outer loop returns objects from this queue one at a time, only after they had to be hit, and should no longer be on screen.
|
||||
/// This means that we can loop through every object that is on screen at the time when a new one appears,
|
||||
/// allowing us to determine a reading strain for the object that just appeared.
|
||||
/// </summary>
|
||||
public IEnumerator<OsuDifficultyHitObject> GetEnumerator()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Add upcoming objects to the queue until we have at least one object that had been hit and can be dequeued.
|
||||
// This means there is always at least one object in the queue unless we reached the end of the map.
|
||||
do
|
||||
{
|
||||
if (!difficultyObjects.MoveNext())
|
||||
break; // New objects can't be added anymore, but we still need to dequeue and return the ones already on screen.
|
||||
|
||||
OsuDifficultyHitObject latest = difficultyObjects.Current;
|
||||
// Calculate flow values here
|
||||
|
||||
foreach (OsuDifficultyHitObject h in onScreen)
|
||||
{
|
||||
h.TimeUntilHit -= latest.DeltaTime;
|
||||
// Calculate reading strain here
|
||||
}
|
||||
|
||||
onScreen.Enqueue(latest);
|
||||
}
|
||||
while (onScreen.Peek().TimeUntilHit > 0); // Keep adding new objects on screen while there is still time before we have to hit the next one.
|
||||
|
||||
if (onScreen.Count == 0) break; // We have reached the end of the map and enumerated all the objects.
|
||||
yield return onScreen.Dequeue(); // Remove and return objects one by one that had to be hit before the latest one appeared.
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
private IEnumerator<OsuDifficultyHitObject> createDifficultyObjectEnumerator(List<OsuHitObject> objects)
|
||||
{
|
||||
// We will process OsuHitObjects in groups of three to form a triangle, so we can calculate an angle for each object.
|
||||
OsuHitObject[] triangle = new OsuHitObject[3];
|
||||
|
||||
// OsuDifficultyHitObject construction requires three components, an extra copy of the first OsuHitObject is used at the beginning.
|
||||
if (objects.Count > 1)
|
||||
{
|
||||
triangle[1] = objects[0]; // This copy will get shifted to the last spot in the triangle.
|
||||
triangle[0] = objects[0]; // This component corresponds to the real first OsuHitOject.
|
||||
}
|
||||
|
||||
// The final component of the first triangle will be the second OsuHitOject of the map, which forms the first jump.
|
||||
// If the map has less than two OsuHitObjects, the enumerator will not return anything.
|
||||
for (int i = 1; i < objects.Count; ++i)
|
||||
{
|
||||
triangle[2] = triangle[1];
|
||||
triangle[1] = triangle[0];
|
||||
triangle[0] = objects[i];
|
||||
|
||||
yield return new OsuDifficultyHitObject(triangle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
{
|
||||
/// <summary>
|
||||
/// A wrapper around <see cref="OsuHitObject"/> extending it with additional data required for difficulty calculation.
|
||||
/// </summary>
|
||||
public class OsuDifficultyHitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="OsuHitObject"/> this <see cref="OsuDifficultyHitObject"/> refers to.
|
||||
/// </summary>
|
||||
public OsuHitObject BaseObject { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Normalized distance from the <see cref="OsuHitObject.StackedPosition"/> of the previous <see cref="OsuDifficultyHitObject"/>.
|
||||
/// </summary>
|
||||
public double Distance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Milliseconds elapsed since the StartTime of the previous <see cref="OsuDifficultyHitObject"/>.
|
||||
/// </summary>
|
||||
public double DeltaTime { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of milliseconds until the <see cref="OsuDifficultyHitObject"/> has to be hit.
|
||||
/// </summary>
|
||||
public double TimeUntilHit { get; set; }
|
||||
|
||||
private const int normalized_radius = 52;
|
||||
|
||||
private readonly OsuHitObject[] t;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the object calculating extra data required for difficulty calculation.
|
||||
/// </summary>
|
||||
public OsuDifficultyHitObject(OsuHitObject[] triangle)
|
||||
{
|
||||
t = triangle;
|
||||
BaseObject = t[0];
|
||||
setDistances();
|
||||
setTimingValues();
|
||||
// Calculate angle here
|
||||
}
|
||||
|
||||
private void setDistances()
|
||||
{
|
||||
// We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps.
|
||||
double scalingFactor = normalized_radius / BaseObject.Radius;
|
||||
if (BaseObject.Radius < 30)
|
||||
{
|
||||
double smallCircleBonus = Math.Min(30 - BaseObject.Radius, 5) / 50;
|
||||
scalingFactor *= 1 + smallCircleBonus;
|
||||
}
|
||||
|
||||
Distance = (t[0].StackedPosition - t[1].StackedPosition).Length * scalingFactor;
|
||||
}
|
||||
|
||||
private void setTimingValues()
|
||||
{
|
||||
// Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure.
|
||||
DeltaTime = Math.Max(40, t[0].StartTime - t[1].StartTime);
|
||||
TimeUntilHit = 450; // BaseObject.PreEmpt;
|
||||
}
|
||||
}
|
||||
}
|
19
osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Aim.cs
Normal file
19
osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Aim.cs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
|
||||
/// </summary>
|
||||
public class Aim : Skill
|
||||
{
|
||||
protected override double SkillMultiplier => 26.25;
|
||||
protected override double StrainDecayBase => 0.15;
|
||||
|
||||
protected override double StrainValueOf(OsuDifficultyHitObject current) => Math.Pow(current.Distance, 0.99) / current.DeltaTime;
|
||||
}
|
||||
}
|
100
osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs
Normal file
100
osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs
Normal file
@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Osu.OsuDifficulty.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to processes strain values of <see cref="OsuDifficultyHitObject"/>s, keep track of strain levels caused by the processed objects
|
||||
/// and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
|
||||
/// </summary>
|
||||
public abstract class Skill
|
||||
{
|
||||
/// <summary>
|
||||
/// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other.
|
||||
/// </summary>
|
||||
protected abstract double SkillMultiplier { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines how quickly strain decays for the given skill.
|
||||
/// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second.
|
||||
/// </summary>
|
||||
protected abstract double StrainDecayBase { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="OsuDifficultyHitObject"/>s that were processed previously. They can affect the strain values of the following objects.
|
||||
/// </summary>
|
||||
protected readonly History<OsuDifficultyHitObject> Previous = new History<OsuDifficultyHitObject>(2); // Contained objects not used yet
|
||||
|
||||
private double currentStrain = 1; // We keep track of the strain level at all times throughout the beatmap.
|
||||
private double currentSectionPeak = 1; // We also keep track of the peak strain level in the current section.
|
||||
private readonly List<double> strainPeaks = new List<double>();
|
||||
|
||||
/// <summary>
|
||||
/// Process an <see cref="OsuDifficultyHitObject"/> and update current strain values accordingly.
|
||||
/// </summary>
|
||||
public void Process(OsuDifficultyHitObject current)
|
||||
{
|
||||
currentStrain *= strainDecay(current.DeltaTime);
|
||||
if (!(current.BaseObject is Spinner))
|
||||
currentStrain += StrainValueOf(current) * SkillMultiplier;
|
||||
|
||||
currentSectionPeak = Math.Max(currentStrain, currentSectionPeak);
|
||||
|
||||
Previous.Push(current);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
|
||||
/// </summary>
|
||||
public void SaveCurrentPeak()
|
||||
{
|
||||
if (Previous.Count > 0)
|
||||
strainPeaks.Add(currentSectionPeak);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the initial strain level for a new section.
|
||||
/// </summary>
|
||||
/// <param name="offset">The beginning of the new section in milliseconds</param>
|
||||
public void StartNewSectionFrom(double offset)
|
||||
{
|
||||
// The maximum strain of the new section is not zero by default, strain decays as usual regardless of section boundaries.
|
||||
// This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level.
|
||||
if (Previous.Count > 0)
|
||||
currentSectionPeak = currentStrain * strainDecay(offset - Previous[0].BaseObject.StartTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the calculated difficulty value representing all processed <see cref="OsuDifficultyHitObject"/>s.
|
||||
/// </summary>
|
||||
public double DifficultyValue()
|
||||
{
|
||||
strainPeaks.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
|
||||
|
||||
double difficulty = 0;
|
||||
double weight = 1;
|
||||
|
||||
// Difficulty is the weighted sum of the highest strains from every section.
|
||||
foreach (double strain in strainPeaks)
|
||||
{
|
||||
difficulty += strain * weight;
|
||||
weight *= 0.9;
|
||||
}
|
||||
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the strain value of an <see cref="OsuDifficultyHitObject"/>. This value is affected by previously processed objects.
|
||||
/// </summary>
|
||||
protected abstract double StrainValueOf(OsuDifficultyHitObject current);
|
||||
|
||||
private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000);
|
||||
}
|
||||
}
|
39
osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Speed.cs
Normal file
39
osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Speed.cs
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the skill required to press keys with regards to keeping up with the speed at which objects need to be hit.
|
||||
/// </summary>
|
||||
public class Speed : Skill
|
||||
{
|
||||
protected override double SkillMultiplier => 1400;
|
||||
protected override double StrainDecayBase => 0.3;
|
||||
|
||||
private const double single_spacing_threshold = 125;
|
||||
private const double stream_spacing_threshold = 110;
|
||||
private const double almost_diameter = 90;
|
||||
|
||||
protected override double StrainValueOf(OsuDifficultyHitObject current)
|
||||
{
|
||||
double distance = current.Distance;
|
||||
|
||||
double speedValue;
|
||||
if (distance > single_spacing_threshold)
|
||||
speedValue = 2.5;
|
||||
else if (distance > stream_spacing_threshold)
|
||||
speedValue = 1.6 + 0.9 * (distance - stream_spacing_threshold) / (single_spacing_threshold - stream_spacing_threshold);
|
||||
else if (distance > almost_diameter)
|
||||
speedValue = 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter);
|
||||
else if (distance > almost_diameter / 2)
|
||||
speedValue = 0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2);
|
||||
else
|
||||
speedValue = 0.95;
|
||||
|
||||
return speedValue / current.DeltaTime;
|
||||
}
|
||||
}
|
||||
}
|
86
osu.Game.Rulesets.Osu/OsuDifficulty/Utils/History.cs
Normal file
86
osu.Game.Rulesets.Osu/OsuDifficulty/Utils/History.cs
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// An indexed stack with Push() only, which disposes items at the bottom after the capacity is full.
|
||||
/// Indexing starts at the top of the stack.
|
||||
/// </summary>
|
||||
public class History<T> : IEnumerable<T>
|
||||
{
|
||||
public int Count { get; private set; }
|
||||
|
||||
private readonly T[] array;
|
||||
private readonly int capacity;
|
||||
private int marker; // Marks the position of the most recently added item.
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the History class that is empty and has the specified capacity.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The number of items the History can hold.</param>
|
||||
public History(int capacity)
|
||||
{
|
||||
if (capacity < 0)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
this.capacity = capacity;
|
||||
array = new T[capacity];
|
||||
marker = capacity; // Set marker to the end of the array, outside of the indexed range by one.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The most recently added item is returned at index 0.
|
||||
/// </summary>
|
||||
public T this[int i]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (i < 0 || i > Count - 1)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
i += marker;
|
||||
if (i > capacity - 1)
|
||||
i -= capacity;
|
||||
|
||||
return array[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the item as the most recent one in the history.
|
||||
/// The oldest item is disposed if the history is full.
|
||||
/// </summary>
|
||||
public void Push(T item) // Overwrite the oldest item instead of shifting every item by one with every addition.
|
||||
{
|
||||
if (marker == 0)
|
||||
marker = capacity - 1;
|
||||
else
|
||||
--marker;
|
||||
|
||||
array[marker] = item;
|
||||
|
||||
if (Count < capacity)
|
||||
++Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator which enumerates items in the history starting from the most recently added one.
|
||||
/// </summary>
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
for (int i = marker; i < capacity; ++i)
|
||||
yield return array[i];
|
||||
|
||||
if (Count == capacity)
|
||||
for (int i = 0; i < marker; ++i)
|
||||
yield return array[i];
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
}
|
@ -1,192 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu
|
||||
{
|
||||
public class OsuDifficultyCalculator : DifficultyCalculator<OsuHitObject>
|
||||
{
|
||||
private const double star_scaling_factor = 0.0675;
|
||||
private const double extreme_scaling_factor = 0.5;
|
||||
|
||||
/// <summary>
|
||||
/// HitObjects are stored as a member variable.
|
||||
/// </summary>
|
||||
internal List<OsuHitObjectDifficulty> DifficultyHitObjects = new List<OsuHitObjectDifficulty>();
|
||||
|
||||
public OsuDifficultyCalculator(Beatmap beatmap) : base(beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void PreprocessHitObjects()
|
||||
{
|
||||
foreach (var h in Objects)
|
||||
(h as Slider)?.Curve?.Calculate();
|
||||
}
|
||||
|
||||
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
|
||||
{
|
||||
// Fill our custom DifficultyHitObject class, that carries additional information
|
||||
DifficultyHitObjects.Clear();
|
||||
|
||||
foreach (var hitObject in Objects)
|
||||
DifficultyHitObjects.Add(new OsuHitObjectDifficulty(hitObject));
|
||||
|
||||
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
|
||||
DifficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
|
||||
|
||||
if (!CalculateStrainValues()) return 0;
|
||||
|
||||
double speedDifficulty = CalculateDifficulty(DifficultyType.Speed);
|
||||
double aimDifficulty = CalculateDifficulty(DifficultyType.Aim);
|
||||
|
||||
// OverallDifficulty is not considered in this algorithm and neither is HpDrainRate. That means, that in this form the algorithm determines how hard it physically is
|
||||
// to play the map, assuming, that too much of an error will not lead to a death.
|
||||
// It might be desirable to include OverallDifficulty into map difficulty, but in my personal opinion it belongs more to the weighting of the actual peformance
|
||||
// and is superfluous in the beatmap difficulty rating.
|
||||
// If it were to be considered, then I would look at the hit window of normal HitCircles only, since Sliders and Spinners are (almost) "free" 300s and take map length
|
||||
// into account as well.
|
||||
|
||||
// The difficulty can be scaled by any desired metric.
|
||||
// In osu!tp it gets squared to account for the rapid increase in difficulty as the limit of a human is approached. (Of course it also gets scaled afterwards.)
|
||||
// It would not be suitable for a star rating, therefore:
|
||||
|
||||
// The following is a proposal to forge a star rating from 0 to 5. It consists of taking the square root of the difficulty, since by simply scaling the easier
|
||||
// 5-star maps would end up with one star.
|
||||
double speedStars = Math.Sqrt(speedDifficulty) * star_scaling_factor;
|
||||
double aimStars = Math.Sqrt(aimDifficulty) * star_scaling_factor;
|
||||
|
||||
if (categoryDifficulty != null)
|
||||
{
|
||||
categoryDifficulty.Add("Aim", aimStars.ToString("0.00"));
|
||||
categoryDifficulty.Add("Speed", speedStars.ToString("0.00"));
|
||||
|
||||
double hitWindow300 = 30/*HitObjectManager.HitWindow300*/ / TimeRate;
|
||||
double preEmpt = 450/*HitObjectManager.PreEmpt*/ / TimeRate;
|
||||
|
||||
categoryDifficulty.Add("OD", (-(hitWindow300 - 80.0) / 6.0).ToString("0.00"));
|
||||
categoryDifficulty.Add("AR", (preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0).ToString("0.00"));
|
||||
|
||||
int maxCombo = 0;
|
||||
foreach (OsuHitObjectDifficulty hitObject in DifficultyHitObjects)
|
||||
maxCombo += hitObject.MaxCombo;
|
||||
|
||||
categoryDifficulty.Add("Max combo", maxCombo.ToString());
|
||||
}
|
||||
|
||||
// Again, from own observations and from the general opinion of the community a map with high speed and low aim (or vice versa) difficulty is harder,
|
||||
// than a map with mediocre difficulty in both. Therefore we can not just add both difficulties together, but will introduce a scaling that favors extremes.
|
||||
double starRating = speedStars + aimStars + Math.Abs(speedStars - aimStars) * extreme_scaling_factor;
|
||||
// Another approach to this would be taking Speed and Aim separately to a chosen power, which again would be equivalent. This would be more convenient if
|
||||
// the hit window size is to be considered as well.
|
||||
|
||||
// Note: The star rating is tuned extremely tight! Airman (/b/104229) and Freedom Dive (/b/126645), two of the hardest ranked maps, both score ~4.66 stars.
|
||||
// Expect the easier kind of maps that officially get 5 stars to obtain around 2 by this metric. The tutorial still scores about half a star.
|
||||
// Tune by yourself as you please. ;)
|
||||
|
||||
return starRating;
|
||||
}
|
||||
|
||||
protected bool CalculateStrainValues()
|
||||
{
|
||||
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
|
||||
using (List<OsuHitObjectDifficulty>.Enumerator hitObjectsEnumerator = DifficultyHitObjects.GetEnumerator())
|
||||
{
|
||||
|
||||
if (!hitObjectsEnumerator.MoveNext()) return false;
|
||||
|
||||
OsuHitObjectDifficulty current = hitObjectsEnumerator.Current;
|
||||
|
||||
// First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject.
|
||||
while (hitObjectsEnumerator.MoveNext())
|
||||
{
|
||||
var next = hitObjectsEnumerator.Current;
|
||||
next?.CalculateStrains(current, TimeRate);
|
||||
current = next;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP.
|
||||
/// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.
|
||||
/// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage.
|
||||
/// </summary>
|
||||
protected const double STRAIN_STEP = 400;
|
||||
|
||||
/// <summary>
|
||||
/// The weighting of each strain value decays to this number * it's previous value
|
||||
/// </summary>
|
||||
protected const double DECAY_WEIGHT = 0.9;
|
||||
|
||||
protected double CalculateDifficulty(DifficultyType type)
|
||||
{
|
||||
double actualStrainStep = STRAIN_STEP * TimeRate;
|
||||
|
||||
// Find the highest strain value within each strain step
|
||||
List<double> highestStrains = new List<double>();
|
||||
double intervalEndTime = actualStrainStep;
|
||||
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
|
||||
|
||||
OsuHitObjectDifficulty previousHitObject = null;
|
||||
foreach (OsuHitObjectDifficulty hitObject in DifficultyHitObjects)
|
||||
{
|
||||
// While we are beyond the current interval push the currently available maximum to our strain list
|
||||
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
|
||||
{
|
||||
highestStrains.Add(maximumStrain);
|
||||
|
||||
// The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
|
||||
// until the beginning of the next interval.
|
||||
if (previousHitObject == null)
|
||||
{
|
||||
maximumStrain = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
double decay = Math.Pow(OsuHitObjectDifficulty.DECAY_BASE[(int)type], (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
|
||||
maximumStrain = previousHitObject.Strains[(int)type] * decay;
|
||||
}
|
||||
|
||||
// Go to the next time interval
|
||||
intervalEndTime += actualStrainStep;
|
||||
}
|
||||
|
||||
// Obtain maximum strain
|
||||
maximumStrain = Math.Max(hitObject.Strains[(int)type], maximumStrain);
|
||||
|
||||
previousHitObject = hitObject;
|
||||
}
|
||||
|
||||
// Build the weighted sum over the highest strains for each interval
|
||||
double difficulty = 0;
|
||||
double weight = 1;
|
||||
highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
|
||||
|
||||
foreach (double strain in highestStrains)
|
||||
{
|
||||
difficulty += weight * strain;
|
||||
weight *= DECAY_WEIGHT;
|
||||
}
|
||||
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
|
||||
|
||||
// Those values are used as array indices. Be careful when changing them!
|
||||
public enum DifficultyType
|
||||
{
|
||||
Speed = 0,
|
||||
Aim,
|
||||
};
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.OsuDifficulty;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Play;
|
||||
@ -46,7 +47,14 @@ namespace osu.Game.Rulesets.Osu
|
||||
{
|
||||
new OsuModEasy(),
|
||||
new OsuModNoFail(),
|
||||
new OsuModHalfTime(),
|
||||
new MultiMod
|
||||
{
|
||||
Mods = new Mod[]
|
||||
{
|
||||
new OsuModHalfTime(),
|
||||
new OsuModDaycore(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
case ModType.DifficultyIncrease:
|
||||
|
@ -34,8 +34,9 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
@ -68,9 +69,14 @@
|
||||
<Compile Include="Objects\Drawables\Pieces\TrianglesPiece.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\SliderBall.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\SliderBody.cs" />
|
||||
<Compile Include="Objects\OsuHitObjectDifficulty.cs" />
|
||||
<Compile Include="Objects\SliderTick.cs" />
|
||||
<Compile Include="OsuDifficultyCalculator.cs" />
|
||||
<Compile Include="OsuDifficulty\OsuDifficultyCalculator.cs" />
|
||||
<Compile Include="OsuDifficulty\Preprocessing\OsuDifficultyBeatmap.cs" />
|
||||
<Compile Include="OsuDifficulty\Preprocessing\OsuDifficultyHitObject.cs" />
|
||||
<Compile Include="OsuDifficulty\Skills\Aim.cs" />
|
||||
<Compile Include="OsuDifficulty\Skills\Skill.cs" />
|
||||
<Compile Include="OsuDifficulty\Skills\Speed.cs" />
|
||||
<Compile Include="OsuDifficulty\Utils\History.cs" />
|
||||
<Compile Include="OsuKeyConversionInputManager.cs" />
|
||||
<Compile Include="Scoring\OsuScoreProcessor.cs" />
|
||||
<Compile Include="UI\OsuHitRenderer.cs" />
|
||||
|
@ -5,5 +5,5 @@ Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
-->
|
||||
<packages>
|
||||
<package id="ppy.OpenTK" version="2.0.50727.1341" targetFramework="net45" />
|
||||
<package id="ppy.OpenTK" version="3.0" targetFramework="net45" />
|
||||
</packages>
|
@ -37,6 +37,11 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
|
||||
}
|
||||
|
||||
public class TaikoModDaycore : ModDaycore
|
||||
{
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
}
|
||||
|
||||
public class TaikoModDoubleTime : ModDoubleTime
|
||||
{
|
||||
public override double ScoreMultiplier => 1.12;
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
|
@ -7,7 +7,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
@ -15,6 +14,7 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER),
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Masking = true,
|
||||
Children = new []
|
||||
Children = new[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new []
|
||||
Children = new[]
|
||||
{
|
||||
symbol = new SwellSymbolPiece()
|
||||
}
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
|
||||
{
|
||||
|
@ -4,7 +4,7 @@
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
|
||||
|
||||
private void resetEdgeEffects()
|
||||
{
|
||||
background.EdgeEffect = new EdgeEffect
|
||||
background.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = AccentColour.Opacity(KiaiMode ? edge_alpha_kiai : 1f),
|
||||
|
@ -3,9 +3,9 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
|
||||
{
|
||||
|
@ -3,9 +3,9 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
|
||||
{
|
||||
|
@ -28,7 +28,14 @@ namespace osu.Game.Rulesets.Taiko
|
||||
{
|
||||
new TaikoModEasy(),
|
||||
new TaikoModNoFail(),
|
||||
new TaikoModHalfTime(),
|
||||
new MultiMod
|
||||
{
|
||||
Mods = new Mod[]
|
||||
{
|
||||
new TaikoModHalfTime(),
|
||||
new TaikoModDaycore(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
case ModType.DifficultyIncrease:
|
||||
|
@ -6,7 +6,7 @@ using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
|
@ -5,7 +5,7 @@ using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
@ -5,7 +5,7 @@ using OpenTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
EdgeEffect = new EdgeEffect
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = isRim ? colours.BlueDarker : colours.PinkDarker,
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using OpenTK;
|
||||
@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
BorderThickness = 2,
|
||||
Masking = true,
|
||||
EdgeEffect = new EdgeEffect
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = Color4.Black.Opacity(0.2f),
|
||||
@ -229,7 +229,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
||||
if (judgedObject.HitObject.Kiai)
|
||||
kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject.Judgement, isRim));
|
||||
|
||||
}
|
||||
else
|
||||
hitExplosionContainer.Children.FirstOrDefault(e => e.Judgement == judgedObject.Judgement)?.VisualiseSecondHit();
|
||||
|
@ -33,8 +33,9 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
|
@ -4,5 +4,5 @@ Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
-->
|
||||
<packages>
|
||||
<package id="ppy.OpenTK" version="2.0.50727.1341" targetFramework="net45" />
|
||||
<package id="ppy.OpenTK" version="3.0" targetFramework="net45" />
|
||||
</packages>
|
@ -166,4 +166,3 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,4 +86,3 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,11 +30,13 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="nunit.framework, Version=3.6.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.6.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Reference Include="nunit.framework, Version=3.7.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="SQLite.Net">
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user