1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-30 06:03:22 +08:00

Merge branch 'master' into modselect

This commit is contained in:
Huo Yaoyuan 2017-03-07 16:10:53 +08:00
commit b56d6c767b
212 changed files with 2573 additions and 740 deletions

@ -1 +1 @@
Subproject commit 169f0a758c6b565ee42832f99bf4b5303f4413a6 Subproject commit 476c8792ee6b42cf4e5f0362b4eb09c0230807d8

@ -1 +1 @@
Subproject commit 93eb5bf99bb642bf339d7dce09c2d946412dadd6 Subproject commit 9533590f839aa6e27ed7f8b9064a0e7dc08ad861

View File

@ -40,7 +40,7 @@ namespace osu.Desktop.Deploy
/// <summary> /// <summary>
/// How many previous build deltas we want to keep when publishing. /// How many previous build deltas we want to keep when publishing.
/// </summary> /// </summary>
const int keep_delta_count = 3; private const int keep_delta_count = 3;
private static string codeSigningCmd => string.IsNullOrEmpty(codeSigningPassword) ? "" : $"-n \"/a /f {codeSigningCertPath} /p {codeSigningPassword} /t http://timestamp.comodoca.com/authenticode\""; private static string codeSigningCmd => string.IsNullOrEmpty(codeSigningPassword) ? "" : $"-n \"/a /f {codeSigningCertPath} /p {codeSigningPassword} /t http://timestamp.comodoca.com/authenticode\"";
@ -172,10 +172,10 @@ namespace osu.Desktop.Deploy
} }
//remove excess deltas //remove excess deltas
var deltas = releaseLines.Where(l => l.Filename.Contains("-delta")); var deltas = releaseLines.Where(l => l.Filename.Contains("-delta")).ToArray();
if (deltas.Count() > keep_delta_count) if (deltas.Length > keep_delta_count)
{ {
foreach (var l in deltas.Take(deltas.Count() - keep_delta_count)) foreach (var l in deltas.Take(deltas.Length - keep_delta_count))
{ {
write($"- Removing old delta {l.Filename}", ConsoleColor.Yellow); write($"- Removing old delta {l.Filename}", ConsoleColor.Yellow);
File.Delete(Path.Combine(ReleasesFolder, l.Filename)); File.Delete(Path.Combine(ReleasesFolder, l.Filename));
@ -342,7 +342,10 @@ namespace osu.Desktop.Deploy
}; };
Process p = Process.Start(psi); Process p = Process.Start(psi);
if (p == null) return false;
string output = p.StandardOutput.ReadToEnd(); string output = p.StandardOutput.ReadToEnd();
if (p.ExitCode == 0) return true; if (p.ExitCode == 0) return true;
write(output); write(output);

View File

@ -23,7 +23,7 @@ namespace osu.Desktop.VisualTests.Platform
platform = new SQLitePlatformWin32(); platform = new SQLitePlatformWin32();
else else
platform = new SQLitePlatformGeneric(); platform = new SQLitePlatformGeneric();
return new SQLiteConnection(platform, $@":memory:"); return new SQLiteConnection(platform, @":memory:");
} }
} }
} }

View File

