1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 07:27:25 +08:00

Resolve merge conflicts

This commit is contained in:
TocoToucan 2018-03-05 22:24:25 +03:00
commit 697ad63d72
199 changed files with 4110 additions and 767 deletions

View File

@ -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 @@
Subproject commit 16a4bef775a49166f38faa6e952d83d8823fe3e0
Subproject commit 71900dc350bcebbb60d912d4023a1d2a6bbbc3c1

View File

@ -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;
@ -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");
}
}
}

View File

@ -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)
{

View File

@ -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;

View File

@ -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));
}

View File

@ -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
},
};
}

View File

@ -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;
}
}
}

View File

@ -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
}]
}]
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
@ -15,6 +16,7 @@ using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestCaseFruitObjects : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]

View File

@ -1,8 +1,11 @@
// 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;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
{
public TestCasePerformancePoints()

View File

@ -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();

View File

@ -95,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" />
@ -128,6 +129,10 @@
<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" />
<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">

View File

@ -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;
}
}

View File

@ -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)
{

View File

@ -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;

View File

@ -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(),
};
}
}

View File

@ -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;

View File

@ -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 } };
}
}

View File

@ -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;
}
}
}
}

View File

@ -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
}
]
}
]
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -1,14 +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
{
[TestFixture]
public class TestCaseAutoGeneration : OsuTestCase
{
[Test]
@ -18,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();
@ -26,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]
@ -39,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();
@ -47,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]
@ -58,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 });
@ -67,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]
@ -80,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 });
@ -89,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]
@ -101,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 });
@ -112,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]
@ -128,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 });
@ -139,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]
@ -154,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 });
@ -164,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));
}
}

View File

@ -1,8 +1,11 @@
// 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;
namespace osu.Game.Rulesets.Mania.Tests
{
[TestFixture]
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
{
public TestCasePerformancePoints()

View File

@ -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;
@ -103,7 +104,7 @@ namespace osu.Game.Rulesets.Mania.UI
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);
}

View File

@ -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;

View File

@ -126,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" />
@ -159,6 +160,10 @@
<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" />
<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">

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -5,7 +5,9 @@ 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;
@ -29,5 +31,7 @@ namespace osu.Game.Rulesets.Osu.Edit
};
protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both };
protected override HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new OsuHitObjectOverlayLayer();
}
}

View File

@ -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

View File

@ -8,6 +8,7 @@ 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
@ -25,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;
}
}
@ -34,17 +38,20 @@ 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))

View File

@ -78,8 +78,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0;
List<Vector2> curve = drawableSlider.Body.CurrentCurve;
var positionOnCurve = isRepeatAtEnd ? end : start;
Position = positionOnCurve + drawableSlider.HitObject.StackOffset;
Position = isRepeatAtEnd ? end : start;
if (curve.Count < 2)
return;
@ -90,10 +89,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
// 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] == positionOnCurve)
if (curve[i] == Position)
continue;
Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - positionOnCurve.Y, curve[i].X - positionOnCurve.X));
Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - Position.Y, curve[i].X - Position.X));
break;
}
}

View File

@ -12,6 +12,7 @@ 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;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
@ -22,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;
@ -30,7 +33,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
slider = s;
DrawableSliderTail tail;
Position = s.StackedPosition;
Container<DrawableSliderTick> ticks;
Container<DrawableRepeatPoint> repeatPoints;
@ -39,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);
@ -60,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.StackedPosition
};
var drawableTick = new DrawableSliderTick(tick) { Position = tick.Position - s.Position };
ticks.Add(drawableTick);
components.Add(drawableTick);
@ -77,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.StackedPosition
};
var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) { Position = repeatPoint.Position - s.Position };
repeatPoints.Add(drawableRepeatPoint);
components.Add(drawableRepeatPoint);
@ -107,11 +105,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
if (!HeadCircle.IsHit)
HeadCircle.Position = slider.StackedPositionAt(completionProgress);
HeadCircle.Position = slider.CurvePositionAt(completionProgress);
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)
@ -154,13 +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 bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos);
public override Vector2 SelectionPoint => ToScreenSpace(Body.Position);
public override Vector2 SelectionPoint => ToScreenSpace(OriginPosition);
public override Quad SelectionQuad => Body.PathDrawQuad;
}
}

View File

@ -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)

View File

@ -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) });
}
}
}

View File

@ -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)

View File

@ -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,
}
};
}
}
}

View File

@ -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);
}
}
}

View File

@ -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",

View File

@ -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
}
}
};
});
}
}
}

View File

