This commit is contained in:
DrabWeb
2017-05-21 22:37:00 -03:00
296 changed files with 5830 additions and 3063 deletions

40
.vscode/launch.json vendored
View File

@ -2,46 +2,60 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "Launch VisualTests", "name": "VisualTests (debug)",
"windows": { "windows": {
"type": "clr" "type": "clr"
}, },
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/osu.Desktop.VisualTests/bin/Debug/osu!.exe", "program": "${workspaceRoot}/osu.Desktop.VisualTests/bin/Debug/osu!.exe",
"args": [],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "build", "preLaunchTask": "Build (Debug)",
"runtimeExecutable": null, "runtimeExecutable": null,
"env": {}, "env": {},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
"name": "Launch Desktop", "name": "VisualTests (release)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/osu.Desktop.VisualTests/bin/Release/osu!.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "osu! (debug)",
"windows": { "windows": {
"type": "clr" "type": "clr"
}, },
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/osu.Desktop/bin/Debug/osu!.exe", "program": "${workspaceRoot}/osu.Desktop/bin/Debug/osu!.exe",
"args": [],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "build", "preLaunchTask": "Build (Debug)",
"runtimeExecutable": null, "runtimeExecutable": null,
"env": {}, "env": {},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
"name": "Attach", "name": "osu! (release)",
"windows": { "windows": {
"type": "clr", "type": "clr"
"request": "attach",
"processName": "osu!"
}, },
"type": "mono", "type": "mono",
"request": "attach", "request": "launch",
"address": "localhost", "program": "${workspaceRoot}/osu.Desktop/bin/Release/osu!.exe",
"port": 55555 "cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
} }
] ]
} }

77
.vscode/tasks.json vendored
View File

@ -1,51 +1,50 @@
{ {
// See https://go.microsoft.com/fwlink/?LinkId=733558 // See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format // for the documentation about the tasks.json format
"version": "0.1.0", "version": "2.0.0",
"taskSelector": "/t:", "problemMatcher": "$msCompile",
"isShellCommand": true,
"command": "msbuild",
"suppressTaskName": true,
"showOutput": "silent",
"args": [
"/property:GenerateFullPaths=true",
"/property:DebugType=portable"
],
"windows": {
"args": [
"/property:GenerateFullPaths=true",
"/property:DebugType=portable",
"/m" //parallel compiling support. doesn't work well with mono atm
]
},
"tasks": [ "tasks": [
{ {
"taskName": "build", "taskName": "Build (Debug)",
"isShellCommand": true,
"showOutput": "silent",
"command": "msbuild",
"args": [
"/property:GenerateFullPaths=true",
"/property:DebugType=portable"
],
"windows": {
"args": [
"/property:GenerateFullPaths=true",
"/property:DebugType=portable",
"/m" //parallel compiling support. doesn't work well with mono atm
]
},
// Use the standard MS compiler pattern to detect errors, warnings and infos
"problemMatcher": "$msCompile",
"isBuildCommand": true "isBuildCommand": true
}, },
{ {
"taskName": "rebuild", "taskName": "Build (Release)",
"isShellCommand": true,
"showOutput": "silent",
"command": "msbuild",
"args": [ "args": [
// Ask msbuild to generate full paths for file names. "/property:Configuration=Release"
"/property:GenerateFullPaths=true", ]
"/property:DebugType=portable", },
"/target:Clean,Build" {
], "taskName": "Clean All",
"windows": { "dependsOn": ["Clean (Debug)", "Clean (Release)"]
"args": [ },
"/property:GenerateFullPaths=true", {
"/property:DebugType=portable", "taskName": "Clean (Debug)",
"/target:Clean,Build", "args": [
"/m" //parallel compiling support. doesn't work well with mono atm "/target:Clean"
] ]
}, },
// Use the standard MS compiler pattern to detect errors, warnings and infos {
"problemMatcher": "$msCompile", "taskName": "Clean (Release)",
"isBuildCommand": true "args": [
"/target:Clean",
"/property:Configuration=Release"
]
} }
] ]
} }

View File

@ -10,7 +10,7 @@ namespace osu.Desktop.VisualTests.Beatmaps
public class TestWorkingBeatmap : WorkingBeatmap public class TestWorkingBeatmap : WorkingBeatmap
{ {
public TestWorkingBeatmap(Beatmap beatmap) public TestWorkingBeatmap(Beatmap beatmap)
: base(beatmap.BeatmapInfo, beatmap.BeatmapInfo.BeatmapSet) : base(beatmap.BeatmapInfo)
{ {
this.beatmap = beatmap; this.beatmap = beatmap;
} }

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 osu.Framework.Graphics.Primitives;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;

View File

@ -5,7 +5,6 @@ using System.Collections.Generic;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Screens.Tournament; using osu.Game.Screens.Tournament;
using osu.Game.Screens.Tournament.Teams; using osu.Game.Screens.Tournament.Teams;
using osu.Game.Users;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
@ -25,57 +24,57 @@ namespace osu.Desktop.VisualTests.Tests
private class TestTeamList : ITeamList private class TestTeamList : ITeamList
{ {
public IEnumerable<Country> Teams { get; } = new[] public IEnumerable<DrawingsTeam> Teams { get; } = new[]
{ {
new Country new DrawingsTeam
{ {
FlagName = "GB", FlagName = "GB",
FullName = "United Kingdom", FullName = "United Kingdom",
Acronym = "UK" Acronym = "UK"
}, },
new Country new DrawingsTeam
{ {
FlagName = "FR", FlagName = "FR",
FullName = "France", FullName = "France",
Acronym = "FRA" Acronym = "FRA"
}, },
new Country new DrawingsTeam
{ {
FlagName = "CN", FlagName = "CN",
FullName = "China", FullName = "China",
Acronym = "CHN" Acronym = "CHN"
}, },
new Country new DrawingsTeam
{ {
FlagName = "AU", FlagName = "AU",
FullName = "Australia", FullName = "Australia",
Acronym = "AUS" Acronym = "AUS"
}, },
new Country new DrawingsTeam
{ {
FlagName = "JP", FlagName = "JP",
FullName = "Japan", FullName = "Japan",
Acronym = "JPN" Acronym = "JPN"
}, },
new Country new DrawingsTeam
{ {
FlagName = "RO", FlagName = "RO",
FullName = "Romania", FullName = "Romania",
Acronym = "ROM" Acronym = "ROM"
}, },
new Country new DrawingsTeam
{ {
FlagName = "IT", FlagName = "IT",
FullName = "Italy", FullName = "Italy",
Acronym = "PIZZA" Acronym = "PIZZA"
}, },
new Country new DrawingsTeam
{ {
FlagName = "VE", FlagName = "VE",
FullName = "Venezuela", FullName = "Venezuela",
Acronym = "VNZ" Acronym = "VNZ"
}, },
new Country new DrawingsTeam
{ {
FlagName = "US", FlagName = "US",
FullName = "United States of America", FullName = "United States of America",

View File

@ -18,6 +18,7 @@ using osu.Game.Rulesets.Taiko.UI;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Desktop.VisualTests.Beatmaps; using osu.Desktop.VisualTests.Beatmaps;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Beatmaps.Timing;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
@ -52,6 +53,12 @@ namespace osu.Desktop.VisualTests.Tests
time += RNG.Next(50, 500); time += RNG.Next(50, 500);
} }
TimingInfo timing = new TimingInfo();
timing.ControlPoints.Add(new ControlPoint
{
BeatLength = 200
});
WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap
{ {
HitObjects = objects, HitObjects = objects,
@ -64,8 +71,9 @@ namespace osu.Desktop.VisualTests.Tests
Artist = @"Unknown", Artist = @"Unknown",
Title = @"Sample Beatmap", Title = @"Sample Beatmap",
Author = @"peppy", Author = @"peppy",
} },
} },
TimingInfo = timing
}); });
Add(new Drawable[] Add(new Drawable[]
@ -77,25 +85,25 @@ namespace osu.Desktop.VisualTests.Tests
Clock = new FramedClock(), Clock = new FramedClock(),
Children = new Drawable[] Children = new Drawable[]
{ {
new OsuHitRenderer(beatmap) new OsuHitRenderer(beatmap, false)
{ {
Scale = new Vector2(0.5f), Scale = new Vector2(0.5f),
Anchor = Anchor.TopLeft, Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft Origin = Anchor.TopLeft
}, },
new TaikoHitRenderer(beatmap) new TaikoHitRenderer(beatmap, false)
{ {
Scale = new Vector2(0.5f), Scale = new Vector2(0.5f),
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopRight Origin = Anchor.TopRight
}, },
new CatchHitRenderer(beatmap) new CatchHitRenderer(beatmap, false)
{ {
Scale = new Vector2(0.5f), Scale = new Vector2(0.5f),
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft Origin = Anchor.BottomLeft
}, },
new ManiaHitRenderer(beatmap) new ManiaHitRenderer(beatmap, false)
{ {
Scale = new Vector2(0.5f), Scale = new Vector2(0.5f),
Anchor = Anchor.BottomRight, Anchor = Anchor.BottomRight,

View File

@ -129,8 +129,6 @@ namespace osu.Desktop.VisualTests.Tests
}; };
Add(clockAdjustContainer); Add(clockAdjustContainer);
load(mode);
} }
private int depth; private int depth;

View File

@ -0,0 +1,81 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using OpenTK.Graphics;
using OpenTK;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseManiaHitObjects : TestCase
{
public override void Reset()
{
base.Reset();
Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
// Imagine that the containers containing the drawable notes are the "columns"
Children = new Drawable[]
{
new Container
{
Name = "Normal note column",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Width = 50,
Children = new[]
{
new Container
{
Name = "Timing section",
RelativeSizeAxes = Axes.Both,
RelativeCoordinateSpace = new Vector2(1, 10000),
Children = new[]
{
new DrawableNote(new Note { StartTime = 5000 }) { AccentColour = Color4.Red },
new DrawableNote(new Note { StartTime = 6000 }) { AccentColour = Color4.Red }
}
}
}
},
new Container
{
Name = "Hold note column",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Width = 50,
Children = new[]
{
new Container
{
Name = "Timing section",
RelativeSizeAxes = Axes.Both,
RelativeCoordinateSpace = new Vector2(1, 10000),
Children = new[]
{
new DrawableHoldNote(new HoldNote
{
StartTime = 5000,
Duration = 1000
}) { AccentColour = Color4.Red }
}
}
}
}
}
});
}
}
}

View File

@ -0,0 +1,98 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Input;
using osu.Framework.Testing;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.UI;
using System;
using System.Collections.Generic;
using osu.Game.Beatmaps.Timing;
using OpenTK;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseManiaPlayfield : TestCase
{
public override string Description => @"Mania playfield";
protected override double TimePerAction => 200;
public override void Reset()
{
base.Reset();
Action<int, SpecialColumnPosition> createPlayfield = (cols, pos) =>
{
Clear();
Add(new ManiaPlayfield(cols, new List<ControlPoint>())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
SpecialColumnPosition = pos,
Scale = new Vector2(1, -1)
});
};
Action<int, SpecialColumnPosition> createPlayfieldWithNotes = (cols, pos) =>
{
Clear();
ManiaPlayfield playField;
Add(playField = new ManiaPlayfield(cols, new List<ControlPoint> { new ControlPoint { BeatLength = 200 } })
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
SpecialColumnPosition = pos,
Scale = new Vector2(1, -1)
});
for (int i = 0; i < cols; i++)
{
playField.Add(new DrawableNote(new Note
{
StartTime = Time.Current + 1000,
Column = i
}));
}
};
AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal));
AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal));
AddStep("Left special style", () => createPlayfield(4, SpecialColumnPosition.Left));
AddStep("Right special style", () => createPlayfield(4, SpecialColumnPosition.Right));
AddStep("5 columns", () => createPlayfield(5, SpecialColumnPosition.Normal));
AddStep("8 columns", () => createPlayfield(8, SpecialColumnPosition.Normal));
AddStep("Left special style", () => createPlayfield(8, SpecialColumnPosition.Left));
AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right));
AddStep("Normal special style", () => createPlayfield(4, SpecialColumnPosition.Normal));
AddStep("Notes", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Normal));
AddWaitStep(10);
AddStep("Left special style", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Left));
AddWaitStep(10);
AddStep("Right special style", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Right));
AddWaitStep(10);
}
private void triggerKeyDown(Column column)
{
column.TriggerKeyDown(new InputState(), new KeyDownEventArgs
{
Key = column.Key,
Repeat = false
});
}
private void triggerKeyUp(Column column)
{
column.TriggerKeyUp(new InputState(), new KeyUpEventArgs
{
Key = column.Key
});
}
}
}

View File

@ -12,7 +12,7 @@ namespace osu.Desktop.VisualTests.Tests
{ {
public override string Description => @"Tests pause and fail overlays"; public override string Description => @"Tests pause and fail overlays";
private PauseOverlay pauseOverlay; private PauseContainer.PauseOverlay pauseOverlay;
private FailOverlay failOverlay; private FailOverlay failOverlay;
private int retryCount; private int retryCount;
@ -22,7 +22,7 @@ namespace osu.Desktop.VisualTests.Tests
retryCount = 0; retryCount = 0;
Add(pauseOverlay = new PauseOverlay Add(pauseOverlay = new PauseContainer.PauseOverlay
{ {
OnResume = () => Logger.Log(@"Resume"), OnResume = () => Logger.Log(@"Resume"),
OnRetry = () => Logger.Log(@"Retry"), OnRetry = () => Logger.Log(@"Retry"),

View File

@ -6,16 +6,21 @@ using osu.Framework.Graphics;
using osu.Game.Overlays.Mods; using osu.Game.Overlays.Mods;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Screens.Play.HUD;
using OpenTK;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
internal class TestCaseModSelectOverlay : TestCase internal class TestCaseMods : TestCase
{ {
public override string Description => @"Tests the mod select overlay"; public override string Description => @"Mod select overlay and in-game display";
private ModSelectOverlay modSelect; private ModSelectOverlay modSelect;
private ModDisplay modDisplay;
private RulesetDatabase rulesets; private RulesetDatabase rulesets;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(RulesetDatabase rulesets) private void load(RulesetDatabase rulesets)
{ {
@ -33,6 +38,16 @@ namespace osu.Desktop.VisualTests.Tests
Anchor = Anchor.BottomCentre, Anchor = Anchor.BottomCentre,
}); });
Add(modDisplay = new ModDisplay
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Position = new Vector2(0, 25),
});
modDisplay.Current.BindTo(modSelect.SelectedMods);
AddStep("Toggle", modSelect.ToggleVisibility); AddStep("Toggle", modSelect.ToggleVisibility);
foreach (var ruleset in rulesets.AllRulesets) foreach (var ruleset in rulesets.AllRulesets)

View File

@ -0,0 +1,47 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Testing;
using osu.Game.Overlays;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseOnScreenDisplay : TestCase
{
private FrameworkConfigManager config;
private Bindable<FrameSync> frameSyncMode;
public override string Description => @"Make it easier to see setting changes";
public override void Reset()
{
base.Reset();
Add(new OnScreenDisplay());
frameSyncMode = config.GetBindable<FrameSync>(FrameworkSetting.FrameSync);
FrameSync initial = frameSyncMode.Value;
AddRepeatStep(@"Change frame limiter", setNextMode, 3);
AddStep(@"Restore frame limiter", () => frameSyncMode.Value = initial);
}
private void setNextMode()
{
var nextMode = frameSyncMode.Value + 1;
if (nextMode > FrameSync.Unlimited)
nextMode = FrameSync.VSync;
frameSyncMode.Value = nextMode;
}
[BackgroundDependencyLoader]
private void load(FrameworkConfigManager config)
{
this.config = config;
}
}
}

View File

@ -47,7 +47,7 @@ namespace osu.Desktop.VisualTests.Tests
Accuracy = 0.98, Accuracy = 0.98,
MaxCombo = 123, MaxCombo = 123,
Rank = ScoreRank.A, Rank = ScoreRank.A,
Date = DateTime.Now, Date = DateTimeOffset.Now,
Statistics = new Dictionary<string, dynamic>() Statistics = new Dictionary<string, dynamic>()
{ {
{ "300", 50 }, { "300", 50 },

View File

@ -3,12 +3,11 @@
using OpenTK; using OpenTK;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.UI; using osu.Game.Screens.Play.HUD;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {

View File

@ -6,18 +6,18 @@ using osu.Game.Overlays;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
internal class TestCaseOptions : TestCase internal class TestCaseSettings : TestCase
{ {
public override string Description => @"Tests the options overlay"; public override string Description => @"Tests the settings overlay";
private OptionsOverlay options; private SettingsOverlay settings;
public override void Reset() public override void Reset()
{ {
base.Reset(); base.Reset();
Children = new[] { options = new OptionsOverlay() }; Children = new[] { settings = new SettingsOverlay() };
options.ToggleVisibility(); settings.ToggleVisibility();
} }
} }
} }

View File

@ -18,10 +18,14 @@ namespace osu.Desktop.VisualTests.Tests
private SongProgress progress; private SongProgress progress;
private SongProgressGraph graph; private SongProgressGraph graph;
private StopwatchClock clock;
public override void Reset() public override void Reset()
{ {
base.Reset(); base.Reset();
clock = new StopwatchClock(true);
Add(progress = new SongProgress Add(progress = new SongProgress
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
@ -55,6 +59,9 @@ namespace osu.Desktop.VisualTests.Tests
progress.Objects = objects; progress.Objects = objects;
graph.Objects = objects; graph.Objects = objects;
progress.AudioClock = clock;
progress.OnSeek = pos => clock.Seek(pos);
} }
} }
} }

View File

@ -1,8 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 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.Graphics;
using OpenTK; using OpenTK;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;

View File

@ -16,7 +16,7 @@ namespace osu.Desktop.VisualTests.Tests
base.Reset(); base.Reset();
Add(new BackButton()); Add(new BackButton());
Add(new SkipButton()); Add(new SkipButton(Clock.CurrentTime + 5000));
} }
} }
} }

