Merge remote-tracking branch 'origin/master' into netstandard

This commit is contained in:
smoogipoo
2018-03-24 14:49:46 +09:00
465 changed files with 12513 additions and 4969 deletions

View File

@ -1,9 +1,11 @@
osu!lazer is currently in early stages of development and is not yet ready for end users. Please avoid creating issues or bugs if you do not personally intend to fix them. Some acceptable topics include: osu!lazer is currently still under heavy development!
Please ensure that you are making an issue for one of the following:
- A bug with currently implemented features (not features that don't exist)
- A feature you are considering adding, so we can collaborate on feedback and design.
- Discussions about technical design decisions - Discussions about technical design decisions
- Bugs that you have found and are personally willing and able to fix
- TODO lists of smaller tasks around larger features
Basically, issues are not a place for you to get help. They are a place for developers to collaborate on the game.
If your issue qualifies, replace this text with a detailed description of your issue with as much relevant information as you can provide. If your issue qualifies, replace this text with a detailed description of your issue with as much relevant information as you can provide.
Screenshots and log files are highly welcomed.

View File

@ -12,7 +12,7 @@ install:
- cmd: git submodule update --init --recursive --depth=5 - cmd: git submodule update --init --recursive --depth=5
- cmd: choco install resharper-clt -y - cmd: choco install resharper-clt -y
- cmd: choco install nvika -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: before_build:
- cmd: CodeFileSanity.exe - cmd: CodeFileSanity.exe
- cmd: nuget restore -verbosity quiet - cmd: nuget restore -verbosity quiet
@ -20,6 +20,10 @@ build:
project: osu.sln project: osu.sln
parallel: true parallel: true
verbosity: minimal verbosity: minimal
test:
assemblies:
only:
- 'osu.Desktop\**\*.dll'
after_build: after_build:
- cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL - cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors - cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors

View File

@ -39,7 +39,7 @@ namespace osu.Desktop.Deploy
/// <summary> /// <summary>
/// How many previous build deltas we want to keep when publishing. /// How many previous build deltas we want to keep when publishing.
/// </summary> /// </summary>
private const int keep_delta_count = 3; private const int keep_delta_count = 4;
private static string codeSigningCmd => string.IsNullOrEmpty(codeSigningPassword) ? "" : $"-n \"/a /f {codeSigningCertPath} /p {codeSigningPassword} /t http://timestamp.comodoca.com/authenticode\""; private static string codeSigningCmd => string.IsNullOrEmpty(codeSigningPassword) ? "" : $"-n \"/a /f {codeSigningCertPath} /p {codeSigningPassword} /t http://timestamp.comodoca.com/authenticode\"";

View File

@ -109,16 +109,11 @@ namespace osu.Desktop
{ {
var filePaths = new[] { e.FileName }; var filePaths = new[] { e.FileName };
if (filePaths.All(f => Path.GetExtension(f) == @".osz")) var firstExtension = Path.GetExtension(filePaths.First());
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));
});
}
private static readonly string[] allowed_extensions = { @".osz", @".osr" }; if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
}
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime;
using osu.Framework; using osu.Framework;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.IPC; using osu.Game.IPC;
@ -18,6 +19,9 @@ namespace osu.Desktop
// required to initialise native SQLite libraries on some platforms. // required to initialise native SQLite libraries on some platforms.
SQLitePCL.Batteries_V2.Init(); SQLitePCL.Batteries_V2.Init();
if (!RuntimeInfo.IsMono)
useMulticoreJit();
// Back up the cwd before DesktopGameHost changes it // Back up the cwd before DesktopGameHost changes it
var cwd = Environment.CurrentDirectory; var cwd = Environment.CurrentDirectory;
@ -25,7 +29,7 @@ namespace osu.Desktop
{ {
if (!host.IsPrimaryInstance) 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 // Restore the cwd so relative paths given at the command line work correctly
Directory.SetCurrentDirectory(cwd); Directory.SetCurrentDirectory(cwd);
foreach (var file in args) foreach (var file in args)
@ -47,8 +51,16 @@ namespace osu.Desktop
break; break;
} }
} }
return 0; return 0;
} }
} }
private static void useMulticoreJit()
{
var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles"));
ProfileOptimization.SetProfileRoot(directory.FullName);
ProfileOptimization.StartProfile("Startup.Profile");
}
} }
} }

View File

@ -16,11 +16,9 @@
<language>en-AU</language> <language>en-AU</language>
</metadata> </metadata>
<files> <files>
<file src="*.exe" target="lib\net45\" exclude="**vshost**"/> <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="**.config" target="lib\net45\"/>
<file src="x86\*.dll" target="lib\net45\x86\"/>
<file src="x64\*.dll" target="lib\net45\x64\"/>
</files> </files>
</package> </package>

View File

@ -16,29 +16,13 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
{ {
public override void PostProcess(Beatmap<CatchHitObject> beatmap) public override void PostProcess(Beatmap<CatchHitObject> beatmap)
{ {
if (beatmap.ComboColors.Count == 0)
return;
int index = 0;
int colourIndex = 0;
CatchHitObject lastObj = null;
initialiseHyperDash(beatmap.HitObjects); initialiseHyperDash(beatmap.HitObjects);
base.PostProcess(beatmap);
int index = 0;
foreach (var obj in beatmap.HitObjects) foreach (var obj in beatmap.HitObjects)
{
if (obj.NewCombo)
{
if (lastObj != null) lastObj.LastInCombo = true;
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
}
obj.IndexInBeatmap = index++; obj.IndexInBeatmap = index++;
obj.ComboColour = beatmap.ComboColors[colourIndex];
lastObj = obj;
}
} }
private void initialiseHyperDash(List<CatchHitObject> objects) private void initialiseHyperDash(List<CatchHitObject> objects)

View File

@ -10,6 +10,8 @@ using osu.Game.Rulesets.UI;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Replays.Types;
namespace osu.Game.Rulesets.Catch namespace osu.Game.Rulesets.Catch
{ {
@ -99,7 +101,9 @@ namespace osu.Game.Rulesets.Catch
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap); public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap);
public override int LegacyID => 2; public override int? LegacyID => 2;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
public CatchRuleset(RulesetInfo rulesetInfo = null) public CatchRuleset(RulesetInfo rulesetInfo = null)
: base(rulesetInfo) : base(rulesetInfo)

View File

@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Catch.Mods
{ {
public class CatchModDaycore : ModDaycore public class CatchModDaycore : ModDaycore
{ {
public override double ScoreMultiplier => 0.5; public override double ScoreMultiplier => 0.3;
} }
} }

View File

@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Catch.Mods
{ {
public class CatchModEasy : ModEasy public class CatchModEasy : ModEasy
{ {
public override string Description => @"Larger fruits, more forgiving HP drain, less accuracy required, and three lives!";
} }
} }

View File

@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Catch.Mods
{ {
public class CatchModHalfTime : ModHalfTime public class CatchModHalfTime : ModHalfTime
{ {
public override double ScoreMultiplier => 0.5; public override double ScoreMultiplier => 0.3;
} }
} }

View File

@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Catch.Mods
public class CatchModHardRock : ModHardRock public class CatchModHardRock : ModHardRock
{ {
public override double ScoreMultiplier => 1.12; public override double ScoreMultiplier => 1.12;
public override bool Ranked => true;
} }
} }

View File

@ -7,7 +7,7 @@ namespace osu.Game.Rulesets.Catch.Mods
{ {
public class CatchModHidden : ModHidden public class CatchModHidden : ModHidden
{ {
public override string Description => @"Play with fading notes for a slight score advantage."; public override string Description => @"Play with fading fruits.";
public override double ScoreMultiplier => 1.06; public override double ScoreMultiplier => 1.06;
} }
} }

View File

@ -3,7 +3,6 @@
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects namespace osu.Game.Rulesets.Catch.Objects
{ {
@ -32,25 +31,11 @@ namespace osu.Game.Rulesets.Catch.Objects
AddNested(new Banana AddNested(new Banana
{ {
Samples = Samples, Samples = Samples,
ComboColour = getNextComboColour(),
StartTime = i, StartTime = i,
X = RNG.NextSingle() X = RNG.NextSingle()
}); });
} }
private Color4 getNextComboColour()
{
switch (RNG.Next(0, 3))
{
default:
return new Color4(255, 240, 0, 255);
case 1:
return new Color4(255, 192, 0, 255);
case 2:
return new Color4(214, 221, 28, 255);
}
}
public double EndTime => StartTime + Duration; public double EndTime => StartTime + Duration;
public double Duration { get; set; } public double Duration { get; set; }

View File

@ -5,24 +5,25 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects namespace osu.Game.Rulesets.Catch.Objects
{ {
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasCombo public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation
{ {
public const double OBJECT_RADIUS = 44; public const double OBJECT_RADIUS = 44;
public float X { get; set; } public float X { get; set; }
public Color4 ComboColour { get; set; }
public int IndexInBeatmap { get; set; } public int IndexInBeatmap { get; set; }
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4); public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4);
public virtual bool NewCombo { get; set; } public virtual bool NewCombo { get; set; }
public int IndexInCurrentCombo { get; set; }
public int ComboIndex { get; set; }
/// <summary> /// <summary>
/// The next fruit starts a new combo. Used for explodey. /// The next fruit starts a new combo. Used for explodey.
/// </summary> /// </summary>

View File

@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
Origin = Anchor.BottomLeft; Origin = Anchor.BottomLeft;
X = 0; X = 0;
Child = bananaContainer = new Container { RelativeSizeAxes = Axes.Both }; InternalChild = bananaContainer = new Container { RelativeSizeAxes = Axes.Both };
foreach (var b in s.NestedHitObjects.Cast<BananaShower.Banana>()) foreach (var b in s.NestedHitObjects.Cast<BananaShower.Banana>())
AddNested(getVisualRepresentation?.Invoke(b)); AddNested(getVisualRepresentation?.Invoke(b));

View File

@ -8,6 +8,8 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using OpenTK; using OpenTK;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable namespace osu.Game.Rulesets.Catch.Objects.Drawable
{ {
@ -57,6 +59,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss }); AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss });
} }
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo)
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
}
private const float preempt = 1000; private const float preempt = 1000;
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)

View File

@ -5,28 +5,39 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
using OpenTK; using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable namespace osu.Game.Rulesets.Catch.Objects.Drawable
{ {
public class DrawableDroplet : PalpableCatchHitObject<Droplet> public class DrawableDroplet : PalpableCatchHitObject<Droplet>
{ {
private Pulp pulp;
public DrawableDroplet(Droplet h) public DrawableDroplet(Droplet h)
: base(h) : base(h)
{ {
Origin = Anchor.Centre; Origin = Anchor.Centre;
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4; Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4;
AccentColour = h.ComboColour;
Masking = false; Masking = false;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Child = new Pulp InternalChild = pulp = new Pulp
{ {
AccentColour = AccentColour,
Size = Size Size = Size
}; };
} }
public override Color4 AccentColour
{
get { return base.AccentColour; }
set
{
base.AccentColour = value;
pulp.AccentColour = AccentColour;
}
}
} }
} }

View File

@ -24,7 +24,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
Origin = Anchor.Centre; Origin = Anchor.Centre;
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS); Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS);
AccentColour = HitObject.ComboColour;
Masking = false; Masking = false;
Rotation = (float)(RNG.NextDouble() - 0.5f) * 40; Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
@ -33,7 +32,10 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Children = new[] // todo: this should come from the skin.
AccentColour = colourForRrepesentation(HitObject.VisualRepresentation);
InternalChildren = new[]
{ {
createPulp(HitObject.VisualRepresentation), createPulp(HitObject.VisualRepresentation),
border = new Circle border = new Circle
@ -65,7 +67,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
if (HitObject.HyperDash) if (HitObject.HyperDash)
{ {
Add(new Pulp AddInternal(new Pulp
{ {
RelativePositionAxes = Axes.Both, RelativePositionAxes = Axes.Both,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
@ -273,5 +275,31 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
border.Alpha = (float)MathHelper.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1); border.Alpha = (float)MathHelper.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1);
} }
private Color4 colourForRrepesentation(FruitVisualRepresentation representation)
{
switch (representation)
{
default:
case FruitVisualRepresentation.Pear:
return new Color4(17, 136, 170, 255);
case FruitVisualRepresentation.Grape:
return new Color4(204, 102, 0, 255);
case FruitVisualRepresentation.Raspberry:
return new Color4(121, 9, 13, 255);
case FruitVisualRepresentation.Pineapple:
return new Color4(102, 136, 0, 255);
case FruitVisualRepresentation.Banana:
switch (RNG.Next(0, 3))
{
default:
return new Color4(255, 240, 0, 255);
case 1:
return new Color4(255, 192, 0, 255);
case 2:
return new Color4(214, 221, 28, 255);
}
}
}
} }
} }

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
Origin = Anchor.BottomLeft; Origin = Anchor.BottomLeft;
X = 0; X = 0;
Child = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, }; InternalChild = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, };
foreach (var o in s.NestedHitObjects.Cast<CatchHitObject>()) foreach (var o in s.NestedHitObjects.Cast<CatchHitObject>())
AddNested(getVisualRepresentation?.Invoke(o)); AddNested(getVisualRepresentation?.Invoke(o));
@ -33,7 +33,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
var catchObject = (DrawableCatchHitObject)h; var catchObject = (DrawableCatchHitObject)h;
catchObject.CheckPosition = o => CheckPosition?.Invoke(o) ?? false; catchObject.CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
catchObject.AccentColour = HitObject.ComboColour;
dropletContainer.Add(h); dropletContainer.Add(h);
base.AddNested(h); base.AddNested(h);

