1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-06 06:57:39 +08:00

Merge branch 'master' of https://github.com/ppy/osu into Private_Messages

# Conflicts:
#	osu.Game/Overlays/Chat/ChatTabControl.cs
#	osu.Game/Overlays/ChatOverlay.cs
This commit is contained in:
miterosan 2018-07-23 15:45:54 +02:00
commit 9f43e0c900
251 changed files with 1959 additions and 1193 deletions

View File

@ -26,7 +26,7 @@ Build and run
- Using Visual Studio 2017, Rider or Visual Studio Code (configurations are included)
- From command line using `dotnet run --project osu.Desktop --framework netcoreapp2.1`
The above methods should automatically do so, but if you run into issues building you may need to restore nuget packages (commonly via `dotnet restore`).
If you run into issues building you may need to restore nuget packages (commonly via `dotnet restore`). Visual Studio Code users must run `Restore` task from debug tab before attempt to build.
# Contributing

View File

@ -2,6 +2,9 @@ clone_depth: 1
version: '{branch}-{build}'
image: Visual Studio 2017
configuration: Debug
cache:
- C:\ProgramData\chocolatey\bin -> appveyor.yml
- C:\ProgramData\chocolatey\lib -> appveyor.yml
install:
- cmd: git submodule update --init --recursive --depth=5
- cmd: choco install resharper-clt -y
@ -18,4 +21,4 @@ build:
verbosity: minimal
after_build:
- cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors

View File

@ -12,6 +12,7 @@ using osu.Framework.Platform;
using osu.Game;
using OpenTK.Input;
using Microsoft.Win32;
using osu.Desktop.Updater;
using osu.Framework.Platform.Windows;
namespace osu.Desktop
@ -38,6 +39,52 @@ namespace osu.Desktop
}
}
protected override void LoadComplete()
{
base.LoadComplete();
if (!noVersionOverlay)
{
LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v =>
{
Add(v);
v.State = Visibility.Visible;
});
#if NET_FRAMEWORK
Add(new SquirrelUpdateManager());
#else
Add(new SimpleUpdateManager());
#endif
}
}
public override void SetHost(GameHost host)
{
base.SetHost(host);
var desktopWindow = host.Window as DesktopGameWindow;
if (desktopWindow != null)
{
desktopWindow.CursorState |= CursorState.Hidden;
desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico"));
desktopWindow.Title = Name;
desktopWindow.FileDrop += fileDrop;
}
}
private void fileDrop(object sender, FileDropEventArgs e)
{
var filePaths = new[] { e.FileName };
var firstExtension = Path.GetExtension(filePaths.First());
if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
}
/// <summary>
/// A method of accessing an osu-stable install in a controlled fashion.
/// </summary>
@ -77,45 +124,5 @@ namespace osu.Desktop
{
}
}
protected override void LoadComplete()
{
base.LoadComplete();
if (!noVersionOverlay)
{
LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v =>
{
Add(v);
v.State = Visibility.Visible;
});
}
}
public override void SetHost(GameHost host)
{
base.SetHost(host);
var desktopWindow = host.Window as DesktopGameWindow;
if (desktopWindow != null)
{
desktopWindow.CursorState |= CursorState.Hidden;
desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico"));
desktopWindow.Title = Name;
desktopWindow.FileDrop += fileDrop;
}
}
private void fileDrop(object sender, FileDropEventArgs e)
{
var filePaths = new[] { e.FileName };
var firstExtension = Path.GetExtension(filePaths.First());
if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
}
}
}

View File

@ -91,10 +91,6 @@ namespace osu.Desktop.Overlays
}
}
};
#if NET_FRAMEWORK
Add(new SquirrelUpdateManager());
#endif
}
protected override void LoadComplete()

View File

@ -7,6 +7,7 @@ using System.Linq;
using osu.Framework;
using osu.Framework.Platform;
using osu.Game.IPC;
#if NET_FRAMEWORK
using System.Runtime;
#endif
@ -18,10 +19,7 @@ namespace osu.Desktop
[STAThread]
public static int Main(string[] args)
{
// required to initialise native SQLite libraries on some platforms.
if (!RuntimeInfo.IsMono)
useMulticoreJit();
useMultiCoreJit();
// Back up the cwd before DesktopGameHost changes it
var cwd = Environment.CurrentDirectory;
@ -54,7 +52,7 @@ namespace osu.Desktop
}
}
private static void useMulticoreJit()
private static void useMultiCoreJit()
{
#if NET_FRAMEWORK
var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles"));

View File

@ -0,0 +1,103 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.IO.Network;
using osu.Framework.Platform;
using osu.Game;
using osu.Game.Graphics;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
namespace osu.Desktop.Updater
{
/// <summary>
/// An update manager that shows notifications if a newer release is detected.
/// Installation is left up to the user.
/// </summary>
internal class SimpleUpdateManager : CompositeDrawable
{
private NotificationOverlay notificationOverlay;
private string version;
private GameHost host;
[BackgroundDependencyLoader]
private void load(NotificationOverlay notification, OsuGameBase game, GameHost host)
{
notificationOverlay = notification;
this.host = host;
version = game.Version;
if (game.IsDeployedBuild)
Schedule(() => Task.Run(() => checkForUpdateAsync()));
}
private async void checkForUpdateAsync()
{
var releases = new JsonWebRequest<GitHubRelease>("https://api.github.com/repos/ppy/osu/releases/latest");
await releases.PerformAsync();
var latest = releases.ResponseObject;
if (latest.TagName != version)
{
notificationOverlay.Post(new SimpleNotification
{
Text = $"A newer release of osu! has been found ({version} → {latest.TagName}).\n\n"
+ "Click here to download the new version, which can be installed over the top of your existing installation",
Icon = FontAwesome.fa_upload,
Activated = () =>
{
host.OpenUrlExternally(getBestUrl(latest));
return true;
}
});
}
}
private string getBestUrl(GitHubRelease release)
{
GitHubAsset bestAsset = null;
switch (RuntimeInfo.OS)
{
case RuntimeInfo.Platform.Windows:
bestAsset = release.Assets?.FirstOrDefault(f => f.Name.EndsWith(".exe"));
break;
case RuntimeInfo.Platform.MacOsx:
bestAsset = release.Assets?.FirstOrDefault(f => f.Name.EndsWith(".app.zip"));
break;
}
return bestAsset?.BrowserDownloadUrl ?? release.HtmlUrl;
}
public class GitHubRelease
{
[JsonProperty("html_url")]
public string HtmlUrl { get; set; }
[JsonProperty("tag_name")]
public string TagName { get; set; }
[JsonProperty("assets")]
public List<GitHubAsset> Assets { get; set; }
}
public class GitHubAsset
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("browser_download_url")]
public string BrowserDownloadUrl { get; set; }
}
}
}

View File

@ -3,6 +3,7 @@
#if NET_FRAMEWORK
using System;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@ -16,7 +17,7 @@ using OpenTK;
using OpenTK.Graphics;
using Squirrel;
namespace osu.Desktop.Overlays
namespace osu.Desktop.Updater
{
public class SquirrelUpdateManager : Component
{
@ -35,7 +36,7 @@ namespace osu.Desktop.Overlays
notificationOverlay = notification;
if (game.IsDeployedBuild)
Schedule(() => checkForUpdateAsync());
Schedule(() => Task.Run(() => checkForUpdateAsync()));
}
private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)

View File

@ -28,18 +28,20 @@ namespace osu.Game.Rulesets.Catch.Tests
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
{
if (hitObject is JuiceStream stream)
switch (hitObject)
{
foreach (var nested in stream.NestedHitObjects)
yield return new ConvertValue((CatchHitObject)nested);
case JuiceStream stream:
foreach (var nested in stream.NestedHitObjects)
yield return new ConvertValue((CatchHitObject)nested);
break;
case BananaShower shower:
foreach (var nested in shower.NestedHitObjects)
yield return new ConvertValue((CatchHitObject)nested);
break;
default:
yield return new ConvertValue((CatchHitObject)hitObject);
break;
}
else if (hitObject is BananaShower shower)
{
foreach (var nested in shower.NestedHitObjects)
yield return new ConvertValue((CatchHitObject)nested);
}
else
yield return new ConvertValue((CatchHitObject)hitObject);
}
protected override Ruleset CreateRuleset() => new CatchRuleset();

View File

@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
public void ToggleHyperDash(bool status) => MovableCatcher.SetHyperdashState(status ? 2 : 1);
public void ToggleHyperDash(bool status) => MovableCatcher.SetHyperDashState(status ? 2 : 1);
}
}
}

View File

@ -8,9 +8,9 @@ using osu.Game.Rulesets.Catch.Objects;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
public class TestCaseHyperDash : Game.Tests.Visual.TestCasePlayer
{
public TestCaseHyperdash()
public TestCaseHyperDash()
: base(new CatchRuleset())
{
}

View File

@ -46,13 +46,16 @@ namespace osu.Game.Rulesets.Catch.Difficulty
foreach (var hitObject in beatmap.HitObjects)
{
// We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations.
if (hitObject is Fruit)
switch (hitObject)
{
difficultyHitObjects.Add(new CatchDifficultyHitObject((CatchHitObject)hitObject, halfCatchWidth));
// We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations.
case Fruit fruit:
difficultyHitObjects.Add(new CatchDifficultyHitObject(fruit, halfCatchWidth));
break;
case JuiceStream _:
difficultyHitObjects.AddRange(hitObject.NestedHitObjects.OfType<CatchHitObject>().Where(o => !(o is TinyDroplet)).Select(o => new CatchDifficultyHitObject(o, halfCatchWidth)));
break;
}
if (hitObject is JuiceStream)
difficultyHitObjects.AddRange(hitObject.NestedHitObjects.OfType<CatchHitObject>().Where(o => !(o is TinyDroplet)).Select(o => new CatchDifficultyHitObject(o, halfCatchWidth)));
}
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
@ -61,12 +64,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty
return new CatchDifficultyAttributes(mods, 0);
// this is the same as osu!, so there's potential to share the implementation... maybe
double preEmpt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, timeRate)) * star_scaling_factor;
return new CatchDifficultyAttributes(mods, starRating)
{
ApproachRate = preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0,
ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
MaxCombo = difficultyHitObjects.Count
};
}

