mirror of
https://github.com/osukey/osukey.git
synced 2025-08-05 07:33:55 +09:00
Merge branch 'master' into mod-wind
This commit is contained in:
@ -1,9 +1,11 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
@ -17,13 +19,30 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
|
|
||||||
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
|
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } };
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
BeatmapInfo =
|
||||||
|
{
|
||||||
|
Ruleset = ruleset.RulesetInfo,
|
||||||
|
BaseDifficulty = new BeatmapDifficulty { CircleSize = 3.6f }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Should produce a hperdash
|
||||||
|
beatmap.HitObjects.Add(new Fruit { StartTime = 816, X = 308 / 512f, NewCombo = true });
|
||||||
|
beatmap.HitObjects.Add(new Fruit { StartTime = 1008, X = 56 / 512f, });
|
||||||
|
|
||||||
for (int i = 0; i < 512; i++)
|
for (int i = 0; i < 512; i++)
|
||||||
if (i % 5 < 3)
|
if (i % 5 < 3)
|
||||||
beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 });
|
beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = 2000 + i * 100, NewCombo = i % 8 == 0 });
|
||||||
|
|
||||||
return beatmap;
|
return beatmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void AddCheckSteps(Func<Player> player)
|
||||||
|
{
|
||||||
|
base.AddCheckSteps(player);
|
||||||
|
AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
CatchHitObject nextObject = objectWithDroplets[i + 1];
|
CatchHitObject nextObject = objectWithDroplets[i + 1];
|
||||||
|
|
||||||
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
|
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
|
||||||
double timeToNext = nextObject.StartTime - currentObject.StartTime;
|
double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable
|
||||||
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
|
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
|
||||||
float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext);
|
float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext);
|
||||||
if (distanceToHyper < 0)
|
if (distanceToHyper < 0)
|
||||||
|
@ -25,8 +25,8 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(RulesetConfigCache configCache)
|
private void load(RulesetConfigCache configCache)
|
||||||
{
|
{
|
||||||
var config = (ManiaConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
|
var config = (ManiaRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
|
||||||
config.BindWith(ManiaSetting.ScrollDirection, direction);
|
config.BindWith(ManiaRulesetSetting.ScrollDirection, direction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,9 @@ using osu.Game.Rulesets.Mania.UI;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Configuration
|
namespace osu.Game.Rulesets.Mania.Configuration
|
||||||
{
|
{
|
||||||
public class ManiaConfigManager : RulesetConfigManager<ManiaSetting>
|
public class ManiaRulesetConfigManager : RulesetConfigManager<ManiaRulesetSetting>
|
||||||
{
|
{
|
||||||
public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null)
|
public ManiaRulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null)
|
||||||
: base(settings, ruleset, variant)
|
: base(settings, ruleset, variant)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -19,17 +19,17 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
|||||||
{
|
{
|
||||||
base.InitialiseDefaults();
|
base.InitialiseDefaults();
|
||||||
|
|
||||||
Set(ManiaSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0);
|
Set(ManiaRulesetSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0);
|
||||||
Set(ManiaSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
Set(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
||||||
{
|
{
|
||||||
new TrackedSetting<double>(ManiaSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
|
new TrackedSetting<double>(ManiaRulesetSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ManiaSetting
|
public enum ManiaRulesetSetting
|
||||||
{
|
{
|
||||||
ScrollTime,
|
ScrollTime,
|
||||||
ScrollDirection
|
ScrollDirection
|
@ -168,7 +168,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
|
|
||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();
|
||||||
|
|
||||||
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaConfigManager(settings, RulesetInfo);
|
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaRulesetConfigManager(settings, RulesetInfo);
|
||||||
|
|
||||||
public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this);
|
public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this);
|
||||||
|
|
||||||
|
@ -22,19 +22,19 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
var config = (ManiaConfigManager)Config;
|
var config = (ManiaRulesetConfigManager)Config;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new SettingsEnumDropdown<ManiaScrollingDirection>
|
new SettingsEnumDropdown<ManiaScrollingDirection>
|
||||||
{
|
{
|
||||||
LabelText = "Scrolling direction",
|
LabelText = "Scrolling direction",
|
||||||
Bindable = config.GetBindable<ManiaScrollingDirection>(ManiaSetting.ScrollDirection)
|
Bindable = config.GetBindable<ManiaScrollingDirection>(ManiaRulesetSetting.ScrollDirection)
|
||||||
},
|
},
|
||||||
new SettingsSlider<double, TimeSlider>
|
new SettingsSlider<double, TimeSlider>
|
||||||
{
|
{
|
||||||
LabelText = "Scroll speed",
|
LabelText = "Scroll speed",
|
||||||
Bindable = config.GetBindable<double>(ManiaSetting.ScrollTime)
|
Bindable = config.GetBindable<double>(ManiaRulesetSetting.ScrollTime)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
public IEnumerable<BarLine> BarLines;
|
public IEnumerable<BarLine> BarLines;
|
||||||
|
|
||||||
protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config;
|
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
||||||
|
|
||||||
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
||||||
|
|
||||||
@ -74,10 +74,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
BarLines.ForEach(Playfield.Add);
|
BarLines.ForEach(Playfield.Add);
|
||||||
|
|
||||||
Config.BindWith(ManiaSetting.ScrollDirection, configDirection);
|
Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection);
|
||||||
configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true);
|
configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true);
|
||||||
|
|
||||||
Config.BindWith(ManiaSetting.ScrollTime, TimeRange);
|
Config.BindWith(ManiaRulesetSetting.ScrollTime, TimeRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Configuration;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Configuration
|
||||||
|
{
|
||||||
|
public class OsuRulesetConfigManager : RulesetConfigManager<OsuRulesetSetting>
|
||||||
|
{
|
||||||
|
public OsuRulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null)
|
||||||
|
: base(settings, ruleset, variant)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void InitialiseDefaults()
|
||||||
|
{
|
||||||
|
base.InitialiseDefaults();
|
||||||
|
|
||||||
|
Set(OsuRulesetSetting.SnakingInSliders, true);
|
||||||
|
Set(OsuRulesetSetting.SnakingOutSliders, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum OsuRulesetSetting
|
||||||
|
{
|
||||||
|
SnakingInSliders,
|
||||||
|
SnakingOutSliders
|
||||||
|
}
|
||||||
|
}
|
@ -10,8 +10,8 @@ using System.Linq;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Configuration;
|
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Configuration;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
@ -33,6 +33,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
private readonly IBindable<float> scaleBindable = new Bindable<float>();
|
private readonly IBindable<float> scaleBindable = new Bindable<float>();
|
||||||
private readonly IBindable<SliderPath> pathBindable = new Bindable<SliderPath>();
|
private readonly IBindable<SliderPath> pathBindable = new Bindable<SliderPath>();
|
||||||
|
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
private OsuRulesetConfigManager config { get; set; }
|
||||||
|
|
||||||
public DrawableSlider(Slider s)
|
public DrawableSlider(Slider s)
|
||||||
: base(s)
|
: base(s)
|
||||||
{
|
{
|
||||||
@ -94,10 +97,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuConfigManager config)
|
private void load()
|
||||||
{
|
{
|
||||||
config.BindWith(OsuSetting.SnakingInSliders, Body.SnakingIn);
|
config?.BindWith(OsuRulesetSetting.SnakingInSliders, Body.SnakingIn);
|
||||||
config.BindWith(OsuSetting.SnakingOutSliders, Body.SnakingOut);
|
config?.BindWith(OsuRulesetSetting.SnakingOutSliders, Body.SnakingOut);
|
||||||
|
|
||||||
positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
|
positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
|
||||||
scaleBindable.BindValueChanged(scale =>
|
scaleBindable.BindValueChanged(scale =>
|
||||||
|
@ -17,8 +17,11 @@ using osu.Game.Rulesets.Osu.Objects;
|
|||||||
using osu.Game.Rulesets.Osu.Replays;
|
using osu.Game.Rulesets.Osu.Replays;
|
||||||
using osu.Game.Rulesets.Replays.Types;
|
using osu.Game.Rulesets.Replays.Types;
|
||||||
using osu.Game.Beatmaps.Legacy;
|
using osu.Game.Beatmaps.Legacy;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Configuration;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Osu.Configuration;
|
||||||
using osu.Game.Rulesets.Osu.Difficulty;
|
using osu.Game.Rulesets.Osu.Difficulty;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
@ -146,12 +149,14 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
|
|
||||||
public override string ShortName => "osu";
|
public override string ShortName => "osu";
|
||||||
|
|
||||||
public override RulesetSettingsSubsection CreateSettings() => new OsuSettings(this);
|
public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this);
|
||||||
|
|
||||||
public override int? LegacyID => 0;
|
public override int? LegacyID => 0;
|
||||||
|
|
||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame();
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame();
|
||||||
|
|
||||||
|
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo);
|
||||||
|
|
||||||
public OsuRuleset(RulesetInfo rulesetInfo = null)
|
public OsuRuleset(RulesetInfo rulesetInfo = null)
|
||||||
: base(rulesetInfo)
|
: base(rulesetInfo)
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Configuration;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Replays;
|
using osu.Game.Rulesets.Osu.Replays;
|
||||||
@ -20,6 +21,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
{
|
{
|
||||||
public class OsuRulesetContainer : RulesetContainer<OsuPlayfield, OsuHitObject>
|
public class OsuRulesetContainer : RulesetContainer<OsuPlayfield, OsuHitObject>
|
||||||
{
|
{
|
||||||
|
protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config;
|
||||||
|
|
||||||
public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
|
public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||||
: base(ruleset, beatmap)
|
: base(ruleset, beatmap)
|
||||||
{
|
{
|
||||||
|
@ -3,34 +3,36 @@
|
|||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Configuration;
|
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Rulesets.Osu.Configuration;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.UI
|
namespace osu.Game.Rulesets.Osu.UI
|
||||||
{
|
{
|
||||||
public class OsuSettings : RulesetSettingsSubsection
|
public class OsuSettingsSubsection : RulesetSettingsSubsection
|
||||||
{
|
{
|
||||||
protected override string Header => "osu!";
|
protected override string Header => "osu!";
|
||||||
|
|
||||||
public OsuSettings(Ruleset ruleset)
|
public OsuSettingsSubsection(Ruleset ruleset)
|
||||||
: base(ruleset)
|
: base(ruleset)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuConfigManager config)
|
private void load()
|
||||||
{
|
{
|
||||||
|
var config = (OsuRulesetConfigManager)Config;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Snaking in sliders",
|
LabelText = "Snaking in sliders",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.SnakingInSliders)
|
Bindable = config.GetBindable<bool>(OsuRulesetSetting.SnakingInSliders)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Snaking out sliders",
|
LabelText = "Snaking out sliders",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.SnakingOutSliders)
|
Bindable = config.GetBindable<bool>(OsuRulesetSetting.SnakingOutSliders)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -333,6 +333,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.AreEqual("hit_2.wav", getTestableSampleInfo(hitObjects[1]).LookupNames.First());
|
Assert.AreEqual("hit_2.wav", getTestableSampleInfo(hitObjects[1]).LookupNames.First());
|
||||||
Assert.AreEqual("normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
|
Assert.AreEqual("normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
|
||||||
Assert.AreEqual("hit_1.wav", getTestableSampleInfo(hitObjects[3]).LookupNames.First());
|
Assert.AreEqual("hit_1.wav", getTestableSampleInfo(hitObjects[3]).LookupNames.First());
|
||||||
|
Assert.AreEqual(70, getTestableSampleInfo(hitObjects[3]).Volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
||||||
|
@ -101,7 +101,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
int fireCount = 0;
|
int fireCount = 0;
|
||||||
|
|
||||||
// ReSharper disable once AccessToModifiedClosure
|
// ReSharper disable once AccessToModifiedClosure
|
||||||
manager.ItemAdded += (_, __, ___) => fireCount++;
|
manager.ItemAdded += (_, __) => fireCount++;
|
||||||
manager.ItemRemoved += _ => fireCount++;
|
manager.ItemRemoved += _ => fireCount++;
|
||||||
|
|
||||||
var imported = LoadOszIntoOsu(osu);
|
var imported = LoadOszIntoOsu(osu);
|
||||||
|
@ -13,4 +13,4 @@ SampleSet: Normal
|
|||||||
255,193,2170,1,0,0:0:0:0:hit_1.wav
|
255,193,2170,1,0,0:0:0:0:hit_1.wav
|
||||||
256,191,2638,5,0,0:0:0:0:hit_2.wav
|
256,191,2638,5,0,0:0:0:0:hit_2.wav
|
||||||
255,193,3107,1,0,0:0:0:0:
|
255,193,3107,1,0,0:0:0:0:
|
||||||
256,191,3576,1,0,0:0:0:0:hit_1.wav
|
256,191,3576,1,0,0:0:0:70:hit_1.wav
|
||||||
|
@ -188,10 +188,10 @@ namespace osu.Game.Tests.Visual
|
|||||||
public void PauseTest()
|
public void PauseTest()
|
||||||
{
|
{
|
||||||
performFullSetup(true);
|
performFullSetup(true);
|
||||||
AddStep("Pause", () => player.CurrentPauseContainer.Pause());
|
AddStep("Pause", () => player.CurrentPausableGameplayContainer.Pause());
|
||||||
waitForDim();
|
waitForDim();
|
||||||
AddAssert("Screen is dimmed", () => songSelect.IsBackgroundDimmed());
|
AddAssert("Screen is dimmed", () => songSelect.IsBackgroundDimmed());
|
||||||
AddStep("Unpause", () => player.CurrentPauseContainer.Resume());
|
AddStep("Unpause", () => player.CurrentPausableGameplayContainer.Resume());
|
||||||
waitForDim();
|
waitForDim();
|
||||||
AddAssert("Screen is dimmed", () => songSelect.IsBackgroundDimmed());
|
AddAssert("Screen is dimmed", () => songSelect.IsBackgroundDimmed());
|
||||||
}
|
}
|
||||||
@ -328,7 +328,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public PauseContainer CurrentPauseContainer => PauseContainer;
|
public PausableGameplayContainer CurrentPausableGameplayContainer => PausableGameplayContainer;
|
||||||
|
|
||||||
public UserDimContainer CurrentStoryboardContainer => StoryboardContainer;
|
public UserDimContainer CurrentStoryboardContainer => StoryboardContainer;
|
||||||
|
|
||||||
|
47
osu.Game.Tests/Visual/TestCaseDirectPanel.cs
Normal file
47
osu.Game.Tests/Visual/TestCaseDirectPanel.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Overlays.Direct;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual
|
||||||
|
{
|
||||||
|
public class TestCaseDirectPanel : OsuTestCase
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(DirectGridPanel),
|
||||||
|
typeof(DirectListPanel),
|
||||||
|
typeof(IconPill)
|
||||||
|
};
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
var beatmap = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, null);
|
||||||
|
beatmap.BeatmapSetInfo.OnlineInfo.HasVideo = true;
|
||||||
|
beatmap.BeatmapSetInfo.OnlineInfo.HasStoryboard = true;
|
||||||
|
|
||||||
|
Child = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Padding = new MarginPadding(20),
|
||||||
|
Spacing = new Vector2(0, 20),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new DirectGridPanel(beatmap.BeatmapSetInfo),
|
||||||
|
new DirectListPanel(beatmap.BeatmapSetInfo)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,15 +17,15 @@ namespace osu.Game.Tests.Visual
|
|||||||
[Description("player pause/fail screens")]
|
[Description("player pause/fail screens")]
|
||||||
public class TestCaseGameplayMenuOverlay : ManualInputManagerTestCase
|
public class TestCaseGameplayMenuOverlay : ManualInputManagerTestCase
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer) };
|
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PausableGameplayContainer) };
|
||||||
|
|
||||||
private FailOverlay failOverlay;
|
private FailOverlay failOverlay;
|
||||||
private PauseContainer.PauseOverlay pauseOverlay;
|
private PausableGameplayContainer.PauseOverlay pauseOverlay;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Add(pauseOverlay = new PauseContainer.PauseOverlay
|
Add(pauseOverlay = new PausableGameplayContainer.PauseOverlay
|
||||||
{
|
{
|
||||||
OnResume = () => Logger.Log(@"Resume"),
|
OnResume = () => Logger.Log(@"Resume"),
|
||||||
OnRetry = () => Logger.Log(@"Retry"),
|
OnRetry = () => Logger.Log(@"Retry"),
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
@ -19,14 +20,20 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
private readonly StopwatchClock clock;
|
private readonly StopwatchClock clock;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private readonly GameplayClock gameplayClock;
|
||||||
|
|
||||||
|
private readonly FramedClock framedClock;
|
||||||
|
|
||||||
public TestCaseSongProgress()
|
public TestCaseSongProgress()
|
||||||
{
|
{
|
||||||
clock = new StopwatchClock(true);
|
clock = new StopwatchClock(true);
|
||||||
|
|
||||||
|
gameplayClock = new GameplayClock(framedClock = new FramedClock(clock));
|
||||||
|
|
||||||
Add(progress = new SongProgress
|
Add(progress = new SongProgress
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AudioClock = new StopwatchClock(true),
|
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
});
|
});
|
||||||
@ -68,8 +75,13 @@ namespace osu.Game.Tests.Visual
|
|||||||
progress.Objects = objects;
|
progress.Objects = objects;
|
||||||
graph.Objects = objects;
|
graph.Objects = objects;
|
||||||
|
|
||||||
progress.AudioClock = clock;
|
progress.RequestSeek = pos => clock.Seek(pos);
|
||||||
progress.OnSeek = pos => clock.Seek(pos);
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
framedClock.ProcessFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestSongProgressGraph : SongProgressGraph
|
private class TestSongProgressGraph : SongProgressGraph
|
||||||
|
@ -11,7 +11,6 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
@ -50,11 +49,6 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<DownloadBeatmapSetRequest> BeatmapDownloadFailed;
|
public event Action<DownloadBeatmapSetRequest> BeatmapDownloadFailed;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fired when a beatmap load is requested (into the interactive game UI).
|
|
||||||
/// </summary>
|
|
||||||
public Action<BeatmapSetInfo> PresentBeatmap;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
|
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -151,8 +145,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
var downloadNotification = new DownloadNotification
|
var downloadNotification = new DownloadNotification
|
||||||
{
|
{
|
||||||
CompletionText = $"Imported {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}!",
|
Text = $"Downloading {beatmapSetInfo}",
|
||||||
Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo);
|
var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo);
|
||||||
@ -165,20 +158,10 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
request.Success += filename =>
|
request.Success += filename =>
|
||||||
{
|
{
|
||||||
downloadNotification.Text = $"Importing {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}";
|
|
||||||
|
|
||||||
Task.Factory.StartNew(() =>
|
Task.Factory.StartNew(() =>
|
||||||
{
|
{
|
||||||
// This gets scheduled back to the update thread, but we want the import to run in the background.
|
// This gets scheduled back to the update thread, but we want the import to run in the background.
|
||||||
var importedBeatmap = Import(filename);
|
Import(downloadNotification, filename);
|
||||||
|
|
||||||
downloadNotification.CompletionClickAction = () =>
|
|
||||||
{
|
|
||||||
PresentCompletedImport(importedBeatmap.Yield());
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
downloadNotification.State = ProgressNotificationState.Completed;
|
|
||||||
|
|
||||||
currentDownloads.Remove(request);
|
currentDownloads.Remove(request);
|
||||||
}, TaskCreationOptions.LongRunning);
|
}, TaskCreationOptions.LongRunning);
|
||||||
};
|
};
|
||||||
@ -221,12 +204,6 @@ namespace osu.Game.Beatmaps
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PresentCompletedImport(IEnumerable<BeatmapSetInfo> imported)
|
|
||||||
{
|
|
||||||
base.PresentCompletedImport(imported);
|
|
||||||
PresentBeatmap?.Invoke(imported.LastOrDefault());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get an existing download request if it exists.
|
/// Get an existing download request if it exists.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -48,11 +48,11 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
|
|
||||||
var rating = beatmap.StarDifficulty;
|
var rating = beatmap.StarDifficulty;
|
||||||
|
|
||||||
if (rating < 1.5) return DifficultyRating.Easy;
|
if (rating < 2.0) return DifficultyRating.Easy;
|
||||||
if (rating < 2.25) return DifficultyRating.Normal;
|
if (rating < 2.7) return DifficultyRating.Normal;
|
||||||
if (rating < 3.75) return DifficultyRating.Hard;
|
if (rating < 4.0) return DifficultyRating.Hard;
|
||||||
if (rating < 5.25) return DifficultyRating.Insane;
|
if (rating < 5.3) return DifficultyRating.Insane;
|
||||||
if (rating < 6.75) return DifficultyRating.Expert;
|
if (rating < 6.5) return DifficultyRating.Expert;
|
||||||
|
|
||||||
return DifficultyRating.ExpertPlus;
|
return DifficultyRating.ExpertPlus;
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,12 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private BeatmapManager beatmaps { get; set; }
|
private BeatmapManager beatmaps { get; set; }
|
||||||
|
|
||||||
public UpdateableBeatmapBackgroundSprite()
|
private readonly BeatmapSetCoverType beatmapSetCoverType;
|
||||||
|
|
||||||
|
public UpdateableBeatmapBackgroundSprite(BeatmapSetCoverType beatmapSetCoverType = BeatmapSetCoverType.Cover)
|
||||||
{
|
{
|
||||||
Beatmap.BindValueChanged(b => Model = b.NewValue);
|
Beatmap.BindValueChanged(b => Model = b.NewValue);
|
||||||
|
this.beatmapSetCoverType = beatmapSetCoverType;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Drawable CreateDrawable(BeatmapInfo model)
|
protected override Drawable CreateDrawable(BeatmapInfo model)
|
||||||
@ -31,10 +34,18 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
|
|
||||||
var localBeatmap = beatmaps.GetWorkingBeatmap(model);
|
var localBeatmap = beatmaps.GetWorkingBeatmap(model);
|
||||||
|
|
||||||
if (localBeatmap.BeatmapInfo.ID == 0 && model?.BeatmapSet?.OnlineInfo != null)
|
if (model?.BeatmapSet?.OnlineInfo != null)
|
||||||
drawable = new BeatmapSetCover(model.BeatmapSet);
|
drawable = new BeatmapSetCover(model.BeatmapSet, beatmapSetCoverType);
|
||||||
else
|
else if (localBeatmap.BeatmapInfo.ID != 0)
|
||||||
|
{
|
||||||
|
// Fall back to local background if one exists
|
||||||
drawable = new BeatmapBackgroundSprite(localBeatmap);
|
drawable = new BeatmapBackgroundSprite(localBeatmap);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use the default background if somehow an online set does not exist and we don't have a local copy.
|
||||||
|
drawable = new BeatmapBackgroundSprite(beatmaps.DefaultBeatmap);
|
||||||
|
}
|
||||||
|
|
||||||
drawable.RelativeSizeAxes = Axes.Both;
|
drawable.RelativeSizeAxes = Axes.Both;
|
||||||
drawable.Anchor = Anchor.Centre;
|
drawable.Anchor = Anchor.Centre;
|
||||||
|
@ -72,9 +72,6 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
Set(OsuSetting.MenuParallax, true);
|
Set(OsuSetting.MenuParallax, true);
|
||||||
|
|
||||||
Set(OsuSetting.SnakingInSliders, true);
|
|
||||||
Set(OsuSetting.SnakingOutSliders, true);
|
|
||||||
|
|
||||||
// Gameplay
|
// Gameplay
|
||||||
Set(OsuSetting.DimLevel, 0.3, 0, 1, 0.01);
|
Set(OsuSetting.DimLevel, 0.3, 0, 1, 0.01);
|
||||||
Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01);
|
Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01);
|
||||||
@ -150,8 +147,6 @@ namespace osu.Game.Configuration
|
|||||||
DisplayStarsMinimum,
|
DisplayStarsMinimum,
|
||||||
DisplayStarsMaximum,
|
DisplayStarsMaximum,
|
||||||
RandomSelectAlgorithm,
|
RandomSelectAlgorithm,
|
||||||
SnakingInSliders,
|
|
||||||
SnakingOutSliders,
|
|
||||||
ShowFpsDisplay,
|
ShowFpsDisplay,
|
||||||
ChatDisplayHeight,
|
ChatDisplayHeight,
|
||||||
Version,
|
Version,
|
||||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Database
|
|||||||
where TModel : class, IHasFiles<TFileModel>, IHasPrimaryKey, ISoftDelete
|
where TModel : class, IHasFiles<TFileModel>, IHasPrimaryKey, ISoftDelete
|
||||||
where TFileModel : INamedFileInfo, new()
|
where TFileModel : INamedFileInfo, new()
|
||||||
{
|
{
|
||||||
public delegate void ItemAddedDelegate(TModel model, bool existing, bool silent);
|
public delegate void ItemAddedDelegate(TModel model, bool existing);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set an endpoint for notifications to be posted to.
|
/// Set an endpoint for notifications to be posted to.
|
||||||
@ -113,7 +113,7 @@ namespace osu.Game.Database
|
|||||||
ContextFactory = contextFactory;
|
ContextFactory = contextFactory;
|
||||||
|
|
||||||
ModelStore = modelStore;
|
ModelStore = modelStore;
|
||||||
ModelStore.ItemAdded += (item, silent) => handleEvent(() => ItemAdded?.Invoke(item, false, silent));
|
ModelStore.ItemAdded += item => handleEvent(() => ItemAdded?.Invoke(item, false));
|
||||||
ModelStore.ItemRemoved += s => handleEvent(() => ItemRemoved?.Invoke(s));
|
ModelStore.ItemRemoved += s => handleEvent(() => ItemRemoved?.Invoke(s));
|
||||||
|
|
||||||
Files = new FileStore(contextFactory, storage);
|
Files = new FileStore(contextFactory, storage);
|
||||||
@ -131,14 +131,18 @@ namespace osu.Game.Database
|
|||||||
/// <param name="paths">One or more archive locations on disk.</param>
|
/// <param name="paths">One or more archive locations on disk.</param>
|
||||||
public void Import(params string[] paths)
|
public void Import(params string[] paths)
|
||||||
{
|
{
|
||||||
var notification = new ProgressNotification
|
var notification = new ProgressNotification { State = ProgressNotificationState.Active };
|
||||||
{
|
|
||||||
Text = "Import is initialising...",
|
|
||||||
Progress = 0,
|
|
||||||
State = ProgressNotificationState.Active,
|
|
||||||
};
|
|
||||||
|
|
||||||
PostNotification?.Invoke(notification);
|
PostNotification?.Invoke(notification);
|
||||||
|
Import(notification, paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void Import(ProgressNotification notification, params string[] paths)
|
||||||
|
{
|
||||||
|
notification.Progress = 0;
|
||||||
|
notification.Text = "Import is initialising...";
|
||||||
|
|
||||||
|
var term = $"{typeof(TModel).Name.Replace("Info", "").ToLower()}";
|
||||||
|
|
||||||
List<TModel> imported = new List<TModel>();
|
List<TModel> imported = new List<TModel>();
|
||||||
|
|
||||||
@ -151,7 +155,18 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
notification.Text = $"Importing ({++current} of {paths.Length})\n{Path.GetFileName(path)}";
|
var text = "Importing ";
|
||||||
|
|
||||||
|
if (path.Length > 1)
|
||||||
|
text += $"{++current} of {paths.Length} {term}s..";
|
||||||
|
else
|
||||||
|
text += $"{term}..";
|
||||||
|
|
||||||
|
// only show the filename if it isn't a temporary one (as those look ugly).
|
||||||
|
if (!path.Contains(Path.GetTempPath()))
|
||||||
|
text += $"\n{Path.GetFileName(path)}";
|
||||||
|
|
||||||
|
notification.Text = text;
|
||||||
|
|
||||||
imported.Add(Import(path));
|
imported.Add(Import(path));
|
||||||
|
|
||||||
@ -171,13 +186,20 @@ namespace osu.Game.Database
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
notification.CompletionText = $"Imported {current} {typeof(TModel).Name.Replace("Info", "").ToLower()}s!";
|
notification.CompletionText = imported.Count == 1
|
||||||
notification.CompletionClickAction += () =>
|
? $"Imported {imported.First()}!"
|
||||||
|
: $"Imported {current} {term}s!";
|
||||||
|
|
||||||
|
if (imported.Count > 0 && PresentImport != null)
|
||||||
{
|
{
|
||||||
if (imported.Count > 0)
|
notification.CompletionText += " Click to view.";
|
||||||
PresentCompletedImport(imported);
|
notification.CompletionClickAction = () =>
|
||||||
|
{
|
||||||
|
PresentImport?.Invoke(imported);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
notification.State = ProgressNotificationState.Completed;
|
notification.State = ProgressNotificationState.Completed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,9 +232,10 @@ namespace osu.Game.Database
|
|||||||
return import;
|
return import;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void PresentCompletedImport(IEnumerable<TModel> imported)
|
/// <summary>
|
||||||
{
|
/// Fired when the user requests to view the resulting import.
|
||||||
}
|
/// </summary>
|
||||||
|
public Action<IEnumerable<TModel>> PresentImport;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Import an item from an <see cref="ArchiveReader"/>.
|
/// Import an item from an <see cref="ArchiveReader"/>.
|
||||||
@ -228,7 +251,7 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
model.Hash = computeHash(archive);
|
model.Hash = computeHash(archive);
|
||||||
|
|
||||||
return Import(model, false, archive);
|
return Import(model, archive);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -262,9 +285,8 @@ namespace osu.Game.Database
|
|||||||
/// Import an item from a <see cref="TModel"/>.
|
/// Import an item from a <see cref="TModel"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The model to be imported.</param>
|
/// <param name="item">The model to be imported.</param>
|
||||||
/// <param name="silent">Whether the user should be notified fo the import.</param>
|
|
||||||
/// <param name="archive">An optional archive to use for model population.</param>
|
/// <param name="archive">An optional archive to use for model population.</param>
|
||||||
public TModel Import(TModel item, bool silent = false, ArchiveReader archive = null)
|
public TModel Import(TModel item, ArchiveReader archive = null)
|
||||||
{
|
{
|
||||||
delayEvents();
|
delayEvents();
|
||||||
|
|
||||||
@ -284,7 +306,7 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
Undelete(existing);
|
Undelete(existing);
|
||||||
Logger.Log($"Found existing {typeof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database);
|
Logger.Log($"Found existing {typeof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database);
|
||||||
handleEvent(() => ItemAdded?.Invoke(existing, true, silent));
|
handleEvent(() => ItemAdded?.Invoke(existing, true));
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,7 +316,7 @@ namespace osu.Game.Database
|
|||||||
Populate(item, archive);
|
Populate(item, archive);
|
||||||
|
|
||||||
// import to store
|
// import to store
|
||||||
ModelStore.Add(item, silent);
|
ModelStore.Add(item);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -16,9 +16,7 @@ namespace osu.Game.Database
|
|||||||
public abstract class MutableDatabaseBackedStore<T> : DatabaseBackedStore
|
public abstract class MutableDatabaseBackedStore<T> : DatabaseBackedStore
|
||||||
where T : class, IHasPrimaryKey, ISoftDelete
|
where T : class, IHasPrimaryKey, ISoftDelete
|
||||||
{
|
{
|
||||||
public delegate void ItemAddedDelegate(T model, bool silent);
|
public event Action<T> ItemAdded;
|
||||||
|
|
||||||
public event ItemAddedDelegate ItemAdded;
|
|
||||||
public event Action<T> ItemRemoved;
|
public event Action<T> ItemRemoved;
|
||||||
|
|
||||||
protected MutableDatabaseBackedStore(IDatabaseContextFactory contextFactory, Storage storage = null)
|
protected MutableDatabaseBackedStore(IDatabaseContextFactory contextFactory, Storage storage = null)
|
||||||
@ -35,8 +33,7 @@ namespace osu.Game.Database
|
|||||||
/// Add a <see cref="T"/> to the database.
|
/// Add a <see cref="T"/> to the database.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The item to add.</param>
|
/// <param name="item">The item to add.</param>
|
||||||
/// <param name="silent">Whether the user should be notified of the addition.</param>
|
public void Add(T item)
|
||||||
public void Add(T item, bool silent)
|
|
||||||
{
|
{
|
||||||
using (var usage = ContextFactory.GetForWrite())
|
using (var usage = ContextFactory.GetForWrite())
|
||||||
{
|
{
|
||||||
@ -44,7 +41,7 @@ namespace osu.Game.Database
|
|||||||
context.Attach(item);
|
context.Attach(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemAdded?.Invoke(item, silent);
|
ItemAdded?.Invoke(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -57,7 +54,7 @@ namespace osu.Game.Database
|
|||||||
usage.Context.Update(item);
|
usage.Context.Update(item);
|
||||||
|
|
||||||
ItemRemoved?.Invoke(item);
|
ItemRemoved?.Invoke(item);
|
||||||
ItemAdded?.Invoke(item, true);
|
ItemAdded?.Invoke(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -94,7 +91,7 @@ namespace osu.Game.Database
|
|||||||
item.DeletePending = false;
|
item.DeletePending = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemAdded?.Invoke(item, true);
|
ItemAdded?.Invoke(item);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +59,6 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
public abstract void Increment(T amount);
|
public abstract void Increment(T amount);
|
||||||
|
|
||||||
|
|
||||||
public float TextSize
|
public float TextSize
|
||||||
{
|
{
|
||||||
get => DisplayedCountSpriteText.Font.Size;
|
get => DisplayedCountSpriteText.Font.Size;
|
||||||
|
@ -36,11 +36,12 @@ namespace osu.Game.Online.API.Requests
|
|||||||
|
|
||||||
[Description("Ranked & Approved")]
|
[Description("Ranked & Approved")]
|
||||||
RankedApproved = 0,
|
RankedApproved = 0,
|
||||||
Approved = 1,
|
Qualified = 3,
|
||||||
Loved = 8,
|
Loved = 8,
|
||||||
Favourites = 2,
|
Favourites = 2,
|
||||||
Qualified = 3,
|
|
||||||
Pending = 4,
|
[Description("Pending & WIP")]
|
||||||
|
PendingWIP = 4,
|
||||||
Graveyard = 5,
|
Graveyard = 5,
|
||||||
|
|
||||||
[Description("My Maps")]
|
[Description("My Maps")]
|
||||||
|
@ -213,12 +213,6 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
pendingUpdateScores?.Cancel();
|
pendingUpdateScores?.Cancel();
|
||||||
pendingUpdateScores = Schedule(() =>
|
pendingUpdateScores = Schedule(() =>
|
||||||
{
|
{
|
||||||
if (api?.IsLoggedIn != true)
|
|
||||||
{
|
|
||||||
PlaceholderState = PlaceholderState.NotLoggedIn;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlaceholderState = PlaceholderState.Retrieving;
|
PlaceholderState = PlaceholderState.Retrieving;
|
||||||
loading.Show();
|
loading.Show();
|
||||||
|
|
||||||
@ -231,6 +225,12 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
if (getScoresRequest == null)
|
if (getScoresRequest == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (api?.IsLoggedIn != true)
|
||||||
|
{
|
||||||
|
PlaceholderState = PlaceholderState.NotLoggedIn;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
getScoresRequest.Failure += e => Schedule(() =>
|
getScoresRequest.Failure += e => Schedule(() =>
|
||||||
{
|
{
|
||||||
if (e is OperationCanceledException)
|
if (e is OperationCanceledException)
|
||||||
@ -243,6 +243,11 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs a fetch/refresh of scores to be displayed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scoresCallback">A callback which should be called when fetching is completed. Scheduling is not required.</param>
|
||||||
|
/// <returns>An <see cref="APIRequest"/> responsible for the fetch operation. This will be queued and performed automatically.</returns>
|
||||||
protected abstract APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback);
|
protected abstract APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback);
|
||||||
|
|
||||||
private Placeholder currentPlaceholder;
|
private Placeholder currentPlaceholder;
|
||||||
|
@ -167,8 +167,6 @@ namespace osu.Game
|
|||||||
{
|
{
|
||||||
this.frameworkConfig = frameworkConfig;
|
this.frameworkConfig = frameworkConfig;
|
||||||
|
|
||||||
ScoreManager.ItemAdded += (score, _, silent) => Schedule(() => LoadScore(score, silent));
|
|
||||||
|
|
||||||
if (!Host.IsPrimaryInstance)
|
if (!Host.IsPrimaryInstance)
|
||||||
{
|
{
|
||||||
Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error);
|
Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error);
|
||||||
@ -217,63 +215,12 @@ namespace osu.Game
|
|||||||
externalLinkOpener.OpenUrlExternally(url);
|
externalLinkOpener.OpenUrlExternally(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScheduledDelegate scoreLoad;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Show a beatmap set as an overlay.
|
/// Show a beatmap set as an overlay.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="setId">The set to display.</param>
|
/// <param name="setId">The set to display.</param>
|
||||||
public void ShowBeatmapSet(int setId) => beatmapSetOverlay.FetchAndShowBeatmapSet(setId);
|
public void ShowBeatmapSet(int setId) => beatmapSetOverlay.FetchAndShowBeatmapSet(setId);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Present a beatmap at song select.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="beatmap">The beatmap to select.</param>
|
|
||||||
public void PresentBeatmap(BeatmapSetInfo beatmap)
|
|
||||||
{
|
|
||||||
if (menuScreen == null)
|
|
||||||
{
|
|
||||||
Schedule(() => PresentBeatmap(beatmap));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseAllOverlays(false);
|
|
||||||
|
|
||||||
void setBeatmap()
|
|
||||||
{
|
|
||||||
if (Beatmap.Disabled)
|
|
||||||
{
|
|
||||||
Schedule(setBeatmap);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var databasedSet = beatmap.OnlineBeatmapSetID != null ? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID) : BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmap.Hash);
|
|
||||||
|
|
||||||
if (databasedSet != null)
|
|
||||||
{
|
|
||||||
// Use first beatmap available for current ruleset, else switch ruleset.
|
|
||||||
var first = databasedSet.Beatmaps.Find(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First();
|
|
||||||
|
|
||||||
ruleset.Value = first.Ruleset;
|
|
||||||
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (screenStack.CurrentScreen)
|
|
||||||
{
|
|
||||||
case SongSelect _:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// navigate to song select if we are not already there.
|
|
||||||
|
|
||||||
menuScreen.MakeCurrent();
|
|
||||||
menuScreen.LoadToSolo();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
setBeatmap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Show a user's profile as an overlay.
|
/// Show a user's profile as an overlay.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -286,19 +233,44 @@ namespace osu.Game
|
|||||||
/// <param name="beatmapId">The beatmap to show.</param>
|
/// <param name="beatmapId">The beatmap to show.</param>
|
||||||
public void ShowBeatmap(int beatmapId) => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId);
|
public void ShowBeatmap(int beatmapId) => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId);
|
||||||
|
|
||||||
protected void LoadScore(ScoreInfo score, bool silent)
|
/// <summary>
|
||||||
|
/// Present a beatmap at song select immediately.
|
||||||
|
/// The user should have already requested this interactively.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beatmap">The beatmap to select.</param>
|
||||||
|
public void PresentBeatmap(BeatmapSetInfo beatmap)
|
||||||
{
|
{
|
||||||
if (silent)
|
var databasedSet = beatmap.OnlineBeatmapSetID != null
|
||||||
return;
|
? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID)
|
||||||
|
: BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmap.Hash);
|
||||||
|
|
||||||
scoreLoad?.Cancel();
|
if (databasedSet == null)
|
||||||
|
|
||||||
if (menuScreen == null)
|
|
||||||
{
|
{
|
||||||
scoreLoad = Schedule(() => LoadScore(score, false));
|
Logger.Log("The requested beatmap could not be loaded.", LoggingTarget.Information);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
performFromMainMenu(() =>
|
||||||
|
{
|
||||||
|
// we might already be at song select, so a check is required before performing the load to solo.
|
||||||
|
if (menuScreen.IsCurrentScreen())
|
||||||
|
menuScreen.LoadToSolo();
|
||||||
|
|
||||||
|
// Use first beatmap available for current ruleset, else switch ruleset.
|
||||||
|
var first = databasedSet.Beatmaps.Find(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First();
|
||||||
|
|
||||||
|
ruleset.Value = first.Ruleset;
|
||||||
|
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first);
|
||||||
|
}, $"load {beatmap}", bypassScreenAllowChecks: true, targetScreen: typeof(PlaySongSelect));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Present a score's replay immediately.
|
||||||
|
/// The user should have already requested this interactively.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beatmap">The beatmap to select.</param>
|
||||||
|
public void PresentScore(ScoreInfo score)
|
||||||
|
{
|
||||||
var databasedScore = ScoreManager.GetScore(score);
|
var databasedScore = ScoreManager.GetScore(score);
|
||||||
var databasedScoreInfo = databasedScore.ScoreInfo;
|
var databasedScoreInfo = databasedScore.ScoreInfo;
|
||||||
if (databasedScore.Replay == null)
|
if (databasedScore.Replay == null)
|
||||||
@ -314,14 +286,40 @@ namespace osu.Game
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((screenStack.CurrentScreen as IOsuScreen)?.AllowExternalScreenChange == false)
|
performFromMainMenu(() =>
|
||||||
|
{
|
||||||
|
ruleset.Value = databasedScoreInfo.Ruleset;
|
||||||
|
|
||||||
|
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
|
||||||
|
Beatmap.Value.Mods.Value = databasedScoreInfo.Mods;
|
||||||
|
|
||||||
|
menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore)));
|
||||||
|
}, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ScheduledDelegate performFromMainMenuTask;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform an action only after returning to the main menu.
|
||||||
|
/// Eagerly tries to exit the current screen until it succeeds.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action">The action to perform once we are in the correct state.</param>
|
||||||
|
/// <param name="taskName">The task name to display in a notification (if we can't immediately reach the main menu state).</param>
|
||||||
|
/// <param name="targetScreen">An optional target screen type. If this screen is already current we can immediately perform the action without returning to the menu.</param>
|
||||||
|
/// <param name="bypassScreenAllowChecks">Whether checking <see cref="IOsuScreen.AllowExternalScreenChange"/> should be bypassed.</param>
|
||||||
|
private void performFromMainMenu(Action action, string taskName, Type targetScreen = null, bool bypassScreenAllowChecks = false)
|
||||||
|
{
|
||||||
|
performFromMainMenuTask?.Cancel();
|
||||||
|
|
||||||
|
// if the current screen does not allow screen changing, give the user an option to try again later.
|
||||||
|
if (!bypassScreenAllowChecks && (screenStack.CurrentScreen as IOsuScreen)?.AllowExternalScreenChange == false)
|
||||||
{
|
{
|
||||||
notifications.Post(new SimpleNotification
|
notifications.Post(new SimpleNotification
|
||||||
{
|
{
|
||||||
Text = $"Click here to watch {databasedScoreInfo.User.Username} on {databasedScoreInfo.Beatmap}",
|
Text = $"Click here to {taskName}",
|
||||||
Activated = () =>
|
Activated = () =>
|
||||||
{
|
{
|
||||||
loadScore();
|
performFromMainMenu(action, taskName, targetScreen, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -329,24 +327,26 @@ namespace osu.Game
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadScore();
|
CloseAllOverlays(false);
|
||||||
|
|
||||||
void loadScore()
|
// we may already be at the target screen type.
|
||||||
|
if (targetScreen != null && screenStack.CurrentScreen?.GetType() == targetScreen)
|
||||||
{
|
{
|
||||||
if (!menuScreen.IsCurrentScreen() || Beatmap.Disabled)
|
action();
|
||||||
{
|
|
||||||
menuScreen.MakeCurrent();
|
|
||||||
this.Delay(500).Schedule(loadScore, out scoreLoad);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ruleset.Value = databasedScoreInfo.Ruleset;
|
// all conditions have been met to continue with the action.
|
||||||
|
if (menuScreen?.IsCurrentScreen() == true && !Beatmap.Disabled)
|
||||||
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
|
{
|
||||||
Beatmap.Value.Mods.Value = databasedScoreInfo.Mods;
|
action();
|
||||||
|
return;
|
||||||
menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// menuScreen may not be initialised yet (null check required).
|
||||||
|
menuScreen?.MakeCurrent();
|
||||||
|
|
||||||
|
performFromMainMenuTask = Schedule(() => performFromMainMenu(action, taskName));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
@ -370,8 +370,10 @@ namespace osu.Game
|
|||||||
|
|
||||||
BeatmapManager.PostNotification = n => notifications?.Post(n);
|
BeatmapManager.PostNotification = n => notifications?.Post(n);
|
||||||
BeatmapManager.GetStableStorage = GetStorageForStableInstall;
|
BeatmapManager.GetStableStorage = GetStorageForStableInstall;
|
||||||
|
BeatmapManager.PresentImport = items => PresentBeatmap(items.First());
|
||||||
|
|
||||||
BeatmapManager.PresentBeatmap = PresentBeatmap;
|
ScoreManager.PostNotification = n => notifications?.Post(n);
|
||||||
|
ScoreManager.PresentImport = items => PresentScore(items.First());
|
||||||
|
|
||||||
Container logoContainer;
|
Container logoContainer;
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Direct
|
namespace osu.Game.Overlays.Direct
|
||||||
{
|
{
|
||||||
@ -23,6 +24,7 @@ namespace osu.Game.Overlays.Direct
|
|||||||
private const float vertical_padding = 5;
|
private const float vertical_padding = 5;
|
||||||
private const float height = 70;
|
private const float height = 70;
|
||||||
|
|
||||||
|
private FillFlowContainer statusContainer;
|
||||||
private PlayButton playButton;
|
private PlayButton playButton;
|
||||||
private Box progressBar;
|
private Box progressBar;
|
||||||
|
|
||||||
@ -107,6 +109,18 @@ namespace osu.Game.Overlays.Direct
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
statusContainer = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Margin = new MarginPadding { Vertical = vertical_padding, Horizontal = 5 },
|
||||||
|
Spacing = new Vector2(5),
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.X,
|
AutoSizeAxes = Axes.X,
|
||||||
Height = 20,
|
Height = 20,
|
||||||
@ -115,6 +129,8 @@ namespace osu.Game.Overlays.Direct
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
@ -194,6 +210,23 @@ namespace osu.Game.Overlays.Direct
|
|||||||
Colour = colours.Yellow,
|
Colour = colours.Yellow,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (SetInfo.OnlineInfo?.HasVideo ?? false)
|
||||||
|
{
|
||||||
|
statusContainer.Add(new IconPill(FontAwesome.fa_film) { IconSize = new Vector2(20) });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SetInfo.OnlineInfo?.HasStoryboard ?? false)
|
||||||
|
{
|
||||||
|
statusContainer.Add(new IconPill(FontAwesome.fa_image) { IconSize = new Vector2(20) });
|
||||||
|
}
|
||||||
|
|
||||||
|
statusContainer.Add(new BeatmapSetOnlineStatusPill
|
||||||
|
{
|
||||||
|
TextSize = 12,
|
||||||
|
TextPadding = new MarginPadding { Horizontal = 10, Vertical = 4 },
|
||||||
|
Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ namespace osu.Game.Overlays.Direct
|
|||||||
|
|
||||||
private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null));
|
private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null));
|
||||||
|
|
||||||
private void setAdded(BeatmapSetInfo s, bool existing, bool silent) => setDownloadStateFromManager(s, DownloadState.LocallyAvailable);
|
private void setAdded(BeatmapSetInfo s, bool existing) => setDownloadStateFromManager(s, DownloadState.LocallyAvailable);
|
||||||
|
|
||||||
private void setRemoved(BeatmapSetInfo s) => setDownloadStateFromManager(s, DownloadState.NotDownloaded);
|
private void setRemoved(BeatmapSetInfo s) => setDownloadStateFromManager(s, DownloadState.NotDownloaded);
|
||||||
|
|
||||||
|
@ -12,6 +12,14 @@ namespace osu.Game.Overlays.Direct
|
|||||||
{
|
{
|
||||||
public class IconPill : CircularContainer
|
public class IconPill : CircularContainer
|
||||||
{
|
{
|
||||||
|
public Vector2 IconSize
|
||||||
|
{
|
||||||
|
get => iconContainer.Size;
|
||||||
|
set => iconContainer.Size = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Container iconContainer;
|
||||||
|
|
||||||
public IconPill(FontAwesome icon)
|
public IconPill(FontAwesome icon)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
@ -25,16 +33,16 @@ namespace osu.Game.Overlays.Direct
|
|||||||
Colour = Color4.Black,
|
Colour = Color4.Black,
|
||||||
Alpha = 0.5f,
|
Alpha = 0.5f,
|
||||||
},
|
},
|
||||||
new Container
|
iconContainer = new Container
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
Size = new Vector2(22),
|
||||||
Margin = new MarginPadding(5),
|
Padding = new MarginPadding(5),
|
||||||
Child = new SpriteIcon
|
Child = new SpriteIcon
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
Icon = icon,
|
Icon = icon,
|
||||||
Size = new Vector2(12),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Music
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(BeatmapManager beatmaps, IBindable<WorkingBeatmap> beatmap)
|
private void load(BeatmapManager beatmaps, IBindable<WorkingBeatmap> beatmap)
|
||||||
{
|
{
|
||||||
beatmaps.GetAllUsableBeatmapSets().ForEach(b => addBeatmapSet(b, false, false));
|
beatmaps.GetAllUsableBeatmapSets().ForEach(b => addBeatmapSet(b, false));
|
||||||
beatmaps.ItemAdded += addBeatmapSet;
|
beatmaps.ItemAdded += addBeatmapSet;
|
||||||
beatmaps.ItemRemoved += removeBeatmapSet;
|
beatmaps.ItemRemoved += removeBeatmapSet;
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ namespace osu.Game.Overlays.Music
|
|||||||
beatmapBacking.ValueChanged += _ => updateSelectedSet();
|
beatmapBacking.ValueChanged += _ => updateSelectedSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addBeatmapSet(BeatmapSetInfo obj, bool existing, bool silent) => Schedule(() =>
|
private void addBeatmapSet(BeatmapSetInfo obj, bool existing) => Schedule(() =>
|
||||||
{
|
{
|
||||||
if (existing)
|
if (existing)
|
||||||
return;
|
return;
|
||||||
|
@ -212,7 +212,7 @@ namespace osu.Game.Overlays
|
|||||||
beatmapSets.Insert(index, beatmapSetInfo);
|
beatmapSets.Insert(index, beatmapSetInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBeatmapAdded(BeatmapSetInfo obj, bool existing, bool silent)
|
private void handleBeatmapAdded(BeatmapSetInfo obj, bool existing)
|
||||||
{
|
{
|
||||||
if (existing)
|
if (existing)
|
||||||
return;
|
return;
|
||||||
|
@ -82,7 +82,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
|||||||
|
|
||||||
private void itemRemoved(SkinInfo s) => Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => i.ID != s.ID).ToArray());
|
private void itemRemoved(SkinInfo s) => Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => i.ID != s.ID).ToArray());
|
||||||
|
|
||||||
private void itemAdded(SkinInfo s, bool existing, bool silent)
|
private void itemAdded(SkinInfo s, bool existing)
|
||||||
{
|
{
|
||||||
if (existing)
|
if (existing)
|
||||||
return;
|
return;
|
||||||
|
@ -301,7 +301,16 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
{
|
{
|
||||||
// Todo: This should return the normal SampleInfos if the specified sample file isn't found, but that's a pretty edge-case scenario
|
// Todo: This should return the normal SampleInfos if the specified sample file isn't found, but that's a pretty edge-case scenario
|
||||||
if (!string.IsNullOrEmpty(bankInfo.Filename))
|
if (!string.IsNullOrEmpty(bankInfo.Filename))
|
||||||
return new List<SampleInfo> { new FileSampleInfo { Filename = bankInfo.Filename } };
|
{
|
||||||
|
return new List<SampleInfo>
|
||||||
|
{
|
||||||
|
new FileSampleInfo
|
||||||
|
{
|
||||||
|
Filename = bankInfo.Filename,
|
||||||
|
Volume = bankInfo.Volume
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
var soundTypes = new List<SampleInfo>
|
var soundTypes = new List<SampleInfo>
|
||||||
{
|
{
|
||||||
|
@ -41,6 +41,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||||
{
|
{
|
||||||
InternalChild = KeyBindingContainer = CreateKeyBindingContainer(ruleset, variant, unique);
|
InternalChild = KeyBindingContainer = CreateKeyBindingContainer(ruleset, variant, unique);
|
||||||
|
gameplayClock = new GameplayClock(framedClock = new FramedClock(manualClock = new ManualClock()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Action mapping (for replays)
|
#region Action mapping (for replays)
|
||||||
@ -86,22 +87,28 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
#region Clock control
|
#region Clock control
|
||||||
|
|
||||||
private ManualClock clock;
|
private readonly ManualClock manualClock;
|
||||||
private IFrameBasedClock parentClock;
|
|
||||||
|
private readonly FramedClock framedClock;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private GameplayClock gameplayClock;
|
||||||
|
|
||||||
|
private IFrameBasedClock parentGameplayClock;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(OsuConfigManager config, GameplayClock clock)
|
||||||
|
{
|
||||||
|
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
|
||||||
|
|
||||||
|
if (clock != null)
|
||||||
|
parentGameplayClock = clock;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
setClock();
|
||||||
//our clock will now be our parent's clock, but we want to replace this to allow manual control.
|
|
||||||
parentClock = Clock;
|
|
||||||
|
|
||||||
ProcessCustomClock = false;
|
|
||||||
Clock = new FramedClock(clock = new ManualClock
|
|
||||||
{
|
|
||||||
CurrentTime = parentClock.CurrentTime,
|
|
||||||
Rate = parentClock.Rate,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -147,25 +154,28 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
private void updateClock()
|
private void updateClock()
|
||||||
{
|
{
|
||||||
if (parentClock == null) return;
|
if (parentGameplayClock == null)
|
||||||
|
setClock(); // LoadComplete may not be run yet, but we still want the clock.
|
||||||
|
|
||||||
clock.Rate = parentClock.Rate;
|
validState = true;
|
||||||
clock.IsRunning = parentClock.IsRunning;
|
|
||||||
|
|
||||||
var newProposedTime = parentClock.CurrentTime;
|
manualClock.Rate = parentGameplayClock.Rate;
|
||||||
|
manualClock.IsRunning = parentGameplayClock.IsRunning;
|
||||||
|
|
||||||
|
var newProposedTime = parentGameplayClock.CurrentTime;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (Math.Abs(clock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f)
|
if (Math.Abs(manualClock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f)
|
||||||
{
|
{
|
||||||
newProposedTime = clock.Rate > 0
|
newProposedTime = manualClock.Rate > 0
|
||||||
? Math.Min(newProposedTime, clock.CurrentTime + sixty_frame_time)
|
? Math.Min(newProposedTime, manualClock.CurrentTime + sixty_frame_time)
|
||||||
: Math.Max(newProposedTime, clock.CurrentTime - sixty_frame_time);
|
: Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isAttached)
|
if (!isAttached)
|
||||||
{
|
{
|
||||||
clock.CurrentTime = newProposedTime;
|
manualClock.CurrentTime = newProposedTime;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -177,35 +187,39 @@ namespace osu.Game.Rulesets.UI
|
|||||||
validState = false;
|
validState = false;
|
||||||
|
|
||||||
requireMoreUpdateLoops = true;
|
requireMoreUpdateLoops = true;
|
||||||
clock.CurrentTime = newProposedTime;
|
manualClock.CurrentTime = newProposedTime;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
clock.CurrentTime = newTime.Value;
|
manualClock.CurrentTime = newTime.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime;
|
requireMoreUpdateLoops = manualClock.CurrentTime != parentGameplayClock.CurrentTime;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
// The manual clock time has changed in the above code. The framed clock now needs to be updated
|
// The manual clock time has changed in the above code. The framed clock now needs to be updated
|
||||||
// to ensure that the its time is valid for our children before input is processed
|
// to ensure that the its time is valid for our children before input is processed
|
||||||
Clock.ProcessFrame();
|
framedClock.ProcessFrame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setClock()
|
||||||
|
{
|
||||||
|
// in case a parent gameplay clock isn't available, just use the parent clock.
|
||||||
|
if (parentGameplayClock == null)
|
||||||
|
parentGameplayClock = Clock;
|
||||||
|
|
||||||
|
Clock = gameplayClock;
|
||||||
|
ProcessCustomClock = false;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Setting application (disables etc.)
|
#region Setting application (disables etc.)
|
||||||
|
|
||||||
private Bindable<bool> mouseDisabled;
|
private Bindable<bool> mouseDisabled;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuConfigManager config)
|
|
||||||
{
|
|
||||||
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool Handle(UIEvent e)
|
protected override bool Handle(UIEvent e)
|
||||||
{
|
{
|
||||||
switch (e)
|
switch (e)
|
||||||
|
@ -178,5 +178,7 @@ namespace osu.Game.Scoring
|
|||||||
{
|
{
|
||||||
public string Acronym { get; set; }
|
public string Acronym { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override string ToString() => $"{User} playing {Beatmap}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Screens
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether a top-level component should be allowed to exit the current screen to, for example,
|
/// Whether a top-level component should be allowed to exit the current screen to, for example,
|
||||||
/// complete an import.
|
/// complete an import. Note that this can be overridden by a user if they specifically request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool AllowExternalScreenChange { get; }
|
bool AllowExternalScreenChange { get; }
|
||||||
|
|
||||||
|
@ -5,14 +5,17 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Online.API;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Menu
|
namespace osu.Game.Screens.Menu
|
||||||
{
|
{
|
||||||
@ -33,13 +36,15 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
private const float icon_y = -85;
|
private const float icon_y = -85;
|
||||||
|
|
||||||
|
private readonly Bindable<User> currentUser = new Bindable<User>();
|
||||||
|
|
||||||
public Disclaimer()
|
public Disclaimer()
|
||||||
{
|
{
|
||||||
ValidForResume = false;
|
ValidForResume = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours, APIAccess api)
|
||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -96,6 +101,13 @@ namespace osu.Game.Screens.Menu
|
|||||||
}).First());
|
}).First());
|
||||||
|
|
||||||
iconColour = colours.Yellow;
|
iconColour = colours.Yellow;
|
||||||
|
|
||||||
|
currentUser.BindTo(api.LocalUser);
|
||||||
|
currentUser.BindValueChanged(e =>
|
||||||
|
{
|
||||||
|
if (e.NewValue.IsSupporter)
|
||||||
|
supporterDrawables.ForEach(d => d.FadeOut(500, Easing.OutQuint).Expire());
|
||||||
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
@ -9,6 +9,13 @@ namespace osu.Game.Screens.Multi.Components
|
|||||||
{
|
{
|
||||||
public class MultiplayerBackgroundSprite : MultiplayerComposite
|
public class MultiplayerBackgroundSprite : MultiplayerComposite
|
||||||
{
|
{
|
||||||
|
private readonly BeatmapSetCoverType beatmapSetCoverType;
|
||||||
|
|
||||||
|
public MultiplayerBackgroundSprite(BeatmapSetCoverType beatmapSetCoverType = BeatmapSetCoverType.Cover)
|
||||||
|
{
|
||||||
|
this.beatmapSetCoverType = beatmapSetCoverType;
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
@ -19,6 +26,6 @@ namespace osu.Game.Screens.Multi.Components
|
|||||||
CurrentItem.BindValueChanged(item => sprite.Beatmap.Value = item.NewValue?.Beatmap, true);
|
CurrentItem.BindValueChanged(item => sprite.Beatmap.Value = item.NewValue?.Beatmap, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both };
|
protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite(beatmapSetCoverType) { RelativeSizeAxes = Axes.Both };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Drawables;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
@ -137,7 +138,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
|
|||||||
Width = cover_width,
|
Width = cover_width,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
Margin = new MarginPadding { Left = side_strip_width },
|
Margin = new MarginPadding { Left = side_strip_width },
|
||||||
Child = new MultiplayerBackgroundSprite { RelativeSizeAxes = Axes.Both }
|
Child = new MultiplayerBackgroundSprite(BeatmapSetCoverType.List) { RelativeSizeAxes = Axes.Both }
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
|
@ -54,7 +54,7 @@ namespace osu.Game.Screens.Multi.Match.Components
|
|||||||
hasBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID) != null;
|
hasBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent)
|
private void beatmapAdded(BeatmapSetInfo model, bool existing)
|
||||||
{
|
{
|
||||||
if (Beatmap.Value == null)
|
if (Beatmap.Value == null)
|
||||||
return;
|
return;
|
||||||
|
@ -202,7 +202,7 @@ namespace osu.Game.Screens.Multi.Match
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle the case where a beatmap is imported (and can be used by this match).
|
/// Handle the case where a beatmap is imported (and can be used by this match).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) => Schedule(() =>
|
private void beatmapAdded(BeatmapSetInfo model, bool existing) => Schedule(() =>
|
||||||
{
|
{
|
||||||
if (Beatmap.Value != beatmapManager.DefaultBeatmap)
|
if (Beatmap.Value != beatmapManager.DefaultBeatmap)
|
||||||
return;
|
return;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
@ -40,13 +41,7 @@ namespace osu.Game.Screens.Play
|
|||||||
private readonly BreakInfo info;
|
private readonly BreakInfo info;
|
||||||
private readonly BreakArrows breakArrows;
|
private readonly BreakArrows breakArrows;
|
||||||
|
|
||||||
public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor)
|
public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor = null)
|
||||||
: this(letterboxing)
|
|
||||||
{
|
|
||||||
bindProcessor(scoreProcessor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BreakOverlay(bool letterboxing)
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
Child = fadeContainer = new Container
|
Child = fadeContainer = new Container
|
||||||
@ -98,6 +93,14 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (scoreProcessor != null) bindProcessor(scoreProcessor);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(GameplayClock clock)
|
||||||
|
{
|
||||||
|
if (clock != null) Clock = clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
43
osu.Game/Screens/Play/GameplayClock.cs
Normal file
43
osu.Game/Screens/Play/GameplayClock.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A clock which is used for gameplay elements that need to follow audio time 1:1.
|
||||||
|
/// Exposed via DI by <see cref="PausableGameplayContainer"/>.
|
||||||
|
/// <remarks>
|
||||||
|
/// The main purpose of this clock is to stop components using it from accidentally processing the main
|
||||||
|
/// <see cref="IFrameBasedClock"/>, as this should only be done once to ensure accuracy.
|
||||||
|
/// </remarks>
|
||||||
|
/// </summary>
|
||||||
|
public class GameplayClock : IFrameBasedClock
|
||||||
|
{
|
||||||
|
private readonly IFrameBasedClock underlyingClock;
|
||||||
|
|
||||||
|
public GameplayClock(IFrameBasedClock underlyingClock)
|
||||||
|
{
|
||||||
|
this.underlyingClock = underlyingClock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double CurrentTime => underlyingClock.CurrentTime;
|
||||||
|
|
||||||
|
public double Rate => underlyingClock.Rate;
|
||||||
|
|
||||||
|
public bool IsRunning => underlyingClock.IsRunning;
|
||||||
|
|
||||||
|
public void ProcessFrame()
|
||||||
|
{
|
||||||
|
// we do not want to process the underlying clock.
|
||||||
|
// this is handled by PauseContainer.
|
||||||
|
}
|
||||||
|
|
||||||
|
public double ElapsedFrameTime => underlyingClock.ElapsedFrameTime;
|
||||||
|
|
||||||
|
public double FramesPerSecond => underlyingClock.FramesPerSecond;
|
||||||
|
|
||||||
|
public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo;
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private static bool hasShownNotificationOnce;
|
private static bool hasShownNotificationOnce;
|
||||||
|
|
||||||
public HUDOverlay(ScoreProcessor scoreProcessor, RulesetContainer rulesetContainer, WorkingBeatmap working, IClock offsetClock, IAdjustableClock adjustableClock)
|
public HUDOverlay(ScoreProcessor scoreProcessor, RulesetContainer rulesetContainer, WorkingBeatmap working, IAdjustableClock adjustableClock)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ namespace osu.Game.Screens.Play
|
|||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
KeyCounter = CreateKeyCounter(adjustableClock as IFrameBasedClock),
|
KeyCounter = CreateKeyCounter(),
|
||||||
HoldToQuit = CreateHoldForMenuButton(),
|
HoldToQuit = CreateHoldForMenuButton(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,9 +91,8 @@ namespace osu.Game.Screens.Play
|
|||||||
BindRulesetContainer(rulesetContainer);
|
BindRulesetContainer(rulesetContainer);
|
||||||
|
|
||||||
Progress.Objects = rulesetContainer.Objects;
|
Progress.Objects = rulesetContainer.Objects;
|
||||||
Progress.AudioClock = offsetClock;
|
|
||||||
Progress.AllowSeeking = rulesetContainer.HasReplayLoaded.Value;
|
Progress.AllowSeeking = rulesetContainer.HasReplayLoaded.Value;
|
||||||
Progress.OnSeek = pos => adjustableClock.Seek(pos);
|
Progress.RequestSeek = pos => adjustableClock.Seek(pos);
|
||||||
|
|
||||||
ModDisplay.Current.BindTo(working.Mods);
|
ModDisplay.Current.BindTo(working.Mods);
|
||||||
|
|
||||||
@ -202,13 +201,12 @@ namespace osu.Game.Screens.Play
|
|||||||
Margin = new MarginPadding { Top = 20 }
|
Margin = new MarginPadding { Top = 20 }
|
||||||
};
|
};
|
||||||
|
|
||||||
protected virtual KeyCounterCollection CreateKeyCounter(IFrameBasedClock offsetClock) => new KeyCounterCollection
|
protected virtual KeyCounterCollection CreateKeyCounter() => new KeyCounterCollection
|
||||||
{
|
{
|
||||||
FadeTime = 50,
|
FadeTime = 50,
|
||||||
Anchor = Anchor.BottomRight,
|
Anchor = Anchor.BottomRight,
|
||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
Margin = new MarginPadding(10),
|
Margin = new MarginPadding(10),
|
||||||
AudioClock = offsetClock
|
|
||||||
};
|
};
|
||||||
|
|
||||||
protected virtual SongProgress CreateProgress() => new SongProgress
|
protected virtual SongProgress CreateProgress() => new SongProgress
|
||||||
|
@ -71,9 +71,12 @@ namespace osu.Game.Screens.Play
|
|||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(TextureStore textures)
|
private void load(TextureStore textures, GameplayClock clock)
|
||||||
{
|
{
|
||||||
|
if (clock != null)
|
||||||
|
Clock = clock;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
buttonSprite = new Sprite
|
buttonSprite = new Sprite
|
||||||
|
@ -8,7 +8,6 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Timing;
|
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -37,9 +36,6 @@ namespace osu.Game.Screens.Play
|
|||||||
key.FadeTime = FadeTime;
|
key.FadeTime = FadeTime;
|
||||||
key.KeyDownTextColor = KeyDownTextColor;
|
key.KeyDownTextColor = KeyDownTextColor;
|
||||||
key.KeyUpTextColor = KeyUpTextColor;
|
key.KeyUpTextColor = KeyUpTextColor;
|
||||||
// Use the same clock object as SongProgress for saving KeyCounter state
|
|
||||||
if (AudioClock != null)
|
|
||||||
key.Clock = AudioClock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetCount()
|
public void ResetCount()
|
||||||
@ -125,8 +121,6 @@ namespace osu.Game.Screens.Play
|
|||||||
public override bool HandleNonPositionalInput => receptor == null;
|
public override bool HandleNonPositionalInput => receptor == null;
|
||||||
public override bool HandlePositionalInput => receptor == null;
|
public override bool HandlePositionalInput => receptor == null;
|
||||||
|
|
||||||
public IFrameBasedClock AudioClock { get; set; }
|
|
||||||
|
|
||||||
private Receptor receptor;
|
private Receptor receptor;
|
||||||
|
|
||||||
public Receptor GetReceptor()
|
public Receptor GetReceptor()
|
||||||
|
@ -14,10 +14,11 @@ using osuTK.Graphics;
|
|||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A container which handles pausing children, displaying a pause overlay with choices etc.
|
/// A container which handles pausing children, displaying a pause overlay with choices and processing the clock.
|
||||||
|
/// Exposes a <see cref="GameplayClock"/> to children via DI.
|
||||||
/// This alleviates a lot of the intricate pause logic from being in <see cref="Player"/>
|
/// This alleviates a lot of the intricate pause logic from being in <see cref="Player"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PauseContainer : Container
|
public class PausableGameplayContainer : Container
|
||||||
{
|
{
|
||||||
public readonly BindableBool IsPaused = new BindableBool();
|
public readonly BindableBool IsPaused = new BindableBool();
|
||||||
|
|
||||||
@ -43,24 +44,32 @@ namespace osu.Game.Screens.Play
|
|||||||
public Action OnRetry;
|
public Action OnRetry;
|
||||||
public Action OnQuit;
|
public Action OnQuit;
|
||||||
|
|
||||||
private readonly FramedClock framedClock;
|
private readonly FramedClock offsetClock;
|
||||||
private readonly DecoupleableInterpolatingFramedClock decoupledClock;
|
private readonly DecoupleableInterpolatingFramedClock adjustableClock;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="PauseContainer"/>.
|
/// The final clock which is exposed to underlying components.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="framedClock">The gameplay clock. This is the clock that will process frames.</param>
|
[Cached]
|
||||||
/// <param name="decoupledClock">The seekable clock. This is the clock that will be paused and resumed.</param>
|
private readonly GameplayClock gameplayClock;
|
||||||
public PauseContainer(FramedClock framedClock, DecoupleableInterpolatingFramedClock decoupledClock)
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="PausableGameplayContainer"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offsetClock">The gameplay clock. This is the clock that will process frames. Includes user/system offsets.</param>
|
||||||
|
/// <param name="adjustableClock">The seekable clock. This is the clock that will be paused and resumed. Should not be processed (it is processed automatically by <see cref="offsetClock"/>).</param>
|
||||||
|
public PausableGameplayContainer(FramedClock offsetClock, DecoupleableInterpolatingFramedClock adjustableClock)
|
||||||
{
|
{
|
||||||
this.framedClock = framedClock;
|
this.offsetClock = offsetClock;
|
||||||
this.decoupledClock = decoupledClock;
|
this.adjustableClock = adjustableClock;
|
||||||
|
|
||||||
|
gameplayClock = new GameplayClock(offsetClock);
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
AddInternal(content = new Container
|
AddInternal(content = new Container
|
||||||
{
|
{
|
||||||
Clock = this.framedClock,
|
Clock = this.offsetClock,
|
||||||
ProcessCustomClock = false,
|
ProcessCustomClock = false,
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
});
|
});
|
||||||
@ -84,7 +93,7 @@ namespace osu.Game.Screens.Play
|
|||||||
if (IsPaused.Value) return;
|
if (IsPaused.Value) return;
|
||||||
|
|
||||||
// stop the seekable clock (stops the audio eventually)
|
// stop the seekable clock (stops the audio eventually)
|
||||||
decoupledClock.Stop();
|
adjustableClock.Stop();
|
||||||
IsPaused.Value = true;
|
IsPaused.Value = true;
|
||||||
|
|
||||||
pauseOverlay.Show();
|
pauseOverlay.Show();
|
||||||
@ -102,8 +111,8 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
// Seeking the decoupled clock to its current time ensures that its source clock will be seeked to the same time
|
// Seeking the decoupled clock to its current time ensures that its source clock will be seeked to the same time
|
||||||
// This accounts for the audio clock source potentially taking time to enter a completely stopped state
|
// This accounts for the audio clock source potentially taking time to enter a completely stopped state
|
||||||
decoupledClock.Seek(decoupledClock.CurrentTime);
|
adjustableClock.Seek(adjustableClock.CurrentTime);
|
||||||
decoupledClock.Start();
|
adjustableClock.Start();
|
||||||
|
|
||||||
pauseOverlay.Hide();
|
pauseOverlay.Hide();
|
||||||
}
|
}
|
||||||
@ -123,7 +132,7 @@ namespace osu.Game.Screens.Play
|
|||||||
Pause();
|
Pause();
|
||||||
|
|
||||||
if (!IsPaused.Value)
|
if (!IsPaused.Value)
|
||||||
framedClock.ProcessFrame();
|
offsetClock.ProcessFrame();
|
||||||
|
|
||||||
base.Update();
|
base.Update();
|
||||||
}
|
}
|
@ -72,7 +72,7 @@ namespace osu.Game.Screens.Play
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private ScoreManager scoreManager { get; set; }
|
private ScoreManager scoreManager { get; set; }
|
||||||
|
|
||||||
protected PauseContainer PauseContainer { get; private set; }
|
protected PausableGameplayContainer PausableGameplayContainer { get; private set; }
|
||||||
|
|
||||||
private RulesetInfo ruleset;
|
private RulesetInfo ruleset;
|
||||||
|
|
||||||
@ -175,10 +175,10 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
PauseContainer = new PauseContainer(offsetClock, adjustableClock)
|
PausableGameplayContainer = new PausableGameplayContainer(offsetClock, adjustableClock)
|
||||||
{
|
{
|
||||||
Retries = RestartCount,
|
Retries = RestartCount,
|
||||||
OnRetry = Restart,
|
OnRetry = restart,
|
||||||
OnQuit = performUserRequestedExit,
|
OnQuit = performUserRequestedExit,
|
||||||
CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded.Value,
|
CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded.Value,
|
||||||
Children = new Container[]
|
Children = new Container[]
|
||||||
@ -196,32 +196,26 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
ProcessCustomClock = false,
|
|
||||||
Breaks = beatmap.Breaks
|
Breaks = beatmap.Breaks
|
||||||
},
|
},
|
||||||
new ScalingContainer(ScalingMode.Gameplay)
|
new ScalingContainer(ScalingMode.Gameplay)
|
||||||
{
|
{
|
||||||
Child = RulesetContainer.Cursor?.CreateProxy() ?? new Container(),
|
Child = RulesetContainer.Cursor?.CreateProxy() ?? new Container(),
|
||||||
},
|
},
|
||||||
HUDOverlay = new HUDOverlay(ScoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
|
HUDOverlay = new HUDOverlay(ScoreProcessor, RulesetContainer, working, adjustableClock)
|
||||||
{
|
{
|
||||||
Clock = Clock, // hud overlay doesn't want to use the audio clock directly
|
|
||||||
ProcessCustomClock = false,
|
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre
|
Origin = Anchor.Centre
|
||||||
},
|
},
|
||||||
new SkipOverlay(RulesetContainer.GameplayStartTime)
|
new SkipOverlay(RulesetContainer.GameplayStartTime)
|
||||||
{
|
{
|
||||||
Clock = Clock, // skip button doesn't want to use the audio clock directly
|
RequestSeek = time => adjustableClock.Seek(time)
|
||||||
ProcessCustomClock = false,
|
|
||||||
AdjustableClock = adjustableClock,
|
|
||||||
FramedClock = offsetClock,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
failOverlay = new FailOverlay
|
failOverlay = new FailOverlay
|
||||||
{
|
{
|
||||||
OnRetry = Restart,
|
OnRetry = restart,
|
||||||
OnQuit = performUserRequestedExit,
|
OnQuit = performUserRequestedExit,
|
||||||
},
|
},
|
||||||
new HotkeyRetryOverlay
|
new HotkeyRetryOverlay
|
||||||
@ -231,7 +225,7 @@ namespace osu.Game.Screens.Play
|
|||||||
if (!this.IsCurrentScreen()) return;
|
if (!this.IsCurrentScreen()) return;
|
||||||
|
|
||||||
fadeOut(true);
|
fadeOut(true);
|
||||||
Restart();
|
restart();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -239,7 +233,7 @@ namespace osu.Game.Screens.Play
|
|||||||
HUDOverlay.HoldToQuit.Action = performUserRequestedExit;
|
HUDOverlay.HoldToQuit.Action = performUserRequestedExit;
|
||||||
HUDOverlay.KeyCounter.Visible.BindTo(RulesetContainer.HasReplayLoaded);
|
HUDOverlay.KeyCounter.Visible.BindTo(RulesetContainer.HasReplayLoaded);
|
||||||
|
|
||||||
RulesetContainer.IsPaused.BindTo(PauseContainer.IsPaused);
|
RulesetContainer.IsPaused.BindTo(PausableGameplayContainer.IsPaused);
|
||||||
|
|
||||||
if (ShowStoryboard.Value)
|
if (ShowStoryboard.Value)
|
||||||
initializeStoryboard(false);
|
initializeStoryboard(false);
|
||||||
@ -268,7 +262,7 @@ namespace osu.Game.Screens.Play
|
|||||||
this.Exit();
|
this.Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Restart()
|
private void restart()
|
||||||
{
|
{
|
||||||
if (!this.IsCurrentScreen()) return;
|
if (!this.IsCurrentScreen()) return;
|
||||||
|
|
||||||
@ -298,7 +292,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
var score = CreateScore();
|
var score = CreateScore();
|
||||||
if (RulesetContainer.ReplayScore == null)
|
if (RulesetContainer.ReplayScore == null)
|
||||||
scoreManager.Import(score, true);
|
scoreManager.Import(score);
|
||||||
|
|
||||||
this.Push(CreateResults(score));
|
this.Push(CreateResults(score));
|
||||||
|
|
||||||
@ -372,7 +366,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
this.Delay(750).Schedule(() =>
|
this.Delay(750).Schedule(() =>
|
||||||
{
|
{
|
||||||
if (!PauseContainer.IsPaused.Value)
|
if (!PausableGameplayContainer.IsPaused.Value)
|
||||||
{
|
{
|
||||||
adjustableClock.Start();
|
adjustableClock.Start();
|
||||||
}
|
}
|
||||||
@ -380,8 +374,8 @@ namespace osu.Game.Screens.Play
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
PauseContainer.Alpha = 0;
|
PausableGameplayContainer.Alpha = 0;
|
||||||
PauseContainer.FadeIn(750, Easing.OutQuint);
|
PausableGameplayContainer.FadeIn(750, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnSuspending(IScreen next)
|
public override void OnSuspending(IScreen next)
|
||||||
@ -399,7 +393,7 @@ namespace osu.Game.Screens.Play
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!AllowPause || HasFailed || !ValidForResume || PauseContainer?.IsPaused.Value != false || RulesetContainer?.HasReplayLoaded.Value != false) && (!PauseContainer?.IsResuming ?? true))
|
if ((!AllowPause || HasFailed || !ValidForResume || PausableGameplayContainer?.IsPaused.Value != false || RulesetContainer?.HasReplayLoaded.Value != false) && (!PausableGameplayContainer?.IsResuming ?? true))
|
||||||
{
|
{
|
||||||
// In the case of replays, we may have changed the playback rate.
|
// In the case of replays, we may have changed the playback rate.
|
||||||
applyRateFromMods();
|
applyRateFromMods();
|
||||||
@ -408,7 +402,7 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (LoadedBeatmapSuccessfully)
|
if (LoadedBeatmapSuccessfully)
|
||||||
PauseContainer?.Pause();
|
PausableGameplayContainer?.Pause();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -422,7 +416,7 @@ namespace osu.Game.Screens.Play
|
|||||||
storyboardReplacesBackground.Value = false;
|
storyboardReplacesBackground.Value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !PauseContainer.IsPaused.Value;
|
protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !PausableGameplayContainer.IsPaused.Value;
|
||||||
|
|
||||||
private void initializeStoryboard(bool asyncLoad)
|
private void initializeStoryboard(bool asyncLoad)
|
||||||
{
|
{
|
||||||
|
@ -17,6 +17,7 @@ using osu.Game.Graphics;
|
|||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Screens.Play.PlayerSettings;
|
using osu.Game.Screens.Play.PlayerSettings;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -296,6 +297,7 @@ namespace osu.Game.Screens.Play
|
|||||||
private readonly WorkingBeatmap beatmap;
|
private readonly WorkingBeatmap beatmap;
|
||||||
private LoadingAnimation loading;
|
private LoadingAnimation loading;
|
||||||
private Sprite backgroundSprite;
|
private Sprite backgroundSprite;
|
||||||
|
private ModDisplay modDisplay;
|
||||||
|
|
||||||
public bool Loading
|
public bool Loading
|
||||||
{
|
{
|
||||||
@ -322,7 +324,7 @@ namespace osu.Game.Screens.Play
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
var metadata = beatmap?.BeatmapInfo?.Metadata ?? new BeatmapMetadata();
|
var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata();
|
||||||
|
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
@ -391,6 +393,14 @@ namespace osu.Game.Screens.Play
|
|||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
},
|
},
|
||||||
|
new ModDisplay
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Margin = new MarginPadding { Top = 20 },
|
||||||
|
Current = beatmap.Mods
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Audio.Sample;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Framework.Timing;
|
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
@ -27,8 +26,7 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
private readonly double startTime;
|
private readonly double startTime;
|
||||||
|
|
||||||
public IAdjustableClock AdjustableClock;
|
public Action<double> RequestSeek;
|
||||||
public IFrameBasedClock FramedClock;
|
|
||||||
|
|
||||||
private Button button;
|
private Button button;
|
||||||
private Box remainingTimeBox;
|
private Box remainingTimeBox;
|
||||||
@ -54,16 +52,13 @@ namespace osu.Game.Screens.Play
|
|||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours, GameplayClock clock)
|
||||||
{
|
{
|
||||||
var baseClock = Clock;
|
var baseClock = Clock;
|
||||||
|
|
||||||
if (FramedClock != null)
|
if (clock != null)
|
||||||
{
|
Clock = clock;
|
||||||
Clock = FramedClock;
|
|
||||||
ProcessCustomClock = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -111,7 +106,7 @@ namespace osu.Game.Screens.Play
|
|||||||
using (BeginAbsoluteSequence(beginFadeTime))
|
using (BeginAbsoluteSequence(beginFadeTime))
|
||||||
this.FadeOut(fade_time);
|
this.FadeOut(fade_time);
|
||||||
|
|
||||||
button.Action = () => AdjustableClock?.Seek(startTime - skip_required_cutoff - fade_time);
|
button.Action = () => RequestSeek?.Invoke(startTime - skip_required_cutoff - fade_time);
|
||||||
|
|
||||||
displayTime = Time.Current;
|
displayTime = Time.Current;
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ using osu.Game.Graphics;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Timing;
|
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
@ -29,18 +28,11 @@ namespace osu.Game.Screens.Play
|
|||||||
private readonly SongProgressGraph graph;
|
private readonly SongProgressGraph graph;
|
||||||
private readonly SongProgressInfo info;
|
private readonly SongProgressInfo info;
|
||||||
|
|
||||||
public Action<double> OnSeek;
|
public Action<double> RequestSeek;
|
||||||
|
|
||||||
public override bool HandleNonPositionalInput => AllowSeeking;
|
public override bool HandleNonPositionalInput => AllowSeeking;
|
||||||
public override bool HandlePositionalInput => AllowSeeking;
|
public override bool HandlePositionalInput => AllowSeeking;
|
||||||
|
|
||||||
private IClock audioClock;
|
|
||||||
|
|
||||||
public IClock AudioClock
|
|
||||||
{
|
|
||||||
set => audioClock = info.AudioClock = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private double lastHitTime => ((objects.Last() as IHasEndTime)?.EndTime ?? objects.Last().StartTime) + 1;
|
private double lastHitTime => ((objects.Last() as IHasEndTime)?.EndTime ?? objects.Last().StartTime) + 1;
|
||||||
|
|
||||||
private double firstHitTime => objects.First().StartTime;
|
private double firstHitTime => objects.First().StartTime;
|
||||||
@ -63,9 +55,14 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private readonly BindableBool replayLoaded = new BindableBool();
|
private readonly BindableBool replayLoaded = new BindableBool();
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
private GameplayClock gameplayClock;
|
||||||
private void load(OsuColour colours)
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(OsuColour colours, GameplayClock clock)
|
||||||
{
|
{
|
||||||
|
if (clock != null)
|
||||||
|
gameplayClock = clock;
|
||||||
|
|
||||||
graph.FillColour = bar.FillColour = colours.BlueLighter;
|
graph.FillColour = bar.FillColour = colours.BlueLighter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,7 +96,7 @@ namespace osu.Game.Screens.Play
|
|||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
OnSeek = position => OnSeek?.Invoke(position),
|
OnSeek = time => RequestSeek?.Invoke(time),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -157,14 +154,11 @@ namespace osu.Game.Screens.Play
|
|||||||
if (objects == null)
|
if (objects == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
double position = audioClock?.CurrentTime ?? Time.Current;
|
double position = gameplayClock?.CurrentTime ?? Time.Current;
|
||||||
double progress = (position - firstHitTime) / (lastHitTime - firstHitTime);
|
double progress = Math.Min(1, (position - firstHitTime) / (lastHitTime - firstHitTime));
|
||||||
|
|
||||||
if (progress < 1)
|
|
||||||
{
|
|
||||||
bar.CurrentTime = position;
|
bar.CurrentTime = position;
|
||||||
graph.Progress = (int)(graph.ColumnCount * progress);
|
graph.Progress = (int)(graph.ColumnCount * progress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Timing;
|
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using System;
|
using System;
|
||||||
@ -27,8 +26,6 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private const int margin = 10;
|
private const int margin = 10;
|
||||||
|
|
||||||
public IClock AudioClock;
|
|
||||||
|
|
||||||
public double StartTime
|
public double StartTime
|
||||||
{
|
{
|
||||||
set => startTime = value;
|
set => startTime = value;
|
||||||
@ -39,9 +36,14 @@ namespace osu.Game.Screens.Play
|
|||||||
set => endTime = value;
|
set => endTime = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
private GameplayClock gameplayClock;
|
||||||
private void load(OsuColour colours)
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(OsuColour colours, GameplayClock clock)
|
||||||
{
|
{
|
||||||
|
if (clock != null)
|
||||||
|
gameplayClock = clock;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
timeCurrent = new OsuSpriteText
|
timeCurrent = new OsuSpriteText
|
||||||
@ -80,7 +82,9 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
double songCurrentTime = AudioClock.CurrentTime - startTime;
|
var time = gameplayClock?.CurrentTime ?? Time.Current;
|
||||||
|
|
||||||
|
double songCurrentTime = time - startTime;
|
||||||
int currentPercent = Math.Max(0, Math.Min(100, (int)(songCurrentTime / songLength * 100)));
|
int currentPercent = Math.Max(0, Math.Min(100, (int)(songCurrentTime / songLength * 100)));
|
||||||
int currentSecond = (int)Math.Floor(songCurrentTime / 1000.0);
|
int currentSecond = (int)Math.Floor(songCurrentTime / 1000.0);
|
||||||
|
|
||||||
@ -93,7 +97,7 @@ namespace osu.Game.Screens.Play
|
|||||||
if (currentSecond != previousSecond && songCurrentTime < songLength)
|
if (currentSecond != previousSecond && songCurrentTime < songLength)
|
||||||
{
|
{
|
||||||
timeCurrent.Text = formatTime(TimeSpan.FromSeconds(currentSecond));
|
timeCurrent.Text = formatTime(TimeSpan.FromSeconds(currentSecond));
|
||||||
timeLeft.Text = formatTime(TimeSpan.FromMilliseconds(endTime - AudioClock.CurrentTime));
|
timeLeft.Text = formatTime(TimeSpan.FromMilliseconds(endTime - time));
|
||||||
|
|
||||||
previousSecond = currentSecond;
|
previousSecond = currentSecond;
|
||||||
}
|
}
|
||||||
|
@ -242,7 +242,11 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
// Reverse drawableRows so when iterating through them they start at the bottom
|
// Reverse drawableRows so when iterating through them they start at the bottom
|
||||||
drawableRows.Reverse();
|
drawableRows.Reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
fillActive();
|
fillActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,7 +582,7 @@ namespace osu.Game.Screens.Select
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onBeatmapSetAdded(BeatmapSetInfo s, bool existing, bool silent) => Carousel.UpdateBeatmapSet(s);
|
private void onBeatmapSetAdded(BeatmapSetInfo s, bool existing) => Carousel.UpdateBeatmapSet(s);
|
||||||
private void onBeatmapSetRemoved(BeatmapSetInfo s) => Carousel.RemoveBeatmapSet(s);
|
private void onBeatmapSetRemoved(BeatmapSetInfo s) => Carousel.RemoveBeatmapSet(s);
|
||||||
private void onBeatmapRestored(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
|
private void onBeatmapRestored(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
|
||||||
private void onBeatmapHidden(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
|
private void onBeatmapHidden(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -21,6 +22,18 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
HitObjects = baseBeatmap.HitObjects;
|
HitObjects = baseBeatmap.HitObjects;
|
||||||
|
|
||||||
BeatmapInfo.Ruleset = ruleset;
|
BeatmapInfo.Ruleset = ruleset;
|
||||||
|
BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata;
|
||||||
|
BeatmapInfo.BeatmapSet.Beatmaps = new List<BeatmapInfo> { BeatmapInfo };
|
||||||
|
BeatmapInfo.BeatmapSet.OnlineInfo = new BeatmapSetOnlineInfo
|
||||||
|
{
|
||||||
|
Status = BeatmapSetOnlineStatus.Ranked,
|
||||||
|
Covers = new BeatmapSetOnlineCovers
|
||||||
|
{
|
||||||
|
Cover = "https://assets.ppy.sh/beatmaps/163112/covers/cover.jpg",
|
||||||
|
Card = "https://assets.ppy.sh/beatmaps/163112/covers/card.jpg",
|
||||||
|
List = "https://assets.ppy.sh/beatmaps/163112/covers/list.jpg"
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Beatmap createTestBeatmap()
|
private static Beatmap createTestBeatmap()
|
||||||
|
@ -230,6 +230,8 @@
|
|||||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTLINE_TYPE_PARAMETER_LIST/@EntryValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTLINE_TYPE_PARAMETER_LIST/@EntryValue">True</s:Boolean>
|
||||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue">NEXT_LINE</s:String>
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue">NEXT_LINE</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INITIALIZER_BRACES/@EntryValue">NEXT_LINE</s:String>
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INITIALIZER_BRACES/@EntryValue">NEXT_LINE</s:String>
|
||||||
|
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
|
||||||
|
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
|
||||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/LINE_FEED_AT_FILE_END/@EntryValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/LINE_FEED_AT_FILE_END/@EntryValue">True</s:Boolean>
|
||||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||||
|
Reference in New Issue
Block a user