View File

@ -60,30 +60,36 @@ namespace osu.Game.Rulesets.Catch.Objects
AddNested(new Fruit AddNested(new Fruit
{ {
Samples = Samples, Samples = Samples,
ComboColour = ComboColour,
StartTime = StartTime, StartTime = StartTime,
X = X X = X
}); });
for (var span = 0; span < this.SpanCount(); span++) double lastDropletTime = StartTime;
for (int span = 0; span < this.SpanCount(); span++)
{ {
var spanStartTime = StartTime + span * spanDuration; var spanStartTime = StartTime + span * spanDuration;
var reversed = span % 2 == 1; var reversed = span % 2 == 1;
for (var d = tickDistance; d <= length; d += tickDistance) for (double d = 0; d <= length; d += tickDistance)
{ {
if (d > length - minDistanceFromEnd)
break;
var timeProgress = d / length; var timeProgress = d / length;
var distanceProgress = reversed ? 1 - timeProgress : timeProgress; var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
var lastTickTime = spanStartTime + timeProgress * spanDuration; double time = spanStartTime + timeProgress * spanDuration;
AddNested(new Droplet
double tinyTickInterval = time - lastDropletTime;
while (tinyTickInterval > 100)
tinyTickInterval /= 2;
for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval)
{ {
StartTime = lastTickTime, double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration;
ComboColour = ComboColour,
X = Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH, AddNested(new TinyDroplet
{
StartTime = t,
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
{ {
Bank = s.Bank, Bank = s.Bank,
@ -93,19 +99,12 @@ namespace osu.Game.Rulesets.Catch.Objects
}); });
} }
double tinyTickInterval = tickDistance / length * spanDuration; if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd)
while (tinyTickInterval > 100)
tinyTickInterval /= 2;
for (double t = 0; t < spanDuration; t += tinyTickInterval)
{ {
double progress = reversed ? 1 - t / spanDuration : t / spanDuration; AddNested(new Droplet
AddNested(new TinyDroplet
{ {
StartTime = spanStartTime + t, StartTime = time,
ComboColour = ComboColour, X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
{ {
Bank = s.Bank, Bank = s.Bank,
@ -115,19 +114,21 @@ namespace osu.Game.Rulesets.Catch.Objects
}); });
} }
lastDropletTime = time;
}
AddNested(new Fruit AddNested(new Fruit
{ {
Samples = Samples, Samples = Samples,
ComboColour = ComboColour,
StartTime = spanStartTime + spanDuration, StartTime = spanStartTime + spanDuration,
X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
}); });
} }
} }
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
public float EndX => Curve.PositionAt(this.ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH; public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH;
public double Duration => EndTime - StartTime; public double Duration => EndTime - StartTime;

View File

@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Replays
} }
else if (h.HyperDash) else if (h.HyperDash)
{ {
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition, ReplayButtonState.Right1)); Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition));
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
} }
else if (dashRequired) else if (dashRequired)
@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Replays
float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable); float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable);
//dash movement //dash movement
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, ReplayButtonState.Left1)); Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, true));
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition)); Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition));
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
} }
@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Catch.Replays
{ {
double timeBefore = positionChange / movement_speed; double timeBefore = positionChange / movement_speed;
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition, ReplayButtonState.Right1)); Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition));
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
} }

View File