View File

@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Input;
using osu.Framework.Input.StateChanges;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Replays;

View File

@ -23,8 +23,10 @@ namespace osu.Game.Rulesets.Catch.UI
private readonly CatcherArea catcherArea;
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation)
: base(ScrollingDirection.Down, BASE_WIDTH)
: base(BASE_WIDTH)
{
Direction.Value = ScrollingDirection.Down;
Container explodingFruitContainer;
Anchor = Anchor.TopCentre;

View File

@ -255,11 +255,11 @@ namespace osu.Game.Rulesets.Catch.UI
double positionDifference = target.X * CatchPlayfield.BASE_WIDTH - catcherPosition;
double velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0);
SetHyperdashState(Math.Abs(velocity), target.X);
SetHyperDashState(Math.Abs(velocity), target.X);
}
else
{
SetHyperdashState();
SetHyperDashState();
}
return validCatch;
@ -270,18 +270,18 @@ namespace osu.Game.Rulesets.Catch.UI
private float hyperDashTargetPosition;
/// <summary>
/// Whether we are hypderdashing or not.
/// Whether we are hyper-dashing or not.
/// </summary>
public bool HyperDashing => hyperDashModifier != 1;
/// <summary>
/// Set hyperdash state.
/// Set hyper-dash state.
/// </summary>
/// <param name="modifier">The speed multiplier. If this is less or equals to 1, this catcher will be non-hyperdashing state.</param>
/// <param name="targetPosition">When this catcher crosses this position, this catcher ends hyperdashing.</param>
public void SetHyperdashState(double modifier = 1, float targetPosition = -1)
/// <param name="modifier">The speed multiplier. If this is less or equals to 1, this catcher will be non-hyper-dashing state.</param>
/// <param name="targetPosition">When this catcher crosses this position, this catcher ends hyper-dashing.</param>
public void SetHyperDashState(double modifier = 1, float targetPosition = -1)
{
const float hyperdash_transition_length = 180;
const float hyper_dash_transition_length = 180;
bool previouslyHyperDashing = HyperDashing;
if (modifier <= 1 || X == targetPosition)
@ -291,8 +291,8 @@ namespace osu.Game.Rulesets.Catch.UI
if (previouslyHyperDashing)
{
this.FadeColour(Color4.White, hyperdash_transition_length, Easing.OutQuint);
this.FadeTo(1, hyperdash_transition_length, Easing.OutQuint);
this.FadeColour(Color4.White, hyper_dash_transition_length, Easing.OutQuint);
this.FadeTo(1, hyper_dash_transition_length, Easing.OutQuint);
}
}
else
@ -303,8 +303,8 @@ namespace osu.Game.Rulesets.Catch.UI
if (!previouslyHyperDashing)
{
this.FadeColour(Color4.OrangeRed, hyperdash_transition_length, Easing.OutQuint);
this.FadeTo(0.2f, hyperdash_transition_length, Easing.OutQuint);
this.FadeColour(Color4.OrangeRed, hyper_dash_transition_length, Easing.OutQuint);
this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint);
Trail = true;
}
}
@ -370,7 +370,7 @@ namespace osu.Game.Rulesets.Catch.UI
hyperDashDirection < 0 && hyperDashTargetPosition > X)
{
X = hyperDashTargetPosition;
SetHyperdashState();
SetHyperDashState();
}
}

View File

@ -21,9 +21,9 @@ namespace osu.Game.Rulesets.Mania.Tests
this.direction = direction;
}
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs<IScrollingInfo>(new ScrollingInfo { Direction = { Value = direction }});
return dependencies;
}

View File

@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Mania.Tests
private Drawable createColumn(ScrollingDirection direction, ManiaAction action)
{
var column = new Column(direction)
var column = new Column
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -137,9 +137,9 @@ namespace osu.Game.Rulesets.Mania.Tests
};
}
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs<IBindable<ManiaAction>>(new Bindable<ManiaAction>());
return dependencies;
}

View File

@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Mania.Tests
{
var specialAction = ManiaAction.Special1;
var stage = new ManiaStage(direction, 0, new StageDefinition { Columns = 2 }, ref action, ref specialAction) { VisibleTimeRange = { Value = 2000 } };
var stage = new ManiaStage(0, new StageDefinition { Columns = 2 }, ref action, ref specialAction) { VisibleTimeRange = { Value = 2000 } };
stages.Add(stage);
return new ScrollingTestContainer(direction)

View File

@ -133,26 +133,26 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (ConversionDifficulty > 6.5)
{
if ((convertType & PatternType.LowProbability) > 0)
if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(HitObject.StartTime, 0.78, 0.3, 0);
return generateNRandomNotes(HitObject.StartTime, 0.85, 0.36, 0.03);
}
if (ConversionDifficulty > 4)
{
if ((convertType & PatternType.LowProbability) > 0)
if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(HitObject.StartTime, 0.43, 0.08, 0);
return generateNRandomNotes(HitObject.StartTime, 0.56, 0.18, 0);
}
if (ConversionDifficulty > 2.5)
{
if ((convertType & PatternType.LowProbability) > 0)
if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(HitObject.StartTime, 0.3, 0, 0);
return generateNRandomNotes(HitObject.StartTime, 0.37, 0.08, 0);
}
if ((convertType & PatternType.LowProbability) > 0)
if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(HitObject.StartTime, 0.17, 0, 0);
return generateNRandomNotes(HitObject.StartTime, 0.27, 0, 0);
}
@ -209,7 +209,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern();
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
{
while (PreviousPattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(RandomStart, TotalColumns);
@ -361,7 +361,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
bool isDoubleSample(SampleInfo sample) => sample.Name == SampleInfo.HIT_CLAP || sample.Name == SampleInfo.HIT_FINISH;
bool canGenerateTwoNotes = (convertType & PatternType.LowProbability) == 0;
bool canGenerateTwoNotes = !convertType.HasFlag(PatternType.LowProbability);
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample);
if (canGenerateTwoNotes)
@ -391,7 +391,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int columnRepeat = Math.Min(spanCount, TotalColumns);
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
{
while (PreviousPattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(RandomStart, TotalColumns);
@ -425,7 +425,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern();
int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
{
while (PreviousPattern.ColumnHasObject(holdColumn))
holdColumn = Random.Next(RandomStart, TotalColumns);

View File

@ -19,9 +19,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, IBeatmap originalBeatmap)
: base(random, hitObject, beatmap, new Pattern(), originalBeatmap)
{
var endtimeData = HitObject as IHasEndTime;
endTime = endtimeData?.EndTime ?? 0;
endTime = (HitObject as IHasEndTime)?.EndTime ?? 0;
}
public override IEnumerable<Pattern> Generate()
@ -35,15 +33,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
bool generateHold = endTime - HitObject.StartTime >= 100;
if (TotalColumns == 8)
switch (TotalColumns)
{
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000)
case 8 when HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000:
addToPattern(pattern, 0, generateHold);
else
break;
case 8:
addToPattern(pattern, getNextRandomColumn(RandomStart), generateHold);
break;
default:
if (TotalColumns > 0)
addToPattern(pattern, getNextRandomColumn(0), generateHold);
break;
}
else if (TotalColumns > 0)
addToPattern(pattern, getNextRandomColumn(0), generateHold);
return pattern;
}

View File

@ -21,7 +21,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private readonly PatternType convertType;
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, IBeatmap originalBeatmap)
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density,
PatternType lastStair, IBeatmap originalBeatmap)
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{
if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
@ -79,7 +80,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
else
convertType |= PatternType.LowProbability;
if ((convertType & PatternType.KeepSingle) == 0)
if (!convertType.HasFlag(PatternType.KeepSingle))
{
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && TotalColumns != 8)
convertType |= PatternType.Mirror;
@ -107,7 +108,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any())
if (convertType.HasFlag(PatternType.Reverse) && PreviousPattern.HitObjects.Any())
{
// Generate a new pattern by copying the last hit objects in reverse-column order
for (int i = RandomStart; i < TotalColumns; i++)
@ -117,7 +118,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
return pattern;
}
if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1
if (convertType.HasFlag(PatternType.Cycle) && PreviousPattern.HitObjects.Count() == 1
// If we convert to 7K + 1, let's not overload the special key
&& (TotalColumns != 8 || lastColumn != 0)
// Make sure the last column was not the centre column
@ -130,7 +131,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
return pattern;
}
if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Any())
if (convertType.HasFlag(PatternType.ForceStack) && PreviousPattern.HitObjects.Any())
{
// Generate a new pattern by placing on the already filled columns
for (int i = RandomStart; i < TotalColumns; i++)
@ -142,7 +143,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (PreviousPattern.HitObjects.Count() == 1)
{
if ((convertType & PatternType.Stair) > 0)
if (convertType.HasFlag(PatternType.Stair))
{
// Generate a new pattern by placing on the next column, cycling back to the start if there is no "next"
int targetColumn = lastColumn + 1;
@ -153,7 +154,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
return pattern;
}
if ((convertType & PatternType.ReverseStair) > 0)
if (convertType.HasFlag(PatternType.ReverseStair))
{
// Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous"
int targetColumn = lastColumn - 1;
@ -165,10 +166,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
}
}
if ((convertType & PatternType.KeepSingle) > 0)
if (convertType.HasFlag(PatternType.KeepSingle))
return pattern = generateRandomNotes(1);
if ((convertType & PatternType.Mirror) > 0)
if (convertType.HasFlag(PatternType.Mirror))
{
if (ConversionDifficulty > 6.5)
return pattern = generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
@ -179,21 +180,21 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (ConversionDifficulty > 6.5)
{
if ((convertType & PatternType.LowProbability) > 0)
if (convertType.HasFlag(PatternType.LowProbability))
return pattern = generateRandomPattern(0.78, 0.42, 0, 0);
return pattern = generateRandomPattern(1, 0.62, 0, 0);
}
if (ConversionDifficulty > 4)
{
if ((convertType & PatternType.LowProbability) > 0)
if (convertType.HasFlag(PatternType.LowProbability))
return pattern = generateRandomPattern(0.35, 0.08, 0, 0);
return pattern = generateRandomPattern(0.52, 0.15, 0, 0);
}
if (ConversionDifficulty > 2)
{
if ((convertType & PatternType.LowProbability) > 0)
if (convertType.HasFlag(PatternType.LowProbability))
return pattern = generateRandomPattern(0.18, 0, 0, 0);
return pattern = generateRandomPattern(0.45, 0, 0, 0);
}
@ -204,9 +205,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
foreach (var obj in pattern.HitObjects)
{
if ((convertType & PatternType.Stair) > 0 && obj.Column == TotalColumns - 1)
if (convertType.HasFlag(PatternType.Stair) && obj.Column == TotalColumns - 1)
StairType = PatternType.ReverseStair;
if ((convertType & PatternType.ReverseStair) > 0 && obj.Column == RandomStart)
if (convertType.HasFlag(PatternType.ReverseStair) && obj.Column == RandomStart)
StairType = PatternType.Stair;
}
}
@ -225,7 +226,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
var pattern = new Pattern();
bool allowStacking = (convertType & PatternType.ForceNotStack) == 0;
bool allowStacking = !convertType.HasFlag(PatternType.ForceNotStack);
if (!allowStacking)
noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects);
@ -235,7 +236,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking)
{
if ((convertType & PatternType.Gathered) > 0)
if (convertType.HasFlag(PatternType.Gathered))
{
nextColumn++;
if (nextColumn == TotalColumns)
@ -367,7 +368,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
addToCentre = false;
if ((convertType & PatternType.ForceNotStack) > 0)
if (convertType.HasFlag(PatternType.ForceNotStack))
return getRandomNoteCount(1 / 2f + p2 / 2, p2, (p2 + p3) / 2, p3);
switch (TotalColumns)

View File

@ -19,14 +19,14 @@ namespace osu.Game.Rulesets.Mania
}
[BackgroundDependencyLoader]
private void load(ManiaConfigManager config)
private void load()
{
Children = new Drawable[]
{
new SettingsEnumDropdown<ManiaScrollingDirection>
{
LabelText = "Scrolling direction",
Bindable = config.GetBindable<ManiaScrollingDirection>(ManiaSetting.ScrollDirection)
Bindable = ((ManiaConfigManager)Config).GetBindable<ManiaScrollingDirection>(ManiaSetting.ScrollDirection)
}
};
}

View File

@ -56,10 +56,15 @@ namespace osu.Game.Rulesets.Mania.Replays
{
foreach (var point in group)
{
if (point is HitPoint)
actions.Add(columnActions[point.Column]);
if (point is ReleasePoint)
actions.Remove(columnActions[point.Column]);
switch (point)
{
case HitPoint _:
actions.Add(columnActions[point.Column]);
break;
case ReleasePoint _:
actions.Remove(columnActions[point.Column]);
break;
}
}
Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray()));