View File

@ -29,7 +29,7 @@ namespace osu.Desktop.VisualTests
host.DrawThread.InactiveHz = host.DrawThread.ActiveHz; host.DrawThread.InactiveHz = host.DrawThread.ActiveHz;
host.InputThread.InactiveHz = host.InputThread.ActiveHz; host.InputThread.InactiveHz = host.InputThread.ActiveHz;
host.Window.CursorState = CursorState.Hidden; host.Window.CursorState |= CursorState.Hidden;
} }
} }
} }

View File

@ -190,9 +190,12 @@
<Compile Include="Tests\TestCaseDrawings.cs" /> <Compile Include="Tests\TestCaseDrawings.cs" />
<Compile Include="Tests\TestCaseGamefield.cs" /> <Compile Include="Tests\TestCaseGamefield.cs" />
<Compile Include="Tests\TestCaseGraph.cs" /> <Compile Include="Tests\TestCaseGraph.cs" />
<Compile Include="Tests\TestCaseManiaHitObjects.cs" />
<Compile Include="Tests\TestCaseManiaPlayfield.cs" />
<Compile Include="Tests\TestCaseMenuOverlays.cs" /> <Compile Include="Tests\TestCaseMenuOverlays.cs" />
<Compile Include="Tests\TestCaseMusicController.cs" /> <Compile Include="Tests\TestCaseMusicController.cs" />
<Compile Include="Tests\TestCaseNotificationManager.cs" /> <Compile Include="Tests\TestCaseNotificationManager.cs" />
<Compile Include="Tests\TestCaseOnScreenDisplay.cs" />
<Compile Include="Tests\TestCasePlayer.cs" /> <Compile Include="Tests\TestCasePlayer.cs" />
<Compile Include="Tests\TestCaseHitObjects.cs" /> <Compile Include="Tests\TestCaseHitObjects.cs" />
<Compile Include="Tests\TestCaseKeyCounter.cs" /> <Compile Include="Tests\TestCaseKeyCounter.cs" />
@ -209,9 +212,9 @@
<Compile Include="Tests\TestCaseTwoLayerButton.cs" /> <Compile Include="Tests\TestCaseTwoLayerButton.cs" />
<Compile Include="VisualTestGame.cs" /> <Compile Include="VisualTestGame.cs" />
<Compile Include="Platform\TestStorage.cs" /> <Compile Include="Platform\TestStorage.cs" />
<Compile Include="Tests\TestCaseOptions.cs" /> <Compile Include="Tests\TestCaseSettings.cs" />
<Compile Include="Tests\TestCaseSongProgress.cs" /> <Compile Include="Tests\TestCaseSongProgress.cs" />
<Compile Include="Tests\TestCaseModSelectOverlay.cs" /> <Compile Include="Tests\TestCaseMods.cs" />
<Compile Include="Tests\TestCaseDialogOverlay.cs" /> <Compile Include="Tests\TestCaseDialogOverlay.cs" />
<Compile Include="Tests\TestCaseBeatmapOptionsOverlay.cs" /> <Compile Include="Tests\TestCaseBeatmapOptionsOverlay.cs" />
<Compile Include="Tests\TestCaseLeaderboard.cs" /> <Compile Include="Tests\TestCaseLeaderboard.cs" />

View File