@ -3,37 +3,51 @@
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Catch.Replays namespace osu.Game.Rulesets.Catch.Replays
{ {
public class CatchFramedReplayInputHandler : FramedReplayInputHandler public class CatchFramedReplayInputHandler : FramedReplayInputHandler<CatchReplayFrame>
{ {
public CatchFramedReplayInputHandler(Replay replay) public CatchFramedReplayInputHandler(Replay replay)
: base(replay) : base(replay)
{ {
} }
protected override bool IsImportant(CatchReplayFrame frame) => frame.Position > 0;
protected float? Position
{
get
{
if (!HasFrames)
return null;
return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time);
}
}
public override List<InputState> GetPendingStates() public override List<InputState> GetPendingStates()
{ {
if (!Position.HasValue) return new List<InputState>(); if (!Position.HasValue) return new List<InputState>();
var action = new List<CatchAction>(); var actions = new List<CatchAction>();
if (CurrentFrame.ButtonState == ReplayButtonState.Left1) if (CurrentFrame.Dashing)
action.Add(CatchAction.Dash); actions.Add(CatchAction.Dash);
if (Position.Value.X > CurrentFrame.Position.X) if (Position.Value > CurrentFrame.Position)
action.Add(CatchAction.MoveRight); actions.Add(CatchAction.MoveRight);
else if (Position.Value.X < CurrentFrame.Position.X) else if (Position.Value < CurrentFrame.Position)
action.Add(CatchAction.MoveLeft); actions.Add(CatchAction.MoveLeft);
return new List<InputState> return new List<InputState>
{ {
new CatchReplayState new CatchReplayState
{ {
PressedActions = action, PressedActions = actions,
CatcherX = Position.Value.X CatcherX = Position.Value
}, },
}; };
} }

View File

@ -1,17 +1,34 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Replays.Legacy;
using osu.Game.Rulesets.Replays.Types;
namespace osu.Game.Rulesets.Catch.Replays namespace osu.Game.Rulesets.Catch.Replays
{ {
public class CatchReplayFrame : ReplayFrame public class CatchReplayFrame : ReplayFrame, IConvertibleReplayFrame
{ {
public override bool IsImportant => MouseX > 0; public float Position;
public bool Dashing;
public CatchReplayFrame(double time, float? x = null, ReplayButtonState button = ReplayButtonState.None) public CatchReplayFrame()
: base(time, x ?? -1, null, button)
{ {
} }
public CatchReplayFrame(double time, float? position = null, bool dashing = false)
: base(time)
{
Position = position ?? -1;
Dashing = dashing;
}
public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap)
{
Position = legacyFrame.Position.X / CatchPlayfield.BASE_WIDTH;
Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1;
}
} }
} }

View File

@ -0,0 +1,957 @@
{
"Mappings": [{
"StartTime": 500.0,
"Objects": [{
"StartTime": 500.0,
"Position": 96.0
}, {
"StartTime": 562.0,
"Position": 100.84
}, {
"StartTime": 625.0,
"Position": 125.0
}, {
"StartTime": 687.0,
"Position": 152.84
}, {
"StartTime": 750.0,
"Position": 191.0
}, {
"StartTime": 812.0,
"Position": 212.84
}, {
"StartTime": 875.0,
"Position": 217.0
}, {
"StartTime": 937.0,
"Position": 234.84
}, {
"StartTime": 1000.0,
"Position": 256.0
}, {
"StartTime": 1062.0,
"Position": 267.84
}, {
"StartTime": 1125.0,
"Position": 284.0
}, {
"StartTime": 1187.0,
"Position": 311.84
}, {
"StartTime": 1250.0,
"Position": 350.0
}, {
"StartTime": 1312.0,
"Position": 359.84
}, {
"StartTime": 1375.0,
"Position": 367.0
}, {
"StartTime": 1437.0,
"Position": 400.84
}, {
"StartTime": 1500.0,
"Position": 416.0
}, {
"StartTime": 1562.0,
"Position": 377.159973
}, {
"StartTime": 1625.0,
"Position": 367.0
}, {
"StartTime": 1687.0,
"Position": 374.159973
}, {
"StartTime": 1750.0,
"Position": 353.0
}, {
"StartTime": 1812.0,
"Position": 329.159973
}, {
"StartTime": 1875.0,
"Position": 288.0
}, {
"StartTime": 1937.0,
"Position": 259.159973
}, {
"StartTime": 2000.0,
"Position": 256.0
}, {
"StartTime": 2058.0,
"Position": 232.44
}, {
"StartTime": 2116.0,
"Position": 222.879974
}, {
"StartTime": 2174.0,
"Position": 185.319992
}, {
"StartTime": 2232.0,
"Position": 177.76001
}, {
"StartTime": 2290.0,
"Position": 162.200012
}, {
"StartTime": 2348.0,
"Position": 158.639984
}, {
"StartTime": 2406.0,
"Position": 111.079994
}, {
"StartTime": 2500.0,
"Position": 96.0
}]
}, {
"StartTime": 3000.0,
"Objects": [{
"StartTime": 3000.0,
"Position": 18.0
}, {
"StartTime": 3062.0,
"Position": 482.0
}, {
"StartTime": 3125.0,
"Position": 243.0
}, {
"StartTime": 3187.0,
"Position": 332.0
}, {
"StartTime": 3250.0,
"Position": 477.0
}, {
"StartTime": 3312.0,
"Position": 376.0
}, {
"StartTime": 3375.0,
"Position": 104.0
}, {
"StartTime": 3437.0,
"Position": 156.0
}, {
"StartTime": 3500.0,
"Position": 135.0
}, {
"StartTime": 3562.0,
"Position": 256.0
}, {
"StartTime": 3625.0,
"Position": 360.0
}, {
"StartTime": 3687.0,
"Position": 199.0
}, {
"StartTime": 3750.0,
"Position": 239.0
}, {
"StartTime": 3812.0,
"Position": 326.0
}, {
"StartTime": 3875.0,
"Position": 393.0
}, {
"StartTime": 3937.0,
"Position": 470.0
}, {
"StartTime": 4000.0,
"Position": 136.0
}]
}, {
"StartTime": 4500.0,
"Objects": [{
"StartTime": 4500.0,
"Position": 317.0
}, {
"StartTime": 4562.0,
"Position": 354.0
}, {
"StartTime": 4625.0,
"Position": 414.0
}, {
"StartTime": 4687.0,
"Position": 39.0
}, {
"StartTime": 4750.0,
"Position": 172.0
}, {
"StartTime": 4812.0,
"Position": 479.0
}, {
"StartTime": 4875.0,
"Position": 18.0
}, {
"StartTime": 4937.0,
"Position": 151.0
}, {
"StartTime": 5000.0,
"Position": 342.0
}, {
"StartTime": 5062.0,
"Position": 400.0
}, {
"StartTime": 5125.0,
"Position": 420.0
}, {
"StartTime": 5187.0,
"Position": 90.0
}, {
"StartTime": 5250.0,
"Position": 220.0
}, {
"StartTime": 5312.0,
"Position": 80.0
}, {
"StartTime": 5375.0,
"Position": 421.0
}, {
"StartTime": 5437.0,
"Position": 473.0
}, {
"StartTime": 5500.0,
"Position": 97.0
}]
}, {
"StartTime": 6000.0,
"Objects": [{
"StartTime": 6000.0,
"Position": 105.0
}, {
"StartTime": 6062.0,
"Position": 249.0
}, {
"StartTime": 6125.0,
"Position": 163.0
}, {
"StartTime": 6187.0,
"Position": 194.0
}, {
"StartTime": 6250.0,
"Position": 106.0
}, {
"StartTime": 6312.0,
"Position": 212.0
}, {
"StartTime": 6375.0,
"Position": 257.0
}, {
"StartTime": 6437.0,
"Position": 461.0
}, {
"StartTime": 6500.0,
"Position": 79.0
}]
}, {
"StartTime": 7000.0,
"Objects": [{
"StartTime": 7000.0,
"Position": 256.0
}, {
"StartTime": 7062.0,
"Position": 294.84
}, {
"StartTime": 7125.0,
"Position": 279.0
}, {
"StartTime": 7187.0,
"Position": 309.84
}, {
"StartTime": 7250.0,
"Position": 336.0
}, {
"StartTime": 7312.0,
"Position": 322.16
}, {
"StartTime": 7375.0,
"Position": 308.0
}, {
"StartTime": 7437.0,
"Position": 263.16
}, {
"StartTime": 7500.0,
"Position": 256.0
}, {
"StartTime": 7562.0,
"Position": 261.84
}, {
"StartTime": 7625.0,
"Position": 277.0
}, {
"StartTime": 7687.0,
"Position": 318.84
}, {
"StartTime": 7750.0,
"Position": 336.0
}, {
"StartTime": 7803.0,
"Position": 305.04
}, {
"StartTime": 7857.0,
"Position": 307.76
}, {
"StartTime": 7910.0,
"Position": 297.8
}, {
"StartTime": 8000.0,
"Position": 256.0
}]
}, {
"StartTime": 8500.0,
"Objects": [{
"StartTime": 8500.0,
"Position": 32.0
}, {
"StartTime": 8562.0,
"Position": 22.8515015
}, {
"StartTime": 8625.0,
"Position": 28.5659637
}, {
"StartTime": 8687.0,
"Position": 50.3433228
}, {
"StartTime": 8750.0,
"Position": 56.58974
}, {
"StartTime": 8812.0,
"Position": 64.23422
}, {
"StartTime": 8875.0,
"Position": 67.7117844
}, {
"StartTime": 8937.0,
"Position": 90.52607
}, {
"StartTime": 9000.0,
"Position": 101.81015
}, {
"StartTime": 9062.0,
"Position": 113.478188
}, {
"StartTime": 9125.0,
"Position": 159.414444
}, {
"StartTime": 9187.0,
"Position": 155.1861
}, {
"StartTime": 9250.0,
"Position": 179.600418
}, {
"StartTime": 9312.0,
"Position": 212.293015
}, {
"StartTime": 9375.0,
"Position": 197.2076
}, {
"StartTime": 9437.0,
"Position": 243.438324
}, {
"StartTime": 9500.0,
"Position": 237.2304
}, {
"StartTime": 9562.0,
"Position": 241.253983
}, {
"StartTime": 9625.0,
"Position": 258.950623
}, {
"StartTime": 9687.0,
"Position": 253.3786
}, {
"StartTime": 9750.0,
"Position": 270.8865
}, {
"StartTime": 9812.0,
"Position": 244.38974
}, {
"StartTime": 9875.0,
"Position": 242.701874
}, {
"StartTime": 9937.0,
"Position": 256.2331
}, {
"StartTime": 10000.0,
"Position": 270.339874
}, {
"StartTime": 10062.0,
"Position": 275.9349
}, {
"StartTime": 10125.0,
"Position": 297.2969
}, {
"StartTime": 10187.0,
"Position": 307.834137
}, {
"StartTime": 10250.0,
"Position": 321.6449
}, {
"StartTime": 10312.0,
"Position": 357.746338
}, {
"StartTime": 10375.0,
"Position": 358.21875
}, {
"StartTime": 10437.0,
"Position": 394.943
}, {
"StartTime": 10500.0,
"Position": 401.0588
}, {
"StartTime": 10558.0,
"Position": 418.21347
}, {
"StartTime": 10616.0,
"Position": 424.6034
}, {
"StartTime": 10674.0,
"Position": 455.835754
}, {
"StartTime": 10732.0,
"Position": 477.5042
}, {
"StartTime": 10790.0,
"Position": 476.290955
}, {
"StartTime": 10848.0,
"Position": 470.943237
}, {
"StartTime": 10906.0,
"Position": 503.3372
}, {
"StartTime": 10999.0,
"Position": 508.166229
}]
}, {
"StartTime": 11500.0,
"Objects": [{
"StartTime": 11500.0,
"Position": 321.0
}, {
"StartTime": 11562.0,
"Position": 17.0
}, {
"StartTime": 11625.0,
"Position": 173.0
}, {
"StartTime": 11687.0,
"Position": 170.0
}, {
"StartTime": 11750.0,
"Position": 447.0
}, {
"StartTime": 11812.0,
"Position": 218.0
}, {
"StartTime": 11875.0,
"Position": 394.0
}, {
"StartTime": 11937.0,
"Position": 46.0
}, {
"StartTime": 12000.0,
"Position": 480.0
}]
}, {
"StartTime": 12500.0,
"Objects": [{
"StartTime": 12500.0,
"Position": 512.0
}, {
"StartTime": 12562.0,
"Position": 491.3132
}, {
"StartTime": 12625.0,
"Position": 484.3089
}, {
"StartTime": 12687.0,
"Position": 454.6221
}, {
"StartTime": 12750.0,
"Position": 433.617767
}, {
"StartTime": 12812.0,
"Position": 399.930969
}, {
"StartTime": 12875.0,
"Position": 395.926666
}, {
"StartTime": 12937.0,
"Position": 361.239868
}, {
"StartTime": 13000.0,
"Position": 353.235535
}, {
"StartTime": 13062.0,
"Position": 314.548767
}, {
"StartTime": 13125.0,
"Position": 315.544434
}, {
"StartTime": 13187.0,
"Position": 288.857635
}, {
"StartTime": 13250.0,
"Position": 254.853333
}, {
"StartTime": 13312.0,
"Position": 239.166534
}, {
"StartTime": 13375.0,
"Position": 240.1622
}, {
"StartTime": 13437.0,
"Position": 212.4754
}, {
"StartTime": 13500.0,
"Position": 194.471069
}, {
"StartTime": 13562.0,
"Position": 161.784271
}, {
"StartTime": 13625.0,
"Position": 145.779968
}, {
"StartTime": 13687.0,
"Position": 129.09314
}, {
"StartTime": 13750.0,
"Position": 104.088837
}, {
"StartTime": 13812.0,
"Position": 95.40204
}, {
"StartTime": 13875.0,
"Position": 61.3977356
}, {
"StartTime": 13937.0,
"Position": 56.710907
}, {
"StartTime": 14000.0,
"Position": 35.7066345
}, {
"StartTime": 14062.0,
"Position": 5.019806
}, {
"StartTime": 14125.0,
"Position": 0.0
}, {
"StartTime": 14187.0,
"Position": 39.7696266
}, {
"StartTime": 14250.0,
"Position": 23.0119171
}, {
"StartTime": 14312.0,
"Position": 75.94882
}, {
"StartTime": 14375.0,
"Position": 98.19112
}, {
"StartTime": 14437.0,
"Position": 82.12803
}, {
"StartTime": 14500.0,
"Position": 118.370323
}, {
"StartTime": 14562.0,
"Position": 149.307236
}, {
"StartTime": 14625.0,
"Position": 168.549515
}, {
"StartTime": 14687.0,
"Position": 190.486435
}, {
"StartTime": 14750.0,
"Position": 186.728714
}, {
"StartTime": 14812.0,
"Position": 199.665634
}, {
"StartTime": 14875.0,
"Position": 228.907928
}, {
"StartTime": 14937.0,
"Position": 264.844849
}, {
"StartTime": 15000.0,
"Position": 271.087128
}, {
"StartTime": 15062.0,
"Position": 290.024017
}, {
"StartTime": 15125.0,
"Position": 302.266327
}, {
"StartTime": 15187.0,
"Position": 344.203247
}, {
"StartTime": 15250.0,
"Position": 356.445526
}, {
"StartTime": 15312.0,
"Position": 359.382446
}, {
"StartTime": 15375.0,
"Position": 401.624725
}, {
"StartTime": 15437.0,
"Position": 388.561646
}, {
"StartTime": 15500.0,
"Position": 423.803925
}, {
"StartTime": 15562.0,
"Position": 425.740845
}, {
"StartTime": 15625.0,
"Position": 449.983124
}, {
"StartTime": 15687.0,
"Position": 468.920044
}, {
"StartTime": 15750.0,
"Position": 492.162323
}, {
"StartTime": 15812.0,
"Position": 506.784332
}, {
"StartTime": 15875.0,
"Position": 474.226227
}, {
"StartTime": 15937.0,
"Position": 482.978638
}, {
"StartTime": 16000.0,
"Position": 446.420532
}, {
"StartTime": 16058.0,
"Position": 418.4146
}, {
"StartTime": 16116.0,
"Position": 425.408844
}, {
"StartTime": 16174.0,
"Position": 383.402924
}, {
"StartTime": 16232.0,
"Position": 363.397156
}, {
"StartTime": 16290.0,
"Position": 343.391235
}, {
"StartTime": 16348.0,
"Position": 328.385468
}, {
"StartTime": 16406.0,
"Position": 322.3797
}, {
"StartTime": 16500.0,
"Position": 291.1977
}]
}, {
"StartTime": 17000.0,
"Objects": [{
"StartTime": 17000.0,
"Position": 256.0
}, {
"StartTime": 17062.0,
"Position": 228.16
}, {
"StartTime": 17125.0,
"Position": 234.0
}, {
"StartTime": 17187.0,
"Position": 202.16
}, {
"StartTime": 17250.0,
"Position": 176.0
}, {
"StartTime": 17312.0,
"Position": 210.84
}, {
"StartTime": 17375.0,
"Position": 221.0
}, {
"StartTime": 17437.0,
"Position": 219.84
}, {
"StartTime": 17500.0,
"Position": 256.0
}, {
"StartTime": 17562.0,
"Position": 219.16
}, {
"StartTime": 17625.0,
"Position": 228.0
}, {
"StartTime": 17687.0,
"Position": 203.16
}, {
"StartTime": 17750.0,
"Position": 176.0
}, {
"StartTime": 17803.0,
"Position": 174.959991
}, {
"StartTime": 17857.0,
"Position": 214.23999
}, {
"StartTime": 17910.0,
"Position": 228.200012
}, {
"StartTime": 18000.0,
"Position": 256.0
}]
}, {
"StartTime": 18500.0,
"Objects": [{
"StartTime": 18500.0,
"Position": 362.0
}, {
"StartTime": 18559.0,
"Position": 249.0
}, {
"StartTime": 18618.0,
"Position": 357.0
}, {
"StartTime": 18678.0,
"Position": 167.0
}, {
"StartTime": 18737.0,
"Position": 477.0
}, {
"StartTime": 18796.0,
"Position": 411.0
}, {
"StartTime": 18856.0,
"Position": 254.0
}, {
"StartTime": 18915.0,
"Position": 308.0
}, {
"StartTime": 18975.0,
"Position": 399.0
}, {
"StartTime": 19034.0,
"Position": 176.0
}, {
"StartTime": 19093.0,
"Position": 14.0
}, {
"StartTime": 19153.0,
"Position": 258.0
}, {
"StartTime": 19212.0,
"Position": 221.0
}, {
"StartTime": 19271.0,
"Position": 481.0
}, {
"StartTime": 19331.0,
"Position": 92.0
}, {
"StartTime": 19390.0,
"Position": 211.0
}, {
"StartTime": 19450.0,
"Position": 135.0
}]
}, {
"StartTime": 19875.0,
"Objects": [{
"StartTime": 19875.0,
"Position": 216.0
}, {
"StartTime": 19937.0,
"Position": 215.307053
}, {
"StartTime": 20000.0,
"Position": 236.036865
}, {
"StartTime": 20062.0,
"Position": 236.312088
}, {
"StartTime": 20125.0,
"Position": 235.838928
}, {
"StartTime": 20187.0,
"Position": 269.9743
}, {
"StartTime": 20250.0,
"Position": 285.999146
}, {
"StartTime": 20312.0,
"Position": 283.669067
}, {
"StartTime": 20375.0,
"Position": 317.446747
}, {
"StartTime": 20437.0,
"Position": 330.750275
}, {
"StartTime": 20500.0,
"Position": 344.0156
}, {
"StartTime": 20562.0,
"Position": 318.472168
}, {
"StartTime": 20625.0,
"Position": 309.165466
}, {
"StartTime": 20687.0,
"Position": 317.044617
}, {
"StartTime": 20750.0,
"Position": 280.457367
}, {
"StartTime": 20812.0,
"Position": 272.220581
}, {
"StartTime": 20875.0,
"Position": 270.3294
}, {
"StartTime": 20937.0,
"Position": 262.57605
}, {
"StartTime": 21000.0,
"Position": 244.803329
}, {
"StartTime": 21062.0,
"Position": 215.958359
}, {
"StartTime": 21125.0,
"Position": 177.79332
}, {
"StartTime": 21187.0,
"Position": 190.948349
}, {
"StartTime": 21250.0,
"Position": 158.78334
}, {
"StartTime": 21312.0,
"Position": 136.93837
}, {
"StartTime": 21375.0,
"Position": 119.121056
}, {
"StartTime": 21437.0,
"Position": 132.387573
}, {
"StartTime": 21500.0,
"Position": 124.503014
}, {
"StartTime": 21562.0,
"Position": 118.749374
}, {
"StartTime": 21625.0,
"Position": 123.165535
}, {
"StartTime": 21687.0,
"Position": 96.02999
}, {
"StartTime": 21750.0,
"Position": 118.547928
}, {
"StartTime": 21812.0,
"Position": 128.856232
}, {
"StartTime": 21875.0,
"Position": 124.28746
}, {
"StartTime": 21937.0,
"Position": 150.754929
}, {
"StartTime": 22000.0,
"Position": 149.528732
}, {
"StartTime": 22062.0,
"Position": 145.1691
}, {
"StartTime": 22125.0,
"Position": 182.802155
}, {
"StartTime": 22187.0,
"Position": 178.6452
}, {
"StartTime": 22250.0,
"Position": 213.892181
}, {
"StartTime": 22312.0,
"Position": 218.713028
}, {
"StartTime": 22375.0,
"Position": 240.4715
}, {
"StartTime": 22437.0,
"Position": 239.371887
}, {
"StartTime": 22500.0,
"Position": 261.907257
}, {
"StartTime": 22562.0,
"Position": 314.353119
}, {
"StartTime": 22625.0,
"Position": 299.273376
}, {
"StartTime": 22687.0,
"Position": 356.98288
}, {
"StartTime": 22750.0,
"Position": 339.078552
}, {
"StartTime": 22812.0,
"Position": 377.8958
}, {
"StartTime": 22875.0,
"Position": 398.054047
}, {
"StartTime": 22937.0,
"Position": 398.739441
}, {
"StartTime": 23000.0,
"Position": 407.178467
}, {
"StartTime": 23062.0,
"Position": 444.8687
}, {
"StartTime": 23125.0,
"Position": 417.069977
}, {
"StartTime": 23187.0,
"Position": 454.688477
}, {
"StartTime": 23250.0,
"Position": 428.9612
}, {
"StartTime": 23312.0,
"Position": 441.92807
}, {
"StartTime": 23375.0,
"Position": 439.749878
}, {
"StartTime": 23433.0,
"Position": 455.644684
}, {
"StartTime": 23491.0,
"Position": 440.7359
}, {
"StartTime": 23549.0,
"Position": 430.0944
}, {
"StartTime": 23607.0,
"Position": 420.796173
}, {
"StartTime": 23665.0,
"Position": 435.897461
}, {
"StartTime": 23723.0,
"Position": 418.462555
}, {
"StartTime": 23781.0,
"Position": 405.53775
}, {
"StartTime": 23874.0,
"Position": 408.720825
}]
}]
}

View File

@ -0,0 +1,27 @@
osu file format v14
[Difficulty]
HPDrainRate:6
CircleSize:4
OverallDifficulty:7
ApproachRate:8.3
SliderMultiplier:1.6
SliderTickRate:1
[TimingPoints]
500,500,4,2,1,50,1,0
13426,-100,4,3,1,45,0,0
14884,-100,4,2,1,50,0,0
[HitObjects]
96,192,500,6,0,L|416:192,2,320
256,192,3000,12,0,4000,0:0:0:0:
256,192,4500,12,0,5500,0:0:0:0:
256,192,6000,12,0,6500,0:0:0:0:
256,128,7000,6,0,L|352:128,4,80
32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800
256,192,11500,12,0,12000,0:0:0:0:
512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280
256,256,17000,6,0,L|160:256,4,80
256,192,18500,12,0,19450,0:0:0:0:
216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280

View File

@ -0,0 +1,67 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Catch.Tests
{
public class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
[TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2232")]
public new void Test(string name)
{
base.Test(name);
}
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
{
if (hitObject is JuiceStream stream)
{
foreach (var nested in stream.NestedHitObjects)
{
yield return new ConvertValue
{
StartTime = nested.StartTime,
Position = ((CatchHitObject)nested).X * CatchPlayfield.BASE_WIDTH
};
}
}
else
{
yield return new ConvertValue
{
StartTime = hitObject.StartTime,
Position = ((CatchHitObject)hitObject).X * CatchPlayfield.BASE_WIDTH
};
}
}
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new CatchBeatmapConverter();
}
public struct ConvertValue : IEquatable<ConvertValue>
{
/// <summary>
/// A sane value to account for osu!stable using ints everwhere.
/// </summary>
private const float conversion_lenience = 2;
public double StartTime;
public float Position;
public bool Equals(ConvertValue other)
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
&& Precision.AlmostEquals(Position, other.Position, conversion_lenience);
}
}

View File

@ -12,7 +12,6 @@ using osu.Game.Rulesets.Catch.UI;
namespace osu.Game.Rulesets.Catch.Tests namespace osu.Game.Rulesets.Catch.Tests
{ {
[TestFixture] [TestFixture]
[Ignore("getting CI working")]
public class TestCaseBananaShower : Game.Tests.Visual.TestCasePlayer public class TestCaseBananaShower : Game.Tests.Visual.TestCasePlayer
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
@ -29,16 +28,14 @@ namespace osu.Game.Rulesets.Catch.Tests
{ {
} }
protected override Beatmap CreateBeatmap() protected override Beatmap CreateBeatmap(Ruleset ruleset)
{ {
var beatmap = new Beatmap var beatmap = new Beatmap
{ {
BeatmapInfo = new BeatmapInfo BeatmapInfo = new BeatmapInfo
{ {
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
{ Ruleset = ruleset.RulesetInfo
CircleSize = 6,
}
} }
}; };

View File

@ -6,7 +6,6 @@ using NUnit.Framework;
namespace osu.Game.Rulesets.Catch.Tests namespace osu.Game.Rulesets.Catch.Tests
{ {
[TestFixture] [TestFixture]
[Ignore("getting CI working")]
public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer
{ {
public TestCaseCatchPlayer() : base(new CatchRuleset()) public TestCaseCatchPlayer() : base(new CatchRuleset())

View File

@ -8,7 +8,6 @@ using osu.Game.Rulesets.Catch.Objects;
namespace osu.Game.Rulesets.Catch.Tests namespace osu.Game.Rulesets.Catch.Tests
{ {
[TestFixture] [TestFixture]
[Ignore("getting CI working")]
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
{ {
public TestCaseCatchStacker() public TestCaseCatchStacker()
@ -16,19 +15,18 @@ namespace osu.Game.Rulesets.Catch.Tests
{ {
} }
protected override Beatmap CreateBeatmap() protected override Beatmap CreateBeatmap(Ruleset ruleset)
{ {
var beatmap = new Beatmap var beatmap = new Beatmap
{ {
BeatmapInfo = new BeatmapInfo BeatmapInfo = new BeatmapInfo
{ {
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
{ Ruleset = ruleset.RulesetInfo
CircleSize = 6,
}
} }
}; };
for (int i = 0; i < 512; i++) for (int i = 0; i < 512; i++)
beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 }); beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 });

View File

@ -13,7 +13,6 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Catch.Tests namespace osu.Game.Rulesets.Catch.Tests
{ {
[TestFixture] [TestFixture]
[Ignore("getting CI working")]
public class TestCaseCatcherArea : OsuTestCase public class TestCaseCatcherArea : OsuTestCase
{ {
private RulesetInfo catchRuleset; private RulesetInfo catchRuleset;

View File

@ -6,17 +6,15 @@ using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
using OpenTK; using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Tests namespace osu.Game.Rulesets.Catch.Tests
{ {
[Ignore("getting CI working")] [TestFixture]
public class TestCaseFruitObjects : OsuTestCase public class TestCaseFruitObjects : OsuTestCase
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
@ -62,8 +60,6 @@ namespace osu.Game.Rulesets.Catch.Tests
Scale = 1.5f, Scale = 1.5f,
}; };
fruit.ComboColour = colourForRrepesentation(fruit.VisualRepresentation);
return new DrawableFruit(fruit) return new DrawableFruit(fruit)
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
@ -74,31 +70,5 @@ namespace osu.Game.Rulesets.Catch.Tests
LifetimeEnd = double.PositiveInfinity, LifetimeEnd = double.PositiveInfinity,
}; };
} }
private Color4 colourForRrepesentation(FruitVisualRepresentation representation)
{
switch (representation)
{
default:
case FruitVisualRepresentation.Pear:
return new Color4(17, 136, 170, 255);
case FruitVisualRepresentation.Grape:
return new Color4(204, 102, 0, 255);
case FruitVisualRepresentation.Raspberry:
return new Color4(121, 9, 13, 255);
case FruitVisualRepresentation.Pineapple:
return new Color4(102, 136, 0, 255);
case FruitVisualRepresentation.Banana:
switch (RNG.Next(0, 3))
{
default:
return new Color4(255, 240, 0, 255);
case 1:
return new Color4(255, 192, 0, 255);
case 2:
return new Color4(214, 221, 28, 255);
}
}
}
} }
} }

View File

@ -8,7 +8,6 @@ using osu.Game.Rulesets.Catch.Objects;
namespace osu.Game.Rulesets.Catch.Tests namespace osu.Game.Rulesets.Catch.Tests
{ {
[TestFixture] [TestFixture]
[Ignore("getting CI working")]
public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
{ {
public TestCaseHyperdash() public TestCaseHyperdash()
@ -16,9 +15,10 @@ namespace osu.Game.Rulesets.Catch.Tests
{ {
} }
protected override Beatmap CreateBeatmap() protected override Beatmap CreateBeatmap(Ruleset ruleset)
{ {
var beatmap = new Beatmap(); var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } };
for (int i = 0; i < 512; i++) for (int i = 0; i < 512; i++)
if (i % 5 < 3) if (i % 5 < 3)

View File

@ -5,7 +5,7 @@ using NUnit.Framework;
namespace osu.Game.Rulesets.Catch.Tests namespace osu.Game.Rulesets.Catch.Tests
{ {
[Ignore("getting CI working")] [TestFixture]
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
{ {
public TestCasePerformancePoints() public TestCasePerformancePoints()

View File

@ -3,6 +3,7 @@
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Input.Handlers;
using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Catch.Objects.Drawable;
@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.UI
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
protected override BeatmapProcessor<CatchHitObject> CreateBeatmapProcessor() => new CatchBeatmapProcessor(); protected override BeatmapProcessor<CatchHitObject> CreateBeatmapProcessor() => new CatchBeatmapProcessor();

View File

@ -54,7 +54,6 @@ namespace osu.Game.Rulesets.Catch.UI
if (caughtFruit == null) return; if (caughtFruit == null) return;
caughtFruit.AccentColour = fruit.AccentColour;
caughtFruit.RelativePositionAxes = Axes.None; caughtFruit.RelativePositionAxes = Axes.None;
caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0); caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0);

View File

@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
yield break; yield break;
} }
var objects = IsForCurrentRuleset ? generateSpecific(original) : generateConverted(original); var objects = IsForCurrentRuleset ? generateSpecific(original, beatmap) : generateConverted(original, beatmap);
if (objects == null) if (objects == null)
yield break; yield break;
@ -110,10 +110,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// Method that generates hit objects for osu!mania specific beatmaps. /// Method that generates hit objects for osu!mania specific beatmaps.
/// </summary> /// </summary>
/// <param name="original">The original hit object.</param> /// <param name="original">The original hit object.</param>
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
/// <returns>The hit objects generated.</returns> /// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original) private IEnumerable<ManiaHitObject> generateSpecific(HitObject original, Beatmap originalBeatmap)
{ {
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern); var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
Pattern newPattern = generator.Generate(); Pattern newPattern = generator.Generate();
lastPattern = newPattern; lastPattern = newPattern;
@ -125,26 +126,25 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// Method that generates hit objects for non-osu!mania beatmaps. /// Method that generates hit objects for non-osu!mania beatmaps.
/// </summary> /// </summary>
/// <param name="original">The original hit object.</param> /// <param name="original">The original hit object.</param>
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
/// <returns>The hit objects generated.</returns> /// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateConverted(HitObject original) private IEnumerable<ManiaHitObject> generateConverted(HitObject original, Beatmap originalBeatmap)
{ {
var endTimeData = original as IHasEndTime; var endTimeData = original as IHasEndTime;
var distanceData = original as IHasDistance; var distanceData = original as IHasDistance;
var positionData = original as IHasPosition; var positionData = original as IHasPosition;
// Following lines currently commented out to appease resharper
Patterns.PatternGenerator conversion = null; Patterns.PatternGenerator conversion = null;
if (distanceData != null) if (distanceData != null)
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern); conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
else if (endTimeData != null) else if (endTimeData != null)
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap); conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, originalBeatmap);
else if (positionData != null) else if (positionData != null)
{ {
computeDensity(original.StartTime); computeDensity(original.StartTime);
conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair); conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);
recordNote(original.StartTime, positionData.Position); recordNote(original.StartTime, positionData.Position);
} }
@ -153,10 +153,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
return null; return null;
Pattern newPattern = conversion.Generate(); Pattern newPattern = conversion.Generate();
lastPattern = newPattern;
var stairPatternGenerator = conversion as HitObjectPatternGenerator; lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
lastStair = stairPatternGenerator?.StairType ?? lastStair; lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;
return newPattern.HitObjects; return newPattern.HitObjects;
} }
@ -166,8 +165,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// </summary> /// </summary>
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
{ {
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern) public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
: base(random, hitObject, beatmap, previousPattern) : base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{ {
} }

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
@ -29,11 +30,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private PatternType convertType; private PatternType convertType;
public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern) public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
: base(random, hitObject, beatmap, previousPattern) : base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{ {
convertType = PatternType.None; convertType = PatternType.None;
if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode) if (!Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
convertType = PatternType.LowProbability; convertType = PatternType.LowProbability;
var distanceData = hitObject as IHasDistance; var distanceData = hitObject as IHasDistance;
@ -305,19 +306,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p4 = 0; p4 = 0;
break; break;
case 3: case 3:
p2 = Math.Max(p2, 0.1); p2 = Math.Min(p2, 0.1);
p3 = 0; p3 = 0;
p4 = 0; p4 = 0;
break; break;
case 4: case 4:
p2 = Math.Max(p2, 0.3); p2 = Math.Min(p2, 0.3);
p3 = Math.Max(p3, 0.04); p3 = Math.Min(p3, 0.04);
p4 = 0; p4 = 0;
break; break;
case 5: case 5:
p2 = Math.Max(p2, 0.34); p2 = Math.Min(p2, 0.34);
p3 = Math.Max(p3, 0.1); p3 = Math.Min(p3, 0.1);
p4 = Math.Max(p4, 0.03); p4 = Math.Min(p4, 0.03);
break; break;
} }
@ -396,17 +397,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// Create the hold note // Create the hold note
addToPattern(pattern, holdColumn, startTime, endTime); addToPattern(pattern, holdColumn, startTime, endTime);
int noteCount = 1; int nextColumn = Random.Next(RandomStart, TotalColumns);
int noteCount;
if (ConversionDifficulty > 6.5) if (ConversionDifficulty > 6.5)
noteCount = GetRandomNoteCount(0.63, 0); noteCount = GetRandomNoteCount(0.63, 0);
else if (ConversionDifficulty > 4) else if (ConversionDifficulty > 4)
noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0.12 : 0.45, 0); noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0.12 : 0.45, 0);
else if (ConversionDifficulty > 2.5) else if (ConversionDifficulty > 2.5)
noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0 : 0.24, 0); noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0 : 0.24, 0);
else
noteCount = 0;
noteCount = Math.Min(TotalColumns - 1, noteCount); noteCount = Math.Min(TotalColumns - 1, noteCount);
bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP); bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP);
int nextColumn = Random.Next(RandomStart, TotalColumns);
var rowPattern = new Pattern(); var rowPattern = new Pattern();
for (int i = 0; i <= spanCount; i++) for (int i = 0; i <= spanCount; i++)

View File

@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using System.Linq; using System.Linq;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
@ -15,8 +16,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{ {
private readonly double endTime; private readonly double endTime;
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap) public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Beatmap originalBeatmap)
: base(random, hitObject, beatmap, new Pattern()) : base(random, hitObject, beatmap, new Pattern(), originalBeatmap)
{ {
var endtimeData = HitObject as IHasEndTime; var endtimeData = HitObject as IHasEndTime;

View File

@ -5,6 +5,7 @@ using System;
using System.Linq; using System.Linq;
using OpenTK; using OpenTK;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
@ -19,8 +20,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private readonly PatternType convertType; private readonly PatternType convertType;
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair) public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, Beatmap originalBeatmap)
: base(random, hitObject, beatmap, previousPattern) : base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{ {
if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime)); if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
if (density < 0) throw new ArgumentOutOfRangeException(nameof(density)); if (density < 0) throw new ArgumentOutOfRangeException(nameof(density));
@ -308,20 +309,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p5 = 0; p5 = 0;
break; break;
case 3: case 3:
p2 = Math.Max(p2, 0.1); p2 = Math.Min(p2, 0.1);
p3 = 0; p3 = 0;
p4 = 0; p4 = 0;
p5 = 0; p5 = 0;
break; break;
case 4: case 4:
p2 = Math.Max(p2, 0.23); p2 = Math.Min(p2, 0.23);
p3 = Math.Max(p3, 0.04); p3 = Math.Min(p3, 0.04);
p4 = 0; p4 = 0;
p5 = 0; p5 = 0;
break; break;
case 5: case 5:
p3 = Math.Max(p3, 0.15); p3 = Math.Min(p3, 0.15);
p4 = Math.Max(p4, 0.03); p4 = Math.Min(p4, 0.03);
p5 = 0; p5 = 0;
break; break;
} }
@ -355,23 +356,23 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p3 = 0; p3 = 0;
break; break;
case 3: case 3:
centreProbability = Math.Max(centreProbability, 0.03); centreProbability = Math.Min(centreProbability, 0.03);
p2 = Math.Max(p2, 0.1); p2 = 0;
p3 = 0; p3 = 0;
break; break;
case 4: case 4:
centreProbability = 0; centreProbability = 0;
p2 = Math.Max(p2 * 2, 0.2); p2 = Math.Min(p2 * 2, 0.2);
p3 = 0; p3 = 0;
break; break;
case 5: case 5:
centreProbability = Math.Max(centreProbability, 0.03); centreProbability = Math.Min(centreProbability, 0.03);
p3 = 0; p3 = 0;
break; break;
case 6: case 6:
centreProbability = 0; centreProbability = 0;
p2 = Math.Max(p2 * 2, 0.5); p2 = Math.Min(p2 * 2, 0.5);
p3 = Math.Max(p3 * 2, 0.15); p3 = Math.Min(p3 * 2, 0.15);
break; break;
} }