View File

@ -3,7 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Input;
using osu.Framework.Input.StateChanges;
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Mania.Replays

View File

@ -32,8 +32,7 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Container<Drawable> Content => hitObjectArea;
public Column(ScrollingDirection direction)
: base(direction)
public Column()
{
RelativeSizeAxes = Axes.Y;
Width = column_width;
@ -118,9 +117,9 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs<IBindable<ManiaAction>>(Action);
return dependencies;
}
@ -139,7 +138,7 @@ namespace osu.Game.Rulesets.Mania.UI
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
if (!judgement.IsHit || !judgedObject.DisplayJudgement)
if (!judgement.IsHit || !judgedObject.DisplayJudgement || !DisplayJudgements)
return;
explosionContainer.Add(new HitExplosion(judgedObject)

View File

@ -8,11 +8,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Mania.UI
{
@ -21,8 +19,7 @@ namespace osu.Game.Rulesets.Mania.UI
public List<Column> Columns => stages.SelectMany(x => x.Columns).ToList();
private readonly List<ManiaStage> stages = new List<ManiaStage>();
public ManiaPlayfield(ScrollingDirection direction, List<StageDefinition> stageDefinitions)
: base(direction)
public ManiaPlayfield(List<StageDefinition> stageDefinitions)
{
if (stageDefinitions == null)
throw new ArgumentNullException(nameof(stageDefinitions));
@ -42,7 +39,7 @@ namespace osu.Game.Rulesets.Mania.UI
int firstColumnIndex = 0;
for (int i = 0; i < stageDefinitions.Count; i++)
{
var newStage = new ManiaStage(direction, firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
var newStage = new ManiaStage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
newStage.VisibleTimeRange.BindTo(VisibleTimeRange);
playfieldGrid.Content[0][i] = newStage;
@ -76,10 +73,5 @@ namespace osu.Game.Rulesets.Mania.UI
{
maniaConfig.BindWith(ManiaSetting.ScrollTime, VisibleTimeRange);
}
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
getStageByColumn(((ManiaHitObject)judgedObject.HitObject).Column).OnJudgement(judgedObject, judgement);
}
}
}

View File

@ -70,24 +70,24 @@ namespace osu.Game.Rulesets.Mania.UI
}
[BackgroundDependencyLoader]
private void load(ManiaConfigManager config)
private void load()
{
BarLines.ForEach(Playfield.Add);
config.BindWith(ManiaSetting.ScrollDirection, configDirection);
((ManiaConfigManager)Config).BindWith(ManiaSetting.ScrollDirection, configDirection);
configDirection.BindValueChanged(d => scrollingInfo.Direction.Value = (ScrollingDirection)d, true);
}
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs<IScrollingInfo>(scrollingInfo = new ScrollingInfo());
return dependencies;
}
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(scrollingInfo.Direction, Beatmap.Stages)
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -11,11 +11,6 @@ namespace osu.Game.Rulesets.Mania.UI
{
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
public ManiaScrollingPlayfield(ScrollingDirection direction)
: base(direction)
{
}
[BackgroundDependencyLoader]
private void load(IScrollingInfo scrollingInfo)
{

View File

@ -43,8 +43,7 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly int firstColumnIndex;
public ManiaStage(ScrollingDirection direction, int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
: base(direction)
public ManiaStage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
{
this.firstColumnIndex = firstColumnIndex;
@ -124,7 +123,7 @@ namespace osu.Game.Rulesets.Mania.UI
for (int i = 0; i < definition.Columns; i++)
{
var isSpecial = definition.IsSpecialColumn(i);
var column = new Column(direction)
var column = new Column
{
IsSpecial = isSpecial,
Action = { Value = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++ }
@ -164,7 +163,7 @@ namespace osu.Game.Rulesets.Mania.UI
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
if (!judgedObject.DisplayJudgement)
if (!judgedObject.DisplayJudgement || !DisplayJudgements)
return;
judgements.Clear();

View File

@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public struct ConvertValue : IEquatable<ConvertValue>
{
/// <summary>
/// A sane value to account for osu!stable using ints everwhere.
/// A sane value to account for osu!stable using <see cref="int"/>s everywhere.
/// </summary>
private const double conversion_lenience = 2;

View File

@ -61,19 +61,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
// Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
// Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate;
double preEmpt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
int maxCombo = beatmap.HitObjects.Count();
// Add the ticks + tail of the slider. 1 is subtracted because the "headcircle" would be counted twice (once for the slider itself in the line above)
// Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
maxCombo += beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count - 1);
return new OsuDifficultyAttributes(mods, starRating)
{
AimStrain = aimRating,
SpeedStrain = speedRating,
ApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5,
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
OverallDifficulty = (80 - hitWindowGreat) / 6,
MaxCombo = maxCombo
};

View File

@ -1,12 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Osu.UI;
namespace osu.Game.Rulesets.Osu.Edit
{
public class OsuEditPlayfield : OsuPlayfield
{
protected override bool DisplayJudgements => false;
}
}

View File

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

View File

@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
{
void adjustFadeIn(OsuHitObject h) => h.TimeFadein = h.TimePreempt * fade_in_duration_multiplier;
void adjustFadeIn(OsuHitObject h) => h.TimeFadeIn = h.TimePreempt * fade_in_duration_multiplier;
foreach (var d in drawables.OfType<DrawableOsuHitObject>())
{
@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Mods
var h = d.HitObject;
var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadein;
var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadeIn;
var fadeOutDuration = h.TimePreempt * fade_out_duration_multiplier;
// new duration from completed fade in to end (before fading out)

View File

@ -96,12 +96,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
using (fp.BeginAbsoluteSequence(fadeInTime))
{
fp.FadeIn(currHitObject.TimeFadein);
fp.ScaleTo(1, currHitObject.TimeFadein, Easing.Out);
fp.FadeIn(currHitObject.TimeFadeIn);
fp.ScaleTo(1, currHitObject.TimeFadeIn, Easing.Out);
fp.MoveTo(pointEndPosition, currHitObject.TimeFadein, Easing.Out);
fp.MoveTo(pointEndPosition, currHitObject.TimeFadeIn, Easing.Out);
fp.Delay(fadeOutTime - fadeInTime).FadeOut(currHitObject.TimeFadein);
fp.Delay(fadeOutTime - fadeInTime).FadeOut(currHitObject.TimeFadeIn);
}
fp.Expire(true);

View File

@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
base.UpdatePreemptState();
ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadein * 2, HitObject.TimePreempt));
ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt));
ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt);
}

View File

@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
}
protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadein);
protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn);
protected virtual void UpdateCurrentState(ArmedState state)
{

View File

@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
Disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton);
if (!spmCounter.IsPresent && Disc.Tracking)
spmCounter.FadeIn(HitObject.TimeFadein);
spmCounter.FadeIn(HitObject.TimeFadeIn);
base.Update();
}

