mirror of
https://github.com/ppy/osu.git
synced 2025-01-31 13:33:20 +08:00
Merge branch 'master' of git://github.com/ppy/osu into caps-warning
This commit is contained in:
commit
26323caf6f
@ -1 +1 @@
|
|||||||
Subproject commit ba70b8eaa9b79d4248873d4399f3b9e918fc3c8f
|
Subproject commit 3db7e231653ec6ffe28b5dcd1a86230ec754cc1c
|
@ -14,7 +14,7 @@ namespace osu.Desktop.Tests.Visual
|
|||||||
[Test]
|
[Test]
|
||||||
public override void RunTest()
|
public override void RunTest()
|
||||||
{
|
{
|
||||||
using (var host = new HeadlessGameHost())
|
using (var host = new HeadlessGameHost(realtime: false))
|
||||||
host.Run(new OsuTestCaseTestRunner(this));
|
host.Run(new OsuTestCaseTestRunner(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Mania;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
@ -40,8 +41,8 @@ namespace osu.Desktop.Tests.Visual
|
|||||||
RelativeChildSize = new Vector2(1, 10000),
|
RelativeChildSize = new Vector2(1, 10000),
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new DrawableNote(new Note { StartTime = 5000 }) { AccentColour = Color4.Red },
|
new DrawableNote(new Note { StartTime = 5000 }, ManiaAction.Key1) { AccentColour = Color4.Red },
|
||||||
new DrawableNote(new Note { StartTime = 6000 }) { AccentColour = Color4.Red }
|
new DrawableNote(new Note { StartTime = 6000 }, ManiaAction.Key1) { AccentColour = Color4.Red }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,7 +67,7 @@ namespace osu.Desktop.Tests.Visual
|
|||||||
{
|
{
|
||||||
StartTime = 5000,
|
StartTime = 5000,
|
||||||
Duration = 1000
|
Duration = 1000
|
||||||
}) { AccentColour = Color4.Red }
|
}, ManiaAction.Key1) { AccentColour = Color4.Red }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,111 +1,34 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input;
|
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Rulesets.Mania;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Mania.Timing;
|
using osu.Game.Rulesets.Mania.Timing;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Timing;
|
using osu.Game.Rulesets.Timing;
|
||||||
using OpenTK;
|
using osu.Game.Rulesets;
|
||||||
using OpenTK.Input;
|
|
||||||
|
|
||||||
namespace osu.Desktop.Tests.Visual
|
namespace osu.Desktop.Tests.Visual
|
||||||
{
|
{
|
||||||
internal class TestCaseManiaPlayfield : OsuTestCase
|
internal class TestCaseManiaPlayfield : OsuTestCase
|
||||||
{
|
{
|
||||||
|
private const double start_time = 500;
|
||||||
|
private const double duration = 500;
|
||||||
|
|
||||||
public override string Description => @"Mania playfield";
|
public override string Description => @"Mania playfield";
|
||||||
|
|
||||||
protected override double TimePerAction => 200;
|
protected override double TimePerAction => 200;
|
||||||
|
|
||||||
|
private RulesetInfo maniaRuleset;
|
||||||
|
|
||||||
public TestCaseManiaPlayfield()
|
public TestCaseManiaPlayfield()
|
||||||
{
|
{
|
||||||
Action<int, SpecialColumnPosition> createPlayfield = (cols, pos) =>
|
|
||||||
{
|
|
||||||
Clear();
|
|
||||||
Add(new ManiaPlayfield(cols)
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
SpecialColumnPosition = pos,
|
|
||||||
Scale = new Vector2(1, -1)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const double start_time = 500;
|
|
||||||
const double duration = 500;
|
|
||||||
|
|
||||||
Func<double, bool, SpeedAdjustmentContainer> createTimingChange = (time, gravity) => new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(time)
|
|
||||||
{
|
|
||||||
TimingPoint = { BeatLength = 1000 }
|
|
||||||
}, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic);
|
|
||||||
|
|
||||||
Action<bool> createPlayfieldWithNotes = gravity =>
|
|
||||||
{
|
|
||||||
Clear();
|
|
||||||
|
|
||||||
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
|
|
||||||
|
|
||||||
ManiaPlayfield playField;
|
|
||||||
Add(playField = new ManiaPlayfield(4)
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Scale = new Vector2(1, -1),
|
|
||||||
Clock = new FramedClock(rateAdjustClock)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!gravity)
|
|
||||||
playField.Columns.ForEach(c => c.Add(createTimingChange(0, false)));
|
|
||||||
|
|
||||||
for (double t = start_time; t <= start_time + duration; t += 100)
|
|
||||||
{
|
|
||||||
if (gravity)
|
|
||||||
playField.Columns.ElementAt(0).Add(createTimingChange(t, true));
|
|
||||||
|
|
||||||
playField.Add(new DrawableNote(new Note
|
|
||||||
{
|
|
||||||
StartTime = t,
|
|
||||||
Column = 0
|
|
||||||
}, new Bindable<Key>(Key.D)));
|
|
||||||
|
|
||||||
if (gravity)
|
|
||||||
playField.Columns.ElementAt(3).Add(createTimingChange(t, true));
|
|
||||||
|
|
||||||
playField.Add(new DrawableNote(new Note
|
|
||||||
{
|
|
||||||
StartTime = t,
|
|
||||||
Column = 3
|
|
||||||
}, new Bindable<Key>(Key.K)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gravity)
|
|
||||||
playField.Columns.ElementAt(1).Add(createTimingChange(start_time, true));
|
|
||||||
|
|
||||||
playField.Add(new DrawableHoldNote(new HoldNote
|
|
||||||
{
|
|
||||||
StartTime = start_time,
|
|
||||||
Duration = duration,
|
|
||||||
Column = 1
|
|
||||||
}, new Bindable<Key>(Key.F)));
|
|
||||||
|
|
||||||
if (gravity)
|
|
||||||
playField.Columns.ElementAt(2).Add(createTimingChange(start_time, true));
|
|
||||||
|
|
||||||
playField.Add(new DrawableHoldNote(new HoldNote
|
|
||||||
{
|
|
||||||
StartTime = start_time,
|
|
||||||
Duration = duration,
|
|
||||||
Column = 2
|
|
||||||
}, new Bindable<Key>(Key.J)));
|
|
||||||
};
|
|
||||||
|
|
||||||
AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal));
|
AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal));
|
||||||
AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal));
|
AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal));
|
||||||
AddStep("Left special style", () => createPlayfield(4, SpecialColumnPosition.Left));
|
AddStep("Left special style", () => createPlayfield(4, SpecialColumnPosition.Left));
|
||||||
@ -114,29 +37,105 @@ namespace osu.Desktop.Tests.Visual
|
|||||||
AddStep("8 columns", () => createPlayfield(8, SpecialColumnPosition.Normal));
|
AddStep("8 columns", () => createPlayfield(8, SpecialColumnPosition.Normal));
|
||||||
AddStep("Left special style", () => createPlayfield(8, SpecialColumnPosition.Left));
|
AddStep("Left special style", () => createPlayfield(8, SpecialColumnPosition.Left));
|
||||||
AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right));
|
AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right));
|
||||||
|
AddStep("Reversed", () => createPlayfield(4, SpecialColumnPosition.Normal, true));
|
||||||
|
|
||||||
AddStep("Notes with input", () => createPlayfieldWithNotes(false));
|
AddStep("Notes with input", () => createPlayfieldWithNotes(false));
|
||||||
AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction));
|
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(false, true));
|
||||||
|
|
||||||
AddStep("Notes with gravity", () => createPlayfieldWithNotes(true));
|
AddStep("Notes with gravity", () => createPlayfieldWithNotes(true));
|
||||||
AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction));
|
AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void triggerKeyDown(Column column)
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(RulesetStore rulesets)
|
||||||
{
|
{
|
||||||
column.TriggerOnKeyDown(new InputState(), new KeyDownEventArgs
|
maniaRuleset = rulesets.GetRuleset(3);
|
||||||
{
|
|
||||||
Key = column.Key,
|
|
||||||
Repeat = false
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void triggerKeyUp(Column column)
|
private SpeedAdjustmentContainer createTimingChange(double time, bool gravity) => new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(time)
|
||||||
{
|
{
|
||||||
column.TriggerOnKeyUp(new InputState(), new KeyUpEventArgs
|
TimingPoint = { BeatLength = 1000 }
|
||||||
|
}, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic);
|
||||||
|
|
||||||
|
private void createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false)
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
var inputManager = new ManiaInputManager(maniaRuleset, cols) { RelativeSizeAxes = Axes.Both };
|
||||||
|
Add(inputManager);
|
||||||
|
|
||||||
|
ManiaPlayfield playfield;
|
||||||
|
inputManager.Add(playfield = new ManiaPlayfield(cols)
|
||||||
{
|
{
|
||||||
Key = column.Key
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
SpecialColumnPosition = specialPos
|
||||||
});
|
});
|
||||||
|
|
||||||
|
playfield.Inverted.Value = inverted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createPlayfieldWithNotes(bool gravity, bool inverted = false)
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
|
||||||
|
|
||||||
|
var inputManager = new ManiaInputManager(maniaRuleset, 4) { RelativeSizeAxes = Axes.Both };
|
||||||
|
Add(inputManager);
|
||||||
|
|
||||||
|
ManiaPlayfield playfield;
|
||||||
|
inputManager.Add(playfield = new ManiaPlayfield(4)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Clock = new FramedClock(rateAdjustClock)
|
||||||
|
});
|
||||||
|
|
||||||
|
playfield.Inverted.Value = inverted;
|
||||||
|
|
||||||
|
if (!gravity)
|
||||||
|
playfield.Columns.ForEach(c => c.Add(createTimingChange(0, false)));
|
||||||
|
|
||||||
|
for (double t = start_time; t <= start_time + duration; t += 100)
|
||||||
|
{
|
||||||
|
if (gravity)
|
||||||
|
playfield.Columns.ElementAt(0).Add(createTimingChange(t, true));
|
||||||
|
|
||||||
|
playfield.Add(new DrawableNote(new Note
|
||||||
|
{
|
||||||
|
StartTime = t,
|
||||||
|
Column = 0
|
||||||
|
}, ManiaAction.Key1));
|
||||||
|
|
||||||
|
if (gravity)
|
||||||
|
playfield.Columns.ElementAt(3).Add(createTimingChange(t, true));
|
||||||
|
|
||||||
|
playfield.Add(new DrawableNote(new Note
|
||||||
|
{
|
||||||
|
StartTime = t,
|
||||||
|
Column = 3
|
||||||
|
}, ManiaAction.Key4));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gravity)
|
||||||
|
playfield.Columns.ElementAt(1).Add(createTimingChange(start_time, true));
|
||||||
|
|
||||||
|
playfield.Add(new DrawableHoldNote(new HoldNote
|
||||||
|
{
|
||||||
|
StartTime = start_time,
|
||||||
|
Duration = duration,
|
||||||
|
Column = 1
|
||||||
|
}, ManiaAction.Key2));
|
||||||
|
|
||||||
|
if (gravity)
|
||||||
|
playfield.Columns.ElementAt(2).Add(createTimingChange(start_time, true));
|
||||||
|
|
||||||
|
playfield.Add(new DrawableHoldNote(new HoldNote
|
||||||
|
{
|
||||||
|
StartTime = start_time,
|
||||||
|
Duration = duration,
|
||||||
|
Column = 2
|
||||||
|
}, ManiaAction.Key3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,8 +64,8 @@ namespace osu.Desktop.Tests.Visual
|
|||||||
|
|
||||||
AddStep("Reverse direction", () =>
|
AddStep("Reverse direction", () =>
|
||||||
{
|
{
|
||||||
horizontalRulesetContainer.Playfield.Reversed.Toggle();
|
horizontalRulesetContainer.Playfield.Reverse();
|
||||||
verticalRulesetContainer.Playfield.Reversed.Toggle();
|
verticalRulesetContainer.Playfield.Reverse();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,18 +115,18 @@ namespace osu.Desktop.Tests.Visual
|
|||||||
Assert.AreEqual(1, speedAdjustments[3].ControlPoint.Multiplier);
|
Assert.AreEqual(1, speedAdjustments[3].ControlPoint.Multiplier);
|
||||||
|
|
||||||
// Check insertion of hit objects
|
// Check insertion of hit objects
|
||||||
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[0]));
|
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[4].Contains(hitObjects[0]));
|
||||||
Assert.IsTrue(speedAdjustments[0].Contains(hitObjects[1]));
|
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1]));
|
||||||
Assert.IsTrue(speedAdjustments[1].Contains(hitObjects[2]));
|
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[2].Contains(hitObjects[2]));
|
||||||
Assert.IsTrue(speedAdjustments[2].Contains(hitObjects[3]));
|
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[1].Contains(hitObjects[3]));
|
||||||
Assert.IsTrue(speedAdjustments[3].Contains(hitObjects[4]));
|
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[4]));
|
||||||
Assert.IsTrue(speedAdjustments[3].Contains(hitObjects[5]));
|
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[5]));
|
||||||
|
|
||||||
hitObjectContainer.RemoveSpeedAdjustment(speedAdjustments[1]);
|
hitObjectContainer.RemoveSpeedAdjustment(hitObjectContainer.SpeedAdjustments[3]);
|
||||||
|
|
||||||
// The hit object contained in this speed adjustment should be resorted into the previous one
|
// The hit object contained in this speed adjustment should be resorted into the one occuring before it
|
||||||
|
|
||||||
Assert.IsTrue(speedAdjustments[0].Contains(hitObjects[2]));
|
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestRulesetContainer : ScrollingRulesetContainer<TestPlayfield, TestHitObject, TestJudgement>
|
private class TestRulesetContainer : ScrollingRulesetContainer<TestPlayfield, TestHitObject, TestJudgement>
|
||||||
@ -210,6 +210,8 @@ namespace osu.Desktop.Tests.Visual
|
|||||||
content = new Container { RelativeSizeAxes = Axes.Both }
|
content = new Container { RelativeSizeAxes = Axes.Both }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Reverse() => Reversed.Toggle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,16 +20,11 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
internal class OsuGameDesktop : OsuGame
|
internal class OsuGameDesktop : OsuGame
|
||||||
{
|
{
|
||||||
private readonly VersionManager versionManager;
|
private VersionManager versionManager;
|
||||||
|
|
||||||
public OsuGameDesktop(string[] args = null)
|
public OsuGameDesktop(string[] args = null)
|
||||||
: base(args)
|
: base(args)
|
||||||
{
|
{
|
||||||
versionManager = new VersionManager
|
|
||||||
{
|
|
||||||
Depth = int.MinValue,
|
|
||||||
State = Visibility.Hidden
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Storage GetStorageForStableInstall()
|
public override Storage GetStorageForStableInstall()
|
||||||
@ -88,11 +83,15 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
LoadComponentAsync(versionManager, Add);
|
LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue });
|
||||||
|
|
||||||
ScreenChanged += s =>
|
ScreenChanged += s =>
|
||||||
{
|
{
|
||||||
if (!versionManager.IsPresent && s is Intro)
|
if (s is Intro && s.ChildScreen == null)
|
||||||
|
{
|
||||||
|
Add(versionManager);
|
||||||
versionManager.State = Visibility.Visible;
|
versionManager.State = Visibility.Visible;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
@ -19,6 +20,7 @@ using OpenTK.Graphics;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
|
||||||
namespace osu.Desktop.Overlays
|
namespace osu.Desktop.Overlays
|
||||||
{
|
{
|
||||||
@ -26,17 +28,22 @@ namespace osu.Desktop.Overlays
|
|||||||
{
|
{
|
||||||
private UpdateManager updateManager;
|
private UpdateManager updateManager;
|
||||||
private NotificationOverlay notificationOverlay;
|
private NotificationOverlay notificationOverlay;
|
||||||
|
private OsuConfigManager config;
|
||||||
|
private OsuGameBase game;
|
||||||
|
|
||||||
public override bool HandleInput => false;
|
public override bool HandleInput => false;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game)
|
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config)
|
||||||
{
|
{
|
||||||
notificationOverlay = notification;
|
notificationOverlay = notification;
|
||||||
|
this.config = config;
|
||||||
|
this.game = game;
|
||||||
|
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
Anchor = Anchor.BottomCentre;
|
Anchor = Anchor.BottomCentre;
|
||||||
Origin = Anchor.BottomCentre;
|
Origin = Anchor.BottomCentre;
|
||||||
|
|
||||||
Alpha = 0;
|
Alpha = 0;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
@ -91,6 +98,42 @@ namespace osu.Desktop.Overlays
|
|||||||
checkForUpdateAsync();
|
checkForUpdateAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
var version = game.Version;
|
||||||
|
var lastVersion = config.Get<string>(OsuSetting.Version);
|
||||||
|
if (game.IsDeployedBuild && version != lastVersion)
|
||||||
|
{
|
||||||
|
config.Set(OsuSetting.Version, version);
|
||||||
|
|
||||||
|
// only show a notification if we've previously saved a version to the config file (ie. not the first run).
|
||||||
|
if (!string.IsNullOrEmpty(lastVersion))
|
||||||
|
Scheduler.AddDelayed(() => notificationOverlay.Post(new UpdateCompleteNotification(version)), 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UpdateCompleteNotification : SimpleNotification
|
||||||
|
{
|
||||||
|
public UpdateCompleteNotification(string version)
|
||||||
|
{
|
||||||
|
Text = $"You are now running osu!lazer {version}.\nClick to see what's new!";
|
||||||
|
Icon = FontAwesome.fa_check_square;
|
||||||
|
Activated = delegate
|
||||||
|
{
|
||||||
|
Process.Start($"https://github.com/ppy/osu/releases/tag/v{version}");
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
IconBackgound.Colour = colours.BlueDark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
@ -8,14 +9,33 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
{
|
{
|
||||||
public class ManiaInputManager : RulesetInputManager<ManiaAction>
|
public class ManiaInputManager : RulesetInputManager<ManiaAction>
|
||||||
{
|
{
|
||||||
public ManiaInputManager(RulesetInfo ruleset)
|
public ManiaInputManager(RulesetInfo ruleset, int variant)
|
||||||
: base(ruleset, 0, SimultaneousBindingMode.Unique)
|
: base(ruleset, variant, SimultaneousBindingMode.Unique)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ManiaAction
|
public enum ManiaAction
|
||||||
{
|
{
|
||||||
// placeholder
|
[Description("Special")]
|
||||||
|
Special,
|
||||||
|
[Description("Key 1")]
|
||||||
|
Key1 = 10,
|
||||||
|
[Description("Key 2")]
|
||||||
|
Key2,
|
||||||
|
[Description("Key 3")]
|
||||||
|
Key3,
|
||||||
|
[Description("Key 4")]
|
||||||
|
Key4,
|
||||||
|
[Description("Key 5")]
|
||||||
|
Key5,
|
||||||
|
[Description("Key 6")]
|
||||||
|
Key6,
|
||||||
|
[Description("Key 7")]
|
||||||
|
Key7,
|
||||||
|
[Description("Key 8")]
|
||||||
|
Key8,
|
||||||
|
[Description("Key 9")]
|
||||||
|
Key9
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Mania.Scoring;
|
using osu.Game.Rulesets.Mania.Scoring;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
@ -120,5 +121,43 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
: base(rulesetInfo)
|
: base(rulesetInfo)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<int> AvailableVariants => new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||||
|
|
||||||
|
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0)
|
||||||
|
{
|
||||||
|
var leftKeys = new[]
|
||||||
|
{
|
||||||
|
InputKey.A,
|
||||||
|
InputKey.S,
|
||||||
|
InputKey.D,
|
||||||
|
InputKey.F
|
||||||
|
};
|
||||||
|
|
||||||
|
var rightKeys = new[]
|
||||||
|
{
|
||||||
|
InputKey.J,
|
||||||
|
InputKey.K,
|
||||||
|
InputKey.L,
|
||||||
|
InputKey.Semicolon
|
||||||
|
};
|
||||||
|
|
||||||
|
ManiaAction currentKey = ManiaAction.Key1;
|
||||||
|
|
||||||
|
var bindings = new List<KeyBinding>();
|
||||||
|
|
||||||
|
for (int i = leftKeys.Length - variant / 2; i < leftKeys.Length; i++)
|
||||||
|
bindings.Add(new KeyBinding(leftKeys[i], currentKey++));
|
||||||
|
|
||||||
|
for (int i = 0; i < variant / 2; i++)
|
||||||
|
bindings.Add(new KeyBinding(rightKeys[i], currentKey++));
|
||||||
|
|
||||||
|
if (variant % 2 == 1)
|
||||||
|
bindings.Add(new KeyBinding(InputKey.Space, ManiaAction.Special));
|
||||||
|
|
||||||
|
return bindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetVariantName(int variant) => $"{variant}K";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,20 +5,18 @@ using osu.Game.Rulesets.Objects.Drawables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Configuration;
|
|
||||||
using OpenTK.Input;
|
|
||||||
using osu.Framework.Input;
|
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Mania.Judgements;
|
using osu.Game.Rulesets.Mania.Judgements;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Visualises a <see cref="HoldNote"/> hit object.
|
/// Visualises a <see cref="HoldNote"/> hit object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DrawableHoldNote : DrawableManiaHitObject<HoldNote>
|
public class DrawableHoldNote : DrawableManiaHitObject<HoldNote>, IKeyBindingHandler<ManiaAction>
|
||||||
{
|
{
|
||||||
private readonly DrawableNote head;
|
private readonly DrawableNote head;
|
||||||
private readonly DrawableNote tail;
|
private readonly DrawableNote tail;
|
||||||
@ -36,8 +34,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private bool hasBroken;
|
private bool hasBroken;
|
||||||
|
|
||||||
public DrawableHoldNote(HoldNote hitObject, Bindable<Key> key = null)
|
public DrawableHoldNote(HoldNote hitObject, ManiaAction action)
|
||||||
: base(hitObject, key)
|
: base(hitObject, action)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
Height = (float)HitObject.Duration;
|
Height = (float)HitObject.Duration;
|
||||||
@ -58,12 +56,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
RelativeChildOffset = new Vector2(0, (float)HitObject.StartTime),
|
RelativeChildOffset = new Vector2(0, (float)HitObject.StartTime),
|
||||||
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
|
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
|
||||||
},
|
},
|
||||||
head = new DrawableHeadNote(this, key)
|
head = new DrawableHeadNote(this, action)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre
|
Origin = Anchor.TopCentre
|
||||||
},
|
},
|
||||||
tail = new DrawableTailNote(this, key)
|
tail = new DrawableTailNote(this, action)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomCentre,
|
Anchor = Anchor.BottomCentre,
|
||||||
Origin = Anchor.TopCentre
|
Origin = Anchor.TopCentre
|
||||||
@ -106,16 +104,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
public bool OnPressed(ManiaAction action)
|
||||||
{
|
{
|
||||||
// Make sure the keypress happened within the body of the hold note
|
// Make sure the action happened within the body of the hold note
|
||||||
if (Time.Current < HitObject.StartTime || Time.Current > HitObject.EndTime)
|
if (Time.Current < HitObject.StartTime || Time.Current > HitObject.EndTime)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (args.Key != Key)
|
if (action != Action)
|
||||||
return false;
|
|
||||||
|
|
||||||
if (args.Repeat)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// The user has pressed during the body of the hold note, after the head note and its hit windows have passed
|
// The user has pressed during the body of the hold note, after the head note and its hit windows have passed
|
||||||
@ -126,13 +121,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
|
public bool OnReleased(ManiaAction action)
|
||||||
{
|
{
|
||||||
// Make sure that the user started holding the key during the hold note
|
// Make sure that the user started holding the key during the hold note
|
||||||
if (!holdStartTime.HasValue)
|
if (!holdStartTime.HasValue)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (args.Key != Key)
|
if (action != Action)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
holdStartTime = null;
|
holdStartTime = null;
|
||||||
@ -151,8 +146,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
{
|
{
|
||||||
private readonly DrawableHoldNote holdNote;
|
private readonly DrawableHoldNote holdNote;
|
||||||
|
|
||||||
public DrawableHeadNote(DrawableHoldNote holdNote, Bindable<Key> key = null)
|
public DrawableHeadNote(DrawableHoldNote holdNote, ManiaAction action)
|
||||||
: base(holdNote.HitObject.Head, key)
|
: base(holdNote.HitObject.Head, action)
|
||||||
{
|
{
|
||||||
this.holdNote = holdNote;
|
this.holdNote = holdNote;
|
||||||
|
|
||||||
@ -160,9 +155,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
Y = 0;
|
Y = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
public override bool OnPressed(ManiaAction action)
|
||||||
{
|
{
|
||||||
if (!base.OnKeyDown(state, args))
|
if (!base.OnPressed(action))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// We only want to trigger a holding state from the head if the head has received a judgement
|
// We only want to trigger a holding state from the head if the head has received a judgement
|
||||||
@ -188,8 +183,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
{
|
{
|
||||||
private readonly DrawableHoldNote holdNote;
|
private readonly DrawableHoldNote holdNote;
|
||||||
|
|
||||||
public DrawableTailNote(DrawableHoldNote holdNote, Bindable<Key> key = null)
|
public DrawableTailNote(DrawableHoldNote holdNote, ManiaAction action)
|
||||||
: base(holdNote.HitObject.Tail, key)
|
: base(holdNote.HitObject.Tail, action)
|
||||||
{
|
{
|
||||||
this.holdNote = holdNote;
|
this.holdNote = holdNote;
|
||||||
|
|
||||||
@ -210,7 +205,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
tailJudgement.HasBroken = holdNote.hasBroken;
|
tailJudgement.HasBroken = holdNote.hasBroken;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
|
public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down
|
||||||
|
|
||||||
|
public override bool OnReleased(ManiaAction action)
|
||||||
{
|
{
|
||||||
// Make sure that the user started holding the key during the hold note
|
// Make sure that the user started holding the key during the hold note
|
||||||
if (!holdNote.holdStartTime.HasValue)
|
if (!holdNote.holdStartTime.HasValue)
|
||||||
@ -219,7 +216,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
if (Judgement.Result != HitResult.None)
|
if (Judgement.Result != HitResult.None)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (args.Key != Key)
|
if (action != Action)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
UpdateJudgement(true);
|
UpdateJudgement(true);
|
||||||
@ -227,12 +224,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
// Handled by the hold note, which will set holding = false
|
// Handled by the hold note, which will set holding = false
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
|
||||||
{
|
|
||||||
// Tail doesn't handle key down
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using OpenTK.Input;
|
|
||||||
using osu.Framework.Configuration;
|
|
||||||
using osu.Game.Rulesets.Mania.Judgements;
|
using osu.Game.Rulesets.Mania.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
@ -15,17 +13,17 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The key that will trigger input for this hit object.
|
/// The key that will trigger input for this hit object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected Bindable<Key> Key { get; private set; } = new Bindable<Key>();
|
protected ManiaAction Action { get; }
|
||||||
|
|
||||||
public new TObject HitObject;
|
public new TObject HitObject;
|
||||||
|
|
||||||
protected DrawableManiaHitObject(TObject hitObject, Bindable<Key> key = null)
|
protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
HitObject = hitObject;
|
HitObject = hitObject;
|
||||||
|
|
||||||
if (key != null)
|
if (action != null)
|
||||||
Key.BindTo(key);
|
Action = action.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Color4 AccentColour
|
public override Color4 AccentColour
|
||||||
|
@ -3,10 +3,8 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using OpenTK.Input;
|
|
||||||
using osu.Framework.Configuration;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Rulesets.Mania.Judgements;
|
using osu.Game.Rulesets.Mania.Judgements;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -16,12 +14,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Visualises a <see cref="Note"/> hit object.
|
/// Visualises a <see cref="Note"/> hit object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DrawableNote : DrawableManiaHitObject<Note>
|
public class DrawableNote : DrawableManiaHitObject<Note>, IKeyBindingHandler<ManiaAction>
|
||||||
{
|
{
|
||||||
private readonly NotePiece headPiece;
|
private readonly NotePiece headPiece;
|
||||||
|
|
||||||
public DrawableNote(Note hitObject, Bindable<Key> key = null)
|
public DrawableNote(Note hitObject, ManiaAction action)
|
||||||
: base(hitObject, key)
|
: base(hitObject, action)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = 100;
|
Height = 100;
|
||||||
@ -81,18 +79,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
public virtual bool OnPressed(ManiaAction action)
|
||||||
{
|
{
|
||||||
if (Judgement.Result != HitResult.None)
|
if (action != Action)
|
||||||
return false;
|
|
||||||
|
|
||||||
if (args.Key != Key)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (args.Repeat)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return UpdateJudgement(true);
|
return UpdateJudgement(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual bool OnReleased(ManiaAction action) => false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,17 +3,15 @@
|
|||||||
|
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using OpenTK.Input;
|
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
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;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Input;
|
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Judgements;
|
using osu.Game.Rulesets.Mania.Judgements;
|
||||||
@ -32,10 +30,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
private const float column_width = 45;
|
private const float column_width = 45;
|
||||||
private const float special_column_width = 70;
|
private const float special_column_width = 70;
|
||||||
|
|
||||||
/// <summary>
|
public ManiaAction Action;
|
||||||
/// The key that will trigger input actions for this column and hit objects contained inside it.
|
|
||||||
/// </summary>
|
|
||||||
public Bindable<Key> Key = new Bindable<Key>();
|
|
||||||
|
|
||||||
private readonly Box background;
|
private readonly Box background;
|
||||||
private readonly Container hitTargetBar;
|
private readonly Container hitTargetBar;
|
||||||
@ -101,8 +96,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
// For column lighting, we need to capture input events before the notes
|
// For column lighting, we need to capture input events before the notes
|
||||||
new InputTarget
|
new InputTarget
|
||||||
{
|
{
|
||||||
KeyDown = onKeyDown,
|
Pressed = onPressed,
|
||||||
KeyUp = onKeyUp
|
Released = onReleased
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -199,12 +194,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
HitObjects.Add(hitObject);
|
HitObjects.Add(hitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool onKeyDown(InputState state, KeyDownEventArgs args)
|
private bool onPressed(ManiaAction action)
|
||||||
{
|
{
|
||||||
if (args.Repeat)
|
if (action == Action)
|
||||||
return false;
|
|
||||||
|
|
||||||
if (args.Key == Key)
|
|
||||||
{
|
{
|
||||||
background.FadeTo(background.Alpha + 0.2f, 50, Easing.OutQuint);
|
background.FadeTo(background.Alpha + 0.2f, 50, Easing.OutQuint);
|
||||||
keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint);
|
keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint);
|
||||||
@ -213,9 +205,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool onKeyUp(InputState state, KeyUpEventArgs args)
|
private bool onReleased(ManiaAction action)
|
||||||
{
|
{
|
||||||
if (args.Key == Key)
|
if (action == Action)
|
||||||
{
|
{
|
||||||
background.FadeTo(0.2f, 800, Easing.OutQuart);
|
background.FadeTo(0.2f, 800, Easing.OutQuart);
|
||||||
keyIcon.ScaleTo(1f, 400, Easing.OutQuart);
|
keyIcon.ScaleTo(1f, 400, Easing.OutQuart);
|
||||||
@ -227,10 +219,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is a simple container which delegates various input events that have to be captured before the notes.
|
/// This is a simple container which delegates various input events that have to be captured before the notes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private class InputTarget : Container
|
private class InputTarget : Container, IKeyBindingHandler<ManiaAction>
|
||||||
{
|
{
|
||||||
public Func<InputState, KeyDownEventArgs, bool> KeyDown;
|
public Func<ManiaAction, bool> Pressed;
|
||||||
public Func<InputState, KeyUpEventArgs, bool> KeyUp;
|
public Func<ManiaAction, bool> Released;
|
||||||
|
|
||||||
public InputTarget()
|
public InputTarget()
|
||||||
{
|
{
|
||||||
@ -239,8 +231,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
Alpha = 0;
|
Alpha = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => KeyDown?.Invoke(state, args) ?? false;
|
public bool OnPressed(ManiaAction action) => Pressed?.Invoke(action) ?? false;
|
||||||
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => KeyUp?.Invoke(state, args) ?? false;
|
public bool OnReleased(ManiaAction action) => Released?.Invoke(action) ?? false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ using osu.Framework.Allocation;
|
|||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
@ -45,6 +46,11 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this playfield should be inverted. This flips everything inside the playfield.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Bindable<bool> Inverted = new Bindable<bool>(true);
|
||||||
|
|
||||||
private readonly FlowContainer<Column> columns;
|
private readonly FlowContainer<Column> columns;
|
||||||
public IEnumerable<Column> Columns => columns.Children;
|
public IEnumerable<Column> Columns => columns.Children;
|
||||||
|
|
||||||
@ -64,6 +70,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
if (columnCount <= 0)
|
if (columnCount <= 0)
|
||||||
throw new ArgumentException("Can't have zero or fewer columns.");
|
throw new ArgumentException("Can't have zero or fewer columns.");
|
||||||
|
|
||||||
|
Inverted.Value = true;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
@ -122,15 +130,26 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var currentAction = ManiaAction.Key1;
|
||||||
for (int i = 0; i < columnCount; i++)
|
for (int i = 0; i < columnCount; i++)
|
||||||
{
|
{
|
||||||
var c = new Column();
|
var c = new Column();
|
||||||
c.Reversed.BindTo(Reversed);
|
|
||||||
c.VisibleTimeRange.BindTo(VisibleTimeRange);
|
c.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||||
|
|
||||||
|
c.IsSpecial = isSpecialColumn(i);
|
||||||
|
c.Action = c.IsSpecial ? ManiaAction.Special : currentAction++;
|
||||||
|
|
||||||
columns.Add(c);
|
columns.Add(c);
|
||||||
AddNested(c);
|
AddNested(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Inverted.ValueChanged += invertedChanged;
|
||||||
|
Inverted.TriggerChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invertedChanged(bool newValue)
|
||||||
|
{
|
||||||
|
Scale = new Vector2(1, newValue ? -1 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -145,15 +164,11 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
specialColumnColour = colours.BlueDark;
|
specialColumnColour = colours.BlueDark;
|
||||||
|
|
||||||
// Set the special column + colour + key
|
// Set the special column + colour + key
|
||||||
for (int i = 0; i < columnCount; i++)
|
foreach (var column in Columns)
|
||||||
{
|
{
|
||||||
Column column = Columns.ElementAt(i);
|
|
||||||
column.IsSpecial = isSpecialColumn(i);
|
|
||||||
|
|
||||||
if (!column.IsSpecial)
|
if (!column.IsSpecial)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
column.Key.Value = Key.Space;
|
|
||||||
column.AccentColour = specialColumnColour;
|
column.AccentColour = specialColumnColour;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,21 +182,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
nonSpecialColumns[i].AccentColour = colour;
|
nonSpecialColumns[i].AccentColour = colour;
|
||||||
nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
|
nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll set the keys for non-special columns in another separate loop because it's not mirrored like the above colours
|
|
||||||
// Todo: This needs to go when we get to bindings and use Button1, ..., ButtonN instead
|
|
||||||
for (int i = 0; i < nonSpecialColumns.Count; i++)
|
|
||||||
{
|
|
||||||
Column column = nonSpecialColumns[i];
|
|
||||||
|
|
||||||
int keyOffset = default_keys.Length / 2 - nonSpecialColumns.Count / 2 + i;
|
|
||||||
if (keyOffset >= 0 && keyOffset < default_keys.Length)
|
|
||||||
column.Key.Value = default_keys[keyOffset];
|
|
||||||
else
|
|
||||||
// There is no default key defined for this column. Let's set this to Unknown for now
|
|
||||||
// however note that this will be gone after bindings are in place
|
|
||||||
column.Key.Value = Key.Unknown;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -5,9 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Input;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Configuration;
|
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
@ -85,7 +83,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
||||||
|
|
||||||
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo);
|
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, availableColumns);
|
||||||
|
|
||||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter()
|
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter()
|
||||||
{
|
{
|
||||||
@ -109,15 +107,15 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h)
|
protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h)
|
||||||
{
|
{
|
||||||
Bindable<Key> key = Playfield.Columns.ElementAt(h.Column).Key;
|
ManiaAction action = Playfield.Columns.ElementAt(h.Column).Action;
|
||||||
|
|
||||||
var holdNote = h as HoldNote;
|
var holdNote = h as HoldNote;
|
||||||
if (holdNote != null)
|
if (holdNote != null)
|
||||||
return new DrawableHoldNote(holdNote, key);
|
return new DrawableHoldNote(holdNote, action);
|
||||||
|
|
||||||
var note = h as Note;
|
var note = h as Note;
|
||||||
if (note != null)
|
if (note != null)
|
||||||
return new DrawableNote(note, key);
|
return new DrawableNote(note, action);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,13 @@ using osu.Game.Rulesets.Osu.Replays;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
@ -28,10 +33,23 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => 1.06;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class OsuModHardRock : ModHardRock
|
public class OsuModHardRock : ModHardRock, IApplicableMod<OsuHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => 1.06;
|
||||||
public override bool Ranked => true;
|
public override bool Ranked => true;
|
||||||
|
|
||||||
|
public void ApplyToRulesetContainer(RulesetContainer<OsuHitObject> rulesetContainer)
|
||||||
|
{
|
||||||
|
rulesetContainer.Objects.OfType<OsuHitObject>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Y));
|
||||||
|
rulesetContainer.Objects.OfType<Slider>().ForEach(s =>
|
||||||
|
{
|
||||||
|
var newControlPoints = new List<Vector2>();
|
||||||
|
s.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y)));
|
||||||
|
|
||||||
|
s.ControlPoints = newControlPoints;
|
||||||
|
s.Curve?.Calculate(); // Recalculate the slider curve
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class OsuModSuddenDeath : ModSuddenDeath
|
public class OsuModSuddenDeath : ModSuddenDeath
|
||||||
|
35
osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs
Normal file
35
osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Rulesets.Replays;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Replays
|
||||||
|
{
|
||||||
|
public class OsuReplayInputHandler : FramedReplayInputHandler
|
||||||
|
{
|
||||||
|
public OsuReplayInputHandler(Replay replay)
|
||||||
|
: base(replay)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<InputState> GetPendingStates()
|
||||||
|
{
|
||||||
|
List<OsuAction> actions = new List<OsuAction>();
|
||||||
|
|
||||||
|
if (CurrentFrame?.MouseLeft ?? false) actions.Add(OsuAction.LeftButton);
|
||||||
|
if (CurrentFrame?.MouseRight ?? false) actions.Add(OsuAction.RightButton);
|
||||||
|
|
||||||
|
return new List<InputState>
|
||||||
|
{
|
||||||
|
new ReplayState<OsuAction>
|
||||||
|
{
|
||||||
|
Mouse = new ReplayMouseState(ToScreenSpace(Position ?? Vector2.Zero)),
|
||||||
|
PressedActions = actions
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,9 +10,11 @@ using osu.Game.Rulesets.Osu.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
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.Scoring;
|
using osu.Game.Rulesets.Osu.Scoring;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Rulesets.Replays;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.UI
|
namespace osu.Game.Rulesets.Osu.UI
|
||||||
{
|
{
|
||||||
@ -49,6 +51,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay);
|
||||||
|
|
||||||
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f);
|
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,7 @@
|
|||||||
<Compile Include="OsuDifficulty\Skills\Speed.cs" />
|
<Compile Include="OsuDifficulty\Skills\Speed.cs" />
|
||||||
<Compile Include="OsuDifficulty\Utils\History.cs" />
|
<Compile Include="OsuDifficulty\Utils\History.cs" />
|
||||||
<Compile Include="OsuInputManager.cs" />
|
<Compile Include="OsuInputManager.cs" />
|
||||||
|
<Compile Include="Replays\OsuReplayInputHandler.cs" />
|
||||||
<Compile Include="UI\Cursor\CursorTrail.cs" />
|
<Compile Include="UI\Cursor\CursorTrail.cs" />
|
||||||
<Compile Include="UI\Cursor\GameplayCursor.cs" />
|
<Compile Include="UI\Cursor\GameplayCursor.cs" />
|
||||||
<Compile Include="UI\OsuSettings.cs" />
|
<Compile Include="UI\OsuSettings.cs" />
|
||||||
|
@ -28,14 +28,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
FillMode = FillMode.Fit;
|
FillMode = FillMode.Fit;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
// We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize
|
|
||||||
Width *= Parent.RelativeChildSize.X;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void CheckJudgement(bool userTriggered)
|
protected override void CheckJudgement(bool userTriggered)
|
||||||
{
|
{
|
||||||
if (!userTriggered)
|
if (!userTriggered)
|
||||||
@ -71,6 +63,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
return UpdateJudgement(true);
|
return UpdateJudgement(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
Size = BaseSize * Parent.RelativeChildSize;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateState(ArmedState state)
|
protected override void UpdateState(ArmedState state)
|
||||||
{
|
{
|
||||||
var circlePiece = MainPiece as CirclePiece;
|
var circlePiece = MainPiece as CirclePiece;
|
||||||
|
@ -199,6 +199,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
|
Size = BaseSize * Parent.RelativeChildSize;
|
||||||
|
|
||||||
// Make the swell stop at the hit target
|
// Make the swell stop at the hit target
|
||||||
X = (float)Math.Max(Time.Current, HitObject.StartTime);
|
X = (float)Math.Max(Time.Current, HitObject.StartTime);
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
{
|
{
|
||||||
public override Vector2 OriginPosition => new Vector2(DrawHeight / 2);
|
public override Vector2 OriginPosition => new Vector2(DrawHeight / 2);
|
||||||
|
|
||||||
|
protected readonly Vector2 BaseSize;
|
||||||
|
|
||||||
protected readonly TaikoPiece MainPiece;
|
protected readonly TaikoPiece MainPiece;
|
||||||
|
|
||||||
public new TaikoHitType HitObject;
|
public new TaikoHitType HitObject;
|
||||||
@ -29,7 +31,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
Origin = Anchor.Custom;
|
Origin = Anchor.Custom;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
Size = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE);
|
Size = BaseSize = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE);
|
||||||
|
|
||||||
Add(MainPiece = CreateMainPiece());
|
Add(MainPiece = CreateMainPiece());
|
||||||
MainPiece.KiaiMode = HitObject.Kiai;
|
MainPiece.KiaiMode = HitObject.Kiai;
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using OpenTK.Input;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Replays
|
namespace osu.Game.Rulesets.Taiko.Replays
|
||||||
{
|
{
|
||||||
@ -17,21 +16,18 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
|||||||
|
|
||||||
public override List<InputState> GetPendingStates()
|
public override List<InputState> GetPendingStates()
|
||||||
{
|
{
|
||||||
var keys = new List<Key>();
|
var actions = new List<TaikoAction>();
|
||||||
|
|
||||||
if (CurrentFrame?.MouseRight1 == true)
|
if (CurrentFrame?.MouseRight1 == true)
|
||||||
keys.Add(Key.F);
|
actions.Add(TaikoAction.LeftCentre);
|
||||||
if (CurrentFrame?.MouseRight2 == true)
|
if (CurrentFrame?.MouseRight2 == true)
|
||||||
keys.Add(Key.J);
|
actions.Add(TaikoAction.RightCentre);
|
||||||
if (CurrentFrame?.MouseLeft1 == true)
|
if (CurrentFrame?.MouseLeft1 == true)
|
||||||
keys.Add(Key.D);
|
actions.Add(TaikoAction.LeftRim);
|
||||||
if (CurrentFrame?.MouseLeft2 == true)
|
if (CurrentFrame?.MouseLeft2 == true)
|
||||||
keys.Add(Key.K);
|
actions.Add(TaikoAction.RightRim);
|
||||||
|
|
||||||
return new List<InputState>
|
return new List<InputState> { new ReplayState<TaikoAction> { PressedActions = actions } };
|
||||||
{
|
|
||||||
new InputState { Keyboard = new ReplayKeyboardState(keys) }
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ using osu.Framework.Platform;
|
|||||||
using osu.Game.IPC;
|
using osu.Game.IPC;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Beatmaps.IO
|
namespace osu.Game.Tests.Beatmaps.IO
|
||||||
{
|
{
|
||||||
@ -98,16 +97,14 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
|
|
||||||
private OsuGameBase loadOsu(GameHost host)
|
private OsuGameBase loadOsu(GameHost host)
|
||||||
{
|
{
|
||||||
|
host.Storage.DeleteDatabase(@"client");
|
||||||
|
|
||||||
var osu = new OsuGameBase();
|
var osu = new OsuGameBase();
|
||||||
Task.Run(() => host.Run(osu));
|
Task.Run(() => host.Run(osu));
|
||||||
|
|
||||||
while (!osu.IsLoaded)
|
while (!osu.IsLoaded)
|
||||||
Thread.Sleep(1);
|
Thread.Sleep(1);
|
||||||
|
|
||||||
//reset beatmap database (sqlite and storage backing)
|
|
||||||
osu.Dependencies.Get<RulesetStore>().Reset();
|
|
||||||
osu.Dependencies.Get<BeatmapManager>().Reset();
|
|
||||||
|
|
||||||
return osu;
|
return osu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,8 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
// Update
|
// Update
|
||||||
Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
|
Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
|
||||||
|
|
||||||
|
Set(OsuSetting.Version, string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsuConfigManager(Storage storage) : base(storage)
|
public OsuConfigManager(Storage storage) : base(storage)
|
||||||
@ -106,6 +108,7 @@ namespace osu.Game.Configuration
|
|||||||
SnakingInSliders,
|
SnakingInSliders,
|
||||||
SnakingOutSliders,
|
SnakingOutSliders,
|
||||||
ShowFpsDisplay,
|
ShowFpsDisplay,
|
||||||
ChatDisplayHeight
|
ChatDisplayHeight,
|
||||||
|
Version
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.Cursor
|
namespace osu.Game.Graphics.Cursor
|
||||||
@ -21,20 +22,31 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
|
|
||||||
private bool dragging;
|
private bool dragging;
|
||||||
|
|
||||||
|
private bool startRotation;
|
||||||
|
|
||||||
protected override bool OnMouseMove(InputState state)
|
protected override bool OnMouseMove(InputState state)
|
||||||
{
|
{
|
||||||
if (dragging)
|
if (dragging)
|
||||||
{
|
{
|
||||||
Vector2 offset = state.Mouse.Position - state.Mouse.PositionMouseDown ?? state.Mouse.Delta;
|
Debug.Assert(state.Mouse.PositionMouseDown != null);
|
||||||
float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f;
|
|
||||||
|
|
||||||
// Always rotate in the direction of least distance
|
// don't start rotating until we're moved a minimum distance away from the mouse down location,
|
||||||
float diff = (degrees - ActiveCursor.Rotation) % 360;
|
// else it can have an annoying effect.
|
||||||
if (diff < -180) diff += 360;
|
startRotation |= Vector2.Distance(state.Mouse.Position, state.Mouse.PositionMouseDown.Value) > 30;
|
||||||
if (diff > 180) diff -= 360;
|
|
||||||
degrees = ActiveCursor.Rotation + diff;
|
|
||||||
|
|
||||||
ActiveCursor.RotateTo(degrees, 600, Easing.OutQuint);
|
if (startRotation)
|
||||||
|
{
|
||||||
|
Vector2 offset = state.Mouse.Position - state.Mouse.PositionMouseDown.Value;
|
||||||
|
float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f;
|
||||||
|
|
||||||
|
// Always rotate in the direction of least distance
|
||||||
|
float diff = (degrees - ActiveCursor.Rotation) % 360;
|
||||||
|
if (diff < -180) diff += 360;
|
||||||
|
if (diff > 180) diff -= 360;
|
||||||
|
degrees = ActiveCursor.Rotation + diff;
|
||||||
|
|
||||||
|
ActiveCursor.RotateTo(degrees, 600, Easing.OutQuint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.OnMouseMove(state);
|
return base.OnMouseMove(state);
|
||||||
@ -61,6 +73,7 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
if (!state.Mouse.HasMainButtonPressed)
|
if (!state.Mouse.HasMainButtonPressed)
|
||||||
{
|
{
|
||||||
dragging = false;
|
dragging = false;
|
||||||
|
startRotation = false;
|
||||||
|
|
||||||
((Cursor)ActiveCursor).AdditiveLayer.FadeOut(500, Easing.OutQuint);
|
((Cursor)ActiveCursor).AdditiveLayer.FadeOut(500, Easing.OutQuint);
|
||||||
ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), Easing.OutElasticHalf);
|
ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), Easing.OutElasticHalf);
|
||||||
|
@ -24,6 +24,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
private SampleChannel sampleClick;
|
private SampleChannel sampleClick;
|
||||||
private SampleChannel sampleHover;
|
private SampleChannel sampleHover;
|
||||||
|
|
||||||
|
protected Triangles Triangles;
|
||||||
|
|
||||||
public OsuButton()
|
public OsuButton()
|
||||||
{
|
{
|
||||||
Height = 40;
|
Height = 40;
|
||||||
@ -52,7 +54,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
AddRange(new Drawable[]
|
AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
new Triangles
|
Triangles = new Triangles
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
ColourDark = colours.BlueDarker,
|
ColourDark = colours.BlueDarker,
|
||||||
@ -120,4 +122,4 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
67
osu.Game/Graphics/UserInterface/ProgressBar.cs
Normal file
67
osu.Game/Graphics/UserInterface/ProgressBar.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterface
|
||||||
|
{
|
||||||
|
public class ProgressBar : SliderBar<double>
|
||||||
|
{
|
||||||
|
public Action<double> OnSeek;
|
||||||
|
|
||||||
|
private readonly Box fill;
|
||||||
|
private readonly Box background;
|
||||||
|
|
||||||
|
public Color4 FillColour
|
||||||
|
{
|
||||||
|
set { fill.Colour = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color4 BackgroundColour
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
background.Alpha = 1;
|
||||||
|
background.Colour = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double EndTime
|
||||||
|
{
|
||||||
|
set { CurrentNumber.MaxValue = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public double CurrentTime
|
||||||
|
{
|
||||||
|
set { CurrentNumber.Value = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProgressBar()
|
||||||
|
{
|
||||||
|
CurrentNumber.MinValue = 0;
|
||||||
|
CurrentNumber.MaxValue = 1;
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
background = new Box
|
||||||
|
{
|
||||||
|
Alpha = 0,
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
fill = new Box { RelativeSizeAxes = Axes.Y }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateValue(float value)
|
||||||
|
{
|
||||||
|
fill.Width = value * UsableWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnUserChange() => OnSeek?.Invoke(Current);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,8 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Handlers;
|
using osu.Framework.Input.Handlers;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
@ -29,5 +31,18 @@ namespace osu.Game.Input.Handlers
|
|||||||
public override bool IsActive => true;
|
public override bool IsActive => true;
|
||||||
|
|
||||||
public override int Priority => 0;
|
public override int Priority => 0;
|
||||||
|
|
||||||
|
public class ReplayState<T> : InputState
|
||||||
|
where T : struct
|
||||||
|
{
|
||||||
|
public List<T> PressedActions;
|
||||||
|
|
||||||
|
public override InputState Clone()
|
||||||
|
{
|
||||||
|
var clone = (ReplayState<T>)base.Clone();
|
||||||
|
clone.PressedActions = new List<T>(PressedActions);
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -22,7 +22,7 @@ namespace osu.Game.Input
|
|||||||
{
|
{
|
||||||
var ruleset = info.CreateInstance();
|
var ruleset = info.CreateInstance();
|
||||||
foreach (var variant in ruleset.AvailableVariants)
|
foreach (var variant in ruleset.AvailableVariants)
|
||||||
insertDefaults(ruleset.GetDefaultKeyBindings(), info.ID, variant);
|
insertDefaults(ruleset.GetDefaultKeyBindings(variant), info.ID, variant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,11 @@ namespace osu.Game.Online.API
|
|||||||
/// An API request with a well-defined response type.
|
/// An API request with a well-defined response type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Type of the response (used for deserialisation).</typeparam>
|
/// <typeparam name="T">Type of the response (used for deserialisation).</typeparam>
|
||||||
public class APIRequest<T> : APIRequest
|
public abstract class APIRequest<T> : APIRequest
|
||||||
{
|
{
|
||||||
protected override WebRequest CreateWebRequest() => new JsonWebRequest<T>(Uri);
|
protected override WebRequest CreateWebRequest() => new JsonWebRequest<T>(Uri);
|
||||||
|
|
||||||
public APIRequest()
|
protected APIRequest()
|
||||||
{
|
{
|
||||||
base.Success += onSuccess;
|
base.Success += onSuccess;
|
||||||
}
|
}
|
||||||
@ -28,10 +28,36 @@ namespace osu.Game.Online.API
|
|||||||
public new event APISuccessHandler<T> Success;
|
public new event APISuccessHandler<T> Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract class APIDownloadRequest : APIRequest
|
||||||
|
{
|
||||||
|
protected override WebRequest CreateWebRequest()
|
||||||
|
{
|
||||||
|
var request = new WebRequest(Uri);
|
||||||
|
request.DownloadProgress += request_Progress;
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void request_Progress(WebRequest request, long current, long total) => API.Scheduler.Add(delegate { Progress?.Invoke(current, total); });
|
||||||
|
|
||||||
|
protected APIDownloadRequest()
|
||||||
|
{
|
||||||
|
base.Success += onSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onSuccess()
|
||||||
|
{
|
||||||
|
Success?.Invoke(WebRequest.ResponseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event APIProgressHandler Progress;
|
||||||
|
|
||||||
|
public new event APISuccessHandler<byte[]> Success;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AN API request with no specified response type.
|
/// AN API request with no specified response type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class APIRequest
|
public abstract class APIRequest
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum amount of time before this request will fail.
|
/// The maximum amount of time before this request will fail.
|
||||||
@ -42,7 +68,7 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri);
|
protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri);
|
||||||
|
|
||||||
protected virtual string Uri => $@"{api.Endpoint}/api/v2/{Target}";
|
protected virtual string Uri => $@"{API.Endpoint}/api/v2/{Target}";
|
||||||
|
|
||||||
private double remainingTime => Math.Max(0, Timeout - (DateTime.Now.TotalMilliseconds() - (startTime ?? 0)));
|
private double remainingTime => Math.Max(0, Timeout - (DateTime.Now.TotalMilliseconds() - (startTime ?? 0)));
|
||||||
|
|
||||||
@ -52,7 +78,7 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
public double StartTime => startTime ?? -1;
|
public double StartTime => startTime ?? -1;
|
||||||
|
|
||||||
private APIAccess api;
|
protected APIAccess API;
|
||||||
protected WebRequest WebRequest;
|
protected WebRequest WebRequest;
|
||||||
|
|
||||||
public event APISuccessHandler Success;
|
public event APISuccessHandler Success;
|
||||||
@ -64,7 +90,7 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
public void Perform(APIAccess api)
|
public void Perform(APIAccess api)
|
||||||
{
|
{
|
||||||
this.api = api;
|
API = api;
|
||||||
|
|
||||||
if (checkAndProcessFailure())
|
if (checkAndProcessFailure())
|
||||||
return;
|
return;
|
||||||
@ -109,9 +135,9 @@ namespace osu.Game.Online.API
|
|||||||
/// <returns>Whether we are in a failed or cancelled state.</returns>
|
/// <returns>Whether we are in a failed or cancelled state.</returns>
|
||||||
private bool checkAndProcessFailure()
|
private bool checkAndProcessFailure()
|
||||||
{
|
{
|
||||||
if (api == null || pendingFailure == null) return cancelled;
|
if (API == null || pendingFailure == null) return cancelled;
|
||||||
|
|
||||||
api.Scheduler.Add(pendingFailure);
|
API.Scheduler.Add(pendingFailure);
|
||||||
pendingFailure = null;
|
pendingFailure = null;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -119,5 +145,6 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
public delegate void APIFailureHandler(Exception e);
|
public delegate void APIFailureHandler(Exception e);
|
||||||
public delegate void APISuccessHandler();
|
public delegate void APISuccessHandler();
|
||||||
|
public delegate void APIProgressHandler(long current, long total);
|
||||||
public delegate void APISuccessHandler<in T>(T content);
|
public delegate void APISuccessHandler<in T>(T content);
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,9 @@ namespace osu.Game.Online.API.Requests
|
|||||||
[JsonProperty(@"favourite_count")]
|
[JsonProperty(@"favourite_count")]
|
||||||
private int favouriteCount { get; set; }
|
private int favouriteCount { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(@"id")]
|
||||||
|
private int onlineId { get; set; }
|
||||||
|
|
||||||
[JsonProperty(@"beatmaps")]
|
[JsonProperty(@"beatmaps")]
|
||||||
private IEnumerable<GetBeatmapSetsBeatmapResponse> beatmaps { get; set; }
|
private IEnumerable<GetBeatmapSetsBeatmapResponse> beatmaps { get; set; }
|
||||||
|
|
||||||
@ -53,6 +56,7 @@ namespace osu.Game.Online.API.Requests
|
|||||||
{
|
{
|
||||||
return new BeatmapSetInfo
|
return new BeatmapSetInfo
|
||||||
{
|
{
|
||||||
|
OnlineBeatmapSetID = onlineId,
|
||||||
Metadata = this,
|
Metadata = this,
|
||||||
OnlineInfo = new BeatmapSetOnlineInfo
|
OnlineInfo = new BeatmapSetOnlineInfo
|
||||||
{
|
{
|
||||||
|
@ -219,15 +219,28 @@ namespace osu.Game
|
|||||||
|
|
||||||
dependencies.Cache(settings);
|
dependencies.Cache(settings);
|
||||||
dependencies.Cache(social);
|
dependencies.Cache(social);
|
||||||
|
dependencies.Cache(direct);
|
||||||
dependencies.Cache(chat);
|
dependencies.Cache(chat);
|
||||||
dependencies.Cache(userProfile);
|
dependencies.Cache(userProfile);
|
||||||
dependencies.Cache(musicController);
|
dependencies.Cache(musicController);
|
||||||
dependencies.Cache(notificationOverlay);
|
dependencies.Cache(notificationOverlay);
|
||||||
dependencies.Cache(dialogOverlay);
|
dependencies.Cache(dialogOverlay);
|
||||||
|
|
||||||
// ensure both overlays aren't presented at the same time
|
// ensure only one of these overlays are open at once.
|
||||||
chat.StateChanged += (container, state) => social.State = state == Visibility.Visible ? Visibility.Hidden : social.State;
|
var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct };
|
||||||
social.StateChanged += (container, state) => chat.State = state == Visibility.Visible ? Visibility.Hidden : chat.State;
|
foreach (var overlay in singleDisplayOverlays)
|
||||||
|
{
|
||||||
|
overlay.StateChanged += (container, state) =>
|
||||||
|
{
|
||||||
|
if (state == Visibility.Hidden) return;
|
||||||
|
|
||||||
|
foreach (var c in singleDisplayOverlays)
|
||||||
|
{
|
||||||
|
if (c == container) continue;
|
||||||
|
c.State = Visibility.Hidden;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
LoadComponentAsync(Toolbar = new Toolbar
|
LoadComponentAsync(Toolbar = new Toolbar
|
||||||
{
|
{
|
||||||
|
@ -152,14 +152,10 @@ namespace osu.Game
|
|||||||
|
|
||||||
Beatmap.ValueChanged += b =>
|
Beatmap.ValueChanged += b =>
|
||||||
{
|
{
|
||||||
// compare to last baetmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)
|
// compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)
|
||||||
if (lastBeatmap?.Track != b.Track)
|
if (lastBeatmap?.Track != b.Track)
|
||||||
{
|
{
|
||||||
// this disposal is done to stop the audio track.
|
lastBeatmap?.Track?.Dispose();
|
||||||
// it may not be exactly what we want for cases beatmaps are reused, as it will
|
|
||||||
// trigger a fresh load of contained resources.
|
|
||||||
lastBeatmap?.Dispose();
|
|
||||||
|
|
||||||
Audio.Track.AddItem(b.Track);
|
Audio.Track.AddItem(b.Track);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ using osu.Game.Graphics;
|
|||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Direct
|
namespace osu.Game.Overlays.Direct
|
||||||
{
|
{
|
||||||
@ -25,23 +26,11 @@ namespace osu.Game.Overlays.Direct
|
|||||||
public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap)
|
public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap)
|
||||||
{
|
{
|
||||||
Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image)
|
Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image)
|
||||||
CornerRadius = 4;
|
|
||||||
Masking = true;
|
|
||||||
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Type = EdgeEffectType.Shadow,
|
|
||||||
Offset = new Vector2(0f, 1f),
|
|
||||||
Radius = 3f,
|
|
||||||
Colour = Color4.Black.Opacity(0.25f),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
this.FadeInFromZero(200, Easing.Out);
|
|
||||||
bottomPanel.LayoutDuration = 200;
|
bottomPanel.LayoutDuration = 200;
|
||||||
bottomPanel.LayoutEasing = Easing.Out;
|
bottomPanel.LayoutEasing = Easing.Out;
|
||||||
bottomPanel.Origin = Anchor.BottomLeft;
|
bottomPanel.Origin = Anchor.BottomLeft;
|
||||||
@ -50,14 +39,10 @@ namespace osu.Game.Overlays.Direct
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours, LocalisationEngine localisation)
|
private void load(OsuColour colours, LocalisationEngine localisation)
|
||||||
{
|
{
|
||||||
Children = new[]
|
Content.CornerRadius = 4;
|
||||||
|
|
||||||
|
AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = Color4.Black,
|
|
||||||
},
|
|
||||||
CreateBackground(),
|
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -185,7 +170,13 @@ namespace osu.Game.Overlays.Direct
|
|||||||
new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0),
|
new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnClick(InputState state)
|
||||||
|
{
|
||||||
|
StartDownload();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,35 +28,15 @@ namespace osu.Game.Overlays.Direct
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = height;
|
Height = height;
|
||||||
CornerRadius = 5;
|
|
||||||
Masking = true;
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Type = EdgeEffectType.Shadow,
|
|
||||||
Offset = new Vector2(0f, 1f),
|
|
||||||
Radius = 3f,
|
|
||||||
Colour = Color4.Black.Opacity(0.25f),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
this.FadeInFromZero(200, Easing.Out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(LocalisationEngine localisation)
|
private void load(LocalisationEngine localisation)
|
||||||
{
|
{
|
||||||
Children = new[]
|
Content.CornerRadius = 5;
|
||||||
|
|
||||||
|
AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = Color4.Black,
|
|
||||||
},
|
|
||||||
CreateBackground(),
|
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -144,10 +124,11 @@ namespace osu.Game.Overlays.Direct
|
|||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
Size = new Vector2(height - vertical_padding * 2),
|
Size = new Vector2(height - vertical_padding * 2),
|
||||||
|
Action = StartDownload
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DownloadButton : OsuClickableContainer
|
private class DownloadButton : OsuClickableContainer
|
||||||
|
@ -2,26 +2,220 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
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.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.Drawables;
|
using osu.Game.Beatmaps.Drawables;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Framework.Logging;
|
||||||
|
using osu.Game.Beatmaps.IO;
|
||||||
|
using osu.Game.Overlays.Notifications;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Direct
|
namespace osu.Game.Overlays.Direct
|
||||||
{
|
{
|
||||||
public abstract class DirectPanel : Container
|
public abstract class DirectPanel : Container
|
||||||
{
|
{
|
||||||
protected readonly BeatmapSetInfo SetInfo;
|
public readonly BeatmapSetInfo SetInfo;
|
||||||
|
|
||||||
|
protected Box BlackBackground;
|
||||||
|
|
||||||
|
private const double hover_transition_time = 400;
|
||||||
|
|
||||||
|
private Container content;
|
||||||
|
|
||||||
|
private APIAccess api;
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
private BeatmapManager beatmaps;
|
||||||
|
private NotificationOverlay notifications;
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
protected DirectPanel(BeatmapSetInfo setInfo)
|
protected DirectPanel(BeatmapSetInfo setInfo)
|
||||||
{
|
{
|
||||||
SetInfo = setInfo;
|
SetInfo = setInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Offset = new Vector2(0f, 1f),
|
||||||
|
Radius = 2f,
|
||||||
|
Colour = Color4.Black.Opacity(0.25f),
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Offset = new Vector2(0f, 5f),
|
||||||
|
Radius = 10f,
|
||||||
|
Colour = Color4.Black.Opacity(0.3f),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(permitNulls: true)]
|
||||||
|
private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications)
|
||||||
|
{
|
||||||
|
this.api = api;
|
||||||
|
this.beatmaps = beatmaps;
|
||||||
|
this.notifications = notifications;
|
||||||
|
|
||||||
|
AddInternal(content = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
EdgeEffect = edgeEffectNormal,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
// temporary blackness until the actual background loads.
|
||||||
|
BlackBackground = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.Black,
|
||||||
|
},
|
||||||
|
CreateBackground(),
|
||||||
|
progressBar = new ProgressBar
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Height = 0,
|
||||||
|
Alpha = 0,
|
||||||
|
BackgroundColour = Color4.Black.Opacity(0.7f),
|
||||||
|
FillColour = colours.Blue,
|
||||||
|
Depth = -1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(InputState state)
|
||||||
|
{
|
||||||
|
content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint);
|
||||||
|
content.MoveToY(-4, hover_transition_time, Easing.OutQuint);
|
||||||
|
|
||||||
|
return base.OnHover(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(InputState state)
|
||||||
|
{
|
||||||
|
content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
|
||||||
|
content.MoveToY(0, hover_transition_time, Easing.OutQuint);
|
||||||
|
|
||||||
|
base.OnHoverLost(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this should eventually be moved to a more central place, like BeatmapManager.
|
||||||
|
private DownloadBeatmapSetRequest downloadRequest;
|
||||||
|
|
||||||
|
protected void StartDownload()
|
||||||
|
{
|
||||||
|
if (api == null) return;
|
||||||
|
|
||||||
|
// we already have an active download running.
|
||||||
|
if (downloadRequest != null)
|
||||||
|
{
|
||||||
|
content.MoveToX(-5, 50, Easing.OutSine).Then()
|
||||||
|
.MoveToX(5, 100, Easing.InOutSine).Then()
|
||||||
|
.MoveToX(-5, 100, Easing.InOutSine).Then()
|
||||||
|
.MoveToX(0, 50, Easing.InSine).Then();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!api.LocalUser.Value.IsSupporter)
|
||||||
|
{
|
||||||
|
notifications.Post(new SimpleNotification
|
||||||
|
{
|
||||||
|
Icon = FontAwesome.fa_superpowers,
|
||||||
|
Text = "You gotta be a supporter to download for now 'yo"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
progressBar.FadeIn(400, Easing.OutQuint);
|
||||||
|
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
|
||||||
|
|
||||||
|
progressBar.Current.Value = 0;
|
||||||
|
|
||||||
|
ProgressNotification downloadNotification = new ProgressNotification
|
||||||
|
{
|
||||||
|
Text = $"Downloading {SetInfo.Metadata.Artist} - {SetInfo.Metadata.Title}",
|
||||||
|
};
|
||||||
|
|
||||||
|
downloadRequest = new DownloadBeatmapSetRequest(SetInfo);
|
||||||
|
downloadRequest.Failure += e =>
|
||||||
|
{
|
||||||
|
progressBar.Current.Value = 0;
|
||||||
|
progressBar.FadeOut(500);
|
||||||
|
downloadNotification.State = ProgressNotificationState.Completed;
|
||||||
|
Logger.Error(e, "Failed to get beatmap download information");
|
||||||
|
|
||||||
|
downloadRequest = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
downloadRequest.Progress += (current, total) =>
|
||||||
|
{
|
||||||
|
float progress = (float)current / total;
|
||||||
|
|
||||||
|
progressBar.Current.Value = progress;
|
||||||
|
|
||||||
|
downloadNotification.State = ProgressNotificationState.Active;
|
||||||
|
downloadNotification.Progress = progress;
|
||||||
|
};
|
||||||
|
|
||||||
|
downloadRequest.Success += data =>
|
||||||
|
{
|
||||||
|
progressBar.Current.Value = 1;
|
||||||
|
progressBar.FadeOut(500);
|
||||||
|
|
||||||
|
downloadNotification.State = ProgressNotificationState.Completed;
|
||||||
|
|
||||||
|
using (var stream = new MemoryStream(data))
|
||||||
|
using (var archive = new OszArchiveReader(stream))
|
||||||
|
beatmaps.Import(archive);
|
||||||
|
};
|
||||||
|
|
||||||
|
downloadNotification.CancelRequested += () =>
|
||||||
|
{
|
||||||
|
downloadRequest.Cancel();
|
||||||
|
downloadRequest = null;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
notifications.Post(downloadNotification);
|
||||||
|
|
||||||
|
// don't run in the main api queue as this is a long-running task.
|
||||||
|
Task.Run(() => downloadRequest.Perform(api));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DownloadBeatmapSetRequest : APIDownloadRequest
|
||||||
|
{
|
||||||
|
private readonly BeatmapSetInfo beatmapSet;
|
||||||
|
|
||||||
|
public DownloadBeatmapSetRequest(BeatmapSetInfo beatmapSet)
|
||||||
|
{
|
||||||
|
this.beatmapSet = beatmapSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string Target => $@"beatmapsets/{beatmapSet.OnlineBeatmapSetID}/download";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
this.FadeInFromZero(200, Easing.Out);
|
||||||
|
}
|
||||||
|
|
||||||
protected List<DifficultyIcon> GetDifficultyIcons()
|
protected List<DifficultyIcon> GetDifficultyIcons()
|
||||||
{
|
{
|
||||||
var icons = new List<DifficultyIcon>();
|
var icons = new List<DifficultyIcon>();
|
||||||
@ -38,7 +232,11 @@ namespace osu.Game.Overlays.Direct
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
FillMode = FillMode.Fill,
|
FillMode = FillMode.Fill,
|
||||||
OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
|
OnLoadComplete = d =>
|
||||||
|
{
|
||||||
|
d.FadeInFromZero(400, Easing.Out);
|
||||||
|
BlackBackground.Delay(400).FadeOut();
|
||||||
|
},
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private readonly FillFlowContainer resultCountsContainer;
|
private readonly FillFlowContainer resultCountsContainer;
|
||||||
private readonly OsuSpriteText resultCountsText;
|
private readonly OsuSpriteText resultCountsText;
|
||||||
private readonly FillFlowContainer<DirectPanel> panels;
|
private FillFlowContainer<DirectPanel> panels;
|
||||||
|
|
||||||
protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74");
|
protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74");
|
||||||
protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71");
|
protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71");
|
||||||
@ -48,17 +48,6 @@ namespace osu.Game.Overlays
|
|||||||
if (beatmapSets?.Equals(value) ?? false) return;
|
if (beatmapSets?.Equals(value) ?? false) return;
|
||||||
beatmapSets = value;
|
beatmapSets = value;
|
||||||
|
|
||||||
if (BeatmapSets == null)
|
|
||||||
{
|
|
||||||
foreach (var p in panels.Children)
|
|
||||||
{
|
|
||||||
p.FadeOut(200);
|
|
||||||
p.Expire();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
|
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,13 +97,6 @@ namespace osu.Game.Overlays
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
panels = new FillFlowContainer<DirectPanel>
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Spacing = new Vector2(panel_padding),
|
|
||||||
Margin = new MarginPadding { Top = 10 },
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Filter.Search.Current.ValueChanged += text => { if (text != string.Empty) Header.Tabs.Current.Value = DirectTab.Search; };
|
Filter.Search.Current.ValueChanged += text => { if (text != string.Empty) Header.Tabs.Current.Value = DirectTab.Search; };
|
||||||
@ -161,11 +143,19 @@ namespace osu.Game.Overlays
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours, APIAccess api, RulesetStore rulesets)
|
private void load(OsuColour colours, APIAccess api, RulesetStore rulesets, BeatmapManager beatmaps)
|
||||||
{
|
{
|
||||||
this.api = api;
|
this.api = api;
|
||||||
this.rulesets = rulesets;
|
this.rulesets = rulesets;
|
||||||
resultCountsContainer.Colour = colours.Yellow;
|
resultCountsContainer.Colour = colours.Yellow;
|
||||||
|
|
||||||
|
beatmaps.BeatmapSetAdded += setAdded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAdded(BeatmapSetInfo set)
|
||||||
|
{
|
||||||
|
// if a new map was imported, we should remove it from search results (download completed etc.)
|
||||||
|
panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateResultCounts()
|
private void updateResultCounts()
|
||||||
@ -185,18 +175,38 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private void recreatePanels(PanelDisplayStyle displayStyle)
|
private void recreatePanels(PanelDisplayStyle displayStyle)
|
||||||
{
|
{
|
||||||
|
if (panels != null)
|
||||||
|
{
|
||||||
|
panels.FadeOut(200);
|
||||||
|
panels.Expire();
|
||||||
|
panels = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (BeatmapSets == null) return;
|
if (BeatmapSets == null) return;
|
||||||
|
|
||||||
panels.ChildrenEnumerable = BeatmapSets.Select<BeatmapSetInfo, DirectPanel>(b =>
|
var newPanels = new FillFlowContainer<DirectPanel>
|
||||||
{
|
{
|
||||||
switch (displayStyle)
|
RelativeSizeAxes = Axes.X,
|
||||||
{
|
AutoSizeAxes = Axes.Y,
|
||||||
case PanelDisplayStyle.Grid:
|
Spacing = new Vector2(panel_padding),
|
||||||
return new DirectGridPanel(b) { Width = 400 };
|
Margin = new MarginPadding { Top = 10 },
|
||||||
default:
|
ChildrenEnumerable = BeatmapSets.Select<BeatmapSetInfo, DirectPanel>(b =>
|
||||||
return new DirectListPanel(b);
|
{
|
||||||
}
|
switch (displayStyle)
|
||||||
});
|
{
|
||||||
|
case PanelDisplayStyle.Grid:
|
||||||
|
return new DirectGridPanel(b) { Width = 400 };
|
||||||
|
default:
|
||||||
|
return new DirectListPanel(b);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
LoadComponentAsync(newPanels, p =>
|
||||||
|
{
|
||||||
|
if (panels != null) ScrollFlow.Remove(panels);
|
||||||
|
ScrollFlow.Add(panels = newPanels);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private GetBeatmapSetsRequest getSetsRequest;
|
private GetBeatmapSetsRequest getSetsRequest;
|
||||||
|
@ -3,21 +3,29 @@
|
|||||||
|
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.KeyBinding
|
namespace osu.Game.Overlays.KeyBinding
|
||||||
{
|
{
|
||||||
public class GlobalKeyBindingsSection : KeyBindingsSection
|
public class GlobalKeyBindingsSection : SettingsSection
|
||||||
{
|
{
|
||||||
private readonly string name;
|
public override FontAwesome Icon => FontAwesome.fa_osu_hot;
|
||||||
|
public override string Header => "Global";
|
||||||
|
|
||||||
public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail;
|
public GlobalKeyBindingsSection(KeyBindingInputManager manager)
|
||||||
public override string Header => name;
|
|
||||||
|
|
||||||
public GlobalKeyBindingsSection(KeyBindingInputManager manager, string name)
|
|
||||||
{
|
{
|
||||||
this.name = name;
|
Add(new DefaultBindingsSubsection(manager));
|
||||||
|
}
|
||||||
|
|
||||||
Defaults = manager.DefaultKeyBindings;
|
private class DefaultBindingsSubsection : KeyBindingsSubsection
|
||||||
|
{
|
||||||
|
protected override string Header => string.Empty;
|
||||||
|
|
||||||
|
public DefaultBindingsSubsection(KeyBindingInputManager manager)
|
||||||
|
: base(null)
|
||||||
|
{
|
||||||
|
Defaults = manager.DefaultKeyBindings;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -22,7 +21,7 @@ namespace osu.Game.Overlays.KeyBinding
|
|||||||
{
|
{
|
||||||
internal class KeyBindingRow : Container, IFilterable
|
internal class KeyBindingRow : Container, IFilterable
|
||||||
{
|
{
|
||||||
private readonly Enum action;
|
private readonly object action;
|
||||||
private readonly IEnumerable<Framework.Input.Bindings.KeyBinding> bindings;
|
private readonly IEnumerable<Framework.Input.Bindings.KeyBinding> bindings;
|
||||||
|
|
||||||
private const float transition_time = 150;
|
private const float transition_time = 150;
|
||||||
@ -50,7 +49,7 @@ namespace osu.Game.Overlays.KeyBinding
|
|||||||
|
|
||||||
public string[] FilterTerms => new[] { text.Text }.Concat(bindings.Select(b => b.KeyCombination.ReadableString())).ToArray();
|
public string[] FilterTerms => new[] { text.Text }.Concat(bindings.Select(b => b.KeyCombination.ReadableString())).ToArray();
|
||||||
|
|
||||||
public KeyBindingRow(Enum action, IEnumerable<Framework.Input.Bindings.KeyBinding> bindings)
|
public KeyBindingRow(object action, IEnumerable<Framework.Input.Bindings.KeyBinding> bindings)
|
||||||
{
|
{
|
||||||
this.action = action;
|
this.action = action;
|
||||||
this.bindings = bindings;
|
this.bindings = bindings;
|
||||||
@ -110,16 +109,27 @@ namespace osu.Game.Overlays.KeyBinding
|
|||||||
buttons.Add(new KeyButton(b));
|
buttons.Add(new KeyButton(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RestoreDefaults()
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
foreach (var d in Defaults)
|
||||||
|
{
|
||||||
|
var button = buttons[i++];
|
||||||
|
button.UpdateKeyCombination(d);
|
||||||
|
store.Update(button.KeyBinding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnHover(InputState state)
|
protected override bool OnHover(InputState state)
|
||||||
{
|
{
|
||||||
this.FadeEdgeEffectTo<Container>(1, transition_time, Easing.OutQuint);
|
FadeEdgeEffectTo(1, transition_time, Easing.OutQuint);
|
||||||
|
|
||||||
return base.OnHover(state);
|
return base.OnHover(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnHoverLost(InputState state)
|
protected override void OnHoverLost(InputState state)
|
||||||
{
|
{
|
||||||
this.FadeEdgeEffectTo<Container>(0, transition_time, Easing.OutQuint);
|
FadeEdgeEffectTo(0, transition_time, Easing.OutQuint);
|
||||||
|
|
||||||
base.OnHoverLost(state);
|
base.OnHoverLost(state);
|
||||||
}
|
}
|
||||||
@ -130,6 +140,8 @@ namespace osu.Game.Overlays.KeyBinding
|
|||||||
|
|
||||||
public bool AllowMainMouseButtons;
|
public bool AllowMainMouseButtons;
|
||||||
|
|
||||||
|
public IEnumerable<KeyCombination> Defaults;
|
||||||
|
|
||||||
private bool isModifier(Key k) => k < Key.F1;
|
private bool isModifier(Key k) => k < Key.F1;
|
||||||
|
|
||||||
protected override bool OnClick(InputState state) => true;
|
protected override bool OnClick(InputState state) => true;
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Game.Input;
|
|
||||||
using osu.Game.Overlays.Settings;
|
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using OpenTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.KeyBinding
|
|
||||||
{
|
|
||||||
public abstract class KeyBindingsSection : SettingsSection
|
|
||||||
{
|
|
||||||
protected IEnumerable<Framework.Input.Bindings.KeyBinding> Defaults;
|
|
||||||
|
|
||||||
protected RulesetInfo Ruleset;
|
|
||||||
|
|
||||||
protected KeyBindingsSection()
|
|
||||||
{
|
|
||||||
FlowContent.Spacing = new Vector2(0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(KeyBindingStore store)
|
|
||||||
{
|
|
||||||
var enumType = Defaults?.FirstOrDefault()?.Action?.GetType();
|
|
||||||
|
|
||||||
if (enumType == null) return;
|
|
||||||
|
|
||||||
// for now let's just assume a variant of zero.
|
|
||||||
// this will need to be implemented in a better way in the future.
|
|
||||||
int? variant = null;
|
|
||||||
if (Ruleset != null)
|
|
||||||
variant = 0;
|
|
||||||
|
|
||||||
var bindings = store.Query(Ruleset?.ID, variant);
|
|
||||||
|
|
||||||
foreach (Enum v in Enum.GetValues(enumType))
|
|
||||||
// one row per valid action.
|
|
||||||
Add(new KeyBindingRow(v, bindings.Where(b => b.Action.Equals((int)(object)v)))
|
|
||||||
{
|
|
||||||
AllowMainMouseButtons = Ruleset != null
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
72
osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs
Normal file
72
osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Input;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using OpenTK;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.KeyBinding
|
||||||
|
{
|
||||||
|
public abstract class KeyBindingsSubsection : SettingsSubsection
|
||||||
|
{
|
||||||
|
protected IEnumerable<Framework.Input.Bindings.KeyBinding> Defaults;
|
||||||
|
|
||||||
|
protected RulesetInfo Ruleset;
|
||||||
|
|
||||||
|
private readonly int? variant;
|
||||||
|
|
||||||
|
protected KeyBindingsSubsection(int? variant)
|
||||||
|
{
|
||||||
|
this.variant = variant;
|
||||||
|
|
||||||
|
FlowContent.Spacing = new Vector2(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(KeyBindingStore store)
|
||||||
|
{
|
||||||
|
var bindings = store.Query(Ruleset?.ID, variant);
|
||||||
|
|
||||||
|
foreach (var defaultGroup in Defaults.GroupBy(d => d.Action))
|
||||||
|
{
|
||||||
|
// one row per valid action.
|
||||||
|
Add(new KeyBindingRow(defaultGroup.Key, bindings.Where(b => b.Action.Equals((int)defaultGroup.Key)))
|
||||||
|
{
|
||||||
|
AllowMainMouseButtons = Ruleset != null,
|
||||||
|
Defaults = defaultGroup.Select(d => d.KeyCombination)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Add(new ResetButton
|
||||||
|
{
|
||||||
|
Action = () => Children.OfType<KeyBindingRow>().ForEach(k => k.RestoreDefaults())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ResetButton : OsuButton
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
Text = "Reset";
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
Margin = new MarginPadding { Top = 5 };
|
||||||
|
Height = 20;
|
||||||
|
|
||||||
|
Content.CornerRadius = 5;
|
||||||
|
|
||||||
|
BackgroundColour = colours.PinkDark;
|
||||||
|
Triangles.ColourDark = colours.PinkDarker;
|
||||||
|
Triangles.ColourLight = colours.Pink;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,20 +2,26 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.KeyBinding
|
namespace osu.Game.Overlays.KeyBinding
|
||||||
{
|
{
|
||||||
public class RulesetBindingsSection : KeyBindingsSection
|
public class RulesetBindingsSection : SettingsSection
|
||||||
{
|
{
|
||||||
public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail;
|
public override FontAwesome Icon => FontAwesome.fa_osu_hot;
|
||||||
public override string Header => Ruleset.Name;
|
public override string Header => ruleset.Name;
|
||||||
|
|
||||||
|
private readonly RulesetInfo ruleset;
|
||||||
|
|
||||||
public RulesetBindingsSection(RulesetInfo ruleset)
|
public RulesetBindingsSection(RulesetInfo ruleset)
|
||||||
{
|
{
|
||||||
Ruleset = ruleset;
|
this.ruleset = ruleset;
|
||||||
|
|
||||||
Defaults = ruleset.CreateInstance().GetDefaultKeyBindings();
|
var r = ruleset.CreateInstance();
|
||||||
|
|
||||||
|
foreach (var variant in r.AvailableVariants)
|
||||||
|
Add(new VariantBindingsSubsection(ruleset, variant));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
24
osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs
Normal file
24
osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.KeyBinding
|
||||||
|
{
|
||||||
|
public class VariantBindingsSubsection : KeyBindingsSubsection
|
||||||
|
{
|
||||||
|
protected override string Header => variantName;
|
||||||
|
private readonly string variantName;
|
||||||
|
|
||||||
|
public VariantBindingsSubsection(RulesetInfo ruleset, int variant)
|
||||||
|
: base(variant)
|
||||||
|
{
|
||||||
|
Ruleset = ruleset;
|
||||||
|
|
||||||
|
var rulesetInstance = ruleset.CreateInstance();
|
||||||
|
|
||||||
|
variantName = rulesetInstance.GetVariantName(variant);
|
||||||
|
Defaults = rulesetInstance.GetDefaultKeyBindings(variant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,7 +17,7 @@ namespace osu.Game.Overlays
|
|||||||
[BackgroundDependencyLoader(permitNulls: true)]
|
[BackgroundDependencyLoader(permitNulls: true)]
|
||||||
private void load(RulesetStore rulesets, GlobalKeyBindingInputManager global)
|
private void load(RulesetStore rulesets, GlobalKeyBindingInputManager global)
|
||||||
{
|
{
|
||||||
AddSection(new GlobalKeyBindingsSection(global, "Global"));
|
AddSection(new GlobalKeyBindingsSection(global));
|
||||||
|
|
||||||
foreach (var ruleset in rulesets.AllRulesets)
|
foreach (var ruleset in rulesets.AllRulesets)
|
||||||
AddSection(new RulesetBindingsSection(ruleset));
|
AddSection(new RulesetBindingsSection(ruleset));
|
||||||
|
@ -15,7 +15,6 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
@ -349,23 +348,23 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
playerContainer.Add(new AsyncLoadWrapper(new Background(beatmap)
|
playerContainer.Add(new AsyncLoadWrapper(new Background(beatmap)
|
||||||
{
|
{
|
||||||
OnLoadComplete = d =>
|
OnLoadComplete = newBackground =>
|
||||||
{
|
{
|
||||||
switch (direction)
|
switch (direction)
|
||||||
{
|
{
|
||||||
case TransformDirection.Next:
|
case TransformDirection.Next:
|
||||||
d.Position = new Vector2(400, 0);
|
newBackground.Position = new Vector2(400, 0);
|
||||||
d.MoveToX(0, 500, Easing.OutCubic);
|
newBackground.MoveToX(0, 500, Easing.OutCubic);
|
||||||
currentBackground.MoveToX(-400, 500, Easing.OutCubic);
|
currentBackground.MoveToX(-400, 500, Easing.OutCubic);
|
||||||
break;
|
break;
|
||||||
case TransformDirection.Prev:
|
case TransformDirection.Prev:
|
||||||
d.Position = new Vector2(-400, 0);
|
newBackground.Position = new Vector2(-400, 0);
|
||||||
d.MoveToX(0, 500, Easing.OutCubic);
|
newBackground.MoveToX(0, 500, Easing.OutCubic);
|
||||||
currentBackground.MoveToX(400, 500, Easing.OutCubic);
|
currentBackground.MoveToX(400, 500, Easing.OutCubic);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
currentBackground.Expire();
|
currentBackground.Expire();
|
||||||
currentBackground = d;
|
currentBackground = newBackground;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
@ -434,49 +433,5 @@ namespace osu.Game.Overlays
|
|||||||
sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
|
sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ProgressBar : SliderBar<double>
|
|
||||||
{
|
|
||||||
public Action<double> OnSeek;
|
|
||||||
|
|
||||||
private readonly Box fill;
|
|
||||||
|
|
||||||
public Color4 FillColour
|
|
||||||
{
|
|
||||||
set { fill.Colour = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public double EndTime
|
|
||||||
{
|
|
||||||
set { CurrentNumber.MaxValue = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public double CurrentTime
|
|
||||||
{
|
|
||||||
set { CurrentNumber.Value = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProgressBar()
|
|
||||||
{
|
|
||||||
CurrentNumber.MinValue = 0;
|
|
||||||
CurrentNumber.MaxValue = 1;
|
|
||||||
RelativeSizeAxes = Axes.X;
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
fill = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Y
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void UpdateValue(float value)
|
|
||||||
{
|
|
||||||
fill.Width = value * UsableWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnUserChange() => OnSeek?.Invoke(Current);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Overlays
|
|||||||
private ScrollContainer scrollContainer;
|
private ScrollContainer scrollContainer;
|
||||||
private FlowContainer<NotificationSection> sections;
|
private FlowContainer<NotificationSection> sections;
|
||||||
|
|
||||||
[BackgroundDependencyLoader(permitNulls: true)]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Width = width;
|
Width = width;
|
||||||
@ -72,6 +72,13 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private int runningDepth;
|
private int runningDepth;
|
||||||
|
|
||||||
|
private void notificationClosed()
|
||||||
|
{
|
||||||
|
// hide ourselves if all notifications have been dismissed.
|
||||||
|
if (sections.Select(c => c.DisplayedCount).Sum() == 0)
|
||||||
|
State = Visibility.Hidden;
|
||||||
|
}
|
||||||
|
|
||||||
public void Post(Notification notification)
|
public void Post(Notification notification)
|
||||||
{
|
{
|
||||||
Schedule(() =>
|
Schedule(() =>
|
||||||
@ -81,6 +88,8 @@ namespace osu.Game.Overlays
|
|||||||
++runningDepth;
|
++runningDepth;
|
||||||
notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth;
|
notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth;
|
||||||
|
|
||||||
|
notification.Closed += notificationClosed;
|
||||||
|
|
||||||
var hasCompletionTarget = notification as IHasCompletionTarget;
|
var hasCompletionTarget = notification as IHasCompletionTarget;
|
||||||
if (hasCompletionTarget != null)
|
if (hasCompletionTarget != null)
|
||||||
hasCompletionTarget.CompletionTarget = Post;
|
hasCompletionTarget.CompletionTarget = Post;
|
||||||
|
@ -19,9 +19,9 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
public abstract class Notification : Container
|
public abstract class Notification : Container
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Use requested close.
|
/// User requested close.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action Closed;
|
public event Action Closed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Run on user activating the notification. Return true to close.
|
/// Run on user activating the notification. Return true to close.
|
||||||
@ -142,12 +142,12 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
NotificationContent.MoveToX(0, 500, Easing.OutQuint);
|
NotificationContent.MoveToX(0, 500, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool wasClosed;
|
public bool WasClosed;
|
||||||
|
|
||||||
public virtual void Close()
|
public virtual void Close()
|
||||||
{
|
{
|
||||||
if (wasClosed) return;
|
if (WasClosed) return;
|
||||||
wasClosed = true;
|
WasClosed = true;
|
||||||
|
|
||||||
Closed?.Invoke();
|
Closed?.Invoke();
|
||||||
this.FadeOut(100);
|
this.FadeOut(100);
|
||||||
|
@ -24,10 +24,9 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
|
|
||||||
private FlowContainer<Notification> notifications;
|
private FlowContainer<Notification> notifications;
|
||||||
|
|
||||||
public void Add(Notification notification)
|
public int DisplayedCount => notifications.Count(n => !n.WasClosed);
|
||||||
{
|
|
||||||
notifications.Add(notification);
|
public void Add(Notification notification) => notifications.Add(notification);
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Type> AcceptTypes;
|
public IEnumerable<Type> AcceptTypes;
|
||||||
|
|
||||||
|
@ -152,11 +152,14 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
break;
|
break;
|
||||||
case ProgressNotificationState.Active:
|
case ProgressNotificationState.Active:
|
||||||
case ProgressNotificationState.Queued:
|
case ProgressNotificationState.Queued:
|
||||||
State = ProgressNotificationState.Cancelled;
|
if (CancelRequested?.Invoke() != false)
|
||||||
|
State = ProgressNotificationState.Cancelled;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Func<bool> CancelRequested { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The function to post completion notifications back to.
|
/// The function to post completion notifications back to.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -4,17 +4,16 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Notifications
|
namespace osu.Game.Overlays.Notifications
|
||||||
{
|
{
|
||||||
public class SimpleNotification : Notification
|
public class SimpleNotification : Notification
|
||||||
{
|
{
|
||||||
private string text;
|
private string text = string.Empty;
|
||||||
public string Text
|
public string Text
|
||||||
{
|
{
|
||||||
get { return text; }
|
get { return text; }
|
||||||
@ -36,7 +35,7 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly SpriteText textDrawable;
|
private readonly TextFlowContainer textDrawable;
|
||||||
private readonly SpriteIcon iconDrawable;
|
private readonly SpriteIcon iconDrawable;
|
||||||
|
|
||||||
protected Box IconBackgound;
|
protected Box IconBackgound;
|
||||||
@ -59,9 +58,8 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Content.Add(textDrawable = new OsuSpriteText
|
Content.Add(textDrawable = new TextFlowContainer(t => t.TextSize = 16)
|
||||||
{
|
{
|
||||||
TextSize = 16,
|
|
||||||
Colour = OsuColour.Gray(128),
|
Colour = OsuColour.Gray(128),
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
@ -7,14 +7,15 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings
|
namespace osu.Game.Overlays.Settings
|
||||||
{
|
{
|
||||||
public abstract class SettingsSubsection : FillFlowContainer, IHasFilterableChildren
|
public abstract class SettingsSubsection : FillFlowContainer, IHasFilterableChildren
|
||||||
{
|
{
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => FlowContent;
|
||||||
|
|
||||||
private readonly Container<Drawable> content;
|
protected readonly FillFlowContainer FlowContent;
|
||||||
|
|
||||||
protected abstract string Header { get; }
|
protected abstract string Header { get; }
|
||||||
|
|
||||||
@ -33,6 +34,19 @@ namespace osu.Game.Overlays.Settings
|
|||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
Direction = FillDirection.Vertical;
|
Direction = FillDirection.Vertical;
|
||||||
|
|
||||||
|
FlowContent = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(0, 5),
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
@ -41,13 +55,7 @@ namespace osu.Game.Overlays.Settings
|
|||||||
Margin = new MarginPadding { Bottom = 10 },
|
Margin = new MarginPadding { Bottom = 10 },
|
||||||
Font = @"Exo2.0-Black",
|
Font = @"Exo2.0-Black",
|
||||||
},
|
},
|
||||||
content = new FillFlowContainer
|
FlowContent
|
||||||
{
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Spacing = new Vector2(0, 5),
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,7 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
AutoSizeAxes = Axes.X,
|
AutoSizeAxes = Axes.X,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
new ToolbarDirectButton(),
|
||||||
new ToolbarChatButton(),
|
new ToolbarChatButton(),
|
||||||
new ToolbarSocialButton(),
|
new ToolbarSocialButton(),
|
||||||
new ToolbarMusicButton(),
|
new ToolbarMusicButton(),
|
||||||
|
22
osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs
Normal file
22
osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Toolbar
|
||||||
|
{
|
||||||
|
internal class ToolbarDirectButton : ToolbarOverlayToggleButton
|
||||||
|
{
|
||||||
|
public ToolbarDirectButton()
|
||||||
|
{
|
||||||
|
SetIcon(FontAwesome.fa_osu_chevron_down_o);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(DirectOverlay direct)
|
||||||
|
{
|
||||||
|
StateContainer = direct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
@ -18,7 +17,7 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
/// The ReplayHandler will take a replay and handle the propagation of updates to the input stack.
|
/// The ReplayHandler will take a replay and handle the propagation of updates to the input stack.
|
||||||
/// It handles logic of any frames which *must* be executed.
|
/// It handles logic of any frames which *must* be executed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FramedReplayInputHandler : ReplayInputHandler
|
public abstract class FramedReplayInputHandler : ReplayInputHandler
|
||||||
{
|
{
|
||||||
private readonly Replay replay;
|
private readonly Replay replay;
|
||||||
|
|
||||||
@ -31,7 +30,7 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
|
|
||||||
private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, Frames.Count - 1);
|
private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, Frames.Count - 1);
|
||||||
|
|
||||||
public FramedReplayInputHandler(Replay replay)
|
protected FramedReplayInputHandler(Replay replay)
|
||||||
{
|
{
|
||||||
this.replay = replay;
|
this.replay = replay;
|
||||||
}
|
}
|
||||||
@ -51,7 +50,7 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vector2? position
|
protected Vector2? Position
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@ -62,23 +61,7 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override List<InputState> GetPendingStates()
|
public override List<InputState> GetPendingStates() => new List<InputState>();
|
||||||
{
|
|
||||||
var buttons = new HashSet<MouseButton>();
|
|
||||||
if (CurrentFrame?.MouseLeft ?? false)
|
|
||||||
buttons.Add(MouseButton.Left);
|
|
||||||
if (CurrentFrame?.MouseRight ?? false)
|
|
||||||
buttons.Add(MouseButton.Right);
|
|
||||||
|
|
||||||
return new List<InputState>
|
|
||||||
{
|
|
||||||
new InputState
|
|
||||||
{
|
|
||||||
Mouse = new ReplayMouseState(ToScreenSpace(position ?? Vector2.Zero), buttons),
|
|
||||||
Keyboard = new ReplayKeyboardState(new List<Key>())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool AtLastFrame => currentFrameIndex == Frames.Count - 1;
|
public bool AtLastFrame => currentFrameIndex == Frames.Count - 1;
|
||||||
public bool AtFirstFrame => currentFrameIndex == 0;
|
public bool AtFirstFrame => currentFrameIndex == 0;
|
||||||
@ -133,10 +116,9 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
|
|
||||||
protected class ReplayMouseState : MouseState
|
protected class ReplayMouseState : MouseState
|
||||||
{
|
{
|
||||||
public ReplayMouseState(Vector2 position, IEnumerable<MouseButton> list)
|
public ReplayMouseState(Vector2 position)
|
||||||
{
|
{
|
||||||
Position = position;
|
Position = position;
|
||||||
list.ForEach(b => SetPressed(b, true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,5 +63,12 @@ namespace osu.Game.Rulesets
|
|||||||
/// <param name="variant">A variant.</param>
|
/// <param name="variant">A variant.</param>
|
||||||
/// <returns>A list of valid <see cref="KeyBinding"/>s.</returns>
|
/// <returns>A list of valid <see cref="KeyBinding"/>s.</returns>
|
||||||
public virtual IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new KeyBinding[] { };
|
public virtual IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new KeyBinding[] { };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name for a key binding variant. This is used for display in the settings overlay.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="variant">The variant.</param>
|
||||||
|
/// <returns>A descriptive name of the variant.</returns>
|
||||||
|
public virtual string GetVariantName(int variant) => string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
frames.Add(new ReplayFrame(
|
frames.Add(new ReplayFrame(
|
||||||
lastTime,
|
lastTime,
|
||||||
float.Parse(split[1]),
|
float.Parse(split[1]),
|
||||||
384 - float.Parse(split[2]),
|
float.Parse(split[2]),
|
||||||
(ReplayButtonState)int.Parse(split[3])
|
(ReplayButtonState)int.Parse(split[3])
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,10 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class LinearScrollingContainer : ScrollingContainer
|
internal class LinearScrollingContainer : ScrollingContainer
|
||||||
{
|
{
|
||||||
private readonly Axes scrollingAxes;
|
|
||||||
private readonly MultiplierControlPoint controlPoint;
|
private readonly MultiplierControlPoint controlPoint;
|
||||||
|
|
||||||
public LinearScrollingContainer(Axes scrollingAxes, MultiplierControlPoint controlPoint)
|
public LinearScrollingContainer(MultiplierControlPoint controlPoint)
|
||||||
{
|
{
|
||||||
this.scrollingAxes = scrollingAxes;
|
|
||||||
this.controlPoint = controlPoint;
|
this.controlPoint = controlPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,8 +21,8 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
if ((scrollingAxes & Axes.X) > 0) X = (float)(controlPoint.StartTime - Time.Current);
|
if ((ScrollingAxes & Axes.X) > 0) X = (float)(controlPoint.StartTime - Time.Current);
|
||||||
if ((scrollingAxes & Axes.Y) > 0) Y = (float)(controlPoint.StartTime - Time.Current);
|
if ((ScrollingAxes & Axes.Y) > 0) Y = (float)(controlPoint.StartTime - Time.Current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Caching;
|
using osu.Framework.Caching;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
@ -45,50 +46,34 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
RelativePositionAxes = Axes.Both;
|
RelativePositionAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void InvalidateFromChild(Invalidation invalidation)
|
protected override int Compare(Drawable x, Drawable y)
|
||||||
{
|
{
|
||||||
// We only want to re-compute our size when a child's size or position has changed
|
var hX = (DrawableHitObject)x;
|
||||||
if ((invalidation & Invalidation.RequiredParentSizeToFit) == 0)
|
var hY = (DrawableHitObject)y;
|
||||||
{
|
|
||||||
base.InvalidateFromChild(invalidation);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
int result = hY.HitObject.StartTime.CompareTo(hX.HitObject.StartTime);
|
||||||
|
if (result != 0)
|
||||||
|
return result;
|
||||||
|
return base.Compare(y, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Add(DrawableHitObject drawable)
|
||||||
|
{
|
||||||
durationBacking.Invalidate();
|
durationBacking.Invalidate();
|
||||||
|
base.Add(drawable);
|
||||||
base.InvalidateFromChild(invalidation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private double computeDuration()
|
public override bool Remove(DrawableHitObject drawable)
|
||||||
{
|
{
|
||||||
if (!Children.Any())
|
durationBacking.Invalidate();
|
||||||
return 0;
|
return base.Remove(drawable);
|
||||||
|
|
||||||
double baseDuration = Children.Max(c => (c.HitObject as IHasEndTime)?.EndTime ?? c.HitObject.StartTime) - ControlPoint.StartTime;
|
|
||||||
|
|
||||||
// If we have a singular hit object at the timing section's start time, let's set a sane default duration
|
|
||||||
if (baseDuration == 0)
|
|
||||||
baseDuration = 1;
|
|
||||||
|
|
||||||
// This container needs to resize such that it completely encloses the hit objects to avoid masking optimisations. This is done by converting the largest
|
|
||||||
// absolutely-sized element along the scrolling axes and adding a corresponding duration value. This introduces a bit of error, but will never under-estimate.ion.
|
|
||||||
|
|
||||||
// Find the largest element that is absolutely-sized along ScrollingAxes
|
|
||||||
float maxAbsoluteSize = Children.Where(c => (c.RelativeSizeAxes & ScrollingAxes) == 0)
|
|
||||||
.Select(c => (ScrollingAxes & Axes.X) > 0 ? c.Width : c.Height)
|
|
||||||
.DefaultIfEmpty().Max();
|
|
||||||
|
|
||||||
float ourAbsoluteSize = (ScrollingAxes & Axes.X) > 0 ? DrawWidth : DrawHeight;
|
|
||||||
|
|
||||||
// Add the extra duration to account for the absolute size
|
|
||||||
baseDuration *= 1 + maxAbsoluteSize / ourAbsoluteSize;
|
|
||||||
|
|
||||||
return baseDuration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Todo: This may underestimate the size of the hit object in some cases, but won't be too much of a problem for now
|
||||||
|
private double computeDuration() => Math.Max(0, Children.Select(c => (c.HitObject as IHasEndTime)?.EndTime ?? c.HitObject.StartTime).DefaultIfEmpty().Max() - ControlPoint.StartTime) + 1000;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum duration of any one hit object inside this <see cref="ScrollingContainer"/>. This is calculated as the maximum
|
/// An approximate total duration of this scrolling container.
|
||||||
/// duration of all hit objects relative to this <see cref="ScrollingContainer"/>'s <see cref="MultiplierControlPoint.StartTime"/>.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double Duration => durationBacking.IsValid ? durationBacking : (durationBacking.Value = computeDuration());
|
public double Duration => durationBacking.IsValid ? durationBacking : (durationBacking.Value = computeDuration());
|
||||||
|
|
||||||
@ -96,6 +81,8 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
|
RelativeChildOffset = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)ControlPoint.StartTime : 0, (ScrollingAxes & Axes.Y) > 0 ? (float)ControlPoint.StartTime : 0);
|
||||||
|
|
||||||
// We want our size and position-space along the scrolling axes to span our duration to completely enclose all the hit objects
|
// We want our size and position-space along the scrolling axes to span our duration to completely enclose all the hit objects
|
||||||
Size = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)Duration : Size.X, (ScrollingAxes & Axes.Y) > 0 ? (float)Duration : Size.Y);
|
Size = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)Duration : Size.X, (ScrollingAxes & Axes.Y) > 0 ? (float)Duration : Size.Y);
|
||||||
// And we need to make sure the hit object's position-space doesn't change due to our resizing
|
// And we need to make sure the hit object's position-space doesn't change due to our resizing
|
||||||
|
@ -31,7 +31,11 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The axes which the content of this container will scroll through.
|
/// The axes which the content of this container will scroll through.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Axes ScrollingAxes { get; internal set; }
|
public Axes ScrollingAxes
|
||||||
|
{
|
||||||
|
get { return scrollingContainer.ScrollingAxes; }
|
||||||
|
set { scrollingContainer.ScrollingAxes = value; }
|
||||||
|
}
|
||||||
|
|
||||||
public override bool RemoveWhenNotAlive => false;
|
public override bool RemoveWhenNotAlive => false;
|
||||||
|
|
||||||
@ -52,11 +56,8 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
scrollingContainer = CreateScrollingContainer();
|
scrollingContainer = CreateScrollingContainer();
|
||||||
|
|
||||||
scrollingContainer.ScrollingAxes = ScrollingAxes;
|
|
||||||
scrollingContainer.ControlPoint = ControlPoint;
|
scrollingContainer.ControlPoint = ControlPoint;
|
||||||
scrollingContainer.VisibleTimeRange.BindTo(VisibleTimeRange);
|
scrollingContainer.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||||
scrollingContainer.RelativeChildOffset = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)ControlPoint.StartTime : 0, (ScrollingAxes & Axes.Y) > 0 ? (float)ControlPoint.StartTime : 0);
|
|
||||||
|
|
||||||
AddInternal(content = scrollingContainer);
|
AddInternal(content = scrollingContainer);
|
||||||
}
|
}
|
||||||
@ -98,11 +99,6 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
base.Add(drawable);
|
base.Add(drawable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether a <see cref="DrawableHitObject"/> falls within this <see cref="SpeedAdjustmentContainer"/>s affecting timespan.
|
|
||||||
/// </summary>
|
|
||||||
public bool CanContain(DrawableHitObject hitObject) => CanContain(hitObject.HitObject.StartTime);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether a point in time falls within this <see cref="SpeedAdjustmentContainer"/>s affecting timespan.
|
/// Whether a point in time falls within this <see cref="SpeedAdjustmentContainer"/>s affecting timespan.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -112,6 +108,6 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
/// Creates the <see cref="ScrollingContainer"/> which contains the scrolling <see cref="DrawableHitObject"/>s of this container.
|
/// Creates the <see cref="ScrollingContainer"/> which contains the scrolling <see cref="DrawableHitObject"/>s of this container.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The <see cref="ScrollingContainer"/>.</returns>
|
/// <returns>The <see cref="ScrollingContainer"/>.</returns>
|
||||||
protected virtual ScrollingContainer CreateScrollingContainer() => new LinearScrollingContainer(ScrollingAxes, ControlPoint);
|
protected virtual ScrollingContainer CreateScrollingContainer() => new LinearScrollingContainer(ControlPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ using osu.Game.Rulesets.Judgements;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Screens.Play;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@ -43,12 +42,12 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The input manager for this RulesetContainer.
|
/// The input manager for this RulesetContainer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal readonly PlayerInputManager InputManager = new PlayerInputManager();
|
internal IHasReplayHandler ReplayInputManager => KeyBindingInputManager as IHasReplayHandler;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The key conversion input manager for this RulesetContainer.
|
/// The key conversion input manager for this RulesetContainer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly PassThroughInputManager KeyBindingInputManager;
|
public PassThroughInputManager KeyBindingInputManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether we are currently providing the local user a gameplay cursor.
|
/// Whether we are currently providing the local user a gameplay cursor.
|
||||||
@ -58,7 +57,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether we have a replay loaded currently.
|
/// Whether we have a replay loaded currently.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasReplayLoaded => InputManager.ReplayInputHandler != null;
|
public bool HasReplayLoaded => ReplayInputManager?.ReplayInputHandler != null;
|
||||||
|
|
||||||
public abstract IEnumerable<HitObject> Objects { get; }
|
public abstract IEnumerable<HitObject> Objects { get; }
|
||||||
|
|
||||||
@ -76,6 +75,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
internal RulesetContainer(Ruleset ruleset)
|
internal RulesetContainer(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
Ruleset = ruleset;
|
Ruleset = ruleset;
|
||||||
|
|
||||||
KeyBindingInputManager = CreateInputManager();
|
KeyBindingInputManager = CreateInputManager();
|
||||||
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
|
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <returns>The input manager.</returns>
|
/// <returns>The input manager.</returns>
|
||||||
public abstract PassThroughInputManager CreateInputManager();
|
public abstract PassThroughInputManager CreateInputManager();
|
||||||
|
|
||||||
protected virtual FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new FramedReplayInputHandler(replay);
|
protected virtual FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => null;
|
||||||
|
|
||||||
public Replay Replay { get; private set; }
|
public Replay Replay { get; private set; }
|
||||||
|
|
||||||
@ -105,10 +105,13 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// Sets a replay to be used, overriding local input.
|
/// Sets a replay to be used, overriding local input.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="replay">The replay, null for local input.</param>
|
/// <param name="replay">The replay, null for local input.</param>
|
||||||
public void SetReplay(Replay replay)
|
public virtual void SetReplay(Replay replay)
|
||||||
{
|
{
|
||||||
|
if (ReplayInputManager == null)
|
||||||
|
throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports replay loading is not available");
|
||||||
|
|
||||||
Replay = replay;
|
Replay = replay;
|
||||||
InputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null;
|
ReplayInputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,7 +247,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
public Playfield<TObject, TJudgement> Playfield { get; private set; }
|
public Playfield<TObject, TJudgement> Playfield { get; private set; }
|
||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => content;
|
||||||
private readonly Container content;
|
private Container content;
|
||||||
|
|
||||||
private readonly List<DrawableHitObject<TObject, TJudgement>> drawableObjects = new List<DrawableHitObject<TObject, TJudgement>>();
|
private readonly List<DrawableHitObject<TObject, TJudgement>> drawableObjects = new List<DrawableHitObject<TObject, TJudgement>>();
|
||||||
|
|
||||||
@ -257,24 +260,28 @@ namespace osu.Game.Rulesets.UI
|
|||||||
protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||||
: base(ruleset, beatmap, isForCurrentRuleset)
|
: base(ruleset, beatmap, isForCurrentRuleset)
|
||||||
{
|
{
|
||||||
InputManager.Add(content = new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Children = new[] { KeyBindingInputManager }
|
|
||||||
});
|
|
||||||
|
|
||||||
AddInternal(InputManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
KeyBindingInputManager.Add(content = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
});
|
||||||
|
|
||||||
|
AddInternal(KeyBindingInputManager);
|
||||||
KeyBindingInputManager.Add(Playfield = CreatePlayfield());
|
KeyBindingInputManager.Add(Playfield = CreatePlayfield());
|
||||||
|
|
||||||
loadObjects();
|
loadObjects();
|
||||||
|
}
|
||||||
|
|
||||||
if (InputManager?.ReplayInputHandler != null)
|
public override void SetReplay(Replay replay)
|
||||||
InputManager.ReplayInputHandler.ToScreenSpace = Playfield.ScaledContent.ToScreenSpace;
|
{
|
||||||
|
base.SetReplay(replay);
|
||||||
|
|
||||||
|
if (ReplayInputManager?.ReplayInputHandler != null)
|
||||||
|
ReplayInputManager.ReplayInputHandler.ToScreenSpace = input => Playfield.ScaledContent.ToScreenSpace(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,20 +1,194 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
|
using osu.Game.Input.Handlers;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using OpenTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.UI
|
namespace osu.Game.Rulesets.UI
|
||||||
{
|
{
|
||||||
public abstract class RulesetInputManager<T> : DatabasedKeyBindingInputManager<T>, ICanAttachKeyCounter
|
public abstract class RulesetInputManager<T> : DatabasedKeyBindingInputManager<T>, ICanAttachKeyCounter, IHasReplayHandler
|
||||||
where T : struct
|
where T : struct
|
||||||
{
|
{
|
||||||
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique)
|
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Action mapping (for replays)
|
||||||
|
|
||||||
|
private List<T> lastPressedActions = new List<T>();
|
||||||
|
|
||||||
|
protected override void HandleNewState(InputState state)
|
||||||
|
{
|
||||||
|
base.HandleNewState(state);
|
||||||
|
|
||||||
|
var replayState = state as ReplayInputHandler.ReplayState<T>;
|
||||||
|
|
||||||
|
if (replayState == null) return;
|
||||||
|
|
||||||
|
// Here we handle states specifically coming from a replay source.
|
||||||
|
// These have extra action information rather than keyboard keys or mouse buttons.
|
||||||
|
|
||||||
|
List<T> newActions = replayState.PressedActions;
|
||||||
|
|
||||||
|
foreach (var released in lastPressedActions.Except(newActions))
|
||||||
|
PropagateReleased(KeyBindingInputQueue, released);
|
||||||
|
|
||||||
|
foreach (var pressed in newActions.Except(lastPressedActions))
|
||||||
|
PropagatePressed(KeyBindingInputQueue, pressed);
|
||||||
|
|
||||||
|
lastPressedActions = newActions;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IHasReplayHandler
|
||||||
|
|
||||||
|
private ReplayInputHandler replayInputHandler;
|
||||||
|
public ReplayInputHandler ReplayInputHandler
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return replayInputHandler;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (replayInputHandler != null) RemoveHandler(replayInputHandler);
|
||||||
|
|
||||||
|
replayInputHandler = value;
|
||||||
|
UseParentState = replayInputHandler == null;
|
||||||
|
|
||||||
|
if (replayInputHandler != null)
|
||||||
|
AddHandler(replayInputHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Clock control
|
||||||
|
|
||||||
|
private ManualClock clock;
|
||||||
|
private IFrameBasedClock parentClock;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
//our clock will now be our parent's clock, but we want to replace this to allow manual control.
|
||||||
|
parentClock = Clock;
|
||||||
|
|
||||||
|
Clock = new FramedClock(clock = new ManualClock
|
||||||
|
{
|
||||||
|
CurrentTime = parentClock.CurrentTime,
|
||||||
|
Rate = parentClock.Rate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether we are running up-to-date with our parent clock.
|
||||||
|
/// If not, we will need to keep processing children until we catch up.
|
||||||
|
/// </summary>
|
||||||
|
private bool requireMoreUpdateLoops;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether we are in a valid state (ie. should we keep processing children frames).
|
||||||
|
/// This should be set to false when the replay is, for instance, waiting for future frames to arrive.
|
||||||
|
/// </summary>
|
||||||
|
private bool validState;
|
||||||
|
|
||||||
|
protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState;
|
||||||
|
|
||||||
|
private bool isAttached => replayInputHandler != null && !UseParentState;
|
||||||
|
|
||||||
|
private const int max_catch_up_updates_per_frame = 50;
|
||||||
|
|
||||||
|
public override bool UpdateSubTree()
|
||||||
|
{
|
||||||
|
requireMoreUpdateLoops = true;
|
||||||
|
validState = true;
|
||||||
|
|
||||||
|
int loops = 0;
|
||||||
|
|
||||||
|
while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame)
|
||||||
|
if (!base.UpdateSubTree())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
if (parentClock == null) return;
|
||||||
|
|
||||||
|
clock.Rate = parentClock.Rate;
|
||||||
|
clock.IsRunning = parentClock.IsRunning;
|
||||||
|
|
||||||
|
if (!isAttached)
|
||||||
|
{
|
||||||
|
clock.CurrentTime = parentClock.CurrentTime;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double? newTime = replayInputHandler.SetFrameFromTime(parentClock.CurrentTime);
|
||||||
|
|
||||||
|
if (newTime == null)
|
||||||
|
{
|
||||||
|
// we shouldn't execute for this time value. probably waiting on more replay data.
|
||||||
|
validState = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clock.CurrentTime = newTime.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime;
|
||||||
|
base.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Setting application (disables etc.)
|
||||||
|
|
||||||
|
private Bindable<bool> mouseDisabled;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuConfigManager config)
|
||||||
|
{
|
||||||
|
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void TransformState(InputState state)
|
||||||
|
{
|
||||||
|
base.TransformState(state);
|
||||||
|
|
||||||
|
// we don't want to transform the state if a replay is present (for now, at least).
|
||||||
|
if (replayInputHandler != null) return;
|
||||||
|
|
||||||
|
var mouse = state.Mouse as Framework.Input.MouseState;
|
||||||
|
|
||||||
|
if (mouse != null)
|
||||||
|
{
|
||||||
|
if (mouseDisabled.Value)
|
||||||
|
{
|
||||||
|
mouse.SetPressed(MouseButton.Left, false);
|
||||||
|
mouse.SetPressed(MouseButton.Right, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Key Counter Attachment
|
||||||
|
|
||||||
public void Attach(KeyCounterCollection keyCounter)
|
public void Attach(KeyCounterCollection keyCounter)
|
||||||
{
|
{
|
||||||
var receptor = new ActionReceptor(keyCounter);
|
var receptor = new ActionReceptor(keyCounter);
|
||||||
@ -35,10 +209,24 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
public bool OnReleased(T action) => Target.Children.OfType<KeyCounterAction<T>>().Any(c => c.OnReleased(action));
|
public bool OnReleased(T action) => Target.Children.OfType<KeyCounterAction<T>>().Any(c => c.OnReleased(action));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Expose the <see cref="ReplayInputHandler"/> in a capable <see cref="InputManager"/>.
|
||||||
|
/// </summary>
|
||||||
|
public interface IHasReplayHandler
|
||||||
|
{
|
||||||
|
ReplayInputHandler ReplayInputHandler { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Supports attaching a <see cref="KeyCounterCollection"/>.
|
||||||
|
/// Keys will be populated automatically and a receptor will be injected inside.
|
||||||
|
/// </summary>
|
||||||
public interface ICanAttachKeyCounter
|
public interface ICanAttachKeyCounter
|
||||||
{
|
{
|
||||||
void Attach(KeyCounterCollection keyCounter);
|
void Attach(KeyCounterCollection keyCounter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,9 +55,9 @@ namespace osu.Game.Rulesets.UI
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether to reverse the scrolling direction is reversed.
|
/// Whether to reverse the scrolling direction is reversed. Note that this does _not_ invert the hit objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly BindableBool Reversed = new BindableBool();
|
protected readonly BindableBool Reversed = new BindableBool();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The container that contains the <see cref="SpeedAdjustmentContainer"/>s and <see cref="DrawableHitObject"/>s.
|
/// The container that contains the <see cref="SpeedAdjustmentContainer"/>s and <see cref="DrawableHitObject"/>s.
|
||||||
@ -151,7 +151,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly BindableBool Reversed = new BindableBool();
|
public readonly BindableBool Reversed = new BindableBool();
|
||||||
|
|
||||||
private readonly Container<SpeedAdjustmentContainer> speedAdjustments;
|
private readonly SortedContainer speedAdjustments;
|
||||||
public IReadOnlyList<SpeedAdjustmentContainer> SpeedAdjustments => speedAdjustments;
|
public IReadOnlyList<SpeedAdjustmentContainer> SpeedAdjustments => speedAdjustments;
|
||||||
|
|
||||||
private readonly SpeedAdjustmentContainer defaultSpeedAdjustment;
|
private readonly SpeedAdjustmentContainer defaultSpeedAdjustment;
|
||||||
@ -166,14 +166,15 @@ namespace osu.Game.Rulesets.UI
|
|||||||
{
|
{
|
||||||
this.scrollingAxes = scrollingAxes;
|
this.scrollingAxes = scrollingAxes;
|
||||||
|
|
||||||
AddInternal(speedAdjustments = new Container<SpeedAdjustmentContainer> { RelativeSizeAxes = Axes.Both });
|
AddInternal(speedAdjustments = new SortedContainer { RelativeSizeAxes = Axes.Both });
|
||||||
|
|
||||||
// Default speed adjustment
|
// Default speed adjustment
|
||||||
AddSpeedAdjustment(defaultSpeedAdjustment = new SpeedAdjustmentContainer(new MultiplierControlPoint(0)));
|
AddSpeedAdjustment(defaultSpeedAdjustment = new SpeedAdjustmentContainer(new MultiplierControlPoint(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a <see cref="SpeedAdjustmentContainer"/> to this container.
|
/// Adds a <see cref="SpeedAdjustmentContainer"/> to this container, re-sorting all hit objects
|
||||||
|
/// in the last <see cref="SpeedAdjustmentContainer"/> that occurred (time-wise) before it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="speedAdjustment">The <see cref="SpeedAdjustmentContainer"/>.</param>
|
/// <param name="speedAdjustment">The <see cref="SpeedAdjustmentContainer"/>.</param>
|
||||||
public void AddSpeedAdjustment(SpeedAdjustmentContainer speedAdjustment)
|
public void AddSpeedAdjustment(SpeedAdjustmentContainer speedAdjustment)
|
||||||
@ -181,26 +182,27 @@ namespace osu.Game.Rulesets.UI
|
|||||||
speedAdjustment.ScrollingAxes = scrollingAxes;
|
speedAdjustment.ScrollingAxes = scrollingAxes;
|
||||||
speedAdjustment.VisibleTimeRange.BindTo(VisibleTimeRange);
|
speedAdjustment.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||||
speedAdjustment.Reversed.BindTo(Reversed);
|
speedAdjustment.Reversed.BindTo(Reversed);
|
||||||
speedAdjustments.Add(speedAdjustment);
|
|
||||||
|
|
||||||
// We now need to re-sort the hit objects in the last speed adjustment prior to this one, to see if they need a new parent
|
if (speedAdjustments.Count > 0)
|
||||||
var previousSpeedAdjustment = speedAdjustments.LastOrDefault(s => s != speedAdjustment && s.ControlPoint.StartTime <= speedAdjustment.ControlPoint.StartTime);
|
|
||||||
if (previousSpeedAdjustment == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (int i = 0; i < previousSpeedAdjustment.Children.Count; i++)
|
|
||||||
{
|
{
|
||||||
DrawableHitObject hitObject = previousSpeedAdjustment[i];
|
// We need to re-sort all hit objects in the speed adjustment container prior to figure out if they
|
||||||
|
// should now lie within this one
|
||||||
|
var existingAdjustment = adjustmentContainerAt(speedAdjustment.ControlPoint.StartTime);
|
||||||
|
for (int i = 0; i < existingAdjustment.Count; i++)
|
||||||
|
{
|
||||||
|
DrawableHitObject hitObject = existingAdjustment[i];
|
||||||
|
|
||||||
var newSpeedAdjustment = adjustmentContainerFor(hitObject);
|
if (!speedAdjustment.CanContain(hitObject.HitObject.StartTime))
|
||||||
if (newSpeedAdjustment == previousSpeedAdjustment)
|
continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
previousSpeedAdjustment.Remove(hitObject);
|
existingAdjustment.Remove(hitObject);
|
||||||
newSpeedAdjustment.Add(hitObject);
|
speedAdjustment.Add(hitObject);
|
||||||
|
|
||||||
i--;
|
i--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
speedAdjustments.Add(speedAdjustment);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -237,27 +239,32 @@ namespace osu.Game.Rulesets.UI
|
|||||||
if (!(hitObject is IScrollingHitObject))
|
if (!(hitObject is IScrollingHitObject))
|
||||||
throw new InvalidOperationException($"Hit objects added to a {nameof(ScrollingHitObjectContainer)} must implement {nameof(IScrollingHitObject)}.");
|
throw new InvalidOperationException($"Hit objects added to a {nameof(ScrollingHitObjectContainer)} must implement {nameof(IScrollingHitObject)}.");
|
||||||
|
|
||||||
adjustmentContainerFor(hitObject).Add(hitObject);
|
adjustmentContainerAt(hitObject.HitObject.StartTime).Add(hitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Remove(DrawableHitObject hitObject) => speedAdjustments.Any(s => s.Remove(hitObject));
|
public override bool Remove(DrawableHitObject hitObject) => speedAdjustments.Any(s => s.Remove(hitObject));
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finds the <see cref="SpeedAdjustmentContainer"/> which provides the speed adjustment active at the start time
|
|
||||||
/// of a hit object. If there is no <see cref="SpeedAdjustmentContainer"/> active at the start time of the hit object,
|
|
||||||
/// then the first (time-wise) speed adjustment is returned.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hitObject">The hit object to find the active <see cref="SpeedAdjustmentContainer"/> for.</param>
|
|
||||||
/// <returns>The <see cref="SpeedAdjustmentContainer"/> active at <paramref name="hitObject"/>'s start time. Null if there are no speed adjustments.</returns>
|
|
||||||
private SpeedAdjustmentContainer adjustmentContainerFor(DrawableHitObject hitObject) => speedAdjustments.LastOrDefault(c => c.CanContain(hitObject)) ?? defaultSpeedAdjustment;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the <see cref="SpeedAdjustmentContainer"/> which provides the speed adjustment active at a time.
|
/// Finds the <see cref="SpeedAdjustmentContainer"/> which provides the speed adjustment active at a time.
|
||||||
/// If there is no <see cref="SpeedAdjustmentContainer"/> active at the time, then the first (time-wise) speed adjustment is returned.
|
/// If there is no <see cref="SpeedAdjustmentContainer"/> active at the time, then the first (time-wise) speed adjustment is returned.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="time">The time to find the active <see cref="SpeedAdjustmentContainer"/> at.</param>
|
/// <param name="time">The time to find the active <see cref="SpeedAdjustmentContainer"/> at.</param>
|
||||||
/// <returns>The <see cref="SpeedAdjustmentContainer"/> active at <paramref name="time"/>. Null if there are no speed adjustments.</returns>
|
/// <returns>The <see cref="SpeedAdjustmentContainer"/> active at <paramref name="time"/>. Null if there are no speed adjustments.</returns>
|
||||||
private SpeedAdjustmentContainer adjustmentContainerAt(double time) => speedAdjustments.LastOrDefault(c => c.CanContain(time)) ?? defaultSpeedAdjustment;
|
private SpeedAdjustmentContainer adjustmentContainerAt(double time) => speedAdjustments.FirstOrDefault(c => c.CanContain(time)) ?? defaultSpeedAdjustment;
|
||||||
|
|
||||||
|
private class SortedContainer : Container<SpeedAdjustmentContainer>
|
||||||
|
{
|
||||||
|
protected override int Compare(Drawable x, Drawable y)
|
||||||
|
{
|
||||||
|
var sX = (SpeedAdjustmentContainer)x;
|
||||||
|
var sY = (SpeedAdjustmentContainer)y;
|
||||||
|
|
||||||
|
int result = sY.ControlPoint.StartTime.CompareTo(sX.ControlPoint.StartTime);
|
||||||
|
if (result != 0)
|
||||||
|
return result;
|
||||||
|
return base.Compare(y, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,140 +0,0 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
using OpenTK.Input;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Configuration;
|
|
||||||
using osu.Framework.Input;
|
|
||||||
using osu.Framework.Timing;
|
|
||||||
using osu.Game.Configuration;
|
|
||||||
using osu.Game.Input.Handlers;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
|
||||||
{
|
|
||||||
public class PlayerInputManager : PassThroughInputManager
|
|
||||||
{
|
|
||||||
private ManualClock clock;
|
|
||||||
private IFrameBasedClock parentClock;
|
|
||||||
|
|
||||||
private ReplayInputHandler replayInputHandler;
|
|
||||||
public ReplayInputHandler ReplayInputHandler
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return replayInputHandler;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (replayInputHandler != null) RemoveHandler(replayInputHandler);
|
|
||||||
|
|
||||||
replayInputHandler = value;
|
|
||||||
UseParentState = replayInputHandler == null;
|
|
||||||
|
|
||||||
if (replayInputHandler != null)
|
|
||||||
AddHandler(replayInputHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Bindable<bool> mouseDisabled;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuConfigManager config)
|
|
||||||
{
|
|
||||||
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
//our clock will now be our parent's clock, but we want to replace this to allow manual control.
|
|
||||||
parentClock = Clock;
|
|
||||||
|
|
||||||
Clock = new FramedClock(clock = new ManualClock
|
|
||||||
{
|
|
||||||
CurrentTime = parentClock.CurrentTime,
|
|
||||||
Rate = parentClock.Rate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether we running up-to-date with our parent clock.
|
|
||||||
/// If not, we will need to keep processing children until we catch up.
|
|
||||||
/// </summary>
|
|
||||||
private bool requireMoreUpdateLoops;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether we in a valid state (ie. should we keep processing children frames).
|
|
||||||
/// This should be set to false when the replay is, for instance, waiting for future frames to arrive.
|
|
||||||
/// </summary>
|
|
||||||
private bool validState;
|
|
||||||
|
|
||||||
protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState;
|
|
||||||
|
|
||||||
private bool isAttached => replayInputHandler != null && !UseParentState;
|
|
||||||
|
|
||||||
private const int max_catch_up_updates_per_frame = 50;
|
|
||||||
|
|
||||||
public override bool UpdateSubTree()
|
|
||||||
{
|
|
||||||
requireMoreUpdateLoops = true;
|
|
||||||
validState = true;
|
|
||||||
|
|
||||||
int loops = 0;
|
|
||||||
|
|
||||||
while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame)
|
|
||||||
if (!base.UpdateSubTree())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
if (parentClock == null) return;
|
|
||||||
|
|
||||||
clock.Rate = parentClock.Rate;
|
|
||||||
clock.IsRunning = parentClock.IsRunning;
|
|
||||||
|
|
||||||
if (!isAttached)
|
|
||||||
{
|
|
||||||
clock.CurrentTime = parentClock.CurrentTime;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
double? newTime = replayInputHandler.SetFrameFromTime(parentClock.CurrentTime);
|
|
||||||
|
|
||||||
if (newTime == null)
|
|
||||||
{
|
|
||||||
// we shouldn't execute for this time value. probably waiting on more replay data.
|
|
||||||
validState = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
clock.CurrentTime = newTime.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime;
|
|
||||||
base.Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void TransformState(InputState state)
|
|
||||||
{
|
|
||||||
base.TransformState(state);
|
|
||||||
|
|
||||||
// we don't want to transform the state if a replay is present (for now, at least).
|
|
||||||
if (replayInputHandler != null) return;
|
|
||||||
|
|
||||||
var mouse = state.Mouse as Framework.Input.MouseState;
|
|
||||||
|
|
||||||
if (mouse != null)
|
|
||||||
{
|
|
||||||
if (mouseDisabled.Value)
|
|
||||||
{
|
|
||||||
mouse.SetPressed(MouseButton.Left, false);
|
|
||||||
mouse.SetPressed(MouseButton.Right, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -105,9 +105,10 @@
|
|||||||
<Compile Include="Overlays\Chat\ChatTabControl.cs" />
|
<Compile Include="Overlays\Chat\ChatTabControl.cs" />
|
||||||
<Compile Include="Overlays\KeyBinding\GlobalKeyBindingsSection.cs" />
|
<Compile Include="Overlays\KeyBinding\GlobalKeyBindingsSection.cs" />
|
||||||
<Compile Include="Overlays\KeyBinding\KeyBindingRow.cs" />
|
<Compile Include="Overlays\KeyBinding\KeyBindingRow.cs" />
|
||||||
<Compile Include="Overlays\KeyBinding\KeyBindingsSection.cs" />
|
<Compile Include="Overlays\KeyBinding\KeyBindingsSubsection.cs" />
|
||||||
<Compile Include="Overlays\KeyBindingOverlay.cs" />
|
<Compile Include="Overlays\KeyBindingOverlay.cs" />
|
||||||
<Compile Include="Overlays\KeyBinding\RulesetBindingsSection.cs" />
|
<Compile Include="Overlays\KeyBinding\RulesetBindingsSection.cs" />
|
||||||
|
<Compile Include="Overlays\KeyBinding\VariantBindingsSubsection.cs" />
|
||||||
<Compile Include="Overlays\MainSettings.cs" />
|
<Compile Include="Overlays\MainSettings.cs" />
|
||||||
<Compile Include="Overlays\Music\CollectionsDropdown.cs" />
|
<Compile Include="Overlays\Music\CollectionsDropdown.cs" />
|
||||||
<Compile Include="Overlays\Music\FilterControl.cs" />
|
<Compile Include="Overlays\Music\FilterControl.cs" />
|
||||||
@ -115,6 +116,7 @@
|
|||||||
<Compile Include="Overlays\Music\PlaylistList.cs" />
|
<Compile Include="Overlays\Music\PlaylistList.cs" />
|
||||||
<Compile Include="Overlays\OnScreenDisplay.cs" />
|
<Compile Include="Overlays\OnScreenDisplay.cs" />
|
||||||
<Compile Include="Graphics\Containers\SectionsContainer.cs" />
|
<Compile Include="Graphics\Containers\SectionsContainer.cs" />
|
||||||
|
<Compile Include="Graphics\UserInterface\ProgressBar.cs" />
|
||||||
<Compile Include="Overlays\Settings\Sections\Maintenance\GeneralSettings.cs" />
|
<Compile Include="Overlays\Settings\Sections\Maintenance\GeneralSettings.cs" />
|
||||||
<Compile Include="Overlays\Settings\SettingsHeader.cs" />
|
<Compile Include="Overlays\Settings\SettingsHeader.cs" />
|
||||||
<Compile Include="Overlays\Settings\Sections\Audio\MainMenuSettings.cs" />
|
<Compile Include="Overlays\Settings\Sections\Audio\MainMenuSettings.cs" />
|
||||||
@ -128,6 +130,7 @@
|
|||||||
<Compile Include="Overlays\Profile\Sections\RanksSection.cs" />
|
<Compile Include="Overlays\Profile\Sections\RanksSection.cs" />
|
||||||
<Compile Include="Overlays\Profile\Sections\RecentSection.cs" />
|
<Compile Include="Overlays\Profile\Sections\RecentSection.cs" />
|
||||||
<Compile Include="Graphics\Containers\ConstrainedIconContainer.cs" />
|
<Compile Include="Graphics\Containers\ConstrainedIconContainer.cs" />
|
||||||
|
<Compile Include="Overlays\Toolbar\ToolbarDirectButton.cs" />
|
||||||
<Compile Include="Rulesets\Mods\IApplicableToDifficulty.cs" />
|
<Compile Include="Rulesets\Mods\IApplicableToDifficulty.cs" />
|
||||||
<Compile Include="Rulesets\UI\RulesetInputManager.cs" />
|
<Compile Include="Rulesets\UI\RulesetInputManager.cs" />
|
||||||
<Compile Include="Screens\Play\KeyCounterAction.cs" />
|
<Compile Include="Screens\Play\KeyCounterAction.cs" />
|
||||||
@ -320,7 +323,6 @@
|
|||||||
<Compile Include="Screens\Multiplayer\MatchCreate.cs" />
|
<Compile Include="Screens\Multiplayer\MatchCreate.cs" />
|
||||||
<Compile Include="Screens\Play\FailOverlay.cs" />
|
<Compile Include="Screens\Play\FailOverlay.cs" />
|
||||||
<Compile Include="Screens\Play\MenuOverlay.cs" />
|
<Compile Include="Screens\Play\MenuOverlay.cs" />
|
||||||
<Compile Include="Screens\Play\PlayerInputManager.cs" />
|
|
||||||
<Compile Include="Screens\Play\PlayerLoader.cs" />
|
<Compile Include="Screens\Play\PlayerLoader.cs" />
|
||||||
<Compile Include="Screens\Play\ReplayPlayer.cs" />
|
<Compile Include="Screens\Play\ReplayPlayer.cs" />
|
||||||
<Compile Include="Screens\Play\SkipButton.cs" />
|
<Compile Include="Screens\Play\SkipButton.cs" />
|
||||||
|
Loading…
Reference in New Issue
Block a user