@ -14,7 +14,7 @@ namespace osu.Desktop.Beatmaps.IO
{ {
public static void Register() => AddReader<LegacyFilesystemReader>((storage, path) => Directory.Exists(path)); public static void Register() => AddReader<LegacyFilesystemReader>((storage, path) => Directory.Exists(path));
private string basePath { get; } private readonly string basePath;
public LegacyFilesystemReader(string path) public LegacyFilesystemReader(string path)
{ {

View File

@ -43,7 +43,7 @@ namespace osu.Desktop
var desktopWindow = host.Window as DesktopGameWindow; var desktopWindow = host.Window as DesktopGameWindow;
if (desktopWindow != null) if (desktopWindow != null)
{ {
desktopWindow.CursorState = CursorState.Hidden; desktopWindow.CursorState |= CursorState.Hidden;
desktopWindow.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location); desktopWindow.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
desktopWindow.Title = Name; desktopWindow.Title = Name;

View File

@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch
{ {
public class CatchRuleset : Ruleset public class CatchRuleset : Ruleset
{ {
public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap) => new CatchHitRenderer(beatmap); public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new CatchHitRenderer(beatmap, isForCurrentRuleset);
public override IEnumerable<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {

View File

@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Catch.UI
{ {
public class CatchHitRenderer : HitRenderer<CatchBaseHit, CatchJudgement> public class CatchHitRenderer : HitRenderer<CatchBaseHit, CatchJudgement>
{ {
public CatchHitRenderer(WorkingBeatmap beatmap) public CatchHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(beatmap) : base(beatmap, isForCurrentRuleset)
{ {
} }

View File

@ -1,23 +1,153 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 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.Mania.Objects;
using System.Collections.Generic;
using System;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Beatmaps; using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using System;
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Database;
namespace osu.Game.Rulesets.Mania.Beatmaps namespace osu.Game.Rulesets.Mania.Beatmaps
{ {
internal class ManiaBeatmapConverter : BeatmapConverter<ManiaBaseHit> public class ManiaBeatmapConverter : BeatmapConverter<ManiaHitObject>
{ {
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) }; protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
protected override IEnumerable<ManiaBaseHit> ConvertHitObject(HitObject original, Beatmap beatmap) private Pattern lastPattern = new Pattern();
private FastRandom random;
private Beatmap beatmap;
private bool isForCurrentRuleset;
protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original, bool isForCurrentRuleset)
{ {
yield return null; this.isForCurrentRuleset = isForCurrentRuleset;
beatmap = original;
BeatmapDifficulty difficulty = original.BeatmapInfo.Difficulty;
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
random = new FastRandom(seed);
return base.ConvertBeatmap(original, isForCurrentRuleset);
}
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
{
var maniaOriginal = original as ManiaHitObject;
if (maniaOriginal != null)
{
yield return maniaOriginal;
yield break;
}
var objects = isForCurrentRuleset ? generateSpecific(original) : generateConverted(original);
if (objects == null)
yield break;
foreach (ManiaHitObject obj in objects)
yield return obj;
}
/// <summary>
/// Method that generates hit objects for osu!mania specific beatmaps.
/// </summary>
/// <param name="original">The original hit object.</param>
/// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original)
{
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern);
Pattern newPattern = generator.Generate();
lastPattern = newPattern;
return newPattern.HitObjects;
}
/// <summary>
/// Method that generates hit objects for non-osu!mania beatmaps.
/// </summary>
/// <param name="original">The original hit object.</param>
/// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateConverted(HitObject original)
{
var endTimeData = original as IHasEndTime;
var distanceData = original as IHasDistance;
var positionData = original as IHasPosition;
// Following lines currently commented out to appease resharper
//Patterns.PatternGenerator conversion = null;
if (distanceData != null)
{
// Slider
}
else if (endTimeData != null)
{
// Spinner
}
else if (positionData != null)
{
// Circle
}
//if (conversion == null)
return null;
//Pattern newPattern = conversion.Generate();
//lastPattern = newPattern;
//return newPattern.HitObjects;
}
/// <summary>
/// A pattern generator for osu!mania-specific beatmaps.
/// </summary>
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
{
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
: base(random, hitObject, beatmap, previousPattern)
{
}
public override Pattern Generate()
{
var endTimeData = HitObject as IHasEndTime;
var positionData = HitObject as IHasXPosition;
int column = GetColumn(positionData?.X ?? 0);
var pattern = new Pattern();
if (endTimeData != null)
{
pattern.Add(new HoldNote
{
StartTime = HitObject.StartTime,
Samples = HitObject.Samples,
Duration = endTimeData.Duration,
Column = column,
});
}
else if (positionData != null)
{
pattern.Add(new Note
{
StartTime = HitObject.StartTime,
Samples = HitObject.Samples,
Column = column
});
}
return pattern;
}
} }
} }
} }

View File

@ -0,0 +1,106 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Objects;
using OpenTK;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
/// <summary>
/// A pattern generator for legacy hit objects.
/// </summary>
internal abstract class PatternGenerator : Patterns.PatternGenerator
{
/// <summary>
/// The column index at which to start generating random notes.
/// </summary>
protected readonly int RandomStart;
/// <summary>
/// The random number generator to use.
/// </summary>
protected readonly FastRandom Random;
protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
: base(hitObject, beatmap, previousPattern)
{
Random = random;
RandomStart = AvailableColumns == 8 ? 1 : 0;
}
/// <summary>
/// Converts an x-position into a column.
/// </summary>
/// <param name="position">The x-position.</param>
/// <param name="allowSpecial">Whether to treat as 7K + 1.</param>
/// <returns>The column.</returns>
protected int GetColumn(float position, bool allowSpecial = false)
{
if (allowSpecial && AvailableColumns == 8)
{
const float local_x_divisor = 512f / 7;
return MathHelper.Clamp((int)Math.Floor(position / local_x_divisor), 0, 6) + 1;
}
float localXDivisor = 512f / AvailableColumns;
return MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, AvailableColumns - 1);
}
/// <summary>
/// Generates a count of notes to be generated from probabilities.
/// </summary>
/// <param name="p2">Probability for 2 notes to be generated.</param>
/// <param name="p3">Probability for 3 notes to be generated.</param>
/// <param name="p4">Probability for 4 notes to be generated.</param>
/// <param name="p5">Probability for 5 notes to be generated.</param>
/// <param name="p6">Probability for 6 notes to be generated.</param>
/// <returns>The amount of notes to be generated.</returns>
protected int GetRandomNoteCount(double p2, double p3, double p4 = 0, double p5 = 0, double p6 = 0)
{
double val = Random.NextDouble();
if (val >= 1 - p6)
return 6;
if (val >= 1 - p5)
return 5;
if (val >= 1 - p4)
return 4;
if (val >= 1 - p3)
return 3;
return val >= 1 - p2 ? 2 : 1;
}
private double? conversionDifficulty;
/// <summary>
/// A difficulty factor used for various conversion methods from osu!stable.
/// </summary>
protected double ConversionDifficulty
{
get
{
if (conversionDifficulty != null)
return conversionDifficulty.Value;
HitObject lastObject = Beatmap.HitObjects.LastOrDefault();
HitObject firstObject = Beatmap.HitObjects.FirstOrDefault();
double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0);
drainTime -= Beatmap.EventInfo.TotalBreakTime;
if (drainTime == 0)
drainTime = 10000;
BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.Difficulty;
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + Beatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
return conversionDifficulty.Value;
}
}
}
}

View File

@ -0,0 +1,65 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
/// <summary>
/// The type of pattern to generate. Used for legacy patterns.
/// </summary>
[Flags]
internal enum PatternType
{
None = 0,
/// <summary>
/// Keep the same as last row.
/// </summary>
ForceStack = 1,
/// <summary>
/// Keep different from last row.
/// </summary>
ForceNotStack = 2,
/// <summary>
/// Keep as single note at its original position.
/// </summary>
KeepSingle = 4,
/// <summary>
/// Use a lower random value.
/// </summary>
LowProbability = 8,
/// <summary>
/// Reserved.
/// </summary>
Alternate = 16,
/// <summary>
/// Ignore the repeat count.
/// </summary>
ForceSigSlider = 32,
/// <summary>
/// Convert slider to circle.
/// </summary>
ForceNotSlider = 64,
/// <summary>
/// Notes gathered together.
/// </summary>
Gathered = 128,
Mirror = 256,
/// <summary>
/// Change 0 -> 6.
/// </summary>
Reverse = 512,
/// <summary>
/// 1 -> 5 -> 1 -> 5 like reverse.
/// </summary>
Cycle = 1024,
/// <summary>
/// Next note will be at column + 1.
/// </summary>
Stair = 2048,
/// <summary>
/// Next note will be at column - 1.
/// </summary>
ReverseStair = 4096
}
}

View File

@ -0,0 +1,61 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Mania.Objects;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
{
/// <summary>
/// Creates a pattern containing hit objects.
/// </summary>
internal class Pattern
{
private readonly List<ManiaHitObject> hitObjects = new List<ManiaHitObject>();
/// <summary>
/// All the hit objects contained in this pattern.
/// </summary>
public IEnumerable<ManiaHitObject> HitObjects => hitObjects;
/// <summary>
/// Whether this pattern already contains a hit object in a code.
/// </summary>
/// <param name="column">The column index.</param>
/// <returns>Whether this pattern already contains a hit object in <paramref name="column"/></returns>
public bool IsFilled(int column) => hitObjects.Exists(h => h.Column == column);
/// <summary>
/// Amount of columns taken up by hit objects in this pattern.
/// </summary>
public int ColumnsFilled => HitObjects.GroupBy(h => h.Column).Count();
/// <summary>
/// Adds a hit object to this pattern.
/// </summary>
/// <param name="hitObject">The hit object to add.</param>
public void Add(ManiaHitObject hitObject) => hitObjects.Add(hitObject);
/// <summary>
/// Copies hit object from another pattern to this one.
/// </summary>
/// <param name="other">The other pattern.</param>
public void Add(Pattern other)
{
other.HitObjects.ForEach(Add);
}
/// <summary>
/// Clears this pattern, removing all hit objects.
/// </summary>
public void Clear() => hitObjects.Clear();
/// <summary>
/// Removes a hit object from this pattern.
/// </summary>
/// <param name="hitObject">The hit object to remove.</param>
public bool Remove(ManiaHitObject hitObject) => hitObjects.Remove(hitObject);
}
}

View File

@ -0,0 +1,50 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
{
/// <summary>
/// Generator to create a pattern <see cref="Pattern"/> from a hit object.
/// </summary>
internal abstract class PatternGenerator
{
/// <summary>
/// The number of columns available to create the pattern.
/// </summary>
protected readonly int AvailableColumns;
/// <summary>
/// The last pattern.
/// </summary>
protected readonly Pattern PreviousPattern;
/// <summary>
/// The hit object to create the pattern for.
/// </summary>
protected readonly HitObject HitObject;
/// <summary>
/// The beatmap which <see cref="HitObject"/> is a part of.
/// </summary>
protected readonly Beatmap Beatmap;
protected PatternGenerator(HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
{
PreviousPattern = previousPattern;
HitObject = hitObject;
Beatmap = beatmap;
AvailableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize);
}
/// <summary>
/// Generates the pattern for <see cref="HitObject"/>, filled with hit objects.
/// </summary>
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
public abstract Pattern Generate();
}
}

View File

@ -0,0 +1,179 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Database;
namespace osu.Game.Rulesets.Mania.Judgements
{
public class HitWindows
{
#region Constants
/// <summary>
/// PERFECT hit window at OD = 10.
/// </summary>
private const double perfect_min = 27.8;
/// <summary>
/// PERFECT hit window at OD = 5.
/// </summary>
private const double perfect_mid = 38.8;
/// <summary>
/// PERFECT hit window at OD = 0.
/// </summary>
private const double perfect_max = 44.8;
/// <summary>
/// GREAT hit window at OD = 10.
/// </summary>
private const double great_min = 68;
/// <summary>
/// GREAT hit window at OD = 5.
/// </summary>
private const double great_mid = 98;
/// <summary>
/// GREAT hit window at OD = 0.
/// </summary>
private const double great_max = 128;
/// <summary>
/// GOOD hit window at OD = 10.
/// </summary>
private const double good_min = 134;
/// <summary>
/// GOOD hit window at OD = 5.
/// </summary>
private const double good_mid = 164;
/// <summary>
/// GOOD hit window at OD = 0.
/// </summary>
private const double good_max = 194;
/// <summary>
/// OK hit window at OD = 10.
/// </summary>
private const double ok_min = 194;
/// <summary>
/// OK hit window at OD = 5.
/// </summary>
private const double ok_mid = 224;
/// <summary>
/// OK hit window at OD = 0.
/// </summary>
private const double ok_max = 254;
/// <summary>
/// BAD hit window at OD = 10.
/// </summary>
private const double bad_min = 242;
/// <summary>
/// BAD hit window at OD = 5.
/// </summary>
private const double bad_mid = 272;
/// <summary>
/// BAD hit window at OD = 0.
/// </summary>
private const double bad_max = 302;
/// <summary>
/// MISS hit window at OD = 10.
/// </summary>
private const double miss_min = 316;
/// <summary>
/// MISS hit window at OD = 5.
/// </summary>
private const double miss_mid = 346;
/// <summary>
/// MISS hit window at OD = 0.
/// </summary>
private const double miss_max = 376;
#endregion
/// <summary>
/// Hit window for a PERFECT hit.
/// </summary>
public double Perfect = perfect_mid;
/// <summary>
/// Hit window for a GREAT hit.
/// </summary>
public double Great = great_mid;
/// <summary>
/// Hit window for a GOOD hit.
/// </summary>
public double Good = good_mid;
/// <summary>
/// Hit window for an OK hit.
/// </summary>
public double Ok = ok_mid;
/// <summary>
/// Hit window for a BAD hit.
/// </summary>
public double Bad = bad_mid;
/// <summary>
/// Hit window for a MISS hit.
/// </summary>
public double Miss = miss_mid;
/// <summary>
/// Constructs default hit windows.
/// </summary>
public HitWindows()
{
}
/// <summary>
/// Constructs hit windows by fitting a parameter to a 2-part piecewise linear function for each hit window.
/// </summary>
/// <param name="difficulty">The parameter.</param>
public HitWindows(double difficulty)
{
Perfect = BeatmapDifficulty.DifficultyRange(difficulty, perfect_max, perfect_mid, perfect_min);
Great = BeatmapDifficulty.DifficultyRange(difficulty, great_max, great_mid, great_min);
Good = BeatmapDifficulty.DifficultyRange(difficulty, good_max, good_mid, good_min);
Ok = BeatmapDifficulty.DifficultyRange(difficulty, ok_max, ok_mid, ok_min);
Bad = BeatmapDifficulty.DifficultyRange(difficulty, bad_max, bad_mid, bad_min);
Miss = BeatmapDifficulty.DifficultyRange(difficulty, miss_max, miss_mid, miss_min);
}
/// <summary>
/// Constructs new hit windows which have been multiplied by a value.
/// </summary>
/// <param name="windows">The original hit windows.</param>
/// <param name="value">The value to multiply each hit window by.</param>
public static HitWindows operator *(HitWindows windows, double value)
{
return new HitWindows
{
Perfect = windows.Perfect * value,
Great = windows.Great * value,
Good = windows.Good * value,
Ok = windows.Ok * value,
Bad = windows.Bad * value,
Miss = windows.Miss * value
};
}
/// <summary>
/// Constructs new hit windows which have been divided by a value.
/// </summary>
/// <param name="windows">The original hit windows.</param>
/// <param name="value">The value to divide each hit window by.</param>
public static HitWindows operator /(HitWindows windows, double value)
{
return new HitWindows
{
Perfect = windows.Perfect / value,
Great = windows.Great / value,
Good = windows.Good / value,
Ok = windows.Ok / value,
Bad = windows.Bad / value,
Miss = windows.Miss / value
};
}
}
}

View File

@ -9,7 +9,7 @@ using System.Collections.Generic;
namespace osu.Game.Rulesets.Mania namespace osu.Game.Rulesets.Mania
{ {
public class ManiaDifficultyCalculator : DifficultyCalculator<ManiaBaseHit> public class ManiaDifficultyCalculator : DifficultyCalculator<ManiaHitObject>
{ {
public ManiaDifficultyCalculator(Beatmap beatmap) public ManiaDifficultyCalculator(Beatmap beatmap)
: base(beatmap) : base(beatmap)
@ -21,6 +21,6 @@ namespace osu.Game.Rulesets.Mania
return 0; return 0;
} }
protected override BeatmapConverter<ManiaBaseHit> CreateBeatmapConverter() => new ManiaBeatmapConverter(); protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter();
} }
} }

View File

@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania
{ {
public class ManiaRuleset : Ruleset public class ManiaRuleset : Ruleset
{ {
public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap) => new ManiaHitRenderer(beatmap); public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaHitRenderer(beatmap, isForCurrentRuleset);
public override IEnumerable<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {

View File

@ -0,0 +1,92 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
namespace osu.Game.Rulesets.Mania.MathUtils
{
/// <summary>
/// A PRNG specified in http://heliosphan.org/fastrandom.html.
/// </summary>
internal class FastRandom
{
private const double uint_to_real = 1.0 / (uint.MaxValue + 1.0);
private const uint int_mask = 0x7FFFFFFF;
private const uint y = 842502087;
private const uint z = 3579807591;
private const uint w = 273326509;
private uint _x, _y = y, _z = z, _w = w;
public FastRandom(int seed)
{
_x = (uint)seed;
}
public FastRandom()
: this(Environment.TickCount)
{
}
/// <summary>
/// Generates a random unsigned integer within the range [<see cref="uint.MinValue"/>, <see cref="uint.MaxValue"/>).
/// </summary>
/// <returns>The random value.</returns>
public uint NextUInt()
{
uint t = _x ^ _x << 11;
_x = _y;
_y = _z;
_z = _w;
return _w = _w ^ _w >> 19 ^ t ^ t >> 8;
}
/// <summary>
/// Generates a random integer value within the range [0, <see cref="int.MaxValue"/>).
/// </summary>
/// <returns>The random value.</returns>
public int Next() => (int)(int_mask & NextUInt());
/// <summary>
/// Generates a random integer value within the range [0, <paramref name="upperBound"/>).
/// </summary>
/// <param name="upperBound">The upper bound.</param>
/// <returns>The random value.</returns>
public int Next(int upperBound) => (int)(NextDouble() * upperBound);
/// <summary>
/// Generates a random integer value within the range [<paramref name="lowerBound"/>, <paramref name="upperBound"/>).
/// </summary>
/// <param name="lowerBound">The lower bound of the range.</param>
/// <param name="upperBound">The upper bound of the range.</param>
/// <returns>The random value.</returns>
public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound));
/// <summary>
/// Generates a random double value within the range [0, 1).
/// </summary>
/// <returns>The random value.</returns>
public double NextDouble() => uint_to_real * NextUInt();
private uint bitBuffer;
private int bitIndex = 32;
/// <summary>
/// Generates a reandom boolean value. Cached such that a random value is only generated once in every 32 calls.
/// </summary>
/// <returns>The random value.</returns>
public bool NextBool()
{
if (bitIndex == 32)
{
bitBuffer = NextUInt();
bitIndex = 1;
return (bitBuffer & 1) == 1;
}
bitIndex++;
return ((bitBuffer >>= 1) & 1) == 1;
}
}
}

View File

@ -64,6 +64,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{ {
public override string Name => "FadeIn"; public override string Name => "FadeIn";
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 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

@ -1,36 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Graphics;
using OpenTK;
namespace osu.Game.Rulesets.Mania.Objects.Drawable
{
public class DrawableNote : Sprite
{
private readonly ManiaBaseHit note;
public DrawableNote(ManiaBaseHit note)
{
this.note = note;
Origin = Anchor.Centre;
Scale = new Vector2(0.1f);
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Texture = textures.Get(@"Menu/logo");
const double duration = 0;
Transforms.Add(new TransformPositionY { StartTime = note.StartTime - 200, EndTime = note.StartTime, StartValue = -0.1f, EndValue = 0.9f });
Transforms.Add(new TransformAlpha { StartTime = note.StartTime + duration + 200, EndTime = note.StartTime + duration + 400, StartValue = 1, EndValue = 0 });
Expire(true);
}
}
}

View File

@ -0,0 +1,76 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
public class DrawableHoldNote : DrawableManiaHitObject<HoldNote>
{
private readonly NotePiece headPiece;
private readonly BodyPiece bodyPiece;
private readonly NotePiece tailPiece;
public DrawableHoldNote(HoldNote hitObject)
: base(hitObject)
{
RelativeSizeAxes = Axes.Both;
Height = (float)HitObject.Duration;
Add(new Drawable[]
{
// For now the body piece covers the entire height of the container
// whereas possibly in the future we don't want to extend under the head/tail.
// This will be fixed when new designs are given or the current design is finalized.
bodyPiece = new BodyPiece
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
headPiece = new NotePiece
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
tailPiece = new NotePiece
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre
}
});
}
public override Color4 AccentColour
{
get { return base.AccentColour; }
set
{
if (base.AccentColour == value)
return;
base.AccentColour = value;
headPiece.AccentColour = value;
bodyPiece.AccentColour = value;
tailPiece.AccentColour = value;
}
}
protected override void UpdateState(ArmedState state)
{
}
protected override void Update()
{
if (Time.Current > HitObject.StartTime)
headPiece.Colour = Color4.Green;
if (Time.Current > HitObject.EndTime)
{
bodyPiece.Colour = Color4.Green;
tailPiece.Colour = Color4.Green;
}
}
}
}

View File

@ -0,0 +1,38 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
public abstract class DrawableManiaHitObject<TObject> : DrawableHitObject<ManiaHitObject, ManiaJudgement>
where TObject : ManiaHitObject
{
public new TObject HitObject;
protected DrawableManiaHitObject(TObject hitObject)
: base(hitObject)
{
HitObject = hitObject;
RelativePositionAxes = Axes.Y;
Y = (float)HitObject.StartTime;
}
public override Color4 AccentColour
{
get { return base.AccentColour; }
set
{
if (base.AccentColour == value)
return;
base.AccentColour = value;
}
}
protected override ManiaJudgement CreateJudgement() => new ManiaJudgement();
}
}

View File

@ -0,0 +1,51 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
public class DrawableNote : DrawableManiaHitObject<Note>
{
private readonly NotePiece headPiece;
public DrawableNote(Note hitObject)
: base(hitObject)
{
RelativeSizeAxes = Axes.Both;
Height = 100;
Add(headPiece = new NotePiece
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
});
}
public override Color4 AccentColour
{
get { return base.AccentColour; }
set
{
if (base.AccentColour == value)
return;
base.AccentColour = value;
headPiece.AccentColour = value;
}
}
protected override void Update()
{
if (Time.Current > HitObject.StartTime)
Colour = Color4.Green;
}
protected override void UpdateState(ArmedState state)
{
}
}
}

View File

@ -0,0 +1,47 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
{
/// <summary>
/// Represents length-wise portion of a hold note.
/// </summary>
internal class BodyPiece : Container, IHasAccentColour
{
private readonly Box box;
public BodyPiece()
{
RelativeSizeAxes = Axes.Both;
Children = new[]
{
box = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.3f
}
};
}
private Color4 accentColour;
public Color4 AccentColour
{
get { return accentColour; }
set
{
if (accentColour == value)
return;
accentColour = value;
box.Colour = accentColour;
}
}
}
}

View File

@ -0,0 +1,59 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
{
/// <summary>
/// Represents the static hit markers of notes.
/// </summary>
internal class NotePiece : Container, IHasAccentColour
{
private const float head_height = 10;
private const float head_colour_height = 6;
private readonly Box colouredBox;
public NotePiece()
{
RelativeSizeAxes = Axes.X;
Height = head_height;
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both
},
colouredBox = new Box
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
Height = head_colour_height,
Alpha = 0.2f
}
};
}
private Color4 accentColour;
public Color4 AccentColour
{
get { return accentColour; }
set
{
if (accentColour == value)
return;
accentColour = value;
colouredBox.Colour = AccentColour.Lighten(0.9f);
}
}
}
}

View File

@ -1,9 +1,37 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 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.Timing;
using osu.Game.Database;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Mania.Objects namespace osu.Game.Rulesets.Mania.Objects
{ {
public class HoldNote : Note /// <summary>
/// Represents a hit object which requires pressing, holding, and releasing a key.
/// </summary>
public class HoldNote : Note, IHasEndTime
{ {
/// <summary>
/// Lenience of release hit windows. This is to make cases where the hold note release
/// is timed alongside presses of other hit objects less awkward.
/// </summary>
private const double release_window_lenience = 1.5;
public double Duration { get; set; }
public double EndTime => StartTime + Duration;
/// <summary>
/// The key-release hit windows for this hold note.
/// </summary>
protected HitWindows ReleaseHitWindows = new HitWindows();
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{
base.ApplyDefaults(timing, difficulty);
ReleaseHitWindows = HitWindows * release_window_lenience;
}
} }
} }

View File