View File

@ -5,7 +5,8 @@ using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Framework.Input.EventArgs;
using osu.Framework.Input.States;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
using OpenTK.Graphics;

View File

@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
var spanProgress = slider.ProgressAt(completionProgress);
double start = 0;
double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1;
double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1;
if (span >= slider.SpanCount() - 1)
{

View File

@ -4,7 +4,7 @@
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Input.States;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;

View File

@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public event Action<Vector2> PositionChanged;
public double TimePreempt = 600;
public double TimeFadein = 400;
public double TimeFadeIn = 400;
private Vector2 position;
@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Objects
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
TimeFadein = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300);
TimeFadeIn = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300);
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
}

View File

@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects
// This is so on repeats ticks don't appear too late to be visually processed by the player.
offset = 200;
else
offset = TimeFadein * 0.66f;
offset = TimeFadeIn * 0.66f;
TimePreempt = (StartTime - SpanStartTime) / 2 + offset;
}

View File

@ -31,8 +31,8 @@ namespace osu.Game.Rulesets.Osu
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{
new KeyBinding(InputKey.Z, OsuAction.LeftButton),
new KeyBinding(InputKey.X, OsuAction.RightButton),
new KeyBinding(InputKey.A, OsuAction.LeftButton),
new KeyBinding(InputKey.S, OsuAction.RightButton),
new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton),
new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
};

View File

@ -285,44 +285,45 @@ namespace osu.Game.Rulesets.Osu.Replays
AddFrameToReplay(startFrame);
// We add intermediate frames for spinning / following a slider here.
if (h is Spinner)
switch (h)
{
Spinner s = h as Spinner;
Vector2 difference = startPosition - 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 < s.EndTime; j += FrameDelay)
// We add intermediate frames for spinning / following a slider here.
case Spinner spinner:
{
t = ApplyModsToTime(j - h.StartTime) * spinnerDirection;
Vector2 difference = startPosition - SPINNER_CENTRE;
Vector2 pos = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS);
AddFrameToReplay(new OsuReplayFrame((int)j, new Vector2(pos.X, pos.Y), action));
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 < spinner.EndTime; j += FrameDelay)
{
t = ApplyModsToTime(j - h.StartTime) * spinnerDirection;
Vector2 pos = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS);
AddFrameToReplay(new OsuReplayFrame((int)j, new Vector2(pos.X, pos.Y), action));
}
t = ApplyModsToTime(spinner.EndTime - h.StartTime) * spinnerDirection;
Vector2 endPosition = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS);
AddFrameToReplay(new OsuReplayFrame(spinner.EndTime, new Vector2(endPosition.X, endPosition.Y), action));
endFrame.Position = endPosition;
break;
}
t = ApplyModsToTime(s.EndTime - h.StartTime) * spinnerDirection;
Vector2 endPosition = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS);
AddFrameToReplay(new OsuReplayFrame(s.EndTime, new Vector2(endPosition.X, endPosition.Y), action));
endFrame.Position = endPosition;
}
else if (h is Slider)
{
Slider s = h as Slider;
for (double j = FrameDelay; j < s.Duration; j += FrameDelay)
case Slider slider:
{
Vector2 pos = s.StackedPositionAt(j / s.Duration);
AddFrameToReplay(new OsuReplayFrame(h.StartTime + j, new Vector2(pos.X, pos.Y), action));
}
for (double j = FrameDelay; j < slider.Duration; j += FrameDelay)
{
Vector2 pos = slider.StackedPositionAt(j / slider.Duration);
AddFrameToReplay(new OsuReplayFrame(h.StartTime + j, new Vector2(pos.X, pos.Y), action));
}
AddFrameToReplay(new OsuReplayFrame(s.EndTime, new Vector2(s.StackedEndPosition.X, s.StackedEndPosition.Y), action));
AddFrameToReplay(new OsuReplayFrame(slider.EndTime, new Vector2(slider.StackedEndPosition.X, slider.StackedEndPosition.Y), action));
break;
}
}
// We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed!

View File

@ -3,7 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Input;
using osu.Framework.Input.StateChanges;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Replays;
using OpenTK;

View File

@ -12,6 +12,7 @@ using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using osu.Framework.Input.States;
using osu.Framework.Timing;
using OpenTK;
using OpenTK.Graphics;

View File

@ -39,6 +39,11 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private int downCount;
private const float pressed_scale = 1.2f;
private const float released_scale = 1f;
private float targetScale => downCount > 0 ? pressed_scale : released_scale;
public bool OnPressed(OsuAction action)
{
switch (action)
@ -46,7 +51,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
case OsuAction.LeftButton:
case OsuAction.RightButton:
downCount++;
ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad);
ActiveCursor.ScaleTo(released_scale).ScaleTo(targetScale, 100, Easing.OutQuad);
break;
}
@ -60,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
case OsuAction.LeftButton:
case OsuAction.RightButton:
if (--downCount == 0)
ActiveCursor.ScaleTo(1, 200, Easing.OutQuad);
ActiveCursor.ScaleTo(targetScale, 200, Easing.OutQuad);
break;
}
@ -72,13 +77,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
protected override void PopIn()
{
fadeContainer.FadeTo(1, 300, Easing.OutQuint);
ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
ActiveCursor.ScaleTo(targetScale, 400, Easing.OutQuint);
}
protected override void PopOut()
{
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
ActiveCursor.ScaleTo(targetScale * 0.8f, 450, Easing.OutQuint);
}
public class OsuCursor : Container

View File

@ -20,8 +20,6 @@ namespace osu.Game.Rulesets.Osu.UI
private readonly JudgementContainer<DrawableOsuJudgement> judgementLayer;
private readonly ConnectionRenderer<OsuHitObject> connectionLayer;
protected virtual bool DisplayJudgements => true;
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
public OsuPlayfield()

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
using OpenTK;
@ -33,19 +34,30 @@ namespace osu.Game.Rulesets.Osu.UI
protected override DrawableHitObject<OsuHitObject> GetVisualRepresentation(OsuHitObject h)
{
if (h is HitCircle circle)
return new DrawableHitCircle(circle);
switch (h)
{
case HitCircle circle:
return new DrawableHitCircle(circle);
case Slider slider:
return new DrawableSlider(slider);
case Spinner spinner:
return new DrawableSpinner(spinner);
}
if (h is Slider slider)
return new DrawableSlider(slider);
if (h is Spinner spinner)
return new DrawableSpinner(spinner);
return null;
}
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay);
public override double GameplayStartTime
{
get
{
var first = (OsuHitObject)Objects.First();
return first.StartTime - first.TimePreempt;
}
}
protected override Vector2 GetAspectAdjustedSize()
{
var aspectSize = DrawSize.X * 0.75f < DrawSize.Y ? new Vector2(DrawSize.X, DrawSize.X * 0.75f) : new Vector2(DrawSize.Y * 4f / 3f, DrawSize.Y);

View File

@ -4,7 +4,7 @@
using osu.Game.Rulesets.Replays;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Input;
using osu.Framework.Input.StateChanges;
namespace osu.Game.Rulesets.Taiko.Replays
{

View File

@ -80,30 +80,31 @@ namespace osu.Game.Rulesets.Taiko.Scoring
foreach (var obj in beatmap.HitObjects)
{
if (obj is Hit)
switch (obj)
{
AddJudgement(new TaikoJudgement { Result = HitResult.Great });
if (obj.IsStrong)
AddJudgement(new TaikoStrongHitJudgement());
}
else if (obj is DrumRoll)
{
for (int i = 0; i < ((DrumRoll)obj).NestedHitObjects.OfType<DrumRollTick>().Count(); i++)
{
AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great });
case Hit _:
AddJudgement(new TaikoJudgement { Result = HitResult.Great });
if (obj.IsStrong)
AddJudgement(new TaikoStrongHitJudgement());
break;
case DrumRoll drumRoll:
var count = drumRoll.NestedHitObjects.OfType<DrumRollTick>().Count();
for (int i = 0; i < count; i++)
{
AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great });
if (obj.IsStrong)
AddJudgement(new TaikoStrongHitJudgement());
}
AddJudgement(new TaikoJudgement { Result = HitResult.Great });
if (obj.IsStrong)
AddJudgement(new TaikoStrongHitJudgement());
}
AddJudgement(new TaikoJudgement { Result = HitResult.Great });
if (obj.IsStrong)
AddJudgement(new TaikoStrongHitJudgement());
}
else if (obj is Swell)
{
AddJudgement(new TaikoJudgement { Result = HitResult.Great });
break;
case Swell _:
AddJudgement(new TaikoJudgement { Result = HitResult.Great });
break;
}
}
}

View File