View File

@ -25,14 +25,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// </summary> /// </summary>
protected readonly FastRandom Random; protected readonly FastRandom Random;
protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern) /// <summary>
/// The beatmap which <see cref="HitObject"/> is being converted from.
/// </summary>
protected readonly Beatmap OriginalBeatmap;
protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
: base(hitObject, beatmap, previousPattern) : base(hitObject, beatmap, previousPattern)
{ {
if (random == null) throw new ArgumentNullException(nameof(random)); if (random == null) throw new ArgumentNullException(nameof(random));
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); if (originalBeatmap == null) throw new ArgumentNullException(nameof(originalBeatmap));
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
Random = random; Random = random;
OriginalBeatmap = originalBeatmap;
RandomStart = TotalColumns == 8 ? 1 : 0; RandomStart = TotalColumns == 8 ? 1 : 0;
} }
@ -94,17 +100,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (conversionDifficulty != null) if (conversionDifficulty != null)
return conversionDifficulty.Value; return conversionDifficulty.Value;
HitObject lastObject = Beatmap.HitObjects.LastOrDefault(); HitObject lastObject = OriginalBeatmap.HitObjects.LastOrDefault();
HitObject firstObject = Beatmap.HitObjects.FirstOrDefault(); HitObject firstObject = OriginalBeatmap.HitObjects.FirstOrDefault();
double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0); double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0);
drainTime -= Beatmap.TotalBreakTime; drainTime -= OriginalBeatmap.TotalBreakTime;
if (drainTime == 0) if (drainTime == 0)
drainTime = 10000; drainTime = 10000000;
BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.BaseDifficulty; // We need this in seconds
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + Beatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; drainTime /= 1000;
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12); conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
return conversionDifficulty.Value; return conversionDifficulty.Value;