@ -1,12 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Objects
{
public abstract class ManiaBaseHit : HitObject
{
public int Column;
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Mania.Objects.Types;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Objects
{
public abstract class ManiaHitObject : HitObject, IHasColumn
{
public int Column { get; set; }
/// <summary>
/// The number of other <see cref="ManiaHitObject"/> that start at
/// the same time as this hit object.
/// </summary>
public int Siblings { get; set; }
}
}

View File

@ -1,9 +1,27 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 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.Timing;
using osu.Game.Database;
using osu.Game.Rulesets.Mania.Judgements;
namespace osu.Game.Rulesets.Mania.Objects namespace osu.Game.Rulesets.Mania.Objects
{ {
public class Note : ManiaBaseHit /// <summary>
/// Represents a hit object which has a single hit press.
/// </summary>
public class Note : ManiaHitObject
{ {
/// <summary>
/// The key-press hit window for this note.
/// </summary>
protected HitWindows HitWindows = new HitWindows();
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{
base.ApplyDefaults(timing, difficulty);
HitWindows = new HitWindows(difficulty.OverallDifficulty);
}
} }
} }

View File

@ -0,0 +1,16 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Rulesets.Mania.Objects.Types
{
/// <summary>
/// A type of hit object which lies in one of a number of predetermined columns.
/// </summary>
public interface IHasColumn
{
/// <summary>
/// The column which the hit object lies in.
/// </summary>
int Column { get; }
}
}

View File

@ -8,13 +8,13 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Scoring namespace osu.Game.Rulesets.Mania.Scoring
{ {
internal class ManiaScoreProcessor : ScoreProcessor<ManiaBaseHit, ManiaJudgement> internal class ManiaScoreProcessor : ScoreProcessor<ManiaHitObject, ManiaJudgement>
{ {
public ManiaScoreProcessor() public ManiaScoreProcessor()
{ {
} }
public ManiaScoreProcessor(HitRenderer<ManiaBaseHit, ManiaJudgement> hitRenderer) public ManiaScoreProcessor(HitRenderer<ManiaHitObject, ManiaJudgement> hitRenderer)
: base(hitRenderer) : base(hitRenderer)
{ {
} }

View File

@ -0,0 +1,153 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using OpenTK;
using osu.Game.Beatmaps.Timing;
namespace osu.Game.Rulesets.Mania.Timing
{
/// <summary>
/// A container in which added drawables are put into a relative coordinate space spanned by a length of time.
/// <para>
/// This container contains <see cref="ControlPoint"/>s which scroll inside this container.
/// Drawables added to this container are moved inside the relevant <see cref="ControlPoint"/>,
/// and as such, will scroll along with the <see cref="ControlPoint"/>s.
/// </para>
/// </summary>
public class ControlPointContainer : Container<Drawable>
{
/// <summary>
/// The amount of time which this container spans.
/// </summary>
public double TimeSpan { get; set; }
private readonly List<DrawableControlPoint> drawableControlPoints;
public ControlPointContainer(IEnumerable<ControlPoint> timingChanges)
{
drawableControlPoints = timingChanges.Select(t => new DrawableControlPoint(t)).ToList();
Children = drawableControlPoints;
}
/// <summary>
/// Adds a drawable to this container. Note that the drawable added must have its Y-position be
/// an absolute unit of time that is _not_ relative to <see cref="TimeSpan"/>.
/// </summary>
/// <param name="drawable">The drawable to add.</param>
public override void Add(Drawable drawable)
{
// Always add timing sections to ourselves
if (drawable is DrawableControlPoint)
{
base.Add(drawable);
return;
}
var controlPoint = drawableControlPoints.LastOrDefault(t => t.CanContain(drawable)) ?? drawableControlPoints.FirstOrDefault();
if (controlPoint == null)
throw new Exception("Could not find suitable timing section to add object to.");
controlPoint.Add(drawable);
}
/// <summary>
/// A container that contains drawables within the time span of a timing section.
/// <para>
/// The content of this container will scroll relative to the current time.
/// </para>
/// </summary>
private class DrawableControlPoint : Container
{
private readonly ControlPoint timingChange;
protected override Container<Drawable> Content => content;
private readonly Container content;
/// <summary>
/// Creates a drawable control point. The height of this container will be proportional
/// to the beat length of the control point it is initialized with such that, e.g. a beat length
/// of 500ms results in this container being twice as high as its parent, which further means that
/// the content container will scroll at twice the normal rate.
/// </summary>
/// <param name="timingChange">The control point to create the drawable control point for.</param>
public DrawableControlPoint(ControlPoint timingChange)
{
this.timingChange = timingChange;
RelativeSizeAxes = Axes.Both;
AddInternal(content = new AutoTimeRelativeContainer
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Y = (float)timingChange.Time
});
}
protected override void Update()
{
var parent = (ControlPointContainer)Parent;
// Adjust our height to account for the speed changes
Height = (float)(1000 / timingChange.BeatLength / timingChange.SpeedMultiplier);
RelativeCoordinateSpace = new Vector2(1, (float)parent.TimeSpan);
// Scroll the content
content.Y = (float)(timingChange.Time - Time.Current);
}
public override void Add(Drawable drawable)
{
// The previously relatively-positioned drawable will now become relative to content, but since the drawable has no knowledge of content,
// we need to offset it back by content's position position so that it becomes correctly relatively-positioned to content
// This can be removed if hit objects were stored such that either their StartTime or their "beat offset" was relative to the timing change
// they belonged to, but this requires a radical change to the beatmap format which we're not ready to do just yet
drawable.Y -= (float)timingChange.Time;
base.Add(drawable);
}
/// <summary>
/// Whether this control point can contain a drawable. This control point can contain a drawable if the drawable is positioned "after" this control point.
/// </summary>
/// <param name="drawable">The drawable to check.</param>
public bool CanContain(Drawable drawable) => content.Y <= drawable.Y;
/// <summary>
/// A container which always keeps its height and relative coordinate space "auto-sized" to its children.
/// <para>
/// This is used in the case where children are relatively positioned/sized to time values (e.g. notes/bar lines) to keep
/// such children wrapped inside a container, otherwise they would disappear due to container flattening.
/// </para>
/// </summary>
private class AutoTimeRelativeContainer : Container
{
public override void InvalidateFromChild(Invalidation invalidation)
{
// We only want to re-compute our size when a child's size or position has changed
if ((invalidation & Invalidation.Geometry) == 0)
{
base.InvalidateFromChild(invalidation);
return;
}
if (!Children.Any())
return;
float height = Children.Select(child => child.Y + child.Height).Max();
Height = height;
RelativeCoordinateSpace = new Vector2(1, height);
base.InvalidateFromChild(invalidation);
}
}
}
}
}

View File

@ -0,0 +1,211 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Colour;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Timing;
using System.Collections.Generic;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Beatmaps.Timing;
namespace osu.Game.Rulesets.Mania.UI
{
public class Column : Container, IHasAccentColour
{
private const float key_icon_size = 10;
private const float key_icon_corner_radius = 3;
private const float key_icon_border_radius = 2;
private const float hit_target_height = 10;
private const float hit_target_bar_height = 2;
private const float column_width = 45;
private const float special_column_width = 70;
public Key Key;
private readonly Box background;
private readonly Container hitTargetBar;
private readonly Container keyIcon;
public readonly ControlPointContainer ControlPointContainer;
public Column(IEnumerable<ControlPoint> timingChanges)
{
RelativeSizeAxes = Axes.Y;
Width = column_width;
Children = new Drawable[]
{
background = new Box
{
Name = "Foreground",
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f
},
new Container
{
Name = "Hit target + hit objects",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = ManiaPlayfield.HIT_TARGET_POSITION},
Children = new Drawable[]
{
new Container
{
Name = "Hit target",
RelativeSizeAxes = Axes.X,
Height = hit_target_height,
Children = new Drawable[]
{
new Box
{
Name = "Background",
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
},
hitTargetBar = new Container
{
Name = "Bar",
RelativeSizeAxes = Axes.X,
Height = hit_target_bar_height,
Masking = true,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both
}
}
}
}
},
ControlPointContainer = new ControlPointContainer(timingChanges)
{
Name = "Hit objects",
RelativeSizeAxes = Axes.Both,
},
}
},
new Container
{
Name = "Key",
RelativeSizeAxes = Axes.X,
Height = ManiaPlayfield.HIT_TARGET_POSITION,
Children = new Drawable[]
{
new Box
{
Name = "Key gradient",
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0)),
Alpha = 0.5f
},
keyIcon = new Container
{
Name = "Key icon",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(key_icon_size),
Masking = true,
CornerRadius = key_icon_corner_radius,
BorderThickness = 2,
BorderColour = Color4.White, // Not true
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
}
}
}
};
}
private bool isSpecial;
public bool IsSpecial
{
get { return isSpecial; }
set
{
if (isSpecial == value)
return;
isSpecial = value;
Width = isSpecial ? special_column_width : column_width;
}
}
private Color4 accentColour;
public Color4 AccentColour
{
get { return accentColour; }
set
{
if (accentColour == value)
return;
accentColour = value;
background.Colour = accentColour;
hitTargetBar.EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Radius = 5,
Colour = accentColour.Opacity(0.5f),
};
keyIcon.EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Radius = 5,
Colour = accentColour.Opacity(0.5f),
};
}
}
public void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> hitObject)
{
ControlPointContainer.Add(hitObject);
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Repeat)
return false;
if (args.Key == Key)
{
background.FadeTo(background.Alpha + 0.2f, 50, EasingTypes.OutQuint);
keyIcon.ScaleTo(1.4f, 50, EasingTypes.OutQuint);
}
return false;
}
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
{
if (args.Key == Key)
{
background.FadeTo(0.2f, 800, EasingTypes.OutQuart);
keyIcon.ScaleTo(1f, 400, EasingTypes.OutQuart);
}
return false;
}
}
}

View File

@ -1,34 +1,92 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 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.Linq;
using OpenTK;
using osu.Framework.Graphics;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Beatmaps; using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
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.Scoring; using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
{ {
public class ManiaHitRenderer : HitRenderer<ManiaBaseHit, ManiaJudgement> public class ManiaHitRenderer : HitRenderer<ManiaHitObject, ManiaJudgement>
{ {
private readonly int columns; public int? Columns;
public ManiaHitRenderer(WorkingBeatmap beatmap, int columns = 5) public ManiaHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(beatmap) : base(beatmap, isForCurrentRuleset)
{ {
this.columns = columns; }
protected override Playfield<ManiaHitObject, ManiaJudgement> CreatePlayfield()
{
ControlPoint firstTimingChange = Beatmap.TimingInfo.ControlPoints.FirstOrDefault(t => t.TimingChange);
if (firstTimingChange == null)
throw new Exception("The Beatmap contains no timing points!");
// Generate the timing points, making non-timing changes use the previous timing change
var timingChanges = Beatmap.TimingInfo.ControlPoints.Select(c =>
{
ControlPoint t = c.Clone();
if (c.TimingChange)
firstTimingChange = c;
else
t.BeatLength = firstTimingChange.BeatLength;
return t;
});
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
// Perform some post processing of the timing changes
timingChanges = timingChanges
// Collapse sections after the last hit object
.Where(s => s.Time <= lastObjectTime)
// Collapse sections with the same start time
.GroupBy(s => s.Time).Select(g => g.Last()).OrderBy(s => s.Time)
// Collapse sections with the same beat length
.GroupBy(s => s.BeatLength * s.SpeedMultiplier).Select(g => g.First())
.ToList();
return new ManiaPlayfield(Columns ?? (int)Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize), timingChanges)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
// Invert by default for now (should be moved to config/skin later)
Scale = new Vector2(1, -1)
};
} }
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
protected override BeatmapConverter<ManiaBaseHit> CreateBeatmapConverter() => new ManiaBeatmapConverter(); protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter();
protected override Playfield<ManiaBaseHit, ManiaJudgement> CreatePlayfield() => new ManiaPlayfield(columns); protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h)
{
var holdNote = h as HoldNote;
if (holdNote != null)
return new DrawableHoldNote(holdNote);
protected override DrawableHitObject<ManiaBaseHit, ManiaJudgement> GetVisualRepresentation(ManiaBaseHit h) => null; var note = h as Note;
if (note != null)
return new DrawableNote(note);
return null;
}
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f);
} }
} }

View File

@ -8,29 +8,253 @@ using osu.Game.Rulesets.UI;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Graphics.Containers;
using System;
using osu.Game.Graphics;
using osu.Framework.Allocation;
using OpenTK.Input;
using System.Linq;
using System.Collections.Generic;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Timing;
using osu.Framework.Input;
using osu.Game.Beatmaps.Timing;
using osu.Framework.Graphics.Transforms;
using osu.Framework.MathUtils;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
{ {
public class ManiaPlayfield : Playfield<ManiaBaseHit, ManiaJudgement> public class ManiaPlayfield : Playfield<ManiaHitObject, ManiaJudgement>
{ {
public ManiaPlayfield(int columns) public const float HIT_TARGET_POSITION = 50;
private const float time_span_default = 5000;
private const float time_span_min = 10;
private const float time_span_max = 50000;
private const float time_span_step = 200;
/// <summary>
/// Default column keys, expanding outwards from the middle as more column are added.
/// E.g. 2 columns use FJ, 4 columns use DFJK, 6 use SDFJKL, etc...
/// </summary>
private static readonly Key[] default_keys = { Key.A, Key.S, Key.D, Key.F, Key.J, Key.K, Key.L, Key.Semicolon };
private SpecialColumnPosition specialColumnPosition;
/// <summary>
/// The style to use for the special column.
/// </summary>
public SpecialColumnPosition SpecialColumnPosition
{ {
Size = new Vector2(0.8f, 1f); get { return specialColumnPosition; }
Anchor = Anchor.BottomCentre; set
Origin = Anchor.BottomCentre; {
if (IsLoaded)
throw new InvalidOperationException($"Setting {nameof(SpecialColumnPosition)} after the playfield is loaded requires re-creating the playfield.");
specialColumnPosition = value;
}
}
Add(new Box { RelativeSizeAxes = Axes.Both, Alpha = 0.5f }); public readonly FlowContainer<Column> Columns;
for (int i = 0; i < columns; i++) private readonly ControlPointContainer barlineContainer;
Add(new Box
private List<Color4> normalColumnColours = new List<Color4>();
private Color4 specialColumnColour;
private readonly int columnCount;
public ManiaPlayfield(int columnCount, IEnumerable<ControlPoint> timingChanges)
{
this.columnCount = columnCount;
if (columnCount <= 0)
throw new ArgumentException("Can't have zero or fewer columns.");
Children = new Drawable[]
{
new Container
{ {
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Size = new Vector2(2, 1), AutoSizeAxes = Axes.X,
RelativePositionAxes = Axes.Both, Masking = true,
Position = new Vector2((float)i / columns, 0), Children = new Drawable[]
Alpha = 0.5f, {
Colour = Color4.Black new Box
}); {
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
},
Columns = new FillFlowContainer<Column>
{
Name = "Columns",
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Direction = FillDirection.Horizontal,
Padding = new MarginPadding { Left = 1, Right = 1 },
Spacing = new Vector2(1, 0)
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = HIT_TARGET_POSITION },
Children = new[]
{
barlineContainer = new ControlPointContainer(timingChanges)
{
Name = "Bar lines",
RelativeSizeAxes = Axes.Both,
}
}
}
}
}
};
for (int i = 0; i < columnCount; i++)
Columns.Add(new Column(timingChanges));
TimeSpan = time_span_default;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
normalColumnColours = new List<Color4>
{
colours.RedDark,
colours.GreenDark
};
specialColumnColour = colours.BlueDark;
// Set the special column + colour + key
for (int i = 0; i < columnCount; i++)
{
Column column = Columns.Children.ElementAt(i);
column.IsSpecial = isSpecialColumn(i);
if (!column.IsSpecial)
continue;
column.Key = Key.Space;
column.AccentColour = specialColumnColour;
}
var nonSpecialColumns = Columns.Children.Where(c => !c.IsSpecial).ToList();
// We'll set the colours of the non-special columns in a separate loop, because the non-special
// column colours are mirrored across their centre and special styles mess with this
for (int i = 0; i < Math.Ceiling(nonSpecialColumns.Count / 2f); i++)
{
Color4 colour = normalColumnColours[i % normalColumnColours.Count];
nonSpecialColumns[i].AccentColour = colour;
nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
}
// We'll set the keys for non-special columns in another separate loop because it's not mirrored like the above colours
// Todo: This needs to go when we get to bindings and use Button1, ..., ButtonN instead
for (int i = 0; i < nonSpecialColumns.Count; i++)
{
Column column = nonSpecialColumns[i];
int keyOffset = default_keys.Length / 2 - nonSpecialColumns.Count / 2 + i;
if (keyOffset >= 0 && keyOffset < default_keys.Length)
column.Key = default_keys[keyOffset];
else
// There is no default key defined for this column. Let's set this to Unknown for now
// however note that this will be gone after bindings are in place
column.Key = Key.Unknown;
}
}
/// <summary>
/// Whether the column index is a special column for this playfield.
/// </summary>
/// <param name="column">The 0-based column index.</param>
/// <returns>Whether the column is a special column.</returns>
private bool isSpecialColumn(int column)
{
switch (SpecialColumnPosition)
{
default:
case SpecialColumnPosition.Normal:
return columnCount % 2 == 1 && column == columnCount / 2;
case SpecialColumnPosition.Left:
return column == 0;
case SpecialColumnPosition.Right:
return column == columnCount - 1;
}
}
public override void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> h) => Columns.Children.ElementAt(h.HitObject.Column).Add(h);
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (state.Keyboard.ControlPressed)
{
switch (args.Key)
{
case Key.Minus:
transformTimeSpanTo(TimeSpan + time_span_step, 200, EasingTypes.OutQuint);
break;
case Key.Plus:
transformTimeSpanTo(TimeSpan - time_span_step, 200, EasingTypes.OutQuint);
break;
}
}
return false;
}
private double timeSpan;
/// <summary>
/// The amount of time which the length of the playfield spans.
/// </summary>
public double TimeSpan
{
get { return timeSpan; }
set
{
if (timeSpan == value)
return;
timeSpan = value;
timeSpan = MathHelper.Clamp(timeSpan, time_span_min, time_span_max);
barlineContainer.TimeSpan = value;
Columns.Children.ForEach(c => c.ControlPointContainer.TimeSpan = value);
}
}
private void transformTimeSpanTo(double newTimeSpan, double duration = 0, EasingTypes easing = EasingTypes.None)
{
TransformTo(() => TimeSpan, newTimeSpan, duration, easing, new TransformTimeSpan());
}
private class TransformTimeSpan : Transform<double>
{
public override double CurrentValue
{
get
{
double time = Time?.Current ?? 0;
if (time < StartTime) return StartValue;
if (time >= EndTime) return EndValue;
return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
}
}
public override void Apply(Drawable d)
{
base.Apply(d);
var p = (ManiaPlayfield)d;
p.TimeSpan = CurrentValue;
}
} }
} }
} }