@ -58,8 +58,9 @@ namespace osu.Game.Rulesets.Taiko.UI
private readonly Box background;
public TaikoPlayfield(ControlPointInfo controlPoints)
: base(ScrollingDirection.Left)
{
Direction.Value = ScrollingDirection.Left;
AddRangeInternal(new Drawable[]
{
backgroundContainer = new Container
@ -212,17 +213,22 @@ namespace osu.Game.Rulesets.Taiko.UI
base.Add(h);
var barline = h as DrawableBarLine;
if (barline != null)
barlineContainer.Add(barline.CreateProxy());
var taikoObject = h as DrawableTaikoHitObject;
if (taikoObject != null)
topLevelHitContainer.Add(taikoObject.CreateProxiedContent());
switch (h)
{
case DrawableBarLine barline:
barlineContainer.Add(barline.CreateProxy());
break;
case DrawableTaikoHitObject taikoObject:
topLevelHitContainer.Add(taikoObject.CreateProxiedContent());
break;
}
}
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
if (!DisplayJudgements)
return;
if (judgedObject.DisplayJudgement && judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null)
{
judgementContainer.Add(new DrawableTaikoJudgement(judgement, judgedObject)

View File

@ -98,32 +98,22 @@ namespace osu.Game.Rulesets.Taiko.UI
protected override DrawableHitObject<TaikoHitObject> GetVisualRepresentation(TaikoHitObject h)
{
var centreHit = h as CentreHit;
if (centreHit != null)
switch (h)
{
if (h.IsStrong)
case CentreHit centreHit when h.IsStrong:
return new DrawableCentreHitStrong(centreHit);
return new DrawableCentreHit(centreHit);
}
var rimHit = h as RimHit;
if (rimHit != null)
{
if (h.IsStrong)
case CentreHit centreHit:
return new DrawableCentreHit(centreHit);
case RimHit rimHit when h.IsStrong:
return new DrawableRimHitStrong(rimHit);
return new DrawableRimHit(rimHit);
case RimHit rimHit:
return new DrawableRimHit(rimHit);
case DrumRoll drumRoll:
return new DrawableDrumRoll(drumRoll);
case Swell swell:
return new DrawableSwell(swell);
}
var drumRoll = h as DrumRoll;
if (drumRoll != null)
{
return new DrawableDrumRoll(drumRoll);
}
var swell = h as Swell;
if (swell != null)
return new DrawableSwell(swell);
return null;
}

View File

@ -214,39 +214,56 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
[Test]
public void TestDecodeCustomSamples()
public void TestDecodeControlPointCustomSampleBank()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("custom-samples.osu"))
using (var resStream = Resource.OpenResource("controlpoint-custom-samplebank.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
Assert.AreEqual("hitnormal", getTestableSampleInfo(hitObjects[0]).Name);
Assert.AreEqual("hitnormal", getTestableSampleInfo(hitObjects[1]).Name);
Assert.AreEqual("hitnormal2", getTestableSampleInfo(hitObjects[2]).Name);
Assert.AreEqual("hitnormal", getTestableSampleInfo(hitObjects[3]).Name);
Assert.AreEqual("normal-hitnormal", getTestableSampleInfo(hitObjects[0]).LookupNames.First());
Assert.AreEqual("normal-hitnormal", getTestableSampleInfo(hitObjects[1]).LookupNames.First());
Assert.AreEqual("normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
Assert.AreEqual("normal-hitnormal", getTestableSampleInfo(hitObjects[3]).LookupNames.First());
}
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(new SampleInfo { Name = "hitnormal" });
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
}
[Test]
public void TestDecodeCustomHitObjectSamples()
public void TestDecodeHitObjectCustomSampleBank()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("custom-hitobject-samples.osu"))
using (var resStream = Resource.OpenResource("hitobject-custom-samplebank.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
Assert.AreEqual("hit_1.wav", hitObjects[0].Samples[0].LookupNames.First());
Assert.AreEqual("hit_2.wav", hitObjects[1].Samples[0].LookupNames.First());
Assert.AreEqual("hitnormal2", getTestableSampleInfo(hitObjects[2]).Name);
Assert.AreEqual("hit_1.wav", hitObjects[3].Samples[0].LookupNames.First());
Assert.AreEqual("normal-hitnormal", getTestableSampleInfo(hitObjects[0]).LookupNames.First());
Assert.AreEqual("normal-hitnormal2", getTestableSampleInfo(hitObjects[1]).LookupNames.First());
Assert.AreEqual("normal-hitnormal3", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
}
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(new SampleInfo { Name = "hitnormal" });
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
}
[Test]
public void TestDecodeHitObjectFileSamples()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("hitobject-file-samples.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
Assert.AreEqual("hit_1.wav", getTestableSampleInfo(hitObjects[0]).LookupNames.First());
Assert.AreEqual("hit_2.wav", getTestableSampleInfo(hitObjects[1]).LookupNames.First());
Assert.AreEqual("normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
Assert.AreEqual("hit_1.wav", getTestableSampleInfo(hitObjects[3]).LookupNames.First());
}
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
}
}
}

View File

@ -0,0 +1,13 @@
osu file format v14
[General]
SampleSet: Normal
[TimingPoints]
2170,468.75,4,1,0,40,1,0
3107,-100,4,1,2,40,0,0
[HitObjects]
255,193,2170,1,0,0:0:0:0:
256,191,3107,5,0,0:0:0:0:
255,193,4000,1,0,0:0:3:70:

View File

@ -110,8 +110,8 @@ namespace osu.Game.Tests.Visual
private void testInfoLabels(int expectedCount)
{
AddAssert("check infolabels exists", () => infoWedge.Info.InfoLabelContainer.Children.Any());
AddAssert("check infolabels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount);
AddAssert("check info labels exists", () => infoWedge.Info.InfoLabelContainer.Children.Any());
AddAssert("check info labels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount);
}
private void testNullBeatmap()
@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual
AddAssert("check default title", () => infoWedge.Info.TitleLabel.Text == Beatmap.Default.BeatmapInfo.Metadata.Title);
AddAssert("check default artist", () => infoWedge.Info.ArtistLabel.Text == Beatmap.Default.BeatmapInfo.Metadata.Artist);
AddAssert("check empty author", () => !infoWedge.Info.MapperContainer.Children.Any());
AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any());
AddAssert("check no info labels", () => !infoWedge.Info.InfoLabelContainer.Children.Any());
}
private void selectBeatmap(IBeatmap b)

View File

@ -1,6 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@ -13,6 +16,13 @@ namespace osu.Game.Tests.Visual
[TestFixture]
public class TestCaseButtonSystem : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ButtonSystem),
typeof(ButtonArea),
typeof(Button)
};
public TestCaseButtonSystem()
{
OsuLogo logo;
@ -30,6 +40,9 @@ namespace osu.Game.Tests.Visual
};
buttons.SetOsuLogo(logo);
foreach (var s in Enum.GetValues(typeof(ButtonSystemState)).OfType<ButtonSystemState>().Skip(1))
AddStep($"State to {s}", () => buttons.State = s);
}
}
}

View File

@ -7,7 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Framework.Input.States;
using osu.Framework.MathUtils;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.Sprites;

View File

@ -0,0 +1,28 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Screens.Menu;
using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
{
public class TestCaseDisclaimer : OsuTestCase
{
[BackgroundDependencyLoader]
private void load()
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
new Disclaimer()
};
}
}
}

View File

@ -8,7 +8,7 @@ using System.Linq;
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Input.EventArgs;
using osu.Framework.Logging;
using osu.Game.Screens.Play;

View File

@ -25,11 +25,7 @@ namespace osu.Game.Tests.Visual
Children = new[]
{
new NamedIconButton("No change", new IconButton()),
new NamedIconButton("Background colours", new IconButton
{
FlashColour = Color4.DarkGreen,
HoverColour = Color4.Green,
}),
new NamedIconButton("Background colours", new ColouredIconButton()),
new NamedIconButton("Full-width", new IconButton { ButtonSize = new Vector2(200, 30) }),
new NamedIconButton("Unchanging size", new IconButton(), false),
new NamedIconButton("Icon colours", new IconButton
@ -41,6 +37,15 @@ namespace osu.Game.Tests.Visual
};
}
private class ColouredIconButton : IconButton
{
public ColouredIconButton()
{
FlashColour = Color4.DarkGreen;
HoverColour = Color4.Green;
}
}
private class NamedIconButton : Container
{
public NamedIconButton(string name, IconButton button, bool allowSizeChange = true)

View File

@ -18,7 +18,6 @@ namespace osu.Game.Tests.Visual
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
IsCounting = true,
Children = new KeyCounter[]
{
new KeyCounterKeyboard(Key.Z),

View File

@ -114,7 +114,7 @@ namespace osu.Game.Tests.Visual
testMultiplierTextColour(noFailMod, modSelect.LowMultiplierColour);
testMultiplierTextColour(hiddenMod, modSelect.HighMultiplierColour);
testUnimplmentedMod(autoPilotMod);
testUnimplementedMod(autoPilotMod);
}
private void testManiaMods(ManiaRuleset ruleset)
@ -154,7 +154,7 @@ namespace osu.Game.Tests.Visual
checkNotSelected(mod);
}
private void testUnimplmentedMod(Mod mod)
private void testUnimplementedMod(Mod mod)
{
selectNext(mod);
checkNotSelected(mod);

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Timing;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using OpenTK.Graphics;
@ -23,19 +22,15 @@ namespace osu.Game.Tests.Visual
public TestCaseOsuGame()
{
var rateAdjustClock = new StopwatchClock(true);
var framedClock = new FramedClock(rateAdjustClock);
framedClock.ProcessFrame();
Add(new Box
Children = new Drawable[]
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
});
Add(new Loader());
AddSliderStep("Playback speed", 0.0, 2.0, 1, v => rateAdjustClock.Rate = v);
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
new Loader()
};
}
}
}

View File

@ -16,7 +16,6 @@ using osu.Game.Rulesets;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
using osu.Game.Tests.Platform;
namespace osu.Game.Tests.Visual
{
@ -28,6 +27,7 @@ namespace osu.Game.Tests.Visual
private RulesetStore rulesets;
private WorkingBeatmap defaultBeatmap;
private DatabaseContextFactory factory;
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@ -54,18 +54,25 @@ namespace osu.Game.Tests.Visual
public new BeatmapCarousel Carousel => base.Carousel;
}
protected override void Dispose(bool isDisposing)
{
factory.ResetDatabase();
base.Dispose(isDisposing);
}
[BackgroundDependencyLoader]
private void load()
{
TestSongSelect songSelect = null;
var storage = new TestStorage(@"TestCasePlaySongSelect");
factory = new DatabaseContextFactory(LocalStorage);
factory.ResetDatabase();
// this is by no means clean. should be replacing inside of OsuGameBase somehow.
IDatabaseContextFactory factory = new SingletonContextFactory(new OsuDbContext());
using (var usage = factory.Get())
usage.Migrate();
Dependencies.Cache(rulesets = new RulesetStore(factory));
Dependencies.Cache(manager = new BeatmapManager(storage, factory, rulesets, null, null)
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, null)
{
DefaultBeatmap = defaultBeatmap = Beatmap.Default
});