View File

@ -14,5 +14,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// The number of <see cref="Column"/>s which this stage contains. /// The number of <see cref="Column"/>s which this stage contains.
/// </summary> /// </summary>
public int Columns; public int Columns;
/// <summary>
/// Whether the column index is a special column for this stage.
/// </summary>
/// <param name="column">The 0-based column index.</param>
/// <returns>Whether the column is a special column.</returns>
public bool IsSpecialColumn(int column) => Columns % 2 == 1 && column == Columns / 2;
} }
} }

View File

@ -4,18 +4,142 @@
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace osu.Game.Rulesets.Mania namespace osu.Game.Rulesets.Mania
{ {
public class ManiaDifficultyCalculator : DifficultyCalculator<ManiaHitObject> internal class ManiaDifficultyCalculator : DifficultyCalculator<ManiaHitObject>
{ {
private const double star_scaling_factor = 0.018;
/// <summary>
/// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size strain_step.
/// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.
/// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage.
/// </summary>
private const double strain_step = 400;
/// <summary>
/// The weighting of each strain value decays to this number * it's previous value
/// </summary>
private const double decay_weight = 0.9;
/// <summary>
/// HitObjects are stored as a member variable.
/// </summary>
private readonly List<ManiaHitObjectDifficulty> difficultyHitObjects = new List<ManiaHitObjectDifficulty>();
public ManiaDifficultyCalculator(Beatmap beatmap) public ManiaDifficultyCalculator(Beatmap beatmap)
: base(beatmap) : base(beatmap)
{ {
} }
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0; public ManiaDifficultyCalculator(Beatmap beatmap, Mod[] mods)
: base(beatmap, mods)
{
}
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
{
// Fill our custom DifficultyHitObject class, that carries additional information
difficultyHitObjects.Clear();
int columnCount = (Beatmap as ManiaBeatmap)?.TotalColumns ?? 7;
foreach (var hitObject in Beatmap.HitObjects)
difficultyHitObjects.Add(new ManiaHitObjectDifficulty(hitObject, columnCount));
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
if (!calculateStrainValues())
return 0;
double starRating = calculateDifficulty() * star_scaling_factor;
categoryDifficulty?.Add("Strain", starRating);
return starRating;
}
private bool calculateStrainValues()
{
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
using (List<ManiaHitObjectDifficulty>.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator())
{
if (!hitObjectsEnumerator.MoveNext())
return false;
ManiaHitObjectDifficulty current = hitObjectsEnumerator.Current;
// First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject.
while (hitObjectsEnumerator.MoveNext())
{
var next = hitObjectsEnumerator.Current;
next?.CalculateStrains(current, TimeRate);
current = next;
}
return true;
}
}
private double calculateDifficulty()
{
double actualStrainStep = strain_step * TimeRate;
// Find the highest strain value within each strain step
List<double> highestStrains = new List<double>();
double intervalEndTime = actualStrainStep;
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
ManiaHitObjectDifficulty previousHitObject = null;
foreach (var hitObject in difficultyHitObjects)
{
// While we are beyond the current interval push the currently available maximum to our strain list
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
{
highestStrains.Add(maximumStrain);
// The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
// until the beginning of the next interval.
if (previousHitObject == null)
{
maximumStrain = 0;
}
else
{
double individualDecay = Math.Pow(ManiaHitObjectDifficulty.INDIVIDUAL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
double overallDecay = Math.Pow(ManiaHitObjectDifficulty.OVERALL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
maximumStrain = previousHitObject.IndividualStrain * individualDecay + previousHitObject.OverallStrain * overallDecay;
}
// Go to the next time interval
intervalEndTime += actualStrainStep;
}
// Obtain maximum strain
double strain = hitObject.IndividualStrain + hitObject.OverallStrain;
maximumStrain = Math.Max(strain, maximumStrain);
previousHitObject = hitObject;
}
// Build the weighted sum over the highest strains for each interval
double difficulty = 0;
double weight = 1;
highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
foreach (double strain in highestStrains)
{
difficulty += weight * strain;
weight *= decay_weight;
}
return difficulty;
}
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, beatmap); protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, beatmap);
} }

View File

@ -12,6 +12,8 @@ using System.Linq;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Replays.Types;
namespace osu.Game.Rulesets.Mania namespace osu.Game.Rulesets.Mania
{ {
@ -89,6 +91,7 @@ namespace osu.Game.Rulesets.Mania
}, },
new ManiaModRandom(), new ManiaModRandom(),
new ManiaModDualStages(), new ManiaModDualStages(),
new ManiaModMirror(),
new MultiMod new MultiMod
{ {
Mods = new Mod[] Mods = new Mod[]
@ -110,9 +113,11 @@ namespace osu.Game.Rulesets.Mania
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o }; public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap); public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap, mods);
public override int LegacyID => 3; public override int? LegacyID => 3;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();
public ManiaRuleset(RulesetInfo rulesetInfo = null) public ManiaRuleset(RulesetInfo rulesetInfo = null)
: base(rulesetInfo) : base(rulesetInfo)

View File

@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils
/// </summary> /// </summary>
internal class FastRandom internal class FastRandom
{ {
private const double uint_to_real = 1.0 / (uint.MaxValue + 1.0); private const double int_to_real = 1.0 / (int.MaxValue + 1.0);
private const uint int_mask = 0x7FFFFFFF; private const uint int_mask = 0x7FFFFFFF;
private const uint y = 842502087; private const uint y = 842502087;
private const uint z = 3579807591; private const uint z = 3579807591;
@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils
/// Generates a random double value within the range [0, 1). /// Generates a random double value within the range [0, 1).
/// </summary> /// </summary>
/// <returns>The random value.</returns> /// <returns>The random value.</returns>
public double NextDouble() => uint_to_real * NextUInt(); public double NextDouble() => int_to_real * Next();
private uint bitBuffer; private uint bitBuffer;
private int bitIndex = 32; private int bitIndex = 32;

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Mods
return new Score return new Score
{ {
User = new User { Username = "osu!topus!" }, User = new User { Username = "osu!topus!" },
Replay = new ManiaAutoGenerator(beatmap).Generate(), Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
}; };
} }
} }

View File

@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModDaycore : ModDaycore public class ManiaModDaycore : ModDaycore
{ {
public override double ScoreMultiplier => 0.3; public override double ScoreMultiplier => 0.5;
} }
} }

View File

@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModDoubleTime : ModDoubleTime public class ManiaModDoubleTime : ModDoubleTime
{ {
public override double ScoreMultiplier => 1.0; public override double ScoreMultiplier => 1;
} }
} }

View File

@ -16,8 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Name => "Dual Stages"; public override string Name => "Dual Stages";
public override string ShortenedName => "DS"; public override string ShortenedName => "DS";
public override string Description => @"Double the stages, double the fun!"; public override string Description => @"Double the stages, double the fun!";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 0;
public override bool Ranked => false;
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter) public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter)
{ {

View File

@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModEasy : ModEasy public class ManiaModEasy : ModEasy
{ {
public override string Description => @"More forgiving HP drain, less accuracy required, and three lives!";
} }
} }

View File

@ -9,10 +9,11 @@ namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModFadeIn : Mod public class ManiaModFadeIn : Mod
{ {
public override string Name => "FadeIn"; public override string Name => "Fade In";
public override string ShortenedName => "FI"; public override string ShortenedName => "FI";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden; public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override ModType Type => ModType.DifficultyIncrease; public override ModType Type => ModType.DifficultyIncrease;
public override string Description => @"Keys appear out of nowhere!";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;
public override bool Ranked => true; public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) }; public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModFlashlight : ModFlashlight public class ManiaModFlashlight : ModFlashlight
{ {
public override double ScoreMultiplier => 1.0; public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(ModHidden) }; public override Type[] IncompatibleMods => new[] { typeof(ModHidden) };
} }
} }

View File

@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModHalfTime : ModHalfTime public class ManiaModHalfTime : ModHalfTime
{ {
public override double ScoreMultiplier => 0.3; public override double ScoreMultiplier => 0.5;
} }
} }

View File

@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModHardRock : ModHardRock public class ManiaModHardRock : ModHardRock
{ {
public override double ScoreMultiplier => 1.0; public override double ScoreMultiplier => 1;
} }
} }

View File

@ -8,8 +8,8 @@ namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModHidden : ModHidden public class ManiaModHidden : ModHidden
{ {
public override string Description => @"The notes fade out before you hit them!"; public override string Description => @"Keys fade out before you hit them!";
public override double ScoreMultiplier => 1.0; public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) }; public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
} }
} }

View File

@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey1 : ManiaKeyMod public class ManiaModKey1 : ManiaKeyMod
{ {
public override int KeyCount => 1; public override int KeyCount => 1;
public override string Name => "1K"; public override string Name => "One Key";
public override string ShortenedName => "1K";
public override string Description => @"Play with one key.";
} }
} }

View File

@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey2 : ManiaKeyMod public class ManiaModKey2 : ManiaKeyMod
{ {
public override int KeyCount => 2; public override int KeyCount => 2;
public override string Name => "2K"; public override string Name => "Two Keys";
public override string ShortenedName => "2K";
public override string Description => @"Play with two keys.";
} }
} }

View File

@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey3 : ManiaKeyMod public class ManiaModKey3 : ManiaKeyMod
{ {
public override int KeyCount => 3; public override int KeyCount => 3;
public override string Name => "3K"; public override string Name => "Three Keys";
public override string ShortenedName => "3K";
public override string Description => @"Play with three keys.";
} }
} }