View File

@ -0,0 +1,21 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Rulesets.Mania.UI
{
public enum SpecialColumnPosition
{
/// <summary>
/// The special column will lie in the center of the columns.
/// </summary>
Normal,
/// <summary>
/// The special column will lie to the left of the columns.
/// </summary>
Left,
/// <summary>
/// The special column will lie to the right of the columns.
/// </summary>
Right
}
}

View File

@ -47,19 +47,33 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Beatmaps\Patterns\Legacy\PatternGenerator.cs" />
<Compile Include="Beatmaps\Patterns\PatternGenerator.cs" />
<Compile Include="Beatmaps\Patterns\Legacy\PatternType.cs" />
<Compile Include="Beatmaps\ManiaBeatmapConverter.cs" /> <Compile Include="Beatmaps\ManiaBeatmapConverter.cs" />
<Compile Include="Beatmaps\Patterns\Pattern.cs" />
<Compile Include="MathUtils\FastRandom.cs" />
<Compile Include="Judgements\HitWindows.cs" />
<Compile Include="Judgements\ManiaJudgement.cs" /> <Compile Include="Judgements\ManiaJudgement.cs" />
<Compile Include="ManiaDifficultyCalculator.cs" /> <Compile Include="ManiaDifficultyCalculator.cs" />
<Compile Include="Objects\Drawables\DrawableHoldNote.cs" />
<Compile Include="Objects\Drawables\DrawableManiaHitObject.cs" />
<Compile Include="Objects\Drawables\DrawableNote.cs" />
<Compile Include="Objects\Drawables\Pieces\BodyPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\NotePiece.cs" />
<Compile Include="Objects\Types\IHasColumn.cs" />
<Compile Include="Scoring\ManiaScoreProcessor.cs" /> <Compile Include="Scoring\ManiaScoreProcessor.cs" />
<Compile Include="Objects\Drawable\DrawableNote.cs" />
<Compile Include="Objects\HoldNote.cs" /> <Compile Include="Objects\HoldNote.cs" />
<Compile Include="Objects\ManiaBaseHit.cs" /> <Compile Include="Objects\ManiaHitObject.cs" />
<Compile Include="Objects\Note.cs" /> <Compile Include="Objects\Note.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UI\Column.cs" />
<Compile Include="UI\ManiaHitRenderer.cs" /> <Compile Include="UI\ManiaHitRenderer.cs" />
<Compile Include="UI\ManiaPlayfield.cs" /> <Compile Include="UI\ManiaPlayfield.cs" />
<Compile Include="ManiaRuleset.cs" /> <Compile Include="ManiaRuleset.cs" />
<Compile Include="Mods\ManiaMod.cs" /> <Compile Include="Mods\ManiaMod.cs" />
<Compile Include="UI\SpecialColumnPosition.cs" />
<Compile Include="Timing\ControlPointContainer.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj"> <ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">

View File

@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModAutoplay : ModAutoplay<OsuHitObject> public class OsuModAutoplay : ModAutoplay<OsuHitObject>
{ {
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray(); public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray();
protected override Score CreateReplayScore(Beatmap<OsuHitObject> beatmap) => new Score protected override Score CreateReplayScore(Beatmap<OsuHitObject> beatmap) => new Score
{ {

View File

@ -1,15 +1,16 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 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 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.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Allocation;
using osu.Game.Screens.Ranking;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
@ -18,9 +19,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly Spinner spinner; private readonly Spinner spinner;
private readonly SpinnerDisc disc; private readonly SpinnerDisc disc;
private readonly SpinnerTicks ticks;
private readonly Container mainContainer;
private readonly SpinnerBackground background; private readonly SpinnerBackground background;
private readonly Container circleContainer; private readonly Container circleContainer;
private readonly DrawableHitCircle circle; private readonly CirclePiece circle;
private readonly GlowPiece glow;
private readonly TextAwesome symbol;
private readonly Color4 baseColour = OsuColour.FromHex(@"002c3c");
private readonly Color4 fillColour = OsuColour.FromHex(@"005b7c");
private Color4 normalColour;
private Color4 completeColour;
public DrawableSpinner(Spinner s) : base(s) public DrawableSpinner(Spinner s) : base(s)
{ {
@ -29,57 +43,91 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Origin = Anchor.Centre; Origin = Anchor.Centre;
Position = s.Position; Position = s.Position;
//take up full playfield. RelativeSizeAxes = Axes.Both;
Size = new Vector2(OsuPlayfield.BASE_SIZE.X);
// we are slightly bigger than our parent, to clip the top and bottom of the circle
Height = 1.3f;
spinner = s; spinner = s;
Children = new Drawable[] Children = new Drawable[]
{ {
background = new SpinnerBackground
{
Alpha = 0,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
DiscColour = Color4.Black
},
disc = new SpinnerDisc
{
Alpha = 0,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
DiscColour = AccentColour
},
circleContainer = new Container circleContainer = new Container
{ {
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Children = new [] Children = new Drawable[]
{ {
circle = new DrawableHitCircle(s) glow = new GlowPiece(),
circle = new CirclePiece
{ {
Interactive = false,
Position = Vector2.Zero, Position = Vector2.Zero,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
} },
new RingPiece(),
symbol = new TextAwesome
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
UseFullGlyphHeight = true,
TextSize = 48,
Icon = FontAwesome.fa_asterisk,
Shadow = false,
},
} }
} },
mainContainer = new AspectContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Children = new Drawable[]
{
background = new SpinnerBackground
{
Alpha = 0.6f,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
disc = new SpinnerDisc(spinner)
{
Scale = Vector2.Zero,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
circleContainer.CreateProxy(),
ticks = new SpinnerTicks
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
}
},
}; };
background.Scale = scaleToCircle;
disc.Scale = scaleToCircle;
} }
public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1);
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
{ {
if (Time.Current < HitObject.StartTime) return; if (Time.Current < HitObject.StartTime) return;
disc.ScaleTo(Interpolation.ValueAt(Math.Sqrt(Progress), scaleToCircle, Vector2.One, 0, 1), 100); if (Progress >= 1 && !disc.Complete)
{
if (Progress >= 1)
disc.Complete = true; disc.Complete = true;
const float duration = 200;
disc.FadeAccent(completeColour, duration);
background.FadeAccent(completeColour, duration);
background.FadeOut(duration);
circle.FadeColour(completeColour, duration);
glow.FadeColour(completeColour, duration);
}
if (!userTriggered && Time.Current >= spinner.EndTime) if (!userTriggered && Time.Current >= spinner.EndTime)
{ {
if (Progress >= 1) if (Progress >= 1)
@ -106,30 +154,48 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
} }
} }
private Vector2 scaleToCircle => circle.Scale * circle.DrawWidth / DrawWidth * 0.95f; [BackgroundDependencyLoader]
private void load(OsuColour colours)
{
normalColour = baseColour;
private const float spins_per_minute_needed = 100 + 5 * 15; //TODO: read per-map OD and place it on the 5 background.AccentColour = normalColour;
private float rotationsNeeded => (float)(spins_per_minute_needed * (spinner.EndTime - spinner.StartTime) / 60000f); completeColour = colours.YellowLight.Opacity(0.75f);
public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / rotationsNeeded, 0, 1); disc.AccentColour = fillColour;
circle.Colour = colours.BlueDark;
glow.Colour = colours.BlueDark;
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
circle.Rotation = disc.Rotation;
ticks.Rotation = disc.Rotation;
float relativeCircleScale = spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight;
disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, EasingTypes.OutQuint);
symbol.RotateTo(disc.Rotation / 2, 500, EasingTypes.OutQuint);
}
protected override void UpdatePreemptState() protected override void UpdatePreemptState()
{ {
base.UpdatePreemptState(); base.UpdatePreemptState();
circleContainer.ScaleTo(1, 400, EasingTypes.OutElastic); circleContainer.ScaleTo(spinner.Scale * 0.3f);
circleContainer.ScaleTo(spinner.Scale, TIME_PREEMPT / 1.4f, EasingTypes.OutQuint);
background.Delay(TIME_PREEMPT - 500); disc.RotateTo(-720);
symbol.RotateTo(-720);
background.ScaleTo(scaleToCircle * 1.2f, 400, EasingTypes.OutQuint); mainContainer.ScaleTo(0);
background.FadeIn(200); mainContainer.ScaleTo(spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, EasingTypes.OutQuint);
background.Delay(400); mainContainer.Delay(TIME_PREEMPT - 150);
background.ScaleTo(1, 250, EasingTypes.OutQuint); mainContainer.ScaleTo(1, 500, EasingTypes.OutQuint);
disc.Delay(TIME_PREEMPT - 50);
disc.FadeIn(200);
} }
protected override void UpdateCurrentState(ArmedState state) protected override void UpdateCurrentState(ArmedState state)

View File

@ -16,7 +16,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
private readonly Sprite disc; private readonly Sprite disc;
public Func<bool> Hit; public Func<bool> Hit;
public CirclePiece() public CirclePiece()

View File

@ -97,8 +97,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config) private void load(OsuConfigManager config)
{ {
snakingIn = config.GetBindable<bool>(OsuConfig.SnakingInSliders); snakingIn = config.GetBindable<bool>(OsuSetting.SnakingInSliders);
snakingOut = config.GetBindable<bool>(OsuConfig.SnakingOutSliders); snakingOut = config.GetBindable<bool>(OsuSetting.SnakingOutSliders);
reloadTexture(); reloadTexture();
} }

View File

@ -1,10 +1,55 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 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 OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
public class SpinnerBackground : SpinnerDisc public class SpinnerBackground : CircularContainer, IHasAccentColour
{ {
public override bool HandleInput => false; public override bool HandleInput => false;
protected Box Disc;
public Color4 AccentColour
{
get
{
return Disc.Colour;
}
set
{
Disc.Colour = value;
EdgeEffect = new EdgeEffect
{
Hollow = true,
Type = EdgeEffectType.Glow,
Radius = 40,
Colour = value,
};
}
}
public SpinnerBackground()
{
RelativeSizeAxes = Axes.Both;
Masking = true;
Children = new Drawable[]
{
Disc = new Box
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 1,
},
};
}
} }
} }

View File

@ -2,13 +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; using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms; using osu.Framework.Graphics.Transforms;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Graphics; using osu.Game.Graphics;
@ -17,104 +12,31 @@ using OpenTK.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
public class SpinnerDisc : CircularContainer public class SpinnerDisc : CircularContainer, IHasAccentColour
{ {
protected Sprite Disc; private readonly Spinner spinner;
public SRGBColour DiscColour public Color4 AccentColour
{ {
get { return Disc.Colour; } get { return background.AccentColour; }
set { Disc.Colour = value; } set { background.AccentColour = value; }
} }
private Color4 completeColour; private readonly SpinnerBackground background;
[BackgroundDependencyLoader] private const float idle_alpha = 0.2f;
private void load(OsuColour colours) private const float tracking_alpha = 0.4f;
public SpinnerDisc(Spinner s)
{ {
completeColour = colours.YellowLight.Opacity(0.8f); spinner = s;
Masking = true;
}
private class SpinnerBorder : Container
{
public SpinnerBorder()
{
Origin = Anchor.Centre;
Anchor = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
layout();
}
private int lastLayoutDotCount;
private void layout()
{
int count = (int)(MathHelper.Pi * ScreenSpaceDrawQuad.Width / 9);
if (count == lastLayoutDotCount) return;
lastLayoutDotCount = count;
while (Children.Count() < count)
{
Add(new CircularContainer
{
Colour = Color4.White,
RelativePositionAxes = Axes.Both,
Masking = true,
Origin = Anchor.Centre,
Size = new Vector2(1 / ScreenSpaceDrawQuad.Width * 2000),
Children = new[]
{
new Box
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
}
}
});
}
var size = new Vector2(1 / ScreenSpaceDrawQuad.Width * 2000);
int i = 0;
foreach (var d in Children)
{
d.Size = size;
d.Position = new Vector2(
0.5f + (float)Math.Sin((float)i / count * 2 * MathHelper.Pi) / 2,
0.5f + (float)Math.Cos((float)i / count * 2 * MathHelper.Pi) / 2
);
i++;
}
}
protected override void Update()
{
base.Update();
layout();
}
}
public SpinnerDisc()
{
AlwaysReceiveInput = true; AlwaysReceiveInput = true;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
Children = new Drawable[] Children = new Drawable[]
{ {
Disc = new Box background = new SpinnerBackground { Alpha = idle_alpha },
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f,
},
new SpinnerBorder()
}; };
} }
@ -125,10 +47,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
set set
{ {
if (value == tracking) return; if (value == tracking) return;
tracking = value; tracking = value;
Disc.FadeTo(tracking ? 0.5f : 0.2f, 100); background.FadeTo(tracking ? tracking_alpha : idle_alpha, 100);
} }
} }
@ -139,31 +60,28 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
set set
{ {
if (value == complete) return; if (value == complete) return;
complete = value; complete = value;
Disc.FadeColour(completeColour, 200);
updateCompleteTick(); updateCompleteTick();
} }
} }
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{ {
Tracking = true; Tracking |= state.Mouse.HasMainButtonPressed;
return base.OnMouseDown(state, args); return base.OnMouseDown(state, args);
} }
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{ {
Tracking = false; Tracking &= state.Mouse.HasMainButtonPressed;
return base.OnMouseUp(state, args); return base.OnMouseUp(state, args);
} }
protected override bool OnMouseMove(InputState state) protected override bool OnMouseMove(InputState state)
{ {
Tracking |= state.Mouse.HasMainButtonPressed; Tracking |= state.Mouse.HasMainButtonPressed;
mousePosition = state.Mouse.Position; mousePosition = Parent.ToLocalSpace(state.Mouse.NativeState.Position);
return base.OnMouseMove(state); return base.OnMouseMove(state);
} }
@ -177,13 +95,24 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private bool updateCompleteTick() => completeTick != (completeTick = (int)(RotationAbsolute / 360)); private bool updateCompleteTick() => completeTick != (completeTick = (int)(RotationAbsolute / 360));
private bool rotationTransferred;
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
var thisAngle = -(float)MathHelper.RadiansToDegrees(Math.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2)); var thisAngle = -(float)MathHelper.RadiansToDegrees(Math.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2));
if (tracking)
bool validAndTracking = tracking && spinner.StartTime <= Time.Current && spinner.EndTime > Time.Current;
if (validAndTracking)
{ {
if (!rotationTransferred)
{
currentRotation = Rotation * 2;
rotationTransferred = true;
}
if (thisAngle - lastAngle > 180) if (thisAngle - lastAngle > 180)
lastAngle += 360; lastAngle += 360;
else if (lastAngle - thisAngle > 180) else if (lastAngle - thisAngle > 180)
@ -192,17 +121,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
currentRotation += thisAngle - lastAngle; currentRotation += thisAngle - lastAngle;
RotationAbsolute += Math.Abs(thisAngle - lastAngle); RotationAbsolute += Math.Abs(thisAngle - lastAngle);
} }
lastAngle = thisAngle; lastAngle = thisAngle;
if (Complete && updateCompleteTick()) if (Complete && updateCompleteTick())
{ {
Disc.Flush(flushType: typeof(TransformAlpha)); background.Flush(flushType: typeof(TransformAlpha));
Disc.FadeTo(0.75f, 30, EasingTypes.OutExpo); background.FadeTo(tracking_alpha + 0.2f, 60, EasingTypes.OutExpo);
Disc.Delay(30); background.Delay(60);
Disc.FadeTo(0.5f, 250, EasingTypes.OutQuint); background.FadeTo(tracking_alpha, 250, EasingTypes.OutQuint);
} }
RotateTo(currentRotation, 100, EasingTypes.OutExpo); RotateTo(currentRotation / 2, validAndTracking ? 500 : 1500, EasingTypes.OutExpo);
} }
} }
} }