@ -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;
@ -141,7 +142,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public void UpdateProgress(double completionProgress)
{
Position = slider.StackedPositionAt(completionProgress);
Position = slider.CurvePositionAt(completionProgress);
}
}
}

View File

@ -29,6 +29,13 @@ 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>();
@ -75,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)
{
@ -84,6 +93,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
container = new BufferedContainer
{
RelativeSizeAxes = Axes.Both,
CacheDrawnFrameBuffer = true,
Children = new Drawable[]
{
@ -107,11 +117,11 @@ 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();
}
@ -121,6 +131,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private void load()
{
reloadTexture();
computeSize();
}
private void reloadTexture()
@ -164,7 +175,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
path.Texture = texture;
}
public 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;
@ -176,7 +199,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
path.ClearVertices();
foreach (Vector2 p in CurrentCurve)
path.AddVertex(p - CurrentCurve[0]);
path.AddVertex(p);
return true;
}

View File

@ -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();
@ -99,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Objects
HeadCircle = new HitCircle
{
StartTime = StartTime,
Position = StackedPosition,
Position = Position,
IndexInCurrentCombo = IndexInCurrentCombo,
ComboColour = ComboColour,
Samples = Samples,
@ -109,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Objects
TailCircle = new HitCircle
{
StartTime = EndTime,
Position = StackedEndPosition,
Position = EndPosition,
IndexInCurrentCombo = IndexInCurrentCombo,
ComboColour = ComboColour
};
@ -120,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;
@ -156,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,
@ -175,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,

View File

@ -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);
}
}
}

View File

@ -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)
{

View File

@ -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++)
{
@ -91,18 +91,18 @@ namespace osu.Game.Rulesets.Osu.Replays
// Make the cursor stay at a hitObject as long as possible (mainly for autopilot).
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.HitWindows.HalfWindowFor(HitResult.Meh), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(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.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50)
{
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(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.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50)
{
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(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)));
}
}
@ -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)
{
@ -316,10 +319,10 @@ namespace osu.Game.Rulesets.Osu.Replays
for (double j = FrameDelay; j < s.Duration; j += FrameDelay)
{
Vector2 pos = s.StackedPositionAt(j / s.Duration);
AddFrameToReplay(new ReplayFrame(h.StartTime + j, pos.X, pos.Y, button));
AddFrameToReplay(new OsuReplayFrame(h.StartTime + j, new Vector2(pos.X, pos.Y), action));
}
AddFrameToReplay(new ReplayFrame(s.EndTime, s.StackedEndPosition.X, s.StackedEndPosition.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!

View File

@ -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);
}
}
}

View File

@ -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
}
};
}

View File

@ -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
}]
}
]
}

View File

@ -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

View File

@ -0,0 +1,13 @@
{
"Mappings": [{
"StartTime": 118858,
"Objects": [{
"StartTime": 118858,
"EndTime": 119088,
"StartX": 219,
"StartY": 215,
"EndX": 239.6507,
"EndY": 29.1437378
}]
}]
}

View File

@ -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:

View File

@ -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);
}
}

View File

@ -16,10 +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
{
[TestFixture]
public class TestCaseHitCircle : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]

View File