View File

@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey4 : ManiaKeyMod public class ManiaModKey4 : ManiaKeyMod
{ {
public override int KeyCount => 4; public override int KeyCount => 4;
public override string Name => "4K"; public override string Name => "Four Keys";
public override string ShortenedName => "4K";
public override string Description => @"Play with four keys.";
} }
} }

View File

@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey5 : ManiaKeyMod public class ManiaModKey5 : ManiaKeyMod
{ {
public override int KeyCount => 5; public override int KeyCount => 5;
public override string Name => "5K"; public override string Name => "Five Keys";
public override string ShortenedName => "5K";
public override string Description => @"Play with five keys.";
} }
} }

View File

@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey6 : ManiaKeyMod public class ManiaModKey6 : ManiaKeyMod
{ {
public override int KeyCount => 6; public override int KeyCount => 6;
public override string Name => "6K"; public override string Name => "Six Keys";
public override string ShortenedName => "6K";
public override string Description => @"Play with six keys.";
} }
} }

View File

@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey7 : ManiaKeyMod public class ManiaModKey7 : ManiaKeyMod
{ {
public override int KeyCount => 7; public override int KeyCount => 7;
public override string Name => "7K"; public override string Name => "Seven Keys";
public override string ShortenedName => "7K";
public override string Description => @"Play with seven keys.";
} }
} }

View File

@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey8 : ManiaKeyMod public class ManiaModKey8 : ManiaKeyMod
{ {
public override int KeyCount => 8; public override int KeyCount => 8;
public override string Name => "8K"; public override string Name => "Eight Keys";
public override string ShortenedName => "8K";
public override string Description => @"Play with eight keys.";
} }
} }

View File

@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey9 : ManiaKeyMod public class ManiaModKey9 : ManiaKeyMod
{ {
public override int KeyCount => 9; public override int KeyCount => 9;
public override string Name => "9K"; public override string Name => "Nine Keys";
public override string ShortenedName => "9K";
public override string Description => @"Play with nine keys.";
} }
} }

View File

@ -0,0 +1,28 @@
// 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.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using System.Linq;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModMirror : Mod, IApplicableToRulesetContainer<ManiaHitObject>
{
public override string Name => "Mirror";
public override string ShortenedName => "MR";
public override ModType Type => ModType.Special;
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
{
var availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns;
rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = availableColumns - 1 - h.Column);
}
}
}

View File

@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModNightcore : ModNightcore public class ManiaModNightcore : ModNightcore
{ {
public override double ScoreMultiplier => 1.0; public override double ScoreMultiplier => 1;
} }
} }

View File