View File

@ -0,0 +1,57 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class SpinnerTicks : Container
{
public SpinnerTicks()
{
Origin = Anchor.Centre;
Anchor = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
const int count = 18;
for (int i = 0; i < count; i++)
{
Add(new Container
{
Colour = Color4.Black,
Alpha = 0.4f,
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Radius = 10,
Colour = Color4.Gray.Opacity(0.2f),
},
RelativePositionAxes = Axes.Both,
Masking = true,
CornerRadius = 5,
Size = new Vector2(60, 10),
Origin = Anchor.Centre,
Position = new Vector2(
0.5f + (float)Math.Sin((float)i / count * 2 * MathHelper.Pi) / 2 * 0.86f,
0.5f + (float)Math.Cos((float)i / count * 2 * MathHelper.Pi) / 2 * 0.86f
),
Rotation = -(float)i / count * 360 + 90,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
}
}
});
}
}
}
}

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 osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.Timing;
using osu.Game.Database;
namespace osu.Game.Rulesets.Osu.Objects namespace osu.Game.Rulesets.Osu.Objects
{ {
@ -10,6 +12,18 @@ namespace osu.Game.Rulesets.Osu.Objects
public double EndTime { get; set; } public double EndTime { get; set; }
public double Duration => EndTime - StartTime; public double Duration => EndTime - StartTime;
/// <summary>
/// Number of spins required to finish the spinner without miss.
/// </summary>
public int SpinsRequired { get; protected set; } = 1;
public override bool NewCombo => true; public override bool NewCombo => true;
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{
base.ApplyDefaults(timing, difficulty);
SpinsRequired = (int)(Duration / 1000 * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5));
}
} }
} }

View File