@ -4,9 +4,9 @@
using osu.Framework.Screens.Testing; using osu.Framework.Screens.Testing;
using osu.Game.Screens.Select.Options; using osu.Game.Screens.Select.Options;
namespace osu.Desktop.VisualTests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCaseBeatmapOptionsOverlay : TestCase internal class TestCaseBeatmapOptionsOverlay : TestCase
{ {
public override string Description => @"Beatmap options in song select"; public override string Description => @"Beatmap options in song select";

View File

@ -8,7 +8,7 @@ using osu.Game.Overlays;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCaseChatDisplay : TestCase internal class TestCaseChatDisplay : TestCase
{ {
private ScheduledDelegate messageRequest; private ScheduledDelegate messageRequest;
@ -18,7 +18,7 @@ namespace osu.Desktop.VisualTests.Tests
{ {
base.Reset(); base.Reset();
Add(new ChatOverlay() Add(new ChatOverlay
{ {
State = Visibility.Visible State = Visibility.Visible
}); });

View File

@ -8,11 +8,11 @@ using osu.Game.Overlays.Dialog;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCaseDialogOverlay : TestCase internal class TestCaseDialogOverlay : TestCase
{ {
public override string Description => @"Display dialogs"; public override string Description => @"Display dialogs";
DialogOverlay overlay; private DialogOverlay overlay;
public override void Reset() public override void Reset()
{ {

View File

@ -2,23 +2,16 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Platform;
using osu.Framework.Screens.Testing; using osu.Framework.Screens.Testing;
using osu.Game.Screens.Tournament; using osu.Game.Screens.Tournament;
using osu.Game.Screens.Tournament.Teams; using osu.Game.Screens.Tournament.Teams;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCaseDrawings : TestCase internal class TestCaseDrawings : TestCase
{ {
public override string Description => "Tournament drawings"; public override string Description => "Tournament drawings";
[BackgroundDependencyLoader]
private void load(Storage storage)
{
}
public override void Reset() public override void Reset()
{ {
base.Reset(); base.Reset();
@ -29,7 +22,7 @@ namespace osu.Desktop.VisualTests.Tests
}); });
} }
class TestTeamList : ITeamList private class TestTeamList : ITeamList
{ {
public IEnumerable<Team> Teams { get; } = new[] public IEnumerable<Team> Teams { get; } = new[]
{ {

View File

@ -18,7 +18,7 @@ using OpenTK;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCaseGamefield : TestCase internal class TestCaseGamefield : TestCase
{ {
public override string Description => @"Showing hitobjects and what not."; public override string Description => @"Showing hitobjects and what not.";
@ -31,7 +31,7 @@ namespace osu.Desktop.VisualTests.Tests
int time = 500; int time = 500;
for (int i = 0; i < 100; i++) for (int i = 0; i < 100; i++)
{ {
objects.Add(new HitCircle() objects.Add(new HitCircle
{ {
StartTime = time, StartTime = time,
Position = new Vector2(RNG.Next(0, 512), RNG.Next(0, 384)), Position = new Vector2(RNG.Next(0, 512), RNG.Next(0, 384)),

View File

@ -17,12 +17,12 @@ using OpenTK.Graphics;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCaseHitObjects : TestCase internal class TestCaseHitObjects : TestCase
{ {
private StopwatchClock rateAdjustClock; private StopwatchClock rateAdjustClock;
private FramedClock framedClock; private FramedClock framedClock;
bool auto = false; private bool auto;
public TestCaseHitObjects() public TestCaseHitObjects()
{ {
@ -31,9 +31,9 @@ namespace osu.Desktop.VisualTests.Tests
playbackSpeed.ValueChanged += delegate { rateAdjustClock.Rate = playbackSpeed.Value; }; playbackSpeed.ValueChanged += delegate { rateAdjustClock.Rate = playbackSpeed.Value; };
} }
HitObjectType mode = HitObjectType.Slider; private HitObjectType mode = HitObjectType.Slider;
BindableNumber<double> playbackSpeed = new BindableDouble(0.5) { MinValue = 0, MaxValue = 1 }; private BindableNumber<double> playbackSpeed = new BindableDouble(0.5) { MinValue = 0, MaxValue = 1 };
private Container playfieldContainer; private Container playfieldContainer;
private Container approachContainer; private Container approachContainer;
@ -61,7 +61,7 @@ namespace osu.Desktop.VisualTests.Tests
add(new DrawableSlider(new Slider add(new DrawableSlider(new Slider
{ {
StartTime = framedClock.CurrentTime + 600, StartTime = framedClock.CurrentTime + 600,
ControlPoints = new List<Vector2>() ControlPoints = new List<Vector2>
{ {
new Vector2(-200, 0), new Vector2(-200, 0),
new Vector2(400, 0), new Vector2(400, 0),
@ -93,7 +93,7 @@ namespace osu.Desktop.VisualTests.Tests
AddButton(@"slider", () => load(HitObjectType.Slider)); AddButton(@"slider", () => load(HitObjectType.Slider));
AddButton(@"spinner", () => load(HitObjectType.Spinner)); AddButton(@"spinner", () => load(HitObjectType.Spinner));
AddToggle(@"auto", (state) => { auto = state; load(mode); }); AddToggle(@"auto", state => { auto = state; load(mode); });
ButtonsContainer.Add(new SpriteText { Text = "Playback Speed" }); ButtonsContainer.Add(new SpriteText { Text = "Playback Speed" });
ButtonsContainer.Add(new BasicSliderBar<double> ButtonsContainer.Add(new BasicSliderBar<double>
@ -122,8 +122,9 @@ namespace osu.Desktop.VisualTests.Tests
load(mode); load(mode);
} }
int depth; private int depth;
void add(DrawableHitObject h)
private void add(DrawableHitObject h)
{ {
h.Anchor = Anchor.Centre; h.Anchor = Anchor.Centre;
h.Depth = depth++; h.Depth = depth++;

View File

@ -15,7 +15,7 @@ using osu.Game.Screens.Play;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCaseKeyCounter : TestCase internal class TestCaseKeyCounter : TestCase
{ {
public override string Description => @"Tests key counter"; public override string Description => @"Tests key counter";

View File

@ -9,7 +9,7 @@ using OpenTK.Graphics;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCaseMenuButtonSystem : TestCase internal class TestCaseMenuButtonSystem : TestCase
{ {
public override string Description => @"Main menu button system"; public override string Description => @"Main menu button system";

View File

@ -8,7 +8,7 @@ using osu.Game.Modes;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCaseModSelectOverlay : TestCase internal class TestCaseModSelectOverlay : TestCase
{ {
public override string Description => @"Tests the mod select overlay"; public override string Description => @"Tests the mod select overlay";

View File

@ -9,7 +9,7 @@ using osu.Framework.Graphics.Containers;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCaseMusicController : TestCase internal class TestCaseMusicController : TestCase
{ {
public override string Description => @"Tests music controller ui."; public override string Description => @"Tests music controller ui.";
@ -30,7 +30,7 @@ namespace osu.Desktop.VisualTests.Tests
Anchor = Anchor.Centre Anchor = Anchor.Centre
}; };
Add(mc); Add(mc);
AddToggle(@"Show", (state) => mc.State = state ? Visibility.Visible : Visibility.Hidden); AddToggle(@"Show", state => mc.State = state ? Visibility.Visible : Visibility.Hidden);
} }
} }
} }

View File

@ -12,11 +12,11 @@ using osu.Framework.Graphics.Containers;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCaseNotificationManager : TestCase internal class TestCaseNotificationManager : TestCase
{ {
public override string Description => @"I handle notifications"; public override string Description => @"I handle notifications";
NotificationManager manager; private NotificationManager manager;
public override void Reset() public override void Reset()
{ {
@ -30,7 +30,7 @@ namespace osu.Desktop.VisualTests.Tests
Origin = Anchor.TopRight, Origin = Anchor.TopRight,
}); });
AddToggle(@"show", (state) => manager.State = state ? Visibility.Visible : Visibility.Hidden); AddToggle(@"show", state => manager.State = state ? Visibility.Visible : Visibility.Hidden);
AddButton(@"simple #1", sendNotification1); AddButton(@"simple #1", sendNotification1);
AddButton(@"simple #2", sendNotification2); AddButton(@"simple #2", sendNotification2);
@ -95,7 +95,7 @@ namespace osu.Desktop.VisualTests.Tests
progressingNotifications.Add(n); progressingNotifications.Add(n);
} }
List<ProgressNotification> progressingNotifications = new List<ProgressNotification>(); private List<ProgressNotification> progressingNotifications = new List<ProgressNotification>();
private void sendProgress1() private void sendProgress1()
{ {

View File

@ -6,7 +6,7 @@ using osu.Game.Overlays;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCaseOptions : TestCase internal class TestCaseOptions : TestCase
{ {
public override string Description => @"Tests the options overlay"; public override string Description => @"Tests the options overlay";

View File

@ -7,7 +7,7 @@ using osu.Game.Screens.Play;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCasePauseOverlay : TestCase internal class TestCasePauseOverlay : TestCase
{ {
public override string Description => @"Tests the pause overlay"; public override string Description => @"Tests the pause overlay";

View File

@ -11,7 +11,7 @@ using osu.Game.Screens.Select;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCasePlaySongSelect : TestCase internal class TestCasePlaySongSelect : TestCase
{ {
private BeatmapDatabase db, oldDb; private BeatmapDatabase db, oldDb;
private TestStorage storage; private TestStorage storage;

View File

@ -18,24 +18,30 @@ using OpenTK.Graphics;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCasePlayer : TestCase internal class TestCasePlayer : TestCase
{ {
private WorkingBeatmap beatmap; protected Player Player;
private BeatmapDatabase db;
public override string Description => @"Showing everything to play the game."; public override string Description => @"Showing everything to play the game.";
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(BeatmapDatabase db) private void load(BeatmapDatabase db)
{ {
var beatmapInfo = db.Query<BeatmapInfo>().Where(b => b.Mode == PlayMode.Osu).FirstOrDefault(); this.db = db;
if (beatmapInfo != null)
beatmap = db.GetWorkingBeatmap(beatmapInfo);
} }
public override void Reset() public override void Reset()
{ {
base.Reset(); base.Reset();
WorkingBeatmap beatmap = null;
var beatmapInfo = db.Query<BeatmapInfo>().Where(b => b.Mode == PlayMode.Osu).FirstOrDefault();
if (beatmapInfo != null)
beatmap = db.GetWorkingBeatmap(beatmapInfo);
if (beatmap?.Track == null) if (beatmap?.Track == null)
{ {
var objects = new List<HitObject>(); var objects = new List<HitObject>();
@ -43,7 +49,7 @@ namespace osu.Desktop.VisualTests.Tests
int time = 1500; int time = 1500;
for (int i = 0; i < 50; i++) for (int i = 0; i < 50; i++)
{ {
objects.Add(new HitCircle() objects.Add(new HitCircle
{ {
StartTime = time, StartTime = time,
Position = new Vector2(i % 4 == 0 || i % 4 == 2 ? 0 : 512, Position = new Vector2(i % 4 == 0 || i % 4 == 2 ? 0 : 512,
@ -81,17 +87,21 @@ namespace osu.Desktop.VisualTests.Tests
Colour = Color4.Black, Colour = Color4.Black,
}); });
Add(new PlayerLoader(new Player Add(new PlayerLoader(Player = CreatePlayer(beatmap))
{
PreferredPlayMode = PlayMode.Osu,
Beatmap = beatmap
})
{ {
Beatmap = beatmap Beatmap = beatmap
}); });
} }
class TestWorkingBeatmap : WorkingBeatmap protected virtual Player CreatePlayer(WorkingBeatmap beatmap)
{
return new Player
{
Beatmap = beatmap
};
}
private class TestWorkingBeatmap : WorkingBeatmap
{ {
public TestWorkingBeatmap(Beatmap beatmap) public TestWorkingBeatmap(Beatmap beatmap)
: base(beatmap.BeatmapInfo, beatmap.BeatmapInfo.BeatmapSet) : base(beatmap.BeatmapInfo, beatmap.BeatmapInfo.BeatmapSet)

View File

@ -0,0 +1,40 @@
// 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.IO;
using osu.Framework.Allocation;
using osu.Framework.Input.Handlers;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Modes;
using osu.Game.Screens.Play;
namespace osu.Desktop.VisualTests.Tests
{
class TestCaseReplay : TestCasePlayer
{
private WorkingBeatmap beatmap;
private InputHandler replay;
private Func<Stream> getReplayStream;
private ScoreDatabase scoreDatabase;
public override string Description => @"Testing replay playback.";
[BackgroundDependencyLoader]
private void load(Storage storage)
{
scoreDatabase = new ScoreDatabase(storage);
}
protected override Player CreatePlayer(WorkingBeatmap beatmap)
{
var player = base.CreatePlayer(beatmap);
player.ReplayInputHandler = Ruleset.GetRuleset(beatmap.PlayMode).CreateAutoplayScore(beatmap.Beatmap)?.Replay?.GetInputHandler();
return player;
}
}
}

View File

@ -18,7 +18,7 @@ using osu.Framework.Graphics.Primitives;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCaseScoreCounter : TestCase internal class TestCaseScoreCounter : TestCase
{ {
public override string Description => @"Tests multiple counters"; public override string Description => @"Tests multiple counters";

View File

@ -12,7 +12,7 @@ using OpenTK.Graphics;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCaseTextAwesome : TestCase internal class TestCaseTextAwesome : TestCase
{ {
public override string Description => @"Tests display of icons"; public override string Description => @"Tests display of icons";
@ -22,7 +22,7 @@ namespace osu.Desktop.VisualTests.Tests
FillFlowContainer flow; FillFlowContainer flow;
Add(flow = new FillFlowContainer() Add(flow = new FillFlowContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.5f), Size = new Vector2(0.5f),

View File

@ -7,7 +7,7 @@ using osu.Game.Screens.Play;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
class TestCaseTwoLayerButton : TestCase internal class TestCaseTwoLayerButton : TestCase
{ {
public override string Description => @"Back and skip and what not"; public override string Description => @"Back and skip and what not";

View File

@ -7,13 +7,13 @@ using osu.Game.Screens.Backgrounds;
namespace osu.Desktop.VisualTests namespace osu.Desktop.VisualTests
{ {
class VisualTestGame : OsuGameBase internal class VisualTestGame : OsuGameBase
{ {
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
(new BackgroundScreenDefault() { Depth = 10 }).LoadAsync(this, AddInternal); new BackgroundScreenDefault { Depth = 10 }.LoadAsync(this, AddInternal);
// Have to construct this here, rather than in the constructor, because // Have to construct this here, rather than in the constructor, because
// we depend on some dependencies to be loaded within OsuGameBase.load(). // we depend on some dependencies to be loaded within OsuGameBase.load().

View File

@ -86,6 +86,10 @@
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll</HintPath> <HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="SharpCompress, Version=0.15.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
<HintPath>..\packages\SharpCompress.0.15.1\lib\net45\SharpCompress.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath> <HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
@ -187,6 +191,7 @@
<Compile Include="Tests\TestCaseHitObjects.cs" /> <Compile Include="Tests\TestCaseHitObjects.cs" />
<Compile Include="Tests\TestCaseKeyCounter.cs" /> <Compile Include="Tests\TestCaseKeyCounter.cs" />
<Compile Include="Tests\TestCaseMenuButtonSystem.cs" /> <Compile Include="Tests\TestCaseMenuButtonSystem.cs" />
<Compile Include="Tests\TestCaseReplay.cs" />
<Compile Include="Tests\TestCaseScoreCounter.cs" /> <Compile Include="Tests\TestCaseScoreCounter.cs" />
<Compile Include="Tests\TestCaseTextAwesome.cs" /> <Compile Include="Tests\TestCaseTextAwesome.cs" />
<Compile Include="Tests\TestCasePlaySongSelect.cs" /> <Compile Include="Tests\TestCasePlaySongSelect.cs" />

View File

@ -6,6 +6,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
<packages> <packages>
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" /> <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
<package id="ppy.OpenTK" version="2.0.50727.1340" targetFramework="net45" /> <package id="ppy.OpenTK" version="2.0.50727.1340" targetFramework="net45" />
<package id="SharpCompress" version="0.15.1" targetFramework="net45" />
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" /> <package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" />
<package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" /> <package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" />
<package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" /> <package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" />

View File

@ -17,16 +17,16 @@ 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; set; } private string basePath { get; }
private Beatmap firstMap { get; set; } private Beatmap firstMap { get; }
public LegacyFilesystemReader(string path) public LegacyFilesystemReader(string path)
{ {
basePath = path; basePath = path;
BeatmapFilenames = Directory.GetFiles(basePath, @"*.osu").Select(f => Path.GetFileName(f)).ToArray(); BeatmapFilenames = Directory.GetFiles(basePath, @"*.osu").Select(Path.GetFileName).ToArray();
if (BeatmapFilenames.Length == 0) if (BeatmapFilenames.Length == 0)
throw new FileNotFoundException(@"This directory contains no beatmaps"); throw new FileNotFoundException(@"This directory contains no beatmaps");
StoryboardFilename = Directory.GetFiles(basePath, @"*.osb").Select(f => Path.GetFileName(f)).FirstOrDefault(); StoryboardFilename = Directory.GetFiles(basePath, @"*.osb").Select(Path.GetFileName).FirstOrDefault();
using (var stream = new StreamReader(GetStream(BeatmapFilenames[0]))) using (var stream = new StreamReader(GetStream(BeatmapFilenames[0])))
{ {
var decoder = BeatmapDecoder.GetDecoder(stream); var decoder = BeatmapDecoder.GetDecoder(stream);

View File

@ -9,16 +9,16 @@ using osu.Framework.Desktop.Platform;
using osu.Desktop.Overlays; using osu.Desktop.Overlays;
using System.Reflection; using System.Reflection;
using System.Drawing; using System.Drawing;
using System.IO;
using System.Threading.Tasks;
using osu.Game.Screens.Menu; using osu.Game.Screens.Menu;
namespace osu.Desktop namespace osu.Desktop
{ {
class OsuGameDesktop : OsuGame internal class OsuGameDesktop : OsuGame
{ {
private VersionManager versionManager; private VersionManager versionManager;
public override bool IsDeployedBuild => versionManager.IsDeployedBuild;
public OsuGameDesktop(string[] args = null) public OsuGameDesktop(string[] args = null)
: base(args) : base(args)
{ {
@ -44,7 +44,7 @@ namespace osu.Desktop
if (desktopWindow != null) if (desktopWindow != null)
{ {
desktopWindow.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location); desktopWindow.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
desktopWindow.Title = @"osu!lazer"; desktopWindow.Title = Name;
desktopWindow.DragEnter += dragEnter; desktopWindow.DragEnter += dragEnter;
desktopWindow.DragDrop += dragDrop; desktopWindow.DragDrop += dragDrop;
@ -54,22 +54,32 @@ namespace osu.Desktop
private void dragDrop(DragEventArgs e) private void dragDrop(DragEventArgs e)
{ {
// this method will only be executed if e.Effect in dragEnter gets set to something other that None. // this method will only be executed if e.Effect in dragEnter gets set to something other that None.
var dropData = e.Data.GetData(DataFormats.FileDrop) as object[]; var dropData = (object[])e.Data.GetData(DataFormats.FileDrop);
var filePaths = dropData.Select(f => f.ToString()).ToArray(); var filePaths = dropData.Select(f => f.ToString()).ToArray();
ImportBeatmapsAsync(filePaths);
if (filePaths.All(f => Path.GetExtension(f) == @".osz"))
Task.Run(() => BeatmapDatabase.Import(filePaths));
else if (filePaths.All(f => Path.GetExtension(f) == @".osr"))
Task.Run(() =>
{
var score = ScoreDatabase.ReadReplayFile(filePaths.First());
Schedule(() => LoadScore(score));
});
} }
static readonly string[] allowed_extensions = { @".osz", @".osr" };
private void dragEnter(DragEventArgs e) private void dragEnter(DragEventArgs e)
{ {
// dragDrop will only be executed if e.Effect gets set to something other that None in this method. // dragDrop will only be executed if e.Effect gets set to something other that None in this method.
bool isFile = e.Data.GetDataPresent(DataFormats.FileDrop); bool isFile = e.Data.GetDataPresent(DataFormats.FileDrop);
if (isFile) if (isFile)
{ {
var paths = (e.Data.GetData(DataFormats.FileDrop) as object[]).Select(f => f.ToString()).ToArray(); var paths = ((object[])e.Data.GetData(DataFormats.FileDrop)).Select(f => f.ToString()).ToArray();
if (paths.Any(p => !p.EndsWith(".osz"))) if (allowed_extensions.Any(ext => paths.All(p => p.EndsWith(ext))))
e.Effect = DragDropEffects.None;
else
e.Effect = DragDropEffects.Copy; e.Effect = DragDropEffects.Copy;
else
e.Effect = DragDropEffects.None;
} }
} }
} }

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 System; using System;
using System.Diagnostics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
@ -11,7 +10,6 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Notifications; using osu.Game.Overlays.Notifications;
using Squirrel; using Squirrel;
using System.Reflection;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Graphics; using osu.Game.Graphics;
@ -19,6 +17,7 @@ using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using System.Net.Http; using System.Net.Http;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Game;
namespace osu.Desktop.Overlays namespace osu.Desktop.Overlays
{ {
@ -27,16 +26,12 @@ namespace osu.Desktop.Overlays
private UpdateManager updateManager; private UpdateManager updateManager;
private NotificationManager notificationManager; private NotificationManager notificationManager;
AssemblyName assembly = Assembly.GetEntryAssembly().GetName();
public bool IsDeployedBuild => assembly.Version.Major > 0;
protected override bool HideOnEscape => false; protected override bool HideOnEscape => false;
public override bool HandleInput => false; public override bool HandleInput => false;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(NotificationManager notification, OsuColour colours, TextureStore textures) private void load(NotificationManager notification, OsuColour colours, TextureStore textures, OsuGameBase game)
{ {
notificationManager = notification; notificationManager = notification;
@ -45,17 +40,6 @@ namespace osu.Desktop.Overlays
Origin = Anchor.BottomCentre; Origin = Anchor.BottomCentre;
Alpha = 0; Alpha = 0;
bool isDebug = false;
Debug.Assert(isDebug = true);
string version;
if (!IsDeployedBuild)
{
version = @"local " + (isDebug ? @"debug" : @"release");
}
else
version = $@"{assembly.Version.Major}.{assembly.Version.Minor}.{assembly.Version.Build}";
Children = new Drawable[] Children = new Drawable[]
{ {
new FillFlowContainer new FillFlowContainer
@ -76,12 +60,12 @@ namespace osu.Desktop.Overlays
new OsuSpriteText new OsuSpriteText
{ {
Font = @"Exo2.0-Bold", Font = @"Exo2.0-Bold",
Text = $@"osu!lazer" Text = game.Name
}, },
new OsuSpriteText new OsuSpriteText
{ {
Colour = isDebug ? colours.Red : Color4.White, Colour = game.IsDebug ? colours.Red : Color4.White,
Text = version Text = game.Version
}, },
} }
}, },
@ -92,7 +76,7 @@ namespace osu.Desktop.Overlays
TextSize = 12, TextSize = 12,
Colour = colours.Yellow, Colour = colours.Yellow,
Font = @"Venera", Font = @"Venera",
Text = $@"Development Build" Text = @"Development Build"
}, },
new Sprite new Sprite
{ {
@ -104,7 +88,7 @@ namespace osu.Desktop.Overlays
} }
}; };
if (IsDeployedBuild) if (game.IsDeployedBuild)
checkForUpdateAsync(); checkForUpdateAsync();
} }
@ -203,7 +187,7 @@ namespace osu.Desktop.Overlays
{ {
} }
class UpdateProgressNotification : ProgressNotification private class UpdateProgressNotification : ProgressNotification
{ {
protected override Notification CreateCompletionNotification() => new ProgressCompletionNotification(this) protected override Notification CreateCompletionNotification() => new ProgressCompletionNotification(this)
{ {
@ -228,6 +212,7 @@ namespace osu.Desktop.Overlays
new TextAwesome new TextAwesome
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.fa_upload, Icon = FontAwesome.fa_upload,
Colour = Color4.White, Colour = Color4.White,
} }

View File

@ -29,7 +29,7 @@ namespace osu.Desktop
{ {
if (!host.IsPrimaryInstance) if (!host.IsPrimaryInstance)
{ {
var importer = new BeatmapImporter(host); var importer = new BeatmapIPCChannel(host);
// Restore the cwd so relative paths given at the command line work correctly // Restore the cwd so relative paths given at the command line work correctly
Directory.SetCurrentDirectory(cwd); Directory.SetCurrentDirectory(cwd);
foreach (var file in args) foreach (var file in args)

View File

@ -101,8 +101,8 @@
<HintPath>$(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.PatchApi.dll</HintPath> <HintPath>$(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.PatchApi.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\ICSharpCode.SharpZipLib.dll</HintPath> <HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> <Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">

View File

@ -7,6 +7,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" /> <package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" /> <package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" /> <package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net45" />
<package id="Splat" version="2.0.0" targetFramework="net45" /> <package id="Splat" version="2.0.0" targetFramework="net45" />
<package id="squirrel.windows" version="1.5.2" targetFramework="net45" /> <package id="squirrel.windows" version="1.5.2" targetFramework="net45" />
</packages> </packages>

View File

@ -1,8 +1,6 @@
// 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;
namespace osu.Game.Modes.Catch namespace osu.Game.Modes.Catch
{ {
public class CatchModNoFail : ModNoFail public class CatchModNoFail : ModNoFail

View File

@ -8,6 +8,7 @@ using osu.Game.Modes.Objects;
using osu.Game.Modes.Osu.UI; using osu.Game.Modes.Osu.UI;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Screens.Play;
namespace osu.Game.Modes.Catch namespace osu.Game.Modes.Catch
{ {
@ -15,7 +16,11 @@ namespace osu.Game.Modes.Catch
{ {
public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay(); public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay();
public override HitRenderer CreateHitRendererWith(Beatmap beatmap) => new CatchHitRenderer { Beatmap = beatmap }; public override HitRenderer CreateHitRendererWith(Beatmap beatmap, PlayerInputManager input = null) => new CatchHitRenderer
{
Beatmap = beatmap,
InputManager = input,
};
public override IEnumerable<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {
@ -76,7 +81,7 @@ namespace osu.Game.Modes.Catch
public override FontAwesome Icon => FontAwesome.fa_osu_fruits_o; public override FontAwesome Icon => FontAwesome.fa_osu_fruits_o;
public override ScoreProcessor CreateScoreProcessor(int hitObjectCount) => null; public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null;
public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser(); public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser();

View File

@ -8,7 +8,7 @@ using osu.Game.Beatmaps;
namespace osu.Game.Modes.Catch.Objects namespace osu.Game.Modes.Catch.Objects
{ {
class CatchConverter : HitObjectConverter<CatchBaseHit> internal class CatchConverter : HitObjectConverter<CatchBaseHit>
{ {
public override List<CatchBaseHit> Convert(Beatmap beatmap) public override List<CatchBaseHit> Convert(Beatmap beatmap)
{ {

View File

@ -10,7 +10,7 @@ using OpenTK;
namespace osu.Game.Modes.Catch.Objects.Drawable namespace osu.Game.Modes.Catch.Objects.Drawable
{ {
class DrawableFruit : Sprite internal class DrawableFruit : Sprite
{ {
private CatchBaseHit h; private CatchBaseHit h;

View File

@ -12,8 +12,8 @@ namespace osu.Game.Modes.Catch.UI
{ {
protected override HitObjectConverter<CatchBaseHit> Converter => new CatchConverter(); protected override HitObjectConverter<CatchBaseHit> Converter => new CatchConverter();
protected override Playfield CreatePlayfield() => new CatchPlayfield(); protected override Playfield<CatchBaseHit> CreatePlayfield() => new CatchPlayfield();
protected override DrawableHitObject GetVisualRepresentation(CatchBaseHit h) => null;// new DrawableFruit(h); protected override DrawableHitObject<CatchBaseHit> GetVisualRepresentation(CatchBaseHit h) => null;// new DrawableFruit(h);
} }
} }

View File

@ -3,12 +3,13 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Modes.Catch.Objects;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using OpenTK; using OpenTK;
namespace osu.Game.Modes.Catch.UI namespace osu.Game.Modes.Catch.UI
{ {
public class CatchPlayfield : Playfield public class CatchPlayfield : Playfield<CatchBaseHit>
{ {
public CatchPlayfield() public CatchPlayfield()
{ {

View File

@ -8,6 +8,7 @@ using osu.Game.Modes.Objects;
using osu.Game.Modes.Osu.UI; using osu.Game.Modes.Osu.UI;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Screens.Play;
namespace osu.Game.Modes.Mania namespace osu.Game.Modes.Mania
{ {
@ -15,7 +16,11 @@ namespace osu.Game.Modes.Mania
{ {
public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay(); public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay();
public override HitRenderer CreateHitRendererWith(Beatmap beatmap) => new ManiaHitRenderer { Beatmap = beatmap }; public override HitRenderer CreateHitRendererWith(Beatmap beatmap, PlayerInputManager input = null) => new ManiaHitRenderer
{
Beatmap = beatmap,
InputManager = input,
};
public override IEnumerable<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {
@ -92,7 +97,7 @@ namespace osu.Game.Modes.Mania
public override FontAwesome Icon => FontAwesome.fa_osu_mania_o; public override FontAwesome Icon => FontAwesome.fa_osu_mania_o;
public override ScoreProcessor CreateScoreProcessor(int hitObjectCount) => null; public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null;
public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser(); public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser();

View File

@ -26,8 +26,8 @@ namespace osu.Game.Modes.Mania.Objects.Drawable
{ {
Texture = textures.Get(@"Menu/logo"); Texture = textures.Get(@"Menu/logo");
Transforms.Add(new TransformPositionY() { StartTime = note.StartTime - 200, EndTime = note.StartTime, StartValue = -0.1f, EndValue = 0.9f }); Transforms.Add(new TransformPositionY { StartTime = note.StartTime - 200, EndTime = note.StartTime, StartValue = -0.1f, EndValue = 0.9f });
Transforms.Add(new TransformAlpha() { StartTime = note.StartTime + note.Duration + 200, EndTime = note.StartTime + note.Duration + 400, StartValue = 1, EndValue = 0 }); Transforms.Add(new TransformAlpha { StartTime = note.StartTime + note.Duration + 200, EndTime = note.StartTime + note.Duration + 400, StartValue = 1, EndValue = 0 });
Expire(true); Expire(true);
} }
} }

View File

@ -9,7 +9,7 @@ using osu.Game.Beatmaps;
namespace osu.Game.Modes.Mania.Objects namespace osu.Game.Modes.Mania.Objects
{ {
class ManiaConverter : HitObjectConverter<ManiaBaseHit> internal class ManiaConverter : HitObjectConverter<ManiaBaseHit>
{ {
private readonly int columns; private readonly int columns;

View File

@ -13,7 +13,7 @@ namespace osu.Game.Modes.Mania.UI
/// </summary> /// </summary>
public class ManiaComboCounter : TaikoComboCounter public class ManiaComboCounter : TaikoComboCounter
{ {
protected ushort KeysHeld = 0; protected ushort KeysHeld;
protected Color4 OriginalColour; protected Color4 OriginalColour;

View File

@ -19,9 +19,9 @@ namespace osu.Game.Modes.Mania.UI
protected override HitObjectConverter<ManiaBaseHit> Converter => new ManiaConverter(columns); protected override HitObjectConverter<ManiaBaseHit> Converter => new ManiaConverter(columns);
protected override Playfield CreatePlayfield() => new ManiaPlayfield(columns); protected override Playfield<ManiaBaseHit> CreatePlayfield() => new ManiaPlayfield(columns);
protected override DrawableHitObject GetVisualRepresentation(ManiaBaseHit h) protected override DrawableHitObject<ManiaBaseHit> GetVisualRepresentation(ManiaBaseHit h)
{ {
return null; return null;
//return new DrawableNote(h) //return new DrawableNote(h)

View File

@ -3,19 +3,17 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Modes.Mania.Objects;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
namespace osu.Game.Modes.Mania.UI namespace osu.Game.Modes.Mania.UI
{ {
public class ManiaPlayfield : Playfield public class ManiaPlayfield : Playfield<ManiaBaseHit>
{ {
private readonly int columns;
public ManiaPlayfield(int columns) public ManiaPlayfield(int columns)
{ {
this.columns = columns;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
Size = new Vector2(columns / 20f, 1f); Size = new Vector2(columns / 20f, 1f);
Anchor = Anchor.BottomCentre; Anchor = Anchor.BottomCentre;
@ -24,7 +22,7 @@ namespace osu.Game.Modes.Mania.UI
Add(new Box { RelativeSizeAxes = Axes.Both, Alpha = 0.5f }); Add(new Box { RelativeSizeAxes = Axes.Both, Alpha = 0.5f });
for (int i = 0; i < columns; i++) for (int i = 0; i < columns; i++)
Add(new Box() Add(new Box
{ {
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Size = new Vector2(2, 1), Size = new Vector2(2, 1),

View File

@ -17,7 +17,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Connections
public double EndTime; public double EndTime;
public Vector2 EndPosition; public Vector2 EndPosition;
const float width = 8; private const float width = 8;
public FollowPoint() public FollowPoint()
{ {

View File

@ -74,13 +74,13 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
for (int d = (int)(PointDistance * 1.5); d < distance - PointDistance; d += PointDistance) for (int d = (int)(PointDistance * 1.5); d < distance - PointDistance; d += PointDistance)
{ {
float fraction = ((float)d / distance); float fraction = (float)d / distance;
Vector2 pointStartPosition = startPosition + (fraction - 0.1f) * distanceVector; Vector2 pointStartPosition = startPosition + (fraction - 0.1f) * distanceVector;
Vector2 pointEndPosition = startPosition + fraction * distanceVector; Vector2 pointEndPosition = startPosition + fraction * distanceVector;
double fadeOutTime = startTime + fraction * duration; double fadeOutTime = startTime + fraction * duration;
double fadeInTime = fadeOutTime - PreEmpt; double fadeInTime = fadeOutTime - PreEmpt;
Add(new FollowPoint() Add(new FollowPoint
{ {
StartTime = fadeInTime, StartTime = fadeInTime,
EndTime = fadeOutTime, EndTime = fadeOutTime,

View File

@ -49,7 +49,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
return true; return true;
}, },
}, },
number = new NumberPiece() number = new NumberPiece
{ {
Text = h is Spinner ? "S" : (HitObject.ComboIndex + 1).ToString(), Text = h is Spinner ? "S" : (HitObject.ComboIndex + 1).ToString(),
}, },
@ -59,7 +59,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
Colour = osuObject.Colour, Colour = osuObject.Colour,
}, },
ApproachCircle = new ApproachCircle() ApproachCircle = new ApproachCircle
{ {
Colour = osuObject.Colour, Colour = osuObject.Colour,
} }
@ -69,33 +69,23 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
Size = circle.DrawSize; Size = circle.DrawSize;
} }
double hit50 = 150;
double hit100 = 80;
double hit300 = 30;
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
{ {
if (!userTriggered) if (!userTriggered)
{ {
if (Judgement.TimeOffset > hit50) if (Judgement.TimeOffset > HitObject.HitWindowFor(OsuScoreResult.Hit50))
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
return; return;
} }
double hitOffset = Math.Abs(Judgement.TimeOffset); double hitOffset = Math.Abs(Judgement.TimeOffset);
OsuJudgementInfo osuJudgement = Judgement as OsuJudgementInfo; OsuJudgementInfo osuJudgement = (OsuJudgementInfo)Judgement;
if (hitOffset < hit50) if (hitOffset < HitObject.HitWindowFor(OsuScoreResult.Hit50))
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
osuJudgement.Score = HitObject.ScoreResultForOffset(hitOffset);
if (hitOffset < hit300)
osuJudgement.Score = OsuScoreResult.Hit300;
else if (hitOffset < hit100)
osuJudgement.Score = OsuScoreResult.Hit100;
else if (hitOffset < hit50)
osuJudgement.Score = OsuScoreResult.Hit50;
} }
else else
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;

View File

@ -2,12 +2,11 @@
// 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.ComponentModel; using System.ComponentModel;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
namespace osu.Game.Modes.Osu.Objects.Drawables namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
public class DrawableOsuHitObject : DrawableHitObject public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject>
{ {
public const float TIME_PREEMPT = 600; public const float TIME_PREEMPT = 600;
public const float TIME_FADEIN = 400; public const float TIME_FADEIN = 400;
@ -18,7 +17,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
} }
public override JudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.Hit300 }; protected override JudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.Hit300 };
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {

View File

@ -21,10 +21,10 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
private Container<DrawableSliderTick> ticks; private Container<DrawableSliderTick> ticks;
SliderBody body; private SliderBody body;
SliderBall ball; private SliderBall ball;
SliderBouncer bouncer1, bouncer2; private SliderBouncer bouncer1, bouncer2;
public DrawableSlider(Slider s) : base(s) public DrawableSlider(Slider s) : base(s)
{ {
@ -94,7 +94,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
// pass all input through. // pass all input through.
public override bool Contains(Vector2 screenSpacePos) => true; public override bool Contains(Vector2 screenSpacePos) => true;
int currentRepeat; private int currentRepeat;
protected override void Update() protected override void Update()
{ {
@ -102,8 +102,8 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
double progress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); double progress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
int repeat = (int)(progress * slider.RepeatCount); int repeat = slider.RepeatAt(progress);
progress = (progress * slider.RepeatCount) % 1; progress = slider.CurveProgressAt(progress);
if (repeat > currentRepeat) if (repeat > currentRepeat)
{ {
@ -112,9 +112,6 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
currentRepeat = repeat; currentRepeat = repeat;
} }
if (repeat % 2 == 1)
progress = 1 - progress;
bouncer2.Position = slider.Curve.PositionAt(body.SnakedEnd ?? 0); bouncer2.Position = slider.Curve.PositionAt(body.SnakedEnd ?? 0);
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.

View File

@ -26,7 +26,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
public override bool RemoveWhenNotAlive => false; public override bool RemoveWhenNotAlive => false;
public override JudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.SliderTick }; protected override JudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.SliderTick };
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
{ {
@ -71,7 +71,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
{ {
var j = Judgement as OsuJudgementInfo; var j = (OsuJudgementInfo)Judgement;
if (Judgement.TimeOffset >= 0) if (Judgement.TimeOffset >= 0)
{ {

View File

@ -75,7 +75,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
if (Time.Current < HitObject.StartTime) return; if (Time.Current < HitObject.StartTime) return;
var j = Judgement as OsuJudgementInfo; var j = (OsuJudgementInfo)Judgement;
disc.ScaleTo(Interpolation.ValueAt(Math.Sqrt(Progress), scaleToCircle, Vector2.One, 0, 1), 100); disc.ScaleTo(Interpolation.ValueAt(Math.Sqrt(Progress), scaleToCircle, Vector2.One, 0, 1), 100);
@ -108,9 +108,9 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
} }
} }
private Vector2 scaleToCircle => (circle.Scale * circle.DrawWidth / DrawWidth) * 0.95f; private Vector2 scaleToCircle => circle.Scale * circle.DrawWidth / DrawWidth * 0.95f;
private float spinsPerMinuteNeeded = 100 + (5 * 15); //TODO: read per-map OD and place it on the 5 private float spinsPerMinuteNeeded = 100 + 5 * 15; //TODO: read per-map OD and place it on the 5
private float rotationsNeeded => (float)(spinsPerMinuteNeeded * (spinner.EndTime - spinner.StartTime) / 60000f); private float rotationsNeeded => (float)(spinsPerMinuteNeeded * (spinner.EndTime - spinner.StartTime) / 60000f);

View File

@ -21,7 +21,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
public CirclePiece() public CirclePiece()
{ {
Size = new Vector2(128); Size = new Vector2((float)OsuHitObject.OBJECT_RADIUS * 2);
Masking = true; Masking = true;
CornerRadius = Size.X / 2; CornerRadius = Size.X / 2;

View File

@ -15,7 +15,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
private readonly Slider slider; private readonly Slider slider;
private Box follow; private Box follow;
const float width = 128; private const float width = 128;
public SliderBall(Slider slider) public SliderBall(Slider slider)
{ {
@ -81,7 +81,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
return base.OnMouseMove(state); return base.OnMouseMove(state);
} }
bool tracking; private bool tracking;
public bool Tracking public bool Tracking
{ {
get { return tracking; } get { return tracking; }

View File

@ -29,7 +29,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
set { Disc.Colour = value; } set { Disc.Colour = value; }
} }
Color4 completeColour; private Color4 completeColour;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
@ -37,7 +37,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
completeColour = colours.YellowLight.Opacity(0.8f); completeColour = colours.YellowLight.Opacity(0.8f);
} }
class SpinnerBorder : Container private class SpinnerBorder : Container
{ {
public SpinnerBorder() public SpinnerBorder()
{ {
@ -116,7 +116,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
}; };
} }
bool tracking; private bool tracking;
public bool Tracking public bool Tracking
{ {
get { return tracking; } get { return tracking; }
@ -130,7 +130,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
} }
} }
bool complete; private bool complete;
public bool Complete public bool Complete
{ {
get { return complete; } get { return complete; }

View File

@ -5,11 +5,19 @@ using System;
using osu.Game.Modes.Objects; using osu.Game.Modes.Objects;
using OpenTK; using OpenTK;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Modes.Osu.Objects.Drawables;
namespace osu.Game.Modes.Osu.Objects namespace osu.Game.Modes.Osu.Objects
{ {
public abstract class OsuHitObject : HitObject public abstract class OsuHitObject : HitObject
{ {
public const double OBJECT_RADIUS = 64;
private const double hittable_range = 300;
private const double hit_window_50 = 150;
private const double hit_window_100 = 80;
private const double hit_window_300 = 30;
public Vector2 Position { get; set; } public Vector2 Position { get; set; }
public Vector2 StackedPosition => Position + StackOffset; public Vector2 StackedPosition => Position + StackOffset;
@ -22,10 +30,38 @@ namespace osu.Game.Modes.Osu.Objects
public Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f); public Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f);
public double Radius => OBJECT_RADIUS * Scale;
public float Scale { get; set; } = 1; public float Scale { get; set; } = 1;
public abstract HitObjectType Type { get; } public abstract HitObjectType Type { get; }
public double HitWindowFor(OsuScoreResult result)
{
switch (result)
{
default:
return 300;
case OsuScoreResult.Hit50:
return 150;
case OsuScoreResult.Hit100:
return 80;
case OsuScoreResult.Hit300:
return 30;
}
}
public OsuScoreResult ScoreResultForOffset(double offset)
{
if (offset < HitWindowFor(OsuScoreResult.Hit300))
return OsuScoreResult.Hit300;
if (offset < HitWindowFor(OsuScoreResult.Hit100))
return OsuScoreResult.Hit100;
if (offset < HitWindowFor(OsuScoreResult.Hit50))
return OsuScoreResult.Hit50;
return OsuScoreResult.Miss;
}
public override void SetDefaultsFromBeatmap(Beatmap beatmap) public override void SetDefaultsFromBeatmap(Beatmap beatmap)
{ {
base.SetDefaultsFromBeatmap(beatmap); base.SetDefaultsFromBeatmap(beatmap);

View File

@ -8,7 +8,7 @@ using System.Linq;
namespace osu.Game.Modes.Osu.Objects namespace osu.Game.Modes.Osu.Objects
{ {
class OsuHitObjectDifficulty internal class OsuHitObjectDifficulty
{ {
/// <summary> /// <summary>
/// Factor by how much speed / aim strain decays per second. /// Factor by how much speed / aim strain decays per second.
@ -63,7 +63,7 @@ namespace osu.Game.Modes.Osu.Objects
MaxCombo += slider.Ticks.Count(); MaxCombo += slider.Ticks.Count();
// We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps. // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
scalingFactor = (52.0f / circleRadius); scalingFactor = 52.0f / circleRadius;
if (circleRadius < 30) if (circleRadius < 30)
{ {
float smallCircleBonus = Math.Min(30.0f - circleRadius, 5.0f) / 50.0f; float smallCircleBonus = Math.Min(30.0f - circleRadius, 5.0f) / 50.0f;
@ -130,7 +130,7 @@ namespace osu.Game.Modes.Osu.Objects
else if (distance > almost_diameter) else if (distance > almost_diameter)
return 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter); return 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter);
else if (distance > almost_diameter / 2) else if (distance > almost_diameter / 2)
return 0.95 + 0.25 * (distance - (almost_diameter / 2)) / (almost_diameter / 2); return 0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2);
else else
return 0.95; return 0.95;

View File

@ -30,11 +30,8 @@ namespace osu.Game.Modes.Osu.Objects
break; break;
case HitObjectType.Slider: case HitObjectType.Slider:
CurveTypes curveType = CurveTypes.Catmull; CurveTypes curveType = CurveTypes.Catmull;
int repeatCount;
double length = 0; double length = 0;
List<Vector2> points = new List<Vector2>(); List<Vector2> points = new List<Vector2> { new Vector2(int.Parse(split[0]), int.Parse(split[1])) };
points.Add(new Vector2(int.Parse(split[0]), int.Parse(split[1])));
string[] pointsplit = split[5].Split('|'); string[] pointsplit = split[5].Split('|');
for (int i = 0; i < pointsplit.Length; i++) for (int i = 0; i < pointsplit.Length; i++)
@ -67,12 +64,10 @@ namespace osu.Game.Modes.Osu.Objects
points.Add(v); points.Add(v);
} }
repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture); int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture);
if (repeatCount > 9000) if (repeatCount > 9000)
{ throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high");
throw new ArgumentOutOfRangeException("wacky man");
}
if (split.Length > 7) if (split.Length > 7)
length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture); length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);

View File

@ -14,7 +14,32 @@ namespace osu.Game.Modes.Osu.Objects
{ {
public override double EndTime => StartTime + RepeatCount * Curve.Length / Velocity; public override double EndTime => StartTime + RepeatCount * Curve.Length / Velocity;
public override Vector2 EndPosition => RepeatCount % 2 == 0 ? Position : Curve.PositionAt(1); public override Vector2 EndPosition => PositionAt(1);
/// <summary>
/// Computes the position on the slider at a given progress that ranges from 0 (beginning of the slider)
/// to 1 (end of the slider). This includes repeat logic.
/// </summary>
/// <param name="progress">Ranges from 0 (beginning of the slider) to 1 (end of the slider).</param>
/// <returns></returns>
public Vector2 PositionAt(double progress) => Curve.PositionAt(CurveProgressAt(progress));
/// <summary>
/// Find the current progress along the curve, accounting for repeat logic.
/// </summary>
public double CurveProgressAt(double progress)
{
var p = progress * RepeatCount % 1;
if (RepeatAt(progress) % 2 == 1)
p = 1 - p;
return p;
}
/// <summary>
/// Determine which repeat of the slider we are on at a given progress.
/// Range is 0..RepeatCount where 0 is the first run.
/// </summary>
public int RepeatAt(double progress) => (int)(progress * RepeatCount);
private int stackHeight; private int stackHeight;
public override int StackHeight public override int StackHeight

View File

@ -59,10 +59,9 @@ namespace osu.Game.Modes.Osu.Objects
if (i == ControlPoints.Count - 1 || ControlPoints[i] == ControlPoints[i + 1]) if (i == ControlPoints.Count - 1 || ControlPoints[i] == ControlPoints[i + 1])
{ {
List<Vector2> subpath = calculateSubpath(subControlPoints); List<Vector2> subpath = calculateSubpath(subControlPoints);
for (int j = 0; j < subpath.Count; ++j) foreach (Vector2 t in subpath)
// Only add those vertices that add a new segment to the path. if (calculatedPath.Count == 0 || calculatedPath.Last() != t)
if (calculatedPath.Count == 0 || calculatedPath.Last() != subpath[j]) calculatedPath.Add(t);
calculatedPath.Add(subpath[j]);
subControlPoints.Clear(); subControlPoints.Clear();
} }
@ -175,7 +174,7 @@ namespace osu.Game.Modes.Osu.Objects
path.Clear(); path.Clear();
int i = 0; int i = 0;
for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) ; for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) { }
path.Add(interpolateVertices(i, d0) + Offset); path.Add(interpolateVertices(i, d0) + Offset);
@ -186,10 +185,10 @@ namespace osu.Game.Modes.Osu.Objects
} }
/// <summary> /// <summary>
/// Computes the position on the slider at a given progress that ranges from 0 (beginning of the slider) /// Computes the position on the slider at a given progress that ranges from 0 (beginning of the curve)
/// to 1 (end of the slider). /// to 1 (end of the curve).
/// </summary> /// </summary>
/// <param name="progress">Ranges from 0 (beginning of the slider) to 1 (end of the slider).</param> /// <param name="progress">Ranges from 0 (beginning of the curve) to 1 (end of the curve).</param>
/// <returns></returns> /// <returns></returns>
public Vector2 PositionAt(double progress) public Vector2 PositionAt(double progress)
{ {

View File

@ -0,0 +1,298 @@
// 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 osu.Game.Beatmaps;
using osu.Game.Modes.Osu.Objects;
using OpenTK;
using System;
using osu.Framework.Graphics.Transforms;
using osu.Game.Modes.Osu.Objects.Drawables;
using osu.Framework.MathUtils;
using System.Diagnostics;
namespace osu.Game.Modes.Osu
{
public class OsuAutoReplay : LegacyReplay
{
static readonly Vector2 spinner_centre = new Vector2(256, 192);
const float spin_radius = 50;
private Beatmap beatmap;
public OsuAutoReplay(Beatmap beatmap)
{
this.beatmap = beatmap;
createAutoReplay();
}
internal class LegacyReplayFrameComparer : IComparer<LegacyReplayFrame>
{
public int Compare(LegacyReplayFrame f1, LegacyReplayFrame f2)
{
return f1.Time.CompareTo(f2.Time);
}
}
private static IComparer<LegacyReplayFrame> replayFrameComparer = new LegacyReplayFrameComparer();
private int findInsertionIndex(LegacyReplayFrame frame)
{
int index = Frames.BinarySearch(frame, replayFrameComparer);
if (index < 0)
{
index = ~index;
}
else
{
// Go to the first index which is actually bigger
while (index < Frames.Count && frame.Time == Frames[index].Time)
{
++index;
}
}
return index;
}
private void addFrameToReplay(LegacyReplayFrame frame) => Frames.Insert(findInsertionIndex(frame), frame);
private static Vector2 circlePosition(double t, double radius) => new Vector2((float)(Math.Cos(t) * radius), (float)(Math.Sin(t) * radius));
private double applyModsToTime(double v) => v;
private double applyModsToRate(double v) => v;
private void createAutoReplay()
{
int buttonIndex = 0;
bool delayedMovements = false;// ModManager.CheckActive(Mods.Relax2);
EasingTypes preferredEasing = delayedMovements ? EasingTypes.InOutCubic : EasingTypes.Out;
addFrameToReplay(new LegacyReplayFrame(-100000, 256, 500, LegacyButtonState.None));
addFrameToReplay(new LegacyReplayFrame(beatmap.HitObjects[0].StartTime - 1500, 256, 500, LegacyButtonState.None));
addFrameToReplay(new LegacyReplayFrame(beatmap.HitObjects[0].StartTime - 1000, 256, 192, LegacyButtonState.None));
// We are using ApplyModsToRate and not ApplyModsToTime to counteract the speed up / slow down from HalfTime / DoubleTime so that we remain at a constant framerate of 60 fps.
float frameDelay = (float)applyModsToRate(1000.0 / 60.0);
// Already superhuman, but still somewhat realistic
int reactionTime = (int)applyModsToRate(100);
for (int i = 0; i < beatmap.HitObjects.Count; i++)
{
OsuHitObject h = beatmap.HitObjects[i] as OsuHitObject;
//if (h.EndTime < InputManager.ReplayStartTime)
//{
// h.IsHit = true;
// continue;
//}
int endDelay = h is Spinner ? 1 : 0;
if (delayedMovements && i > 0)
{
OsuHitObject last = beatmap.HitObjects[i - 1] as OsuHitObject;
//Make the cursor stay at a hitObject as long as possible (mainly for autopilot).
if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{
if (!(last is Spinner) && h.StartTime - last.EndTime < 1000) addFrameToReplay(new LegacyReplayFrame(last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None));
if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, LegacyButtonState.None));
}
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{
if (!(last is Spinner) && h.StartTime - last.EndTime < 1000) addFrameToReplay(new LegacyReplayFrame(last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None));
if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, LegacyButtonState.None));
}
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > last.EndTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50)
{
if (!(last is Spinner) && h.StartTime - last.EndTime < 1000) addFrameToReplay(new LegacyReplayFrame(last.EndTime + h.HitWindowFor(OsuScoreResult.Hit100), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None));
if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, LegacyButtonState.None));
}
}
Vector2 targetPosition = h.Position;
EasingTypes easing = preferredEasing;
float spinnerDirection = -1;
if (h is Spinner)
{
targetPosition.X = Frames[Frames.Count - 1].MouseX;
targetPosition.Y = Frames[Frames.Count - 1].MouseY;
Vector2 difference = spinner_centre - targetPosition;
float differenceLength = difference.Length;
float newLength = (float)Math.Sqrt(differenceLength * differenceLength - spin_radius * spin_radius);
if (differenceLength > spin_radius)
{
float angle = (float)Math.Asin(spin_radius / differenceLength);
if (angle > 0)
{
spinnerDirection = -1;
}
else
{
spinnerDirection = 1;
}
difference.X = difference.X * (float)Math.Cos(angle) - difference.Y * (float)Math.Sin(angle);
difference.Y = difference.X * (float)Math.Sin(angle) + difference.Y * (float)Math.Cos(angle);
difference.Normalize();
difference *= newLength;
targetPosition += difference;
easing = EasingTypes.In;
}
else if (difference.Length > 0)
{
targetPosition = spinner_centre - difference * (spin_radius / difference.Length);
}
else
{
targetPosition = spinner_centre + new Vector2(0, -spin_radius);
}
}
// Do some nice easing for cursor movements
if (Frames.Count > 0)
{
LegacyReplayFrame lastFrame = Frames[Frames.Count - 1];
// Wait until Auto could "see and react" to the next note.
double waitTime = h.StartTime - Math.Max(0.0, DrawableOsuHitObject.TIME_PREEMPT - reactionTime);
if (waitTime > lastFrame.Time)
{
lastFrame = new LegacyReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState);
addFrameToReplay(lastFrame);
}
Vector2 lastPosition = new Vector2(lastFrame.MouseX, lastFrame.MouseY);
double timeDifference = applyModsToTime(h.StartTime - lastFrame.Time);
// Only "snap" to hitcircles if they are far enough apart. As the time between hitcircles gets shorter the snapping threshold goes up.
if (timeDifference > 0 && // Sanity checks
((lastPosition - targetPosition).Length > h.Radius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough
timeDifference >= 266)) // ... or the beats are slow enough to tap anyway.
{
// Perform eased movement
for (double time = lastFrame.Time + frameDelay; time < h.StartTime; time += frameDelay)
{
Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPosition, lastFrame.Time, h.StartTime, easing);
addFrameToReplay(new LegacyReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState));
}
buttonIndex = 0;
}
else
{
buttonIndex++;
}
}
LegacyButtonState button = buttonIndex % 2 == 0 ? LegacyButtonState.Left1 : LegacyButtonState.Right1;
LegacyReplayFrame newFrame = new LegacyReplayFrame(h.StartTime, targetPosition.X, targetPosition.Y, button);
LegacyReplayFrame endFrame = new LegacyReplayFrame(h.EndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, LegacyButtonState.None);
// Decrement because we want the previous frame, not the next one
int index = findInsertionIndex(newFrame) - 1;
// Do we have a previous frame? No need to check for < replay.Count since we decremented!
if (index >= 0)
{
LegacyReplayFrame previousFrame = Frames[index];
var previousButton = previousFrame.ButtonState;
// If a button is already held, then we simply alternate
if (previousButton != LegacyButtonState.None)
{
Debug.Assert(previousButton != (LegacyButtonState.Left1 | LegacyButtonState.Right1));
// Force alternation if we have the same button. Otherwise we can just keep the naturally to us assigned button.
if (previousButton == button)
{
button = (LegacyButtonState.Left1 | LegacyButtonState.Right1) & ~button;
newFrame.SetButtonStates(button);
}
// We always follow the most recent slider / spinner, so remove any other frames that occur while it exists.
int endIndex = findInsertionIndex(endFrame);
if (index < Frames.Count - 1)
Frames.RemoveRange(index + 1, Math.Max(0, endIndex - (index + 1)));
// After alternating we need to keep holding the other button in the future rather than the previous one.
for (int j = index + 1; j < Frames.Count; ++j)
{
// Don't affect frames which stop pressing a button!
if (j < Frames.Count - 1 || Frames[j].ButtonState == previousButton)
Frames[j].SetButtonStates(button);
}
}
}
addFrameToReplay(newFrame);
// We add intermediate frames for spinning / following a slider here.
if (h is Spinner)
{
Vector2 difference = targetPosition - spinner_centre;
float radius = difference.Length;
float angle = radius == 0 ? 0 : (float)Math.Atan2(difference.Y, difference.X);
double t;
for (double j = h.StartTime + frameDelay; j < h.EndTime; j += frameDelay)
{
t = applyModsToTime(j - h.StartTime) * spinnerDirection;
Vector2 pos = spinner_centre + circlePosition(t / 20 + angle, spin_radius);
addFrameToReplay(new LegacyReplayFrame((int)j, pos.X, pos.Y, button));
}
t = applyModsToTime(h.EndTime - h.StartTime) * spinnerDirection;
Vector2 endPosition = spinner_centre + circlePosition(t / 20 + angle, spin_radius);
addFrameToReplay(new LegacyReplayFrame(h.EndTime, endPosition.X, endPosition.Y, button));
endFrame.MouseX = endPosition.X;
endFrame.MouseY = endPosition.Y;
}
else if (h is Slider)
{
Slider s = h as Slider;
for (double j = frameDelay; j < s.Duration; j += frameDelay)
{
Vector2 pos = s.PositionAt(j / s.Duration);
addFrameToReplay(new LegacyReplayFrame(h.StartTime + j, pos.X, pos.Y, button));
}
addFrameToReplay(new LegacyReplayFrame(h.EndTime, s.EndPosition.X, s.EndPosition.Y, button));
}
// We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed!
if (Frames[Frames.Count - 1].Time <= endFrame.Time)
addFrameToReplay(endFrame);
}
//Player.currentScore.Replay = InputManager.ReplayScore.Replay;
//Player.currentScore.PlayerName = "osu!";
}
}
}

View File

@ -100,22 +100,23 @@ namespace osu.Game.Modes.Osu
protected bool CalculateStrainValues() protected bool CalculateStrainValues()
{ {
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
List<OsuHitObjectDifficulty>.Enumerator hitObjectsEnumerator = DifficultyHitObjects.GetEnumerator(); using (List<OsuHitObjectDifficulty>.Enumerator hitObjectsEnumerator = DifficultyHitObjects.GetEnumerator())
if (!hitObjectsEnumerator.MoveNext()) return false;
OsuHitObjectDifficulty currentHitObject = hitObjectsEnumerator.Current;
OsuHitObjectDifficulty nextHitObject;
// First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject.
while (hitObjectsEnumerator.MoveNext())
{ {
nextHitObject = hitObjectsEnumerator.Current;
nextHitObject.CalculateStrains(currentHitObject, TimeRate);
currentHitObject = nextHitObject;
}
return true; if (!hitObjectsEnumerator.MoveNext()) return false;
OsuHitObjectDifficulty current = hitObjectsEnumerator.Current;
// First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject.
while (hitObjectsEnumerator.MoveNext())
{
var next = hitObjectsEnumerator.Current;
next?.CalculateStrains(current, TimeRate);
current = next;
}
return true;
}
} }
/// <summary> /// <summary>
@ -184,7 +185,7 @@ namespace osu.Game.Modes.Osu
} }
// Those values are used as array indices. Be careful when changing them! // Those values are used as array indices. Be careful when changing them!
public enum DifficultyType : int public enum DifficultyType
{ {
Speed = 0, Speed = 0,
Aim, Aim,

View File

@ -85,7 +85,7 @@ namespace osu.Game.Modes.Osu
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModPerfect), typeof(ModNoFail), typeof(ModAutoplay), typeof(ModCinema) }; public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModPerfect), typeof(ModNoFail), typeof(ModAutoplay), typeof(ModCinema) };
} }
public class OsuModeAutoplay : ModAutoplay public class OsuModAutoplay : ModAutoplay
{ {
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray(); public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
} }

View File

@ -9,6 +9,7 @@ using osu.Game.Modes.Objects;
using osu.Game.Modes.Osu.Objects; using osu.Game.Modes.Osu.Objects;
using osu.Game.Modes.Osu.UI; using osu.Game.Modes.Osu.UI;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using osu.Game.Screens.Play;
namespace osu.Game.Modes.Osu namespace osu.Game.Modes.Osu
{ {
@ -16,7 +17,11 @@ namespace osu.Game.Modes.Osu
{ {
public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay(); public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay();
public override HitRenderer CreateHitRendererWith(Beatmap beatmap) => new OsuHitRenderer { Beatmap = beatmap }; public override HitRenderer CreateHitRendererWith(Beatmap beatmap, PlayerInputManager input = null) => new OsuHitRenderer
{
Beatmap = beatmap,
InputManager = input
};
public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new[] public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new[]
{ {
@ -81,7 +86,7 @@ namespace osu.Game.Modes.Osu
{ {
Mods = new Mod[] Mods = new Mod[]
{ {
new OsuModeAutoplay(), new OsuModAutoplay(),
new ModCinema(), new ModCinema(),
}, },
}, },
@ -96,10 +101,17 @@ namespace osu.Game.Modes.Osu
public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser(); public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser();
public override ScoreProcessor CreateScoreProcessor(int hitObjectCount) => new OsuScoreProcessor(hitObjectCount); public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => new OsuScoreProcessor(hitObjectCount);
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new OsuDifficultyCalculator(beatmap); public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new OsuDifficultyCalculator(beatmap);
public override Score CreateAutoplayScore(Beatmap beatmap)
{
var score = CreateScoreProcessor().GetScore();
score.Replay = new OsuAutoReplay(beatmap);
return score;
}
protected override PlayMode PlayMode => PlayMode.Osu; protected override PlayMode PlayMode => PlayMode.Osu;
} }
} }

View File

@ -3,7 +3,7 @@
namespace osu.Game.Modes.Osu namespace osu.Game.Modes.Osu
{ {
class OsuScore : Score internal class OsuScore : Score
{ {
} }
} }

View File

@ -6,9 +6,9 @@ using osu.Game.Modes.Osu.Objects.Drawables;
namespace osu.Game.Modes.Osu namespace osu.Game.Modes.Osu
{ {
class OsuScoreProcessor : ScoreProcessor internal class OsuScoreProcessor : ScoreProcessor
{ {
public OsuScoreProcessor(int hitObjectCount) public OsuScoreProcessor(int hitObjectCount = 0)
: base(hitObjectCount) : base(hitObjectCount)
{ {
Health.Value = 1; Health.Value = 1;
@ -35,8 +35,9 @@ namespace osu.Game.Modes.Osu
int score = 0; int score = 0;
int maxScore = 0; int maxScore = 0;
foreach (OsuJudgementInfo j in Judgements) foreach (var judgementInfo in Judgements)
{ {
var j = (OsuJudgementInfo)judgementInfo;
score += j.ScoreValue; score += j.ScoreValue;
maxScore += j.MaxScoreValue; maxScore += j.MaxScoreValue;
} }

View File

@ -11,7 +11,7 @@ namespace osu.Game.Modes.Osu.UI
/// </summary> /// </summary>
public class OsuComboCounter : ComboCounter public class OsuComboCounter : ComboCounter
{ {
protected uint ScheduledPopOutCurrentId = 0; protected uint ScheduledPopOutCurrentId;
protected virtual float PopOutSmallScale => 1.1f; protected virtual float PopOutSmallScale => 1.1f;
protected virtual bool CanPopOutWhileRolling => false; protected virtual bool CanPopOutWhileRolling => false;

View File

@ -13,16 +13,21 @@ namespace osu.Game.Modes.Osu.UI
{ {
protected override HitObjectConverter<OsuHitObject> Converter => new OsuHitObjectConverter(); protected override HitObjectConverter<OsuHitObject> Converter => new OsuHitObjectConverter();
protected override Playfield CreatePlayfield() => new OsuPlayfield(); protected override Playfield<OsuHitObject> CreatePlayfield() => new OsuPlayfield();
protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h) protected override DrawableHitObject<OsuHitObject> GetVisualRepresentation(OsuHitObject h)
{ {
if (h is HitCircle) var circle = h as HitCircle;
return new DrawableHitCircle(h as HitCircle); if (circle != null)
if (h is Slider) return new DrawableHitCircle(circle);
return new DrawableSlider(h as Slider);
if (h is Spinner) var slider = h as Slider;
return new DrawableSpinner(h as Spinner); if (slider != null)
return new DrawableSlider(slider);
var spinner = h as Spinner;
if (spinner != null)
return new DrawableSpinner(spinner);
return null; return null;
} }
} }

View File

@ -10,10 +10,12 @@ using osu.Game.Modes.Osu.Objects.Drawables;
using osu.Game.Modes.Osu.Objects.Drawables.Connections; using osu.Game.Modes.Osu.Objects.Drawables.Connections;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using System.Linq; using System.Linq;
using osu.Game.Graphics.Cursor;
using OpenTK.Graphics;
namespace osu.Game.Modes.Osu.UI namespace osu.Game.Modes.Osu.UI
{ {
public class OsuPlayfield : Playfield public class OsuPlayfield : Playfield<OsuHitObject>
{ {
private Container approachCircles; private Container approachCircles;
private Container judgementLayer; private Container judgementLayer;
@ -53,11 +55,18 @@ namespace osu.Game.Modes.Osu.UI
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Depth = -1, Depth = -1,
} },
}); });
} }
public override void Add(DrawableHitObject h) protected override void LoadComplete()
{
base.LoadComplete();
if (InputManager?.ReplayInputHandler != null)
Add(new OsuCursorContainer { Colour = Color4.LightYellow });
}
public override void Add(DrawableHitObject<OsuHitObject> h)
{ {
h.Depth = (float)h.HitObject.StartTime; h.Depth = (float)h.HitObject.StartTime;
IDrawableHitObjectWithProxiedApproach c = h as IDrawableHitObjectWithProxiedApproach; IDrawableHitObjectWithProxiedApproach c = h as IDrawableHitObjectWithProxiedApproach;
@ -78,9 +87,9 @@ namespace osu.Game.Modes.Osu.UI
.OrderBy(h => h.StartTime); .OrderBy(h => h.StartTime);
} }
private void judgement(DrawableHitObject h, JudgementInfo j) private void judgement(DrawableHitObject<OsuHitObject> h, JudgementInfo j)
{ {
HitExplosion explosion = new HitExplosion((OsuJudgementInfo)j, (OsuHitObject)h.HitObject); HitExplosion explosion = new HitExplosion((OsuJudgementInfo)j, h.HitObject);
judgementLayer.Add(explosion); judgementLayer.Add(explosion);
} }

View File

@ -22,7 +22,7 @@ namespace osu.Game.Modes.Osu.UI
Margin = new MarginPadding { Right = 5 }, Margin = new MarginPadding { Right = 5 },
}; };
protected override PercentageCounter CreateAccuracyCounter() => new PercentageCounter() protected override PercentageCounter CreateAccuracyCounter() => new PercentageCounter
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
@ -31,7 +31,7 @@ namespace osu.Game.Modes.Osu.UI
Margin = new MarginPadding { Right = 5 }, Margin = new MarginPadding { Right = 5 },
}; };
protected override ComboCounter CreateComboCounter() => new OsuComboCounter() protected override ComboCounter CreateComboCounter() => new OsuComboCounter
{ {
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,

View File

@ -70,6 +70,7 @@
<Compile Include="Objects\OsuHitObjectParser.cs" /> <Compile Include="Objects\OsuHitObjectParser.cs" />
<Compile Include="Objects\SliderCurve.cs" /> <Compile Include="Objects\SliderCurve.cs" />
<Compile Include="Objects\SliderTick.cs" /> <Compile Include="Objects\SliderTick.cs" />
<Compile Include="OsuAutoReplay.cs" />
<Compile Include="OsuDifficultyCalculator.cs" /> <Compile Include="OsuDifficultyCalculator.cs" />
<Compile Include="OsuScore.cs" /> <Compile Include="OsuScore.cs" />
<Compile Include="OsuScoreProcessor.cs" /> <Compile Include="OsuScoreProcessor.cs" />

View File

@ -10,7 +10,7 @@ using OpenTK;
namespace osu.Game.Modes.Taiko.Objects.Drawable namespace osu.Game.Modes.Taiko.Objects.Drawable
{ {
class DrawableTaikoHit : Sprite internal class DrawableTaikoHit : Sprite
{ {
private TaikoBaseHit h; private TaikoBaseHit h;

View File

@ -8,7 +8,7 @@ using osu.Game.Beatmaps;
namespace osu.Game.Modes.Taiko.Objects namespace osu.Game.Modes.Taiko.Objects
{ {
class TaikoConverter : HitObjectConverter<TaikoBaseHit> internal class TaikoConverter : HitObjectConverter<TaikoBaseHit>
{ {
public override List<TaikoBaseHit> Convert(Beatmap beatmap) public override List<TaikoBaseHit> Convert(Beatmap beatmap)
{ {

View File

@ -1,8 +1,6 @@
// 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;
namespace osu.Game.Modes.Taiko namespace osu.Game.Modes.Taiko
{ {
public class TaikoModNoFail : ModNoFail public class TaikoModNoFail : ModNoFail

View File

@ -8,6 +8,7 @@ using osu.Game.Modes.Osu.UI;
using osu.Game.Modes.Taiko.UI; using osu.Game.Modes.Taiko.UI;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Screens.Play;
namespace osu.Game.Modes.Taiko namespace osu.Game.Modes.Taiko
{ {
@ -15,7 +16,11 @@ namespace osu.Game.Modes.Taiko
{ {
public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay(); public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay();
public override HitRenderer CreateHitRendererWith(Beatmap beatmap) => new TaikoHitRenderer { Beatmap = beatmap }; public override HitRenderer CreateHitRendererWith(Beatmap beatmap, PlayerInputManager input = null) => new TaikoHitRenderer
{
Beatmap = beatmap,
InputManager = input,
};
public override IEnumerable<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {
@ -76,7 +81,7 @@ namespace osu.Game.Modes.Taiko
public override FontAwesome Icon => FontAwesome.fa_osu_taiko_o; public override FontAwesome Icon => FontAwesome.fa_osu_taiko_o;
public override ScoreProcessor CreateScoreProcessor(int hitObjectCount) => null; public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null;
public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser(); public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser();

View File

@ -12,8 +12,8 @@ namespace osu.Game.Modes.Taiko.UI
{ {
protected override HitObjectConverter<TaikoBaseHit> Converter => new TaikoConverter(); protected override HitObjectConverter<TaikoBaseHit> Converter => new TaikoConverter();
protected override Playfield CreatePlayfield() => new TaikoPlayfield(); protected override Playfield<TaikoBaseHit> CreatePlayfield() => new TaikoPlayfield();
protected override DrawableHitObject GetVisualRepresentation(TaikoBaseHit h) => null;// new DrawableTaikoHit(h); protected override DrawableHitObject<TaikoBaseHit> GetVisualRepresentation(TaikoBaseHit h) => null;// new DrawableTaikoHit(h);
} }
} }

View File

@ -5,13 +5,14 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Modes.Taiko.Objects;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
namespace osu.Game.Modes.Taiko.UI namespace osu.Game.Modes.Taiko.UI
{ {
public class TaikoPlayfield : Playfield public class TaikoPlayfield : Playfield<TaikoBaseHit>
{ {
public TaikoPlayfield() public TaikoPlayfield()
{ {

View File

@ -23,7 +23,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[TestFixture] [TestFixture]
public class ImportBeatmapTest public class ImportBeatmapTest
{ {
const string osz_path = @"../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz"; private const string osz_path = @"../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz";
[OneTimeSetUp] [OneTimeSetUp]
public void SetUp() public void SetUp()
@ -69,7 +69,7 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.IsTrue(File.Exists(temp)); Assert.IsTrue(File.Exists(temp));
var importer = new BeatmapImporter(client); var importer = new BeatmapIPCChannel(client);
if (!importer.ImportAsync(temp).Wait(1000)) if (!importer.ImportAsync(temp).Wait(1000))
Assert.Fail(@"IPC took too long to send"); Assert.Fail(@"IPC took too long to send");
@ -91,7 +91,7 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.IsTrue(File.Exists(temp)); Assert.IsTrue(File.Exists(temp));
using (FileStream stream = File.OpenRead(temp)) using (File.OpenRead(temp))
osu.Dependencies.Get<BeatmapDatabase>().Import(temp); osu.Dependencies.Get<BeatmapDatabase>().Import(temp);
ensureLoaded(osu); ensureLoaded(osu);
@ -107,7 +107,7 @@ namespace osu.Game.Tests.Beatmaps.IO
private string prepareTempCopy(string path) private string prepareTempCopy(string path)
{ {
var temp = Path.GetTempFileName(); var temp = Path.GetTempFileName();
return new FileInfo(osz_path).CopyTo(temp, true).FullName; return new FileInfo(path).CopyTo(temp, true).FullName;
} }
private OsuGameBase loadOsu(GameHost host) private OsuGameBase loadOsu(GameHost host)
@ -130,13 +130,13 @@ namespace osu.Game.Tests.Beatmaps.IO
Action waitAction = () => Action waitAction = () =>
{ {
while ((resultSets = osu.Dependencies.Get<BeatmapDatabase>() while (!(resultSets = osu.Dependencies.Get<BeatmapDatabase>()
.Query<BeatmapSetInfo>().Where(s => s.OnlineBeatmapSetID == 241526)).Count() == 0) .Query<BeatmapSetInfo>().Where(s => s.OnlineBeatmapSetID == 241526)).Any())
Thread.Sleep(50); Thread.Sleep(50);
}; };
Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout), Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout),
$@"BeatmapSet did not import to the database in allocated time."); @"BeatmapSet did not import to the database in allocated time.");
//ensure we were stored to beatmap database backing... //ensure we were stored to beatmap database backing...
@ -168,7 +168,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var beatmap = osu.Dependencies.Get<BeatmapDatabase>().GetWorkingBeatmap(set.Beatmaps.First(b => b.Mode == PlayMode.Osu))?.Beatmap; var beatmap = osu.Dependencies.Get<BeatmapDatabase>().GetWorkingBeatmap(set.Beatmaps.First(b => b.Mode == PlayMode.Osu))?.Beatmap;
Assert.IsTrue(beatmap.HitObjects.Count > 0); Assert.IsTrue(beatmap?.HitObjects.Count > 0);
} }
} }
} }

View File

@ -77,7 +77,7 @@ namespace osu.Game.Tests.Beatmaps.IO
using (var stream = new StreamReader( using (var stream = new StreamReader(
reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
{ {
Assert.AreEqual("osu file format v13", stream.ReadLine().Trim()); Assert.AreEqual("osu file format v13", stream.ReadLine()?.Trim());
} }
} }
} }

View File

@ -37,7 +37,7 @@ namespace osu.Game.Beatmaps
protected abstract HitObjectConverter<T> Converter { get; } protected abstract HitObjectConverter<T> Converter { get; }
public DifficultyCalculator(Beatmap beatmap) protected DifficultyCalculator(Beatmap beatmap)
{ {
Objects = Converter.Convert(beatmap); Objects = Converter.Convert(beatmap);
PreprocessHitObjects(); PreprocessHitObjects();

View File

@ -6,7 +6,7 @@ using osu.Framework.Graphics.Sprites;
namespace osu.Game.Beatmaps.Drawables namespace osu.Game.Beatmaps.Drawables
{ {
class BeatmapBackgroundSprite : Sprite internal class BeatmapBackgroundSprite : Sprite
{ {
private readonly WorkingBeatmap working; private readonly WorkingBeatmap working;

View File

@ -10,7 +10,7 @@ using osu.Game.Database;
namespace osu.Game.Beatmaps.Drawables namespace osu.Game.Beatmaps.Drawables
{ {
class BeatmapGroup : IStateful<BeatmapGroupState> internal class BeatmapGroup : IStateful<BeatmapGroupState>
{ {
public BeatmapPanel SelectedPanel; public BeatmapPanel SelectedPanel;

View File

@ -18,7 +18,7 @@ using osu.Game.Graphics.Sprites;
namespace osu.Game.Beatmaps.Drawables namespace osu.Game.Beatmaps.Drawables
{ {
class BeatmapPanel : Panel internal class BeatmapPanel : Panel
{ {
public BeatmapInfo Beatmap; public BeatmapInfo Beatmap;
private Sprite background; private Sprite background;

View File

@ -17,7 +17,7 @@ using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawables namespace osu.Game.Beatmaps.Drawables
{ {
class BeatmapSetHeader : Panel internal class BeatmapSetHeader : Panel
{ {
public Action<BeatmapSetHeader> GainedSelection; public Action<BeatmapSetHeader> GainedSelection;
private SpriteText title, artist; private SpriteText title, artist;
@ -96,7 +96,7 @@ namespace osu.Game.Beatmaps.Drawables
base.Dispose(isDisposing); base.Dispose(isDisposing);
} }
class PanelBackground : BufferedContainer private class PanelBackground : BufferedContainer
{ {
private readonly WorkingBeatmap working; private readonly WorkingBeatmap working;
@ -160,7 +160,7 @@ namespace osu.Game.Beatmaps.Drawables
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
FillMode = FillMode.Fill, FillMode = FillMode.Fill,
}.LoadAsync(game, (bg) => }.LoadAsync(game, bg =>
{ {
Add(bg); Add(bg);
ForceRedraw(); ForceRedraw();

View File

@ -12,7 +12,7 @@ using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawables namespace osu.Game.Beatmaps.Drawables
{ {
class DifficultyIcon : Container internal class DifficultyIcon : Container
{ {
private readonly BeatmapInfo beatmap; private readonly BeatmapInfo beatmap;
private OsuColour palette; private OsuColour palette;
@ -34,6 +34,7 @@ namespace osu.Game.Beatmaps.Drawables
new TextAwesome new TextAwesome
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = Size.X, TextSize = Size.X,
Colour = getColour(beatmap), Colour = getColour(beatmap),
Icon = FontAwesome.fa_circle Icon = FontAwesome.fa_circle
@ -41,6 +42,7 @@ namespace osu.Game.Beatmaps.Drawables
new TextAwesome new TextAwesome
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = Size.X, TextSize = Size.X,
Colour = Color4.White, Colour = Color4.White,
Icon = Ruleset.GetRuleset(beatmap.Mode).Icon Icon = Ruleset.GetRuleset(beatmap.Mode).Icon
@ -48,7 +50,7 @@ namespace osu.Game.Beatmaps.Drawables
}; };
} }
enum DifficultyRating private enum DifficultyRating
{ {
Easy, Easy,
Normal, Normal,

View File

@ -12,7 +12,7 @@ using osu.Framework.Extensions.Color4Extensions;
namespace osu.Game.Beatmaps.Drawables namespace osu.Game.Beatmaps.Drawables
{ {
class Panel : Container, IStateful<PanelSelectedState> internal class Panel : Container, IStateful<PanelSelectedState>
{ {
public const float MAX_HEIGHT = 80; public const float MAX_HEIGHT = 80;
@ -115,7 +115,7 @@ namespace osu.Game.Beatmaps.Drawables
} }
} }
enum PanelSelectedState internal enum PanelSelectedState
{ {
Hidden, Hidden,
NotSelected, NotSelected,

View File

@ -17,8 +17,9 @@ namespace osu.Game.Beatmaps.Formats
public static BeatmapDecoder GetDecoder(TextReader stream) public static BeatmapDecoder GetDecoder(TextReader stream)
{ {
var line = stream.ReadLine().Trim(); var line = stream.ReadLine()?.Trim();
if (!decoders.ContainsKey(line))
if (line == null || !decoders.ContainsKey(line))
throw new IOException(@"Unknown file format"); throw new IOException(@"Unknown file format");
return (BeatmapDecoder)Activator.CreateInstance(decoders[line]); return (BeatmapDecoder)Activator.CreateInstance(decoders[line]);
} }

View File

@ -197,7 +197,7 @@ namespace osu.Game.Beatmaps.Formats
if (split.Length > 2) if (split.Length > 2)
{ {
int kiaiFlags = split.Length > 7 ? Convert.ToInt32(split[7], NumberFormatInfo.InvariantInfo) : 0; //int kiaiFlags = split.Length > 7 ? Convert.ToInt32(split[7], NumberFormatInfo.InvariantInfo) : 0;
double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo); double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo);
cp = new ControlPoint cp = new ControlPoint
{ {
@ -219,15 +219,18 @@ namespace osu.Game.Beatmaps.Formats
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {val}"); throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {val}");
byte r, g, b; byte r, g, b;
if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b)) if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b))
throw new InvalidOperationException($@"Color must be specified with 8-bit integer components"); throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
// Note: the combo index specified in the beatmap is discarded // Note: the combo index specified in the beatmap is discarded
beatmap.ComboColors.Add(new Color4 if (key.StartsWith(@"Combo"))
{ {
R = r / 255f, beatmap.ComboColors.Add(new Color4
G = g / 255f, {
B = b / 255f, R = r / 255f,
A = 1f, G = g / 255f,
}); B = b / 255f,
A = 1f,
});
}
} }
protected override void ParseFile(TextReader stream, Beatmap beatmap) protected override void ParseFile(TextReader stream, Beatmap beatmap)
@ -235,10 +238,9 @@ namespace osu.Game.Beatmaps.Formats
HitObjectParser parser = null; HitObjectParser parser = null;
var section = Section.None; var section = Section.None;
string line;
while (true) while (true)
{ {
line = stream.ReadLine(); var line = stream.ReadLine();
if (line == null) if (line == null)
break; break;
if (string.IsNullOrEmpty(line)) if (string.IsNullOrEmpty(line))

View File

@ -15,7 +15,8 @@ namespace osu.Game.Beatmaps.Timing
public double BeatLength; public double BeatLength;
public double VelocityAdjustment; public double VelocityAdjustment;
public bool TimingChange; public bool TimingChange;
public bool KiaiMode;
} }
internal enum TimeSignatures internal enum TimeSignatures

View File

@ -3,7 +3,7 @@
namespace osu.Game.Beatmaps.Timing namespace osu.Game.Beatmaps.Timing
{ {
class TimingChange : ControlPoint internal class TimingChange : ControlPoint
{ {
public TimingChange(double beatLength) public TimingChange(double beatLength)
{ {

View File

@ -2,12 +2,15 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO; using osu.Game.Beatmaps.IO;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Modes;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
@ -17,6 +20,16 @@ namespace osu.Game.Beatmaps
public readonly BeatmapSetInfo BeatmapSetInfo; public readonly BeatmapSetInfo BeatmapSetInfo;
/// <summary>
/// A play mode that is preferred for this beatmap. PlayMode will become this mode where conversion is feasible,
/// or otherwise to the beatmap's default.
/// </summary>
public PlayMode? PreferredPlayMode;
public PlayMode PlayMode => beatmap?.BeatmapInfo?.Mode > PlayMode.Osu ? beatmap.BeatmapInfo.Mode : PreferredPlayMode ?? PlayMode.Osu;
public readonly Bindable<IEnumerable<Mod>> Mods = new Bindable<IEnumerable<Mod>>();
public readonly bool WithStoryboard; public readonly bool WithStoryboard;
protected abstract ArchiveReader GetReader(); protected abstract ArchiveReader GetReader();

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Security.Cryptography; using osu.Framework.Extensions;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -20,19 +20,20 @@ namespace osu.Game.Database
{ {
public class BeatmapDatabase public class BeatmapDatabase
{ {
private SQLiteConnection connection { get; set; } private SQLiteConnection connection { get; }
private Storage storage; private Storage storage;
public event Action<BeatmapSetInfo> BeatmapSetAdded; public event Action<BeatmapSetInfo> BeatmapSetAdded;
public event Action<BeatmapSetInfo> BeatmapSetRemoved; public event Action<BeatmapSetInfo> BeatmapSetRemoved;
private BeatmapImporter ipc; // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private BeatmapIPCChannel ipc;
public BeatmapDatabase(Storage storage, GameHost importHost = null) public BeatmapDatabase(Storage storage, IIpcHost importHost = null)
{ {
this.storage = storage; this.storage = storage;
if (importHost != null) if (importHost != null)
ipc = new BeatmapImporter(importHost, this); ipc = new BeatmapIPCChannel(importHost, this);
if (connection == null) if (connection == null)
{ {
@ -73,7 +74,7 @@ namespace osu.Game.Database
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Error(e, $@"Could not delete beatmap {b.ToString()}"); Logger.Error(e, $@"Could not delete beatmap {b}");
} }
} }
@ -149,9 +150,9 @@ namespace osu.Game.Database
catch (Exception e) catch (Exception e)
{ {
e = e.InnerException ?? e; e = e.InnerException ?? e;
Logger.Error(e, $@"Could not import beatmap set"); Logger.Error(e, @"Could not import beatmap set");
} }
// Batch commit with multiple sets to database // Batch commit with multiple sets to database
Import(sets); Import(sets);
} }
@ -162,7 +163,7 @@ namespace osu.Game.Database
/// <param name="path">Location on disk</param> /// <param name="path">Location on disk</param>
public void Import(string path) public void Import(string path)
{ {
Import(new [] { path }); Import(new[] { path });
} }
/// <summary> /// <summary>
@ -181,10 +182,9 @@ namespace osu.Game.Database
if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader
{ {
using (var md5 = MD5.Create())
using (var input = storage.GetStream(path)) using (var input = storage.GetStream(path))
{ {
hash = BitConverter.ToString(md5.ComputeHash(input)).Replace("-", "").ToLowerInvariant(); hash = input.GetMd5Hash();
input.Seek(0, SeekOrigin.Begin); input.Seek(0, SeekOrigin.Begin);
path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash); path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash);
if (!storage.Exists(path)) if (!storage.Exists(path))
@ -216,22 +216,29 @@ namespace osu.Game.Database
Metadata = metadata Metadata = metadata
}; };
using (var reader = ArchiveReader.GetReader(storage, path)) using (var archive = ArchiveReader.GetReader(storage, path))
{ {
string[] mapNames = reader.BeatmapFilenames; string[] mapNames = archive.BeatmapFilenames;
foreach (var name in mapNames) foreach (var name in mapNames)
using (var stream = new StreamReader(reader.GetStream(name))) using (var raw = archive.GetStream(name))
using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit
using (var sr = new StreamReader(ms))
{ {
var decoder = BeatmapDecoder.GetDecoder(stream); raw.CopyTo(ms);
Beatmap beatmap = decoder.Decode(stream); ms.Position = 0;
var decoder = BeatmapDecoder.GetDecoder(sr);
Beatmap beatmap = decoder.Decode(sr);
beatmap.BeatmapInfo.Path = name; beatmap.BeatmapInfo.Path = name;
beatmap.BeatmapInfo.Hash = ms.GetMd5Hash();
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary // TODO: Diff beatmap metadata with set metadata and leave it here if necessary
beatmap.BeatmapInfo.Metadata = null; beatmap.BeatmapInfo.Metadata = null;
beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
} }
beatmapSet.StoryboardFile = reader.StoryboardFilename; beatmapSet.StoryboardFile = archive.StoryboardFilename;
} }
return beatmapSet; return beatmapSet;
@ -318,8 +325,7 @@ namespace osu.Game.Database
return item; return item;
} }
readonly Type[] validTypes = new[] private readonly Type[] validTypes = {
{
typeof(BeatmapSetInfo), typeof(BeatmapSetInfo),
typeof(BeatmapInfo), typeof(BeatmapInfo),
typeof(BeatmapMetadata), typeof(BeatmapMetadata),
@ -329,7 +335,7 @@ namespace osu.Game.Database
public void Update<T>(T record, bool cascade = true) where T : class public void Update<T>(T record, bool cascade = true) where T : class
{ {
if (validTypes.All(t => t != typeof(T))) if (validTypes.All(t => t != typeof(T)))
throw new ArgumentException(nameof(T), "Must be a type managed by BeatmapDatabase"); throw new ArgumentException("Must be a type managed by BeatmapDatabase", nameof(T));
if (cascade) if (cascade)
connection.UpdateWithChildren(record); connection.UpdateWithChildren(record);
else else

View File

@ -15,9 +15,9 @@ namespace osu.Game.Database
[PrimaryKey, AutoIncrement] [PrimaryKey, AutoIncrement]
public int ID { get; set; } public int ID { get; set; }
public int? OnlineBeatmapID { get; set; } = null; public int? OnlineBeatmapID { get; set; }
public int? OnlineBeatmapSetID { get; set; } = null; public int? OnlineBeatmapSetID { get; set; }
[ForeignKey(typeof(BeatmapSetInfo))] [ForeignKey(typeof(BeatmapSetInfo))]
public int BeatmapSetInfoID { get; set; } public int BeatmapSetInfoID { get; set; }
@ -39,6 +39,8 @@ namespace osu.Game.Database
public string Path { get; set; } public string Path { get; set; }
public string Hash { get; set; }
// General // General
public int AudioLeadIn { get; set; } public int AudioLeadIn { get; set; }
public bool Countdown { get; set; } public bool Countdown { get; set; }
@ -57,13 +59,14 @@ namespace osu.Game.Database
{ {
get get
{ {
return StoredBookmarks.Split(',').Select(b => int.Parse(b)).ToArray(); return StoredBookmarks.Split(',').Select(int.Parse).ToArray();
} }
set set
{ {
StoredBookmarks = string.Join(",", value); StoredBookmarks = string.Join(",", value);
} }
} }
public double DistanceSpacing { get; set; } public double DistanceSpacing { get; set; }
public int BeatDivisor { get; set; } public int BeatDivisor { get; set; }
public int GridSize { get; set; } public int GridSize { get; set; }
@ -77,7 +80,7 @@ namespace osu.Game.Database
{ {
get get
{ {
return (starDifficulty < 0) ? (BaseDifficulty?.OverallDifficulty ?? 5) : starDifficulty; return starDifficulty < 0 ? (BaseDifficulty?.OverallDifficulty ?? 5) : starDifficulty;
} }
set { starDifficulty = value; } set { starDifficulty = value; }

View File

@ -10,7 +10,7 @@ namespace osu.Game.Database
[PrimaryKey, AutoIncrement] [PrimaryKey, AutoIncrement]
public int ID { get; set; } public int ID { get; set; }
public int? OnlineBeatmapSetID { get; set; } = null; public int? OnlineBeatmapSetID { get; set; }
public string Title { get; set; } public string Title { get; set; }
public string TitleUnicode { get; set; } public string TitleUnicode { get; set; }

View File

@ -12,7 +12,7 @@ namespace osu.Game.Database
[PrimaryKey, AutoIncrement] [PrimaryKey, AutoIncrement]
public int ID { get; set; } public int ID { get; set; }
public int? OnlineBeatmapSetID { get; set; } = null; public int? OnlineBeatmapSetID { get; set; }
[OneToOne(CascadeOperations = CascadeOperation.All)] [OneToOne(CascadeOperations = CascadeOperation.All)]
public BeatmapMetadata Metadata { get; set; } public BeatmapMetadata Metadata { get; set; }

View File

@ -0,0 +1,110 @@
// 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.IO;
using osu.Framework.Platform;
using osu.Game.IO.Legacy;
using osu.Game.IPC;
using osu.Game.Modes;
using SharpCompress.Compressors.LZMA;
namespace osu.Game.Database
{
public class ScoreDatabase
{
private readonly Storage storage;
private readonly BeatmapDatabase beatmaps;
private const string replay_folder = @"replays";
private ScoreIPCChannel ipc;
public ScoreDatabase(Storage storage, IIpcHost importHost = null, BeatmapDatabase beatmaps = null)
{
this.storage = storage;
this.beatmaps = beatmaps;
if (importHost != null)
ipc = new ScoreIPCChannel(importHost, this);
}
public Score ReadReplayFile(string replayFilename)
{
Score score;
using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename)))
using (SerializationReader sr = new SerializationReader(s))
{
var ruleset = Ruleset.GetRuleset((PlayMode)sr.ReadByte());
var processor = ruleset.CreateScoreProcessor();
score = processor.GetScore();
/* score.Pass = true;*/
var version = sr.ReadInt32();
/* score.FileChecksum = */
var beatmapHash = sr.ReadString();
score.Beatmap = beatmaps.Query<BeatmapInfo>().Where(b => b.Hash == beatmapHash).FirstOrDefault();
/* score.PlayerName = */
sr.ReadString();
/* var localScoreChecksum = */
sr.ReadString();
/* score.Count300 = */
sr.ReadUInt16();
/* score.Count100 = */
sr.ReadUInt16();
/* score.Count50 = */
sr.ReadUInt16();
/* score.CountGeki = */
sr.ReadUInt16();
/* score.CountKatu = */
sr.ReadUInt16();
/* score.CountMiss = */
sr.ReadUInt16();
score.TotalScore = sr.ReadInt32();
score.MaxCombo = sr.ReadUInt16();
/* score.Perfect = */
sr.ReadBoolean();
/* score.EnabledMods = (Mods)*/
sr.ReadInt32();
/* score.HpGraphString = */
sr.ReadString();
/* score.Date = */
sr.ReadDateTime();
var compressedReplay = sr.ReadByteArray();
if (version >= 20140721)
/*OnlineId =*/
sr.ReadInt64();
else if (version >= 20121008)
/*OnlineId =*/
sr.ReadInt32();
using (var replayInStream = new MemoryStream(compressedReplay))
{
byte[] properties = new byte[5];
if (replayInStream.Read(properties, 0, 5) != 5)
throw (new Exception("input .lzma is too short"));
long outSize = 0;
for (int i = 0; i < 8; i++)
{
int v = replayInStream.ReadByte();
if (v < 0)
throw (new Exception("Can't Read 1"));
outSize |= ((long)(byte)v) << (8 * i);
}
long compressedSize = replayInStream.Length - replayInStream.Position;
using (var lzma = new LzmaStream(properties, replayInStream, compressedSize, outSize))
using (var reader = new StreamReader(lzma))
score.Replay = new LegacyReplay(reader);
}
}
return score;
}
}
}

View File

@ -14,7 +14,7 @@ namespace osu.Game.Graphics.Backgrounds
{ {
public Sprite Sprite; public Sprite Sprite;
string textureName; private string textureName;
public Background(string textureName = @"") public Background(string textureName = @"")
{ {

View File

@ -117,7 +117,7 @@ namespace osu.Game.Graphics.Backgrounds
private void addTriangle(bool randomY) private void addTriangle(bool randomY)
{ {
var sprite = CreateTriangle(); var sprite = CreateTriangle();
float triangleHeight = (sprite.DrawHeight / DrawHeight); float triangleHeight = sprite.DrawHeight / DrawHeight;
sprite.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() * (1 + triangleHeight) - triangleHeight : 1); sprite.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() * (1 + triangleHeight) - triangleHeight : 1);
Add(sprite); Add(sprite);
} }

View File

@ -12,7 +12,7 @@ using osu.Framework.Configuration;
namespace osu.Game.Graphics.Containers namespace osu.Game.Graphics.Containers
{ {
class ParallaxContainer : Container internal class ParallaxContainer : Container
{ {
public float ParallaxAmount = 0.02f; public float ParallaxAmount = 0.02f;
@ -51,7 +51,7 @@ namespace osu.Game.Graphics.Containers
}; };
} }
bool firstUpdate = true; private bool firstUpdate = true;
protected override void Update() protected override void Update()
{ {

View File

@ -16,8 +16,7 @@ using osu.Framework.Graphics.Colour;
namespace osu.Game.Graphics.Cursor namespace osu.Game.Graphics.Cursor
{ {
internal class CursorTrail : Drawable
class CursorTrail : Drawable
{ {
public override bool Contains(Vector2 screenSpacePos) => true; public override bool Contains(Vector2 screenSpacePos) => true;
public override bool HandleInput => true; public override bool HandleInput => true;
@ -32,7 +31,7 @@ namespace osu.Game.Graphics.Cursor
private double timeOffset; private double timeOffset;
private float time; private float time;
private TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData(); private TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData();
private const int max_sprites = 2048; private const int max_sprites = 2048;
@ -46,7 +45,7 @@ namespace osu.Game.Graphics.Cursor
{ {
base.ApplyDrawNode(node); base.ApplyDrawNode(node);
TrailDrawNode tNode = node as TrailDrawNode; TrailDrawNode tNode = (TrailDrawNode)node;
tNode.Shader = shader; tNode.Shader = shader;
tNode.Texture = texture; tNode.Texture = texture;
tNode.Size = size; tNode.Size = size;
@ -77,6 +76,12 @@ namespace osu.Game.Graphics.Cursor
Scale = new Vector2(1 / texture.ScaleAdjust); Scale = new Vector2(1 / texture.ScaleAdjust);
} }
protected override void LoadComplete()
{
base.LoadComplete();
resetTime();
}
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
@ -117,7 +122,7 @@ namespace osu.Game.Graphics.Cursor
float distance = diff.Length; float distance = diff.Length;
Vector2 direction = diff / distance; Vector2 direction = diff / distance;
float interval = (size.X / 2) * 0.9f; float interval = size.X / 2 * 0.9f;
for (float d = interval; d < distance; d += interval) for (float d = interval; d < distance; d += interval)
{ {
@ -137,7 +142,7 @@ namespace osu.Game.Graphics.Cursor
currentIndex = (currentIndex + 1) % max_sprites; currentIndex = (currentIndex + 1) % max_sprites;
} }
struct TrailPart private struct TrailPart
{ {
public Vector2 Position; public Vector2 Position;
public float Time; public float Time;
@ -145,12 +150,12 @@ namespace osu.Game.Graphics.Cursor
public bool WasUpdated; public bool WasUpdated;
} }
class TrailDrawNodeSharedData private class TrailDrawNodeSharedData
{ {
public VertexBuffer<TexturedVertex2D> VertexBuffer; public VertexBuffer<TexturedVertex2D> VertexBuffer;
} }
class TrailDrawNode : DrawNode private class TrailDrawNode : DrawNode
{ {
public Shader Shader; public Shader Shader;
public Texture Texture; public Texture Texture;

View File

@ -17,7 +17,7 @@ using System;
namespace osu.Game.Graphics.Cursor namespace osu.Game.Graphics.Cursor
{ {
class OsuCursorContainer : CursorContainer public class OsuCursorContainer : CursorContainer
{ {
protected override Drawable CreateCursor() => new OsuCursor(); protected override Drawable CreateCursor() => new OsuCursor();
@ -40,7 +40,7 @@ namespace osu.Game.Graphics.Cursor
return base.OnMouseUp(state, args); return base.OnMouseUp(state, args);
} }
class OsuCursor : Container public class OsuCursor : Container
{ {
private Container cursorContainer; private Container cursorContainer;
private Bindable<double> cursorScale; private Bindable<double> cursorScale;

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