@ -4,10 +4,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Game.Rulesets.Osu.Mods;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class TestCaseHitCircleHidden : TestCaseHitCircle
{
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();

View File

@ -1,8 +1,11 @@
// 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;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
{
public TestCasePerformancePoints()

View File

@ -15,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;
@ -23,6 +24,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class TestCaseSlider : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
@ -118,8 +120,8 @@ 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,
@ -139,9 +141,9 @@ 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,
RepeatCount = repeats,
@ -163,12 +165,12 @@ namespace osu.Game.Rulesets.Osu.Tests
ComboColour = Color4.LightSeaGreen,
ControlPoints = new List<Vector2>
{
new Vector2(-200, 0),
new Vector2(-50, 75),
new Vector2(0, 100),
new Vector2(100, -200),
Vector2.Zero,
new Vector2(150, 75),
new Vector2(200, 0),
new Vector2(230, 0)
new Vector2(300, -200),
new Vector2(400, 0),
new Vector2(430, 0)
},
Distance = 793.4417,
RepeatCount = repeats,
@ -190,11 +192,11 @@ namespace osu.Game.Rulesets.Osu.Tests
ComboColour = Color4.LightSeaGreen,
ControlPoints = new List<Vector2>
{
new Vector2(-200, 0),
new Vector2(-50, 75),
new Vector2(0, 100),
new Vector2(100, -200),
new Vector2(230, 0)
Vector2.Zero,
new Vector2(150, 75),
new Vector2(200, 100),
new Vector2(300, -200),
new Vector2(430, 0)
},
Distance = 480,
RepeatCount = repeats,
@ -216,7 +218,7 @@ namespace osu.Game.Rulesets.Osu.Tests
ComboColour = Color4.LightSeaGreen,
ControlPoints = new List<Vector2>
{
new Vector2(0, 0),
Vector2.Zero,
new Vector2(-200, 0),
new Vector2(0, 0),
new Vector2(0, -200),
@ -247,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,

View File

@ -4,10 +4,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Game.Rulesets.Osu.Mods;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class TestCaseSliderHidden : TestCaseSlider
{
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
@ -16,6 +17,7 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class TestCaseSpinner : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]

View File

@ -4,10 +4,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Game.Rulesets.Osu.Mods;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class TestCaseSpinnerHidden : TestCaseSpinner
{
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();

View File

@ -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,7 +49,7 @@ 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 GetAspectAdjustedSize()
{

View File

@ -64,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" />
@ -92,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" />
@ -121,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" />
@ -167,6 +174,12 @@
<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" />
<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">

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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 } };
}
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,209 @@
{
"Mappings": [{
"StartTime": 500,
"Objects": [{
"StartTime": 500,
"EndTime": 2499,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
},
{
"StartTime": 3000,
"Objects": [{
"StartTime": 3000,
"EndTime": 4000,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": true,
"IsStrong": false
}]
},
{
"StartTime": 4500,
"Objects": [{
"StartTime": 4500,
"EndTime": 5500,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": true,
"IsStrong": false
}]
},
{
"StartTime": 6000,
"Objects": [{
"StartTime": 6000,
"EndTime": 6500,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": true,
"IsStrong": false
}]
},
{
"StartTime": 7000,
"Objects": [{
"StartTime": 7000,
"EndTime": 7000,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 7249,
"EndTime": 7249,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 7499,
"EndTime": 7499,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 7749,
"EndTime": 7749,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 7999,
"EndTime": 7999,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
}
]
},
{
"StartTime": 8500,
"Objects": [{
"StartTime": 8500,
"EndTime": 10999,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
},
{
"StartTime": 11500,
"Objects": [{
"StartTime": 11500,
"EndTime": 12000,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": true,
"IsStrong": false
}]
},
{
"StartTime": 12500,
"Objects": [{
"StartTime": 12500,
"EndTime": 16499,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
},
{
"StartTime": 17000,
"Objects": [{
"StartTime": 17000,
"EndTime": 17000,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 17249,
"EndTime": 17249,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 17499,
"EndTime": 17499,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 17749,
"EndTime": 17749,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 17999,
"EndTime": 17999,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
}
]
},
{
"StartTime": 18500,
"Objects": [{
"StartTime": 18500,
"EndTime": 19450,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": true,
"IsStrong": false
}]
},
{
"StartTime": 19875,
"Objects": [{
"StartTime": 19875,
"EndTime": 23874,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
}
]
}

View File

@ -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

View File

@ -0,0 +1,87 @@
{
"Mappings": [{
"StartTime": 6590,
"Objects": [{
"StartTime": 6590,
"EndTime": 8320,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
},
{
"StartTime": 8436,
"Objects": [{
"StartTime": 8436,
"EndTime": 10166,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
},
{
"StartTime": 10282,
"Objects": [{
"StartTime": 10282,
"EndTime": 12012,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
},
{
"StartTime": 12128,
"Objects": [{
"StartTime": 12128,
"EndTime": 13858,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
},
{
"StartTime": 41666,
"Objects": [{
"StartTime": 41666,
"EndTime": 42589,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
},
{
"StartTime": 62666,
"Objects": [{
"StartTime": 62666,
"EndTime": 63127,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
},
{
"StartTime": 208743,
"Objects": [{
"StartTime": 208743,
"EndTime": 209204,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
}
]
}

View File

@ -0,0 +1,25 @@
osu file format v14
[Difficulty]
HPDrainRate:6
CircleSize:4.2
OverallDifficulty:9
ApproachRate:9.8
SliderMultiplier:1.87
SliderTickRate:1
[TimingPoints]
6590,461.538461538462,4,2,2,15,1,0
6590,-200,4,2,2,15,0,0
49051,230.769230769231,4,2,1,15,1,0
62666,-200,4,2,1,60,0,0
197666,-100,4,2,1,85,0,1
[HitObjects]
88,104,6590,6,0,B|176:156|256:108|256:108|336:60|423:112,1,350.625,6|0,0:0|0:0,0:0:0:0:
396,213,8436,2,0,P|277:247|376:172,1,350.625,6|0,0:0|0:0,0:0:0:0:
472,220,10282,2,0,P|456:288|220:300,1,350.625,6|0,0:0|0:0,0:0:0:0:
277,200,12128,2,0,P|398:225|276:244,1,350.625,6|0,0:0|0:0,0:0:0:0:
268,229,41666,2,0,L|473:210,1,187,2|2,0:0|0:0,0:0:0:0:
133,342,62666,2,0,B|132:316|132:316|128:316|128:316|130:295|130:295|126:296|126:296|129:275|129:275|125:275|125:275|127:254|127:254|123:255|123:255|125:234|125:234|121:234|121:234|123:213|123:213|119:214|119:214|121:193|121:193|118:193|118:193|118:172,1,187,8|8,0:0|0:0,0:0:0:0:
481,338,208743,6,0,P|492:262|383:195,2,187,2|8|2,0:0|0:0|0:0,0:0:0:0:

View File

@ -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.Replays.Types;
using osu.Game.Rulesets.Taiko.Replays;
namespace osu.Game.Rulesets.Taiko
{
@ -103,6 +105,8 @@ namespace osu.Game.Rulesets.Taiko
public override int LegacyID => 1;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame();
public TaikoRuleset(RulesetInfo rulesetInfo = null)
: base(rulesetInfo)
{

View File

@ -0,0 +1,73 @@
// 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.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Taiko.Tests
{
public class TaikoBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
private bool isForCurrentRuleset;
[NonParallelizable]
[TestCase("basic", false), Ignore("See: https://github.com/ppy/osu/issues/2152")]
[TestCase("slider-generating-drumroll", false)]
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,
IsRim = hitObject is RimHit,
IsCentre = hitObject is CentreHit,
IsDrumRoll = hitObject is DrumRoll,
IsSwell = hitObject is Swell,
IsStrong = ((TaikoHitObject)hitObject).IsStrong
};
}
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new TaikoBeatmapConverter(isForCurrentRuleset);
}
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 bool IsRim;
public bool IsCentre;
public bool IsDrumRoll;
public bool IsSwell;
public bool IsStrong;
public bool Equals(ConvertValue other)
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
&& Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience)
&& IsRim == other.IsRim
&& IsCentre == other.IsCentre
&& IsDrumRoll == other.IsDrumRoll
&& IsSwell == other.IsSwell
&& IsStrong == other.IsStrong;
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -14,6 +15,7 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests
{
[TestFixture]
public class TestCaseInputDrum : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]

View File

@ -1,8 +1,11 @@
// 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;
namespace osu.Game.Rulesets.Taiko.Tests
{
[TestFixture]
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
{
public TestCasePerformancePoints()

View File

@ -4,7 +4,6 @@
using System;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
@ -34,9 +33,9 @@ namespace osu.Game.Rulesets.Taiko.UI
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
private void load()
{
var sampleMappings = new DrumSampleMapping(controlPoints, audio);
var sampleMappings = new DrumSampleMapping(controlPoints);
Children = new Drawable[]
{
@ -63,6 +62,8 @@ namespace osu.Game.Rulesets.Taiko.UI
CentreAction = TaikoAction.RightCentre
}
};
AddRangeInternal(sampleMappings.Sounds);
}
/// <summary>

View File

@ -17,6 +17,7 @@ using osu.Game.Rulesets.Taiko.Replays;
using OpenTK;
using System.Linq;
using osu.Framework.Input;
using osu.Game.Input.Handlers;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Taiko.UI
@ -133,6 +134,6 @@ namespace osu.Game.Rulesets.Taiko.UI
return null;
}
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay);
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay);
}
}

View File

@ -112,6 +112,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Scoring\TaikoScoreProcessor.cs" />
<Compile Include="TaikoInputManager.cs" />
<Compile Include="Tests\TaikoBeatmapConversionTest.cs" />
<Compile Include="Tests\TestCaseInputDrum.cs" />
<Compile Include="Tests\TestCasePerformancePoints.cs" />
<Compile Include="Tests\TestCaseTaikoPlayfield.cs" />
@ -145,6 +146,12 @@
<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\slider-generating-drumroll-expected-conversion.json" />
<EmbeddedResource Include="Resources\Testing\Beatmaps\slider-generating-drumroll.osu" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<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">

View File

@ -20,7 +20,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
[Test]
public void TestDecodeBeatmapGeneral()
{
var decoder = new LegacyBeatmapDecoder();
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
@ -102,7 +102,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(4, difficulty.CircleSize);
Assert.AreEqual(8, difficulty.OverallDifficulty);
Assert.AreEqual(9, difficulty.ApproachRate);
Assert.AreEqual(1.8f, difficulty.SliderMultiplier);
Assert.AreEqual(1.8, difficulty.SliderMultiplier);
Assert.AreEqual(2, difficulty.SliderTickRate);
}
}
@ -110,7 +110,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
[Test]
public void TestDecodeBeatmapEvents()
{
var decoder = new LegacyBeatmapDecoder();
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
@ -128,7 +128,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
[Test]
public void TestDecodeBeatmapTimingPoints()
{
var decoder = new LegacyBeatmapDecoder();
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
@ -187,7 +187,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
[Test]
public void TestDecodeBeatmapHitObjects()
{
var decoder = new LegacyBeatmapDecoder();
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{

View File

@ -85,7 +85,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(4, difficulty.CircleSize);
Assert.AreEqual(8, difficulty.OverallDifficulty);
Assert.AreEqual(9, difficulty.ApproachRate);
Assert.AreEqual(1.8f, difficulty.SliderMultiplier);
Assert.AreEqual(1.8, difficulty.SliderMultiplier);
Assert.AreEqual(2, difficulty.SliderTickRate);
}
@ -159,7 +159,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
using (var sr = new StreamReader(stream))
{
var legacyDecoded = new LegacyBeatmapDecoder().DecodeBeatmap(sr);
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.DecodeBeatmap(sr);
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms))
using (var sr2 = new StreamReader(ms))

View File

@ -58,7 +58,7 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile);
Assert.AreEqual("Deif", meta.AuthorString);
Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile);
Assert.AreEqual(164471, meta.PreviewTime);
Assert.AreEqual(164471 + LegacyBeatmapDecoder.UniversalOffset, meta.PreviewTime);
Assert.AreEqual(string.Empty, meta.Source);
Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags);
Assert.AreEqual("Renatus", meta.Title);

View File

@ -1,8 +1,11 @@
// 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;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseAllPlayers : TestCasePlayer
{
}

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using NUnit.Framework;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@ -17,6 +18,7 @@ using osu.Framework.Lists;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseBeatSyncedContainer : OsuTestCase
{
private readonly MusicController mc;

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
@ -17,6 +18,7 @@ using osu.Game.Screens.Select.Filter;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseBeatmapCarousel : OsuTestCase
{
private TestBeatmapCarousel carousel;
@ -207,6 +209,12 @@ namespace osu.Game.Tests.Visual
checkVisibleItemCount(true, 0);
AddAssert("Selection is null", () => currentSelection == null);
advanceSelection(true);
AddAssert("Selection is null", () => currentSelection == null);
advanceSelection(false);
AddAssert("Selection is null", () => currentSelection == null);
AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false));
AddAssert("Selection is non-null", () => currentSelection != null);

View File

@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
@ -18,6 +19,7 @@ using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseBeatmapInfoWedge : OsuTestCase
{
private RulesetStore rulesets;

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Overlays;
@ -12,6 +13,7 @@ using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseBeatmapSetOverlay : OsuTestCase
{
private readonly BeatmapSetOverlay overlay;

View File

@ -1,11 +1,13 @@
// 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.Game.Graphics.UserInterface;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseBreadcrumbs : OsuTestCase
{
public TestCaseBreadcrumbs()

View File

@ -5,9 +5,11 @@ using osu.Framework.Timing;
using osu.Game.Beatmaps.Timing;
using osu.Game.Screens.Play.BreaksOverlay;
using System.Collections.Generic;
using NUnit.Framework;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseBreakOverlay : OsuTestCase
{
private readonly BreakOverlay breakOverlay;

View File

@ -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 NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Shapes;
@ -9,6 +10,7 @@ using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseButtonSystem : OsuTestCase
{
public TestCaseButtonSystem()

View File

@ -12,12 +12,14 @@ using osu.Game.Users;
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseChatLink : OsuTestCase
{
private readonly TestChatLineContainer textContainer;

View File

@ -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 NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
@ -13,6 +14,7 @@ using osu.Game.Graphics.Cursor;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseContextMenu : OsuTestCase
{
private const int start_time = 0;

View File

@ -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 NUnit.Framework;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -16,6 +17,7 @@ using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseCursors : OsuTestCase
{
private readonly ManualInputManager inputManager;

View File

@ -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 NUnit.Framework;
using osu.Game.Graphics;
using osu.Game.Overlays;
using osu.Game.Overlays.Dialog;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseDialogOverlay : OsuTestCase
{
public TestCaseDialogOverlay()

Some files were not shown because too many files have changed in this diff Show More