@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu
{ {
public class OsuRuleset : Ruleset public class OsuRuleset : Ruleset
{ {
public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap) => new OsuHitRenderer(beatmap); public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new OsuHitRenderer(beatmap, isForCurrentRuleset);
public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new[] public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new[]
{ {

View File

@ -91,25 +91,25 @@ namespace osu.Game.Rulesets.Osu.Replays
// Make the cursor stay at a hitObject as long as possible (mainly for autopilot). // Make the cursor stay at a hitObject as long as possible (mainly for autopilot).
if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
} }
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
} }
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50) else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
} }
} }
private void addHitObjectReplay(OsuHitObject h) private void addHitObjectReplay(OsuHitObject h)
{ {
// Default values for circles/sliders // Default values for circles/sliders
Vector2 startPosition = h.Position; Vector2 startPosition = h.StackedPosition;
EasingTypes easing = preferredEasing; EasingTypes easing = preferredEasing;
float spinnerDirection = -1; float spinnerDirection = -1;
@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Osu.Replays
// TODO: Why do we delay 1 ms if the object is a spinner? There already is KEY_UP_DELAY from hEndTime. // TODO: Why do we delay 1 ms if the object is a spinner? There already is KEY_UP_DELAY from hEndTime.
double hEndTime = ((h as IHasEndTime)?.EndTime ?? h.StartTime) + KEY_UP_DELAY; double hEndTime = ((h as IHasEndTime)?.EndTime ?? h.StartTime) + KEY_UP_DELAY;
int endDelay = h is Spinner ? 1 : 0; int endDelay = h is Spinner ? 1 : 0;
ReplayFrame endFrame = new ReplayFrame(hEndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, ReplayButtonState.None); ReplayFrame endFrame = new ReplayFrame(hEndTime + endDelay, h.StackedEndPosition.X, h.StackedEndPosition.Y, ReplayButtonState.None);
// Decrement because we want the previous frame, not the next one // Decrement because we want the previous frame, not the next one
int index = FindInsertionIndex(startFrame) - 1; int index = FindInsertionIndex(startFrame) - 1;

View File

@ -58,8 +58,8 @@ namespace osu.Game.Rulesets.Osu.Replays
{ {
public int Compare(ReplayFrame f1, ReplayFrame f2) public int Compare(ReplayFrame f1, ReplayFrame f2)
{ {
if (f1 == null) throw new NullReferenceException($@"{nameof(f1)} cannot be null"); if (f1 == null) throw new ArgumentNullException(nameof(f1));
if (f2 == null) throw new NullReferenceException($@"{nameof(f2)} cannot be null"); if (f2 == null) throw new ArgumentNullException(nameof(f2));
return f1.Time.CompareTo(f2.Time); return f1.Time.CompareTo(f2.Time);
} }

View File

@ -18,8 +18,8 @@ namespace osu.Game.Rulesets.Osu.UI
{ {
public class OsuHitRenderer : HitRenderer<OsuHitObject, OsuJudgement> public class OsuHitRenderer : HitRenderer<OsuHitObject, OsuJudgement>
{ {
public OsuHitRenderer(WorkingBeatmap beatmap) public OsuHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(beatmap) : base(beatmap, isForCurrentRuleset)
{ {
} }

View File

@ -64,6 +64,7 @@
<Compile Include="Objects\Drawables\Pieces\RingPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\RingPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\SliderBouncer.cs" /> <Compile Include="Objects\Drawables\Pieces\SliderBouncer.cs" />
<Compile Include="Objects\Drawables\Pieces\SpinnerDisc.cs" /> <Compile Include="Objects\Drawables\Pieces\SpinnerDisc.cs" />
<Compile Include="Objects\Drawables\Pieces\SpinnerTicks.cs" />
<Compile Include="Objects\Drawables\Pieces\TrianglesPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\TrianglesPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\SliderBall.cs" /> <Compile Include="Objects\Drawables\Pieces\SliderBall.cs" />
<Compile Include="Objects\Drawables\Pieces\SliderBody.cs" /> <Compile Include="Objects\Drawables\Pieces\SliderBody.cs" />
@ -103,9 +104,7 @@
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<ItemGroup> <ItemGroup />
<Folder Include="Replays\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -41,13 +41,13 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(HitObject) }; protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(HitObject) };
protected override Beatmap<TaikoHitObject> ConvertBeatmap(Beatmap original) protected override Beatmap<TaikoHitObject> ConvertBeatmap(Beatmap original, bool isForCurrentRuleset)
{ {
// Rewrite the beatmap info to add the slider velocity multiplier // Rewrite the beatmap info to add the slider velocity multiplier
BeatmapInfo info = original.BeatmapInfo.DeepClone<BeatmapInfo>(); BeatmapInfo info = original.BeatmapInfo.DeepClone();
info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier; info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier;
Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original); Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original, isForCurrentRuleset);
// Post processing step to transform hit objects with the same start time into strong hits // Post processing step to transform hit objects with the same start time into strong hits
converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x => converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x =>

View File

@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
protected override Score CreateReplayScore(Beatmap<TaikoHitObject> beatmap) => new Score protected override Score CreateReplayScore(Beatmap<TaikoHitObject> beatmap) => new Score
{ {
User = new User { Username = "mekkadosu!" }, User = new User { Username = "mekkadosu!" },
Replay = new TaikoAutoReplay(beatmap).Generate(), Replay = new TaikoAutoGenerator(beatmap).Generate(),
}; };
} }
} }

View File

@ -2,7 +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 System; using System;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
{ {

View File

@ -7,16 +7,24 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
using osu.Game.Users;
namespace osu.Game.Rulesets.Taiko.Replays namespace osu.Game.Rulesets.Taiko.Replays
{ {
public class TaikoAutoReplay : AutoGenerator<TaikoHitObject> public class TaikoAutoGenerator : AutoGenerator<TaikoHitObject>
{ {
private const double swell_hit_speed = 50; private const double swell_hit_speed = 50;
public TaikoAutoReplay(Beatmap<TaikoHitObject> beatmap) public TaikoAutoGenerator(Beatmap<TaikoHitObject> beatmap)
: base(beatmap) : base(beatmap)
{ {
Replay = new Replay
{
User = new User
{
Username = @"Autoplay",
}
};
} }
protected Replay Replay; protected Replay Replay;
@ -101,7 +109,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
Frames.Add(new ReplayFrame(h.StartTime, null, null, button)); Frames.Add(new ReplayFrame(h.StartTime, null, null, button));
} }
else else
throw new Exception("Unknown hit object type."); throw new InvalidOperationException("Unknown hit object type.");
Frames.Add(new ReplayFrame(endTime + KEY_UP_DELAY, null, null, ReplayButtonState.None)); Frames.Add(new ReplayFrame(endTime + KEY_UP_DELAY, null, null, ReplayButtonState.None));

View File

@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko
{ {
public class TaikoRuleset : Ruleset public class TaikoRuleset : Ruleset
{ {
public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap) => new TaikoHitRenderer(beatmap); public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new TaikoHitRenderer(beatmap, isForCurrentRuleset);
public override IEnumerable<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {

View File

@ -22,8 +22,8 @@ namespace osu.Game.Rulesets.Taiko.UI
{ {
public class TaikoHitRenderer : HitRenderer<TaikoHitObject, TaikoJudgement> public class TaikoHitRenderer : HitRenderer<TaikoHitObject, TaikoJudgement>
{ {
public TaikoHitRenderer(WorkingBeatmap beatmap) public TaikoHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(beatmap) : base(beatmap, isForCurrentRuleset)
{ {
} }

View File

@ -13,7 +13,6 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics.Primitives;
using System.Linq; using System.Linq;
using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects.Drawables;
using System; using System;
@ -54,33 +53,33 @@ namespace osu.Game.Rulesets.Taiko.UI
{ {
AddInternal(new Drawable[] AddInternal(new Drawable[]
{ {
rightBackgroundContainer = new Container
{
Name = "Transparent playfield background",
RelativeSizeAxes = Axes.Both,
BorderThickness = 2,
Masking = true,
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.2f),
Radius = 5,
},
Children = new Drawable[]
{
rightBackground = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.6f
},
}
},
new ScaleFixContainer new ScaleFixContainer
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = DEFAULT_PLAYFIELD_HEIGHT, Height = DEFAULT_PLAYFIELD_HEIGHT,
Children = new[] Children = new[]
{ {
rightBackgroundContainer = new Container
{
Name = "Transparent playfield background",
RelativeSizeAxes = Axes.Both,
BorderThickness = 2,
Masking = true,
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.2f),
Radius = 5,
},
Children = new Drawable[]
{
rightBackground = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.6f
},
}
},
new Container new Container
{ {
Name = "Transparent playfield elements", Name = "Transparent playfield elements",

View File

@ -79,7 +79,7 @@
<Compile Include="Objects\RimHit.cs" /> <Compile Include="Objects\RimHit.cs" />
<Compile Include="Objects\Swell.cs" /> <Compile Include="Objects\Swell.cs" />
<Compile Include="Replays\TaikoFramedReplayInputHandler.cs" /> <Compile Include="Replays\TaikoFramedReplayInputHandler.cs" />
<Compile Include="Replays\TaikoAutoReplay.cs" /> <Compile Include="Replays\TaikoAutoGenerator.cs" />
<Compile Include="Objects\TaikoHitObject.cs" /> <Compile Include="Objects\TaikoHitObject.cs" />
<Compile Include="Objects\TaikoHitObjectDifficulty.cs" /> <Compile Include="Objects\TaikoHitObjectDifficulty.cs" />
<Compile Include="TaikoDifficultyCalculator.cs" /> <Compile Include="TaikoDifficultyCalculator.cs" />

View File

@ -135,14 +135,13 @@ namespace osu.Game.Tests.Beatmaps.IO
waitAction = () => waitAction = () =>
{ {
while ((resultBeatmaps = host.Dependencies.Get<BeatmapDatabase>() while ((resultBeatmaps = host.Dependencies.Get<BeatmapDatabase>()
.Query<BeatmapInfo>().Where(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0)).Count() != 12) .GetAllWithChildren<BeatmapInfo>(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0)).Count() != 12)
Thread.Sleep(50); Thread.Sleep(50);
}; };
Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout), Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout),
@"Beatmaps did not import to the database in allocated time"); @"Beatmaps did not import to the database in allocated time");
//fetch children and check we can load from the post-storage path...
var set = host.Dependencies.Get<BeatmapDatabase>().GetChildren(resultSets.First()); var set = host.Dependencies.Get<BeatmapDatabase>().GetChildren(resultSets.First());
Assert.IsTrue(set.Beatmaps.Count == resultBeatmaps.Count(), Assert.IsTrue(set.Beatmaps.Count == resultBeatmaps.Count(),

View File

@ -11,9 +11,8 @@ namespace osu.Game.Audio
{ {
} }
public SampleInfoList(IEnumerable<SampleInfo> elements) public SampleInfoList(IEnumerable<SampleInfo> elements) : base(elements)
{ {
AddRange(elements);
} }
} }
} }

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 OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Beatmaps.Events;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
@ -17,6 +18,7 @@ namespace osu.Game.Beatmaps
{ {
public BeatmapInfo BeatmapInfo; public BeatmapInfo BeatmapInfo;
public TimingInfo TimingInfo = new TimingInfo(); public TimingInfo TimingInfo = new TimingInfo();
public EventInfo EventInfo = new EventInfo();
public readonly List<Color4> ComboColors = new List<Color4> public readonly List<Color4> ComboColors = new List<Color4>
{ {
new Color4(17, 136, 170, 255), new Color4(17, 136, 170, 255),
@ -40,6 +42,7 @@ namespace osu.Game.Beatmaps
{ {
BeatmapInfo = original?.BeatmapInfo ?? BeatmapInfo; BeatmapInfo = original?.BeatmapInfo ?? BeatmapInfo;
TimingInfo = original?.TimingInfo ?? TimingInfo; TimingInfo = original?.TimingInfo ?? TimingInfo;
EventInfo = original?.EventInfo ?? EventInfo;
ComboColors = original?.ComboColors ?? ComboColors; ComboColors = original?.ComboColors ?? ComboColors;
} }
} }

View File

@ -34,7 +34,7 @@ namespace osu.Game.Beatmaps
protected DifficultyCalculator(Beatmap beatmap) protected DifficultyCalculator(Beatmap beatmap)
{ {
Objects = CreateBeatmapConverter().Convert(beatmap).HitObjects; Objects = CreateBeatmapConverter().Convert(beatmap, true).HitObjects;
foreach (var h in Objects) foreach (var h in Objects)
h.ApplyDefaults(beatmap.TimingInfo, beatmap.BeatmapInfo.Difficulty); h.ApplyDefaults(beatmap.TimingInfo, beatmap.BeatmapInfo.Difficulty);

View File

@ -5,7 +5,6 @@ using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Graphics; using osu.Game.Graphics;

View File

@ -9,7 +9,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
@ -51,14 +50,12 @@ namespace osu.Game.Beatmaps.Drawables
title = new OsuSpriteText title = new OsuSpriteText
{ {
Font = @"Exo2.0-BoldItalic", Font = @"Exo2.0-BoldItalic",
Text = beatmap.BeatmapSetInfo.Metadata.Title,
TextSize = 22, TextSize = 22,
Shadow = true, Shadow = true,
}, },
artist = new OsuSpriteText artist = new OsuSpriteText
{ {
Font = @"Exo2.0-SemiBoldItalic", Font = @"Exo2.0-SemiBoldItalic",
Text = beatmap.BeatmapSetInfo.Metadata.Artist,
TextSize = 17, TextSize = 17,
Shadow = true, Shadow = true,
}, },
@ -81,8 +78,8 @@ namespace osu.Game.Beatmaps.Drawables
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(LocalisationEngine localisation) private void load(LocalisationEngine localisation)
{ {
title.Current = localisation.GetUnicodePreference(beatmap.BeatmapSetInfo.Metadata.TitleUnicode, beatmap.BeatmapSetInfo.Metadata.Title); title.Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title);
artist.Current = localisation.GetUnicodePreference(beatmap.BeatmapSetInfo.Metadata.ArtistUnicode, beatmap.BeatmapSetInfo.Metadata.Artist); artist.Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist);
} }
private class PanelBackground : BufferedContainer private class PanelBackground : BufferedContainer

View File

@ -3,14 +3,11 @@
namespace osu.Game.Beatmaps.Events namespace osu.Game.Beatmaps.Events
{ {
public enum EventType public class BackgroundEvent : Event
{ {
Background = 0, /// <summary>
Video = 1, /// The file name.
Break = 2, /// </summary>
Colour = 3, public string Filename;
Sprite = 4,
Sample = 5,
Animation = 6
} }
} }

View File

@ -0,0 +1,28 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Beatmaps.Events
{
public class BreakEvent : Event
{
/// <summary>
/// The minimum duration required for a break to have any effect.
/// </summary>
private const double min_break_duration = 650;
/// <summary>
/// The break end time.
/// </summary>
public double EndTime;
/// <summary>
/// The duration of the break.
/// </summary>
public double Duration => EndTime - StartTime;
/// <summary>
/// Whether the break has any effect. Breaks that are too short are culled before they reach the EventInfo.
/// </summary>
public bool HasEffect => Duration >= min_break_duration;
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Beatmaps.Events
{
public abstract class Event
{
/// <summary>
/// The event start time.
/// </summary>
public double StartTime;
}
}

View File

@ -0,0 +1,33 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Beatmaps.Events
{
public class EventInfo
{
/// <summary>
/// All the background events.
/// </summary>
public readonly List<BackgroundEvent> Backgrounds = new List<BackgroundEvent>();
/// <summary>
/// All the break events.
/// </summary>
public readonly List<BreakEvent> Breaks = new List<BreakEvent>();
/// <summary>
/// Total duration of all breaks.
/// </summary>
public double TotalBreakTime => Breaks.Sum(b => b.Duration);
/// <summary>
/// Retrieves the active background at a time.
/// </summary>
/// <param name="time">The time to retrieve the background at.</param>
/// <returns>The background.</returns>
public BackgroundEvent BackgroundAt(double time) => Backgrounds.FirstOrDefault(b => b.StartTime <= time);
}
}

View File

@ -11,7 +11,7 @@ namespace osu.Game.Beatmaps.Formats
{ {
public abstract class BeatmapDecoder public abstract class BeatmapDecoder
{ {
private static Dictionary<string, Type> decoders { get; } = new Dictionary<string, Type>(); private static readonly Dictionary<string, Type> decoders = new Dictionary<string, Type>();
public static BeatmapDecoder GetDecoder(StreamReader stream) public static BeatmapDecoder GetDecoder(StreamReader stream)
{ {

View File

@ -204,23 +204,42 @@ namespace osu.Game.Beatmaps.Formats
private void handleEvents(Beatmap beatmap, string val) private void handleEvents(Beatmap beatmap, string val)
{ {
if (val.StartsWith(@"//"))
return;
if (val.StartsWith(@" "))
return; // TODO
string[] split = val.Split(','); string[] split = val.Split(',');
EventType type; EventType type;
int intType; if (!Enum.TryParse(split[0], out type))
if (!int.TryParse(split[0], out intType)) throw new InvalidDataException($@"Unknown event type {split[0]}");
// Todo: Implement the rest
switch (type)
{ {
if (!Enum.TryParse(split[0], out type)) case EventType.Video:
throw new InvalidDataException($@"Unknown event type {split[0]}"); case EventType.Background:
string filename = split[2].Trim('"');
beatmap.EventInfo.Backgrounds.Add(new BackgroundEvent
{
StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo),
Filename = filename
});
if (type == EventType.Background)
beatmap.BeatmapInfo.Metadata.BackgroundFile = filename;
break;
case EventType.Break:
var breakEvent = new BreakEvent
{
StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo),
EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo)
};
if (!breakEvent.HasEffect)
return;
beatmap.EventInfo.Breaks.Add(breakEvent);
break;
} }
else
type = (EventType)intType;
// TODO: Parse and store the rest of the event
if (type == EventType.Background)
beatmap.BeatmapInfo.Metadata.BackgroundFile = split[2].Trim('"');
} }
private void handleTimingPoints(Beatmap beatmap, string val) private void handleTimingPoints(Beatmap beatmap, string val)
@ -330,6 +349,9 @@ namespace osu.Game.Beatmaps.Formats
if (string.IsNullOrEmpty(line)) if (string.IsNullOrEmpty(line))
continue; continue;
if (line.StartsWith(" ") || line.StartsWith("_") || line.StartsWith("//"))
continue;
if (line.StartsWith(@"osu file format v")) if (line.StartsWith(@"osu file format v"))
{ {
beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17));
@ -390,5 +412,16 @@ namespace osu.Game.Beatmaps.Formats
Soft = 2, Soft = 2,
Drum = 3 Drum = 3
} }
internal enum EventType
{
Background = 0,
Video = 1,
Break = 2,
Colour = 3,
Sprite = 4,
Sample = 5,
Animation = 6
}
} }
} }

View File

@ -13,11 +13,11 @@ namespace osu.Game.Beatmaps.IO
{ {
private class Reader private class Reader
{ {
public Func<Storage, string, bool> Test { get; set; } public Func<Storage, string, bool> Test;
public Type Type { get; set; } public Type Type;
} }
private static List<Reader> readers { get; } = new List<Reader>(); private static readonly List<Reader> readers = new List<Reader>();
public static ArchiveReader GetReader(Storage storage, string path) public static ArchiveReader GetReader(Storage storage, string path)
{ {
@ -58,11 +58,9 @@ namespace osu.Game.Beatmaps.IO
if (input == null) if (input == null)
return null; return null;
using (MemoryStream ms = new MemoryStream()) byte[] buffer = new byte[input.Length];
{ input.Read(buffer, 0, buffer.Length);
input.CopyTo(ms); return buffer;
return ms.ToArray();
}
} }
} }
} }

View File

@ -7,7 +7,7 @@ namespace osu.Game.Beatmaps.Timing
{ {
public string SampleBank; public string SampleBank;
public int SampleVolume; public int SampleVolume;
public TimeSignatures TimeSignature; public TimeSignatures TimeSignature = TimeSignatures.SimpleQuadruple;
public double Time; public double Time;
public double BeatLength = 500; public double BeatLength = 500;
public double SpeedMultiplier = 1; public double SpeedMultiplier = 1;

View File

@ -18,14 +18,17 @@ namespace osu.Game.Beatmaps
public readonly BeatmapSetInfo BeatmapSetInfo; public readonly BeatmapSetInfo BeatmapSetInfo;
public readonly BeatmapMetadata Metadata;
public readonly Bindable<IEnumerable<Mod>> Mods = new Bindable<IEnumerable<Mod>>(new Mod[] { }); public readonly Bindable<IEnumerable<Mod>> Mods = new Bindable<IEnumerable<Mod>>(new Mod[] { });
public readonly bool WithStoryboard; public readonly bool WithStoryboard;
protected WorkingBeatmap(BeatmapInfo beatmapInfo, BeatmapSetInfo beatmapSetInfo, bool withStoryboard = false) protected WorkingBeatmap(BeatmapInfo beatmapInfo, bool withStoryboard = false)
{ {
BeatmapInfo = beatmapInfo; BeatmapInfo = beatmapInfo;
BeatmapSetInfo = beatmapSetInfo; BeatmapSetInfo = beatmapInfo.BeatmapSet;
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo.Metadata;
WithStoryboard = withStoryboard; WithStoryboard = withStoryboard;
Mods.ValueChanged += mods => applyRateAdjustments(); Mods.ValueChanged += mods => applyRateAdjustments();

View File

@ -1,12 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Configuration
{
public enum ConfineMouseMode
{
Never,
Fullscreen,
Always
}
}

View File

@ -1,183 +1,77 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 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 osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Overlays;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
namespace osu.Game.Configuration namespace osu.Game.Configuration
{ {
public class OsuConfigManager : ConfigManager<OsuConfig> public class OsuConfigManager : ConfigManager<OsuSetting>
{ {
protected override void InitialiseDefaults() protected override void InitialiseDefaults()
{ {
#pragma warning disable CS0612 // Type or member is obsolete // UI/selection defaults
Set(OsuConfig.Username, string.Empty); Set(OsuSetting.Ruleset, 0, 0, int.MaxValue);
Set(OsuConfig.Token, string.Empty); Set(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Details);
Set(OsuConfig.Ruleset, 0, 0, int.MaxValue); Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10);
Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10);
Set(OsuConfig.AudioDevice, string.Empty); Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1);
Set(OsuConfig.SavePassword, false);
Set(OsuConfig.SaveUsername, true);
Set(OsuConfig.MenuCursorSize, 1.0, 0.5f, 2); // Online settings
Set(OsuConfig.GameplayCursorSize, 1.0, 0.5f, 2);
Set(OsuConfig.DimLevel, 0.3, 0, 1);
Set(OsuConfig.MouseDisableButtons, false); Set(OsuSetting.Username, string.Empty);
Set(OsuConfig.MouseDisableWheel, false); Set(OsuSetting.Token, string.Empty);
Set(OsuConfig.SnakingInSliders, true); Set(OsuSetting.SavePassword, false).ValueChanged += val =>
Set(OsuConfig.SnakingOutSliders, true);
Set(OsuConfig.MenuParallax, true);
Set(OsuConfig.MenuVoice, true);
Set(OsuConfig.MenuMusic, true);
Set(OsuConfig.BeatmapDetailTab, BeatmapDetailTab.Details);
Set(OsuConfig.ShowInterface, true);
Set(OsuConfig.KeyOverlay, false);
//todo: implement all settings below this line (remove the Disabled set when doing so).
Set(OsuConfig.AudioOffset, 0, -500.0, 500.0);
Set(OsuConfig.MouseSpeed, 1.0).Disabled = true;
Set(OsuConfig.BeatmapDirectory, @"Songs").Disabled = true; // TODO: use thi.Disabled = trues
Set(OsuConfig.AllowPublicInvites, true).Disabled = true;
Set(OsuConfig.AutoChatHide, true).Disabled = true;
Set(OsuConfig.AutomaticDownload, true).Disabled = true;
Set(OsuConfig.AutomaticDownloadNoVideo, false).Disabled = true;
Set(OsuConfig.BlockNonFriendPM, false).Disabled = true;
Set(OsuConfig.Bloom, false).Disabled = true;
Set(OsuConfig.BloomSoftening, false).Disabled = true;
Set(OsuConfig.BossKeyFirstActivation, true).Disabled = true;
Set(OsuConfig.ChatAudibleHighlight, true).Disabled = true;
Set(OsuConfig.ChatChannels, string.Empty).Disabled = true;
Set(OsuConfig.ChatFilter, false).Disabled = true;
Set(OsuConfig.ChatHighlightName, true).Disabled = true;
Set(OsuConfig.ChatMessageNotification, true).Disabled = true;
Set(OsuConfig.ChatLastChannel, string.Empty).Disabled = true;
Set(OsuConfig.ChatRemoveForeign, false).Disabled = true;
//Set(OsuConfig.ChatSortMode, UserSortMode.Rank).Disabled = true;
Set(OsuConfig.ComboBurst, true).Disabled = true;
Set(OsuConfig.ComboFire, false).Disabled = true;
Set(OsuConfig.ComboFireHeight, 3).Disabled = true;
Set(OsuConfig.ConfirmExit, false).Disabled = true;
Set(OsuConfig.AutoSendNowPlaying, true).Disabled = true;
Set(OsuConfig.AutomaticCursorSizing, false).Disabled = true;
Set(OsuConfig.Display, 1).Disabled = true;
Set(OsuConfig.DisplayCityLocation, false).Disabled = true;
Set(OsuConfig.DistanceSpacingEnabled, true).Disabled = true;
Set(OsuConfig.EditorTip, 0).Disabled = true;
Set(OsuConfig.VideoEditor, true).Disabled = true;
Set(OsuConfig.EditorDefaultSkin, false).Disabled = true;
Set(OsuConfig.EditorSnakingSliders, true).Disabled = true;
Set(OsuConfig.EditorHitAnimations, false).Disabled = true;
Set(OsuConfig.EditorFollowPoints, true).Disabled = true;
Set(OsuConfig.EditorStacking, true).Disabled = true;
Set(OsuConfig.ForceSliderRendering, false).Disabled = true;
Set(OsuConfig.FpsCounter, false).Disabled = true;
Set(OsuConfig.FrameTimeDisplay, false).Disabled = true;
Set(OsuConfig.GuideTips, @"").Disabled = true;
Set(OsuConfig.CursorRipple, false).Disabled = true;
Set(OsuConfig.HighlightWords, string.Empty).Disabled = true;
Set(OsuConfig.HighResolution, false).Disabled = true;
Set(OsuConfig.HitLighting, true).Disabled = true;
Set(OsuConfig.IgnoreBarline, false).Disabled = true;
Set(OsuConfig.IgnoreBeatmapSamples, false).Disabled = true;
Set(OsuConfig.IgnoreBeatmapSkins, false).Disabled = true;
Set(OsuConfig.IgnoreList, string.Empty).Disabled = true;
Set(OsuConfig.AllowNowPlayingHighlights, false).Disabled = true;
Set(OsuConfig.LastVersion, string.Empty).Disabled = true;
Set(OsuConfig.LastVersionPermissionsFailed, string.Empty).Disabled = true;
Set(OsuConfig.LoadSubmittedThread, true).Disabled = true;
Set(OsuConfig.LobbyPlayMode, -1).Disabled = true;
Set(OsuConfig.ShowInterfaceDuringRelax, false).Disabled = true;
Set(OsuConfig.LobbyShowExistingOnly, false).Disabled = true;
Set(OsuConfig.LobbyShowFriendsOnly, false).Disabled = true;
Set(OsuConfig.LobbyShowFull, false).Disabled = true;
Set(OsuConfig.LobbyShowInProgress, true).Disabled = true;
Set(OsuConfig.LobbyShowPassworded, true).Disabled = true;
Set(OsuConfig.LogPrivateMessages, false).Disabled = true;
Set(OsuConfig.LowResolution, false).Disabled = true;
//Set(OsuConfig.ManiaSpeed, SpeedMania.SPEED_DEFAULT, SpeedMania.SPEED_MIN, SpeedMania.SPEED_MAX).Disabled = true;
Set(OsuConfig.UsePerBeatmapManiaSpeed, true).Disabled = true;
Set(OsuConfig.ManiaSpeedBPMScale, true).Disabled = true;
Set(OsuConfig.MenuTip, 0).Disabled = true;
Set(OsuConfig.MouseSpeed, 1, 0.4, 6).Disabled = true;
Set(OsuConfig.ScoreMeterScale, 1, 0.5, 2).Disabled = true;
//Set(OsuConfig.ScoreMeterScale, 1, 0.5, OsuGame.Tournament ? 10 : 2).Disabled = true;
Set(OsuConfig.DistanceSpacing, 0.8, 0.1, 6).Disabled = true;
Set(OsuConfig.EditorBeatDivisor, 1, 1, 16).Disabled = true;
Set(OsuConfig.EditorGridSize, 32, 4, 32).Disabled = true;
Set(OsuConfig.EditorGridSizeDesign, 32, 4, 32).Disabled = true;
Set(OsuConfig.CustomFrameLimit, 240, 240, 999).Disabled = true;
Set(OsuConfig.MsnIntegration, false).Disabled = true;
Set(OsuConfig.MyPcSucks, false).Disabled = true;
Set(OsuConfig.NotifyFriends, true).Disabled = true;
Set(OsuConfig.NotifySubmittedThread, true).Disabled = true;
Set(OsuConfig.PopupDuringGameplay, true).Disabled = true;
Set(OsuConfig.ProgressBarType, ProgressBarType.Pie).Disabled = true;
Set(OsuConfig.RankType, RankingType.Top).Disabled = true;
Set(OsuConfig.RefreshRate, 60).Disabled = true;
Set(OsuConfig.OverrideRefreshRate, Get<int>(OsuConfig.RefreshRate) != 60).Disabled = true;
//Set(OsuConfig.ScaleMode, ScaleMode.WidescreenConservative).Disabled = true;
Set(OsuConfig.ScoreboardVisible, true).Disabled = true;
Set(OsuConfig.ScoreMeter, ScoreMeterType.Error).Disabled = true;
//Set(OsuConfig.ScoreMeter, OsuGame.Tournament ? ScoreMeterType.Colour : ScoreMeterType.Error).Disabled = true;
Set(OsuConfig.ScreenshotId, 0).Disabled = true;
Set(OsuConfig.MenuSnow, false).Disabled = true;
Set(OsuConfig.MenuTriangles, true).Disabled = true;
Set(OsuConfig.SongSelectThumbnails, true).Disabled = true;
Set(OsuConfig.ScreenshotFormat, ScreenshotFormat.Jpg).Disabled = true;
Set(OsuConfig.ShowReplayComments, true).Disabled = true;
Set(OsuConfig.ShowSpectators, true).Disabled = true;
Set(OsuConfig.ShowStoryboard, true).Disabled = true;
//Set(OsuConfig.Skin, SkinManager.DEFAULT_SKIN).Disabled = true;
Set(OsuConfig.SkinSamples, true).Disabled = true;
Set(OsuConfig.SkipTablet, false).Disabled = true;
Set(OsuConfig.Tablet, false).Disabled = true;
Set(OsuConfig.UpdatePending, false).Disabled = true;
Set(OsuConfig.UseSkinCursor, false).Disabled = true;
Set(OsuConfig.UseTaikoSkin, false).Disabled = true;
Set(OsuConfig.Video, true).Disabled = true;
Set(OsuConfig.Wiimote, false).Disabled = true;
Set(OsuConfig.YahooIntegration, false).Disabled = true;
Set(OsuConfig.ForceFrameFlush, false).Disabled = true;
Set(OsuConfig.DetectPerformanceIssues, true).Disabled = true;
Set(OsuConfig.RawInput, false).Disabled = true;
Set(OsuConfig.AbsoluteToOsuWindow, Get<bool>(OsuConfig.RawInput)).Disabled = true;
Set(OsuConfig.ShowMenuTips, true).Disabled = true;
Set(OsuConfig.HiddenShowFirstApproach, true).Disabled = true;
Set(OsuConfig.ComboColourSliderBall, true).Disabled = true;
Set(OsuConfig.AlternativeChatFont, false).Disabled = true;
Set(OsuConfig.DisplayStarsMaximum, 10.0, 0.0, 10.0).Disabled = true;
Set(OsuConfig.DisplayStarsMinimum, 0.0, 0.0, 10.0).Disabled = true;
Set(OsuConfig.ReleaseStream, ReleaseStream.Lazer).Disabled = true;
Set(OsuConfig.UpdateFailCount, 0).Disabled = true;
//Set(OsuConfig.TreeSortMode, TreeGroupMode.Show_All).Disabled = true;
//Set(OsuConfig.TreeSortMode2, TreeSortMode.Title).Disabled = true;
Set(OsuConfig.PermanentSongInfo, false).Disabled = true;
Set(OsuConfig.Ticker, false).Disabled = true;
Set(OsuConfig.CompatibilityContext, false).Disabled = true;
Set(OsuConfig.CanForceOptimusCompatibility, true).Disabled = true;
Set(OsuConfig.ConfineMouse, Get<bool>(OsuConfig.ConfineMouseToFullscreen) ?
ConfineMouseMode.Fullscreen : ConfineMouseMode.Never).Disabled = true;
GetOriginalBindable<bool>(OsuConfig.SavePassword).ValueChanged += delegate
{ {
if (Get<bool>(OsuConfig.SavePassword)) Set(OsuConfig.SaveUsername, true); if (val) Set(OsuSetting.SaveUsername, true);
}; };
GetOriginalBindable<bool>(OsuConfig.SaveUsername).ValueChanged += delegate
Set(OsuSetting.SaveUsername, true).ValueChanged += val =>
{ {
if (!Get<bool>(OsuConfig.SaveUsername)) Set(OsuConfig.SavePassword, false); if (!val) Set(OsuSetting.SavePassword, false);
}; };
#pragma warning restore CS0612 // Type or member is obsolete
// Audio
Set(OsuSetting.MenuVoice, true);
Set(OsuSetting.MenuMusic, true);
Set(OsuSetting.AudioOffset, 0, -500.0, 500.0);
// Input
Set(OsuSetting.MenuCursorSize, 1.0, 0.5f, 2);
Set(OsuSetting.GameplayCursorSize, 1.0, 0.5f, 2);
Set(OsuSetting.AutoCursorSize, false);
Set(OsuSetting.MouseDisableButtons, false);
Set(OsuSetting.MouseDisableWheel, false);
// Graphics
Set(OsuSetting.ShowFpsDisplay, false);
Set(OsuSetting.MenuParallax, true);
Set(OsuSetting.SnakingInSliders, true);
Set(OsuSetting.SnakingOutSliders, true);
// Gameplay
Set(OsuSetting.DimLevel, 0.3, 0, 1);
Set(OsuSetting.ShowInterface, true);
Set(OsuSetting.KeyOverlay, false);
// Update
Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
} }
public OsuConfigManager(Storage storage) : base(storage) public OsuConfigManager(Storage storage) : base(storage)
@ -185,152 +79,32 @@ namespace osu.Game.Configuration
} }
} }
public enum OsuConfig public enum OsuSetting
{ {
// New osu:
Ruleset, Ruleset,
Token, Token,
// Imported from old osu:
BeatmapDirectory,
AllowPublicInvites,
AutoChatHide,
AutomaticDownload,
AutomaticDownloadNoVideo,
BlockNonFriendPM,
Bloom,
BloomSoftening,
BossKeyFirstActivation,
ChatAudibleHighlight,
ChatChannels,
ChatFilter,
ChatHighlightName,
ChatMessageNotification,
ChatLastChannel,
ChatRemoveForeign,
ChatSortMode,
ComboBurst,
ComboFire,
ComboFireHeight,
ConfirmExit,
AutoSendNowPlaying,
MenuCursorSize, MenuCursorSize,
GameplayCursorSize, GameplayCursorSize,
AutomaticCursorSizing, AutoCursorSize,
DimLevel, DimLevel,
Display,
DisplayCityLocation,
DistanceSpacingEnabled,
EditorTip,
VideoEditor,
EditorDefaultSkin,
EditorSnakingSliders,
EditorHitAnimations,
EditorFollowPoints,
EditorStacking,
ForceSliderRendering,
FpsCounter,
FrameTimeDisplay,
GuideTips,
CursorRipple,
HighlightWords,
HighResolution,
HitLighting,
IgnoreBarline,
IgnoreBeatmapSamples,
IgnoreBeatmapSkins,
IgnoreList,
KeyOverlay, KeyOverlay,
Language,
LastPlayMode,
AllowNowPlayingHighlights,
LastVersion,
LastVersionPermissionsFailed,
LoadSubmittedThread,
LobbyPlayMode,
ShowInterface, ShowInterface,
ShowInterfaceDuringRelax,
LobbyShowExistingOnly,
LobbyShowFriendsOnly,
LobbyShowFull,
LobbyShowInProgress,
LobbyShowPassworded,
LogPrivateMessages,
LowResolution,
ManiaSpeed,
UsePerBeatmapManiaSpeed,
ManiaSpeedBPMScale,
MenuTip,
MouseDisableButtons, MouseDisableButtons,
MouseDisableWheel, MouseDisableWheel,
MouseSpeed,
AudioOffset, AudioOffset,
ScoreMeterScale,
DistanceSpacing,
EditorBeatDivisor,
EditorGridSize,
EditorGridSizeDesign,
CustomFrameLimit,
MsnIntegration,
MyPcSucks,
NotifyFriends,
NotifySubmittedThread,
PopupDuringGameplay,
ProgressBarType,
RankType,
RefreshRate,
OverrideRefreshRate,
ScaleMode,
ScoreboardVisible,
ScoreMeter,
ScreenshotId,
MenuSnow,
MenuTriangles,
SongSelectThumbnails,
ScreenshotFormat,
ShowReplayComments,
ShowSpectators,
ShowStoryboard,
Skin,
SkinSamples,
SkipTablet,
SnakingInSliders,
SnakingOutSliders,
Tablet,
UpdatePending,
UserFilter,
UseSkinCursor,
UseTaikoSkin,
Video,
Wiimote,
YahooIntegration,
ForceFrameFlush,
DetectPerformanceIssues,
MenuMusic, MenuMusic,
MenuVoice, MenuVoice,
MenuParallax, MenuParallax,
BeatmapDetailTab, BeatmapDetailTab,
RawInput,
AbsoluteToOsuWindow,
ConfineMouse,
[Obsolete]
ConfineMouseToFullscreen,
ShowMenuTips,
HiddenShowFirstApproach,
ComboColourSliderBall,
AlternativeChatFont,
Username, Username,
DisplayStarsMaximum,
DisplayStarsMinimum,
AudioDevice,
ReleaseStream, ReleaseStream,
UpdateFailCount,
SavePassword, SavePassword,
SaveUsername, SaveUsername,
TreeSortMode, DisplayStarsMinimum,
TreeSortMode2, DisplayStarsMaximum,
PermanentSongInfo, SnakingInSliders,
Ticker, SnakingOutSliders,
CompatibilityContext, ShowFpsDisplay,
CanForceOptimusCompatibility, ChatDisplayHeight
} }
} }

View File

@ -1,18 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Configuration
{
public enum ProgressBarType
{
Off,
Pie,
[Description("Top Right")]
TopRight,
[Description("Bottom Right")]
BottomRight,
Bottom
}
}

View File

@ -36,14 +36,12 @@ namespace osu.Game.Database
private void deletePending() private void deletePending()
{ {
foreach (var b in Query<BeatmapSetInfo>().Where(b => b.DeletePending)) foreach (var b in GetAllWithChildren<BeatmapSetInfo>(b => b.DeletePending))
{ {
try try
{ {
Storage.Delete(b.Path); Storage.Delete(b.Path);
GetChildren(b, true);
foreach (var i in b.Beatmaps) foreach (var i in b.Beatmaps)
{ {
if (i.Metadata != null) Connection.Delete(i.Metadata); if (i.Metadata != null) Connection.Delete(i.Metadata);
@ -269,20 +267,16 @@ namespace osu.Game.Database
public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null, bool withStoryboard = false) public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null, bool withStoryboard = false)
{ {
var beatmapSetInfo = Query<BeatmapSetInfo>().FirstOrDefault(s => s.ID == beatmapInfo.BeatmapSetInfoID); if (beatmapInfo.BeatmapSet == null || beatmapInfo.Ruleset == null)
beatmapInfo = GetChildren(beatmapInfo, true);
if (beatmapSetInfo == null) if (beatmapInfo.BeatmapSet == null)
throw new InvalidOperationException($@"Beatmap set {beatmapInfo.BeatmapSetInfoID} is not in the local database."); throw new InvalidOperationException($@"Beatmap set {beatmapInfo.BeatmapSetInfoID} is not in the local database.");
//we need metadata
GetChildren(beatmapSetInfo);
//we also need a ruleset
GetChildren(beatmapInfo);
if (beatmapInfo.Metadata == null) if (beatmapInfo.Metadata == null)
beatmapInfo.Metadata = beatmapSetInfo.Metadata; beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata;
WorkingBeatmap working = new DatabaseWorkingBeatmap(this, beatmapInfo, beatmapSetInfo, withStoryboard); WorkingBeatmap working = new DatabaseWorkingBeatmap(this, beatmapInfo, withStoryboard);
previous?.TransferTo(working); previous?.TransferTo(working);

View File

@ -7,12 +7,17 @@ namespace osu.Game.Database
{ {
public class BeatmapDifficulty public class BeatmapDifficulty
{ {
/// <summary>
/// The default value used for all difficulty settings except <see cref="SliderMultiplier"/> and <see cref="SliderTickRate"/>.
/// </summary>
public const float DEFAULT_DIFFICULTY = 5;
[PrimaryKey, AutoIncrement] [PrimaryKey, AutoIncrement]
public int ID { get; set; } public int ID { get; set; }
public float DrainRate { get; set; } = 5; public float DrainRate { get; set; } = DEFAULT_DIFFICULTY;
public float CircleSize { get; set; } = 5; public float CircleSize { get; set; } = DEFAULT_DIFFICULTY;
public float OverallDifficulty { get; set; } = 5; public float OverallDifficulty { get; set; } = DEFAULT_DIFFICULTY;
public float ApproachRate { get; set; } = 5; public float ApproachRate { get; set; } = DEFAULT_DIFFICULTY;
public float SliderMultiplier { get; set; } = 1; public float SliderMultiplier { get; set; } = 1;
public float SliderTickRate { get; set; } = 1; public float SliderTickRate { get; set; } = 1;

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 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 SQLite.Net.Attributes; using SQLite.Net.Attributes;
namespace osu.Game.Database namespace osu.Game.Database
@ -25,12 +26,13 @@ namespace osu.Game.Database
public string[] SearchableTerms => new[] public string[] SearchableTerms => new[]
{ {
Author,
Artist, Artist,
ArtistUnicode, ArtistUnicode,
Title, Title,
TitleUnicode, TitleUnicode,
Source, Source,
Tags Tags
}; }.Where(s => !string.IsNullOrEmpty(s)).ToArray();
} }
} }

View File

@ -48,11 +48,9 @@ namespace osu.Game.Database
return Connection.Table<T>(); return Connection.Table<T>();
} }
public T GetWithChildren<T>(object id) where T : class /// <summary>
{ /// This is expensive. Use with caution.
return Connection.GetWithChildren<T>(id); /// </summary>
}
public List<T> GetAllWithChildren<T>(Expression<Func<T, bool>> filter = null, bool recursive = true) public List<T> GetAllWithChildren<T>(Expression<Func<T, bool>> filter = null, bool recursive = true)
where T : class where T : class
{ {

View File

@ -14,8 +14,8 @@ namespace osu.Game.Database
{ {
private readonly BeatmapDatabase database; private readonly BeatmapDatabase database;
public DatabaseWorkingBeatmap(BeatmapDatabase database, BeatmapInfo beatmapInfo, BeatmapSetInfo beatmapSetInfo, bool withStoryboard = false) public DatabaseWorkingBeatmap(BeatmapDatabase database, BeatmapInfo beatmapInfo, bool withStoryboard = false)
: base(beatmapInfo, beatmapSetInfo, withStoryboard) : base(beatmapInfo, withStoryboard)
{ {
this.database = database; this.database = database;
} }
@ -51,13 +51,13 @@ namespace osu.Game.Database
protected override Texture GetBackground() protected override Texture GetBackground()
{ {
if (BeatmapInfo?.Metadata?.BackgroundFile == null) if (Metadata?.BackgroundFile == null)
return null; return null;
try try
{ {
using (var reader = getReader()) using (var reader = getReader())
return new TextureStore(new RawTextureLoaderStore(reader), false).Get(BeatmapInfo.Metadata.BackgroundFile); return new TextureStore(new RawTextureLoaderStore(reader), false).Get(Metadata.BackgroundFile);
} }
catch { return null; } catch { return null; }
} }
@ -66,7 +66,7 @@ namespace osu.Game.Database
{ {
try try
{ {
var trackData = getReader()?.GetStream(BeatmapInfo.Metadata.AudioFile); var trackData = getReader()?.GetStream(Metadata.AudioFile);
return trackData == null ? null : new TrackBass(trackData); return trackData == null ? null : new TrackBass(trackData);
} }
catch { return null; } catch { return null; }

View File

@ -94,13 +94,13 @@ namespace osu.Game.Database
{ {
byte[] properties = new byte[5]; byte[] properties = new byte[5];
if (replayInStream.Read(properties, 0, 5) != 5) if (replayInStream.Read(properties, 0, 5) != 5)
throw new Exception("input .lzma is too short"); throw new IOException("input .lzma is too short");
long outSize = 0; long outSize = 0;
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
int v = replayInStream.ReadByte(); int v = replayInStream.ReadByte();
if (v < 0) if (v < 0)
throw new Exception("Can't Read 1"); throw new IOException("Can't Read 1");
outSize |= (long)(byte)v << (8 * i); outSize |= (long)(byte)v << (8 * i);
} }

View File

@ -39,7 +39,7 @@ namespace osu.Game.Graphics.Containers
private void load(UserInputManager input, OsuConfigManager config) private void load(UserInputManager input, OsuConfigManager config)
{ {
this.input = input; this.input = input;
parallaxEnabled = config.GetBindable<bool>(OsuConfig.MenuParallax); parallaxEnabled = config.GetBindable<bool>(OsuSetting.MenuParallax);
parallaxEnabled.ValueChanged += delegate parallaxEnabled.ValueChanged += delegate
{ {
if (!parallaxEnabled) if (!parallaxEnabled)

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