View File

@ -14,9 +14,9 @@ namespace osu.Game.Tests.Visual
{
private readonly PreviewTrackManager trackManager = new TestPreviewTrackManager();
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs(trackManager);
dependencies.CacheAs<IPreviewTrackOwner>(this);
return dependencies;
@ -101,9 +101,9 @@ namespace osu.Game.Tests.Visual
AddInternal(track);
}
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs<IPreviewTrackOwner>(this);
return dependencies;
}

View File

@ -117,7 +117,6 @@ namespace osu.Game.Tests.Visual
public new readonly ScrollingDirection Direction;
public TestPlayfield(ScrollingDirection direction)
: base(direction)
{
Direction = direction;

View File

@ -16,8 +16,8 @@ namespace osu.Game.Tests.Visual
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ToolbarButton),
typeof(ToolbarModeSelector),
typeof(ToolbarModeButton),
typeof(ToolbarRulesetSelector),
typeof(ToolbarRulesetButton),
typeof(ToolbarNotificationButton),
};

View File

@ -29,6 +29,11 @@ namespace osu.Game.Audio
/// </summary>
public string Name;
/// <summary>
/// An optional suffix to provide priority lookup. Falls back to non-suffixed <see cref="Name"/>.
/// </summary>
public string Suffix;
/// <summary>
/// The sample volume.
/// </summary>
@ -42,9 +47,16 @@ namespace osu.Game.Audio
get
{
if (!string.IsNullOrEmpty(Namespace))
{
if (!string.IsNullOrEmpty(Suffix))
yield return $"{Namespace}/{Bank}-{Name}{Suffix}";
yield return $"{Namespace}/{Bank}-{Name}";
}
yield return $"{Bank}-{Name}"; // Without namespace as a fallback even when we have a namespace
// check non-namespace as a fallback even when we have a namespace
if (!string.IsNullOrEmpty(Suffix))
yield return $"{Bank}-{Name}{Suffix}";
yield return $"{Bank}-{Name}";
}
}

View File

@ -45,6 +45,16 @@ namespace osu.Game.Beatmaps
/// </summary>
public event Action<DownloadBeatmapSetRequest> BeatmapDownloadBegan;
/// <summary>
/// Fired when a beatmap download is interrupted, due to user cancellation or other failures.
/// </summary>
public event Action<DownloadBeatmapSetRequest> BeatmapDownloadFailed;
/// <summary>
/// Fired when a beatmap load is requested (into the interactive game UI).
/// </summary>
public Action<BeatmapSetInfo> PresentBeatmap;
/// <summary>
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
/// </summary>
@ -79,33 +89,46 @@ namespace osu.Game.Beatmaps
this.audioManager = audioManager;
}
protected override void Populate(BeatmapSetInfo model, ArchiveReader archive)
protected override void Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive)
{
model.Beatmaps = createBeatmapDifficulties(archive);
if (archive != null)
beatmapSet.Beatmaps = createBeatmapDifficulties(archive);
foreach (BeatmapInfo b in model.Beatmaps)
foreach (BeatmapInfo b in beatmapSet.Beatmaps)
{
// remove metadata from difficulties where it matches the set
if (model.Metadata.Equals(b.Metadata))
if (beatmapSet.Metadata.Equals(b.Metadata))
b.Metadata = null;
// by setting the model here, we can update the noline set id below.
b.BeatmapSet = model;
fetchAndPopulateOnlineIDs(b);
b.BeatmapSet = beatmapSet;
}
// check if a set already exists with the same online id, delete if it does.
if (model.OnlineBeatmapSetID != null)
if (beatmapSet.OnlineBeatmapSetID != null)
{
var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == model.OnlineBeatmapSetID);
var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == beatmapSet.OnlineBeatmapSetID);
if (existingOnlineId != null)
{
Delete(existingOnlineId);
beatmaps.PurgeDeletable(s => s.ID == existingOnlineId.ID);
Logger.Log($"Found existing beatmap set with same OnlineBeatmapSetID ({model.OnlineBeatmapSetID}). It has been purged.", LoggingTarget.Database);
Logger.Log($"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineBeatmapSetID}). It has been purged.", LoggingTarget.Database);
}
}
validateOnlineIds(beatmapSet.Beatmaps);
foreach (BeatmapInfo b in beatmapSet.Beatmaps)
fetchAndPopulateOnlineIDs(b, beatmapSet.Beatmaps);
}
private void validateOnlineIds(List<BeatmapInfo> beatmaps)
{
var beatmapIds = beatmaps.Where(b => b.OnlineBeatmapID.HasValue).Select(b => b.OnlineBeatmapID).ToList();
// ensure all IDs are unique in this set and none match existing IDs in the local beatmap store.
if (beatmapIds.GroupBy(b => b).Any(g => g.Count() > 1) || QueryBeatmaps(b => beatmapIds.Contains(b.OnlineBeatmapID)).Any())
// remove all online IDs if any problems were found.
beatmaps.ForEach(b => b.OnlineBeatmapID = null);
}
protected override BeatmapSetInfo CheckForExisting(BeatmapSetInfo model)
@ -143,7 +166,7 @@ namespace osu.Game.Beatmaps
return;
}
var downloadNotification = new ProgressNotification
var downloadNotification = new DownloadNotification
{
CompletionText = $"Imported {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}!",
Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
@ -163,18 +186,28 @@ namespace osu.Game.Beatmaps
Task.Factory.StartNew(() =>
{
BeatmapSetInfo importedBeatmap;
// This gets scheduled back to the update thread, but we want the import to run in the background.
using (var stream = new MemoryStream(data))
using (var archive = new ZipArchiveReader(stream, beatmapSetInfo.ToString()))
Import(archive);
importedBeatmap = Import(archive);
downloadNotification.CompletionClickAction = () =>
{
PresentBeatmap?.Invoke(importedBeatmap);
return true;
};
downloadNotification.State = ProgressNotificationState.Completed;
currentDownloads.Remove(request);
}, TaskCreationOptions.LongRunning);
};
request.Failure += error =>
{
BeatmapDownloadFailed?.Invoke(request);
if (error is OperationCanceledException) return;
downloadNotification.State = ProgressNotificationState.Cancelled;
@ -276,7 +309,7 @@ namespace osu.Game.Beatmaps
/// </summary>
/// <param name="query">The query.</param>
/// <returns>Results from the provided query.</returns>
public IEnumerable<BeatmapInfo> QueryBeatmaps(Expression<Func<BeatmapInfo, bool>> query) => beatmaps.Beatmaps.AsNoTracking().Where(query);
public IQueryable<BeatmapInfo> QueryBeatmaps(Expression<Func<BeatmapInfo, bool>> query) => beatmaps.Beatmaps.AsNoTracking().Where(query);
/// <summary>
/// Denotes whether an osu-stable installation is present to perform automated imports from.
@ -355,21 +388,10 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
// check that no existing beatmap exists that is imported with the same online beatmap ID. if so, give it precedence.
if (beatmap.BeatmapInfo.OnlineBeatmapID.HasValue && QueryBeatmap(b => b.OnlineBeatmapID.Value == beatmap.BeatmapInfo.OnlineBeatmapID.Value) != null)
beatmap.BeatmapInfo.OnlineBeatmapID = null;
RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
var ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
beatmap.BeatmapInfo.Ruleset = ruleset;
if (ruleset != null)
{
// TODO: this should be done in a better place once we actually need to dynamically update it.
beatmap.BeatmapInfo.StarDifficulty = ruleset.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating;
}
else
beatmap.BeatmapInfo.StarDifficulty = 0;
// TODO: this should be done in a better place once we actually need to dynamically update it.
beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0;
beatmapInfos.Add(beatmap.BeatmapInfo);
}
@ -382,16 +404,17 @@ namespace osu.Game.Beatmaps
/// Query the API to populate mising OnlineBeatmapID / OnlineBeatmapSetID properties.
/// </summary>
/// <param name="beatmap">The beatmap to populate.</param>
/// <param name="otherBeatmaps">The other beatmaps contained within this set.</param>
/// <param name="force">Whether to re-query if the provided beatmap already has populated values.</param>
/// <returns>True if population was successful.</returns>
private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, bool force = false)
private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, IEnumerable<BeatmapInfo> otherBeatmaps, bool force = false)
{
if (api?.State != APIState.Online)
return false;
if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null)
return true;
if (api.State != APIState.Online)
return false;
Logger.Log("Attempting online lookup for IDs...", LoggingTarget.Database);
try
@ -404,6 +427,12 @@ namespace osu.Game.Beatmaps
Logger.Log($"Successfully mapped to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.", LoggingTarget.Database);
if (otherBeatmaps.Any(b => b.OnlineBeatmapID == res.OnlineBeatmapID))
{
Logger.Log("Another beatmap in the same set already mapped to this ID. We'll skip adding it this time.", LoggingTarget.Database);
return false;
}
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
return true;
@ -432,5 +461,21 @@ namespace osu.Game.Beatmaps
protected override Texture GetBackground() => null;
protected override Track GetTrack() => null;
}
private class DownloadNotification : ProgressNotification
{
public override bool IsImportant => false;
protected override Notification CreateCompletionNotification() => new SilencedProgressCompletionNotification
{
Activated = CompletionClickAction,
Text = CompletionText
};
private class SilencedProgressCompletionNotification : ProgressCompletionNotification
{
public override bool IsImportant => false;
}
}
}
}

View File

@ -14,7 +14,6 @@ namespace osu.Game.Beatmaps
public class BeatmapMetadata : IEquatable<BeatmapMetadata>
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[JsonIgnore]
public int ID { get; set; }
public string Title { get; set; }

View File

@ -40,7 +40,6 @@ namespace osu.Game.Beatmaps.ControlPoints
{
var newSampleInfo = sampleInfo.Clone();
newSampleInfo.Bank = sampleInfo.Bank ?? SampleBank;
newSampleInfo.Name = sampleInfo.Name;
newSampleInfo.Volume = sampleInfo.Volume > 0 ? sampleInfo.Volume : SampleVolume;
return newSampleInfo;
}