@ -17,8 +17,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Name => "Random"; public override string Name => "Random";
public override string ShortenedName => "RD"; public override string ShortenedName => "RD";
public override FontAwesome Icon => FontAwesome.fa_osu_dice; public override FontAwesome Icon => FontAwesome.fa_osu_dice;
public override string Description => @"Shuffle around the notes!"; public override string Description => @"Shuffle around the keys!";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 0;
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer) public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
{ {

View File

@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = 1; Height = 1;
Add(new Box AddInternal(new Box
{ {
Name = "Bar line", Name = "Bar line",
Anchor = Anchor.BottomCentre, Anchor = Anchor.BottomCentre,
@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (isMajor) if (isMajor)
{ {
Add(new EquilateralTriangle AddInternal(new EquilateralTriangle
{ {
Name = "Left triangle", Name = "Left triangle",
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Rotation = 90 Rotation = 90
}); });
Add(new EquilateralTriangle AddInternal(new EquilateralTriangle
{ {
Name = "Right triangle", Name = "Right triangle",
Anchor = Anchor.BottomRight, Anchor = Anchor.BottomRight,

View File

@ -8,7 +8,6 @@ using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -24,7 +23,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
private readonly GlowPiece glowPiece; private readonly GlowPiece glowPiece;
private readonly BodyPiece bodyPiece; private readonly BodyPiece bodyPiece;
private readonly Container<DrawableHoldNoteTick> tickContainer;
private readonly Container fullHeightContainer; private readonly Container fullHeightContainer;
/// <summary> /// <summary>
@ -40,9 +38,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public DrawableHoldNote(HoldNote hitObject, ManiaAction action) public DrawableHoldNote(HoldNote hitObject, ManiaAction action)
: base(hitObject, action) : base(hitObject, action)
{ {
Container<DrawableHoldNoteTick> tickContainer;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AddRange(new Drawable[] InternalChildren = new Drawable[]
{ {
// The hit object itself cannot be used for various elements because the tail overshoots it // The hit object itself cannot be used for various elements because the tail overshoots it
// So a specialized container that is updated to contain the tail height is used // So a specialized container that is updated to contain the tail height is used
@ -57,7 +56,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
}, },
tickContainer = new Container<DrawableHoldNoteTick> { RelativeSizeAxes = Axes.Both }, tickContainer = new Container<DrawableHoldNoteTick>
{
RelativeSizeAxes = Axes.Both,
ChildrenEnumerable = HitObject.NestedHitObjects.OfType<HoldNoteTick>().Select(tick => new DrawableHoldNoteTick(tick)
{
HoldStartTime = () => holdStartTime
})
},
head = new DrawableHeadNote(this, action) head = new DrawableHeadNote(this, action)
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
@ -68,18 +74,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre Origin = Anchor.TopCentre
} }
});
foreach (var tick in HitObject.NestedHitObjects.OfType<HoldNoteTick>())
{
var drawableTick = new DrawableHoldNoteTick(tick)
{
HoldStartTime = () => holdStartTime
}; };
tickContainer.Add(drawableTick); foreach (var tick in tickContainer)
AddNested(drawableTick); AddNested(tick);
}
AddNested(head); AddNested(head);
AddNested(tail); AddNested(tail);
@ -90,12 +88,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
get { return base.AccentColour; } get { return base.AccentColour; }
set set
{ {
if (base.AccentColour == value)
return;
base.AccentColour = value; base.AccentColour = value;
tickContainer.Children.ForEach(t => t.AccentColour = value);
glowPiece.AccentColour = value; glowPiece.AccentColour = value;
bodyPiece.AccentColour = value; bodyPiece.AccentColour = value;
head.AccentColour = value; head.AccentColour = value;

View File

@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Size = new Vector2(1); Size = new Vector2(1);
Children = new[] InternalChildren = new[]
{ {
glowContainer = new CircularContainer glowContainer = new CircularContainer
{ {

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics; using osu.Framework.Graphics;
using OpenTK.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables namespace osu.Game.Rulesets.Mania.Objects.Drawables
@ -28,16 +27,5 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (action != null) if (action != null)
Action = action.Value; Action = action.Value;
} }
public override Color4 AccentColour
{
get { return base.AccentColour; }
set
{
if (base.AccentColour == value)
return;
base.AccentColour = value;
}
}
} }
} }

View File

@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
Children = new Drawable[] InternalChildren = new Drawable[]
{ {
laneGlowPiece = new LaneGlowPiece laneGlowPiece = new LaneGlowPiece
{ {
@ -48,13 +48,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
get { return base.AccentColour; } get { return base.AccentColour; }
set set
{ {
if (base.AccentColour == value)
return;
base.AccentColour = value; base.AccentColour = value;
laneGlowPiece.AccentColour = AccentColour;
laneGlowPiece.AccentColour = value; GlowPiece.AccentColour = AccentColour;
GlowPiece.AccentColour = value; headPiece.AccentColour = AccentColour;
headPiece.AccentColour = value;
} }
} }

View File

@ -0,0 +1,113 @@
// 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.Objects.Types;
using System;
namespace osu.Game.Rulesets.Mania.Objects
{
internal class ManiaHitObjectDifficulty
{
/// <summary>
/// Factor by how much individual / overall strain decays per second.
/// </summary>
/// <remarks>
/// These values are results of tweaking a lot and taking into account general feedback.
/// </remarks>
internal const double INDIVIDUAL_DECAY_BASE = 0.125;
internal const double OVERALL_DECAY_BASE = 0.30;
internal ManiaHitObject BaseHitObject;
private readonly int beatmapColumnCount;
private readonly double endTime;
private readonly double[] heldUntil;
/// <summary>
/// Measures jacks or more generally: repeated presses of the same button
/// </summary>
private readonly double[] individualStrains;
internal double IndividualStrain
{
get
{
return individualStrains[BaseHitObject.Column];
}
set
{
individualStrains[BaseHitObject.Column] = value;
}
}
/// <summary>
/// Measures note density in a way
/// </summary>
internal double OverallStrain = 1;
public ManiaHitObjectDifficulty(ManiaHitObject baseHitObject, int columnCount)
{
BaseHitObject = baseHitObject;
endTime = (baseHitObject as IHasEndTime)?.EndTime ?? baseHitObject.StartTime;
beatmapColumnCount = columnCount;
heldUntil = new double[beatmapColumnCount];
individualStrains = new double[beatmapColumnCount];
for (int i = 0; i < beatmapColumnCount; ++i)
{
individualStrains[i] = 0;
heldUntil[i] = 0;
}
}
internal void CalculateStrains(ManiaHitObjectDifficulty previousHitObject, double timeRate)
{
// TODO: Factor in holds
double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
double individualDecay = Math.Pow(INDIVIDUAL_DECAY_BASE, timeElapsed / 1000);
double overallDecay = Math.Pow(OVERALL_DECAY_BASE, timeElapsed / 1000);
double holdFactor = 1.0; // Factor to all additional strains in case something else is held
double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly
// Fill up the heldUntil array
for (int i = 0; i < beatmapColumnCount; ++i)
{
heldUntil[i] = previousHitObject.heldUntil[i];
// If there is at least one other overlapping end or note, then we get an addition, buuuuuut...
if (BaseHitObject.StartTime < heldUntil[i] && endTime > heldUntil[i])
{
holdAddition = 1.0;
}
// ... this addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1
if (endTime == heldUntil[i])
{
holdAddition = 0;
}
// We give a slight bonus to everything if something is held meanwhile
if (heldUntil[i] > endTime)
{
holdFactor = 1.25;
}
// Decay individual strains
individualStrains[i] = previousHitObject.individualStrains[i] * individualDecay;
}
heldUntil[BaseHitObject.Column] = endTime;
// Increase individual strain in own column
IndividualStrain += 2.0 * holdFactor;
OverallStrain = previousHitObject.OverallStrain * overallDecay + (1.0 + holdAddition) * holdFactor;
}
}
}

View File

@ -3,7 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
@ -15,10 +15,31 @@ namespace osu.Game.Rulesets.Mania.Replays
{ {
public const double RELEASE_DELAY = 20; public const double RELEASE_DELAY = 20;
public ManiaAutoGenerator(Beatmap<ManiaHitObject> beatmap) public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap;
private readonly ManiaAction[] columnActions;
public ManiaAutoGenerator(ManiaBeatmap beatmap)
: base(beatmap) : base(beatmap)
{ {
Replay = new Replay { User = new User { Username = @"Autoplay" } }; Replay = new Replay { User = new User { Username = @"Autoplay" } };
columnActions = new ManiaAction[Beatmap.TotalColumns];
var normalAction = ManiaAction.Key1;
var specialAction = ManiaAction.Special1;
int totalCounter = 0;
foreach (var stage in Beatmap.Stages)
{
for (int i = 0; i < stage.Columns; i++)
{
if (stage.IsSpecialColumn(i))
columnActions[totalCounter] = specialAction++;
else
columnActions[totalCounter] = normalAction++;
totalCounter++;
}
}
} }
protected Replay Replay; protected Replay Replay;
@ -30,18 +51,18 @@ namespace osu.Game.Rulesets.Mania.Replays
var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time); var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time);
int activeColumns = 0; var actions = new List<ManiaAction>();
foreach (var group in pointGroups) foreach (var group in pointGroups)
{ {
foreach (var point in group) foreach (var point in group)
{ {
if (point is HitPoint) if (point is HitPoint)
activeColumns |= 1 << point.Column; actions.Add(columnActions[point.Column]);
if (point is ReleasePoint) if (point is ReleasePoint)
activeColumns ^= 1 << point.Column; actions.Remove(columnActions[point.Column]);
} }
Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, activeColumns)); Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray()));
} }
return Replay; return Replay;

View File

@ -4,40 +4,19 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Mania.Replays namespace osu.Game.Rulesets.Mania.Replays
{ {
internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler<ManiaReplayFrame>
{ {
private readonly ManiaRulesetContainer container; public ManiaFramedReplayInputHandler(Replay replay)
public ManiaFramedReplayInputHandler(Replay replay, ManiaRulesetContainer container)
: base(replay) : base(replay)
{ {
this.container = container;
} }
private ManiaPlayfield playfield; protected override bool IsImportant(ManiaReplayFrame frame) => frame.Actions.Any();
public override List<InputState> GetPendingStates()
{
var actions = new List<ManiaAction>();
if (playfield == null) public override List<InputState> GetPendingStates() => new List<InputState> { new ReplayState<ManiaAction> { PressedActions = CurrentFrame.Actions } };
playfield = (ManiaPlayfield)container.Playfield;
int activeColumns = (int)(CurrentFrame.MouseX ?? 0);
int counter = 0;
while (activeColumns > 0)
{
if ((activeColumns & 1) > 0)
actions.Add(playfield.Columns.ElementAt(counter).Action);
counter++;
activeColumns >>= 1;
}
return new List<InputState> { new ReplayState<ManiaAction> { PressedActions = actions } };
}
} }
} }

View File

@ -1,17 +1,59 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Replays.Legacy;
using osu.Game.Rulesets.Replays.Types;
namespace osu.Game.Rulesets.Mania.Replays namespace osu.Game.Rulesets.Mania.Replays
{ {
public class ManiaReplayFrame : ReplayFrame public class ManiaReplayFrame : ReplayFrame, IConvertibleReplayFrame
{ {
public override bool IsImportant => MouseX > 0; public List<ManiaAction> Actions = new List<ManiaAction>();
public ManiaReplayFrame(double time, int activeColumns) public ManiaReplayFrame()
: base(time, activeColumns, null, ReplayButtonState.None)
{ {
} }
public ManiaReplayFrame(double time, params ManiaAction[] actions)
: base(time)
{
Actions.AddRange(actions);
}
public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap)
{
// We don't need to fully convert, just create the converter
var converter = new ManiaBeatmapConverter(beatmap.BeatmapInfo.RulesetID == 3, beatmap);
// NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling
// elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage.
var stage = new StageDefinition { Columns = converter.TargetColumns };
var normalAction = ManiaAction.Key1;
var specialAction = ManiaAction.Special1;
int activeColumns = (int)(legacyFrame.MouseX ?? 0);
int counter = 0;
while (activeColumns > 0)
{
var isSpecial = stage.IsSpecialColumn(counter);
if ((activeColumns & 1) > 0)
Actions.Add(isSpecial ? specialAction : normalAction);
if (isSpecial)
specialAction++;
else
normalAction++;
counter++;
activeColumns >>= 1;
}
}
} }
} }

View File

@ -0,0 +1,103 @@
{
"Mappings": [{
"StartTime": 500,
"Objects": [{
"StartTime": 500,
"EndTime": 2500,
"Column": 0
},
{
"StartTime": 1500,
"EndTime": 2500,
"Column": 1
}
]
},
{
"StartTime": 3000,
"Objects": [{
"StartTime": 3000,
"EndTime": 4000,
"Column": 2
}]
},
{
"StartTime": 4500,
"Objects": [{
"StartTime": 4500,
"EndTime": 5500,
"Column": 4
}]
},
{
"StartTime": 6000,
"Objects": [{
"StartTime": 6000,
"EndTime": 6500,
"Column": 2
}]
},
{
"StartTime": 7000,
"Objects": [{
"StartTime": 7000,
"EndTime": 8000,
"Column": 2
}]
},
{
"StartTime": 8500,
"Objects": [{
"StartTime": 8500,
"EndTime": 11000,
"Column": 0
}]
},
{
"StartTime": 11500,
"Objects": [{
"StartTime": 11500,
"EndTime": 12000,
"Column": 1
}]
},
{
"StartTime": 12500,
"Objects": [{
"StartTime": 12500,
"EndTime": 16500,
"Column": 4
}]
},
{
"StartTime": 17000,
"Objects": [{
"StartTime": 17000,
"EndTime": 18000,
"Column": 2
}]
},
{
"StartTime": 18500,
"Objects": [{
"StartTime": 18500,
"EndTime": 19450,
"Column": 0
}]
},
{
"StartTime": 19875,
"Objects": [{
"StartTime": 19875,
"EndTime": 23875,
"Column": 1
},
{
"StartTime": 19875,
"EndTime": 23875,
"Column": 0
}
]
}
]
}

View File

@ -0,0 +1,27 @@
osu file format v14
[Difficulty]
HPDrainRate:6
CircleSize:4
OverallDifficulty:7
ApproachRate:8.3
SliderMultiplier:1.6
SliderTickRate:1
[TimingPoints]
500,500,4,2,1,50,1,0
13426,-100,4,3,1,45,0,0
14884,-100,4,2,1,50,0,0
[HitObjects]
96,192,500,6,0,L|416:192,2,320
256,192,3000,12,0,4000,0:0:0:0:
256,192,4500,12,0,5500,0:0:0:0:
256,192,6000,12,0,6500,0:0:0:0:
256,128,7000,6,0,L|352:128,4,80
32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800
256,192,11500,12,0,12000,0:0:0:0:
512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280
256,256,17000,6,0,L|160:256,4,80
256,192,18500,12,0,19450,0:0:0:0:
216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280

View File

@ -0,0 +1,60 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Mania.Tests
{
public class ManiaBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
private bool isForCurrentRuleset;
[NonParallelizable]
[TestCase("basic", false)]
public void Test(string name, bool isForCurrentRuleset)
{
this.isForCurrentRuleset = isForCurrentRuleset;
base.Test(name);
}
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
{
yield return new ConvertValue
{
StartTime = hitObject.StartTime,
EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime,
Column = ((ManiaHitObject)hitObject).Column
};
}
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new ManiaBeatmapConverter(isForCurrentRuleset, beatmap);
}
public struct ConvertValue : IEquatable<ConvertValue>
{
/// <summary>
/// A sane value to account for osu!stable using ints everwhere.
/// </summary>
private const float conversion_lenience = 2;
public double StartTime;
public double EndTime;
public int Column;
public bool Equals(ConvertValue other)
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
&& Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience)
&& Column == other.Column;
}
}

View File

@ -1,15 +1,17 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Replays;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests namespace osu.Game.Rulesets.Mania.Tests
{ {
[Ignore("getting CI working")] [TestFixture]
public class TestCaseAutoGeneration : OsuTestCase public class TestCaseAutoGeneration : OsuTestCase
{ {
[Test] [Test]
@ -19,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Tests
// | - | // | - |
// | | // | |
var beatmap = new Beatmap<ManiaHitObject>(); var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 });
beatmap.HitObjects.Add(new Note { StartTime = 1000 }); beatmap.HitObjects.Add(new Note { StartTime = 1000 });
var generated = new ManiaAutoGenerator(beatmap).Generate(); var generated = new ManiaAutoGenerator(beatmap).Generate();
@ -27,8 +29,8 @@ namespace osu.Game.Rulesets.Mania.Tests
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 0 has not been pressed"); Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed");
Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 0 has not been released"); Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released");
} }
[Test] [Test]
@ -40,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Tests
// | * | // | * |
// | | // | |
var beatmap = new Beatmap<ManiaHitObject>(); var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 });
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
var generated = new ManiaAutoGenerator(beatmap).Generate(); var generated = new ManiaAutoGenerator(beatmap).Generate();
@ -48,8 +50,8 @@ namespace osu.Game.Rulesets.Mania.Tests
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 0 has not been pressed"); Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed");
Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 0 has not been released"); Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released");
} }
[Test] [Test]
@ -59,7 +61,7 @@ namespace osu.Game.Rulesets.Mania.Tests
// | - | - | // | - | - |
// | | | // | | |
var beatmap = new Beatmap<ManiaHitObject>(); var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
beatmap.HitObjects.Add(new Note { StartTime = 1000 }); beatmap.HitObjects.Add(new Note { StartTime = 1000 });
beatmap.HitObjects.Add(new Note { StartTime = 1000, Column = 1 }); beatmap.HitObjects.Add(new Note { StartTime = 1000, Column = 1 });
@ -68,8 +70,8 @@ namespace osu.Game.Rulesets.Mania.Tests
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
Assert.AreEqual(3, generated.Frames[1].MouseX, "Keys 1 and 2 have not been pressed"); Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
Assert.AreEqual(0, generated.Frames[2].MouseX, "Keys 1 and 2 have not been released"); Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
} }
[Test] [Test]
@ -81,7 +83,7 @@ namespace osu.Game.Rulesets.Mania.Tests
// | * | * | // | * | * |
// | | | // | | |
var beatmap = new Beatmap<ManiaHitObject>(); var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000, Column = 1 }); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000, Column = 1 });
@ -90,8 +92,8 @@ namespace osu.Game.Rulesets.Mania.Tests
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
Assert.AreEqual(3, generated.Frames[1].MouseX, "Keys 1 and 2 have not been pressed"); Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
Assert.AreEqual(0, generated.Frames[2].MouseX, "Keys 1 and 2 have not been released"); Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
} }
[Test] [Test]
@ -102,7 +104,7 @@ namespace osu.Game.Rulesets.Mania.Tests
// | - | | // | - | |
// | | | // | | |
var beatmap = new Beatmap<ManiaHitObject>(); var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
beatmap.HitObjects.Add(new Note { StartTime = 1000 }); beatmap.HitObjects.Add(new Note { StartTime = 1000 });
beatmap.HitObjects.Add(new Note { StartTime = 2000, Column = 1 }); beatmap.HitObjects.Add(new Note { StartTime = 2000, Column = 1 });
@ -113,10 +115,10 @@ namespace osu.Game.Rulesets.Mania.Tests
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect first note release time"); Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect first note release time");
Assert.AreEqual(2000, generated.Frames[3].Time, "Incorrect second note hit time"); Assert.AreEqual(2000, generated.Frames[3].Time, "Incorrect second note hit time");
Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time"); Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed"); Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 1 has not been released"); Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released");
Assert.AreEqual(2, generated.Frames[3].MouseX, "Key 2 has not been pressed"); Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been pressed");
Assert.AreEqual(0, generated.Frames[4].MouseX, "Key 2 has not been released"); Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released");
} }
[Test] [Test]
@ -129,7 +131,7 @@ namespace osu.Game.Rulesets.Mania.Tests
// | * | | // | * | |
// | | | // | | |
var beatmap = new Beatmap<ManiaHitObject>(); var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
beatmap.HitObjects.Add(new HoldNote { StartTime = 2000, Duration = 2000, Column = 1 }); beatmap.HitObjects.Add(new HoldNote { StartTime = 2000, Duration = 2000, Column = 1 });
@ -140,10 +142,11 @@ namespace osu.Game.Rulesets.Mania.Tests
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect first note release time"); Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect first note release time");
Assert.AreEqual(2000, generated.Frames[2].Time, "Incorrect second note hit time"); Assert.AreEqual(2000, generated.Frames[2].Time, "Incorrect second note hit time");
Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time"); Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed"); Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
Assert.AreEqual(3, generated.Frames[2].MouseX, "Keys 1 and 2 have not been pressed"); Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
Assert.AreEqual(2, generated.Frames[3].MouseX, "Key 1 has not been released"); Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key1), "Key1 has not been released");
Assert.AreEqual(0, generated.Frames[4].MouseX, "Key 2 has not been released"); Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has been released");
Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released");
} }
[Test] [Test]
@ -155,7 +158,7 @@ namespace osu.Game.Rulesets.Mania.Tests
// | * | | // | * | |
// | | | // | | |
var beatmap = new Beatmap<ManiaHitObject>(); var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY }); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY });
beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 }); beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 });
@ -165,9 +168,12 @@ namespace osu.Game.Rulesets.Mania.Tests
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
Assert.AreEqual(3000, generated.Frames[2].Time, "Incorrect second note press time + first note release time"); Assert.AreEqual(3000, generated.Frames[2].Time, "Incorrect second note press time + first note release time");
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect second note release time"); Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect second note release time");
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed"); Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
Assert.AreEqual(2, generated.Frames[2].MouseX, "Key 1 has not been released or key 2 has not been pressed"); Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released");
Assert.AreEqual(0, generated.Frames[3].MouseX, "Keys 1 and 2 have not been released"); Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key2), "Key2 has not been pressed");
Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been released");
} }
private bool checkContains(ReplayFrame frame, params ManiaAction[] actions) => actions.All(action => ((ManiaReplayFrame)frame).Actions.Contains(action));
} }
} }

View File

