mirror of
https://github.com/ppy/osu.git
synced 2026-05-21 01:39:54 +08:00
Compare commits
731 Commits
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ install:
|
||||
- cmd: git submodule update --init --recursive --depth=5
|
||||
- cmd: choco install resharper-clt -y
|
||||
- cmd: choco install nvika -y
|
||||
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.3/CodeFileSanity.exe
|
||||
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.4/CodeFileSanity.exe
|
||||
before_build:
|
||||
- cmd: CodeFileSanity.exe
|
||||
- cmd: nuget restore -verbosity quiet
|
||||
|
||||
+1
-1
Submodule osu-framework updated: 8f36ddab94...458ebc2d46
+1
-1
Submodule osu-resources updated: 7724abdf1d...92ec3d10b1
@@ -111,16 +111,11 @@ namespace osu.Desktop
|
||||
{
|
||||
var filePaths = new [] { e.FileName };
|
||||
|
||||
if (filePaths.All(f => Path.GetExtension(f) == @".osz"))
|
||||
Task.Factory.StartNew(() => BeatmapManager.Import(filePaths), TaskCreationOptions.LongRunning);
|
||||
else if (filePaths.All(f => Path.GetExtension(f) == @".osr"))
|
||||
Task.Run(() =>
|
||||
{
|
||||
var score = ScoreStore.ReadReplayFile(filePaths.First());
|
||||
Schedule(() => LoadScore(score));
|
||||
});
|
||||
}
|
||||
var firstExtension = Path.GetExtension(filePaths.First());
|
||||
|
||||
private static readonly string[] allowed_extensions = { @".osz", @".osr" };
|
||||
if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
|
||||
|
||||
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace osu.Desktop
|
||||
{
|
||||
if (!host.IsPrimaryInstance)
|
||||
{
|
||||
var importer = new BeatmapIPCChannel(host);
|
||||
var importer = new ArchiveImportIPCChannel(host);
|
||||
// Restore the cwd so relative paths given at the command line work correctly
|
||||
Directory.SetCurrentDirectory(cwd);
|
||||
foreach (var file in args)
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SharpCompress, Version=0.18.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
<description>click the circles. to the beat.</description>
|
||||
<summary>click the circles.</summary>
|
||||
<releaseNotes>testing</releaseNotes>
|
||||
<copyright>Copyright ppy Pty Ltd 2007-2017</copyright>
|
||||
<copyright>Copyright ppy Pty Ltd 2007-2018</copyright>
|
||||
<language>en-AU</language>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="*.exe" target="lib\net45\" exclude="**vshost**"/>
|
||||
<file src="*.dll" target="lib\net45\"/>
|
||||
<file src="*.dll" target="lib\net45\"/>
|
||||
<file src="*.config" target="lib\net45\"/>
|
||||
<file src="x86\*.dll" target="lib\net45\x86\"/>
|
||||
<file src="x64\*.dll" target="lib\net45\x64\"/>
|
||||
|
||||
@@ -6,7 +6,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
|
||||
<packages>
|
||||
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" />
|
||||
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.13" targetFramework="net461" />
|
||||
<package id="SharpCompress" version="0.18.1" targetFramework="net461" />
|
||||
<package id="Splat" version="2.0.0" targetFramework="net45" />
|
||||
<package id="SQLitePCLRaw.bundle_green" version="1.1.8" targetFramework="net461" />
|
||||
|
||||
@@ -23,6 +23,5 @@ namespace osu.Game.Rulesets.Catch
|
||||
MoveRight,
|
||||
[Description("Engage dash")]
|
||||
Dash,
|
||||
PositionUpdate
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
if (CheckPosition == null) return;
|
||||
|
||||
if (timeOffset > 0)
|
||||
if (timeOffset >= 0)
|
||||
AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss });
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
AddNested(getVisualRepresentation?.Invoke(o));
|
||||
}
|
||||
|
||||
protected override bool ProvidesJudgement => false;
|
||||
|
||||
protected override void AddNested(DrawableHitObject h)
|
||||
{
|
||||
var catchObject = (DrawableCatchHitObject)h;
|
||||
|
||||
@@ -21,9 +21,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
/// </summary>
|
||||
private const float base_scoring_distance = 100;
|
||||
|
||||
public readonly SliderCurve Curve = new SliderCurve();
|
||||
|
||||
public int RepeatCount { get; set; } = 1;
|
||||
public int RepeatCount { get; set; }
|
||||
|
||||
public double Velocity;
|
||||
public double TickDistance;
|
||||
@@ -55,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
var length = Curve.Distance;
|
||||
var tickDistance = Math.Min(TickDistance, length);
|
||||
var repeatDuration = length / Velocity;
|
||||
var spanDuration = length / Velocity;
|
||||
|
||||
var minDistanceFromEnd = Velocity * 0.01;
|
||||
|
||||
@@ -67,10 +65,10 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
X = X
|
||||
});
|
||||
|
||||
for (var repeat = 0; repeat < RepeatCount; repeat++)
|
||||
for (var span = 0; span < this.SpanCount(); span++)
|
||||
{
|
||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||
var reversed = repeat % 2 == 1;
|
||||
var spanStartTime = StartTime + span * spanDuration;
|
||||
var reversed = span % 2 == 1;
|
||||
|
||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||
{
|
||||
@@ -80,7 +78,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
var timeProgress = d / length;
|
||||
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
|
||||
|
||||
var lastTickTime = repeatStartTime + timeProgress * repeatDuration;
|
||||
var lastTickTime = spanStartTime + timeProgress * spanDuration;
|
||||
AddNested(new Droplet
|
||||
{
|
||||
StartTime = lastTickTime,
|
||||
@@ -95,17 +93,17 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
});
|
||||
}
|
||||
|
||||
double tinyTickInterval = tickDistance / length * repeatDuration;
|
||||
double tinyTickInterval = tickDistance / length * spanDuration;
|
||||
while (tinyTickInterval > 100)
|
||||
tinyTickInterval /= 2;
|
||||
|
||||
for (double t = 0; t < repeatDuration; t += tinyTickInterval)
|
||||
for (double t = 0; t < spanDuration; t += tinyTickInterval)
|
||||
{
|
||||
double progress = reversed ? 1 - t / repeatDuration : t / repeatDuration;
|
||||
double progress = reversed ? 1 - t / spanDuration : t / spanDuration;
|
||||
|
||||
AddNested(new TinyDroplet
|
||||
{
|
||||
StartTime = repeatStartTime + t,
|
||||
StartTime = spanStartTime + t,
|
||||
ComboColour = ComboColour,
|
||||
X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
@@ -121,15 +119,15 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
Samples = Samples,
|
||||
ComboColour = ComboColour,
|
||||
StartTime = repeatStartTime + repeatDuration,
|
||||
StartTime = spanStartTime + spanDuration,
|
||||
X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity;
|
||||
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
||||
|
||||
public float EndX => Curve.PositionAt(ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH;
|
||||
public float EndX => Curve.PositionAt(this.ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH;
|
||||
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
@@ -139,6 +137,8 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
set { Curve.Distance = value; }
|
||||
}
|
||||
|
||||
public SliderCurve Curve { get; } = new SliderCurve();
|
||||
|
||||
public List<Vector2> ControlPoints
|
||||
{
|
||||
get { return Curve.ControlPoints; }
|
||||
@@ -152,17 +152,5 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
get { return Curve.CurveType; }
|
||||
set { Curve.CurveType = value; }
|
||||
}
|
||||
|
||||
public Vector2 PositionAt(double progress) => Curve.PositionAt(ProgressAt(progress));
|
||||
|
||||
public double ProgressAt(double progress)
|
||||
{
|
||||
double p = progress * RepeatCount % 1;
|
||||
if (RepeatAt(progress) % 2 == 1)
|
||||
p = 1 - p;
|
||||
return p;
|
||||
}
|
||||
|
||||
public int RepeatAt(double progress) => (int)(progress * RepeatCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Users;
|
||||
|
||||
@@ -23,15 +26,78 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
|
||||
public override Replay Generate()
|
||||
{
|
||||
// todo: add support for HT DT
|
||||
const double dash_speed = CatcherArea.Catcher.BASE_SPEED;
|
||||
const double movement_speed = dash_speed / 2;
|
||||
float lastPosition = 0.5f;
|
||||
double lastTime = 0;
|
||||
|
||||
// Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
|
||||
Replay.Frames.Add(new CatchReplayFrame(-100000, 0));
|
||||
Replay.Frames.Add(new CatchReplayFrame(-100000, lastPosition));
|
||||
|
||||
void moveToNext(CatchHitObject h)
|
||||
{
|
||||
float positionChange = Math.Abs(lastPosition - h.X);
|
||||
double timeAvailable = h.StartTime - lastTime;
|
||||
|
||||
//So we can either make it there without a dash or not.
|
||||
double speedRequired = positionChange / timeAvailable;
|
||||
|
||||
bool dashRequired = speedRequired > movement_speed && h.StartTime != 0;
|
||||
|
||||
// todo: get correct catcher size, based on difficulty CS.
|
||||
const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f;
|
||||
|
||||
if (lastPosition - catcher_width_half < h.X && lastPosition + catcher_width_half > h.X)
|
||||
{
|
||||
//we are already in the correct range.
|
||||
lastTime = h.StartTime;
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, lastPosition));
|
||||
return;
|
||||
}
|
||||
|
||||
if (h is BananaShower.Banana)
|
||||
{
|
||||
// auto bananas unrealistically warp to catch 100% combo.
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
}
|
||||
else if (h.HyperDash)
|
||||
{
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition, ReplayButtonState.Right1));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
}
|
||||
else if (dashRequired)
|
||||
{
|
||||
//we do a movement in two parts - the dash part then the normal part...
|
||||
double timeAtNormalSpeed = positionChange / movement_speed;
|
||||
double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable;
|
||||
double timeAtDashSpeed = timeWeNeedToSave / 2;
|
||||
|
||||
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 + timeAtDashSpeed, midPosition));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
}
|
||||
else
|
||||
{
|
||||
double timeBefore = positionChange / movement_speed;
|
||||
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition, ReplayButtonState.Right1));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
}
|
||||
|
||||
lastTime = h.StartTime;
|
||||
lastPosition = h.X;
|
||||
}
|
||||
|
||||
foreach (var obj in Beatmap.HitObjects)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case Fruit _:
|
||||
Replay.Frames.Add(new CatchReplayFrame(obj.StartTime, obj.X));
|
||||
moveToNext(obj);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -42,7 +108,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
case BananaShower.Banana _:
|
||||
case TinyDroplet _:
|
||||
case Droplet _:
|
||||
Replay.Frames.Add(new CatchReplayFrame(nestedObj.StartTime, nestedObj.X));
|
||||
moveToNext(nestedObj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,15 +14,29 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
{
|
||||
}
|
||||
|
||||
public override List<InputState> GetPendingStates() => new List<InputState>
|
||||
public override List<InputState> GetPendingStates()
|
||||
{
|
||||
new CatchReplayState
|
||||
if (!Position.HasValue) return new List<InputState>();
|
||||
|
||||
var action = new List<CatchAction>();
|
||||
|
||||
if (CurrentFrame.ButtonState == ReplayButtonState.Left1)
|
||||
action.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);
|
||||
|
||||
return new List<InputState>
|
||||
{
|
||||
PressedActions = new List<CatchAction> { CatchAction.PositionUpdate },
|
||||
CatcherX = ((CatchReplayFrame)CurrentFrame).MouseX
|
||||
},
|
||||
new CatchReplayState { PressedActions = new List<CatchAction>() },
|
||||
};
|
||||
new CatchReplayState
|
||||
{
|
||||
PressedActions = action,
|
||||
CatcherX = Position.Value.X
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public class CatchReplayState : ReplayState<CatchAction>
|
||||
{
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
{
|
||||
public override bool IsImportant => MouseX > 0;
|
||||
|
||||
public CatchReplayFrame(double time, float? x = null)
|
||||
: base(time, x ?? -1, null, ReplayButtonState.None)
|
||||
public CatchReplayFrame(double time, float? x = null, ReplayButtonState button = ReplayButtonState.None)
|
||||
: base(time, x ?? -1, null, button)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,17 +24,13 @@ namespace osu.Game.Rulesets.Catch.Scoring
|
||||
switch (obj)
|
||||
{
|
||||
case JuiceStream stream:
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
|
||||
foreach (var _ in stream.NestedHitObjects.Cast<CatchHitObject>())
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
break;
|
||||
case BananaShower shower:
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
|
||||
foreach (var _ in shower.NestedHitObjects.Cast<CatchHitObject>())
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
break;
|
||||
case Fruit _:
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
|
||||
@@ -12,7 +12,6 @@ using osu.Game.Rulesets.Catch.UI;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseBananaShower : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
|
||||
@@ -6,7 +6,6 @@ using NUnit.Framework;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public TestCaseCatchPlayer() : base(new CatchRuleset())
|
||||
|
||||
@@ -8,7 +8,6 @@ using osu.Game.Rulesets.Catch.Objects;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public TestCaseCatchStacker()
|
||||
|
||||
@@ -13,7 +13,6 @@ using osu.Game.Tests.Visual;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseCatcherArea : OsuTestCase
|
||||
{
|
||||
private RulesetInfo catchRuleset;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.MathUtils;
|
||||
@@ -16,7 +15,6 @@ using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseFruitObjects : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
|
||||
@@ -8,7 +8,6 @@ using osu.Game.Rulesets.Catch.Objects;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public TestCaseHyperdash()
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
|
||||
@@ -35,10 +35,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
ScaledContent.AddRange(new Drawable[]
|
||||
{
|
||||
content = new Container<Drawable>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
explodingFruitContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@@ -49,7 +45,11 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
ExplodingFruitTarget = explodingFruitContainer,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
}
|
||||
},
|
||||
content = new Container<Drawable>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class CatcherArea : Container, IKeyBindingHandler<CatchAction>
|
||||
public class CatcherArea : Container
|
||||
{
|
||||
public const float CATCHER_SIZE = 172;
|
||||
|
||||
@@ -84,16 +84,14 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(CatchAction action)
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
if (action != CatchAction.PositionUpdate) return false;
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
CatchFramedReplayInputHandler.CatchReplayState state = (CatchFramedReplayInputHandler.CatchReplayState)GetContainingInputManager().CurrentState;
|
||||
var state = GetContainingInputManager().CurrentState as CatchFramedReplayInputHandler.CatchReplayState;
|
||||
|
||||
if (state.CatcherX.HasValue)
|
||||
if (state?.CatcherX != null)
|
||||
MovableCatcher.X = state.CatcherX.Value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OnReleased(CatchAction action) => false;
|
||||
|
||||
@@ -32,14 +32,30 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="JetBrains.Annotations, Version=11.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_green, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a84b7dcfb1391f7f, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_v2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.provider.e_sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9c301db686d0bd12, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Collections" />
|
||||
<Reference Include="System.Core" />
|
||||
@@ -113,11 +129,15 @@
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets'))" />
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" />
|
||||
</Project>
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.13" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.bundle_green" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.8" targetFramework="net461" />
|
||||
</packages>
|
||||
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
/// <summary>
|
||||
/// The definitions for each stage in a <see cref="ManiaPlayfield"/>.
|
||||
/// </summary>
|
||||
public readonly List<StageDefinition> Stages = new List<StageDefinition>();
|
||||
public List<StageDefinition> Stages = new List<StageDefinition>();
|
||||
|
||||
/// <summary>
|
||||
/// Total number of columns represented by all stages in this <see cref="ManiaBeatmap"/>.
|
||||
@@ -24,10 +24,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ManiaBeatmap"/>.
|
||||
/// </summary>
|
||||
/// <param name="initialStage">The initial stage.</param>
|
||||
public ManiaBeatmap(StageDefinition initialStage)
|
||||
/// <param name="defaultStage">The initial stages.</param>
|
||||
public ManiaBeatmap(StageDefinition defaultStage)
|
||||
{
|
||||
Stages.Add(initialStage);
|
||||
Stages.Add(defaultStage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
if (curveData == null)
|
||||
return HitObject.Samples;
|
||||
|
||||
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.RepeatCount;
|
||||
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount();
|
||||
|
||||
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
||||
return curveData.RepeatSamples[index];
|
||||
|
||||
+12
-12
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
private readonly double endTime;
|
||||
private readonly double segmentDuration;
|
||||
private readonly int repeatCount;
|
||||
private readonly int spanCount;
|
||||
|
||||
private PatternType convertType;
|
||||
|
||||
@@ -39,25 +39,25 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
var distanceData = hitObject as IHasDistance;
|
||||
var repeatsData = hitObject as IHasRepeats;
|
||||
|
||||
repeatCount = repeatsData?.RepeatCount ?? 1;
|
||||
spanCount = repeatsData?.SpanCount() ?? 1;
|
||||
|
||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
||||
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(hitObject.StartTime);
|
||||
|
||||
// The true distance, accounting for any repeats
|
||||
double distance = (distanceData?.Distance ?? 0) * repeatCount;
|
||||
double distance = (distanceData?.Distance ?? 0) * spanCount;
|
||||
// The velocity of the osu! hit object - calculated as the velocity of a slider
|
||||
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength;
|
||||
// The duration of the osu! hit object
|
||||
double osuDuration = distance / osuVelocity;
|
||||
|
||||
endTime = hitObject.StartTime + osuDuration;
|
||||
segmentDuration = (endTime - HitObject.StartTime) / repeatCount;
|
||||
segmentDuration = (endTime - HitObject.StartTime) / spanCount;
|
||||
}
|
||||
|
||||
public override Pattern Generate()
|
||||
{
|
||||
if (repeatCount > 1)
|
||||
if (spanCount > 1)
|
||||
{
|
||||
if (segmentDuration <= 90)
|
||||
return generateRandomHoldNotes(HitObject.StartTime, 1);
|
||||
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
if (segmentDuration <= 120)
|
||||
{
|
||||
convertType |= PatternType.ForceNotStack;
|
||||
return generateRandomNotes(HitObject.StartTime, repeatCount + 1);
|
||||
return generateRandomNotes(HitObject.StartTime, spanCount + 1);
|
||||
}
|
||||
|
||||
if (segmentDuration <= 160)
|
||||
@@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
if (duration >= 4000)
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0);
|
||||
|
||||
if (segmentDuration > 400 && repeatCount < TotalColumns - 1 - RandomStart)
|
||||
if (segmentDuration > 400 && spanCount < TotalColumns - 1 - RandomStart)
|
||||
return generateTiledHoldNotes(HitObject.StartTime);
|
||||
|
||||
return generateHoldAndNormalNotes(HitObject.StartTime);
|
||||
@@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
int column = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||
bool increasing = Random.NextDouble() > 0.5;
|
||||
|
||||
for (int i = 0; i <= repeatCount; i++)
|
||||
for (int i = 0; i <= spanCount; i++)
|
||||
{
|
||||
addToPattern(pattern, column, startTime, startTime);
|
||||
startTime += segmentDuration;
|
||||
@@ -262,7 +262,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
int interval = Random.Next(1, TotalColumns - (legacy ? 1 : 0));
|
||||
|
||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||
for (int i = 0; i <= repeatCount; i++)
|
||||
for (int i = 0; i <= spanCount; i++)
|
||||
{
|
||||
addToPattern(pattern, nextColumn, startTime, startTime);
|
||||
|
||||
@@ -350,7 +350,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
var pattern = new Pattern();
|
||||
|
||||
int columnRepeat = Math.Min(repeatCount, TotalColumns);
|
||||
int columnRepeat = Math.Min(spanCount, TotalColumns);
|
||||
|
||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||
@@ -409,7 +409,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
int nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
|
||||
var rowPattern = new Pattern();
|
||||
for (int i = 0; i <= repeatCount; i++)
|
||||
for (int i = 0; i <= spanCount; i++)
|
||||
{
|
||||
if (!(ignoreHead && startTime == HitObject.StartTime))
|
||||
{
|
||||
@@ -442,7 +442,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
if (curveData == null)
|
||||
return HitObject.Samples;
|
||||
|
||||
double segmentTime = (endTime - HitObject.StartTime) / repeatCount;
|
||||
double segmentTime = (endTime - HitObject.StartTime) / spanCount;
|
||||
|
||||
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
||||
return curveData.RepeatSamples[index];
|
||||
|
||||
@@ -0,0 +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.Framework.Configuration.Tracking;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Configuration;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Configuration
|
||||
{
|
||||
public class ManiaConfigManager : RulesetConfigManager<ManiaSetting>
|
||||
{
|
||||
public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant)
|
||||
: base(settings, ruleset, variant)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void InitialiseDefaults()
|
||||
{
|
||||
base.InitialiseDefaults();
|
||||
|
||||
Set(ManiaSetting.ScrollTime, 1500.0, 50.0, 10000.0, 50.0);
|
||||
}
|
||||
|
||||
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
||||
{
|
||||
new TrackedSetting<double>(ManiaSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
|
||||
};
|
||||
}
|
||||
|
||||
public enum ManiaSetting
|
||||
{
|
||||
ScrollTime
|
||||
}
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Judgements
|
||||
{
|
||||
public class HitWindows
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// PERFECT hit window at OD = 10.
|
||||
/// </summary>
|
||||
private const double perfect_min = 27.8;
|
||||
/// <summary>
|
||||
/// PERFECT hit window at OD = 5.
|
||||
/// </summary>
|
||||
private const double perfect_mid = 38.8;
|
||||
/// <summary>
|
||||
/// PERFECT hit window at OD = 0.
|
||||
/// </summary>
|
||||
private const double perfect_max = 44.8;
|
||||
|
||||
/// <summary>
|
||||
/// GREAT hit window at OD = 10.
|
||||
/// </summary>
|
||||
private const double great_min = 68;
|
||||
/// <summary>
|
||||
/// GREAT hit window at OD = 5.
|
||||
/// </summary>
|
||||
private const double great_mid = 98;
|
||||
/// <summary>
|
||||
/// GREAT hit window at OD = 0.
|
||||
/// </summary>
|
||||
private const double great_max = 128;
|
||||
|
||||
/// <summary>
|
||||
/// GOOD hit window at OD = 10.
|
||||
/// </summary>
|
||||
private const double good_min = 134;
|
||||
/// <summary>
|
||||
/// GOOD hit window at OD = 5.
|
||||
/// </summary>
|
||||
private const double good_mid = 164;
|
||||
/// <summary>
|
||||
/// GOOD hit window at OD = 0.
|
||||
/// </summary>
|
||||
private const double good_max = 194;
|
||||
|
||||
/// <summary>
|
||||
/// OK hit window at OD = 10.
|
||||
/// </summary>
|
||||
private const double ok_min = 194;
|
||||
/// <summary>
|
||||
/// OK hit window at OD = 5.
|
||||
/// </summary>
|
||||
private const double ok_mid = 224;
|
||||
/// <summary>
|
||||
/// OK hit window at OD = 0.
|
||||
/// </summary>
|
||||
private const double ok_max = 254;
|
||||
|
||||
/// <summary>
|
||||
/// BAD hit window at OD = 10.
|
||||
/// </summary>
|
||||
private const double bad_min = 242;
|
||||
/// <summary>
|
||||
/// BAD hit window at OD = 5.
|
||||
/// </summary>
|
||||
private const double bad_mid = 272;
|
||||
/// <summary>
|
||||
/// BAD hit window at OD = 0.
|
||||
/// </summary>
|
||||
private const double bad_max = 302;
|
||||
|
||||
/// <summary>
|
||||
/// MISS hit window at OD = 10.
|
||||
/// </summary>
|
||||
private const double miss_min = 316;
|
||||
/// <summary>
|
||||
/// MISS hit window at OD = 5.
|
||||
/// </summary>
|
||||
private const double miss_mid = 346;
|
||||
/// <summary>
|
||||
/// MISS hit window at OD = 0.
|
||||
/// </summary>
|
||||
private const double miss_max = 376;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Hit window for a PERFECT hit.
|
||||
/// </summary>
|
||||
public double Perfect = perfect_mid;
|
||||
|
||||
/// <summary>
|
||||
/// Hit window for a GREAT hit.
|
||||
/// </summary>
|
||||
public double Great = great_mid;
|
||||
|
||||
/// <summary>
|
||||
/// Hit window for a GOOD hit.
|
||||
/// </summary>
|
||||
public double Good = good_mid;
|
||||
|
||||
/// <summary>
|
||||
/// Hit window for an OK hit.
|
||||
/// </summary>
|
||||
public double Ok = ok_mid;
|
||||
|
||||
/// <summary>
|
||||
/// Hit window for a BAD hit.
|
||||
/// </summary>
|
||||
public double Bad = bad_mid;
|
||||
|
||||
/// <summary>
|
||||
/// Hit window for a MISS hit.
|
||||
/// </summary>
|
||||
public double Miss = miss_mid;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs default hit windows.
|
||||
/// </summary>
|
||||
public HitWindows()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs hit windows by fitting a parameter to a 2-part piecewise linear function for each hit window.
|
||||
/// </summary>
|
||||
/// <param name="difficulty">The parameter.</param>
|
||||
public HitWindows(double difficulty)
|
||||
{
|
||||
Perfect = BeatmapDifficulty.DifficultyRange(difficulty, perfect_max, perfect_mid, perfect_min);
|
||||
Great = BeatmapDifficulty.DifficultyRange(difficulty, great_max, great_mid, great_min);
|
||||
Good = BeatmapDifficulty.DifficultyRange(difficulty, good_max, good_mid, good_min);
|
||||
Ok = BeatmapDifficulty.DifficultyRange(difficulty, ok_max, ok_mid, ok_min);
|
||||
Bad = BeatmapDifficulty.DifficultyRange(difficulty, bad_max, bad_mid, bad_min);
|
||||
Miss = BeatmapDifficulty.DifficultyRange(difficulty, miss_max, miss_mid, miss_min);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the hit result for a time offset.
|
||||
/// </summary>
|
||||
/// <param name="hitOffset">The time offset.</param>
|
||||
/// <returns>The hit result, or null if the time offset results in a miss.</returns>
|
||||
public HitResult? ResultFor(double hitOffset)
|
||||
{
|
||||
if (hitOffset <= Perfect / 2)
|
||||
return HitResult.Perfect;
|
||||
if (hitOffset <= Great / 2)
|
||||
return HitResult.Great;
|
||||
if (hitOffset <= Good / 2)
|
||||
return HitResult.Good;
|
||||
if (hitOffset <= Ok / 2)
|
||||
return HitResult.Ok;
|
||||
if (hitOffset <= Bad / 2)
|
||||
return HitResult.Meh;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs new hit windows which have been multiplied by a value.
|
||||
/// </summary>
|
||||
/// <param name="windows">The original hit windows.</param>
|
||||
/// <param name="value">The value to multiply each hit window by.</param>
|
||||
public static HitWindows operator *(HitWindows windows, double value)
|
||||
{
|
||||
return new HitWindows
|
||||
{
|
||||
Perfect = windows.Perfect * value,
|
||||
Great = windows.Great * value,
|
||||
Good = windows.Good * value,
|
||||
Ok = windows.Ok * value,
|
||||
Bad = windows.Bad * value,
|
||||
Miss = windows.Miss * value
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs new hit windows which have been divided by a value.
|
||||
/// </summary>
|
||||
/// <param name="windows">The original hit windows.</param>
|
||||
/// <param name="value">The value to divide each hit window by.</param>
|
||||
public static HitWindows operator /(HitWindows windows, double value)
|
||||
{
|
||||
return new HitWindows
|
||||
{
|
||||
Perfect = windows.Perfect / value,
|
||||
Great = windows.Great / value,
|
||||
Good = windows.Good / value,
|
||||
Ok = windows.Ok / value,
|
||||
Bad = windows.Bad / value,
|
||||
Miss = windows.Miss / value
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,13 @@ namespace osu.Game.Rulesets.Mania
|
||||
|
||||
public enum ManiaAction
|
||||
{
|
||||
[Description("Special")]
|
||||
Special,
|
||||
[Description("Special 1")]
|
||||
Special1 = 1,
|
||||
[Description("Special 2")]
|
||||
Special2,
|
||||
|
||||
// This offsets the start value of normal keys in-case we add more special keys
|
||||
// above at a later time, without breaking replays/configs.
|
||||
[Description("Key 1")]
|
||||
Key1 = 10,
|
||||
[Description("Key 2")]
|
||||
@@ -36,6 +41,24 @@ namespace osu.Game.Rulesets.Mania
|
||||
[Description("Key 8")]
|
||||
Key8,
|
||||
[Description("Key 9")]
|
||||
Key9
|
||||
Key9,
|
||||
[Description("Key 10")]
|
||||
Key10,
|
||||
[Description("Key 11")]
|
||||
Key11,
|
||||
[Description("Key 12")]
|
||||
Key12,
|
||||
[Description("Key 13")]
|
||||
Key13,
|
||||
[Description("Key 14")]
|
||||
Key14,
|
||||
[Description("Key 15")]
|
||||
Key15,
|
||||
[Description("Key 16")]
|
||||
Key16,
|
||||
[Description("Key 17")]
|
||||
Key17,
|
||||
[Description("Key 18")]
|
||||
Key18,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Graphics;
|
||||
@@ -86,7 +88,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
},
|
||||
},
|
||||
new ManiaModRandom(),
|
||||
new ManiaModKeyCoop(),
|
||||
new ManiaModDualStages(),
|
||||
new MultiMod
|
||||
{
|
||||
Mods = new Mod[]
|
||||
@@ -117,42 +119,189 @@ namespace osu.Game.Rulesets.Mania
|
||||
{
|
||||
}
|
||||
|
||||
public override IEnumerable<int> AvailableVariants => new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
public override IEnumerable<int> AvailableVariants
|
||||
{
|
||||
get
|
||||
{
|
||||
for (int i = 1; i <= 9; i++)
|
||||
yield return (int)PlayfieldType.Single + i;
|
||||
for (int i = 2; i <= 18; i += 2)
|
||||
yield return (int)PlayfieldType.Dual + i;
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0)
|
||||
{
|
||||
var leftKeys = new[]
|
||||
switch (getPlayfieldType(variant))
|
||||
{
|
||||
InputKey.A,
|
||||
InputKey.S,
|
||||
InputKey.D,
|
||||
InputKey.F
|
||||
};
|
||||
case PlayfieldType.Single:
|
||||
return new VariantMappingGenerator
|
||||
{
|
||||
LeftKeys = new[]
|
||||
{
|
||||
InputKey.A,
|
||||
InputKey.S,
|
||||
InputKey.D,
|
||||
InputKey.F
|
||||
},
|
||||
RightKeys = new[]
|
||||
{
|
||||
InputKey.J,
|
||||
InputKey.K,
|
||||
InputKey.L,
|
||||
InputKey.Semicolon
|
||||
},
|
||||
SpecialKey = InputKey.Space,
|
||||
SpecialAction = ManiaAction.Special1,
|
||||
NormalActionStart = ManiaAction.Key1,
|
||||
}.GenerateKeyBindingsFor(variant, out _);
|
||||
case PlayfieldType.Dual:
|
||||
int keys = getDualStageKeyCount(variant);
|
||||
|
||||
var rightKeys = new[]
|
||||
{
|
||||
InputKey.J,
|
||||
InputKey.K,
|
||||
InputKey.L,
|
||||
InputKey.Semicolon
|
||||
};
|
||||
var stage1Bindings = new VariantMappingGenerator
|
||||
{
|
||||
LeftKeys = new[]
|
||||
{
|
||||
InputKey.Number1,
|
||||
InputKey.Number2,
|
||||
InputKey.Number3,
|
||||
InputKey.Number4,
|
||||
},
|
||||
RightKeys = new[]
|
||||
{
|
||||
InputKey.Z,
|
||||
InputKey.X,
|
||||
InputKey.C,
|
||||
InputKey.V
|
||||
},
|
||||
SpecialKey = InputKey.Tilde,
|
||||
SpecialAction = ManiaAction.Special1,
|
||||
NormalActionStart = ManiaAction.Key1
|
||||
}.GenerateKeyBindingsFor(keys, out var nextNormal);
|
||||
|
||||
ManiaAction currentKey = ManiaAction.Key1;
|
||||
var stage2Bindings = new VariantMappingGenerator
|
||||
{
|
||||
LeftKeys = new[]
|
||||
{
|
||||
InputKey.Number7,
|
||||
InputKey.Number8,
|
||||
InputKey.Number9,
|
||||
InputKey.Number0
|
||||
},
|
||||
RightKeys = new[]
|
||||
{
|
||||
InputKey.O,
|
||||
InputKey.P,
|
||||
InputKey.BracketLeft,
|
||||
InputKey.BracketRight
|
||||
},
|
||||
SpecialKey = InputKey.BackSlash,
|
||||
SpecialAction = ManiaAction.Special2,
|
||||
NormalActionStart = nextNormal
|
||||
}.GenerateKeyBindingsFor(keys, out _);
|
||||
|
||||
var bindings = new List<KeyBinding>();
|
||||
return stage1Bindings.Concat(stage2Bindings);
|
||||
}
|
||||
|
||||
for (int i = leftKeys.Length - variant / 2; i < leftKeys.Length; i++)
|
||||
bindings.Add(new KeyBinding(leftKeys[i], currentKey++));
|
||||
|
||||
for (int i = 0; i < variant / 2; i++)
|
||||
bindings.Add(new KeyBinding(rightKeys[i], currentKey++));
|
||||
|
||||
if (variant % 2 == 1)
|
||||
bindings.Add(new KeyBinding(InputKey.Space, ManiaAction.Special));
|
||||
|
||||
return bindings;
|
||||
return new KeyBinding[0];
|
||||
}
|
||||
|
||||
public override string GetVariantName(int variant) => $"{variant}K";
|
||||
public override string GetVariantName(int variant)
|
||||
{
|
||||
switch (getPlayfieldType(variant))
|
||||
{
|
||||
default:
|
||||
return $"{variant}K";
|
||||
case PlayfieldType.Dual:
|
||||
{
|
||||
var keys = getDualStageKeyCount(variant);
|
||||
return $"{keys}K + {keys}K";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the number of keys for each stage in a <see cref="PlayfieldType.Dual"/> variant.
|
||||
/// </summary>
|
||||
/// <param name="variant">The variant.</param>
|
||||
private int getDualStageKeyCount(int variant) => (variant - (int)PlayfieldType.Dual) / 2;
|
||||
|
||||
/// <summary>
|
||||
/// Finds the <see cref="PlayfieldType"/> that corresponds to a variant value.
|
||||
/// </summary>
|
||||
/// <param name="variant">The variant value.</param>
|
||||
/// <returns>The <see cref="PlayfieldType"/> that corresponds to <paramref name="variant"/>.</returns>
|
||||
private PlayfieldType getPlayfieldType(int variant)
|
||||
{
|
||||
return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast<int>().OrderByDescending(i => i).First(v => variant >= v);
|
||||
}
|
||||
|
||||
private class VariantMappingGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// All the <see cref="InputKey"/>s available to the left hand.
|
||||
/// </summary>
|
||||
public InputKey[] LeftKeys;
|
||||
|
||||
/// <summary>
|
||||
/// All the <see cref="InputKey"/>s available to the right hand.
|
||||
/// </summary>
|
||||
public InputKey[] RightKeys;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="InputKey"/> for the special key.
|
||||
/// </summary>
|
||||
public InputKey SpecialKey;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ManiaAction"/> at which the normal columns should begin.
|
||||
/// </summary>
|
||||
public ManiaAction NormalActionStart;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ManiaAction"/> for the special column.
|
||||
/// </summary>
|
||||
public ManiaAction SpecialAction;
|
||||
|
||||
/// <summary>
|
||||
/// Generates a list of <see cref="KeyBinding"/>s for a specific number of columns.
|
||||
/// </summary>
|
||||
/// <param name="columns">The number of columns that need to be bound.</param>
|
||||
/// <param name="nextNormalAction">The next <see cref="ManiaAction"/> to use for normal columns.</param>
|
||||
/// <returns>The keybindings.</returns>
|
||||
public IEnumerable<KeyBinding> GenerateKeyBindingsFor(int columns, out ManiaAction nextNormalAction)
|
||||
{
|
||||
ManiaAction currentNormalAction = NormalActionStart;
|
||||
|
||||
var bindings = new List<KeyBinding>();
|
||||
|
||||
for (int i = LeftKeys.Length - columns / 2; i < LeftKeys.Length; i++)
|
||||
bindings.Add(new KeyBinding(LeftKeys[i], currentNormalAction++));
|
||||
|
||||
for (int i = 0; i < columns / 2; i++)
|
||||
bindings.Add(new KeyBinding(RightKeys[i], currentNormalAction++));
|
||||
|
||||
if (columns % 2 == 1)
|
||||
bindings.Add(new KeyBinding(SpecialKey, SpecialAction));
|
||||
|
||||
nextNormalAction = currentNormalAction;
|
||||
return bindings;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum PlayfieldType
|
||||
{
|
||||
/// <summary>
|
||||
/// Columns are grouped into a single stage.
|
||||
/// Number of columns in this stage lies at (item - Single).
|
||||
/// </summary>
|
||||
Single = 0,
|
||||
/// <summary>
|
||||
/// Columns are grouped into two stages.
|
||||
/// Overall number of columns lies at (item - Dual), further computation is required for
|
||||
/// number of columns in each individual stage.
|
||||
/// </summary>
|
||||
Dual = 1000,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// 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.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public interface IPlayfieldTypeMod : IApplicableMod
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="PlayfieldType"/> which this <see cref="IPlayfieldTypeMod"/> requires.
|
||||
/// </summary>
|
||||
PlayfieldType PlayfieldType { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter<ManiaHitObject>, IApplicableToRulesetContainer<ManiaHitObject>
|
||||
{
|
||||
public override string Name => "Dual Stages";
|
||||
public override string ShortenedName => "DS";
|
||||
public override string Description => @"Double the stages, double the fun!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool Ranked => false;
|
||||
|
||||
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter)
|
||||
{
|
||||
var mbc = (ManiaBeatmapConverter)beatmapConverter;
|
||||
|
||||
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
|
||||
if (mbc.IsForCurrentRuleset)
|
||||
return;
|
||||
|
||||
mbc.TargetColumns *= 2;
|
||||
}
|
||||
|
||||
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
|
||||
{
|
||||
var mrc = (ManiaRulesetContainer)rulesetContainer;
|
||||
|
||||
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
|
||||
if (mrc.IsForCurrentRuleset)
|
||||
return;
|
||||
|
||||
var newDefinitions = new List<StageDefinition>();
|
||||
foreach (var existing in mrc.Beatmap.Stages)
|
||||
{
|
||||
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
|
||||
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
|
||||
}
|
||||
|
||||
mrc.Beatmap.Stages = newDefinitions;
|
||||
}
|
||||
|
||||
public PlayfieldType PlayfieldType => PlayfieldType.Dual;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModKeyCoop : Mod
|
||||
{
|
||||
public override string Name => "KeyCoop";
|
||||
public override string ShortenedName => "2P";
|
||||
public override string Description => @"Double the key amount, double the fun!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool Ranked => true;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -212,7 +211,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (timeOffset > HitObject.HitWindows.Bad / 2)
|
||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||
{
|
||||
AddJudgement(new HoldNoteTailJudgement
|
||||
{
|
||||
@@ -224,14 +223,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
return;
|
||||
}
|
||||
|
||||
double offset = Math.Abs(timeOffset);
|
||||
|
||||
if (offset > HitObject.HitWindows.Miss / 2)
|
||||
var result = HitObject.HitWindows.ResultFor(timeOffset);
|
||||
if (result == HitResult.None)
|
||||
return;
|
||||
|
||||
AddJudgement(new HoldNoteTailJudgement
|
||||
{
|
||||
Result = HitObject.HitWindows.ResultFor(offset) ?? HitResult.Miss,
|
||||
Result = result,
|
||||
HasBroken = holdNote.hasBroken
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Bindings;
|
||||
@@ -63,17 +62,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (timeOffset > HitObject.HitWindows.Bad / 2)
|
||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||
AddJudgement(new ManiaJudgement { Result = HitResult.Miss });
|
||||
return;
|
||||
}
|
||||
|
||||
double offset = Math.Abs(timeOffset);
|
||||
|
||||
if (offset > HitObject.HitWindows.Miss / 2)
|
||||
var result = HitObject.HitWindows.ResultFor(timeOffset);
|
||||
if (result == HitResult.None)
|
||||
return;
|
||||
|
||||
AddJudgement(new ManiaJudgement { Result = HitObject.HitWindows.ResultFor(offset) ?? HitResult.Miss });
|
||||
AddJudgement(new ManiaJudgement { Result = result });
|
||||
}
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Mania.Objects.Types;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
@@ -9,5 +11,13 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
public abstract class ManiaHitObject : HitObject, IHasColumn
|
||||
{
|
||||
public virtual int Column { get; set; }
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
HitWindows.AllowsPerfect = true;
|
||||
HitWindows.AllowsOk = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects
|
||||
{
|
||||
/// <summary>
|
||||
@@ -13,17 +8,5 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
/// </summary>
|
||||
public class Note : ManiaHitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The key-press hit window for this note.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public HitWindows HitWindows { get; protected set; } = new HitWindows();
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
HitWindows = new HitWindows(difficulty.OverallDifficulty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseAutoGeneration : OsuTestCase
|
||||
{
|
||||
[Test]
|
||||
|
||||
@@ -13,7 +13,6 @@ using OpenTK.Graphics;
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseManiaHitObjects : OsuTestCase
|
||||
{
|
||||
public TestCaseManiaHitObjects()
|
||||
@@ -78,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
RelativeChildSize = new Vector2(1, 10000),
|
||||
Children = new[]
|
||||
{
|
||||
new DrawableHoldNote(new HoldNote { Duration = 1 }, ManiaAction.Key1)
|
||||
new DrawableHoldNote(new HoldNote { Duration = 1000 } , ManiaAction.Key1)
|
||||
{
|
||||
Y = 5000,
|
||||
Height = 1000,
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
@@ -17,7 +21,6 @@ using osu.Game.Tests.Visual;
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseManiaPlayfield : OsuTestCase
|
||||
{
|
||||
private const double start_time = 500;
|
||||
@@ -31,15 +34,43 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
var rng = new Random(1337);
|
||||
|
||||
AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal));
|
||||
AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal));
|
||||
AddStep("Left special style", () => createPlayfield(4, SpecialColumnPosition.Left));
|
||||
AddStep("Right special style", () => createPlayfield(4, SpecialColumnPosition.Right));
|
||||
AddStep("5 columns", () => createPlayfield(5, SpecialColumnPosition.Normal));
|
||||
AddStep("8 columns", () => createPlayfield(8, SpecialColumnPosition.Normal));
|
||||
AddStep("Left special style", () => createPlayfield(8, SpecialColumnPosition.Left));
|
||||
AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right));
|
||||
AddStep("Reversed", () => createPlayfield(4, SpecialColumnPosition.Normal, true));
|
||||
AddStep("1 column", () => createPlayfield(1));
|
||||
AddStep("4 columns", () => createPlayfield(4));
|
||||
AddStep("5 columns", () => createPlayfield(5));
|
||||
AddStep("8 columns", () => createPlayfield(8));
|
||||
AddStep("4 + 4 columns", () =>
|
||||
{
|
||||
var stages = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = 4 },
|
||||
new StageDefinition { Columns = 4 },
|
||||
};
|
||||
createPlayfield(stages);
|
||||
});
|
||||
|
||||
AddStep("2 + 4 + 2 columns", () =>
|
||||
{
|
||||
var stages = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = 2 },
|
||||
new StageDefinition { Columns = 4 },
|
||||
new StageDefinition { Columns = 2 },
|
||||
};
|
||||
createPlayfield(stages);
|
||||
});
|
||||
|
||||
AddStep("1 + 8 + 1 columns", () =>
|
||||
{
|
||||
var stages = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = 1 },
|
||||
new StageDefinition { Columns = 8 },
|
||||
new StageDefinition { Columns = 1 },
|
||||
};
|
||||
createPlayfield(stages);
|
||||
});
|
||||
|
||||
AddStep("Reversed", () => createPlayfield(4, true));
|
||||
|
||||
AddStep("Notes with input", () => createPlayfieldWithNotes());
|
||||
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(true));
|
||||
@@ -48,7 +79,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
AddStep("Hit explosion", () =>
|
||||
{
|
||||
var playfield = createPlayfield(4, SpecialColumnPosition.Normal);
|
||||
var playfield = createPlayfield(4);
|
||||
|
||||
int col = rng.Next(0, 4);
|
||||
|
||||
@@ -58,28 +89,46 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
};
|
||||
|
||||
playfield.OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
|
||||
playfield.Columns[col].OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
|
||||
});
|
||||
}
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
private void load(RulesetStore rulesets, SettingsStore settings)
|
||||
{
|
||||
maniaRuleset = rulesets.GetRuleset(3);
|
||||
|
||||
dependencies.Cache(new ManiaConfigManager(settings, maniaRuleset, 4));
|
||||
}
|
||||
|
||||
private ManiaPlayfield createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false)
|
||||
private ManiaPlayfield createPlayfield(int cols, bool inverted = false)
|
||||
{
|
||||
var stages = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = cols },
|
||||
};
|
||||
|
||||
return createPlayfield(stages, inverted);
|
||||
}
|
||||
|
||||
private ManiaPlayfield createPlayfield(List<StageDefinition> stages, bool inverted = false)
|
||||
{
|
||||
Clear();
|
||||
|
||||
var inputManager = new ManiaInputManager(maniaRuleset, cols) { RelativeSizeAxes = Axes.Both };
|
||||
var inputManager = new ManiaInputManager(maniaRuleset, stages.Sum(g => g.Columns)) { RelativeSizeAxes = Axes.Both };
|
||||
Add(inputManager);
|
||||
|
||||
ManiaPlayfield playfield;
|
||||
inputManager.Add(playfield = new ManiaPlayfield(cols)
|
||||
|
||||
inputManager.Add(playfield = new ManiaPlayfield(stages)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
SpecialColumnPosition = specialPos
|
||||
});
|
||||
|
||||
playfield.Inverted.Value = inverted;
|
||||
@@ -97,7 +146,12 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
Add(inputManager);
|
||||
|
||||
ManiaPlayfield playfield;
|
||||
inputManager.Add(playfield = new ManiaPlayfield(4)
|
||||
var stages = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = 4 },
|
||||
};
|
||||
|
||||
inputManager.Add(playfield = new ManiaPlayfield(stages)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
|
||||
@@ -11,13 +11,14 @@ using osu.Framework.Graphics.Colour;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
public class Column : ScrollingPlayfield, IHasAccentColour
|
||||
public class Column : ScrollingPlayfield, IKeyBindingHandler<ManiaAction>, IHasAccentColour
|
||||
{
|
||||
private const float key_icon_size = 10;
|
||||
private const float key_icon_corner_radius = 3;
|
||||
@@ -47,6 +48,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
public Column()
|
||||
: base(ScrollingDirection.Up)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
Width = column_width;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
@@ -61,7 +63,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
Name = "Hit target + hit objects",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = ManiaPlayfield.HIT_TARGET_POSITION },
|
||||
Padding = new MarginPadding { Top = ManiaStage.HIT_TARGET_POSITION },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
@@ -115,7 +117,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
Name = "Key",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = ManiaPlayfield.HIT_TARGET_POSITION,
|
||||
Height = ManiaStage.HIT_TARGET_POSITION,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
@@ -205,12 +207,12 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
hitObject.Depth = (float)hitObject.HitObject.StartTime;
|
||||
hitObject.AccentColour = AccentColour;
|
||||
hitObject.OnJudgement += onJudgement;
|
||||
hitObject.OnJudgement += OnJudgement;
|
||||
|
||||
HitObjects.Add(hitObject);
|
||||
}
|
||||
|
||||
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||
{
|
||||
if (!judgement.IsHit)
|
||||
return;
|
||||
@@ -258,5 +260,26 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
public bool OnPressed(ManiaAction action) => Pressed?.Invoke(action) ?? false;
|
||||
public bool OnReleased(ManiaAction action) => Released?.Invoke(action) ?? false;
|
||||
}
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
{
|
||||
// Play the sounds of the next hitobject
|
||||
if (HitObjects.AliveObjects.Any())
|
||||
{
|
||||
// If there are alive hitobjects, we can abuse the fact that AliveObjects are sorted by time (see: Add())
|
||||
HitObjects.AliveObjects.First().PlaySamples();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not, we do a slow search - we might want to do a BinarySearch here if this becomes problematic
|
||||
// We fallback to LastOrDefault() if we're beyond the last note in the map
|
||||
var hitObject = HitObjects.Objects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current) ?? HitObjects.Objects.LastOrDefault();
|
||||
hitObject?.PlaySamples();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(ManiaAction action) => false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
internal class DrawableManiaJudgement : DrawableJudgement
|
||||
{
|
||||
public DrawableManiaJudgement(Judgement judgement)
|
||||
: base(judgement)
|
||||
: base(judgement)
|
||||
{
|
||||
JudgementText.TextSize = 25;
|
||||
}
|
||||
|
||||
@@ -3,237 +3,92 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using System;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
public class ManiaPlayfield : ScrollingPlayfield
|
||||
{
|
||||
public const float HIT_TARGET_POSITION = 50;
|
||||
|
||||
private SpecialColumnPosition specialColumnPosition;
|
||||
/// <summary>
|
||||
/// The style to use for the special column.
|
||||
/// </summary>
|
||||
public SpecialColumnPosition SpecialColumnPosition
|
||||
{
|
||||
get { return specialColumnPosition; }
|
||||
set
|
||||
{
|
||||
if (IsLoaded)
|
||||
throw new InvalidOperationException($"Setting {nameof(SpecialColumnPosition)} after the playfield is loaded requires re-creating the playfield.");
|
||||
specialColumnPosition = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this playfield should be inverted. This flips everything inside the playfield.
|
||||
/// </summary>
|
||||
public readonly Bindable<bool> Inverted = new Bindable<bool>(true);
|
||||
|
||||
private readonly FlowContainer<Column> columns;
|
||||
public IEnumerable<Column> Columns => columns.Children;
|
||||
public List<Column> Columns => stages.SelectMany(x => x.Columns).ToList();
|
||||
private readonly List<ManiaStage> stages = new List<ManiaStage>();
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container<Drawable> content;
|
||||
|
||||
private List<Color4> normalColumnColours = new List<Color4>();
|
||||
private Color4 specialColumnColour;
|
||||
|
||||
private readonly Container<DrawableManiaJudgement> judgements;
|
||||
|
||||
private readonly int columnCount;
|
||||
|
||||
public ManiaPlayfield(int columnCount)
|
||||
public ManiaPlayfield(List<StageDefinition> stageDefinitions)
|
||||
: base(ScrollingDirection.Up)
|
||||
{
|
||||
this.columnCount = columnCount;
|
||||
if (stageDefinitions == null)
|
||||
throw new ArgumentNullException(nameof(stageDefinitions));
|
||||
|
||||
if (columnCount <= 0)
|
||||
throw new ArgumentException("Can't have zero or fewer columns.");
|
||||
if (stageDefinitions.Count <= 0)
|
||||
throw new ArgumentException("Can't have zero or fewer stages.");
|
||||
|
||||
Inverted.Value = true;
|
||||
|
||||
Container topLevelContainer;
|
||||
InternalChildren = new Drawable[]
|
||||
GridContainer playfieldGrid;
|
||||
InternalChild = playfieldGrid = new GridContainer
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Name = "Playfield elements",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Name = "Columns mask",
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Name = "Background",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black
|
||||
},
|
||||
columns = new FillFlowContainer<Column>
|
||||
{
|
||||
Name = "Columns",
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Padding = new MarginPadding { Left = 1, Right = 1 },
|
||||
Spacing = new Vector2(1, 0)
|
||||
},
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Name = "Barlines mask",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 1366, // Bar lines should only be masked on the vertical axis
|
||||
BypassAutoSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Child = content = new Container
|
||||
{
|
||||
Name = "Bar lines",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
|
||||
}
|
||||
},
|
||||
judgements = new Container<DrawableManiaJudgement>
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Y = HIT_TARGET_POSITION + 150,
|
||||
BypassAutoSizeAxes = Axes.Both
|
||||
},
|
||||
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
|
||||
}
|
||||
}
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[] { new Drawable[stageDefinitions.Count] }
|
||||
};
|
||||
|
||||
var currentAction = ManiaAction.Key1;
|
||||
for (int i = 0; i < columnCount; i++)
|
||||
var normalColumnAction = ManiaAction.Key1;
|
||||
var specialColumnAction = ManiaAction.Special1;
|
||||
int firstColumnIndex = 0;
|
||||
for (int i = 0; i < stageDefinitions.Count; i++)
|
||||
{
|
||||
var c = new Column();
|
||||
c.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||
var newStage = new ManiaStage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
|
||||
newStage.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||
newStage.Inverted.BindTo(Inverted);
|
||||
|
||||
c.IsSpecial = isSpecialColumn(i);
|
||||
c.Action = c.IsSpecial ? ManiaAction.Special : currentAction++;
|
||||
playfieldGrid.Content[0][i] = newStage;
|
||||
|
||||
topLevelContainer.Add(c.TopLevelContainer.CreateProxy());
|
||||
stages.Add(newStage);
|
||||
AddNested(newStage);
|
||||
|
||||
columns.Add(c);
|
||||
AddNested(c);
|
||||
firstColumnIndex += newStage.Columns.Count;
|
||||
}
|
||||
|
||||
Inverted.ValueChanged += invertedChanged;
|
||||
Inverted.TriggerChange();
|
||||
}
|
||||
|
||||
private void invertedChanged(bool newValue)
|
||||
public override void Add(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Add(h);
|
||||
|
||||
public void Add(BarLine barline) => stages.ForEach(s => s.Add(barline));
|
||||
|
||||
private ManiaStage getStageByColumn(int column)
|
||||
{
|
||||
Scale = new Vector2(1, newValue ? -1 : 1);
|
||||
judgements.Scale = Scale;
|
||||
int sum = 0;
|
||||
foreach (var stage in stages)
|
||||
{
|
||||
sum = sum + stage.Columns.Count;
|
||||
if (sum > column)
|
||||
return stage;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private void load(ManiaConfigManager maniaConfig)
|
||||
{
|
||||
normalColumnColours = new List<Color4>
|
||||
{
|
||||
colours.RedDark,
|
||||
colours.GreenDark
|
||||
};
|
||||
|
||||
specialColumnColour = colours.BlueDark;
|
||||
|
||||
// Set the special column + colour + key
|
||||
foreach (var column in Columns)
|
||||
{
|
||||
if (!column.IsSpecial)
|
||||
continue;
|
||||
|
||||
column.AccentColour = specialColumnColour;
|
||||
}
|
||||
|
||||
var nonSpecialColumns = Columns.Where(c => !c.IsSpecial).ToList();
|
||||
|
||||
// We'll set the colours of the non-special columns in a separate loop, because the non-special
|
||||
// column colours are mirrored across their centre and special styles mess with this
|
||||
for (int i = 0; i < Math.Ceiling(nonSpecialColumns.Count / 2f); i++)
|
||||
{
|
||||
Color4 colour = normalColumnColours[i % normalColumnColours.Count];
|
||||
nonSpecialColumns[i].AccentColour = colour;
|
||||
nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
|
||||
}
|
||||
maniaConfig.BindWith(ManiaSetting.ScrollTime, VisibleTimeRange);
|
||||
}
|
||||
|
||||
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||
{
|
||||
judgements.Clear();
|
||||
judgements.Add(new DrawableManiaJudgement(judgement)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (SpecialColumnPosition)
|
||||
{
|
||||
default:
|
||||
case SpecialColumnPosition.Normal:
|
||||
return columnCount % 2 == 1 && column == columnCount / 2;
|
||||
case SpecialColumnPosition.Left:
|
||||
return column == 0;
|
||||
case SpecialColumnPosition.Right:
|
||||
return column == columnCount - 1;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Add(DrawableHitObject h)
|
||||
{
|
||||
h.OnJudgement += OnJudgement;
|
||||
Columns.ElementAt(((ManiaHitObject)h.HitObject).Column).Add(h);
|
||||
}
|
||||
|
||||
public void Add(DrawableBarLine barline) => HitObjects.Add(barline);
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
// Due to masking differences, it is not possible to get the width of the columns container automatically
|
||||
// While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually
|
||||
content.Width = columns.Width;
|
||||
getStageByColumn(((ManiaHitObject)judgedObject.HitObject).Column).OnJudgement(judgedObject, judgement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -11,7 +10,11 @@ using osu.Framework.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Replays;
|
||||
@@ -22,6 +25,7 @@ using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
@@ -29,7 +33,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap;
|
||||
|
||||
public IEnumerable<DrawableBarLine> BarLines;
|
||||
public IEnumerable<BarLine> BarLines;
|
||||
|
||||
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||
: base(ruleset, beatmap, isForCurrentRuleset)
|
||||
@@ -38,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
|
||||
|
||||
var timingPoints = Beatmap.ControlPointInfo.TimingPoints;
|
||||
var barLines = new List<DrawableBarLine>();
|
||||
var barLines = new List<BarLine>();
|
||||
|
||||
for (int i = 0; i < timingPoints.Count; i++)
|
||||
{
|
||||
@@ -50,12 +54,12 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
int index = 0;
|
||||
for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
|
||||
{
|
||||
barLines.Add(new DrawableBarLine(new BarLine
|
||||
barLines.Add(new BarLine
|
||||
{
|
||||
StartTime = t,
|
||||
ControlPoint = point,
|
||||
BeatIndex = index
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +72,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
BarLines.ForEach(Playfield.Add);
|
||||
}
|
||||
|
||||
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.TotalColumns)
|
||||
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@@ -76,7 +80,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
||||
|
||||
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Beatmap.TotalColumns);
|
||||
public override int Variant => (int)(Mods.OfType<IPlayfieldTypeMod>().FirstOrDefault()?.PlayfieldType ?? PlayfieldType.Single) + Beatmap.TotalColumns;
|
||||
|
||||
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
|
||||
|
||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(IsForCurrentRuleset, WorkingBeatmap.Beatmap);
|
||||
|
||||
@@ -95,8 +101,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f);
|
||||
protected override Vector2 GetAspectAdjustedSize() => new Vector2(1, 0.8f);
|
||||
|
||||
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this);
|
||||
|
||||
protected override IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => new ManiaConfigManager(settings, Ruleset.RulesetInfo, Variant);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of <see cref="Column"/>s.
|
||||
/// </summary>
|
||||
internal class ManiaStage : ScrollingPlayfield
|
||||
{
|
||||
public const float HIT_TARGET_POSITION = 50;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this playfield should be inverted. This flips everything inside the playfield.
|
||||
/// </summary>
|
||||
public readonly Bindable<bool> Inverted = new Bindable<bool>(true);
|
||||
|
||||
public IReadOnlyList<Column> Columns => columnFlow.Children;
|
||||
private readonly FillFlowContainer<Column> columnFlow;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container<Drawable> content;
|
||||
|
||||
public Container<DrawableManiaJudgement> Judgements => judgements;
|
||||
private readonly Container<DrawableManiaJudgement> judgements;
|
||||
|
||||
private readonly Container topLevelContainer;
|
||||
|
||||
private List<Color4> normalColumnColours = new List<Color4>();
|
||||
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";
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
AutoSizeAxes = Axes.X;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Name = "Columns mask",
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Name = "Background",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black
|
||||
},
|
||||
columnFlow = new FillFlowContainer<Column>
|
||||
{
|
||||
Name = "Columns",
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Padding = new MarginPadding { Left = 1, Right = 1 },
|
||||
Spacing = new Vector2(1, 0)
|
||||
},
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Name = "Barlines mask",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 1366, // Bar lines should only be masked on the vertical axis
|
||||
BypassAutoSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Child = content = new Container
|
||||
{
|
||||
Name = "Bar lines",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
|
||||
}
|
||||
},
|
||||
judgements = new Container<DrawableManiaJudgement>
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Y = HIT_TARGET_POSITION + 150,
|
||||
BypassAutoSizeAxes = Axes.Both
|
||||
},
|
||||
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < definition.Columns; i++)
|
||||
{
|
||||
var isSpecial = isSpecialColumn(i);
|
||||
var column = new Column
|
||||
{
|
||||
IsSpecial = isSpecial,
|
||||
Action = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++
|
||||
};
|
||||
|
||||
AddColumn(column);
|
||||
}
|
||||
|
||||
Inverted.ValueChanged += invertedChanged;
|
||||
Inverted.TriggerChange();
|
||||
}
|
||||
|
||||
private void invertedChanged(bool newValue)
|
||||
{
|
||||
Scale = new Vector2(1, newValue ? -1 : 1);
|
||||
Judgements.Scale = Scale;
|
||||
}
|
||||
|
||||
public void AddColumn(Column c)
|
||||
{
|
||||
c.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||
|
||||
topLevelContainer.Add(c.TopLevelContainer.CreateProxy());
|
||||
columnFlow.Add(c);
|
||||
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;
|
||||
int columnIndex = maniaObject.Column - firstColumnIndex;
|
||||
Columns.ElementAt(columnIndex).Add(h);
|
||||
h.OnJudgement += OnJudgement;
|
||||
}
|
||||
|
||||
public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline));
|
||||
|
||||
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||
{
|
||||
judgements.Clear();
|
||||
judgements.Add(new DrawableManiaJudgement(judgement)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
normalColumnColours = new List<Color4>
|
||||
{
|
||||
colours.RedDark,
|
||||
colours.GreenDark
|
||||
};
|
||||
|
||||
specialColumnColour = colours.BlueDark;
|
||||
|
||||
// Set the special column + colour + key
|
||||
foreach (var column in Columns)
|
||||
{
|
||||
if (!column.IsSpecial)
|
||||
continue;
|
||||
|
||||
column.AccentColour = specialColumnColour;
|
||||
}
|
||||
|
||||
var nonSpecialColumns = Columns.Where(c => !c.IsSpecial).ToList();
|
||||
|
||||
// We'll set the colours of the non-special columns in a separate loop, because the non-special
|
||||
// column colours are mirrored across their centre and special styles mess with this
|
||||
for (int i = 0; i < Math.Ceiling(nonSpecialColumns.Count / 2f); i++)
|
||||
{
|
||||
Color4 colour = normalColumnColours[i % normalColumnColours.Count];
|
||||
nonSpecialColumns[i].AccentColour = colour;
|
||||
nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
// Due to masking differences, it is not possible to get the width of the columns container automatically
|
||||
// While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually
|
||||
content.Width = columnFlow.Width;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
public enum SpecialColumnPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// The special column will lie in the center of the columns.
|
||||
/// </summary>
|
||||
Normal,
|
||||
/// <summary>
|
||||
/// The special column will lie to the left of the columns.
|
||||
/// </summary>
|
||||
Left,
|
||||
/// <summary>
|
||||
/// The special column will lie to the right of the columns.
|
||||
/// </summary>
|
||||
Right
|
||||
}
|
||||
}
|
||||
@@ -32,14 +32,30 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="JetBrains.Annotations, Version=11.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_green, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a84b7dcfb1391f7f, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_v2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.provider.e_sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9c301db686d0bd12, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
@@ -58,12 +74,13 @@
|
||||
<Compile Include="Beatmaps\Patterns\Legacy\PatternType.cs" />
|
||||
<Compile Include="Beatmaps\ManiaBeatmapConverter.cs" />
|
||||
<Compile Include="Beatmaps\Patterns\Pattern.cs" />
|
||||
<Compile Include="Configuration\ManiaConfigManager.cs" />
|
||||
<Compile Include="MathUtils\FastRandom.cs" />
|
||||
<Compile Include="Judgements\HitWindows.cs" />
|
||||
<Compile Include="Judgements\HoldNoteTailJudgement.cs" />
|
||||
<Compile Include="Judgements\HoldNoteTickJudgement.cs" />
|
||||
<Compile Include="Judgements\ManiaJudgement.cs" />
|
||||
<Compile Include="ManiaDifficultyCalculator.cs" />
|
||||
<Compile Include="Mods\IPlayfieldTypeMod.cs" />
|
||||
<Compile Include="Mods\ManiaKeyMod.cs" />
|
||||
<Compile Include="Mods\ManiaModAutoplay.cs" />
|
||||
<Compile Include="Mods\ManiaModDaycore.cs" />
|
||||
@@ -83,7 +100,7 @@
|
||||
<Compile Include="Mods\ManiaModKey7.cs" />
|
||||
<Compile Include="Mods\ManiaModKey8.cs" />
|
||||
<Compile Include="Mods\ManiaModKey9.cs" />
|
||||
<Compile Include="Mods\ManiaModKeyCoop.cs" />
|
||||
<Compile Include="Mods\ManiaModDualStages.cs" />
|
||||
<Compile Include="Mods\ManiaModNightcore.cs" />
|
||||
<Compile Include="Mods\ManiaModPerfect.cs" />
|
||||
<Compile Include="Mods\ManiaModRandom.cs" />
|
||||
@@ -115,12 +132,12 @@
|
||||
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||
<Compile Include="UI\Column.cs" />
|
||||
<Compile Include="UI\DrawableManiaJudgement.cs" />
|
||||
<Compile Include="UI\ManiaStage.cs" />
|
||||
<Compile Include="UI\HitExplosion.cs" />
|
||||
<Compile Include="UI\ManiaRulesetContainer.cs" />
|
||||
<Compile Include="UI\ManiaPlayfield.cs" />
|
||||
<Compile Include="ManiaRuleset.cs" />
|
||||
<Compile Include="Mods\ManiaModNoFail.cs" />
|
||||
<Compile Include="UI\SpecialColumnPosition.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
@@ -143,11 +160,15 @@
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets'))" />
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" />
|
||||
</Project>
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.13" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.bundle_green" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.8" targetFramework="net461" />
|
||||
</packages>
|
||||
@@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
continue;
|
||||
|
||||
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
|
||||
float stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
|
||||
double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
|
||||
|
||||
if (objectN.StartTime - endTime > stackThreshold)
|
||||
//We are no longer within stacking range of the next object.
|
||||
@@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
OsuHitObject objectI = beatmap.HitObjects[i];
|
||||
if (objectI.StackHeight != 0 || objectI is Spinner) continue;
|
||||
|
||||
float stackThreshold = objectI.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
|
||||
double stackThreshold = objectI.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
|
||||
|
||||
/* If this object is a hitcircle, then we enter this "special" case.
|
||||
* It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider.
|
||||
|
||||
@@ -0,0 +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 osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Judgements
|
||||
{
|
||||
public class OsuSliderTailJudgement : OsuJudgement
|
||||
{
|
||||
public override bool AffectsCombo => false;
|
||||
protected override int NumericResultFor(HitResult result) => 0;
|
||||
}
|
||||
}
|
||||
@@ -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 System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -16,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override string Description => @"Play with no approach circles and fading notes for a slight score advantage.";
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
|
||||
private const float fade_in_duration_multiplier = 0.4f;
|
||||
private const double fade_in_duration_multiplier = 0.4;
|
||||
private const double fade_out_duration_multiplier = 0.3;
|
||||
|
||||
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||
@@ -47,16 +48,20 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
// fade out immediately after fade in.
|
||||
using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true))
|
||||
{
|
||||
circle.FadeOut(fadeOutDuration);
|
||||
}
|
||||
|
||||
break;
|
||||
case DrawableSlider slider:
|
||||
using (slider.BeginAbsoluteSequence(fadeOutStartTime, true))
|
||||
{
|
||||
slider.Body.FadeOut(longFadeDuration, Easing.Out);
|
||||
}
|
||||
|
||||
break;
|
||||
case DrawableSliderTick sliderTick:
|
||||
// slider ticks fade out over up to one second
|
||||
var tickFadeOutDuration = Math.Min(sliderTick.HitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000);
|
||||
|
||||
using (sliderTick.BeginAbsoluteSequence(sliderTick.HitObject.StartTime - tickFadeOutDuration, true))
|
||||
sliderTick.FadeOut(tickFadeOutDuration);
|
||||
|
||||
break;
|
||||
case DrawableSpinner spinner:
|
||||
@@ -66,9 +71,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
spinner.Background.Hide();
|
||||
|
||||
using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true))
|
||||
{
|
||||
spinner.FadeOut(fadeOutDuration);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -72,14 +72,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (timeOffset > HitObject.HitWindowFor(HitResult.Meh))
|
||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||
AddJudgement(new OsuJudgement { Result = HitResult.Miss });
|
||||
return;
|
||||
}
|
||||
|
||||
var result = HitObject.HitWindows.ResultFor(timeOffset);
|
||||
if (result == HitResult.None)
|
||||
return;
|
||||
|
||||
AddJudgement(new OsuJudgement
|
||||
{
|
||||
Result = HitObject.ScoreResultForOffset(Math.Abs(timeOffset)),
|
||||
Result = result,
|
||||
PositionOffset = Vector2.Zero //todo: set to correct value
|
||||
});
|
||||
}
|
||||
@@ -104,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Expire(true);
|
||||
|
||||
// override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early.
|
||||
LifetimeEnd = HitObject.StartTime + HitObject.HitWindowFor(HitResult.Miss);
|
||||
LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.HalfWindowFor(HitResult.Miss);
|
||||
break;
|
||||
case ArmedState.Miss:
|
||||
ApproachCircle.FadeOut(50);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
@@ -11,13 +12,12 @@ using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public class DrawableRepeatPoint : DrawableOsuHitObject
|
||||
public class DrawableRepeatPoint : DrawableOsuHitObject, ITrackSnaking
|
||||
{
|
||||
private readonly RepeatPoint repeatPoint;
|
||||
private readonly DrawableSlider drawableSlider;
|
||||
|
||||
public double FadeInTime;
|
||||
public double FadeOutTime;
|
||||
private double animDuration;
|
||||
|
||||
public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider)
|
||||
: base(repeatPoint)
|
||||
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
this.repeatPoint = repeatPoint;
|
||||
this.drawableSlider = drawableSlider;
|
||||
|
||||
Size = new Vector2(32 * repeatPoint.Scale);
|
||||
Size = new Vector2(45 * repeatPoint.Scale);
|
||||
|
||||
Blending = BlendingMode.Additive;
|
||||
Origin = Anchor.Centre;
|
||||
@@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
new SpriteIcon
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Icon = FontAwesome.fa_eercast
|
||||
Icon = FontAwesome.fa_chevron_right
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -48,11 +48,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
protected override void UpdatePreemptState()
|
||||
{
|
||||
var animIn = Math.Min(150, repeatPoint.StartTime - FadeInTime);
|
||||
animDuration = Math.Min(150, repeatPoint.SpanDuration / 2);
|
||||
|
||||
this.FadeIn(animIn).ScaleTo(1.2f, animIn)
|
||||
.Then()
|
||||
.ScaleTo(1, 150, Easing.Out);
|
||||
this.Animate(
|
||||
d => d.FadeIn(animDuration),
|
||||
d => d.ScaleTo(0.5f).ScaleTo(1f, animDuration * 4, Easing.OutElasticHalf)
|
||||
);
|
||||
}
|
||||
|
||||
protected override void UpdateCurrentState(ArmedState state)
|
||||
@@ -60,16 +61,40 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
this.Delay(FadeOutTime - repeatPoint.StartTime).FadeOut();
|
||||
this.Delay(HitObject.TimePreempt).FadeOut();
|
||||
break;
|
||||
case ArmedState.Miss:
|
||||
this.FadeOut(160);
|
||||
this.FadeOut(animDuration);
|
||||
break;
|
||||
case ArmedState.Hit:
|
||||
this.FadeOut(120, Easing.OutQuint)
|
||||
.ScaleTo(Scale * 1.5f, 120, Easing.OutQuint);
|
||||
this.FadeOut(animDuration, Easing.OutQuint)
|
||||
.ScaleTo(Scale * 1.5f, animDuration, Easing.Out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSnakingPosition(Vector2 start, Vector2 end)
|
||||
{
|
||||
bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0;
|
||||
List<Vector2> curve = drawableSlider.Body.CurrentCurve;
|
||||
|
||||
Position = isRepeatAtEnd ? end : start;
|
||||
|
||||
if (curve.Count < 2)
|
||||
return;
|
||||
|
||||
int searchStart = isRepeatAtEnd ? curve.Count - 1 : 0;
|
||||
int direction = isRepeatAtEnd ? -1 : 1;
|
||||
|
||||
// find the next vector2 in the curve which is not equal to our current position to infer a rotation.
|
||||
for (int i = searchStart; i >= 0 && i < curve.Count; i += direction)
|
||||
{
|
||||
if (curve[i] == Position)
|
||||
continue;
|
||||
|
||||
Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - Position.Y, curve[i].X - Position.X));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.Linq;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
@@ -17,14 +18,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
public class DrawableSlider : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach
|
||||
{
|
||||
private readonly Slider slider;
|
||||
private readonly List<Drawable> components = new List<Drawable>();
|
||||
|
||||
public readonly DrawableHitCircle InitialCircle;
|
||||
|
||||
private readonly List<ISliderProgress> components = new List<ISliderProgress>();
|
||||
|
||||
private readonly Container<DrawableSliderTick> ticks;
|
||||
private readonly Container<DrawableRepeatPoint> repeatPoints;
|
||||
|
||||
public readonly DrawableHitCircle HeadCircle;
|
||||
public readonly SliderBody Body;
|
||||
public readonly SliderBall Ball;
|
||||
|
||||
@@ -33,6 +29,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
slider = s;
|
||||
|
||||
DrawableSliderTail tail;
|
||||
Container<DrawableSliderTick> ticks;
|
||||
Container<DrawableRepeatPoint> repeatPoints;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Body = new SliderBody(s)
|
||||
@@ -50,62 +50,44 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0
|
||||
},
|
||||
InitialCircle = new DrawableHitCircle(new HitCircle
|
||||
{
|
||||
StartTime = s.StartTime,
|
||||
Position = s.StackedPosition,
|
||||
IndexInCurrentCombo = s.IndexInCurrentCombo,
|
||||
Scale = s.Scale,
|
||||
ComboColour = s.ComboColour,
|
||||
Samples = s.Samples,
|
||||
SampleControlPoint = s.SampleControlPoint,
|
||||
TimePreempt = s.TimePreempt,
|
||||
TimeFadein = s.TimeFadein
|
||||
})
|
||||
HeadCircle = new DrawableHitCircle(s.HeadCircle),
|
||||
tail = new DrawableSliderTail(s.TailCircle)
|
||||
};
|
||||
|
||||
components.Add(Body);
|
||||
components.Add(Ball);
|
||||
|
||||
AddNested(InitialCircle);
|
||||
AddNested(HeadCircle);
|
||||
|
||||
AddNested(tail);
|
||||
components.Add(tail);
|
||||
|
||||
var repeatDuration = s.Curve.Distance / s.Velocity;
|
||||
foreach (var tick in s.NestedHitObjects.OfType<SliderTick>())
|
||||
{
|
||||
var repeatStartTime = s.StartTime + tick.RepeatIndex * repeatDuration;
|
||||
var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? HitObject.TimeFadein : HitObject.TimeFadein / 2);
|
||||
var fadeOutTime = repeatStartTime + repeatDuration;
|
||||
|
||||
var drawableTick = new DrawableSliderTick(tick)
|
||||
{
|
||||
FadeInTime = fadeInTime,
|
||||
FadeOutTime = fadeOutTime,
|
||||
Position = tick.Position,
|
||||
Position = tick.Position
|
||||
};
|
||||
|
||||
ticks.Add(drawableTick);
|
||||
components.Add(drawableTick);
|
||||
AddNested(drawableTick);
|
||||
}
|
||||
|
||||
foreach (var repeatPoint in s.NestedHitObjects.OfType<RepeatPoint>())
|
||||
{
|
||||
var repeatStartTime = s.StartTime + repeatPoint.RepeatIndex * repeatDuration;
|
||||
var fadeInTime = repeatStartTime + (repeatPoint.StartTime - repeatStartTime) / 2 - (repeatPoint.RepeatIndex == 0 ? HitObject.TimeFadein : HitObject.TimeFadein / 2);
|
||||
var fadeOutTime = repeatStartTime + repeatDuration;
|
||||
|
||||
var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this)
|
||||
{
|
||||
FadeInTime = fadeInTime,
|
||||
FadeOutTime = fadeOutTime,
|
||||
Position = repeatPoint.Position,
|
||||
Position = repeatPoint.Position
|
||||
};
|
||||
|
||||
repeatPoints.Add(drawableRepeatPoint);
|
||||
components.Add(drawableRepeatPoint);
|
||||
AddNested(drawableRepeatPoint);
|
||||
}
|
||||
}
|
||||
|
||||
private int currentRepeat;
|
||||
private int currentSpan;
|
||||
public bool Tracking;
|
||||
|
||||
protected override void Update()
|
||||
@@ -116,33 +98,32 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
double progress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
|
||||
|
||||
int repeat = slider.RepeatAt(progress);
|
||||
int span = slider.SpanAt(progress);
|
||||
progress = slider.ProgressAt(progress);
|
||||
|
||||
if (repeat > currentRepeat)
|
||||
currentRepeat = repeat;
|
||||
if (span > currentSpan)
|
||||
currentSpan = span;
|
||||
|
||||
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
|
||||
if (!InitialCircle.Judgements.Any(j => j.IsHit))
|
||||
InitialCircle.Position = slider.Curve.PositionAt(progress);
|
||||
if (!HeadCircle.IsHit)
|
||||
HeadCircle.Position = slider.Curve.PositionAt(progress);
|
||||
|
||||
foreach (var c in components) c.UpdateProgress(progress, repeat);
|
||||
foreach (var t in ticks.Children) t.Tracking = Ball.Tracking;
|
||||
foreach (var c in components.OfType<ISliderProgress>()) c.UpdateProgress(progress, span);
|
||||
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;
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (!userTriggered && Time.Current >= slider.EndTime)
|
||||
{
|
||||
var judgementsCount = ticks.Children.Count + repeatPoints.Children.Count + 1;
|
||||
var judgementsHit = ticks.Children.Count(t => t.Judgements.Any(j => j.IsHit)) + repeatPoints.Children.Count(t => t.Judgements.Any(j => j.IsHit));
|
||||
if (InitialCircle.Judgements.Any(j => j.IsHit))
|
||||
judgementsHit++;
|
||||
var judgementsCount = NestedHitObjects.Count;
|
||||
var judgementsHit = NestedHitObjects.Count(h => h.IsHit);
|
||||
|
||||
var hitFraction = (double)judgementsHit / judgementsCount;
|
||||
if (hitFraction == 1 && InitialCircle.Judgements.Any(j => j.Result == HitResult.Great))
|
||||
if (hitFraction == 1 && HeadCircle.Judgements.Any(j => j.Result == HitResult.Great))
|
||||
AddJudgement(new OsuJudgement { Result = HitResult.Great });
|
||||
else if (hitFraction >= 0.5 && InitialCircle.Judgements.Any(j => j.Result >= HitResult.Good))
|
||||
else if (hitFraction >= 0.5 && HeadCircle.Judgements.Any(j => j.Result >= HitResult.Good))
|
||||
AddJudgement(new OsuJudgement { Result = HitResult.Good });
|
||||
else if (hitFraction > 0)
|
||||
AddJudgement(new OsuJudgement { Result = HitResult.Meh });
|
||||
@@ -174,7 +155,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
}
|
||||
}
|
||||
|
||||
public Drawable ProxiedLayer => InitialCircle.ApproachCircle;
|
||||
public Drawable ProxiedLayer => HeadCircle.ApproachCircle;
|
||||
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos);
|
||||
|
||||
public override Vector2 SelectionPoint => ToScreenSpace(Body.Position);
|
||||
public override Quad SelectionQuad => Body.PathDrawQuad;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking
|
||||
{
|
||||
/// <summary>
|
||||
/// The judgement text is provided by the <see cref="DrawableSlider"/>.
|
||||
/// </summary>
|
||||
public override bool DisplayJudgement => false;
|
||||
|
||||
public bool Tracking { get; set; }
|
||||
|
||||
public DrawableSliderTail(HitCircle hitCircle)
|
||||
: base(hitCircle)
|
||||
{
|
||||
AlwaysPresent = true;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (!userTriggered && timeOffset >= 0)
|
||||
AddJudgement(new OsuSliderTailJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
@@ -12,21 +11,16 @@ using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public class DrawableSliderTick : DrawableOsuHitObject
|
||||
public class DrawableSliderTick : DrawableOsuHitObject, IRequireTracking
|
||||
{
|
||||
private readonly SliderTick sliderTick;
|
||||
public const double ANIM_DURATION = 150;
|
||||
|
||||
public double FadeInTime;
|
||||
public double FadeOutTime;
|
||||
|
||||
public bool Tracking;
|
||||
public bool Tracking { get; set; }
|
||||
|
||||
public override bool DisplayJudgement => false;
|
||||
|
||||
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
|
||||
{
|
||||
this.sliderTick = sliderTick;
|
||||
|
||||
Size = new Vector2(16) * sliderTick.Scale;
|
||||
|
||||
Masking = true;
|
||||
@@ -56,13 +50,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
protected override void UpdatePreemptState()
|
||||
{
|
||||
var animIn = Math.Min(150, sliderTick.StartTime - FadeInTime);
|
||||
|
||||
this.Animate(
|
||||
d => d.FadeIn(animIn),
|
||||
d => d.ScaleTo(0.5f).ScaleTo(1.2f, animIn)
|
||||
).Then(
|
||||
d => d.ScaleTo(1, 150, Easing.Out)
|
||||
d => d.FadeIn(ANIM_DURATION),
|
||||
d => d.ScaleTo(0.5f).ScaleTo(1f, ANIM_DURATION * 4, Easing.OutElasticHalf)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -71,15 +61,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
this.Delay(FadeOutTime - sliderTick.StartTime).FadeOut();
|
||||
this.Delay(HitObject.TimePreempt).FadeOut();
|
||||
break;
|
||||
case ArmedState.Miss:
|
||||
this.FadeOut(160)
|
||||
.FadeColour(Color4.Red, 80);
|
||||
this.FadeOut(ANIM_DURATION)
|
||||
.FadeColour(Color4.Red, ANIM_DURATION / 2);
|
||||
break;
|
||||
case ArmedState.Hit:
|
||||
this.FadeOut(120, Easing.OutQuint)
|
||||
.ScaleTo(Scale * 1.5f, 120, Easing.OutQuint);
|
||||
this.FadeOut(ANIM_DURATION, Easing.OutQuint)
|
||||
.ScaleTo(Scale * 1.5f, ANIM_DURATION, Easing.Out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +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
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public interface IRequireTracking
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the <see cref="DrawableSlider"/> is currently being tracked by the user.
|
||||
/// </summary>
|
||||
bool Tracking { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
/// <summary>
|
||||
/// A component which tracks the current end snaking position of a slider.
|
||||
/// </summary>
|
||||
public interface ITrackSnaking
|
||||
{
|
||||
void UpdateSnakingPosition(Vector2 start, Vector2 end);
|
||||
}
|
||||
}
|
||||
@@ -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) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateProgress(double progress, int repeat)
|
||||
public void UpdateProgress(double progress, int span)
|
||||
{
|
||||
Position = slider.Curve.PositionAt(progress);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ using OpenTK;
|
||||
using OpenTK.Graphics.ES30;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
@@ -77,6 +78,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
container.Attach(RenderbufferInternalFormat.DepthComponent16);
|
||||
}
|
||||
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => path.ReceiveMouseInputAt(screenSpacePos);
|
||||
|
||||
public void SetRange(double p0, double p1)
|
||||
{
|
||||
if (p0 > p1)
|
||||
@@ -88,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
// 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]);
|
||||
container.Position = -path.PositionInBoundingBox(slider.Curve.PositionAt(0) - CurrentCurve[0]);
|
||||
|
||||
container.ForceRedraw();
|
||||
}
|
||||
@@ -147,7 +150,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
path.Texture = texture;
|
||||
}
|
||||
|
||||
private readonly List<Vector2> currentCurve = new List<Vector2>();
|
||||
public readonly List<Vector2> CurrentCurve = new List<Vector2>();
|
||||
private bool updateSnaking(double p0, double p1)
|
||||
{
|
||||
if (SnakedStart == p0 && SnakedEnd == p1) return false;
|
||||
@@ -155,23 +158,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
SnakedStart = p0;
|
||||
SnakedEnd = p1;
|
||||
|
||||
slider.Curve.GetPathToProgress(currentCurve, p0, p1);
|
||||
slider.Curve.GetPathToProgress(CurrentCurve, p0, p1);
|
||||
|
||||
path.ClearVertices();
|
||||
foreach (Vector2 p in currentCurve)
|
||||
path.AddVertex(p - currentCurve[0]);
|
||||
foreach (Vector2 p in CurrentCurve)
|
||||
path.AddVertex(p - CurrentCurve[0]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void UpdateProgress(double progress, int repeat)
|
||||
public void UpdateProgress(double progress, int span)
|
||||
{
|
||||
double start = 0;
|
||||
double end = snakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1;
|
||||
|
||||
if (repeat >= slider.RepeatCount - 1)
|
||||
if (span >= slider.SpanCount() - 1)
|
||||
{
|
||||
if (Math.Min(repeat, slider.RepeatCount - 1) % 2 == 1)
|
||||
if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1)
|
||||
{
|
||||
start = 0;
|
||||
end = snakingOut ? progress : 1;
|
||||
|
||||
@@ -5,6 +5,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
public interface ISliderProgress
|
||||
{
|
||||
void UpdateProgress(double progress, int repeat);
|
||||
void UpdateProgress(double progress, int span);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ using OpenTK;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
@@ -15,13 +14,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
public const double OBJECT_RADIUS = 64;
|
||||
|
||||
private const double hittable_range = 300;
|
||||
private const double hit_window_50 = 150;
|
||||
private const double hit_window_100 = 80;
|
||||
private const double hit_window_300 = 30;
|
||||
|
||||
public float TimePreempt = 600;
|
||||
public float TimeFadein = 400;
|
||||
public double TimePreempt = 600;
|
||||
public double TimeFadein = 400;
|
||||
|
||||
public Vector2 Position { get; set; }
|
||||
public float X => Position.X;
|
||||
@@ -45,32 +39,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
public virtual bool NewCombo { get; set; }
|
||||
public int IndexInCurrentCombo { get; set; }
|
||||
|
||||
public double HitWindowFor(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return 300;
|
||||
case HitResult.Meh:
|
||||
return 150;
|
||||
case HitResult.Good:
|
||||
return 80;
|
||||
case HitResult.Great:
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
|
||||
public HitResult ScoreResultForOffset(double offset)
|
||||
{
|
||||
if (offset < HitWindowFor(HitResult.Great))
|
||||
return HitResult.Great;
|
||||
if (offset < HitWindowFor(HitResult.Good))
|
||||
return HitResult.Good;
|
||||
if (offset < HitWindowFor(HitResult.Meh))
|
||||
return HitResult.Meh;
|
||||
return HitResult.Miss;
|
||||
}
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
// 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.Beatmaps.ControlPoints;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
public class RepeatPoint : OsuHitObject
|
||||
{
|
||||
public int RepeatIndex { get; set; }
|
||||
public double SpanDuration { get; set; }
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
// We want to show the first RepeatPoint as the TimePreempt dictates but on short (and possibly fast) sliders
|
||||
// we may need to cut down this time on following RepeatPoints to only show up to two RepeatPoints at any given time.
|
||||
if (RepeatIndex > 0)
|
||||
TimePreempt = Math.Min(SpanDuration * 2, TimePreempt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,13 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
/// </summary>
|
||||
private const float base_scoring_distance = 100;
|
||||
|
||||
public readonly SliderCurve Curve = new SliderCurve();
|
||||
|
||||
public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity;
|
||||
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
public override Vector2 EndPosition => PositionAt(1);
|
||||
public Vector2 StackedPositionAt(double t) => this.PositionAt(t) + StackOffset;
|
||||
public override Vector2 EndPosition => this.PositionAt(1);
|
||||
|
||||
public SliderCurve Curve { get; } = new SliderCurve();
|
||||
|
||||
public List<Vector2> ControlPoints
|
||||
{
|
||||
@@ -58,9 +59,15 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
internal float LazyTravelDistance;
|
||||
|
||||
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>();
|
||||
public int RepeatCount { get; set; } = 1;
|
||||
public int RepeatCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The length of one span of this <see cref="Slider"/>.
|
||||
/// </summary>
|
||||
public double SpanDuration => Duration / this.SpanCount();
|
||||
|
||||
private int stackHeight;
|
||||
|
||||
public override int StackHeight
|
||||
{
|
||||
get { return stackHeight; }
|
||||
@@ -74,6 +81,9 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
public double Velocity;
|
||||
public double TickDistance;
|
||||
|
||||
public HitCircle HeadCircle;
|
||||
public HitCircle TailCircle;
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
@@ -87,40 +97,52 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
||||
}
|
||||
|
||||
public Vector2 PositionAt(double progress) => Curve.PositionAt(ProgressAt(progress));
|
||||
|
||||
public double ProgressAt(double progress)
|
||||
{
|
||||
double p = progress * RepeatCount % 1;
|
||||
if (RepeatAt(progress) % 2 == 1)
|
||||
p = 1 - p;
|
||||
return p;
|
||||
}
|
||||
|
||||
public int RepeatAt(double progress) => (int)(progress * RepeatCount);
|
||||
|
||||
protected override void CreateNestedHitObjects()
|
||||
{
|
||||
base.CreateNestedHitObjects();
|
||||
|
||||
createSliderEnds();
|
||||
createTicks();
|
||||
createRepeatPoints();
|
||||
}
|
||||
|
||||
private void createSliderEnds()
|
||||
{
|
||||
HeadCircle = new HitCircle
|
||||
{
|
||||
StartTime = StartTime,
|
||||
Position = StackedPosition,
|
||||
IndexInCurrentCombo = IndexInCurrentCombo,
|
||||
ComboColour = ComboColour,
|
||||
Samples = Samples,
|
||||
SampleControlPoint = SampleControlPoint
|
||||
};
|
||||
|
||||
TailCircle = new HitCircle
|
||||
{
|
||||
StartTime = EndTime,
|
||||
Position = StackedEndPosition,
|
||||
IndexInCurrentCombo = IndexInCurrentCombo,
|
||||
ComboColour = ComboColour
|
||||
};
|
||||
|
||||
AddNested(HeadCircle);
|
||||
AddNested(TailCircle);
|
||||
}
|
||||
|
||||
private void createTicks()
|
||||
{
|
||||
if (TickDistance == 0) return;
|
||||
|
||||
var length = Curve.Distance;
|
||||
var tickDistance = Math.Min(TickDistance, length);
|
||||
var repeatDuration = length / Velocity;
|
||||
|
||||
var minDistanceFromEnd = Velocity * 0.01;
|
||||
|
||||
for (var repeat = 0; repeat < RepeatCount; repeat++)
|
||||
for (var span = 0; span < this.SpanCount(); span++)
|
||||
{
|
||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||
var reversed = repeat % 2 == 1;
|
||||
var spanStartTime = StartTime + span * SpanDuration;
|
||||
var reversed = span % 2 == 1;
|
||||
|
||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||
{
|
||||
@@ -130,20 +152,27 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
var distanceProgress = d / length;
|
||||
var timeProgress = reversed ? 1 - distanceProgress : distanceProgress;
|
||||
|
||||
var firstSample = Samples.FirstOrDefault(s => s.Name == SampleInfo.HIT_NORMAL) ?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933)
|
||||
var sampleList = new List<SampleInfo>();
|
||||
|
||||
if (firstSample != null)
|
||||
sampleList.Add(new SampleInfo
|
||||
{
|
||||
Bank = firstSample.Bank,
|
||||
Volume = firstSample.Volume,
|
||||
Name = @"slidertick",
|
||||
});
|
||||
|
||||
AddNested(new SliderTick
|
||||
{
|
||||
RepeatIndex = repeat,
|
||||
StartTime = repeatStartTime + timeProgress * repeatDuration,
|
||||
SpanIndex = span,
|
||||
SpanStartTime = spanStartTime,
|
||||
StartTime = spanStartTime + timeProgress * SpanDuration,
|
||||
Position = Curve.PositionAt(distanceProgress),
|
||||
StackHeight = StackHeight,
|
||||
Scale = Scale,
|
||||
ComboColour = ComboColour,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
Samples = sampleList
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -151,21 +180,18 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
private void createRepeatPoints()
|
||||
{
|
||||
var repeatDuration = Distance / Velocity;
|
||||
|
||||
for (var repeat = 1; repeat < RepeatCount; repeat++)
|
||||
for (int repeatIndex = 0, repeat = 1; repeatIndex < RepeatCount; repeatIndex++, repeat++)
|
||||
{
|
||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||
|
||||
AddNested(new RepeatPoint
|
||||
{
|
||||
RepeatIndex = repeat,
|
||||
StartTime = repeatStartTime,
|
||||
RepeatIndex = repeatIndex,
|
||||
SpanDuration = SpanDuration,
|
||||
StartTime = StartTime + repeat * SpanDuration,
|
||||
Position = Curve.PositionAt(repeat % 2),
|
||||
StackHeight = StackHeight,
|
||||
Scale = Scale,
|
||||
ComboColour = ComboColour,
|
||||
Samples = new List<SampleInfo>(RepeatSamples[repeat])
|
||||
Samples = new List<SampleInfo>(RepeatSamples[repeatIndex])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,30 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
public class SliderTick : OsuHitObject
|
||||
{
|
||||
public int RepeatIndex { get; set; }
|
||||
public int SpanIndex { get; set; }
|
||||
public double SpanStartTime { get; set; }
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
double offset;
|
||||
|
||||
if (SpanIndex > 0)
|
||||
// Adding 200 to include the offset stable used.
|
||||
// This is so on repeats ticks don't appear too late to be visually processed by the player.
|
||||
offset = 200;
|
||||
else
|
||||
offset = TimeFadein * 0.66f;
|
||||
|
||||
TimePreempt = (StartTime - SpanStartTime) / 2 + offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty
|
||||
|
||||
protected override void PreprocessHitObjects()
|
||||
{
|
||||
foreach (OsuHitObject h in Beatmap.HitObjects)
|
||||
(h as Slider)?.Curve?.Calculate();
|
||||
new OsuBeatmapProcessor().PostProcess(Beatmap);
|
||||
}
|
||||
|
||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
var computeVertex = new Action<double>(t =>
|
||||
{
|
||||
// ReSharper disable once PossibleInvalidOperationException (bugged in current r# version)
|
||||
var diff = slider.PositionAt(t) - slider.LazyEndPosition.Value;
|
||||
var diff = slider.StackedPositionAt(t) - slider.LazyEndPosition.Value;
|
||||
float dist = diff.Length;
|
||||
|
||||
if (dist > approxFollowCircleRadius)
|
||||
|
||||
@@ -89,20 +89,20 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
double endTime = (prev as IHasEndTime)?.EndTime ?? prev.StartTime;
|
||||
|
||||
// Make the cursor stay at a hitObject as long as possible (mainly for autopilot).
|
||||
if (h.StartTime - h.HitWindowFor(HitResult.Miss) > endTime + h.HitWindowFor(HitResult.Meh) + 50)
|
||||
if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50)
|
||||
{
|
||||
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(HitResult.Meh), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
|
||||
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(HitResult.Miss), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
|
||||
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new 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));
|
||||
}
|
||||
else if (h.StartTime - h.HitWindowFor(HitResult.Meh) > endTime + h.HitWindowFor(HitResult.Meh) + 50)
|
||||
else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50)
|
||||
{
|
||||
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(HitResult.Meh), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
|
||||
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(HitResult.Meh), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
|
||||
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new 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));
|
||||
}
|
||||
else if (h.StartTime - h.HitWindowFor(HitResult.Good) > endTime + h.HitWindowFor(HitResult.Good) + 50)
|
||||
else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50)
|
||||
{
|
||||
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(HitResult.Good), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
|
||||
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(HitResult.Good), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
|
||||
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
@@ -21,7 +20,6 @@ using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseHitCircle : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseHitCircleHidden : TestCaseHitCircle
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Audio;
|
||||
@@ -16,18 +15,23 @@ using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using System.Linq;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseSlider : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(SliderBall),
|
||||
typeof(SliderBody),
|
||||
typeof(SliderTick),
|
||||
typeof(DrawableSlider),
|
||||
typeof(DrawableSliderTick),
|
||||
typeof(DrawableRepeatPoint),
|
||||
typeof(DrawableOsuHitObject)
|
||||
};
|
||||
@@ -63,9 +67,27 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
AddStep("Fast Short Slider", () => testShortHighSpeed());
|
||||
AddStep("Fast Short Slider 1 Repeat", () => testShortHighSpeed(1));
|
||||
AddStep("Fast Short Slider 2 Repeats", () => testShortHighSpeed(2));
|
||||
AddStep("Fast Short Slider 6 Repeats", () => testShortHighSpeed(6));
|
||||
|
||||
AddStep("Perfect Curve", testCurve);
|
||||
// TODO more curve types?
|
||||
AddStep("Perfect Curve", () => testPerfect());
|
||||
AddStep("Perfect Curve 1 Repeat", () => testPerfect(1));
|
||||
AddStep("Perfect Curve 2 Repeats", () => testPerfect(2));
|
||||
|
||||
AddStep("Linear Slider", () => testLinear());
|
||||
AddStep("Linear Slider 1 Repeat", () => testLinear(1));
|
||||
AddStep("Linear Slider 2 Repeats", () => testLinear(2));
|
||||
|
||||
AddStep("Bezier Slider", () => testBezier());
|
||||
AddStep("Bezier Slider 1 Repeat", () => testBezier(1));
|
||||
AddStep("Bezier Slider 2 Repeats", () => testBezier(2));
|
||||
|
||||
AddStep("Linear Overlapping", () => testLinearOverlapping());
|
||||
AddStep("Linear Overlapping 1 Repeat", () => testLinearOverlapping(1));
|
||||
AddStep("Linear Overlapping 2 Repeats", () => testLinearOverlapping(2));
|
||||
|
||||
AddStep("Catmull Slider", () => testCatmull());
|
||||
AddStep("Catmull Slider 1 Repeat", () => testCatmull(1));
|
||||
AddStep("Catmull Slider 2 Repeats", () => testCatmull(2));
|
||||
}
|
||||
|
||||
private void testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats);
|
||||
@@ -84,15 +106,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2)
|
||||
{
|
||||
repeats++; // The first run through the slider is considered a repeat
|
||||
|
||||
var repeatSamples = new List<List<SampleInfo>>();
|
||||
if (repeats > 1)
|
||||
{
|
||||
for (int i = 0; i < repeats; i++)
|
||||
repeatSamples.Add(new List<SampleInfo>());
|
||||
}
|
||||
|
||||
var slider = new Slider
|
||||
{
|
||||
StartTime = Time.Current + 1000,
|
||||
@@ -105,13 +118,13 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
},
|
||||
Distance = distance,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = repeatSamples
|
||||
RepeatSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, circleSize, speedMultiplier);
|
||||
}
|
||||
|
||||
private void testCurve()
|
||||
private void testPerfect(int repeats = 0)
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
@@ -124,18 +137,137 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
new Vector2(0, 200),
|
||||
new Vector2(200, 0)
|
||||
},
|
||||
Distance = 600
|
||||
Distance = 600,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
}
|
||||
|
||||
private void testLinear(int repeats = 0) => createLinear(repeats);
|
||||
|
||||
private void createLinear(int repeats)
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
CurveType = CurveType.Linear,
|
||||
StartTime = Time.Current + 1000,
|
||||
Position = new Vector2(-200, 0),
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(-200, 0),
|
||||
new Vector2(-50, 75),
|
||||
new Vector2(0, 100),
|
||||
new Vector2(100, -200),
|
||||
new Vector2(200, 0),
|
||||
new Vector2(230, 0)
|
||||
},
|
||||
Distance = 793.4417,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
}
|
||||
|
||||
private void testBezier(int repeats = 0) => createBezier(repeats);
|
||||
|
||||
private void createBezier(int repeats)
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
CurveType = CurveType.Bezier,
|
||||
StartTime = Time.Current + 1000,
|
||||
Position = new Vector2(-200, 0),
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(-200, 0),
|
||||
new Vector2(-50, 75),
|
||||
new Vector2(0, 100),
|
||||
new Vector2(100, -200),
|
||||
new Vector2(230, 0)
|
||||
},
|
||||
Distance = 480,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
}
|
||||
|
||||
private void testLinearOverlapping(int repeats = 0) => createOverlapping(repeats);
|
||||
|
||||
private void createOverlapping(int repeats)
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
CurveType = CurveType.Linear,
|
||||
StartTime = Time.Current + 1000,
|
||||
Position = new Vector2(0, 0),
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(0, 0),
|
||||
new Vector2(-200, 0),
|
||||
new Vector2(0, 0),
|
||||
new Vector2(0, -200),
|
||||
new Vector2(-200, -200),
|
||||
new Vector2(0, -200)
|
||||
},
|
||||
Distance = 1000,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
}
|
||||
|
||||
private void testCatmull(int repeats = 0) => createCatmull(repeats);
|
||||
|
||||
private void createCatmull(int repeats = 0)
|
||||
{
|
||||
var repeatSamples = new List<List<SampleInfo>>();
|
||||
for (int i = 0; i < repeats; i++)
|
||||
repeatSamples.Add(new List<SampleInfo>());
|
||||
|
||||
var slider = new Slider
|
||||
{
|
||||
StartTime = Time.Current + 1000,
|
||||
Position = new Vector2(-100, 0),
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
CurveType = CurveType.Catmull,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(-100, 0),
|
||||
new Vector2(-50, -50),
|
||||
new Vector2(50, 50),
|
||||
new Vector2(100, 0)
|
||||
},
|
||||
Distance = 300,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = repeatSamples
|
||||
};
|
||||
|
||||
addSlider(slider, 3, 1);
|
||||
}
|
||||
|
||||
private List<List<SampleInfo>> createEmptySamples(int repeats)
|
||||
{
|
||||
var repeatSamples = new List<List<SampleInfo>>();
|
||||
for (int i = 0; i < repeats; i++)
|
||||
repeatSamples.Add(new List<SampleInfo>());
|
||||
return repeatSamples;
|
||||
}
|
||||
|
||||
private void addSlider(Slider slider, float circleSize, double speedMultiplier)
|
||||
{
|
||||
var cpi = new ControlPointInfo();
|
||||
cpi.DifficultyPoints.Add(new DifficultyControlPoint { SpeedMultiplier = speedMultiplier });
|
||||
|
||||
slider.ApplyDefaults(cpi, new BeatmapDifficulty { CircleSize = circleSize });
|
||||
slider.ApplyDefaults(cpi, new BeatmapDifficulty { CircleSize = circleSize, SliderTickRate = 3 });
|
||||
|
||||
var drawable = new DrawableSlider(slider)
|
||||
{
|
||||
@@ -146,7 +278,34 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
foreach (var mod in Mods.OfType<IApplicableToDrawableHitObjects>())
|
||||
mod.ApplyToDrawableHitObjects(new[] { drawable });
|
||||
|
||||
drawable.OnJudgement += onJudgement;
|
||||
|
||||
Add(drawable);
|
||||
}
|
||||
|
||||
private float judgementOffsetDirection = 1;
|
||||
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||
{
|
||||
var osuObject = judgedObject as DrawableOsuHitObject;
|
||||
if (osuObject == null)
|
||||
return;
|
||||
|
||||
OsuSpriteText text;
|
||||
Add(text = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = judgement.IsHit ? "Hit!" : "Miss!",
|
||||
Colour = judgement.IsHit ? Color4.Green : Color4.Red,
|
||||
TextSize = 30,
|
||||
Position = osuObject.HitObject.StackedEndPosition + judgementOffsetDirection * new Vector2(0, 45)
|
||||
});
|
||||
|
||||
text.Delay(150)
|
||||
.Then().FadeOut(200)
|
||||
.Then().Expire();
|
||||
|
||||
judgementOffsetDirection *= -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseSliderHidden : TestCaseSlider
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
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;
|
||||
@@ -17,7 +16,6 @@ using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseSpinner : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseSpinnerHidden : TestCaseSpinner
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
||||
|
||||
@@ -27,21 +27,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
|
||||
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
|
||||
|
||||
public override Vector2 Size
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Parent == null)
|
||||
return Vector2.Zero;
|
||||
|
||||
var parentSize = Parent.DrawSize;
|
||||
var aspectSize = parentSize.X * 0.75f < parentSize.Y ? new Vector2(parentSize.X, parentSize.X * 0.75f) : new Vector2(parentSize.Y * 4f / 3f, parentSize.Y);
|
||||
|
||||
return new Vector2(aspectSize.X / parentSize.X, aspectSize.Y / parentSize.Y) * base.Size;
|
||||
}
|
||||
}
|
||||
|
||||
public OsuPlayfield() : base(BASE_SIZE.X)
|
||||
public OsuPlayfield()
|
||||
: base(BASE_SIZE.X)
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
@@ -50,7 +50,11 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
|
||||
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay);
|
||||
|
||||
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f);
|
||||
protected override Vector2 GetAspectAdjustedSize()
|
||||
{
|
||||
var aspectSize = DrawSize.X * 0.75f < DrawSize.Y ? new Vector2(DrawSize.X, DrawSize.X * 0.75f) : new Vector2(DrawSize.Y * 4f / 3f, DrawSize.Y);
|
||||
return new Vector2(aspectSize.X / DrawSize.X, aspectSize.Y / DrawSize.Y) * 0.75f;
|
||||
}
|
||||
|
||||
protected override CursorContainer CreateCursor() => new GameplayCursor();
|
||||
}
|
||||
|
||||
@@ -33,14 +33,30 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="JetBrains.Annotations, Version=11.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_green, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a84b7dcfb1391f7f, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_v2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.provider.e_sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9c301db686d0bd12, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
@@ -51,6 +67,7 @@
|
||||
<Compile Include="Edit\OsuEditPlayfield.cs" />
|
||||
<Compile Include="Edit\OsuEditRulesetContainer.cs" />
|
||||
<Compile Include="Edit\OsuHitObjectComposer.cs" />
|
||||
<Compile Include="Judgements\OsuSliderTailJudgement.cs" />
|
||||
<Compile Include="Mods\OsuModAutopilot.cs" />
|
||||
<Compile Include="Mods\OsuModAutoplay.cs" />
|
||||
<Compile Include="Mods\OsuModDaycore.cs" />
|
||||
@@ -71,6 +88,9 @@
|
||||
<Compile Include="Objects\Drawables\Connections\FollowPointRenderer.cs" />
|
||||
<Compile Include="Judgements\OsuJudgement.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableRepeatPoint.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableSliderTail.cs" />
|
||||
<Compile Include="Objects\Drawables\IRequireTracking.cs" />
|
||||
<Compile Include="Objects\Drawables\ITrackSnaking.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\ApproachCircle.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\SpinnerBackground.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\CirclePiece.cs" />
|
||||
@@ -148,11 +168,15 @@
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets'))" />
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" />
|
||||
</Project>
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.13" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.bundle_green" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.8" targetFramework="net461" />
|
||||
</packages>
|
||||
@@ -29,8 +29,8 @@ namespace osu.Game.Rulesets.Taiko.Audio
|
||||
{
|
||||
mappings[s.Time] = new DrumSample
|
||||
{
|
||||
Centre = s.GetSampleInfo().GetChannel(audio.Sample, "Taiko"),
|
||||
Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample, "Taiko")
|
||||
Centre = s.GetSampleInfo().GetChannel(audio.Sample.Get, "Taiko"),
|
||||
Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample.Get, "Taiko")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,8 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
|
||||
if (distanceData != null)
|
||||
{
|
||||
int repeats = repeatsData?.RepeatCount ?? 1;
|
||||
// Number of spans of the object - one for the initial length and for each repeat
|
||||
int spans = repeatsData?.SpanCount() ?? 1;
|
||||
|
||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime);
|
||||
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);
|
||||
@@ -93,7 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment;
|
||||
|
||||
// The true distance, accounting for any repeats. This ends up being the drum roll distance later
|
||||
double distance = distanceData.Distance * repeats * legacy_velocity_multiplier;
|
||||
double distance = distanceData.Distance * spans * legacy_velocity_multiplier;
|
||||
|
||||
// The velocity of the taiko hit object - calculated as the velocity of a drum roll
|
||||
double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
|
||||
@@ -111,7 +112,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
double osuDuration = distance / osuVelocity;
|
||||
|
||||
// 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 / repeats);
|
||||
double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans);
|
||||
|
||||
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@@ -38,30 +37,27 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (timeOffset > HitObject.HitWindowGood)
|
||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||
AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
|
||||
return;
|
||||
}
|
||||
|
||||
double hitOffset = Math.Abs(timeOffset);
|
||||
|
||||
if (hitOffset > HitObject.HitWindowMiss)
|
||||
var result = HitObject.HitWindows.ResultFor(timeOffset);
|
||||
if (result == HitResult.None)
|
||||
return;
|
||||
|
||||
if (!validKeyPressed)
|
||||
if (!validKeyPressed || result == HitResult.Miss)
|
||||
AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
|
||||
else if (hitOffset < HitObject.HitWindowGood)
|
||||
else
|
||||
{
|
||||
AddJudgement(new TaikoJudgement
|
||||
{
|
||||
Result = hitOffset < HitObject.HitWindowGreat ? HitResult.Great : HitResult.Good,
|
||||
Result = result,
|
||||
Final = !HitObject.IsStrong
|
||||
});
|
||||
|
||||
SecondHitAllowed = true;
|
||||
}
|
||||
else
|
||||
AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
|
||||
}
|
||||
|
||||
public override bool OnPressed(TaikoAction action)
|
||||
@@ -90,7 +86,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
switch (State.Value)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
this.Delay(HitObject.HitWindowMiss).Expire();
|
||||
this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire();
|
||||
break;
|
||||
case ArmedState.Miss:
|
||||
this.FadeOut(100)
|
||||
|
||||
@@ -1,35 +1,9 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects
|
||||
{
|
||||
public class Hit : TaikoHitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The hit window that results in a "GREAT" hit.
|
||||
/// </summary>
|
||||
public double HitWindowGreat = 35;
|
||||
|
||||
/// <summary>
|
||||
/// The hit window that results in a "GOOD" hit.
|
||||
/// </summary>
|
||||
public double HitWindowGood = 80;
|
||||
|
||||
/// <summary>
|
||||
/// The hit window that results in a "MISS".
|
||||
/// </summary>
|
||||
public double HitWindowMiss = 95;
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
HitWindowGreat = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 50, 35, 20);
|
||||
HitWindowGood = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 120, 80, 50);
|
||||
HitWindowMiss = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 135, 95, 70);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
||||
{
|
||||
bool hitButton = true;
|
||||
|
||||
Frames.Add(new ReplayFrame(-100000, null, null, ReplayButtonState.None));
|
||||
Frames.Add(new ReplayFrame(Beatmap.HitObjects[0].StartTime - 1000, null, null, ReplayButtonState.None));
|
||||
Frames.Add(new TaikoReplayFrame(-100000, ReplayButtonState.None));
|
||||
Frames.Add(new TaikoReplayFrame(Beatmap.HitObjects[0].StartTime - 1000, ReplayButtonState.None));
|
||||
|
||||
for (int i = 0; i < Beatmap.HitObjects.Count; i++)
|
||||
{
|
||||
@@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
||||
break;
|
||||
}
|
||||
|
||||
Frames.Add(new ReplayFrame(j, null, null, button));
|
||||
Frames.Add(new TaikoReplayFrame(j, button));
|
||||
d = (d + 1) % 4;
|
||||
if (++count == req)
|
||||
break;
|
||||
@@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
||||
{
|
||||
foreach (var tick in drumRoll.NestedHitObjects.OfType<DrumRollTick>())
|
||||
{
|
||||
Frames.Add(new ReplayFrame(tick.StartTime, null, null, hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2));
|
||||
Frames.Add(new TaikoReplayFrame(tick.StartTime, hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2));
|
||||
hitButton = !hitButton;
|
||||
}
|
||||
}
|
||||
@@ -107,18 +107,18 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
||||
button = hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2;
|
||||
}
|
||||
|
||||
Frames.Add(new ReplayFrame(h.StartTime, null, null, button));
|
||||
Frames.Add(new TaikoReplayFrame(h.StartTime, button));
|
||||
}
|
||||
else
|
||||
throw new InvalidOperationException("Unknown hit object type.");
|
||||
|
||||
Frames.Add(new ReplayFrame(endTime + KEY_UP_DELAY, null, null, ReplayButtonState.None));
|
||||
Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY, ReplayButtonState.None));
|
||||
|
||||
if (i < Beatmap.HitObjects.Count - 1)
|
||||
{
|
||||
double waitTime = Beatmap.HitObjects[i + 1].StartTime - 1000;
|
||||
if (waitTime > endTime)
|
||||
Frames.Add(new ReplayFrame(waitTime, null, null, ReplayButtonState.None));
|
||||
Frames.Add(new TaikoReplayFrame(waitTime, ReplayButtonState.None));
|
||||
}
|
||||
|
||||
hitButton = !hitButton;
|
||||
|
||||
@@ -0,0 +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 osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Replays
|
||||
{
|
||||
public class TaikoReplayFrame : ReplayFrame
|
||||
{
|
||||
public override bool IsImportant => MouseLeft || MouseRight;
|
||||
|
||||
public TaikoReplayFrame(double time, ReplayButtonState buttons)
|
||||
: base(time, null, null, buttons)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
|
||||
/// <summary>
|
||||
/// Taiko fails at the end of the map if the player has not half-filled their HP bar.
|
||||
/// </summary>
|
||||
protected override bool DefaultFailCondition => Hits == MaxHits && Health.Value <= 0.5;
|
||||
protected override bool DefaultFailCondition => JudgedHits == MaxHits && Health.Value <= 0.5;
|
||||
|
||||
private double hpIncreaseTick;
|
||||
private double hpIncreaseGreat;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@@ -15,7 +14,6 @@ using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseInputDrum : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
|
||||
@@ -25,7 +25,6 @@ using osu.Game.Rulesets.Scoring;
|
||||
namespace osu.Game.Rulesets.Taiko.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseTaikoPlayfield : OsuTestCase
|
||||
{
|
||||
private const double default_duration = 1000;
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
}
|
||||
}
|
||||
|
||||
protected override Vector2 GetPlayfieldAspectAdjust()
|
||||
protected override Vector2 GetAspectAdjustedSize()
|
||||
{
|
||||
const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768;
|
||||
const float default_aspect = 16f / 9f;
|
||||
|
||||
@@ -32,14 +32,30 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="JetBrains.Annotations, Version=11.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_green, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a84b7dcfb1391f7f, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_v2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.provider.e_sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9c301db686d0bd12, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
@@ -91,6 +107,7 @@
|
||||
<Compile Include="Replays\TaikoAutoGenerator.cs" />
|
||||
<Compile Include="Objects\TaikoHitObject.cs" />
|
||||
<Compile Include="Objects\TaikoHitObjectDifficulty.cs" />
|
||||
<Compile Include="Replays\TaikoReplayFrame.cs" />
|
||||
<Compile Include="TaikoDifficultyCalculator.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Scoring\TaikoScoreProcessor.cs" />
|
||||
@@ -129,11 +146,15 @@
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets'))" />
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" />
|
||||
</Project>
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.13" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.bundle_green" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.8" targetFramework="net461" />
|
||||
</packages>
|
||||
@@ -70,7 +70,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition);
|
||||
Assert.IsTrue(sprite.IsDrawable);
|
||||
Assert.AreEqual(Anchor.Centre, sprite.Origin);
|
||||
Assert.AreEqual(Path.Combine("SB", "lyric", "ja-21.png"), sprite.Path);
|
||||
Assert.AreEqual("SB/lyric/ja-21.png", sprite.Path);
|
||||
|
||||
var animation = background.Elements.ElementAt(12) as StoryboardAnimation;
|
||||
Assert.NotNull(animation);
|
||||
@@ -82,7 +82,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.IsTrue(animation.IsDrawable);
|
||||
Assert.AreEqual(AnimationLoopType.LoopForever, animation.LoopType);
|
||||
Assert.AreEqual(Anchor.Centre, animation.Origin);
|
||||
Assert.AreEqual(Path.Combine("SB", "red jitter", "red_0000.jpg"), animation.Path);
|
||||
Assert.AreEqual("SB/red jitter/red_0000.jpg", animation.Path);
|
||||
Assert.AreEqual(78993, animation.StartTime);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user