View File

@ -5,6 +5,7 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests;
namespace osu.Game.Beatmaps.Drawables
{
@ -19,9 +20,9 @@ namespace osu.Game.Beatmaps.Drawables
private BeatmapManager beatmaps;
/// <summary>
/// Whether the associated beatmap set has been downloading (by this instance or any other instance).
/// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded.
/// </summary>
public readonly BindableBool Downloaded = new BindableBool();
public readonly Bindable<DownloadStatus> DownloadState = new Bindable<DownloadStatus>();
public BeatmapSetDownloader(BeatmapSetInfo set, bool noVideo = false)
{
@ -36,10 +37,16 @@ namespace osu.Game.Beatmaps.Drawables
beatmaps.ItemAdded += setAdded;
beatmaps.ItemRemoved += setRemoved;
beatmaps.BeatmapDownloadBegan += downloadBegan;
beatmaps.BeatmapDownloadFailed += downloadFailed;
// initial value
if (set.OnlineBeatmapSetID != null)
Downloaded.Value = beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID && !s.DeletePending).Any();
if (set.OnlineBeatmapSetID != null && beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID && !s.DeletePending).Any())
DownloadState.Value = DownloadStatus.Downloaded;
else if (beatmaps.GetExistingDownload(set) != null)
DownloadState.Value = DownloadStatus.Downloading;
else
DownloadState.Value = DownloadStatus.NotDownloaded;
}
protected override void Dispose(bool isDisposing)
@ -50,6 +57,8 @@ namespace osu.Game.Beatmaps.Drawables
{
beatmaps.ItemAdded -= setAdded;
beatmaps.ItemRemoved -= setRemoved;
beatmaps.BeatmapDownloadBegan -= downloadBegan;
beatmaps.BeatmapDownloadFailed -= downloadFailed;
}
}
@ -57,28 +66,45 @@ namespace osu.Game.Beatmaps.Drawables
/// Begin downloading the associated beatmap set.
/// </summary>
/// <returns>True if downloading began. False if an existing download is active or completed.</returns>
public bool Download()
public void Download()
{
if (Downloaded.Value)
return false;
if (beatmaps.GetExistingDownload(set) != null)
return false;
if (DownloadState.Value > DownloadStatus.NotDownloaded)
return;
beatmaps.Download(set, noVideo);
return true;
DownloadState.Value = DownloadStatus.Downloading;
}
private void setAdded(BeatmapSetInfo s)
{
if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
Downloaded.Value = true;
DownloadState.Value = DownloadStatus.Downloaded;
}
private void setRemoved(BeatmapSetInfo s)
{
if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
Downloaded.Value = false;
DownloadState.Value = DownloadStatus.NotDownloaded;
}
private void downloadBegan(DownloadBeatmapSetRequest d)
{
if (d.BeatmapSet.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
DownloadState.Value = DownloadStatus.Downloading;
}
private void downloadFailed(DownloadBeatmapSetRequest d)
{
if (d.BeatmapSet.OnlineBeatmapSetID == set.OnlineBeatmapSetID)
DownloadState.Value = DownloadStatus.NotDownloaded;
}
public enum DownloadStatus
{
NotDownloaded,
Downloading,
Downloaded,
}
}
}

View File

@ -9,14 +9,14 @@ using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
public class DifficultyColouredContainer : Container, IHasAccentColour
public abstract class DifficultyColouredContainer : Container, IHasAccentColour
{
public Color4 AccentColour { get; set; }
private readonly BeatmapInfo beatmap;
private OsuColour palette;
public DifficultyColouredContainer(BeatmapInfo beatmap)
protected DifficultyColouredContainer(BeatmapInfo beatmap)
{
this.beatmap = beatmap;
}

View File

@ -3,10 +3,14 @@
using System;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
@ -14,7 +18,8 @@ namespace osu.Game.Beatmaps.Drawables
{
private readonly BeatmapInfo beatmap;
public DifficultyIcon(BeatmapInfo beatmap) : base(beatmap)
public DifficultyIcon(BeatmapInfo beatmap)
: base(beatmap)
{
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
@ -28,16 +33,29 @@ namespace osu.Game.Beatmaps.Drawables
{
Children = new Drawable[]
{
new SpriteIcon
new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.84f),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Colour = AccentColour,
Icon = FontAwesome.fa_circle
Masking = true,
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.08f),
Type = EdgeEffectType.Shadow,
Radius = 5,
},
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = AccentColour,
},
},
new ConstrainedIconContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
// the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment)
Icon = beatmap.Ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.fa_question_circle_o }

View File

@ -62,28 +62,30 @@ namespace osu.Game.Beatmaps.Formats
protected override void ParseLine(Beatmap beatmap, Section section, string line)
{
var strippedLine = StripComments(line);
switch (section)
{
case Section.General:
handleGeneral(line);
handleGeneral(strippedLine);
return;
case Section.Editor:
handleEditor(line);
handleEditor(strippedLine);
return;
case Section.Metadata:
handleMetadata(line);
return;
case Section.Difficulty:
handleDifficulty(line);
handleDifficulty(strippedLine);
return;
case Section.Events:
handleEvent(line);
handleEvent(strippedLine);
return;
case Section.TimingPoints:
handleTimingPoint(line);
handleTimingPoint(strippedLine);
return;
case Section.HitObjects:
handleHitObject(line);
handleHitObject(strippedLine);
return;
}
@ -305,9 +307,9 @@ namespace osu.Game.Beatmaps.Formats
bool omitFirstBarSignature = false;
if (split.Length >= 8)
{
int effectFlags = int.Parse(split[7]);
kiaiMode = (effectFlags & 1) > 0;
omitFirstBarSignature = (effectFlags & 8) > 0;
EffectFlags effectFlags = (EffectFlags)int.Parse(split[7]);
kiaiMode = effectFlags.HasFlag(EffectFlags.Kiai);
omitFirstBarSignature = effectFlags.HasFlag(EffectFlags.OmitFirstBarLine);
}
string stringSampleSet = sampleSet.ToString().ToLower();
@ -405,5 +407,13 @@ namespace osu.Game.Beatmaps.Formats
private double getOffsetTime() => ApplyOffsets ? offset : 0;
private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0);
[Flags]
internal enum EffectFlags
{
None = 0,
Kiai = 1,
OmitFirstBarLine = 8
}
}
}

View File

@ -57,6 +57,8 @@ namespace osu.Game.Beatmaps.Formats
protected virtual void ParseLine(T output, Section section, string line)
{
line = StripComments(line);
switch (section)
{
case Section.Colours:
@ -65,6 +67,14 @@ namespace osu.Game.Beatmaps.Formats
}
}
protected string StripComments(string line)
{
var index = line.IndexOf("//", StringComparison.Ordinal);
if (index > 0)
return line.Substring(0, index);
return line;
}
private bool hasComboColours;
private void handleColours(T output, string line)
@ -178,8 +188,8 @@ namespace osu.Game.Beatmaps.Formats
{
var baseInfo = base.ApplyTo(sampleInfo);
if (CustomSampleBank > 1)
baseInfo.Name += CustomSampleBank;
if (string.IsNullOrEmpty(baseInfo.Suffix) && CustomSampleBank > 1)
baseInfo.Suffix = CustomSampleBank.ToString();
return baseInfo;
}

View File