@ -13,7 +13,6 @@ using OpenTK.Graphics;
namespace osu.Game.Rulesets.Mania.Tests namespace osu.Game.Rulesets.Mania.Tests
{ {
[TestFixture] [TestFixture]
[Ignore("getting CI working")]
public class TestCaseManiaHitObjects : OsuTestCase public class TestCaseManiaHitObjects : OsuTestCase
{ {
public TestCaseManiaHitObjects() public TestCaseManiaHitObjects()

View File

@ -8,7 +8,9 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Configuration;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
@ -19,7 +21,6 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests namespace osu.Game.Rulesets.Mania.Tests
{ {
[TestFixture] [TestFixture]
[Ignore("getting CI working")]
public class TestCaseManiaPlayfield : OsuTestCase public class TestCaseManiaPlayfield : OsuTestCase
{ {
private const double start_time = 500; private const double start_time = 500;
@ -92,10 +93,17 @@ namespace osu.Game.Rulesets.Mania.Tests
}); });
} }
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(RulesetStore rulesets) private void load(RulesetStore rulesets, SettingsStore settings)
{ {
maniaRuleset = rulesets.GetRuleset(3); maniaRuleset = rulesets.GetRuleset(3);
dependencies.Cache(new ManiaConfigManager(settings, maniaRuleset, 4));
} }
private ManiaPlayfield createPlayfield(int cols, bool inverted = false) private ManiaPlayfield createPlayfield(int cols, bool inverted = false)

View File

@ -5,7 +5,7 @@ using NUnit.Framework;
namespace osu.Game.Rulesets.Mania.Tests namespace osu.Game.Rulesets.Mania.Tests
{ {
[Ignore("getting CI working")] [TestFixture]
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
{ {
public TestCasePerformancePoints() public TestCasePerformancePoints()

View File

@ -1,16 +1,24 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
{ {
internal class DrawableManiaJudgement : DrawableJudgement internal class DrawableManiaJudgement : DrawableJudgement
{ {
public DrawableManiaJudgement(Judgement judgement) public DrawableManiaJudgement(Judgement judgement, DrawableHitObject judgedObject)
: base(judgement) : base(judgement, judgedObject)
{ {
}
[BackgroundDependencyLoader]
private void load()
{
if (JudgementText != null)
JudgementText.TextSize = 25; JudgementText.TextSize = 25;
} }

View File

@ -11,6 +11,7 @@ using osu.Framework.MathUtils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Input.Handlers;
using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mania.Mods;
@ -101,9 +102,9 @@ namespace osu.Game.Rulesets.Mania.UI
return null; return null;
} }
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f); protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f);
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this); protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
protected override IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => new ManiaConfigManager(settings, Ruleset.RulesetInfo, Variant); protected override IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => new ManiaConfigManager(settings, Ruleset.RulesetInfo, Variant);
} }

View File

@ -15,6 +15,7 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -40,7 +41,7 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly Container<Drawable> content; private readonly Container<Drawable> content;
public Container<DrawableManiaJudgement> Judgements => judgements; public Container<DrawableManiaJudgement> Judgements => judgements;
private readonly Container<DrawableManiaJudgement> judgements; private readonly JudgementContainer<DrawableManiaJudgement> judgements;
private readonly Container topLevelContainer; private readonly Container topLevelContainer;
@ -48,13 +49,11 @@ namespace osu.Game.Rulesets.Mania.UI
private Color4 specialColumnColour; private Color4 specialColumnColour;
private readonly int firstColumnIndex; private readonly int firstColumnIndex;
private readonly StageDefinition definition;
public ManiaStage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction) public ManiaStage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
: base(ScrollingDirection.Up) : base(ScrollingDirection.Up)
{ {
this.firstColumnIndex = firstColumnIndex; this.firstColumnIndex = firstColumnIndex;
this.definition = definition;
Name = "Stage"; Name = "Stage";
@ -116,7 +115,7 @@ namespace osu.Game.Rulesets.Mania.UI
Padding = new MarginPadding { Top = HIT_TARGET_POSITION } Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
} }
}, },
judgements = new Container<DrawableManiaJudgement> judgements = new JudgementContainer<DrawableManiaJudgement>
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
@ -131,7 +130,7 @@ namespace osu.Game.Rulesets.Mania.UI
for (int i = 0; i < definition.Columns; i++) for (int i = 0; i < definition.Columns; i++)
{ {
var isSpecial = isSpecialColumn(i); var isSpecial = definition.IsSpecialColumn(i);
var column = new Column var column = new Column
{ {
IsSpecial = isSpecial, IsSpecial = isSpecial,
@ -160,13 +159,6 @@ namespace osu.Game.Rulesets.Mania.UI
AddNested(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) public override void Add(DrawableHitObject h)
{ {
var maniaObject = (ManiaHitObject)h.HitObject; var maniaObject = (ManiaHitObject)h.HitObject;
@ -180,7 +172,7 @@ namespace osu.Game.Rulesets.Mania.UI
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{ {
judgements.Clear(); judgements.Clear();
judgements.Add(new DrawableManiaJudgement(judgement) judgements.Add(new DrawableManiaJudgement(judgement, judgedObject)
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,

View File

@ -13,24 +13,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
public override void PostProcess(Beatmap<OsuHitObject> beatmap) public override void PostProcess(Beatmap<OsuHitObject> beatmap)
{ {
applyStacking(beatmap); applyStacking(beatmap);
base.PostProcess(beatmap);
if (beatmap.ComboColors.Count == 0)
return;
int comboIndex = 0;
int colourIndex = 0;
foreach (var obj in beatmap.HitObjects)
{
if (obj.NewCombo)
{
comboIndex = 0;
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
}
obj.IndexInCurrentCombo = comboIndex++;
obj.ComboColour = beatmap.ComboColors[colourIndex];
}
} }
private void applyStacking(Beatmap<OsuHitObject> beatmap) private void applyStacking(Beatmap<OsuHitObject> beatmap)

View File

@ -0,0 +1,35 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
{
public class HitCircleMask : HitObjectMask
{
public HitCircleMask(DrawableHitCircle hitCircle)
: base(hitCircle)
{
Origin = Anchor.Centre;
Position = hitCircle.Position;
Size = hitCircle.Size;
Scale = hitCircle.Scale;
AddInternal(new RingPiece());
hitCircle.HitObject.PositionChanged += _ => Position = hitCircle.Position;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Colour = colours.Yellow;
}
}
}

View File

@ -0,0 +1,56 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
{
public class SliderCircleMask : HitObjectMask
{
public SliderCircleMask(DrawableHitCircle sliderHead, DrawableSlider slider)
: this(sliderHead, Vector2.Zero, slider)
{
}
public SliderCircleMask(DrawableSliderTail sliderTail, DrawableSlider slider)
: this(sliderTail, ((Slider)slider.HitObject).Curve.PositionAt(1), slider)
{
}
private readonly DrawableOsuHitObject hitObject;
private SliderCircleMask(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider)
: base(hitObject)
{
this.hitObject = hitObject;
Origin = Anchor.Centre;
Position = position;
Size = slider.HeadCircle.Size;
Scale = slider.HeadCircle.Scale;
AddInternal(new RingPiece());
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Colour = colours.Yellow;
}
protected override void Update()
{
base.Update();
RelativeAnchorPosition = hitObject.RelativeAnchorPosition;
}
}
}

View File

@ -0,0 +1,63 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
{
public class SliderMask : HitObjectMask
{
private readonly SliderBody body;
private readonly DrawableSlider slider;
public SliderMask(DrawableSlider slider)
: base(slider)
{
this.slider = slider;
Position = slider.Position;
var sliderObject = (Slider)slider.HitObject;
InternalChildren = new Drawable[]
{
body = new SliderBody(sliderObject)
{
AccentColour = Color4.Transparent,
PathWidth = sliderObject.Scale * 64
},
new SliderCircleMask(slider.HeadCircle, slider),
new SliderCircleMask(slider.TailCircle, slider),
};
sliderObject.PositionChanged += _ => Position = slider.Position;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
body.BorderColour = colours.Yellow;
}
protected override void Update()
{
base.Update();
Size = slider.Size;
OriginPosition = slider.OriginPosition;
// Need to cause one update
body.UpdateProgress(0);
}
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => body.ReceiveMouseInputAt(screenSpacePos);
}
}

View File

@ -5,6 +5,7 @@ using osu.Framework.Graphics.Cursor;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Edit namespace osu.Game.Rulesets.Osu.Edit
{ {
@ -17,6 +18,8 @@ namespace osu.Game.Rulesets.Osu.Edit
protected override Playfield CreatePlayfield() => new OsuEditPlayfield(); protected override Playfield CreatePlayfield() => new OsuEditPlayfield();
protected override Vector2 PlayfieldArea => Vector2.One;
protected override CursorContainer CreateCursor() => null; protected override CursorContainer CreateCursor() => null;
} }
} }

View File

@ -2,10 +2,15 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Osu.Edit namespace osu.Game.Rulesets.Osu.Edit
@ -25,5 +30,20 @@ namespace osu.Game.Rulesets.Osu.Edit
new HitObjectCompositionTool<Slider>(), new HitObjectCompositionTool<Slider>(),
new HitObjectCompositionTool<Spinner>() new HitObjectCompositionTool<Spinner>()
}; };
protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both };
public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableHitCircle circle:
return new HitCircleMask(circle);
case DrawableSlider slider:
return new SliderMask(slider);
}
return base.CreateMaskFor(hitObject);
}
} }
} }

View File

@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
public class OsuModDaycore : ModDaycore public class OsuModDaycore : ModDaycore
{ {
public override double ScoreMultiplier => 0.5; public override double ScoreMultiplier => 0.3;
} }
} }

View File

@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
public class OsuModEasy : ModEasy public class OsuModEasy : ModEasy
{ {
public override string Description => @"Larger circles, more forgiving HP drain, less accuracy required, and three lives!";
} }
} }

View File

@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
public class OsuModHalfTime : ModHalfTime public class OsuModHalfTime : ModHalfTime
{ {
public override double ScoreMultiplier => 0.5; public override double ScoreMultiplier => 0.3;
} }
} }

View File

@ -2,6 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
@ -12,7 +14,6 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModHardRock : ModHardRock, IApplicableToHitObject<OsuHitObject> public class OsuModHardRock : ModHardRock, IApplicableToHitObject<OsuHitObject>
{ {
public override double ScoreMultiplier => 1.06; public override double ScoreMultiplier => 1.06;
public override bool Ranked => true;
public void ApplyToHitObject(OsuHitObject hitObject) public void ApplyToHitObject(OsuHitObject hitObject)
{ {
@ -22,8 +23,14 @@ namespace osu.Game.Rulesets.Osu.Mods
if (slider == null) if (slider == null)
return; return;
slider.HeadCircle.Position = new Vector2(slider.HeadCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.HeadCircle.Position.Y);
slider.TailCircle.Position = new Vector2(slider.TailCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.TailCircle.Position.Y);
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
slider.NestedHitObjects.OfType<RepeatPoint>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
var newControlPoints = new List<Vector2>(); var newControlPoints = new List<Vector2>();
slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y))); slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, -c.Y)));
slider.ControlPoints = newControlPoints; slider.ControlPoints = newControlPoints;
slider.Curve?.Calculate(); // Recalculate the slider curve slider.Curve?.Calculate(); // Recalculate the slider curve

View File

@ -1,19 +1,21 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Mods namespace osu.Game.Rulesets.Osu.Mods
{ {
public class OsuModHidden : ModHidden, IApplicableToDrawableHitObjects public class OsuModHidden : ModHidden, IApplicableToDrawableHitObjects
{ {
public override string Description => @"Play with no approach circles and fading notes for a slight score advantage."; public override string Description => @"Play with no approach circles and fading circles/sliders.";
public override double ScoreMultiplier => 1.06; public override double ScoreMultiplier => 1.06;
private const double fade_in_duration_multiplier = 0.4; private const double fade_in_duration_multiplier = 0.4;
@ -24,7 +26,10 @@ namespace osu.Game.Rulesets.Osu.Mods
foreach (var d in drawables.OfType<DrawableOsuHitObject>()) foreach (var d in drawables.OfType<DrawableOsuHitObject>())
{ {
d.ApplyCustomUpdateState += ApplyHiddenState; d.ApplyCustomUpdateState += ApplyHiddenState;
d.HitObject.TimeFadein = d.HitObject.TimePreempt * fade_in_duration_multiplier; d.HitObject.TimeFadein = d.HitObject.TimePreempt * fade_in_duration_multiplier;
foreach (var h in d.HitObject.NestedHitObjects.OfType<OsuHitObject>())
h.TimeFadein = h.TimePreempt * fade_in_duration_multiplier;
} }
} }
@ -33,30 +38,37 @@ namespace osu.Game.Rulesets.Osu.Mods
if (!(drawable is DrawableOsuHitObject d)) if (!(drawable is DrawableOsuHitObject d))
return; return;
var fadeOutStartTime = d.HitObject.StartTime - d.HitObject.TimePreempt + d.HitObject.TimeFadein; var h = d.HitObject;
var fadeOutDuration = d.HitObject.TimePreempt * fade_out_duration_multiplier;
var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadein;
var fadeOutDuration = h.TimePreempt * fade_out_duration_multiplier;
// new duration from completed fade in to end (before fading out) // new duration from completed fade in to end (before fading out)
var longFadeDuration = ((d.HitObject as IHasEndTime)?.EndTime ?? d.HitObject.StartTime) - fadeOutStartTime; var longFadeDuration = ((h as IHasEndTime)?.EndTime ?? h.StartTime) - fadeOutStartTime;
switch (drawable) switch (drawable)
{ {
case DrawableHitCircle circle: case DrawableHitCircle circle:
// we don't want to see the approach circle // we don't want to see the approach circle
using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
circle.ApproachCircle.Hide(); circle.ApproachCircle.Hide();
// fade out immediately after fade in. // fade out immediately after fade in.
using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true)) using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true))
{
circle.FadeOut(fadeOutDuration); circle.FadeOut(fadeOutDuration);
}
break; break;
case DrawableSlider slider: case DrawableSlider slider:
using (slider.BeginAbsoluteSequence(fadeOutStartTime, true)) using (slider.BeginAbsoluteSequence(fadeOutStartTime, true))
{
slider.Body.FadeOut(longFadeDuration, Easing.Out); 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; break;
case DrawableSpinner spinner: case DrawableSpinner spinner:
@ -66,9 +78,7 @@ namespace osu.Game.Rulesets.Osu.Mods
spinner.Background.Hide(); spinner.Background.Hide();
using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true)) using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true))
{
spinner.FadeOut(fadeOutDuration); spinner.FadeOut(fadeOutDuration);
}
break; break;
} }

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
public class OsuModRelax : ModRelax public class OsuModRelax : ModRelax
{ {
public override string Description => "You don't need to click.\nGive your clicking/tapping finger a break from the heat of things."; public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray(); public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
} }
} }

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Name => "Spun Out"; public override string Name => "Spun Out";
public override string ShortenedName => "SO"; public override string ShortenedName => "SO";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout; public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout;
public override string Description => @"Spinners will be automatically completed"; public override string Description => @"Spinners will be automatically completed.";
public override double ScoreMultiplier => 0.9; public override double ScoreMultiplier => 0.9;
public override bool Ranked => true; public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot) }; public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot) };

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Name => "Target"; public override string Name => "Target";
public override string ShortenedName => "TP"; public override string ShortenedName => "TP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_target; public override FontAwesome Icon => FontAwesome.fa_osu_mod_target;
public override string Description => @""; public override string Description => @"Practice keeping up with the beat of the song.";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;
} }
} }

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