mirror of
https://github.com/ppy/osu.git
synced 2026-05-16 04:52:35 +08:00
Compare commits
504 Commits
+5
-1
@@ -12,7 +12,7 @@ install:
|
||||
- cmd: git submodule update --init --recursive --depth=5
|
||||
- cmd: choco install resharper-clt -y
|
||||
- cmd: choco install nvika -y
|
||||
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.3/CodeFileSanity.exe
|
||||
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.4/CodeFileSanity.exe
|
||||
before_build:
|
||||
- cmd: CodeFileSanity.exe
|
||||
- cmd: nuget restore -verbosity quiet
|
||||
@@ -20,6 +20,10 @@ build:
|
||||
project: osu.sln
|
||||
parallel: true
|
||||
verbosity: minimal
|
||||
test:
|
||||
assemblies:
|
||||
only:
|
||||
- 'osu.Desktop\**\*.dll'
|
||||
after_build:
|
||||
- cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL
|
||||
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
|
||||
+1
-1
Submodule osu-framework updated: d89e6cd631...71900dc350
@@ -111,16 +111,11 @@ namespace osu.Desktop
|
||||
{
|
||||
var filePaths = new [] { e.FileName };
|
||||
|
||||
if (filePaths.All(f => Path.GetExtension(f) == @".osz"))
|
||||
Task.Factory.StartNew(() => BeatmapManager.Import(filePaths), TaskCreationOptions.LongRunning);
|
||||
else if (filePaths.All(f => Path.GetExtension(f) == @".osr"))
|
||||
Task.Run(() =>
|
||||
{
|
||||
var score = ScoreStore.ReadReplayFile(filePaths.First());
|
||||
Schedule(() => LoadScore(score));
|
||||
});
|
||||
}
|
||||
var firstExtension = Path.GetExtension(filePaths.First());
|
||||
|
||||
private static readonly string[] allowed_extensions = { @".osz", @".osr" };
|
||||
if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
|
||||
|
||||
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-1
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.IPC;
|
||||
@@ -15,6 +16,9 @@ namespace osu.Desktop
|
||||
[STAThread]
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
if (!RuntimeInfo.IsMono)
|
||||
useMulticoreJit();
|
||||
|
||||
// Back up the cwd before DesktopGameHost changes it
|
||||
var cwd = Environment.CurrentDirectory;
|
||||
|
||||
@@ -22,7 +26,7 @@ namespace osu.Desktop
|
||||
{
|
||||
if (!host.IsPrimaryInstance)
|
||||
{
|
||||
var importer = new BeatmapIPCChannel(host);
|
||||
var importer = new ArchiveImportIPCChannel(host);
|
||||
// Restore the cwd so relative paths given at the command line work correctly
|
||||
Directory.SetCurrentDirectory(cwd);
|
||||
foreach (var file in args)
|
||||
@@ -44,8 +48,16 @@ namespace osu.Desktop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void useMulticoreJit()
|
||||
{
|
||||
var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles"));
|
||||
ProfileOptimization.SetProfileRoot(directory.FullName);
|
||||
ProfileOptimization.StartProfile("Startup.Profile");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SharpCompress, Version=0.18.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
|
||||
|
||||
@@ -6,7 +6,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
|
||||
<packages>
|
||||
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" />
|
||||
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.13" targetFramework="net461" />
|
||||
<package id="SharpCompress" version="0.18.1" targetFramework="net461" />
|
||||
<package id="Splat" version="2.0.0" targetFramework="net45" />
|
||||
<package id="SQLitePCLRaw.bundle_green" version="1.1.8" targetFramework="net461" />
|
||||
|
||||
@@ -10,6 +10,8 @@ using osu.Game.Rulesets.UI;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Catch.Replays;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch
|
||||
{
|
||||
@@ -101,6 +103,8 @@ namespace osu.Game.Rulesets.Catch
|
||||
|
||||
public override int LegacyID => 2;
|
||||
|
||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
|
||||
|
||||
public CatchRuleset(RulesetInfo rulesetInfo = null)
|
||||
: base(rulesetInfo)
|
||||
{
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
StartTime = lastTickTime,
|
||||
ComboColour = ComboColour,
|
||||
X = Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
@@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
StartTime = spanStartTime + t,
|
||||
ComboColour = ComboColour,
|
||||
X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
@@ -120,14 +120,14 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
Samples = Samples,
|
||||
ComboColour = ComboColour,
|
||||
StartTime = spanStartTime + spanDuration,
|
||||
X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
||||
|
||||
public float EndX => Curve.PositionAt(this.ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH;
|
||||
public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH;
|
||||
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
}
|
||||
else if (h.HyperDash)
|
||||
{
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition, ReplayButtonState.Right1));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
}
|
||||
else if (dashRequired)
|
||||
@@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable);
|
||||
|
||||
//dash movement
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, ReplayButtonState.Left1));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, true));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
}
|
||||
@@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
{
|
||||
double timeBefore = positionChange / movement_speed;
|
||||
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition, ReplayButtonState.Right1));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,37 +3,51 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Replays
|
||||
{
|
||||
public class CatchFramedReplayInputHandler : FramedReplayInputHandler
|
||||
public class CatchFramedReplayInputHandler : FramedReplayInputHandler<CatchReplayFrame>
|
||||
{
|
||||
public CatchFramedReplayInputHandler(Replay replay)
|
||||
: base(replay)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool IsImportant(CatchReplayFrame frame) => frame.Position > 0;
|
||||
|
||||
protected float? Position
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!HasFrames)
|
||||
return null;
|
||||
|
||||
return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time);
|
||||
}
|
||||
}
|
||||
|
||||
public override List<InputState> GetPendingStates()
|
||||
{
|
||||
if (!Position.HasValue) return new List<InputState>();
|
||||
|
||||
var action = new List<CatchAction>();
|
||||
var actions = new List<CatchAction>();
|
||||
|
||||
if (CurrentFrame.ButtonState == ReplayButtonState.Left1)
|
||||
action.Add(CatchAction.Dash);
|
||||
if (CurrentFrame.Dashing)
|
||||
actions.Add(CatchAction.Dash);
|
||||
|
||||
if (Position.Value.X > CurrentFrame.Position.X)
|
||||
action.Add(CatchAction.MoveRight);
|
||||
else if (Position.Value.X < CurrentFrame.Position.X)
|
||||
action.Add(CatchAction.MoveLeft);
|
||||
if (Position.Value > CurrentFrame.Position)
|
||||
actions.Add(CatchAction.MoveRight);
|
||||
else if (Position.Value < CurrentFrame.Position)
|
||||
actions.Add(CatchAction.MoveLeft);
|
||||
|
||||
return new List<InputState>
|
||||
{
|
||||
new CatchReplayState
|
||||
{
|
||||
PressedActions = action,
|
||||
CatcherX = Position.Value.X
|
||||
PressedActions = actions,
|
||||
CatcherX = Position.Value
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,17 +1,34 @@
|
||||
// Copyright (c) 2007-2018 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.Catch.UI;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Replays.Legacy;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Replays
|
||||
{
|
||||
public class CatchReplayFrame : ReplayFrame
|
||||
public class CatchReplayFrame : ReplayFrame, IConvertibleReplayFrame
|
||||
{
|
||||
public override bool IsImportant => MouseX > 0;
|
||||
public float Position;
|
||||
public bool Dashing;
|
||||
|
||||
public CatchReplayFrame(double time, float? x = null, ReplayButtonState button = ReplayButtonState.None)
|
||||
: base(time, x ?? -1, null, button)
|
||||
public CatchReplayFrame()
|
||||
{
|
||||
}
|
||||
|
||||
public CatchReplayFrame(double time, float? position = null, bool dashing = false)
|
||||
: base(time)
|
||||
{
|
||||
Position = position ?? -1;
|
||||
Dashing = dashing;
|
||||
}
|
||||
|
||||
public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap)
|
||||
{
|
||||
Position = legacyFrame.Position.X / CatchPlayfield.BASE_WIDTH;
|
||||
Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,957 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 500.0,
|
||||
"Position": 96.0
|
||||
}, {
|
||||
"StartTime": 562.0,
|
||||
"Position": 100.84
|
||||
}, {
|
||||
"StartTime": 625.0,
|
||||
"Position": 125.0
|
||||
}, {
|
||||
"StartTime": 687.0,
|
||||
"Position": 152.84
|
||||
}, {
|
||||
"StartTime": 750.0,
|
||||
"Position": 191.0
|
||||
}, {
|
||||
"StartTime": 812.0,
|
||||
"Position": 212.84
|
||||
}, {
|
||||
"StartTime": 875.0,
|
||||
"Position": 217.0
|
||||
}, {
|
||||
"StartTime": 937.0,
|
||||
"Position": 234.84
|
||||
}, {
|
||||
"StartTime": 1000.0,
|
||||
"Position": 256.0
|
||||
}, {
|
||||
"StartTime": 1062.0,
|
||||
"Position": 267.84
|
||||
}, {
|
||||
"StartTime": 1125.0,
|
||||
"Position": 284.0
|
||||
}, {
|
||||
"StartTime": 1187.0,
|
||||
"Position": 311.84
|
||||
}, {
|
||||
"StartTime": 1250.0,
|
||||
"Position": 350.0
|
||||
}, {
|
||||
"StartTime": 1312.0,
|
||||
"Position": 359.84
|
||||
}, {
|
||||
"StartTime": 1375.0,
|
||||
"Position": 367.0
|
||||
}, {
|
||||
"StartTime": 1437.0,
|
||||
"Position": 400.84
|
||||
}, {
|
||||
"StartTime": 1500.0,
|
||||
"Position": 416.0
|
||||
}, {
|
||||
"StartTime": 1562.0,
|
||||
"Position": 377.159973
|
||||
}, {
|
||||
"StartTime": 1625.0,
|
||||
"Position": 367.0
|
||||
}, {
|
||||
"StartTime": 1687.0,
|
||||
"Position": 374.159973
|
||||
}, {
|
||||
"StartTime": 1750.0,
|
||||
"Position": 353.0
|
||||
}, {
|
||||
"StartTime": 1812.0,
|
||||
"Position": 329.159973
|
||||
}, {
|
||||
"StartTime": 1875.0,
|
||||
"Position": 288.0
|
||||
}, {
|
||||
"StartTime": 1937.0,
|
||||
"Position": 259.159973
|
||||
}, {
|
||||
"StartTime": 2000.0,
|
||||
"Position": 256.0
|
||||
}, {
|
||||
"StartTime": 2058.0,
|
||||
"Position": 232.44
|
||||
}, {
|
||||
"StartTime": 2116.0,
|
||||
"Position": 222.879974
|
||||
}, {
|
||||
"StartTime": 2174.0,
|
||||
"Position": 185.319992
|
||||
}, {
|
||||
"StartTime": 2232.0,
|
||||
"Position": 177.76001
|
||||
}, {
|
||||
"StartTime": 2290.0,
|
||||
"Position": 162.200012
|
||||
}, {
|
||||
"StartTime": 2348.0,
|
||||
"Position": 158.639984
|
||||
}, {
|
||||
"StartTime": 2406.0,
|
||||
"Position": 111.079994
|
||||
}, {
|
||||
"StartTime": 2500.0,
|
||||
"Position": 96.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 3000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 3000.0,
|
||||
"Position": 18.0
|
||||
}, {
|
||||
"StartTime": 3062.0,
|
||||
"Position": 482.0
|
||||
}, {
|
||||
"StartTime": 3125.0,
|
||||
"Position": 243.0
|
||||
}, {
|
||||
"StartTime": 3187.0,
|
||||
"Position": 332.0
|
||||
}, {
|
||||
"StartTime": 3250.0,
|
||||
"Position": 477.0
|
||||
}, {
|
||||
"StartTime": 3312.0,
|
||||
"Position": 376.0
|
||||
}, {
|
||||
"StartTime": 3375.0,
|
||||
"Position": 104.0
|
||||
}, {
|
||||
"StartTime": 3437.0,
|
||||
"Position": 156.0
|
||||
}, {
|
||||
"StartTime": 3500.0,
|
||||
"Position": 135.0
|
||||
}, {
|
||||
"StartTime": 3562.0,
|
||||
"Position": 256.0
|
||||
}, {
|
||||
"StartTime": 3625.0,
|
||||
"Position": 360.0
|
||||
}, {
|
||||
"StartTime": 3687.0,
|
||||
"Position": 199.0
|
||||
}, {
|
||||
"StartTime": 3750.0,
|
||||
"Position": 239.0
|
||||
}, {
|
||||
"StartTime": 3812.0,
|
||||
"Position": 326.0
|
||||
}, {
|
||||
"StartTime": 3875.0,
|
||||
"Position": 393.0
|
||||
}, {
|
||||
"StartTime": 3937.0,
|
||||
"Position": 470.0
|
||||
}, {
|
||||
"StartTime": 4000.0,
|
||||
"Position": 136.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 4500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 4500.0,
|
||||
"Position": 317.0
|
||||
}, {
|
||||
"StartTime": 4562.0,
|
||||
"Position": 354.0
|
||||
}, {
|
||||
"StartTime": 4625.0,
|
||||
"Position": 414.0
|
||||
}, {
|
||||
"StartTime": 4687.0,
|
||||
"Position": 39.0
|
||||
}, {
|
||||
"StartTime": 4750.0,
|
||||
"Position": 172.0
|
||||
}, {
|
||||
"StartTime": 4812.0,
|
||||
"Position": 479.0
|
||||
}, {
|
||||
"StartTime": 4875.0,
|
||||
"Position": 18.0
|
||||
}, {
|
||||
"StartTime": 4937.0,
|
||||
"Position": 151.0
|
||||
}, {
|
||||
"StartTime": 5000.0,
|
||||
"Position": 342.0
|
||||
}, {
|
||||
"StartTime": 5062.0,
|
||||
"Position": 400.0
|
||||
}, {
|
||||
"StartTime": 5125.0,
|
||||
"Position": 420.0
|
||||
}, {
|
||||
"StartTime": 5187.0,
|
||||
"Position": 90.0
|
||||
}, {
|
||||
"StartTime": 5250.0,
|
||||
"Position": 220.0
|
||||
}, {
|
||||
"StartTime": 5312.0,
|
||||
"Position": 80.0
|
||||
}, {
|
||||
"StartTime": 5375.0,
|
||||
"Position": 421.0
|
||||
}, {
|
||||
"StartTime": 5437.0,
|
||||
"Position": 473.0
|
||||
}, {
|
||||
"StartTime": 5500.0,
|
||||
"Position": 97.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 6000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 6000.0,
|
||||
"Position": 105.0
|
||||
}, {
|
||||
"StartTime": 6062.0,
|
||||
"Position": 249.0
|
||||
}, {
|
||||
"StartTime": 6125.0,
|
||||
"Position": 163.0
|
||||
}, {
|
||||
"StartTime": 6187.0,
|
||||
"Position": 194.0
|
||||
}, {
|
||||
"StartTime": 6250.0,
|
||||
"Position": 106.0
|
||||
}, {
|
||||
"StartTime": 6312.0,
|
||||
"Position": 212.0
|
||||
}, {
|
||||
"StartTime": 6375.0,
|
||||
"Position": 257.0
|
||||
}, {
|
||||
"StartTime": 6437.0,
|
||||
"Position": 461.0
|
||||
}, {
|
||||
"StartTime": 6500.0,
|
||||
"Position": 79.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 7000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 7000.0,
|
||||
"Position": 256.0
|
||||
}, {
|
||||
"StartTime": 7062.0,
|
||||
"Position": 294.84
|
||||
}, {
|
||||
"StartTime": 7125.0,
|
||||
"Position": 279.0
|
||||
}, {
|
||||
"StartTime": 7187.0,
|
||||
"Position": 309.84
|
||||
}, {
|
||||
"StartTime": 7250.0,
|
||||
"Position": 336.0
|
||||
}, {
|
||||
"StartTime": 7312.0,
|
||||
"Position": 322.16
|
||||
}, {
|
||||
"StartTime": 7375.0,
|
||||
"Position": 308.0
|
||||
}, {
|
||||
"StartTime": 7437.0,
|
||||
"Position": 263.16
|
||||
}, {
|
||||
"StartTime": 7500.0,
|
||||
"Position": 256.0
|
||||
}, {
|
||||
"StartTime": 7562.0,
|
||||
"Position": 261.84
|
||||
}, {
|
||||
"StartTime": 7625.0,
|
||||
"Position": 277.0
|
||||
}, {
|
||||
"StartTime": 7687.0,
|
||||
"Position": 318.84
|
||||
}, {
|
||||
"StartTime": 7750.0,
|
||||
"Position": 336.0
|
||||
}, {
|
||||
"StartTime": 7803.0,
|
||||
"Position": 305.04
|
||||
}, {
|
||||
"StartTime": 7857.0,
|
||||
"Position": 307.76
|
||||
}, {
|
||||
"StartTime": 7910.0,
|
||||
"Position": 297.8
|
||||
}, {
|
||||
"StartTime": 8000.0,
|
||||
"Position": 256.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 8500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 8500.0,
|
||||
"Position": 32.0
|
||||
}, {
|
||||
"StartTime": 8562.0,
|
||||
"Position": 22.8515015
|
||||
}, {
|
||||
"StartTime": 8625.0,
|
||||
"Position": 28.5659637
|
||||
}, {
|
||||
"StartTime": 8687.0,
|
||||
"Position": 50.3433228
|
||||
}, {
|
||||
"StartTime": 8750.0,
|
||||
"Position": 56.58974
|
||||
}, {
|
||||
"StartTime": 8812.0,
|
||||
"Position": 64.23422
|
||||
}, {
|
||||
"StartTime": 8875.0,
|
||||
"Position": 67.7117844
|
||||
}, {
|
||||
"StartTime": 8937.0,
|
||||
"Position": 90.52607
|
||||
}, {
|
||||
"StartTime": 9000.0,
|
||||
"Position": 101.81015
|
||||
}, {
|
||||
"StartTime": 9062.0,
|
||||
"Position": 113.478188
|
||||
}, {
|
||||
"StartTime": 9125.0,
|
||||
"Position": 159.414444
|
||||
}, {
|
||||
"StartTime": 9187.0,
|
||||
"Position": 155.1861
|
||||
}, {
|
||||
"StartTime": 9250.0,
|
||||
"Position": 179.600418
|
||||
}, {
|
||||
"StartTime": 9312.0,
|
||||
"Position": 212.293015
|
||||
}, {
|
||||
"StartTime": 9375.0,
|
||||
"Position": 197.2076
|
||||
}, {
|
||||
"StartTime": 9437.0,
|
||||
"Position": 243.438324
|
||||
}, {
|
||||
"StartTime": 9500.0,
|
||||
"Position": 237.2304
|
||||
}, {
|
||||
"StartTime": 9562.0,
|
||||
"Position": 241.253983
|
||||
}, {
|
||||
"StartTime": 9625.0,
|
||||
"Position": 258.950623
|
||||
}, {
|
||||
"StartTime": 9687.0,
|
||||
"Position": 253.3786
|
||||
}, {
|
||||
"StartTime": 9750.0,
|
||||
"Position": 270.8865
|
||||
}, {
|
||||
"StartTime": 9812.0,
|
||||
"Position": 244.38974
|
||||
}, {
|
||||
"StartTime": 9875.0,
|
||||
"Position": 242.701874
|
||||
}, {
|
||||
"StartTime": 9937.0,
|
||||
"Position": 256.2331
|
||||
}, {
|
||||
"StartTime": 10000.0,
|
||||
"Position": 270.339874
|
||||
}, {
|
||||
"StartTime": 10062.0,
|
||||
"Position": 275.9349
|
||||
}, {
|
||||
"StartTime": 10125.0,
|
||||
"Position": 297.2969
|
||||
}, {
|
||||
"StartTime": 10187.0,
|
||||
"Position": 307.834137
|
||||
}, {
|
||||
"StartTime": 10250.0,
|
||||
"Position": 321.6449
|
||||
}, {
|
||||
"StartTime": 10312.0,
|
||||
"Position": 357.746338
|
||||
}, {
|
||||
"StartTime": 10375.0,
|
||||
"Position": 358.21875
|
||||
}, {
|
||||
"StartTime": 10437.0,
|
||||
"Position": 394.943
|
||||
}, {
|
||||
"StartTime": 10500.0,
|
||||
"Position": 401.0588
|
||||
}, {
|
||||
"StartTime": 10558.0,
|
||||
"Position": 418.21347
|
||||
}, {
|
||||
"StartTime": 10616.0,
|
||||
"Position": 424.6034
|
||||
}, {
|
||||
"StartTime": 10674.0,
|
||||
"Position": 455.835754
|
||||
}, {
|
||||
"StartTime": 10732.0,
|
||||
"Position": 477.5042
|
||||
}, {
|
||||
"StartTime": 10790.0,
|
||||
"Position": 476.290955
|
||||
}, {
|
||||
"StartTime": 10848.0,
|
||||
"Position": 470.943237
|
||||
}, {
|
||||
"StartTime": 10906.0,
|
||||
"Position": 503.3372
|
||||
}, {
|
||||
"StartTime": 10999.0,
|
||||
"Position": 508.166229
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 11500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 11500.0,
|
||||
"Position": 321.0
|
||||
}, {
|
||||
"StartTime": 11562.0,
|
||||
"Position": 17.0
|
||||
}, {
|
||||
"StartTime": 11625.0,
|
||||
"Position": 173.0
|
||||
}, {
|
||||
"StartTime": 11687.0,
|
||||
"Position": 170.0
|
||||
}, {
|
||||
"StartTime": 11750.0,
|
||||
"Position": 447.0
|
||||
}, {
|
||||
"StartTime": 11812.0,
|
||||
"Position": 218.0
|
||||
}, {
|
||||
"StartTime": 11875.0,
|
||||
"Position": 394.0
|
||||
}, {
|
||||
"StartTime": 11937.0,
|
||||
"Position": 46.0
|
||||
}, {
|
||||
"StartTime": 12000.0,
|
||||
"Position": 480.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 12500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 12500.0,
|
||||
"Position": 512.0
|
||||
}, {
|
||||
"StartTime": 12562.0,
|
||||
"Position": 491.3132
|
||||
}, {
|
||||
"StartTime": 12625.0,
|
||||
"Position": 484.3089
|
||||
}, {
|
||||
"StartTime": 12687.0,
|
||||
"Position": 454.6221
|
||||
}, {
|
||||
"StartTime": 12750.0,
|
||||
"Position": 433.617767
|
||||
}, {
|
||||
"StartTime": 12812.0,
|
||||
"Position": 399.930969
|
||||
}, {
|
||||
"StartTime": 12875.0,
|
||||
"Position": 395.926666
|
||||
}, {
|
||||
"StartTime": 12937.0,
|
||||
"Position": 361.239868
|
||||
}, {
|
||||
"StartTime": 13000.0,
|
||||
"Position": 353.235535
|
||||
}, {
|
||||
"StartTime": 13062.0,
|
||||
"Position": 314.548767
|
||||
}, {
|
||||
"StartTime": 13125.0,
|
||||
"Position": 315.544434
|
||||
}, {
|
||||
"StartTime": 13187.0,
|
||||
"Position": 288.857635
|
||||
}, {
|
||||
"StartTime": 13250.0,
|
||||
"Position": 254.853333
|
||||
}, {
|
||||
"StartTime": 13312.0,
|
||||
"Position": 239.166534
|
||||
}, {
|
||||
"StartTime": 13375.0,
|
||||
"Position": 240.1622
|
||||
}, {
|
||||
"StartTime": 13437.0,
|
||||
"Position": 212.4754
|
||||
}, {
|
||||
"StartTime": 13500.0,
|
||||
"Position": 194.471069
|
||||
}, {
|
||||
"StartTime": 13562.0,
|
||||
"Position": 161.784271
|
||||
}, {
|
||||
"StartTime": 13625.0,
|
||||
"Position": 145.779968
|
||||
}, {
|
||||
"StartTime": 13687.0,
|
||||
"Position": 129.09314
|
||||
}, {
|
||||
"StartTime": 13750.0,
|
||||
"Position": 104.088837
|
||||
}, {
|
||||
"StartTime": 13812.0,
|
||||
"Position": 95.40204
|
||||
}, {
|
||||
"StartTime": 13875.0,
|
||||
"Position": 61.3977356
|
||||
}, {
|
||||
"StartTime": 13937.0,
|
||||
"Position": 56.710907
|
||||
}, {
|
||||
"StartTime": 14000.0,
|
||||
"Position": 35.7066345
|
||||
}, {
|
||||
"StartTime": 14062.0,
|
||||
"Position": 5.019806
|
||||
}, {
|
||||
"StartTime": 14125.0,
|
||||
"Position": 0.0
|
||||
}, {
|
||||
"StartTime": 14187.0,
|
||||
"Position": 39.7696266
|
||||
}, {
|
||||
"StartTime": 14250.0,
|
||||
"Position": 23.0119171
|
||||
}, {
|
||||
"StartTime": 14312.0,
|
||||
"Position": 75.94882
|
||||
}, {
|
||||
"StartTime": 14375.0,
|
||||
"Position": 98.19112
|
||||
}, {
|
||||
"StartTime": 14437.0,
|
||||
"Position": 82.12803
|
||||
}, {
|
||||
"StartTime": 14500.0,
|
||||
"Position": 118.370323
|
||||
}, {
|
||||
"StartTime": 14562.0,
|
||||
"Position": 149.307236
|
||||
}, {
|
||||
"StartTime": 14625.0,
|
||||
"Position": 168.549515
|
||||
}, {
|
||||
"StartTime": 14687.0,
|
||||
"Position": 190.486435
|
||||
}, {
|
||||
"StartTime": 14750.0,
|
||||
"Position": 186.728714
|
||||
}, {
|
||||
"StartTime": 14812.0,
|
||||
"Position": 199.665634
|
||||
}, {
|
||||
"StartTime": 14875.0,
|
||||
"Position": 228.907928
|
||||
}, {
|
||||
"StartTime": 14937.0,
|
||||
"Position": 264.844849
|
||||
}, {
|
||||
"StartTime": 15000.0,
|
||||
"Position": 271.087128
|
||||
}, {
|
||||
"StartTime": 15062.0,
|
||||
"Position": 290.024017
|
||||
}, {
|
||||
"StartTime": 15125.0,
|
||||
"Position": 302.266327
|
||||
}, {
|
||||
"StartTime": 15187.0,
|
||||
"Position": 344.203247
|
||||
}, {
|
||||
"StartTime": 15250.0,
|
||||
"Position": 356.445526
|
||||
}, {
|
||||
"StartTime": 15312.0,
|
||||
"Position": 359.382446
|
||||
}, {
|
||||
"StartTime": 15375.0,
|
||||
"Position": 401.624725
|
||||
}, {
|
||||
"StartTime": 15437.0,
|
||||
"Position": 388.561646
|
||||
}, {
|
||||
"StartTime": 15500.0,
|
||||
"Position": 423.803925
|
||||
}, {
|
||||
"StartTime": 15562.0,
|
||||
"Position": 425.740845
|
||||
}, {
|
||||
"StartTime": 15625.0,
|
||||
"Position": 449.983124
|
||||
}, {
|
||||
"StartTime": 15687.0,
|
||||
"Position": 468.920044
|
||||
}, {
|
||||
"StartTime": 15750.0,
|
||||
"Position": 492.162323
|
||||
}, {
|
||||
"StartTime": 15812.0,
|
||||
"Position": 506.784332
|
||||
}, {
|
||||
"StartTime": 15875.0,
|
||||
"Position": 474.226227
|
||||
}, {
|
||||
"StartTime": 15937.0,
|
||||
"Position": 482.978638
|
||||
}, {
|
||||
"StartTime": 16000.0,
|
||||
"Position": 446.420532
|
||||
}, {
|
||||
"StartTime": 16058.0,
|
||||
"Position": 418.4146
|
||||
}, {
|
||||
"StartTime": 16116.0,
|
||||
"Position": 425.408844
|
||||
}, {
|
||||
"StartTime": 16174.0,
|
||||
"Position": 383.402924
|
||||
}, {
|
||||
"StartTime": 16232.0,
|
||||
"Position": 363.397156
|
||||
}, {
|
||||
"StartTime": 16290.0,
|
||||
"Position": 343.391235
|
||||
}, {
|
||||
"StartTime": 16348.0,
|
||||
"Position": 328.385468
|
||||
}, {
|
||||
"StartTime": 16406.0,
|
||||
"Position": 322.3797
|
||||
}, {
|
||||
"StartTime": 16500.0,
|
||||
"Position": 291.1977
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 17000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 17000.0,
|
||||
"Position": 256.0
|
||||
}, {
|
||||
"StartTime": 17062.0,
|
||||
"Position": 228.16
|
||||
}, {
|
||||
"StartTime": 17125.0,
|
||||
"Position": 234.0
|
||||
}, {
|
||||
"StartTime": 17187.0,
|
||||
"Position": 202.16
|
||||
}, {
|
||||
"StartTime": 17250.0,
|
||||
"Position": 176.0
|
||||
}, {
|
||||
"StartTime": 17312.0,
|
||||
"Position": 210.84
|
||||
}, {
|
||||
"StartTime": 17375.0,
|
||||
"Position": 221.0
|
||||
}, {
|
||||
"StartTime": 17437.0,
|
||||
"Position": 219.84
|
||||
}, {
|
||||
"StartTime": 17500.0,
|
||||
"Position": 256.0
|
||||
}, {
|
||||
"StartTime": 17562.0,
|
||||
"Position": 219.16
|
||||
}, {
|
||||
"StartTime": 17625.0,
|
||||
"Position": 228.0
|
||||
}, {
|
||||
"StartTime": 17687.0,
|
||||
"Position": 203.16
|
||||
}, {
|
||||
"StartTime": 17750.0,
|
||||
"Position": 176.0
|
||||
}, {
|
||||
"StartTime": 17803.0,
|
||||
"Position": 174.959991
|
||||
}, {
|
||||
"StartTime": 17857.0,
|
||||
"Position": 214.23999
|
||||
}, {
|
||||
"StartTime": 17910.0,
|
||||
"Position": 228.200012
|
||||
}, {
|
||||
"StartTime": 18000.0,
|
||||
"Position": 256.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 18500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 18500.0,
|
||||
"Position": 362.0
|
||||
}, {
|
||||
"StartTime": 18559.0,
|
||||
"Position": 249.0
|
||||
}, {
|
||||
"StartTime": 18618.0,
|
||||
"Position": 357.0
|
||||
}, {
|
||||
"StartTime": 18678.0,
|
||||
"Position": 167.0
|
||||
}, {
|
||||
"StartTime": 18737.0,
|
||||
"Position": 477.0
|
||||
}, {
|
||||
"StartTime": 18796.0,
|
||||
"Position": 411.0
|
||||
}, {
|
||||
"StartTime": 18856.0,
|
||||
"Position": 254.0
|
||||
}, {
|
||||
"StartTime": 18915.0,
|
||||
"Position": 308.0
|
||||
}, {
|
||||
"StartTime": 18975.0,
|
||||
"Position": 399.0
|
||||
}, {
|
||||
"StartTime": 19034.0,
|
||||
"Position": 176.0
|
||||
}, {
|
||||
"StartTime": 19093.0,
|
||||
"Position": 14.0
|
||||
}, {
|
||||
"StartTime": 19153.0,
|
||||
"Position": 258.0
|
||||
}, {
|
||||
"StartTime": 19212.0,
|
||||
"Position": 221.0
|
||||
}, {
|
||||
"StartTime": 19271.0,
|
||||
"Position": 481.0
|
||||
}, {
|
||||
"StartTime": 19331.0,
|
||||
"Position": 92.0
|
||||
}, {
|
||||
"StartTime": 19390.0,
|
||||
"Position": 211.0
|
||||
}, {
|
||||
"StartTime": 19450.0,
|
||||
"Position": 135.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 19875.0,
|
||||
"Objects": [{
|
||||
"StartTime": 19875.0,
|
||||
"Position": 216.0
|
||||
}, {
|
||||
"StartTime": 19937.0,
|
||||
"Position": 215.307053
|
||||
}, {
|
||||
"StartTime": 20000.0,
|
||||
"Position": 236.036865
|
||||
}, {
|
||||
"StartTime": 20062.0,
|
||||
"Position": 236.312088
|
||||
}, {
|
||||
"StartTime": 20125.0,
|
||||
"Position": 235.838928
|
||||
}, {
|
||||
"StartTime": 20187.0,
|
||||
"Position": 269.9743
|
||||
}, {
|
||||
"StartTime": 20250.0,
|
||||
"Position": 285.999146
|
||||
}, {
|
||||
"StartTime": 20312.0,
|
||||
"Position": 283.669067
|
||||
}, {
|
||||
"StartTime": 20375.0,
|
||||
"Position": 317.446747
|
||||
}, {
|
||||
"StartTime": 20437.0,
|
||||
"Position": 330.750275
|
||||
}, {
|
||||
"StartTime": 20500.0,
|
||||
"Position": 344.0156
|
||||
}, {
|
||||
"StartTime": 20562.0,
|
||||
"Position": 318.472168
|
||||
}, {
|
||||
"StartTime": 20625.0,
|
||||
"Position": 309.165466
|
||||
}, {
|
||||
"StartTime": 20687.0,
|
||||
"Position": 317.044617
|
||||
}, {
|
||||
"StartTime": 20750.0,
|
||||
"Position": 280.457367
|
||||
}, {
|
||||
"StartTime": 20812.0,
|
||||
"Position": 272.220581
|
||||
}, {
|
||||
"StartTime": 20875.0,
|
||||
"Position": 270.3294
|
||||
}, {
|
||||
"StartTime": 20937.0,
|
||||
"Position": 262.57605
|
||||
}, {
|
||||
"StartTime": 21000.0,
|
||||
"Position": 244.803329
|
||||
}, {
|
||||
"StartTime": 21062.0,
|
||||
"Position": 215.958359
|
||||
}, {
|
||||
"StartTime": 21125.0,
|
||||
"Position": 177.79332
|
||||
}, {
|
||||
"StartTime": 21187.0,
|
||||
"Position": 190.948349
|
||||
}, {
|
||||
"StartTime": 21250.0,
|
||||
"Position": 158.78334
|
||||
}, {
|
||||
"StartTime": 21312.0,
|
||||
"Position": 136.93837
|
||||
}, {
|
||||
"StartTime": 21375.0,
|
||||
"Position": 119.121056
|
||||
}, {
|
||||
"StartTime": 21437.0,
|
||||
"Position": 132.387573
|
||||
}, {
|
||||
"StartTime": 21500.0,
|
||||
"Position": 124.503014
|
||||
}, {
|
||||
"StartTime": 21562.0,
|
||||
"Position": 118.749374
|
||||
}, {
|
||||
"StartTime": 21625.0,
|
||||
"Position": 123.165535
|
||||
}, {
|
||||
"StartTime": 21687.0,
|
||||
"Position": 96.02999
|
||||
}, {
|
||||
"StartTime": 21750.0,
|
||||
"Position": 118.547928
|
||||
}, {
|
||||
"StartTime": 21812.0,
|
||||
"Position": 128.856232
|
||||
}, {
|
||||
"StartTime": 21875.0,
|
||||
"Position": 124.28746
|
||||
}, {
|
||||
"StartTime": 21937.0,
|
||||
"Position": 150.754929
|
||||
}, {
|
||||
"StartTime": 22000.0,
|
||||
"Position": 149.528732
|
||||
}, {
|
||||
"StartTime": 22062.0,
|
||||
"Position": 145.1691
|
||||
}, {
|
||||
"StartTime": 22125.0,
|
||||
"Position": 182.802155
|
||||
}, {
|
||||
"StartTime": 22187.0,
|
||||
"Position": 178.6452
|
||||
}, {
|
||||
"StartTime": 22250.0,
|
||||
"Position": 213.892181
|
||||
}, {
|
||||
"StartTime": 22312.0,
|
||||
"Position": 218.713028
|
||||
}, {
|
||||
"StartTime": 22375.0,
|
||||
"Position": 240.4715
|
||||
}, {
|
||||
"StartTime": 22437.0,
|
||||
"Position": 239.371887
|
||||
}, {
|
||||
"StartTime": 22500.0,
|
||||
"Position": 261.907257
|
||||
}, {
|
||||
"StartTime": 22562.0,
|
||||
"Position": 314.353119
|
||||
}, {
|
||||
"StartTime": 22625.0,
|
||||
"Position": 299.273376
|
||||
}, {
|
||||
"StartTime": 22687.0,
|
||||
"Position": 356.98288
|
||||
}, {
|
||||
"StartTime": 22750.0,
|
||||
"Position": 339.078552
|
||||
}, {
|
||||
"StartTime": 22812.0,
|
||||
"Position": 377.8958
|
||||
}, {
|
||||
"StartTime": 22875.0,
|
||||
"Position": 398.054047
|
||||
}, {
|
||||
"StartTime": 22937.0,
|
||||
"Position": 398.739441
|
||||
}, {
|
||||
"StartTime": 23000.0,
|
||||
"Position": 407.178467
|
||||
}, {
|
||||
"StartTime": 23062.0,
|
||||
"Position": 444.8687
|
||||
}, {
|
||||
"StartTime": 23125.0,
|
||||
"Position": 417.069977
|
||||
}, {
|
||||
"StartTime": 23187.0,
|
||||
"Position": 454.688477
|
||||
}, {
|
||||
"StartTime": 23250.0,
|
||||
"Position": 428.9612
|
||||
}, {
|
||||
"StartTime": 23312.0,
|
||||
"Position": 441.92807
|
||||
}, {
|
||||
"StartTime": 23375.0,
|
||||
"Position": 439.749878
|
||||
}, {
|
||||
"StartTime": 23433.0,
|
||||
"Position": 455.644684
|
||||
}, {
|
||||
"StartTime": 23491.0,
|
||||
"Position": 440.7359
|
||||
}, {
|
||||
"StartTime": 23549.0,
|
||||
"Position": 430.0944
|
||||
}, {
|
||||
"StartTime": 23607.0,
|
||||
"Position": 420.796173
|
||||
}, {
|
||||
"StartTime": 23665.0,
|
||||
"Position": 435.897461
|
||||
}, {
|
||||
"StartTime": 23723.0,
|
||||
"Position": 418.462555
|
||||
}, {
|
||||
"StartTime": 23781.0,
|
||||
"Position": 405.53775
|
||||
}, {
|
||||
"StartTime": 23874.0,
|
||||
"Position": 408.720825
|
||||
}]
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
osu file format v14
|
||||
|
||||
[Difficulty]
|
||||
HPDrainRate:6
|
||||
CircleSize:4
|
||||
OverallDifficulty:7
|
||||
ApproachRate:8.3
|
||||
SliderMultiplier:1.6
|
||||
SliderTickRate:1
|
||||
|
||||
[TimingPoints]
|
||||
500,500,4,2,1,50,1,0
|
||||
13426,-100,4,3,1,45,0,0
|
||||
14884,-100,4,2,1,50,0,0
|
||||
|
||||
[HitObjects]
|
||||
96,192,500,6,0,L|416:192,2,320
|
||||
256,192,3000,12,0,4000,0:0:0:0:
|
||||
256,192,4500,12,0,5500,0:0:0:0:
|
||||
256,192,6000,12,0,6500,0:0:0:0:
|
||||
256,128,7000,6,0,L|352:128,4,80
|
||||
32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800
|
||||
256,192,11500,12,0,12000,0:0:0:0:
|
||||
512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280
|
||||
256,256,17000,6,0,L|160:256,4,80
|
||||
256,192,18500,12,0,19450,0:0:0:0:
|
||||
216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2007-2018 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 NUnit.Framework;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
public class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
||||
|
||||
[TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2149")]
|
||||
public new void Test(string name)
|
||||
{
|
||||
base.Test(name);
|
||||
}
|
||||
|
||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
{
|
||||
if (hitObject is JuiceStream stream)
|
||||
{
|
||||
foreach (var nested in stream.NestedHitObjects)
|
||||
{
|
||||
yield return new ConvertValue
|
||||
{
|
||||
StartTime = nested.StartTime,
|
||||
Position = ((CatchHitObject)nested).X * CatchPlayfield.BASE_WIDTH
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new ConvertValue
|
||||
{
|
||||
StartTime = hitObject.StartTime,
|
||||
Position = ((CatchHitObject)hitObject).X * CatchPlayfield.BASE_WIDTH
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new CatchBeatmapConverter();
|
||||
}
|
||||
|
||||
public struct ConvertValue : IEquatable<ConvertValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// A sane value to account for osu!stable using ints everwhere.
|
||||
/// </summary>
|
||||
private const float conversion_lenience = 2;
|
||||
|
||||
public double StartTime;
|
||||
public float Position;
|
||||
|
||||
public bool Equals(ConvertValue other)
|
||||
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
|
||||
&& Precision.AlmostEquals(Position, other.Position, conversion_lenience);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ using osu.Game.Rulesets.Catch.UI;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseBananaShower : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
|
||||
@@ -6,7 +6,6 @@ using NUnit.Framework;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public TestCaseCatchPlayer() : base(new CatchRuleset())
|
||||
|
||||
@@ -8,7 +8,6 @@ using osu.Game.Rulesets.Catch.Objects;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public TestCaseCatchStacker()
|
||||
|
||||
@@ -13,7 +13,6 @@ using osu.Game.Tests.Visual;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseCatcherArea : OsuTestCase
|
||||
{
|
||||
private RulesetInfo catchRuleset;
|
||||
|
||||
@@ -16,7 +16,7 @@ using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
[TestFixture]
|
||||
public class TestCaseFruitObjects : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
|
||||
@@ -8,7 +8,6 @@ using osu.Game.Rulesets.Catch.Objects;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public TestCaseHyperdash()
|
||||
|
||||
@@ -5,7 +5,7 @@ using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
[TestFixture]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Input.Handlers;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||
@@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
|
||||
|
||||
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
|
||||
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
|
||||
|
||||
protected override BeatmapProcessor<CatchHitObject> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
|
||||
|
||||
|
||||
@@ -41,9 +41,21 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_green, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a84b7dcfb1391f7f, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_v2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.provider.e_sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9c301db686d0bd12, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Collections" />
|
||||
<Reference Include="System.Core" />
|
||||
@@ -83,6 +95,7 @@
|
||||
<Compile Include="Objects\Fruit.cs" />
|
||||
<Compile Include="Objects\TinyDroplet.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Tests\CatchBeatmapConversionTest.cs" />
|
||||
<Compile Include="Tests\TestCaseBananaShower.cs" />
|
||||
<Compile Include="Tests\TestCaseCatcherArea.cs" />
|
||||
<Compile Include="Tests\TestCaseCatchStacker.cs" />
|
||||
@@ -116,12 +129,20 @@
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\Testing\Beatmaps\basic-expected-conversion.json" />
|
||||
<EmbeddedResource Include="Resources\Testing\Beatmaps\basic.osu" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets'))" />
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" />
|
||||
</Project>
|
||||
@@ -2,5 +2,11 @@
|
||||
<packages>
|
||||
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.13" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.bundle_green" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.8" targetFramework="net461" />
|
||||
</packages>
|
||||
@@ -14,5 +14,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
/// The number of <see cref="Column"/>s which this stage contains.
|
||||
/// </summary>
|
||||
public int Columns;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the column index is a special column for this stage.
|
||||
/// </summary>
|
||||
/// <param name="column">The 0-based column index.</param>
|
||||
/// <returns>Whether the column is a special column.</returns>
|
||||
public bool IsSpecialColumn(int column) => Columns % 2 == 1 && column == Columns / 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
// Copyright (c) 2007-2018 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.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Judgements
|
||||
{
|
||||
public class HitWindows
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// PERFECT hit window at OD = 10.
|
||||
/// </summary>
|
||||
private const double perfect_min = 27.8;
|
||||
/// <summary>
|
||||
/// PERFECT hit window at OD = 5.
|
||||
/// </summary>
|
||||
private const double perfect_mid = 38.8;
|
||||
/// <summary>
|
||||
/// PERFECT hit window at OD = 0.
|
||||
/// </summary>
|
||||
private const double perfect_max = 44.8;
|
||||
|
||||
/// <summary>
|
||||
/// GREAT hit window at OD = 10.
|
||||
/// </summary>
|
||||
private const double great_min = 68;
|
||||
/// <summary>
|
||||
/// GREAT hit window at OD = 5.
|
||||
/// </summary>
|
||||
private const double great_mid = 98;
|
||||
/// <summary>
|
||||
/// GREAT hit window at OD = 0.
|
||||
/// </summary>
|
||||
private const double great_max = 128;
|
||||
|
||||
/// <summary>
|
||||
/// GOOD hit window at OD = 10.
|
||||
/// </summary>
|
||||
private const double good_min = 134;
|
||||
/// <summary>
|
||||
/// GOOD hit window at OD = 5.
|
||||
/// </summary>
|
||||
private const double good_mid = 164;
|
||||
/// <summary>
|
||||
/// GOOD hit window at OD = 0.
|
||||
/// </summary>
|
||||
private const double good_max = 194;
|
||||
|
||||
/// <summary>
|
||||
/// OK hit window at OD = 10.
|
||||
/// </summary>
|
||||
private const double ok_min = 194;
|
||||
/// <summary>
|
||||
/// OK hit window at OD = 5.
|
||||
/// </summary>
|
||||
private const double ok_mid = 224;
|
||||
/// <summary>
|
||||
/// OK hit window at OD = 0.
|
||||
/// </summary>
|
||||
private const double ok_max = 254;
|
||||
|
||||
/// <summary>
|
||||
/// BAD hit window at OD = 10.
|
||||
/// </summary>
|
||||
private const double bad_min = 242;
|
||||
/// <summary>
|
||||
/// BAD hit window at OD = 5.
|
||||
/// </summary>
|
||||
private const double bad_mid = 272;
|
||||
/// <summary>
|
||||
/// BAD hit window at OD = 0.
|
||||
/// </summary>
|
||||
private const double bad_max = 302;
|
||||
|
||||
/// <summary>
|
||||
/// MISS hit window at OD = 10.
|
||||
/// </summary>
|
||||
private const double miss_min = 316;
|
||||
/// <summary>
|
||||
/// MISS hit window at OD = 5.
|
||||
/// </summary>
|
||||
private const double miss_mid = 346;
|
||||
/// <summary>
|
||||
/// MISS hit window at OD = 0.
|
||||
/// </summary>
|
||||
private const double miss_max = 376;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Hit window for a PERFECT hit.
|
||||
/// </summary>
|
||||
public double Perfect = perfect_mid;
|
||||
|
||||
/// <summary>
|
||||
/// Hit window for a GREAT hit.
|
||||
/// </summary>
|
||||
public double Great = great_mid;
|
||||
|
||||
/// <summary>
|
||||
/// Hit window for a GOOD hit.
|
||||
/// </summary>
|
||||
public double Good = good_mid;
|
||||
|
||||
/// <summary>
|
||||
/// Hit window for an OK hit.
|
||||
/// </summary>
|
||||
public double Ok = ok_mid;
|
||||
|
||||
/// <summary>
|
||||
/// Hit window for a BAD hit.
|
||||
/// </summary>
|
||||
public double Bad = bad_mid;
|
||||
|
||||
/// <summary>
|
||||
/// Hit window for a MISS hit.
|
||||
/// </summary>
|
||||
public double Miss = miss_mid;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs default hit windows.
|
||||
/// </summary>
|
||||
public HitWindows()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs hit windows by fitting a parameter to a 2-part piecewise linear function for each hit window.
|
||||
/// </summary>
|
||||
/// <param name="difficulty">The parameter.</param>
|
||||
public HitWindows(double difficulty)
|
||||
{
|
||||
Perfect = BeatmapDifficulty.DifficultyRange(difficulty, perfect_max, perfect_mid, perfect_min);
|
||||
Great = BeatmapDifficulty.DifficultyRange(difficulty, great_max, great_mid, great_min);
|
||||
Good = BeatmapDifficulty.DifficultyRange(difficulty, good_max, good_mid, good_min);
|
||||
Ok = BeatmapDifficulty.DifficultyRange(difficulty, ok_max, ok_mid, ok_min);
|
||||
Bad = BeatmapDifficulty.DifficultyRange(difficulty, bad_max, bad_mid, bad_min);
|
||||
Miss = BeatmapDifficulty.DifficultyRange(difficulty, miss_max, miss_mid, miss_min);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the hit result for a time offset.
|
||||
/// </summary>
|
||||
/// <param name="hitOffset">The time offset.</param>
|
||||
/// <returns>The hit result, or null if the time offset results in a miss.</returns>
|
||||
public HitResult? ResultFor(double hitOffset)
|
||||
{
|
||||
if (hitOffset <= Perfect / 2)
|
||||
return HitResult.Perfect;
|
||||
if (hitOffset <= Great / 2)
|
||||
return HitResult.Great;
|
||||
if (hitOffset <= Good / 2)
|
||||
return HitResult.Good;
|
||||
if (hitOffset <= Ok / 2)
|
||||
return HitResult.Ok;
|
||||
if (hitOffset <= Bad / 2)
|
||||
return HitResult.Meh;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs new hit windows which have been multiplied by a value.
|
||||
/// </summary>
|
||||
/// <param name="windows">The original hit windows.</param>
|
||||
/// <param name="value">The value to multiply each hit window by.</param>
|
||||
public static HitWindows operator *(HitWindows windows, double value)
|
||||
{
|
||||
return new HitWindows
|
||||
{
|
||||
Perfect = windows.Perfect * value,
|
||||
Great = windows.Great * value,
|
||||
Good = windows.Good * value,
|
||||
Ok = windows.Ok * value,
|
||||
Bad = windows.Bad * value,
|
||||
Miss = windows.Miss * value
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs new hit windows which have been divided by a value.
|
||||
/// </summary>
|
||||
/// <param name="windows">The original hit windows.</param>
|
||||
/// <param name="value">The value to divide each hit window by.</param>
|
||||
public static HitWindows operator /(HitWindows windows, double value)
|
||||
{
|
||||
return new HitWindows
|
||||
{
|
||||
Perfect = windows.Perfect / value,
|
||||
Great = windows.Great / value,
|
||||
Good = windows.Good / value,
|
||||
Ok = windows.Ok / value,
|
||||
Bad = windows.Bad / value,
|
||||
Miss = windows.Miss / value
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Replays;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania
|
||||
{
|
||||
@@ -114,6 +116,8 @@ namespace osu.Game.Rulesets.Mania
|
||||
|
||||
public override int LegacyID => 3;
|
||||
|
||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();
|
||||
|
||||
public ManiaRuleset(RulesetInfo rulesetInfo = null)
|
||||
: base(rulesetInfo)
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils
|
||||
/// </summary>
|
||||
internal class FastRandom
|
||||
{
|
||||
private const double uint_to_real = 1.0 / (uint.MaxValue + 1.0);
|
||||
private const double int_to_real = 1.0 / (int.MaxValue + 1.0);
|
||||
private const uint int_mask = 0x7FFFFFFF;
|
||||
private const uint y = 842502087;
|
||||
private const uint z = 3579807591;
|
||||
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils
|
||||
/// Generates a random double value within the range [0, 1).
|
||||
/// </summary>
|
||||
/// <returns>The random value.</returns>
|
||||
public double NextDouble() => uint_to_real * NextUInt();
|
||||
public double NextDouble() => int_to_real * Next();
|
||||
|
||||
private uint bitBuffer;
|
||||
private int bitIndex = 32;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Replays;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
return new Score
|
||||
{
|
||||
User = new User { Username = "osu!topus!" },
|
||||
Replay = new ManiaAutoGenerator(beatmap).Generate(),
|
||||
Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -212,7 +211,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (timeOffset > HitObject.HitWindows.Bad / 2)
|
||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||
{
|
||||
AddJudgement(new HoldNoteTailJudgement
|
||||
{
|
||||
@@ -224,14 +223,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
return;
|
||||
}
|
||||
|
||||
double offset = Math.Abs(timeOffset);
|
||||
|
||||
if (offset > HitObject.HitWindows.Miss / 2)
|
||||
var result = HitObject.HitWindows.ResultFor(timeOffset);
|
||||
if (result == HitResult.None)
|
||||
return;
|
||||
|
||||
AddJudgement(new HoldNoteTailJudgement
|
||||
{
|
||||
Result = HitObject.HitWindows.ResultFor(offset) ?? HitResult.Miss,
|
||||
Result = result,
|
||||
HasBroken = holdNote.hasBroken
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Bindings;
|
||||
@@ -63,17 +62,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (timeOffset > HitObject.HitWindows.Bad / 2)
|
||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||
AddJudgement(new ManiaJudgement { Result = HitResult.Miss });
|
||||
return;
|
||||
}
|
||||
|
||||
double offset = Math.Abs(timeOffset);
|
||||
|
||||
if (offset > HitObject.HitWindows.Miss / 2)
|
||||
var result = HitObject.HitWindows.ResultFor(timeOffset);
|
||||
if (result == HitResult.None)
|
||||
return;
|
||||
|
||||
AddJudgement(new ManiaJudgement { Result = HitObject.HitWindows.ResultFor(offset) ?? HitResult.Miss });
|
||||
AddJudgement(new ManiaJudgement { Result = result });
|
||||
}
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2007-2018 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.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Mania.Objects.Types;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
@@ -9,5 +11,13 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
public abstract class ManiaHitObject : HitObject, IHasColumn
|
||||
{
|
||||
public virtual int Column { get; set; }
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
HitWindows.AllowsPerfect = true;
|
||||
HitWindows.AllowsOk = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects
|
||||
{
|
||||
/// <summary>
|
||||
@@ -13,17 +8,5 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
/// </summary>
|
||||
public class Note : ManiaHitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The key-press hit window for this note.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public HitWindows HitWindows { get; protected set; } = new HitWindows();
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
HitWindows = new HitWindows(difficulty.OverallDifficulty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
@@ -15,10 +15,31 @@ namespace osu.Game.Rulesets.Mania.Replays
|
||||
{
|
||||
public const double RELEASE_DELAY = 20;
|
||||
|
||||
public ManiaAutoGenerator(Beatmap<ManiaHitObject> beatmap)
|
||||
public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap;
|
||||
|
||||
private readonly ManiaAction[] columnActions;
|
||||
|
||||
public ManiaAutoGenerator(ManiaBeatmap beatmap)
|
||||
: base(beatmap)
|
||||
{
|
||||
Replay = new Replay { User = new User { Username = @"Autoplay" } };
|
||||
|
||||
columnActions = new ManiaAction[Beatmap.TotalColumns];
|
||||
|
||||
var normalAction = ManiaAction.Key1;
|
||||
var specialAction = ManiaAction.Special1;
|
||||
int totalCounter = 0;
|
||||
foreach (var stage in Beatmap.Stages)
|
||||
{
|
||||
for (int i = 0; i < stage.Columns; i++)
|
||||
{
|
||||
if (stage.IsSpecialColumn(i))
|
||||
columnActions[totalCounter] = specialAction++;
|
||||
else
|
||||
columnActions[totalCounter] = normalAction++;
|
||||
totalCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Replay Replay;
|
||||
@@ -30,18 +51,18 @@ namespace osu.Game.Rulesets.Mania.Replays
|
||||
|
||||
var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time);
|
||||
|
||||
int activeColumns = 0;
|
||||
var actions = new List<ManiaAction>();
|
||||
foreach (var group in pointGroups)
|
||||
{
|
||||
foreach (var point in group)
|
||||
{
|
||||
if (point is HitPoint)
|
||||
activeColumns |= 1 << point.Column;
|
||||
actions.Add(columnActions[point.Column]);
|
||||
if (point is ReleasePoint)
|
||||
activeColumns ^= 1 << point.Column;
|
||||
actions.Remove(columnActions[point.Column]);
|
||||
}
|
||||
|
||||
Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, activeColumns));
|
||||
Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray()));
|
||||
}
|
||||
|
||||
return Replay;
|
||||
|
||||
@@ -4,40 +4,19 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Replays
|
||||
{
|
||||
internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler
|
||||
internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler<ManiaReplayFrame>
|
||||
{
|
||||
private readonly ManiaRulesetContainer container;
|
||||
|
||||
public ManiaFramedReplayInputHandler(Replay replay, ManiaRulesetContainer container)
|
||||
public ManiaFramedReplayInputHandler(Replay replay)
|
||||
: base(replay)
|
||||
{
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
private ManiaPlayfield playfield;
|
||||
public override List<InputState> GetPendingStates()
|
||||
{
|
||||
var actions = new List<ManiaAction>();
|
||||
protected override bool IsImportant(ManiaReplayFrame frame) => frame.Actions.Any();
|
||||
|
||||
if (playfield == null)
|
||||
playfield = (ManiaPlayfield)container.Playfield;
|
||||
|
||||
int activeColumns = (int)(CurrentFrame.MouseX ?? 0);
|
||||
int counter = 0;
|
||||
while (activeColumns > 0)
|
||||
{
|
||||
if ((activeColumns & 1) > 0)
|
||||
actions.Add(playfield.Columns.ElementAt(counter).Action);
|
||||
counter++;
|
||||
activeColumns >>= 1;
|
||||
}
|
||||
|
||||
return new List<InputState> { new ReplayState<ManiaAction> { PressedActions = actions } };
|
||||
}
|
||||
public override List<InputState> GetPendingStates() => new List<InputState> { new ReplayState<ManiaAction> { PressedActions = CurrentFrame.Actions } };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,59 @@
|
||||
// Copyright (c) 2007-2018 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.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Replays.Legacy;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Replays
|
||||
{
|
||||
public class ManiaReplayFrame : ReplayFrame
|
||||
public class ManiaReplayFrame : ReplayFrame, IConvertibleReplayFrame
|
||||
{
|
||||
public override bool IsImportant => MouseX > 0;
|
||||
public List<ManiaAction> Actions = new List<ManiaAction>();
|
||||
|
||||
public ManiaReplayFrame(double time, int activeColumns)
|
||||
: base(time, activeColumns, null, ReplayButtonState.None)
|
||||
public ManiaReplayFrame()
|
||||
{
|
||||
}
|
||||
|
||||
public ManiaReplayFrame(double time, params ManiaAction[] actions)
|
||||
: base(time)
|
||||
{
|
||||
Actions.AddRange(actions);
|
||||
}
|
||||
|
||||
public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap)
|
||||
{
|
||||
// We don't need to fully convert, just create the converter
|
||||
var converter = new ManiaBeatmapConverter(beatmap.BeatmapInfo.RulesetID == 3, beatmap);
|
||||
|
||||
// NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling
|
||||
// elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage.
|
||||
|
||||
var stage = new StageDefinition { Columns = converter.TargetColumns };
|
||||
|
||||
var normalAction = ManiaAction.Key1;
|
||||
var specialAction = ManiaAction.Special1;
|
||||
|
||||
int activeColumns = (int)(legacyFrame.MouseX ?? 0);
|
||||
int counter = 0;
|
||||
while (activeColumns > 0)
|
||||
{
|
||||
var isSpecial = stage.IsSpecialColumn(counter);
|
||||
|
||||
if ((activeColumns & 1) > 0)
|
||||
Actions.Add(isSpecial ? specialAction : normalAction);
|
||||
|
||||
if (isSpecial)
|
||||
specialAction++;
|
||||
else
|
||||
normalAction++;
|
||||
|
||||
counter++;
|
||||
activeColumns >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 500,
|
||||
"Objects": [{
|
||||
"StartTime": 500,
|
||||
"EndTime": 2500,
|
||||
"Column": 0
|
||||
},
|
||||
{
|
||||
"StartTime": 1500,
|
||||
"EndTime": 2500,
|
||||
"Column": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StartTime": 3000,
|
||||
"Objects": [{
|
||||
"StartTime": 3000,
|
||||
"EndTime": 4000,
|
||||
"Column": 2
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 4500,
|
||||
"Objects": [{
|
||||
"StartTime": 4500,
|
||||
"EndTime": 5500,
|
||||
"Column": 4
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 6000,
|
||||
"Objects": [{
|
||||
"StartTime": 6000,
|
||||
"EndTime": 6500,
|
||||
"Column": 2
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 7000,
|
||||
"Objects": [{
|
||||
"StartTime": 7000,
|
||||
"EndTime": 8000,
|
||||
"Column": 2
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 8500,
|
||||
"Objects": [{
|
||||
"StartTime": 8500,
|
||||
"EndTime": 11000,
|
||||
"Column": 0
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 11500,
|
||||
"Objects": [{
|
||||
"StartTime": 11500,
|
||||
"EndTime": 12000,
|
||||
"Column": 1
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 12500,
|
||||
"Objects": [{
|
||||
"StartTime": 12500,
|
||||
"EndTime": 16500,
|
||||
"Column": 4
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 17000,
|
||||
"Objects": [{
|
||||
"StartTime": 17000,
|
||||
"EndTime": 18000,
|
||||
"Column": 2
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 18500,
|
||||
"Objects": [{
|
||||
"StartTime": 18500,
|
||||
"EndTime": 19450,
|
||||
"Column": 0
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 19875,
|
||||
"Objects": [{
|
||||
"StartTime": 19875,
|
||||
"EndTime": 23875,
|
||||
"Column": 1
|
||||
},
|
||||
{
|
||||
"StartTime": 19875,
|
||||
"EndTime": 23875,
|
||||
"Column": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
osu file format v14
|
||||
|
||||
[Difficulty]
|
||||
HPDrainRate:6
|
||||
CircleSize:4
|
||||
OverallDifficulty:7
|
||||
ApproachRate:8.3
|
||||
SliderMultiplier:1.6
|
||||
SliderTickRate:1
|
||||
|
||||
[TimingPoints]
|
||||
500,500,4,2,1,50,1,0
|
||||
13426,-100,4,3,1,45,0,0
|
||||
14884,-100,4,2,1,50,0,0
|
||||
|
||||
[HitObjects]
|
||||
96,192,500,6,0,L|416:192,2,320
|
||||
256,192,3000,12,0,4000,0:0:0:0:
|
||||
256,192,4500,12,0,5500,0:0:0:0:
|
||||
256,192,6000,12,0,6500,0:0:0:0:
|
||||
256,128,7000,6,0,L|352:128,4,80
|
||||
32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800
|
||||
256,192,11500,12,0,12000,0:0:0:0:
|
||||
512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280
|
||||
256,256,17000,6,0,L|160:256,4,80
|
||||
256,192,18500,12,0,19450,0:0:0:0:
|
||||
216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2007-2018 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 NUnit.Framework;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
public class ManiaBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
||||
|
||||
private bool isForCurrentRuleset;
|
||||
|
||||
[NonParallelizable]
|
||||
[TestCase("basic", false), Ignore("See: https://github.com/ppy/osu/issues/2150")]
|
||||
public void Test(string name, bool isForCurrentRuleset)
|
||||
{
|
||||
this.isForCurrentRuleset = isForCurrentRuleset;
|
||||
base.Test(name);
|
||||
}
|
||||
|
||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
{
|
||||
yield return new ConvertValue
|
||||
{
|
||||
StartTime = hitObject.StartTime,
|
||||
EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime,
|
||||
Column = ((ManiaHitObject)hitObject).Column
|
||||
};
|
||||
}
|
||||
|
||||
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new ManiaBeatmapConverter(isForCurrentRuleset, beatmap);
|
||||
}
|
||||
|
||||
public struct ConvertValue : IEquatable<ConvertValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// A sane value to account for osu!stable using ints everwhere.
|
||||
/// </summary>
|
||||
private const float conversion_lenience = 2;
|
||||
|
||||
public double StartTime;
|
||||
public double EndTime;
|
||||
public int Column;
|
||||
|
||||
public bool Equals(ConvertValue other)
|
||||
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
|
||||
&& Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience)
|
||||
&& Column == other.Column;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Replays;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
[TestFixture]
|
||||
public class TestCaseAutoGeneration : OsuTestCase
|
||||
{
|
||||
[Test]
|
||||
@@ -19,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
// | - |
|
||||
// | |
|
||||
|
||||
var beatmap = new Beatmap<ManiaHitObject>();
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 });
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
@@ -27,8 +29,8 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 0 has not been pressed");
|
||||
Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 0 has not been released");
|
||||
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -40,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
// | * |
|
||||
// | |
|
||||
|
||||
var beatmap = new Beatmap<ManiaHitObject>();
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 });
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
@@ -48,8 +50,8 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 0 has not been pressed");
|
||||
Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 0 has not been released");
|
||||
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -59,7 +61,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
// | - | - |
|
||||
// | | |
|
||||
|
||||
var beatmap = new Beatmap<ManiaHitObject>();
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 1000, Column = 1 });
|
||||
|
||||
@@ -68,8 +70,8 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||
Assert.AreEqual(3, generated.Frames[1].MouseX, "Keys 1 and 2 have not been pressed");
|
||||
Assert.AreEqual(0, generated.Frames[2].MouseX, "Keys 1 and 2 have not been released");
|
||||
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -81,7 +83,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
// | * | * |
|
||||
// | | |
|
||||
|
||||
var beatmap = new Beatmap<ManiaHitObject>();
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000, Column = 1 });
|
||||
|
||||
@@ -90,8 +92,8 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||
Assert.AreEqual(3, generated.Frames[1].MouseX, "Keys 1 and 2 have not been pressed");
|
||||
Assert.AreEqual(0, generated.Frames[2].MouseX, "Keys 1 and 2 have not been released");
|
||||
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -102,7 +104,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
// | - | |
|
||||
// | | |
|
||||
|
||||
var beatmap = new Beatmap<ManiaHitObject>();
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 2000, Column = 1 });
|
||||
|
||||
@@ -113,10 +115,10 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect first note release time");
|
||||
Assert.AreEqual(2000, generated.Frames[3].Time, "Incorrect second note hit time");
|
||||
Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
|
||||
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed");
|
||||
Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 1 has not been released");
|
||||
Assert.AreEqual(2, generated.Frames[3].MouseX, "Key 2 has not been pressed");
|
||||
Assert.AreEqual(0, generated.Frames[4].MouseX, "Key 2 has not been released");
|
||||
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released");
|
||||
Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -129,7 +131,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
// | * | |
|
||||
// | | |
|
||||
|
||||
var beatmap = new Beatmap<ManiaHitObject>();
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 2000, Duration = 2000, Column = 1 });
|
||||
|
||||
@@ -140,10 +142,11 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect first note release time");
|
||||
Assert.AreEqual(2000, generated.Frames[2].Time, "Incorrect second note hit time");
|
||||
Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
|
||||
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed");
|
||||
Assert.AreEqual(3, generated.Frames[2].MouseX, "Keys 1 and 2 have not been pressed");
|
||||
Assert.AreEqual(2, generated.Frames[3].MouseX, "Key 1 has not been released");
|
||||
Assert.AreEqual(0, generated.Frames[4].MouseX, "Key 2 has not been released");
|
||||
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
|
||||
Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key1), "Key1 has not been released");
|
||||
Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has been released");
|
||||
Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -155,7 +158,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
// | * | |
|
||||
// | | |
|
||||
|
||||
var beatmap = new Beatmap<ManiaHitObject>();
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY });
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 });
|
||||
|
||||
@@ -165,9 +168,12 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
|
||||
Assert.AreEqual(3000, generated.Frames[2].Time, "Incorrect second note press time + first note release time");
|
||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect second note release time");
|
||||
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed");
|
||||
Assert.AreEqual(2, generated.Frames[2].MouseX, "Key 1 has not been released or key 2 has not been pressed");
|
||||
Assert.AreEqual(0, generated.Frames[3].MouseX, "Keys 1 and 2 have not been released");
|
||||
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released");
|
||||
Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key2), "Key2 has not been pressed");
|
||||
Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been released");
|
||||
}
|
||||
|
||||
private bool checkContains(ReplayFrame frame, params ManiaAction[] actions) => actions.All(action => ((ManiaReplayFrame)frame).Actions.Contains(action));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ using OpenTK.Graphics;
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseManiaHitObjects : OsuTestCase
|
||||
{
|
||||
public TestCaseManiaHitObjects()
|
||||
|
||||
@@ -8,7 +8,9 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
@@ -19,7 +21,6 @@ using osu.Game.Tests.Visual;
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseManiaPlayfield : OsuTestCase
|
||||
{
|
||||
private const double start_time = 500;
|
||||
@@ -92,10 +93,17 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
});
|
||||
}
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
private void load(RulesetStore rulesets, SettingsStore settings)
|
||||
{
|
||||
maniaRuleset = rulesets.GetRuleset(3);
|
||||
|
||||
dependencies.Cache(new ManiaConfigManager(settings, maniaRuleset, 4));
|
||||
}
|
||||
|
||||
private ManiaPlayfield createPlayfield(int cols, bool inverted = false)
|
||||
|
||||
@@ -5,7 +5,7 @@ using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
[TestFixture]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
|
||||
@@ -11,6 +11,7 @@ using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Input.Handlers;
|
||||
using osu.Game.Rulesets.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
@@ -101,9 +102,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f);
|
||||
protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f);
|
||||
|
||||
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this);
|
||||
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
|
||||
|
||||
protected override IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => new ManiaConfigManager(settings, Ruleset.RulesetInfo, Variant);
|
||||
}
|
||||
|
||||
@@ -48,13 +48,11 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
private Color4 specialColumnColour;
|
||||
|
||||
private readonly int firstColumnIndex;
|
||||
private readonly StageDefinition definition;
|
||||
|
||||
public ManiaStage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
|
||||
: base(ScrollingDirection.Up)
|
||||
{
|
||||
this.firstColumnIndex = firstColumnIndex;
|
||||
this.definition = definition;
|
||||
|
||||
Name = "Stage";
|
||||
|
||||
@@ -131,7 +129,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
for (int i = 0; i < definition.Columns; i++)
|
||||
{
|
||||
var isSpecial = isSpecialColumn(i);
|
||||
var isSpecial = definition.IsSpecialColumn(i);
|
||||
var column = new Column
|
||||
{
|
||||
IsSpecial = isSpecial,
|
||||
@@ -160,13 +158,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
AddNested(c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the column index is a special column for this playfield.
|
||||
/// </summary>
|
||||
/// <param name="column">The 0-based column index.</param>
|
||||
/// <returns>Whether the column is a special column.</returns>
|
||||
private bool isSpecialColumn(int column) => definition.Columns % 2 == 1 && column == definition.Columns / 2;
|
||||
|
||||
public override void Add(DrawableHitObject h)
|
||||
{
|
||||
var maniaObject = (ManiaHitObject)h.HitObject;
|
||||
|
||||
@@ -41,9 +41,21 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_green, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a84b7dcfb1391f7f, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_v2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.provider.e_sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9c301db686d0bd12, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
@@ -64,7 +76,6 @@
|
||||
<Compile Include="Beatmaps\Patterns\Pattern.cs" />
|
||||
<Compile Include="Configuration\ManiaConfigManager.cs" />
|
||||
<Compile Include="MathUtils\FastRandom.cs" />
|
||||
<Compile Include="Judgements\HitWindows.cs" />
|
||||
<Compile Include="Judgements\HoldNoteTailJudgement.cs" />
|
||||
<Compile Include="Judgements\HoldNoteTickJudgement.cs" />
|
||||
<Compile Include="Judgements\ManiaJudgement.cs" />
|
||||
@@ -115,6 +126,7 @@
|
||||
<Compile Include="Objects\Note.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ManiaInputManager.cs" />
|
||||
<Compile Include="Tests\ManiaBeatmapConversionTest.cs" />
|
||||
<Compile Include="Tests\TestCaseAutoGeneration.cs" />
|
||||
<Compile Include="Tests\TestCaseManiaHitObjects.cs" />
|
||||
<Compile Include="Tests\TestCaseManiaPlayfield.cs" />
|
||||
@@ -148,12 +160,20 @@
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\Testing\Beatmaps\basic-expected-conversion.json" />
|
||||
<EmbeddedResource Include="Resources\Testing\Beatmaps\basic.osu" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets'))" />
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" />
|
||||
</Project>
|
||||
@@ -2,5 +2,11 @@
|
||||
<packages>
|
||||
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.13" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.bundle_green" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.8" targetFramework="net461" />
|
||||
</packages>
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Edit.Layers.Selection;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection
|
||||
{
|
||||
public class OsuHitObjectOverlayLayer : HitObjectOverlayLayer
|
||||
{
|
||||
protected override HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject)
|
||||
{
|
||||
switch (hitObject)
|
||||
{
|
||||
case DrawableHitCircle circle:
|
||||
return new HitCircleOverlay(circle);
|
||||
case DrawableSlider slider:
|
||||
return new SliderOverlay(slider);
|
||||
}
|
||||
|
||||
return base.CreateOverlayFor(hitObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2007-2018 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.Allocation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Edit.Layers.Selection;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
||||
{
|
||||
public class HitCircleOverlay : HitObjectOverlay
|
||||
{
|
||||
public HitCircleOverlay(DrawableHitCircle hitCircle)
|
||||
: base(hitCircle)
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Position = hitCircle.Position;
|
||||
Size = hitCircle.Size;
|
||||
Scale = hitCircle.Scale;
|
||||
|
||||
AddInternal(new RingPiece());
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Colour = colours.Yellow;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Edit.Layers.Selection;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
||||
{
|
||||
public class SliderCircleOverlay : HitObjectOverlay
|
||||
{
|
||||
public SliderCircleOverlay(DrawableHitCircle sliderHead, DrawableSlider slider)
|
||||
: this(sliderHead, sliderHead.Position, slider)
|
||||
{
|
||||
}
|
||||
|
||||
public SliderCircleOverlay(DrawableSliderTail sliderTail, DrawableSlider slider)
|
||||
: this(sliderTail, sliderTail.Position, slider)
|
||||
{
|
||||
}
|
||||
|
||||
private readonly DrawableOsuHitObject hitObject;
|
||||
|
||||
private SliderCircleOverlay(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider)
|
||||
: base(hitObject)
|
||||
{
|
||||
this.hitObject = hitObject;
|
||||
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Position = position;
|
||||
Size = slider.HeadCircle.Size;
|
||||
Scale = slider.HeadCircle.Scale;
|
||||
|
||||
AddInternal(new RingPiece());
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Colour = colours.Yellow;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
RelativeAnchorPosition = hitObject.RelativeAnchorPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Edit.Layers.Selection;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
||||
{
|
||||
public class SliderOverlay : HitObjectOverlay
|
||||
{
|
||||
private readonly SliderBody body;
|
||||
private readonly DrawableSlider slider;
|
||||
|
||||
public SliderOverlay(DrawableSlider slider)
|
||||
: base(slider)
|
||||
{
|
||||
this.slider = slider;
|
||||
|
||||
var obj = (Slider)slider.HitObject;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
body = new SliderBody(obj)
|
||||
{
|
||||
AccentColour = Color4.Transparent,
|
||||
PathWidth = obj.Scale * 64
|
||||
},
|
||||
new SliderCircleOverlay(slider.HeadCircle, slider),
|
||||
new SliderCircleOverlay(slider.TailCircle, slider),
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
body.BorderColour = colours.Yellow;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Position = slider.Position;
|
||||
Size = slider.Size;
|
||||
OriginPosition = slider.OriginPosition;
|
||||
|
||||
// Need to cause one update
|
||||
body.UpdateProgress(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
@@ -17,6 +18,8 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
protected override Playfield CreatePlayfield() => new OsuEditPlayfield();
|
||||
|
||||
protected override Vector2 PlayfieldArea => Vector2.One;
|
||||
|
||||
protected override CursorContainer CreateCursor() => null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Layers.Selection;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
using osu.Game.Rulesets.Osu.Edit.Layers.Selection;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
@@ -25,5 +29,9 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
new HitObjectCompositionTool<Slider>(),
|
||||
new HitObjectCompositionTool<Spinner>()
|
||||
};
|
||||
|
||||
protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
protected override HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new OsuHitObjectOverlayLayer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
@@ -22,8 +24,14 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
if (slider == null)
|
||||
return;
|
||||
|
||||
slider.HeadCircle.Position = new Vector2(slider.HeadCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.HeadCircle.Position.Y);
|
||||
slider.TailCircle.Position = new Vector2(slider.TailCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.TailCircle.Position.Y);
|
||||
|
||||
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||
slider.NestedHitObjects.OfType<RepeatPoint>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||
|
||||
var newControlPoints = new List<Vector2>();
|
||||
slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y)));
|
||||
slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, -c.Y)));
|
||||
|
||||
slider.ControlPoints = newControlPoints;
|
||||
slider.Curve?.Calculate(); // Recalculate the slider curve
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
// Copyright (c) 2007-2018 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.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
@@ -24,7 +26,10 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
foreach (var d in drawables.OfType<DrawableOsuHitObject>())
|
||||
{
|
||||
d.ApplyCustomUpdateState += ApplyHiddenState;
|
||||
|
||||
d.HitObject.TimeFadein = d.HitObject.TimePreempt * fade_in_duration_multiplier;
|
||||
foreach (var h in d.HitObject.NestedHitObjects.OfType<OsuHitObject>())
|
||||
h.TimeFadein = h.TimePreempt * fade_in_duration_multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,30 +38,37 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
if (!(drawable is DrawableOsuHitObject d))
|
||||
return;
|
||||
|
||||
var fadeOutStartTime = d.HitObject.StartTime - d.HitObject.TimePreempt + d.HitObject.TimeFadein;
|
||||
var fadeOutDuration = d.HitObject.TimePreempt * fade_out_duration_multiplier;
|
||||
var h = d.HitObject;
|
||||
|
||||
var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadein;
|
||||
var fadeOutDuration = h.TimePreempt * fade_out_duration_multiplier;
|
||||
|
||||
// new duration from completed fade in to end (before fading out)
|
||||
var longFadeDuration = ((d.HitObject as IHasEndTime)?.EndTime ?? d.HitObject.StartTime) - fadeOutStartTime;
|
||||
var longFadeDuration = ((h as IHasEndTime)?.EndTime ?? h.StartTime) - fadeOutStartTime;
|
||||
|
||||
switch (drawable)
|
||||
{
|
||||
case DrawableHitCircle circle:
|
||||
// we don't want to see the approach circle
|
||||
circle.ApproachCircle.Hide();
|
||||
using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
|
||||
circle.ApproachCircle.Hide();
|
||||
|
||||
// fade out immediately after fade in.
|
||||
using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true))
|
||||
{
|
||||
circle.FadeOut(fadeOutDuration);
|
||||
}
|
||||
|
||||
break;
|
||||
case DrawableSlider slider:
|
||||
using (slider.BeginAbsoluteSequence(fadeOutStartTime, true))
|
||||
{
|
||||
slider.Body.FadeOut(longFadeDuration, Easing.Out);
|
||||
}
|
||||
|
||||
break;
|
||||
case DrawableSliderTick sliderTick:
|
||||
// slider ticks fade out over up to one second
|
||||
var tickFadeOutDuration = Math.Min(sliderTick.HitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000);
|
||||
|
||||
using (sliderTick.BeginAbsoluteSequence(sliderTick.HitObject.StartTime - tickFadeOutDuration, true))
|
||||
sliderTick.FadeOut(tickFadeOutDuration);
|
||||
|
||||
break;
|
||||
case DrawableSpinner spinner:
|
||||
@@ -66,9 +78,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
spinner.Background.Hide();
|
||||
|
||||
using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true))
|
||||
{
|
||||
spinner.FadeOut(fadeOutDuration);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -72,14 +72,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (timeOffset > HitObject.HitWindowFor(HitResult.Meh))
|
||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||
AddJudgement(new OsuJudgement { Result = HitResult.Miss });
|
||||
return;
|
||||
}
|
||||
|
||||
var result = HitObject.HitWindows.ResultFor(timeOffset);
|
||||
if (result == HitResult.None)
|
||||
return;
|
||||
|
||||
AddJudgement(new OsuJudgement
|
||||
{
|
||||
Result = HitObject.ScoreResultForOffset(Math.Abs(timeOffset)),
|
||||
Result = result,
|
||||
PositionOffset = Vector2.Zero //todo: set to correct value
|
||||
});
|
||||
}
|
||||
@@ -104,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Expire(true);
|
||||
|
||||
// override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early.
|
||||
LifetimeEnd = HitObject.StartTime + HitObject.HitWindowFor(HitResult.Miss);
|
||||
LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.HalfWindowFor(HitResult.Miss);
|
||||
break;
|
||||
case ArmedState.Miss:
|
||||
ApproachCircle.FadeOut(50);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
@@ -24,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
this.repeatPoint = repeatPoint;
|
||||
this.drawableSlider = drawableSlider;
|
||||
|
||||
Size = new Vector2(32 * repeatPoint.Scale);
|
||||
Size = new Vector2(45 * repeatPoint.Scale);
|
||||
|
||||
Blending = BlendingMode.Additive;
|
||||
Origin = Anchor.Centre;
|
||||
@@ -34,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
new SpriteIcon
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Icon = FontAwesome.fa_eercast
|
||||
Icon = FontAwesome.fa_chevron_right
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -49,9 +50,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
animDuration = Math.Min(150, repeatPoint.SpanDuration / 2);
|
||||
|
||||
this.FadeIn(animDuration).ScaleTo(1.2f, animDuration / 2)
|
||||
.Then()
|
||||
.ScaleTo(1, animDuration / 2, Easing.Out);
|
||||
this.Animate(
|
||||
d => d.FadeIn(animDuration),
|
||||
d => d.ScaleTo(0.5f).ScaleTo(1f, animDuration * 4, Easing.OutElasticHalf)
|
||||
);
|
||||
}
|
||||
|
||||
protected override void UpdateCurrentState(ArmedState state)
|
||||
@@ -66,11 +68,33 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
break;
|
||||
case ArmedState.Hit:
|
||||
this.FadeOut(animDuration, Easing.OutQuint)
|
||||
.ScaleTo(Scale * 1.5f, animDuration, Easing.OutQuint);
|
||||
.ScaleTo(Scale * 1.5f, animDuration, Easing.Out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSnakingPosition(Vector2 start, Vector2 end) => Position = repeatPoint.RepeatIndex % 2 == 0 ? end : start;
|
||||
public void UpdateSnakingPosition(Vector2 start, Vector2 end)
|
||||
{
|
||||
bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0;
|
||||
List<Vector2> curve = drawableSlider.Body.CurrentCurve;
|
||||
|
||||
Position = isRepeatAtEnd ? end : start;
|
||||
|
||||
if (curve.Count < 2)
|
||||
return;
|
||||
|
||||
int searchStart = isRepeatAtEnd ? curve.Count - 1 : 0;
|
||||
int direction = isRepeatAtEnd ? -1 : 1;
|
||||
|
||||
// find the next vector2 in the curve which is not equal to our current position to infer a rotation.
|
||||
for (int i = searchStart; i >= 0 && i < curve.Count; i += direction)
|
||||
{
|
||||
if (curve[i] == Position)
|
||||
continue;
|
||||
|
||||
Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - Position.Y, curve[i].X - Position.X));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,11 @@ using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
@@ -21,6 +23,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
private readonly List<Drawable> components = new List<Drawable>();
|
||||
|
||||
public readonly DrawableHitCircle HeadCircle;
|
||||
public readonly DrawableSliderTail TailCircle;
|
||||
|
||||
public readonly SliderBody Body;
|
||||
public readonly SliderBall Ball;
|
||||
|
||||
@@ -29,7 +33,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
slider = s;
|
||||
|
||||
DrawableSliderTail tail;
|
||||
Position = s.StackedPosition;
|
||||
|
||||
Container<DrawableSliderTick> ticks;
|
||||
Container<DrawableRepeatPoint> repeatPoints;
|
||||
|
||||
@@ -38,20 +43,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Body = new SliderBody(s)
|
||||
{
|
||||
AccentColour = AccentColour,
|
||||
Position = s.StackedPosition,
|
||||
PathWidth = s.Scale * 64,
|
||||
},
|
||||
ticks = new Container<DrawableSliderTick>(),
|
||||
repeatPoints = new Container<DrawableRepeatPoint>(),
|
||||
ticks = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
|
||||
repeatPoints = new Container<DrawableRepeatPoint> { RelativeSizeAxes = Axes.Both },
|
||||
Ball = new SliderBall(s)
|
||||
{
|
||||
BypassAutoSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(s.Scale),
|
||||
AccentColour = AccentColour,
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0
|
||||
},
|
||||
HeadCircle = new DrawableHitCircle(s.HeadCircle),
|
||||
tail = new DrawableSliderTail(s.TailCircle)
|
||||
HeadCircle = new DrawableHitCircle(s.HeadCircle) { Position = s.TailCircle.Position - s.Position },
|
||||
TailCircle = new DrawableSliderTail(s.TailCircle) { Position = s.TailCircle.Position - s.Position }
|
||||
};
|
||||
|
||||
components.Add(Body);
|
||||
@@ -59,15 +64,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
AddNested(HeadCircle);
|
||||
|
||||
AddNested(tail);
|
||||
components.Add(tail);
|
||||
AddNested(TailCircle);
|
||||
components.Add(TailCircle);
|
||||
|
||||
foreach (var tick in s.NestedHitObjects.OfType<SliderTick>())
|
||||
{
|
||||
var drawableTick = new DrawableSliderTick(tick)
|
||||
{
|
||||
Position = tick.Position
|
||||
};
|
||||
var drawableTick = new DrawableSliderTick(tick) { Position = tick.Position - s.Position };
|
||||
|
||||
ticks.Add(drawableTick);
|
||||
components.Add(drawableTick);
|
||||
@@ -76,10 +78,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
foreach (var repeatPoint in s.NestedHitObjects.OfType<RepeatPoint>())
|
||||
{
|
||||
var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this)
|
||||
{
|
||||
Position = repeatPoint.Position
|
||||
};
|
||||
var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) { Position = repeatPoint.Position - s.Position };
|
||||
|
||||
repeatPoints.Add(drawableRepeatPoint);
|
||||
components.Add(drawableRepeatPoint);
|
||||
@@ -87,7 +86,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
}
|
||||
}
|
||||
|
||||
private int currentSpan;
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
config.BindWith(OsuSetting.SnakingInSliders, Body.SnakingIn);
|
||||
config.BindWith(OsuSetting.SnakingOutSliders, Body.SnakingOut);
|
||||
}
|
||||
|
||||
public bool Tracking;
|
||||
|
||||
protected override void Update()
|
||||
@@ -96,21 +101,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
Tracking = Ball.Tracking;
|
||||
|
||||
double progress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
|
||||
|
||||
int span = slider.SpanAt(progress);
|
||||
progress = slider.ProgressAt(progress);
|
||||
|
||||
if (span > currentSpan)
|
||||
currentSpan = span;
|
||||
double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
|
||||
|
||||
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
|
||||
if (!HeadCircle.IsHit)
|
||||
HeadCircle.Position = slider.Curve.PositionAt(progress);
|
||||
HeadCircle.Position = slider.CurvePositionAt(completionProgress);
|
||||
|
||||
foreach (var c in components.OfType<ISliderProgress>()) c.UpdateProgress(progress, span);
|
||||
foreach (var c in components.OfType<ISliderProgress>()) c.UpdateProgress(completionProgress);
|
||||
foreach (var c in components.OfType<ITrackSnaking>()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0));
|
||||
foreach (var t in components.OfType<IRequireTracking>()) t.Tracking = Ball.Tracking;
|
||||
|
||||
Size = Body.Size;
|
||||
OriginPosition = Body.PathOffset;
|
||||
|
||||
if (DrawSize != Vector2.Zero)
|
||||
{
|
||||
var childAnchorPosition = Vector2.Divide(OriginPosition, DrawSize);
|
||||
foreach (var obj in NestedHitObjects)
|
||||
obj.RelativeAnchorPosition = childAnchorPosition;
|
||||
Ball.RelativeAnchorPosition = childAnchorPosition;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
@@ -153,11 +163,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
this.FadeOut(fade_out_time, Easing.OutQuint).Expire();
|
||||
}
|
||||
|
||||
Expire(true);
|
||||
}
|
||||
|
||||
public Drawable ProxiedLayer => HeadCircle.ApproachCircle;
|
||||
|
||||
public override Vector2 SelectionPoint => ToScreenSpace(Body.Position);
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos);
|
||||
|
||||
public override Vector2 SelectionPoint => ToScreenSpace(OriginPosition);
|
||||
public override Quad SelectionQuad => Body.PathDrawQuad;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
public DrawableSliderTail(HitCircle hitCircle)
|
||||
: base(hitCircle)
|
||||
{
|
||||
AlwaysPresent = true;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
FillMode = FillMode.Fit;
|
||||
|
||||
AlwaysPresent = true;
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public class DrawableSliderTick : DrawableOsuHitObject, IRequireTracking
|
||||
{
|
||||
private const double anim_duration = 150;
|
||||
public const double ANIM_DURATION = 150;
|
||||
|
||||
public bool Tracking { get; set; }
|
||||
|
||||
@@ -51,10 +51,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
protected override void UpdatePreemptState()
|
||||
{
|
||||
this.Animate(
|
||||
d => d.FadeIn(anim_duration),
|
||||
d => d.ScaleTo(0.5f).ScaleTo(1.2f, anim_duration / 2)
|
||||
).Then(
|
||||
d => d.ScaleTo(1, anim_duration / 2, Easing.Out)
|
||||
d => d.FadeIn(ANIM_DURATION),
|
||||
d => d.ScaleTo(0.5f).ScaleTo(1f, ANIM_DURATION * 4, Easing.OutElasticHalf)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -66,12 +64,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
this.Delay(HitObject.TimePreempt).FadeOut();
|
||||
break;
|
||||
case ArmedState.Miss:
|
||||
this.FadeOut(anim_duration)
|
||||
.FadeColour(Color4.Red, anim_duration / 2);
|
||||
this.FadeOut(ANIM_DURATION)
|
||||
.FadeColour(Color4.Red, ANIM_DURATION / 2);
|
||||
break;
|
||||
case ArmedState.Hit:
|
||||
this.FadeOut(anim_duration, Easing.OutQuint)
|
||||
.ScaleTo(Scale * 1.5f, anim_duration, Easing.OutQuint);
|
||||
this.FadeOut(ANIM_DURATION, Easing.OutQuint)
|
||||
.ScaleTo(Scale * 1.5f, ANIM_DURATION, Easing.Out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,30 +6,24 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
public class ApproachCircle : Container
|
||||
{
|
||||
private readonly Sprite approachCircle;
|
||||
|
||||
public ApproachCircle()
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
approachCircle = new Sprite()
|
||||
};
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
approachCircle.Texture = textures.Get(@"Play/osu/approachcircle");
|
||||
Child = new SkinnableDrawable("Play/osu/approachcircle", name => new Sprite { Texture = textures.Get(name) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,20 +2,16 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Skinning;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
public class CirclePiece : Container, IKeyBindingHandler<OsuAction>
|
||||
{
|
||||
private readonly Sprite disc;
|
||||
|
||||
public Func<bool> Hit;
|
||||
|
||||
public CirclePiece()
|
||||
@@ -27,26 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
disc = new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
},
|
||||
new TrianglesPiece
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingMode.Additive,
|
||||
Alpha = 0.5f,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
disc.Texture = textures.Get(@"Play/osu/disc");
|
||||
InternalChild = new SkinnableDrawable("Play/osu/hitcircle", _ => new DefaultCirclePiece());
|
||||
}
|
||||
|
||||
public bool OnPressed(OsuAction action)
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
public class DefaultCirclePiece : Container
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Texture = textures.Get(@"Play/osu/disc"),
|
||||
},
|
||||
new TrianglesPiece
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingMode.Additive,
|
||||
Alpha = 0.5f,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,34 +6,30 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
public class GlowPiece : Container
|
||||
{
|
||||
private readonly Sprite layer;
|
||||
|
||||
public GlowPiece()
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Children = new[]
|
||||
{
|
||||
layer = new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Blending = BlendingMode.Additive,
|
||||
Alpha = 0.5f
|
||||
}
|
||||
};
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
layer.Texture = textures.Get(@"Play/osu/ring-glow");
|
||||
Child = new SkinnableDrawable("Play/osu/ring-glow", name => new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Texture = textures.Get(name),
|
||||
Blending = BlendingMode.Additive,
|
||||
Alpha = 0.5f
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
@@ -28,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new CircularContainer
|
||||
new SkinnableDrawable("Play/osu/number-glow", name => new CircularContainer
|
||||
{
|
||||
Masking = true,
|
||||
Origin = Anchor.Centre,
|
||||
@@ -38,11 +39,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
Radius = 60,
|
||||
Colour = Color4.White.Opacity(0.5f),
|
||||
},
|
||||
Children = new[]
|
||||
{
|
||||
new Box()
|
||||
}
|
||||
},
|
||||
Child = new Box()
|
||||
}, false),
|
||||
number = new OsuSpriteText
|
||||
{
|
||||
Text = @"1",
|
||||
|
||||
@@ -6,6 +6,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
@@ -15,24 +16,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
Size = new Vector2(128);
|
||||
|
||||
Masking = true;
|
||||
CornerRadius = Size.X / 2;
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
BorderThickness = 10;
|
||||
BorderColour = Color4.White;
|
||||
|
||||
Children = new Drawable[]
|
||||
InternalChild = new SkinnableDrawable("Play/osu/hitcircleoverlay", _ => new Container
|
||||
{
|
||||
new Box
|
||||
Masking = true,
|
||||
CornerRadius = Size.X / 2,
|
||||
BorderThickness = 10,
|
||||
BorderColour = Color4.White,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
new Box
|
||||
{
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
@@ -139,9 +140,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateProgress(double progress, int span)
|
||||
public void UpdateProgress(double completionProgress)
|
||||
{
|
||||
Position = slider.Curve.PositionAt(progress);
|
||||
Position = slider.CurvePositionAt(completionProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.OpenGL.Textures;
|
||||
using osu.Framework.Graphics.Lines;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Configuration;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics.ES30;
|
||||
using OpenTK.Graphics;
|
||||
@@ -30,6 +29,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
set { path.PathWidth = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Offset in absolute coordinates from the start of the curve.
|
||||
/// </summary>
|
||||
public Vector2 PathOffset { get; private set; }
|
||||
|
||||
public readonly List<Vector2> CurrentCurve = new List<Vector2>();
|
||||
|
||||
public readonly Bindable<bool> SnakingIn = new Bindable<bool>();
|
||||
public readonly Bindable<bool> SnakingOut = new Bindable<bool>();
|
||||
|
||||
public double? SnakedStart { get; private set; }
|
||||
public double? SnakedEnd { get; private set; }
|
||||
|
||||
@@ -46,8 +55,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
return;
|
||||
accentColour = value;
|
||||
|
||||
if (LoadState == LoadState.Ready)
|
||||
Schedule(reloadTexture);
|
||||
if (LoadState >= LoadState.Ready)
|
||||
reloadTexture();
|
||||
}
|
||||
}
|
||||
|
||||
private Color4 borderColour = Color4.White;
|
||||
/// <summary>
|
||||
/// Used to colour the path border.
|
||||
/// </summary>
|
||||
public new Color4 BorderColour
|
||||
{
|
||||
get { return borderColour; }
|
||||
set
|
||||
{
|
||||
if (borderColour == value)
|
||||
return;
|
||||
borderColour = value;
|
||||
|
||||
if (LoadState >= LoadState.Ready)
|
||||
reloadTexture();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +82,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
private int textureWidth => (int)PathWidth * 2;
|
||||
|
||||
private Vector2 topLeftOffset;
|
||||
|
||||
private readonly Slider slider;
|
||||
public SliderBody(Slider s)
|
||||
{
|
||||
@@ -64,6 +93,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
container = new BufferedContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CacheDrawnFrameBuffer = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@@ -78,6 +108,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
container.Attach(RenderbufferInternalFormat.DepthComponent16);
|
||||
}
|
||||
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => path.ReceiveMouseInputAt(screenSpacePos);
|
||||
|
||||
public void SetRange(double p0, double p1)
|
||||
{
|
||||
if (p0 > p1)
|
||||
@@ -85,26 +117,21 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
if (updateSnaking(p0, p1))
|
||||
{
|
||||
// Autosizing does not give us the desired behaviour here.
|
||||
// We want the container to have the same size as the slider,
|
||||
// and to be positioned such that the slider head is at (0,0).
|
||||
container.Size = path.Size;
|
||||
container.Position = -path.PositionInBoundingBox(slider.Curve.PositionAt(0) - currentCurve[0]);
|
||||
// The path is generated such that its size encloses it. This change of size causes the path
|
||||
// to move around while snaking, so we need to offset it to make sure it maintains the
|
||||
// same position as when it is fully snaked.
|
||||
var newTopLeftOffset = path.PositionInBoundingBox(Vector2.Zero);
|
||||
path.Position = topLeftOffset - newTopLeftOffset;
|
||||
|
||||
container.ForceRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
private Bindable<bool> snakingIn;
|
||||
private Bindable<bool> snakingOut;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
private void load()
|
||||
{
|
||||
snakingIn = config.GetBindable<bool>(OsuSetting.SnakingInSliders);
|
||||
snakingOut = config.GetBindable<bool>(OsuSetting.SnakingOutSliders);
|
||||
|
||||
reloadTexture();
|
||||
computeSize();
|
||||
}
|
||||
|
||||
private void reloadTexture()
|
||||
@@ -128,10 +155,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
if (progress <= border_portion)
|
||||
{
|
||||
bytes[i * 4] = 255;
|
||||
bytes[i * 4 + 1] = 255;
|
||||
bytes[i * 4 + 2] = 255;
|
||||
bytes[i * 4 + 3] = (byte)(Math.Min(progress / aa_portion, 1) * 255);
|
||||
bytes[i * 4] = (byte)(BorderColour.R * 255);
|
||||
bytes[i * 4 + 1] = (byte)(BorderColour.G * 255);
|
||||
bytes[i * 4 + 2] = (byte)(BorderColour.B * 255);
|
||||
bytes[i * 4 + 3] = (byte)(Math.Min(progress / aa_portion, 1) * (BorderColour.A * 255));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -148,7 +175,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
path.Texture = texture;
|
||||
}
|
||||
|
||||
private readonly List<Vector2> currentCurve = new List<Vector2>();
|
||||
private void computeSize()
|
||||
{
|
||||
// Generate the entire curve
|
||||
slider.Curve.GetPathToProgress(CurrentCurve, 0, 1);
|
||||
foreach (Vector2 p in CurrentCurve)
|
||||
path.AddVertex(p);
|
||||
|
||||
Size = path.Size;
|
||||
|
||||
topLeftOffset = path.PositionInBoundingBox(Vector2.Zero);
|
||||
PathOffset = path.PositionInBoundingBox(CurrentCurve[0]);
|
||||
}
|
||||
|
||||
private bool updateSnaking(double p0, double p1)
|
||||
{
|
||||
if (SnakedStart == p0 && SnakedEnd == p1) return false;
|
||||
@@ -156,30 +195,33 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
SnakedStart = p0;
|
||||
SnakedEnd = p1;
|
||||
|
||||
slider.Curve.GetPathToProgress(currentCurve, p0, p1);
|
||||
slider.Curve.GetPathToProgress(CurrentCurve, p0, p1);
|
||||
|
||||
path.ClearVertices();
|
||||
foreach (Vector2 p in currentCurve)
|
||||
path.AddVertex(p - currentCurve[0]);
|
||||
foreach (Vector2 p in CurrentCurve)
|
||||
path.AddVertex(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void UpdateProgress(double progress, int span)
|
||||
public void UpdateProgress(double completionProgress)
|
||||
{
|
||||
var span = slider.SpanAt(completionProgress);
|
||||
var spanProgress = slider.ProgressAt(completionProgress);
|
||||
|
||||
double start = 0;
|
||||
double end = snakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1;
|
||||
double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1;
|
||||
|
||||
if (span >= slider.SpanCount() - 1)
|
||||
{
|
||||
if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1)
|
||||
{
|
||||
start = 0;
|
||||
end = snakingOut ? progress : 1;
|
||||
end = SnakingOut ? spanProgress : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = snakingOut ? progress : 0;
|
||||
start = SnakingOut ? spanProgress : 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,10 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
public interface ISliderProgress
|
||||
{
|
||||
void UpdateProgress(double progress, int span);
|
||||
/// <summary>
|
||||
/// Updates the progress of this <see cref="ISliderProgress"/> element along the slider.
|
||||
/// </summary>
|
||||
/// <param name="completionProgress">Amount of the slider completed.</param>
|
||||
void UpdateProgress(double completionProgress);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ using OpenTK;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
@@ -15,11 +14,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
public const double OBJECT_RADIUS = 64;
|
||||
|
||||
private const double hittable_range = 300;
|
||||
public double HitWindow50 = 150;
|
||||
public double HitWindow100 = 80;
|
||||
public double HitWindow300 = 30;
|
||||
|
||||
public double TimePreempt = 600;
|
||||
public double TimeFadein = 400;
|
||||
|
||||
@@ -45,32 +39,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
public virtual bool NewCombo { get; set; }
|
||||
public int IndexInCurrentCombo { get; set; }
|
||||
|
||||
public double HitWindowFor(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return hittable_range;
|
||||
case HitResult.Meh:
|
||||
return HitWindow50;
|
||||
case HitResult.Good:
|
||||
return HitWindow100;
|
||||
case HitResult.Great:
|
||||
return HitWindow300;
|
||||
}
|
||||
}
|
||||
|
||||
public HitResult ScoreResultForOffset(double offset)
|
||||
{
|
||||
if (offset < HitWindowFor(HitResult.Great))
|
||||
return HitResult.Great;
|
||||
if (offset < HitWindowFor(HitResult.Good))
|
||||
return HitResult.Good;
|
||||
if (offset < HitWindowFor(HitResult.Meh))
|
||||
return HitResult.Meh;
|
||||
return HitResult.Miss;
|
||||
}
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
@@ -78,10 +46,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
|
||||
TimeFadein = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300);
|
||||
|
||||
HitWindow50 = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 200, 150, 100);
|
||||
HitWindow100 = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 140, 100, 60);
|
||||
HitWindow300 = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 80, 50, 20);
|
||||
|
||||
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using System.Linq;
|
||||
@@ -23,8 +22,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
public Vector2 StackedPositionAt(double t) => this.PositionAt(t) + StackOffset;
|
||||
public override Vector2 EndPosition => this.PositionAt(1);
|
||||
public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t);
|
||||
public override Vector2 EndPosition => Position + this.CurvePositionAt(1);
|
||||
|
||||
public SliderCurve Curve { get; } = new SliderCurve();
|
||||
|
||||
@@ -66,18 +65,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
/// </summary>
|
||||
public double SpanDuration => Duration / this.SpanCount();
|
||||
|
||||
private int stackHeight;
|
||||
|
||||
public override int StackHeight
|
||||
{
|
||||
get { return stackHeight; }
|
||||
set
|
||||
{
|
||||
stackHeight = value;
|
||||
Curve.Offset = StackOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public double Velocity;
|
||||
public double TickDistance;
|
||||
|
||||
@@ -111,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
HeadCircle = new HitCircle
|
||||
{
|
||||
StartTime = StartTime,
|
||||
Position = StackedPosition,
|
||||
Position = Position,
|
||||
IndexInCurrentCombo = IndexInCurrentCombo,
|
||||
ComboColour = ComboColour,
|
||||
Samples = Samples,
|
||||
@@ -121,11 +108,9 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
TailCircle = new HitCircle
|
||||
{
|
||||
StartTime = EndTime,
|
||||
Position = StackedEndPosition,
|
||||
Position = EndPosition,
|
||||
IndexInCurrentCombo = IndexInCurrentCombo,
|
||||
ComboColour = ComboColour,
|
||||
Samples = Samples,
|
||||
SampleControlPoint = SampleControlPoint
|
||||
ComboColour = ComboColour
|
||||
};
|
||||
|
||||
AddNested(HeadCircle);
|
||||
@@ -134,14 +119,16 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
private void createTicks()
|
||||
{
|
||||
if (TickDistance == 0) return;
|
||||
|
||||
var length = Curve.Distance;
|
||||
var tickDistance = Math.Min(TickDistance, length);
|
||||
var tickDistance = MathHelper.Clamp(TickDistance, 0, length);
|
||||
|
||||
if (tickDistance == 0) return;
|
||||
|
||||
var minDistanceFromEnd = Velocity * 0.01;
|
||||
|
||||
for (var span = 0; span < this.SpanCount(); span++)
|
||||
var spanCount = this.SpanCount();
|
||||
|
||||
for (var span = 0; span < spanCount; span++)
|
||||
{
|
||||
var spanStartTime = StartTime + span * SpanDuration;
|
||||
var reversed = span % 2 == 1;
|
||||
@@ -170,7 +157,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
SpanIndex = span,
|
||||
SpanStartTime = spanStartTime,
|
||||
StartTime = spanStartTime + timeProgress * SpanDuration,
|
||||
Position = Curve.PositionAt(distanceProgress),
|
||||
Position = Position + Curve.PositionAt(distanceProgress),
|
||||
StackHeight = StackHeight,
|
||||
Scale = Scale,
|
||||
ComboColour = ComboColour,
|
||||
@@ -189,7 +176,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
RepeatIndex = repeatIndex,
|
||||
SpanDuration = SpanDuration,
|
||||
StartTime = StartTime + repeat * SpanDuration,
|
||||
Position = Curve.PositionAt(repeat % 2),
|
||||
Position = Position + Curve.PositionAt(repeat % 2),
|
||||
StackHeight = StackHeight,
|
||||
Scale = Scale,
|
||||
ComboColour = ComboColour,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
@@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
SpinsRequired = (int)(Duration / 1000 * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5));
|
||||
|
||||
// spinning doesn't match 1:1 with stable, so let's fudge them easier for the time being.
|
||||
SpinsRequired = (int)(SpinsRequired * 0.6);
|
||||
SpinsRequired = (int)Math.Max(1, SpinsRequired * 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ using osu.Game.Rulesets.Osu.Edit;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Replays;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu
|
||||
{
|
||||
@@ -145,6 +147,8 @@ namespace osu.Game.Rulesets.Osu
|
||||
|
||||
public override int LegacyID => 0;
|
||||
|
||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame();
|
||||
|
||||
public OsuRuleset(RulesetInfo rulesetInfo = null)
|
||||
: base(rulesetInfo)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
@@ -64,9 +64,9 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
{
|
||||
buttonIndex = 0;
|
||||
|
||||
AddFrameToReplay(new ReplayFrame(-100000, 256, 500, ReplayButtonState.None));
|
||||
AddFrameToReplay(new ReplayFrame(Beatmap.HitObjects[0].StartTime - 1500, 256, 500, ReplayButtonState.None));
|
||||
AddFrameToReplay(new ReplayFrame(Beatmap.HitObjects[0].StartTime - 1000, 256, 192, ReplayButtonState.None));
|
||||
AddFrameToReplay(new OsuReplayFrame(-100000, new Vector2(256, 500)));
|
||||
AddFrameToReplay(new OsuReplayFrame(Beatmap.HitObjects[0].StartTime - 1500, new Vector2(256, 500)));
|
||||
AddFrameToReplay(new OsuReplayFrame(Beatmap.HitObjects[0].StartTime - 1500, new Vector2(256, 500)));
|
||||
|
||||
for (int i = 0; i < Beatmap.HitObjects.Count; i++)
|
||||
{
|
||||
@@ -89,20 +89,20 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
double endTime = (prev as IHasEndTime)?.EndTime ?? prev.StartTime;
|
||||
|
||||
// Make the cursor stay at a hitObject as long as possible (mainly for autopilot).
|
||||
if (h.StartTime - h.HitWindowFor(HitResult.Miss) > endTime + h.HitWindowFor(HitResult.Meh) + 50)
|
||||
if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50)
|
||||
{
|
||||
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(HitResult.Meh), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
|
||||
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(HitResult.Miss), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
|
||||
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
|
||||
if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
|
||||
}
|
||||
else if (h.StartTime - h.HitWindowFor(HitResult.Meh) > endTime + h.HitWindowFor(HitResult.Meh) + 50)
|
||||
else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50)
|
||||
{
|
||||
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(HitResult.Meh), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
|
||||
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(HitResult.Meh), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
|
||||
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
|
||||
if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
|
||||
}
|
||||
else if (h.StartTime - h.HitWindowFor(HitResult.Good) > endTime + h.HitWindowFor(HitResult.Good) + 50)
|
||||
else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50)
|
||||
{
|
||||
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(HitResult.Good), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
|
||||
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(HitResult.Good), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
|
||||
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
|
||||
if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,9 +118,9 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
// TODO: Shouldn't the spinner always spin in the same direction?
|
||||
if (h is Spinner)
|
||||
{
|
||||
calcSpinnerStartPosAndDirection(Frames[Frames.Count - 1].Position, out startPosition, out spinnerDirection);
|
||||
calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[Frames.Count - 1]).Position, out startPosition, out spinnerDirection);
|
||||
|
||||
Vector2 spinCentreOffset = SPINNER_CENTRE - Frames[Frames.Count - 1].Position;
|
||||
Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[Frames.Count - 1]).Position;
|
||||
|
||||
if (spinCentreOffset.Length > SPIN_RADIUS)
|
||||
{
|
||||
@@ -192,13 +192,13 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
|
||||
private void moveToHitObject(OsuHitObject h, Vector2 targetPos, Easing easing)
|
||||
{
|
||||
ReplayFrame lastFrame = Frames[Frames.Count - 1];
|
||||
OsuReplayFrame lastFrame = (OsuReplayFrame)Frames[Frames.Count - 1];
|
||||
|
||||
// Wait until Auto could "see and react" to the next note.
|
||||
double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - reactionTime);
|
||||
if (waitTime > lastFrame.Time)
|
||||
{
|
||||
lastFrame = new ReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState);
|
||||
lastFrame = new OsuReplayFrame(waitTime, lastFrame.Position) { Actions = lastFrame.Actions };
|
||||
AddFrameToReplay(lastFrame);
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
for (double time = lastFrame.Time + FrameDelay; time < h.StartTime; time += FrameDelay)
|
||||
{
|
||||
Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, h.StartTime, easing);
|
||||
AddFrameToReplay(new ReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState));
|
||||
AddFrameToReplay(new OsuReplayFrame((int)time, new Vector2(currentPosition.X, currentPosition.Y)) { Actions = lastFrame.Actions });
|
||||
}
|
||||
|
||||
buttonIndex = 0;
|
||||
@@ -231,14 +231,14 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
{
|
||||
// Time to insert the first frame which clicks the object
|
||||
// Here we mainly need to determine which button to use
|
||||
ReplayButtonState button = buttonIndex % 2 == 0 ? ReplayButtonState.Left1 : ReplayButtonState.Right1;
|
||||
var action = buttonIndex % 2 == 0 ? OsuAction.LeftButton : OsuAction.RightButton;
|
||||
|
||||
ReplayFrame startFrame = new ReplayFrame(h.StartTime, startPosition.X, startPosition.Y, button);
|
||||
var startFrame = new OsuReplayFrame(h.StartTime, new Vector2(startPosition.X, startPosition.Y), action);
|
||||
|
||||
// TODO: Why do we delay 1 ms if the object is a spinner? There already is KEY_UP_DELAY from hEndTime.
|
||||
double hEndTime = ((h as IHasEndTime)?.EndTime ?? h.StartTime) + KEY_UP_DELAY;
|
||||
int endDelay = h is Spinner ? 1 : 0;
|
||||
ReplayFrame endFrame = new ReplayFrame(hEndTime + endDelay, h.StackedEndPosition.X, h.StackedEndPosition.Y, ReplayButtonState.None);
|
||||
var endFrame = new OsuReplayFrame(hEndTime + endDelay, new Vector2(h.StackedEndPosition.X, h.StackedEndPosition.Y));
|
||||
|
||||
// Decrement because we want the previous frame, not the next one
|
||||
int index = FindInsertionIndex(startFrame) - 1;
|
||||
@@ -248,19 +248,18 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
// Do we have a previous frame? No need to check for < replay.Count since we decremented!
|
||||
if (index >= 0)
|
||||
{
|
||||
ReplayFrame previousFrame = Frames[index];
|
||||
var previousButton = previousFrame.ButtonState;
|
||||
var previousFrame = (OsuReplayFrame)Frames[index];
|
||||
var previousActions = previousFrame.Actions;
|
||||
|
||||
// If a button is already held, then we simply alternate
|
||||
if (previousButton != ReplayButtonState.None)
|
||||
if (previousActions.Any())
|
||||
{
|
||||
Debug.Assert(previousButton != (ReplayButtonState.Left1 | ReplayButtonState.Right1), "Previous button state was not Left1 nor Right1 despite only using those two states.");
|
||||
|
||||
// Force alternation if we have the same button. Otherwise we can just keep the naturally to us assigned button.
|
||||
if (previousButton == button)
|
||||
if (previousActions.Contains(action))
|
||||
{
|
||||
button = (ReplayButtonState.Left1 | ReplayButtonState.Right1) & ~button;
|
||||
startFrame.ButtonState = button;
|
||||
action = action == OsuAction.LeftButton ? OsuAction.RightButton : OsuAction.LeftButton;
|
||||
startFrame.Actions.Clear();
|
||||
startFrame.Actions.Add(action);
|
||||
}
|
||||
|
||||
// We always follow the most recent slider / spinner, so remove any other frames that occur while it exists.
|
||||
@@ -272,9 +271,14 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
// After alternating we need to keep holding the other button in the future rather than the previous one.
|
||||
for (int j = index + 1; j < Frames.Count; ++j)
|
||||
{
|
||||
var frame = (OsuReplayFrame)Frames[j];
|
||||
|
||||
// Don't affect frames which stop pressing a button!
|
||||
if (j < Frames.Count - 1 || Frames[j].ButtonState == previousButton)
|
||||
Frames[j].ButtonState = button;
|
||||
if (j < Frames.Count - 1 || frame.Actions.SequenceEqual(previousActions))
|
||||
{
|
||||
frame.Actions.Clear();
|
||||
frame.Actions.Add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -298,16 +302,15 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
t = ApplyModsToTime(j - h.StartTime) * spinnerDirection;
|
||||
|
||||
Vector2 pos = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS);
|
||||
AddFrameToReplay(new ReplayFrame((int)j, pos.X, pos.Y, button));
|
||||
AddFrameToReplay(new OsuReplayFrame((int)j, new Vector2(pos.X, pos.Y), action));
|
||||
}
|
||||
|
||||
t = ApplyModsToTime(s.EndTime - h.StartTime) * spinnerDirection;
|
||||
Vector2 endPosition = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS);
|
||||
|
||||
AddFrameToReplay(new ReplayFrame(s.EndTime, endPosition.X, endPosition.Y, button));
|
||||
AddFrameToReplay(new OsuReplayFrame(s.EndTime, new Vector2(endPosition.X, endPosition.Y), action));
|
||||
|
||||
endFrame.MouseX = endPosition.X;
|
||||
endFrame.MouseY = endPosition.Y;
|
||||
endFrame.Position = endPosition;
|
||||
}
|
||||
else if (h is Slider)
|
||||
{
|
||||
@@ -315,11 +318,11 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
|
||||
for (double j = FrameDelay; j < s.Duration; j += FrameDelay)
|
||||
{
|
||||
Vector2 pos = s.PositionAt(j / s.Duration);
|
||||
AddFrameToReplay(new ReplayFrame(h.StartTime + j, pos.X, pos.Y, button));
|
||||
Vector2 pos = s.StackedPositionAt(j / s.Duration);
|
||||
AddFrameToReplay(new OsuReplayFrame(h.StartTime + j, new Vector2(pos.X, pos.Y), action));
|
||||
}
|
||||
|
||||
AddFrameToReplay(new ReplayFrame(s.EndTime, s.EndPosition.X, s.EndPosition.Y, button));
|
||||
AddFrameToReplay(new OsuReplayFrame(s.EndTime, new Vector2(s.StackedEndPosition.X, s.StackedEndPosition.Y), action));
|
||||
}
|
||||
|
||||
// We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed!
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2007-2018 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.Beatmaps;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Replays.Legacy;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Replays
|
||||
{
|
||||
public class OsuReplayFrame : ReplayFrame, IConvertibleReplayFrame
|
||||
{
|
||||
public Vector2 Position;
|
||||
public List<OsuAction> Actions = new List<OsuAction>();
|
||||
|
||||
public OsuReplayFrame()
|
||||
{
|
||||
}
|
||||
|
||||
public OsuReplayFrame(double time, Vector2 position, params OsuAction[] actions)
|
||||
: base(time)
|
||||
{
|
||||
Position = position;
|
||||
Actions.AddRange(actions);
|
||||
}
|
||||
|
||||
public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap)
|
||||
{
|
||||
Position = legacyFrame.Position;
|
||||
if (legacyFrame.MouseLeft) Actions.Add(OsuAction.LeftButton);
|
||||
if (legacyFrame.MouseRight) Actions.Add(OsuAction.RightButton);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,32 +2,42 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Replays
|
||||
{
|
||||
public class OsuReplayInputHandler : FramedReplayInputHandler
|
||||
public class OsuReplayInputHandler : FramedReplayInputHandler<OsuReplayFrame>
|
||||
{
|
||||
public OsuReplayInputHandler(Replay replay)
|
||||
: base(replay)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool IsImportant(OsuReplayFrame frame) => frame.Actions.Any();
|
||||
|
||||
protected Vector2? Position
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!HasFrames)
|
||||
return null;
|
||||
|
||||
return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time);
|
||||
}
|
||||
}
|
||||
|
||||
public override List<InputState> GetPendingStates()
|
||||
{
|
||||
List<OsuAction> actions = new List<OsuAction>();
|
||||
|
||||
if (CurrentFrame?.MouseLeft ?? false) actions.Add(OsuAction.LeftButton);
|
||||
if (CurrentFrame?.MouseRight ?? false) actions.Add(OsuAction.RightButton);
|
||||
|
||||
return new List<InputState>
|
||||
{
|
||||
new ReplayState<OsuAction>
|
||||
{
|
||||
Mouse = new ReplayMouseState(ToScreenSpace(Position ?? Vector2.Zero)),
|
||||
PressedActions = actions
|
||||
PressedActions = CurrentFrame.Actions
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 500,
|
||||
"Objects": [{
|
||||
"StartTime": 500,
|
||||
"EndTime": 2500,
|
||||
"StartX": 96,
|
||||
"StartY": 192,
|
||||
"EndX": 96,
|
||||
"EndY": 192
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 3000,
|
||||
"Objects": [{
|
||||
"StartTime": 3000,
|
||||
"EndTime": 4000,
|
||||
"StartX": 256,
|
||||
"StartY": 192,
|
||||
"EndX": 256,
|
||||
"EndY": 192
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 4500,
|
||||
"Objects": [{
|
||||
"StartTime": 4500,
|
||||
"EndTime": 5500,
|
||||
"StartX": 256,
|
||||
"StartY": 192,
|
||||
"EndX": 256,
|
||||
"EndY": 192
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 6000,
|
||||
"Objects": [{
|
||||
"StartTime": 6000,
|
||||
"EndTime": 6500,
|
||||
"StartX": 256,
|
||||
"StartY": 192,
|
||||
"EndX": 256,
|
||||
"EndY": 192
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 7000,
|
||||
"Objects": [{
|
||||
"StartTime": 7000,
|
||||
"EndTime": 8000,
|
||||
"StartX": 256,
|
||||
"StartY": 128,
|
||||
"EndX": 256,
|
||||
"EndY": 128
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 8500,
|
||||
"Objects": [{
|
||||
"StartTime": 8500,
|
||||
"EndTime": 10999,
|
||||
"StartX": 32,
|
||||
"StartY": 192,
|
||||
"EndX": 508.166229,
|
||||
"EndY": 153.299271
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 11500,
|
||||
"Objects": [{
|
||||
"StartTime": 11500,
|
||||
"EndTime": 12000,
|
||||
"StartX": 256,
|
||||
"StartY": 192,
|
||||
"EndX": 256,
|
||||
"EndY": 192
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 12500,
|
||||
"Objects": [{
|
||||
"StartTime": 12500,
|
||||
"EndTime": 16500,
|
||||
"StartX": 512,
|
||||
"StartY": 320,
|
||||
"EndX": 291.1977,
|
||||
"EndY": 40.799427
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 17000,
|
||||
"Objects": [{
|
||||
"StartTime": 17000,
|
||||
"EndTime": 18000,
|
||||
"StartX": 256,
|
||||
"StartY": 256,
|
||||
"EndX": 256,
|
||||
"EndY": 256
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 18500,
|
||||
"Objects": [{
|
||||
"StartTime": 18500,
|
||||
"EndTime": 19450,
|
||||
"StartX": 256,
|
||||
"StartY": 192,
|
||||
"EndX": 256,
|
||||
"EndY": 192
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 19875,
|
||||
"Objects": [{
|
||||
"StartTime": 19875,
|
||||
"EndTime": 23874,
|
||||
"StartX": 216,
|
||||
"StartY": 231,
|
||||
"EndX": 408.720825,
|
||||
"EndY": 339.810455
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
osu file format v14
|
||||
|
||||
[Difficulty]
|
||||
HPDrainRate:6
|
||||
CircleSize:4
|
||||
OverallDifficulty:7
|
||||
ApproachRate:8.3
|
||||
SliderMultiplier:1.6
|
||||
SliderTickRate:1
|
||||
|
||||
[TimingPoints]
|
||||
500,500,4,2,1,50,1,0
|
||||
13426,-100,4,3,1,45,0,0
|
||||
14884,-100,4,2,1,50,0,0
|
||||
|
||||
[HitObjects]
|
||||
96,192,500,6,0,L|416:192,2,320
|
||||
256,192,3000,12,0,4000,0:0:0:0:
|
||||
256,192,4500,12,0,5500,0:0:0:0:
|
||||
256,192,6000,12,0,6500,0:0:0:0:
|
||||
256,128,7000,6,0,L|352:128,4,80
|
||||
32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800
|
||||
256,192,11500,12,0,12000,0:0:0:0:
|
||||
512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280
|
||||
256,256,17000,6,0,L|160:256,4,80
|
||||
256,192,18500,12,0,19450,0:0:0:0:
|
||||
216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 118858,
|
||||
"Objects": [{
|
||||
"StartTime": 118858,
|
||||
"EndTime": 119088,
|
||||
"StartX": 219,
|
||||
"StartY": 215,
|
||||
"EndX": 239.6507,
|
||||
"EndY": 29.1437378
|
||||
}]
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
osu file format v14
|
||||
|
||||
[Difficulty]
|
||||
HPDrainRate:6
|
||||
CircleSize:4.2
|
||||
OverallDifficulty:9
|
||||
ApproachRate:9.8
|
||||
SliderMultiplier:1.87
|
||||
SliderTickRate:1
|
||||
|
||||
[TimingPoints]
|
||||
49051,230.769230769231,4,2,1,15,1,0
|
||||
|
||||
[HitObjects]
|
||||
219,215,118858,2,0,P|224:170|244:-10,1,187,8|2,0:0|0:0,0:0:0:0:
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2007-2018 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 NUnit.Framework;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public class OsuBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||
|
||||
[TestCase("basic")]
|
||||
[TestCase("colinear-perfect-curve")]
|
||||
public new void Test(string name)
|
||||
{
|
||||
base.Test(name);
|
||||
}
|
||||
|
||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
{
|
||||
var startPosition = (hitObject as IHasPosition)?.Position ?? new Vector2(256, 192);
|
||||
var endPosition = (hitObject as Slider)?.EndPosition ?? startPosition;
|
||||
|
||||
yield return new ConvertValue
|
||||
{
|
||||
StartTime = hitObject.StartTime,
|
||||
EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime,
|
||||
StartX = startPosition.X,
|
||||
StartY = startPosition.Y,
|
||||
EndX = endPosition.X,
|
||||
EndY = endPosition.Y
|
||||
};
|
||||
}
|
||||
|
||||
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new OsuBeatmapConverter();
|
||||
}
|
||||
|
||||
public struct ConvertValue : IEquatable<ConvertValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// A sane value to account for osu!stable using ints everwhere.
|
||||
/// </summary>
|
||||
private const double conversion_lenience = 2;
|
||||
|
||||
public double StartTime;
|
||||
public double EndTime;
|
||||
public float StartX;
|
||||
public float StartY;
|
||||
public float EndX;
|
||||
public float EndY;
|
||||
|
||||
public bool Equals(ConvertValue other)
|
||||
=> Precision.AlmostEquals(StartTime, other.StartTime)
|
||||
&& Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience)
|
||||
&& Precision.AlmostEquals(StartX, other.StartX)
|
||||
&& Precision.AlmostEquals(StartY, other.StartY, conversion_lenience)
|
||||
&& Precision.AlmostEquals(EndX, other.EndX, conversion_lenience)
|
||||
&& Precision.AlmostEquals(EndY, other.EndY, conversion_lenience);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
@@ -17,11 +16,12 @@ using System.Collections.Generic;
|
||||
using System;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
[TestFixture]
|
||||
public class TestCaseHitCircle : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
|
||||
@@ -9,7 +9,7 @@ using osu.Game.Rulesets.Osu.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
[TestFixture]
|
||||
public class TestCaseHitCircleHidden : TestCaseHitCircle
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
||||
|
||||
@@ -5,7 +5,7 @@ using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
[TestFixture]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Audio;
|
||||
@@ -16,6 +15,7 @@ using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@@ -24,7 +24,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
[TestFixture]
|
||||
public class TestCaseSlider : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
@@ -71,16 +71,34 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
AddStep("Fast Short Slider 2 Repeats", () => testShortHighSpeed(2));
|
||||
AddStep("Fast Short Slider 6 Repeats", () => testShortHighSpeed(6));
|
||||
|
||||
AddStep("Perfect Curve", testCurve);
|
||||
AddStep("Perfect Curve", () => testPerfect());
|
||||
AddStep("Perfect Curve 1 Repeat", () => testPerfect(1));
|
||||
AddStep("Perfect Curve 2 Repeats", () => testPerfect(2));
|
||||
|
||||
AddStep("Catmull", () => testCatmull());
|
||||
AddStep("Catmull 1 Repeat", () => testCatmull(1));
|
||||
AddStep("Linear Slider", () => testLinear());
|
||||
AddStep("Linear Slider 1 Repeat", () => testLinear(1));
|
||||
AddStep("Linear Slider 2 Repeats", () => testLinear(2));
|
||||
|
||||
// TODO more curve types?
|
||||
AddStep("Bezier Slider", () => testBezier());
|
||||
AddStep("Bezier Slider 1 Repeat", () => testBezier(1));
|
||||
AddStep("Bezier Slider 2 Repeats", () => testBezier(2));
|
||||
|
||||
AddStep("Linear Overlapping", () => testLinearOverlapping());
|
||||
AddStep("Linear Overlapping 1 Repeat", () => testLinearOverlapping(1));
|
||||
AddStep("Linear Overlapping 2 Repeats", () => testLinearOverlapping(2));
|
||||
|
||||
AddStep("Catmull Slider", () => testCatmull());
|
||||
AddStep("Catmull Slider 1 Repeat", () => testCatmull(1));
|
||||
AddStep("Catmull Slider 2 Repeats", () => testCatmull(2));
|
||||
|
||||
AddStep("Big Single, Large StackOffset", () => testSimpleBigLargeStackOffset());
|
||||
AddStep("Big 1 Repeat, Large StackOffset", () => testSimpleBigLargeStackOffset(1));
|
||||
}
|
||||
|
||||
private void testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats);
|
||||
|
||||
private void testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10);
|
||||
|
||||
private void testSimpleMedium(int repeats = 0) => createSlider(5, repeats: repeats);
|
||||
|
||||
private void testSimpleSmall(int repeats = 0) => createSlider(7, repeats: repeats);
|
||||
@@ -93,12 +111,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
private void testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15);
|
||||
|
||||
private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2)
|
||||
private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2, int stackHeight = 0)
|
||||
{
|
||||
var repeatSamples = new List<List<SampleInfo>>();
|
||||
for (int i = 0; i < repeats; i++)
|
||||
repeatSamples.Add(new List<SampleInfo>());
|
||||
|
||||
var slider = new Slider
|
||||
{
|
||||
StartTime = Time.Current + 1000,
|
||||
@@ -106,18 +120,19 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(-(distance / 2), 0),
|
||||
new Vector2(distance / 2, 0),
|
||||
Vector2.Zero,
|
||||
new Vector2(distance, 0),
|
||||
},
|
||||
Distance = distance,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = repeatSamples
|
||||
RepeatSamples = createEmptySamples(repeats),
|
||||
StackHeight = stackHeight
|
||||
};
|
||||
|
||||
addSlider(slider, circleSize, speedMultiplier);
|
||||
}
|
||||
|
||||
private void testCurve()
|
||||
private void testPerfect(int repeats = 0)
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
@@ -126,11 +141,93 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(-200, 0),
|
||||
new Vector2(0, 200),
|
||||
new Vector2(200, 0)
|
||||
Vector2.Zero,
|
||||
new Vector2(200, 200),
|
||||
new Vector2(400, 0)
|
||||
},
|
||||
Distance = 600
|
||||
Distance = 600,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
}
|
||||
|
||||
private void testLinear(int repeats = 0) => createLinear(repeats);
|
||||
|
||||
private void createLinear(int repeats)
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
CurveType = CurveType.Linear,
|
||||
StartTime = Time.Current + 1000,
|
||||
Position = new Vector2(-200, 0),
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(150, 75),
|
||||
new Vector2(200, 0),
|
||||
new Vector2(300, -200),
|
||||
new Vector2(400, 0),
|
||||
new Vector2(430, 0)
|
||||
},
|
||||
Distance = 793.4417,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
}
|
||||
|
||||
private void testBezier(int repeats = 0) => createBezier(repeats);
|
||||
|
||||
private void createBezier(int repeats)
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
CurveType = CurveType.Bezier,
|
||||
StartTime = Time.Current + 1000,
|
||||
Position = new Vector2(-200, 0),
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(150, 75),
|
||||
new Vector2(200, 100),
|
||||
new Vector2(300, -200),
|
||||
new Vector2(430, 0)
|
||||
},
|
||||
Distance = 480,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
}
|
||||
|
||||
private void testLinearOverlapping(int repeats = 0) => createOverlapping(repeats);
|
||||
|
||||
private void createOverlapping(int repeats)
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
CurveType = CurveType.Linear,
|
||||
StartTime = Time.Current + 1000,
|
||||
Position = new Vector2(0, 0),
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(-200, 0),
|
||||
new Vector2(0, 0),
|
||||
new Vector2(0, -200),
|
||||
new Vector2(-200, -200),
|
||||
new Vector2(0, -200)
|
||||
},
|
||||
Distance = 1000,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
@@ -152,10 +249,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
CurveType = CurveType.Catmull,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(-100, 0),
|
||||
new Vector2(-50, -50),
|
||||
new Vector2(50, 50),
|
||||
new Vector2(100, 0)
|
||||
Vector2.Zero,
|
||||
new Vector2(50, -50),
|
||||
new Vector2(150, 50),
|
||||
new Vector2(200, 0)
|
||||
},
|
||||
Distance = 300,
|
||||
RepeatCount = repeats,
|
||||
@@ -165,6 +262,14 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
addSlider(slider, 3, 1);
|
||||
}
|
||||
|
||||
private List<List<SampleInfo>> createEmptySamples(int repeats)
|
||||
{
|
||||
var repeatSamples = new List<List<SampleInfo>>();
|
||||
for (int i = 0; i < repeats; i++)
|
||||
repeatSamples.Add(new List<SampleInfo>());
|
||||
return repeatSamples;
|
||||
}
|
||||
|
||||
private void addSlider(Slider slider, float circleSize, double speedMultiplier)
|
||||
{
|
||||
var cpi = new ControlPointInfo();
|
||||
|
||||
@@ -9,7 +9,7 @@ using osu.Game.Rulesets.Osu.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
[TestFixture]
|
||||
public class TestCaseSliderHidden : TestCaseSlider
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
||||
|
||||
@@ -17,7 +17,7 @@ using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
[TestFixture]
|
||||
public class TestCaseSpinner : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
|
||||
@@ -9,7 +9,7 @@ using osu.Game.Rulesets.Osu.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
[TestFixture]
|
||||
public class TestCaseSpinnerHidden : TestCaseSpinner
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
||||
|
||||
@@ -27,21 +27,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
|
||||
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
|
||||
|
||||
public override Vector2 Size
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Parent == null)
|
||||
return Vector2.Zero;
|
||||
|
||||
var parentSize = Parent.DrawSize;
|
||||
var aspectSize = parentSize.X * 0.75f < parentSize.Y ? new Vector2(parentSize.X, parentSize.X * 0.75f) : new Vector2(parentSize.Y * 4f / 3f, parentSize.Y);
|
||||
|
||||
return new Vector2(aspectSize.X / parentSize.X, aspectSize.Y / parentSize.Y) * base.Size;
|
||||
}
|
||||
}
|
||||
|
||||
public OsuPlayfield() : base(BASE_SIZE.X)
|
||||
public OsuPlayfield()
|
||||
: base(BASE_SIZE.X)
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
@@ -5,6 +5,7 @@ using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Input;
|
||||
using OpenTK;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Input.Handlers;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@@ -48,9 +49,13 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay);
|
||||
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay);
|
||||
|
||||
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f);
|
||||
protected override Vector2 GetAspectAdjustedSize()
|
||||
{
|
||||
var aspectSize = DrawSize.X * 0.75f < DrawSize.Y ? new Vector2(DrawSize.X, DrawSize.X * 0.75f) : new Vector2(DrawSize.Y * 4f / 3f, DrawSize.Y);
|
||||
return new Vector2(aspectSize.X / DrawSize.X, aspectSize.Y / DrawSize.Y);
|
||||
}
|
||||
|
||||
protected override CursorContainer CreateCursor() => new GameplayCursor();
|
||||
}
|
||||
|
||||
@@ -42,9 +42,21 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_green, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a84b7dcfb1391f7f, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_v2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.provider.e_sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9c301db686d0bd12, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
@@ -52,6 +64,10 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="Beatmaps\OsuBeatmapConverter.cs" />
|
||||
<Compile Include="Beatmaps\OsuBeatmapProcessor.cs" />
|
||||
<Compile Include="Edit\Layers\Selection\OsuHitObjectOverlayLayer.cs" />
|
||||
<Compile Include="Edit\Layers\Selection\Overlays\HitCircleOverlay.cs" />
|
||||
<Compile Include="Edit\Layers\Selection\Overlays\SliderCircleOverlay.cs" />
|
||||
<Compile Include="Edit\Layers\Selection\Overlays\SliderOverlay.cs" />
|
||||
<Compile Include="Edit\OsuEditPlayfield.cs" />
|
||||
<Compile Include="Edit\OsuEditRulesetContainer.cs" />
|
||||
<Compile Include="Edit\OsuHitObjectComposer.cs" />
|
||||
@@ -80,6 +96,7 @@
|
||||
<Compile Include="Objects\Drawables\IRequireTracking.cs" />
|
||||
<Compile Include="Objects\Drawables\ITrackSnaking.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\ApproachCircle.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\DefaultCirclePiece.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\SpinnerBackground.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\CirclePiece.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableSlider.cs" />
|
||||
@@ -109,7 +126,9 @@
|
||||
<Compile Include="OsuDifficulty\Skills\Speed.cs" />
|
||||
<Compile Include="OsuDifficulty\Utils\History.cs" />
|
||||
<Compile Include="OsuInputManager.cs" />
|
||||
<Compile Include="Replays\OsuReplayFrame.cs" />
|
||||
<Compile Include="Replays\OsuReplayInputHandler.cs" />
|
||||
<Compile Include="Tests\OsuBeatmapConversionTest.cs" />
|
||||
<Compile Include="Tests\TestCaseHitCircle.cs" />
|
||||
<Compile Include="Tests\TestCaseHitCircleHidden.cs" />
|
||||
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||
@@ -155,12 +174,22 @@
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\Testing\Beatmaps\basic-expected-conversion.json" />
|
||||
<EmbeddedResource Include="Resources\Testing\Beatmaps\basic.osu" />
|
||||
<EmbeddedResource Include="Resources\Testing\Beatmaps\colinear-perfect-curve-expected-conversion.json" />
|
||||
<EmbeddedResource Include="Resources\Testing\Beatmaps\colinear-perfect-curve.osu" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets'))" />
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" />
|
||||
</Project>
|
||||
@@ -2,5 +2,11 @@
|
||||
<packages>
|
||||
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.13" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.bundle_green" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.8" targetFramework="net461" />
|
||||
</packages>
|
||||
@@ -2,10 +2,9 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Audio
|
||||
{
|
||||
@@ -14,7 +13,9 @@ namespace osu.Game.Rulesets.Taiko.Audio
|
||||
private readonly ControlPointInfo controlPoints;
|
||||
private readonly Dictionary<double, DrumSample> mappings = new Dictionary<double, DrumSample>();
|
||||
|
||||
public DrumSampleMapping(ControlPointInfo controlPoints, AudioManager audio)
|
||||
public readonly List<SkinnableSound> Sounds = new List<SkinnableSound>();
|
||||
|
||||
public DrumSampleMapping(ControlPointInfo controlPoints)
|
||||
{
|
||||
this.controlPoints = controlPoints;
|
||||
|
||||
@@ -27,20 +28,34 @@ namespace osu.Game.Rulesets.Taiko.Audio
|
||||
|
||||
foreach (var s in samplePoints)
|
||||
{
|
||||
var centre = s.GetSampleInfo();
|
||||
var rim = s.GetSampleInfo(SampleInfo.HIT_CLAP);
|
||||
|
||||
// todo: this is ugly
|
||||
centre.Namespace = "taiko";
|
||||
rim.Namespace = "taiko";
|
||||
|
||||
mappings[s.Time] = new DrumSample
|
||||
{
|
||||
Centre = s.GetSampleInfo().GetChannel(audio.Sample, "Taiko"),
|
||||
Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample, "Taiko")
|
||||
Centre = addSound(centre),
|
||||
Rim = addSound(rim)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private SkinnableSound addSound(SampleInfo sampleInfo)
|
||||
{
|
||||
var drawable = new SkinnableSound(sampleInfo);
|
||||
Sounds.Add(drawable);
|
||||
return drawable;
|
||||
}
|
||||
|
||||
public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time).Time];
|
||||
|
||||
public class DrumSample
|
||||
{
|
||||
public SampleChannel Centre;
|
||||
public SampleChannel Rim;
|
||||
public SkinnableSound Centre;
|
||||
public SkinnableSound Rim;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,16 +101,16 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
// The duration of the taiko hit object
|
||||
double taikoDuration = distance / taikoVelocity;
|
||||
|
||||
// For some reason, old osu! always uses speedAdjustment to determine the taiko velocity, but
|
||||
// only uses it to determine osu! velocity if beatmap version < 8. Let's account for that here.
|
||||
if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
|
||||
speedAdjustedBeatLength *= speedAdjustment;
|
||||
|
||||
// The velocity of the osu! hit object - calculated as the velocity of a slider
|
||||
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
|
||||
// The duration of the osu! hit object
|
||||
double osuDuration = distance / osuVelocity;
|
||||
|
||||
// osu-stable always uses the speed-adjusted beatlength to determine the velocities, but
|
||||
// only uses it for tick rate if beatmap version < 8
|
||||
if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
|
||||
speedAdjustedBeatLength *= speedAdjustment;
|
||||
|
||||
// If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
|
||||
double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans);
|
||||
|
||||
|
||||
@@ -82,8 +82,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
return;
|
||||
|
||||
int countHit = NestedHitObjects.Count(o => o.IsHit);
|
||||
|
||||
if (countHit > HitObject.RequiredGoodHits)
|
||||
if (countHit >= HitObject.RequiredGoodHits)
|
||||
{
|
||||
AddJudgement(new TaikoJudgement { Result = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good });
|
||||
if (HitObject.IsStrong)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@@ -38,30 +37,27 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (timeOffset > HitObject.HitWindowGood)
|
||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||
AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
|
||||
return;
|
||||
}
|
||||
|
||||
double hitOffset = Math.Abs(timeOffset);
|
||||
|
||||
if (hitOffset > HitObject.HitWindowMiss)
|
||||
var result = HitObject.HitWindows.ResultFor(timeOffset);
|
||||
if (result == HitResult.None)
|
||||
return;
|
||||
|
||||
if (!validKeyPressed)
|
||||
if (!validKeyPressed || result == HitResult.Miss)
|
||||
AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
|
||||
else if (hitOffset < HitObject.HitWindowGood)
|
||||
else
|
||||
{
|
||||
AddJudgement(new TaikoJudgement
|
||||
{
|
||||
Result = hitOffset < HitObject.HitWindowGreat ? HitResult.Great : HitResult.Good,
|
||||
Result = result,
|
||||
Final = !HitObject.IsStrong
|
||||
});
|
||||
|
||||
SecondHitAllowed = true;
|
||||
}
|
||||
else
|
||||
AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
|
||||
}
|
||||
|
||||
public override bool OnPressed(TaikoAction action)
|
||||
@@ -90,7 +86,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
switch (State.Value)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
this.Delay(HitObject.HitWindowMiss).Expire();
|
||||
this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire();
|
||||
break;
|
||||
case ArmedState.Miss:
|
||||
this.FadeOut(100)
|
||||
|
||||
@@ -1,35 +1,9 @@
|
||||
// Copyright (c) 2007-2018 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.Beatmaps.ControlPoints;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects
|
||||
{
|
||||
public class Hit : TaikoHitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The hit window that results in a "GREAT" hit.
|
||||
/// </summary>
|
||||
public double HitWindowGreat = 35;
|
||||
|
||||
/// <summary>
|
||||
/// The hit window that results in a "GOOD" hit.
|
||||
/// </summary>
|
||||
public double HitWindowGood = 80;
|
||||
|
||||
/// <summary>
|
||||
/// The hit window that results in a "MISS".
|
||||
/// </summary>
|
||||
public double HitWindowMiss = 95;
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
HitWindowGreat = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 50, 35, 20);
|
||||
HitWindowGood = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 120, 80, 50);
|
||||
HitWindowMiss = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 135, 95, 70);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,15 +35,13 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
||||
{
|
||||
bool hitButton = true;
|
||||
|
||||
Frames.Add(new TaikoReplayFrame(-100000, ReplayButtonState.None));
|
||||
Frames.Add(new TaikoReplayFrame(Beatmap.HitObjects[0].StartTime - 1000, ReplayButtonState.None));
|
||||
Frames.Add(new TaikoReplayFrame(-100000));
|
||||
Frames.Add(new TaikoReplayFrame(Beatmap.HitObjects[0].StartTime - 1000));
|
||||
|
||||
for (int i = 0; i < Beatmap.HitObjects.Count; i++)
|
||||
{
|
||||
TaikoHitObject h = Beatmap.HitObjects[i];
|
||||
|
||||
ReplayButtonState button;
|
||||
|
||||
IHasEndTime endTimeData = h as IHasEndTime;
|
||||
double endTime = endTimeData?.EndTime ?? h.StartTime;
|
||||
|
||||
@@ -59,24 +57,26 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
||||
double hitRate = Math.Min(swell_hit_speed, swell.Duration / req);
|
||||
for (double j = h.StartTime; j < endTime; j += hitRate)
|
||||
{
|
||||
TaikoAction action;
|
||||
|
||||
switch (d)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
button = ReplayButtonState.Left1;
|
||||
action = TaikoAction.LeftCentre;
|
||||
break;
|
||||
case 1:
|
||||
button = ReplayButtonState.Right1;
|
||||
action = TaikoAction.LeftRim;
|
||||
break;
|
||||
case 2:
|
||||
button = ReplayButtonState.Left2;
|
||||
action = TaikoAction.RightCentre;
|
||||
break;
|
||||
case 3:
|
||||
button = ReplayButtonState.Right2;
|
||||
action = TaikoAction.RightRim;
|
||||
break;
|
||||
}
|
||||
|
||||
Frames.Add(new TaikoReplayFrame(j, button));
|
||||
Frames.Add(new TaikoReplayFrame(j, action));
|
||||
d = (d + 1) % 4;
|
||||
if (++count == req)
|
||||
break;
|
||||
@@ -86,39 +86,39 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
||||
{
|
||||
foreach (var tick in drumRoll.NestedHitObjects.OfType<DrumRollTick>())
|
||||
{
|
||||
Frames.Add(new TaikoReplayFrame(tick.StartTime, hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2));
|
||||
Frames.Add(new TaikoReplayFrame(tick.StartTime, hitButton ? TaikoAction.LeftCentre : TaikoAction.RightCentre));
|
||||
hitButton = !hitButton;
|
||||
}
|
||||
}
|
||||
else if (hit != null)
|
||||
{
|
||||
TaikoAction[] actions;
|
||||
|
||||
if (hit is CentreHit)
|
||||
{
|
||||
if (h.IsStrong)
|
||||
button = ReplayButtonState.Right1 | ReplayButtonState.Right2;
|
||||
else
|
||||
button = hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2;
|
||||
actions = h.IsStrong
|
||||
? new[] { TaikoAction.LeftCentre, TaikoAction.RightCentre }
|
||||
: new[] { hitButton ? TaikoAction.LeftCentre : TaikoAction.RightCentre };
|
||||
}
|
||||
else
|
||||
{
|
||||
if (h.IsStrong)
|
||||
button = ReplayButtonState.Left1 | ReplayButtonState.Left2;
|
||||
else
|
||||
button = hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2;
|
||||
actions = h.IsStrong
|
||||
? new[] { TaikoAction.LeftRim, TaikoAction.RightRim }
|
||||
: new[] { hitButton ? TaikoAction.LeftRim : TaikoAction.RightRim };
|
||||
}
|
||||
|
||||
Frames.Add(new TaikoReplayFrame(h.StartTime, button));
|
||||
Frames.Add(new TaikoReplayFrame(h.StartTime, actions));
|
||||
}
|
||||
else
|
||||
throw new InvalidOperationException("Unknown hit object type.");
|
||||
|
||||
Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY, ReplayButtonState.None));
|
||||
Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY));
|
||||
|
||||
if (i < Beatmap.HitObjects.Count - 1)
|
||||
{
|
||||
double waitTime = Beatmap.HitObjects[i + 1].StartTime - 1000;
|
||||
if (waitTime > endTime)
|
||||
Frames.Add(new TaikoReplayFrame(waitTime, ReplayButtonState.None));
|
||||
Frames.Add(new TaikoReplayFrame(waitTime));
|
||||
}
|
||||
|
||||
hitButton = !hitButton;
|
||||
|
||||
@@ -3,31 +3,20 @@
|
||||
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Replays
|
||||
{
|
||||
internal class TaikoFramedReplayInputHandler : FramedReplayInputHandler
|
||||
internal class TaikoFramedReplayInputHandler : FramedReplayInputHandler<TaikoReplayFrame>
|
||||
{
|
||||
public TaikoFramedReplayInputHandler(Replay replay)
|
||||
: base(replay)
|
||||
{
|
||||
}
|
||||
|
||||
public override List<InputState> GetPendingStates()
|
||||
{
|
||||
var actions = new List<TaikoAction>();
|
||||
protected override bool IsImportant(TaikoReplayFrame frame) => frame.Actions.Any();
|
||||
|
||||
if (CurrentFrame?.MouseRight1 == true)
|
||||
actions.Add(TaikoAction.LeftCentre);
|
||||
if (CurrentFrame?.MouseRight2 == true)
|
||||
actions.Add(TaikoAction.RightCentre);
|
||||
if (CurrentFrame?.MouseLeft1 == true)
|
||||
actions.Add(TaikoAction.LeftRim);
|
||||
if (CurrentFrame?.MouseLeft2 == true)
|
||||
actions.Add(TaikoAction.RightRim);
|
||||
|
||||
return new List<InputState> { new ReplayState<TaikoAction> { PressedActions = actions } };
|
||||
}
|
||||
public override List<InputState> GetPendingStates() => new List<InputState> { new ReplayState<TaikoAction> { PressedActions = CurrentFrame.Actions } };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,34 @@
|
||||
// Copyright (c) 2007-2018 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.Beatmaps;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Replays.Legacy;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Replays
|
||||
{
|
||||
public class TaikoReplayFrame : ReplayFrame
|
||||
public class TaikoReplayFrame : ReplayFrame, IConvertibleReplayFrame
|
||||
{
|
||||
public override bool IsImportant => MouseLeft || MouseRight;
|
||||
public List<TaikoAction> Actions = new List<TaikoAction>();
|
||||
|
||||
public TaikoReplayFrame(double time, ReplayButtonState buttons)
|
||||
: base(time, null, null, buttons)
|
||||
public TaikoReplayFrame()
|
||||
{
|
||||
}
|
||||
|
||||
public TaikoReplayFrame(double time, params TaikoAction[] actions)
|
||||
: base(time)
|
||||
{
|
||||
Actions.AddRange(actions);
|
||||
}
|
||||
|
||||
public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap)
|
||||
{
|
||||
if (legacyFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim);
|
||||
if (legacyFrame.MouseRight2) Actions.Add(TaikoAction.RightRim);
|
||||
if (legacyFrame.MouseLeft1) Actions.Add(TaikoAction.LeftCentre);
|
||||
if (legacyFrame.MouseLeft2) Actions.Add(TaikoAction.RightCentre);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user