@ -42,6 +42,8 @@ namespace osu.Game.Beatmaps.Formats
protected override void ParseLine(Storyboard storyboard, Section section, string line)
{
line = StripComments(line);
switch (section)
{
case Section.Events:

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Beatmaps

View File

@ -1,6 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Beatmaps
{
/// <summary>

View File

@ -14,6 +14,7 @@ using osu.Framework.IO.File;
using System.IO;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI;
using osu.Game.Skinning;

View File

@ -3,6 +3,7 @@
using System.Linq;
using osu.Framework.Audio.Track;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Beatmaps

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using osu.Framework.IO.File;
using osu.Framework.Logging;
@ -175,7 +176,24 @@ namespace osu.Game.Database
/// <param name="archive">The archive to be imported.</param>
public TModel Import(ArchiveReader archive)
{
TModel item = null;
try
{
return Import(CreateModel(archive), archive);
}
catch (Exception e)
{
Logger.Error(e, $"Model creation of {archive.Name} failed.", LoggingTarget.Database);
return null;
}
}
/// <summary>
/// Import an item from a <see cref="TModel"/>.
/// </summary>
/// <param name="item">The model to be imported.</param>
/// <param name="archive">An optional archive to use for model population.</param>
public TModel Import(TModel item, ArchiveReader archive = null)
{
delayEvents();
try
@ -186,18 +204,16 @@ namespace osu.Game.Database
{
if (!write.IsTransactionLeader) throw new InvalidOperationException($"Ensure there is no parent transaction so errors can correctly be handled by {this}");
// create a new model (don't yet add to database)
item = CreateModel(archive);
var existing = CheckForExisting(item);
if (existing != null)
{
Logger.Log($"Found existing {typeof(TModel)} for {archive.Name} (ID {existing.ID}). Skipping import.", LoggingTarget.Database);
Logger.Log($"Found existing {typeof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database);
return existing;
}
item.Files = createFileInfos(archive, Files);
if (archive != null)
item.Files = createFileInfos(archive, Files);
Populate(item, archive);
@ -211,11 +227,11 @@ namespace osu.Game.Database
}
}
Logger.Log($"Import of {archive.Name} successfully completed!", LoggingTarget.Database);
Logger.Log($"Import of {item} successfully completed!", LoggingTarget.Database);
}
catch (Exception e)
{
Logger.Error(e, $"Import of {archive.Name} failed and has been rolled back.", LoggingTarget.Database);
Logger.Error(e, $"Import of {item} failed and has been rolled back.", LoggingTarget.Database);
item = null;
}
finally
@ -227,12 +243,6 @@ namespace osu.Game.Database
return item;
}
/// <summary>
/// Import an item from a <see cref="TModel"/>.
/// </summary>
/// <param name="item">The model to be imported.</param>
public void Import(TModel item) => ModelStore.Add(item);
/// <summary>
/// Perform an update of the specified item.
/// TODO: Support file changes.
@ -385,8 +395,8 @@ namespace osu.Game.Database
/// After this method, the model should be in a state ready to commit to a store.
/// </summary>
/// <param name="model">The model to populate.</param>
/// <param name="archive">The archive to use as a reference for population.</param>
protected virtual void Populate(TModel model, ArchiveReader archive)
/// <param name="archive">The archive to use as a reference for population. May be null.</param>
protected virtual void Populate(TModel model, [CanBeNull] ArchiveReader archive)
{
}

View File

@ -11,7 +11,7 @@ namespace osu.Game.Database
{
public class DatabaseContextFactory : IDatabaseContextFactory
{
private readonly GameHost host;
private readonly Storage storage;
private const string database_name = @"client";
@ -26,9 +26,9 @@ namespace osu.Game.Database
private IDbContextTransaction currentWriteTransaction;
public DatabaseContextFactory(GameHost host)
public DatabaseContextFactory(Storage storage)
{
this.host = host;
this.storage = storage;
recycleThreadContexts();
}
@ -117,7 +117,7 @@ namespace osu.Game.Database
private void recycleThreadContexts() => threadContexts = new ThreadLocal<OsuDbContext>(CreateContext);
protected virtual OsuDbContext CreateContext() => new OsuDbContext(host.Storage.GetDatabaseConnectionString(database_name))
protected virtual OsuDbContext CreateContext() => new OsuDbContext(storage.GetDatabaseConnectionString(database_name))
{
Database = { AutoTransactionsEnabled = false }
};
@ -129,7 +129,7 @@ namespace osu.Game.Database
recycleThreadContexts();
GC.Collect();
GC.WaitForPendingFinalizers();
host.Storage.DeleteDatabase(database_name);
storage.DeleteDatabase(database_name);
}
}
}

View File

@ -1,19 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Database
{
public class SingletonContextFactory : IDatabaseContextFactory
{
private readonly OsuDbContext context;
public SingletonContextFactory(OsuDbContext context)
{
this.context = context;
}
public OsuDbContext Get() => context;
public DatabaseWriteUsage GetForWrite(bool withTransaction = true) => new DatabaseWriteUsage(context, null);
}
}

View File

@ -5,10 +5,10 @@ using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using OpenTK;
using osu.Framework.Configuration;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.States;
using osu.Game.Audio;
using osu.Game.Input.Bindings;
using osu.Game.Overlays;
@ -22,13 +22,16 @@ namespace osu.Game.Graphics.Containers
protected virtual bool PlaySamplesOnStateChange => true;
protected override bool BlockPassThroughKeyboard => true;
private PreviewTrackManager previewTrackManager;
protected readonly Bindable<OverlayActivation> OverlayActivationMode = new Bindable<OverlayActivation>(OverlayActivation.All);
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs<IPreviewTrackOwner>(this);
return dependencies;
}
@ -69,10 +72,13 @@ namespace osu.Game.Graphics.Containers
public virtual bool OnPressed(GlobalAction action)
{
if (action == GlobalAction.Back)
switch (action)
{
State = Visibility.Hidden;
return true;
case GlobalAction.Back:
State = Visibility.Hidden;
return true;
case GlobalAction.Select:
return true;
}
return false;

View File

@ -6,7 +6,7 @@ using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Framework.Input.States;
namespace osu.Game.Graphics.Containers
{

View File

@ -2,7 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Input.EventArgs;
using osu.Framework.Input.States;
using OpenTK.Input;
namespace osu.Game.Graphics.Containers

View File

@ -8,11 +8,12 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Game.Configuration;
using System;
using JetBrains.Annotations;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.EventArgs;
using osu.Framework.Input.States;
using OpenTK.Input;
namespace osu.Game.Graphics.Cursor

View File

@ -21,6 +21,8 @@ namespace osu.Game.Graphics.Cursor
{
}
protected override double AppearDelay => (1 - CurrentTooltip.Alpha) * base.AppearDelay; // reduce appear delay if the tooltip is already partly visible.
public class OsuTooltip : Tooltip
{
private readonly Box background;

View File

@ -26,10 +26,10 @@ namespace osu.Game.Graphics.UserInterface
set
{
direction = value;
base.Direction = (direction & BarDirection.Horizontal) > 0 ? FillDirection.Vertical : FillDirection.Horizontal;
base.Direction = direction.HasFlag(BarDirection.Horizontal) ? FillDirection.Vertical : FillDirection.Horizontal;
foreach (var bar in Children)
{
bar.Size = (direction & BarDirection.Horizontal) > 0 ? new Vector2(1, 1.0f / Children.Count) : new Vector2(1.0f / Children.Count, 1);
bar.Size = direction.HasFlag(BarDirection.Horizontal) ? new Vector2(1, 1.0f / Children.Count) : new Vector2(1.0f / Children.Count, 1);
bar.Direction = direction;
}
}
@ -56,14 +56,14 @@ namespace osu.Game.Graphics.UserInterface
if (bar.Bar != null)
{
bar.Bar.Length = length;
bar.Bar.Size = (direction & BarDirection.Horizontal) > 0 ? new Vector2(1, size) : new Vector2(size, 1);
bar.Bar.Size = direction.HasFlag(BarDirection.Horizontal) ? new Vector2(1, size) : new Vector2(size, 1);
}
else
{
Add(new Bar
{
RelativeSizeAxes = Axes.Both,
Size = (direction & BarDirection.Horizontal) > 0 ? new Vector2(1, size) : new Vector2(size, 1),
Size = direction.HasFlag(BarDirection.Horizontal) ? new Vector2(1, size) : new Vector2(size, 1),
Length = length,
Direction = Direction,
});

View File

@ -13,7 +13,7 @@ using osu.Game.Graphics.Sprites;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Graphics.Containers;
using osu.Framework.Configuration;
using osu.Framework.Input;
using osu.Framework.Input.States;
namespace osu.Game.Graphics.UserInterface
{

View File

@ -5,7 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
using osu.Framework.Input.States;
using osu.Framework.Platform;
using OpenTK;
using OpenTK.Graphics;

View File

@ -2,8 +2,9 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Input;
using System;
using osu.Framework.Input.EventArgs;
using osu.Framework.Input.States;
using osu.Game.Input.Bindings;
using OpenTK.Input;

View File

@ -5,7 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions;
using osu.Framework.Input;
using osu.Framework.Input.States;
namespace osu.Game.Graphics.UserInterface
{

View File

@ -8,7 +8,7 @@ using osu.Framework.Audio.Sample;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Input.States;
namespace osu.Game.Graphics.UserInterface
{

View File

@ -3,31 +3,17 @@
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics.Containers;
using osu.Framework.Input.States;
namespace osu.Game.Graphics.UserInterface
{
public class IconButton : OsuClickableContainer
public class IconButton : OsuAnimatedButton
{
public const float BUTTON_SIZE = 30;
private Color4? flashColour;
/// <summary>
/// The colour that should be flashed when the <see cref="IconButton"/> is clicked.
/// </summary>
public Color4 FlashColour
{
get { return flashColour ?? Color4.White; }
set { flashColour = value; }
}
private Color4? iconColour;
/// <summary>
/// The icon colour. This does not affect <see cref="IconButton.Colour"/>.
/// </summary>
@ -42,6 +28,7 @@ namespace osu.Game.Graphics.UserInterface
}
private Color4? iconHoverColour;
/// <summary>
/// The icon colour while the <see cref="IconButton"/> is hovered.
/// </summary>
@ -51,20 +38,6 @@ namespace osu.Game.Graphics.UserInterface
set { iconHoverColour = value; }
}
private Color4? hoverColour;
/// <summary>
/// The background colour of the <see cref="IconButton"/> while it is hovered.
/// </summary>
public Color4 HoverColour
{
get { return hoverColour ?? Color4.White; }
set
{
hoverColour = value;
hover.Colour = value;
}
}
/// <summary>
/// The icon.
/// </summary>
@ -88,93 +61,39 @@ namespace osu.Game.Graphics.UserInterface
/// </summary>
public Vector2 ButtonSize
{
get { return content.Size; }
set { content.Size = value; }
get => Content.Size;
set
{
Content.RelativeSizeAxes = Axes.None;
Content.Size = value;
}
}
private readonly Container content;
private readonly SpriteIcon icon;
private readonly Box hover;
public IconButton()
{
AutoSizeAxes = Axes.Both;
ButtonSize = new Vector2(BUTTON_SIZE);
Children = new Drawable[]
Add(icon = new SpriteIcon
{
content = new Container
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(BUTTON_SIZE),
CornerRadius = 5,
Masking = true,
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.04f),
Type = EdgeEffectType.Shadow,
Radius = 5,
},
Children = new Drawable[]
{
hover = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
},
icon = new SpriteIcon
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(18),
}
}
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
if (hoverColour == null)
HoverColour = colours.Yellow.Opacity(0.6f);
if (flashColour == null)
FlashColour = colours.Yellow;
Enabled.ValueChanged += enabled => this.FadeColour(enabled ? Color4.White : colours.Gray9, 200, Easing.OutQuint);
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(18),
});
}
protected override bool OnHover(InputState state)
{
hover.FadeIn(500, Easing.OutQuint);
icon.FadeColour(IconHoverColour, 500, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
hover.FadeOut(500, Easing.OutQuint);
icon.FadeColour(IconColour, 500, Easing.OutQuint);
base.OnHoverLost(state);
}
protected override bool OnClick(InputState state)
{
hover.FlashColour(FlashColour, 800, Easing.OutQuint);
return base.OnClick(state);
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
content.ScaleTo(0.75f, 2000, Easing.OutQuint);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
content.ScaleTo(1, 1000, Easing.OutElastic);
return base.OnMouseUp(state, args);
}
}
}

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