mirror of
https://github.com/osukey/osukey.git
synced 2025-05-20 13:07:21 +09:00
Merge remote-tracking branch 'refs/remotes/origin/master' into fix-editor-movement-crash
This commit is contained in:
commit
39489e2901
120
osu.Desktop/DiscordRichPresence.cs
Normal file
120
osu.Desktop/DiscordRichPresence.cs
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using DiscordRPC;
|
||||||
|
using DiscordRPC.Message;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Logging;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Users;
|
||||||
|
using LogLevel = osu.Framework.Logging.LogLevel;
|
||||||
|
using User = osu.Game.Users.User;
|
||||||
|
|
||||||
|
namespace osu.Desktop
|
||||||
|
{
|
||||||
|
internal class DiscordRichPresence : Component
|
||||||
|
{
|
||||||
|
private const string client_id = "367827983903490050";
|
||||||
|
|
||||||
|
private DiscordRpcClient client;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IBindable<RulesetInfo> ruleset { get; set; }
|
||||||
|
|
||||||
|
private Bindable<User> user;
|
||||||
|
|
||||||
|
private readonly IBindable<UserStatus> status = new Bindable<UserStatus>();
|
||||||
|
private readonly IBindable<UserActivity> activity = new Bindable<UserActivity>();
|
||||||
|
|
||||||
|
private readonly RichPresence presence = new RichPresence
|
||||||
|
{
|
||||||
|
Assets = new Assets { LargeImageKey = "osu_logo_lazer", }
|
||||||
|
};
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(IAPIProvider provider)
|
||||||
|
{
|
||||||
|
client = new DiscordRpcClient(client_id)
|
||||||
|
{
|
||||||
|
SkipIdenticalPresence = false // handles better on discord IPC loss, see updateStatus call in onReady.
|
||||||
|
};
|
||||||
|
|
||||||
|
client.OnReady += onReady;
|
||||||
|
client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network, LogLevel.Error);
|
||||||
|
client.OnConnectionFailed += (_, e) => Logger.Log($"An connection occurred with Discord RPC Client: {e.Type}", LoggingTarget.Network, LogLevel.Error);
|
||||||
|
|
||||||
|
(user = provider.LocalUser.GetBoundCopy()).BindValueChanged(u =>
|
||||||
|
{
|
||||||
|
status.UnbindBindings();
|
||||||
|
status.BindTo(u.NewValue.Status);
|
||||||
|
|
||||||
|
activity.UnbindBindings();
|
||||||
|
activity.BindTo(u.NewValue.Activity);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
ruleset.BindValueChanged(_ => updateStatus());
|
||||||
|
status.BindValueChanged(_ => updateStatus());
|
||||||
|
activity.BindValueChanged(_ => updateStatus());
|
||||||
|
|
||||||
|
client.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onReady(object _, ReadyMessage __)
|
||||||
|
{
|
||||||
|
Logger.Log("Discord RPC Client ready.", LoggingTarget.Network, LogLevel.Debug);
|
||||||
|
updateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateStatus()
|
||||||
|
{
|
||||||
|
if (status.Value is UserStatusOffline)
|
||||||
|
{
|
||||||
|
client.ClearPresence();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.Value is UserStatusOnline && activity.Value != null)
|
||||||
|
{
|
||||||
|
presence.State = activity.Value.Status;
|
||||||
|
presence.Details = getDetails(activity.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
presence.State = "Idle";
|
||||||
|
presence.Details = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update user information
|
||||||
|
presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty);
|
||||||
|
|
||||||
|
// update ruleset
|
||||||
|
presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_custom";
|
||||||
|
presence.Assets.SmallImageText = ruleset.Value.Name;
|
||||||
|
|
||||||
|
client.SetPresence(presence);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string getDetails(UserActivity activity)
|
||||||
|
{
|
||||||
|
switch (activity)
|
||||||
|
{
|
||||||
|
case UserActivity.SoloGame solo:
|
||||||
|
return solo.Beatmap.ToString();
|
||||||
|
|
||||||
|
case UserActivity.Editing edit:
|
||||||
|
return edit.Beatmap.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
client.Dispose();
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -60,6 +60,8 @@ namespace osu.Desktop
|
|||||||
else
|
else
|
||||||
Add(new SimpleUpdateManager());
|
Add(new SimpleUpdateManager());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LoadComponentAsync(new DiscordRichPresence(), Add);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen)
|
protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen)
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
|
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
|
||||||
|
<PackageReference Include="DiscordRichPresence" Version="1.0.121" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Resources">
|
<ItemGroup Label="Resources">
|
||||||
<EmbeddedResource Include="lazer.ico" />
|
<EmbeddedResource Include="lazer.ico" />
|
||||||
|
@ -137,10 +137,5 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
public override int? LegacyID => 2;
|
public override int? LegacyID => 2;
|
||||||
|
|
||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
|
||||||
|
|
||||||
public CatchRuleset(RulesetInfo rulesetInfo = null)
|
|
||||||
: base(rulesetInfo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModNightcore : ModNightcore
|
public class CatchModNightcore : ModNightcore<CatchHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => 1.06;
|
||||||
}
|
}
|
||||||
|
@ -186,11 +186,6 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
|
|
||||||
public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this);
|
public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this);
|
||||||
|
|
||||||
public ManiaRuleset(RulesetInfo rulesetInfo = null)
|
|
||||||
: base(rulesetInfo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IEnumerable<int> AvailableVariants
|
public override IEnumerable<int> AvailableVariants
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModNightcore : ModNightcore
|
public class ManiaModNightcore : ModNightcore<ManiaHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,21 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddAssert("is rotation absolute almost same", () => Precision.AlmostEquals(drawableSpinner.Disc.RotationAbsolute, estimatedRotation, 100));
|
AddAssert("is rotation absolute almost same", () => Precision.AlmostEquals(drawableSpinner.Disc.RotationAbsolute, estimatedRotation, 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSpinPerMinuteOnRewind()
|
||||||
|
{
|
||||||
|
double estimatedSpm = 0;
|
||||||
|
|
||||||
|
addSeekStep(2500);
|
||||||
|
AddStep("retrieve spm", () => estimatedSpm = drawableSpinner.SpmCounter.SpinsPerMinute);
|
||||||
|
|
||||||
|
addSeekStep(5000);
|
||||||
|
AddAssert("spm still valid", () => Precision.AlmostEquals(drawableSpinner.SpmCounter.SpinsPerMinute, estimatedSpm, 1.0));
|
||||||
|
|
||||||
|
addSeekStep(2500);
|
||||||
|
AddAssert("spm still valid", () => Precision.AlmostEquals(drawableSpinner.SpmCounter.SpinsPerMinute, estimatedSpm, 1.0));
|
||||||
|
}
|
||||||
|
|
||||||
private void addSeekStep(double time)
|
private void addSeekStep(double time)
|
||||||
{
|
{
|
||||||
AddStep($"seek to {time}", () => track.Seek(time));
|
AddStep($"seek to {time}", () => track.Seek(time));
|
||||||
@ -84,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
new Spinner
|
new Spinner
|
||||||
{
|
{
|
||||||
Position = new Vector2(256, 192),
|
Position = new Vector2(256, 192),
|
||||||
EndTime = 5000,
|
EndTime = 6000,
|
||||||
},
|
},
|
||||||
// placeholder object to avoid hitting the results screen
|
// placeholder object to avoid hitting the results screen
|
||||||
new HitObject
|
new HitObject
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
public class OsuModNightcore : ModNightcore
|
public class OsuModNightcore : ModNightcore<OsuHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.12;
|
public override double ScoreMultiplier => 1.12;
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
public readonly SpinnerDisc Disc;
|
public readonly SpinnerDisc Disc;
|
||||||
public readonly SpinnerTicks Ticks;
|
public readonly SpinnerTicks Ticks;
|
||||||
private readonly SpinnerSpmCounter spmCounter;
|
public readonly SpinnerSpmCounter SpmCounter;
|
||||||
|
|
||||||
private readonly Container mainContainer;
|
private readonly Container mainContainer;
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
spmCounter = new SpinnerSpmCounter
|
SpmCounter = new SpinnerSpmCounter
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -177,8 +177,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
Disc.Tracking = OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false;
|
Disc.Tracking = OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false;
|
||||||
if (!spmCounter.IsPresent && Disc.Tracking)
|
if (!SpmCounter.IsPresent && Disc.Tracking)
|
||||||
spmCounter.FadeIn(HitObject.TimeFadeIn);
|
SpmCounter.FadeIn(HitObject.TimeFadeIn);
|
||||||
|
|
||||||
base.Update();
|
base.Update();
|
||||||
}
|
}
|
||||||
@ -189,7 +189,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
circle.Rotation = Disc.Rotation;
|
circle.Rotation = Disc.Rotation;
|
||||||
Ticks.Rotation = Disc.Rotation;
|
Ticks.Rotation = Disc.Rotation;
|
||||||
spmCounter.SetRotation(Disc.RotationAbsolute);
|
SpmCounter.SetRotation(Disc.RotationAbsolute);
|
||||||
|
|
||||||
float relativeCircleScale = Spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight;
|
float relativeCircleScale = Spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight;
|
||||||
Disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint);
|
Disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint);
|
||||||
|
@ -24,16 +24,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
private OsuRulesetConfigManager config { get; set; }
|
private OsuRulesetConfigManager config { get; set; }
|
||||||
|
|
||||||
private Slider slider;
|
private Slider slider;
|
||||||
private float defaultPathRadius;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skin)
|
private void load(ISkinSource skin)
|
||||||
{
|
{
|
||||||
slider = (Slider)drawableObject.HitObject;
|
slider = (Slider)drawableObject.HitObject;
|
||||||
defaultPathRadius = skin.GetConfig<OsuSkinConfiguration, float>(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS;
|
|
||||||
|
|
||||||
scaleBindable = slider.ScaleBindable.GetBoundCopy();
|
scaleBindable = slider.ScaleBindable.GetBoundCopy();
|
||||||
scaleBindable.BindValueChanged(_ => updatePathRadius(), true);
|
scaleBindable.BindValueChanged(scale => PathRadius = OsuHitObject.OBJECT_RADIUS * scale.NewValue, true);
|
||||||
|
|
||||||
pathVersion = slider.Path.Version.GetBoundCopy();
|
pathVersion = slider.Path.Version.GetBoundCopy();
|
||||||
pathVersion.BindValueChanged(_ => Refresh());
|
pathVersion.BindValueChanged(_ => Refresh());
|
||||||
@ -48,9 +46,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
BorderColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBorder)?.Value ?? Color4.White;
|
BorderColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBorder)?.Value ?? Color4.White;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePathRadius()
|
|
||||||
=> PathRadius = defaultPathRadius * scaleBindable.Value;
|
|
||||||
|
|
||||||
private void updateAccentColour(ISkinSource skin, Color4 defaultAccentColour)
|
private void updateAccentColour(ISkinSource skin, Color4 defaultAccentColour)
|
||||||
=> AccentColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderTrackOverride)?.Value ?? defaultAccentColour;
|
=> AccentColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderTrackOverride)?.Value ?? defaultAccentColour;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
@ -62,6 +63,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
|
|
||||||
public void SetRotation(float currentRotation)
|
public void SetRotation(float currentRotation)
|
||||||
{
|
{
|
||||||
|
// Never calculate SPM by same time of record to avoid 0 / 0 = NaN or X / 0 = Infinity result.
|
||||||
|
if (Precision.AlmostEquals(0, Time.Elapsed))
|
||||||
|
return;
|
||||||
|
|
||||||
// If we've gone back in time, it's fine to work with a fresh set of records for now
|
// If we've gone back in time, it's fine to work with a fresh set of records for now
|
||||||
if (records.Count > 0 && Time.Current < records.Last().Time)
|
if (records.Count > 0 && Time.Current < records.Last().Time)
|
||||||
records.Clear();
|
records.Clear();
|
||||||
@ -71,6 +76,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
var record = records.Peek();
|
var record = records.Peek();
|
||||||
while (records.Count > 0 && Time.Current - records.Peek().Time > spm_count_duration)
|
while (records.Count > 0 && Time.Current - records.Peek().Time > spm_count_duration)
|
||||||
record = records.Dequeue();
|
record = records.Dequeue();
|
||||||
|
|
||||||
SpinsPerMinute = (currentRotation - record.Rotation) / (Time.Current - record.Time) * 1000 * 60 / 360;
|
SpinsPerMinute = (currentRotation - record.Rotation) / (Time.Current - record.Time) * 1000 * 60 / 360;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,10 +183,5 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame();
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame();
|
||||||
|
|
||||||
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo);
|
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo);
|
||||||
|
|
||||||
public OsuRuleset(RulesetInfo rulesetInfo = null)
|
|
||||||
: base(rulesetInfo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -15,19 +16,27 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
private class LegacyDrawableSliderPath : DrawableSliderPath
|
private class LegacyDrawableSliderPath : DrawableSliderPath
|
||||||
{
|
{
|
||||||
|
private const float shadow_portion = 1 - (OsuLegacySkinTransformer.LEGACY_CIRCLE_RADIUS / OsuHitObject.OBJECT_RADIUS);
|
||||||
|
|
||||||
public new Color4 AccentColour => new Color4(base.AccentColour.R, base.AccentColour.G, base.AccentColour.B, base.AccentColour.A * 0.70f);
|
public new Color4 AccentColour => new Color4(base.AccentColour.R, base.AccentColour.G, base.AccentColour.B, base.AccentColour.A * 0.70f);
|
||||||
|
|
||||||
protected override Color4 ColourAt(float position)
|
protected override Color4 ColourAt(float position)
|
||||||
{
|
{
|
||||||
if (CalculatedBorderPortion != 0f && position <= CalculatedBorderPortion)
|
float realBorderPortion = shadow_portion + CalculatedBorderPortion;
|
||||||
|
float realGradientPortion = 1 - realBorderPortion;
|
||||||
|
|
||||||
|
if (position <= shadow_portion)
|
||||||
|
return new Color4(0f, 0f, 0f, 0.25f * position / shadow_portion);
|
||||||
|
|
||||||
|
if (position <= realBorderPortion)
|
||||||
return BorderColour;
|
return BorderColour;
|
||||||
|
|
||||||
position -= BORDER_PORTION;
|
position -= realBorderPortion;
|
||||||
|
|
||||||
Color4 outerColour = AccentColour.Darken(0.1f);
|
Color4 outerColour = AccentColour.Darken(0.1f);
|
||||||
Color4 innerColour = lighten(AccentColour, 0.5f);
|
Color4 innerColour = lighten(AccentColour, 0.5f);
|
||||||
|
|
||||||
return Interpolation.ValueAt(position / GRADIENT_PORTION, outerColour, innerColour, 0, 1);
|
return Interpolation.ValueAt(position / realGradientPortion, outerColour, innerColour, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
/// Their hittable area is 128px, but the actual circle portion is 118px.
|
/// Their hittable area is 128px, but the actual circle portion is 118px.
|
||||||
/// We must account for some gameplay elements such as slider bodies, where this padding is not present.
|
/// We must account for some gameplay elements such as slider bodies, where this padding is not present.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float legacy_circle_radius = 64 - 5;
|
public const float LEGACY_CIRCLE_RADIUS = 64 - 5;
|
||||||
|
|
||||||
public OsuLegacySkinTransformer(ISkinSource source)
|
public OsuLegacySkinTransformer(ISkinSource source)
|
||||||
{
|
{
|
||||||
@ -130,7 +130,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
case OsuSkinConfiguration.SliderPathRadius:
|
case OsuSkinConfiguration.SliderPathRadius:
|
||||||
if (hasHitCircle.Value)
|
if (hasHitCircle.Value)
|
||||||
return SkinUtils.As<TValue>(new BindableFloat(legacy_circle_radius));
|
return SkinUtils.As<TValue>(new BindableFloat(LEGACY_CIRCLE_RADIUS));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Mods
|
namespace osu.Game.Rulesets.Taiko.Mods
|
||||||
{
|
{
|
||||||
public class TaikoModNightcore : ModNightcore
|
public class TaikoModNightcore : ModNightcore<TaikoHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.12;
|
public override double ScoreMultiplier => 1.12;
|
||||||
}
|
}
|
||||||
|
@ -136,10 +136,5 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
public override int? LegacyID => 1;
|
public override int? LegacyID => 1;
|
||||||
|
|
||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame();
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame();
|
||||||
|
|
||||||
public TaikoRuleset(RulesetInfo rulesetInfo = null)
|
|
||||||
: base(rulesetInfo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
54
osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs
Normal file
54
osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Formats;
|
||||||
|
using osu.Game.IO;
|
||||||
|
using osu.Game.IO.Serialization;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Beatmaps.Formats
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class LegacyBeatmapEncoderTest
|
||||||
|
{
|
||||||
|
private const string normal = "Soleily - Renatus (Gamu) [Insane].osu";
|
||||||
|
|
||||||
|
private static IEnumerable<string> allBeatmaps => TestResources.GetStore().GetAvailableResources().Where(res => res.EndsWith(".osu"));
|
||||||
|
|
||||||
|
[TestCaseSource(nameof(allBeatmaps))]
|
||||||
|
public void TestDecodeEncodedBeatmap(string name)
|
||||||
|
{
|
||||||
|
var decoded = decode(normal, out var encoded);
|
||||||
|
|
||||||
|
Assert.That(decoded.HitObjects.Count, Is.EqualTo(encoded.HitObjects.Count));
|
||||||
|
Assert.That(encoded.Serialize(), Is.EqualTo(decoded.Serialize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Beatmap decode(string filename, out Beatmap encoded)
|
||||||
|
{
|
||||||
|
using (var stream = TestResources.OpenResource(filename))
|
||||||
|
using (var sr = new LineBufferedReader(stream))
|
||||||
|
{
|
||||||
|
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr);
|
||||||
|
|
||||||
|
using (var ms = new MemoryStream())
|
||||||
|
using (var sw = new StreamWriter(ms))
|
||||||
|
using (var sr2 = new LineBufferedReader(ms))
|
||||||
|
{
|
||||||
|
new LegacyBeatmapEncoder(legacyDecoded).Encode(sw);
|
||||||
|
sw.Flush();
|
||||||
|
|
||||||
|
ms.Position = 0;
|
||||||
|
|
||||||
|
encoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr2);
|
||||||
|
return legacyDecoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,9 @@ namespace osu.Game.Tests.Resources
|
|||||||
{
|
{
|
||||||
public static class TestResources
|
public static class TestResources
|
||||||
{
|
{
|
||||||
public static Stream OpenResource(string name) => new DllResourceStore("osu.Game.Tests.dll").GetStream($"Resources/{name}");
|
public static DllResourceStore GetStore() => new DllResourceStore("osu.Game.Tests.dll");
|
||||||
|
|
||||||
|
public static Stream OpenResource(string name) => GetStore().GetStream($"Resources/{name}");
|
||||||
|
|
||||||
public static Stream GetTestBeatmapStream(bool virtualTrack = false) => new DllResourceStore("osu.Game.Resources.dll").GetStream($"Beatmaps/241526 Soleily - Renatus{(virtualTrack ? "_virtual" : "")}.osz");
|
public static Stream GetTestBeatmapStream(bool virtualTrack = false) => new DllResourceStore("osu.Game.Resources.dll").GetStream($"Beatmaps/241526 Soleily - Renatus{(virtualTrack ? "_virtual" : "")}.osz");
|
||||||
|
|
||||||
|
@ -194,11 +194,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
private class TestScrollingRuleset : Ruleset
|
private class TestScrollingRuleset : Ruleset
|
||||||
{
|
{
|
||||||
public TestScrollingRuleset(RulesetInfo rulesetInfo = null)
|
|
||||||
: base(rulesetInfo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IEnumerable<Mod> GetModsFor(ModType type) => throw new NotImplementedException();
|
public override IEnumerable<Mod> GetModsFor(ModType type) => throw new NotImplementedException();
|
||||||
|
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new TestDrawableScrollingRuleset(this, beatmap, mods);
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new TestDrawableScrollingRuleset(this, beatmap, mods);
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Tests.Visual.UserInterface;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
public class TestSceneNightcoreBeatContainer : TestSceneBeatSyncedContainer
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(ModNightcore<>)
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||||
|
|
||||||
|
Beatmap.Value.Track.Start();
|
||||||
|
Beatmap.Value.Track.Seek(Beatmap.Value.Beatmap.HitObjects.First().StartTime - 1000);
|
||||||
|
|
||||||
|
Add(new ModNightcore<HitObject>.NightcoreBeatContainer());
|
||||||
|
|
||||||
|
AddStep("change signature to quadruple", () => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.ForEach(p => p.TimeSignature = TimeSignatures.SimpleQuadruple));
|
||||||
|
AddStep("change signature to triple", () => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.ForEach(p => p.TimeSignature = TimeSignatures.SimpleTriple));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +1,66 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.News;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Online
|
namespace osu.Game.Tests.Visual.Online
|
||||||
{
|
{
|
||||||
public class TestSceneNewsOverlay : OsuTestScene
|
public class TestSceneNewsOverlay : OsuTestScene
|
||||||
{
|
{
|
||||||
private NewsOverlay news;
|
private TestNewsOverlay news;
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
Add(news = new NewsOverlay());
|
Add(news = new TestNewsOverlay());
|
||||||
AddStep(@"Show", news.Show);
|
AddStep(@"Show", news.Show);
|
||||||
AddStep(@"Hide", news.Hide);
|
AddStep(@"Hide", news.Hide);
|
||||||
|
|
||||||
AddStep(@"Show front page", () => news.ShowFrontPage());
|
AddStep(@"Show front page", () => news.ShowFrontPage());
|
||||||
AddStep(@"Custom article", () => news.Current.Value = "Test Article 101");
|
AddStep(@"Custom article", () => news.Current.Value = "Test Article 101");
|
||||||
|
|
||||||
|
AddStep(@"Article covers", () => news.LoadAndShowContent(new NewsCoverTest()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestNewsOverlay : NewsOverlay
|
||||||
|
{
|
||||||
|
public new void LoadAndShowContent(NewsContent content) => base.LoadAndShowContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NewsCoverTest : NewsContent
|
||||||
|
{
|
||||||
|
public NewsCoverTest()
|
||||||
|
{
|
||||||
|
Spacing = new osuTK.Vector2(0, 10);
|
||||||
|
|
||||||
|
var article = new NewsArticleCover.ArticleInfo
|
||||||
|
{
|
||||||
|
Author = "Ephemeral",
|
||||||
|
CoverUrl = "https://assets.ppy.sh/artists/58/header.jpg",
|
||||||
|
Time = new DateTime(2019, 12, 4),
|
||||||
|
Title = "New Featured Artist: Kurokotei"
|
||||||
|
};
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new NewsArticleCover(article)
|
||||||
|
{
|
||||||
|
Height = 200
|
||||||
|
},
|
||||||
|
new NewsArticleCover(article)
|
||||||
|
{
|
||||||
|
Height = 120
|
||||||
|
},
|
||||||
|
new NewsArticleCover(article)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.None,
|
||||||
|
Size = new osuTK.Vector2(400, 200),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,9 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -20,8 +23,10 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(BeatmapDetails) };
|
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(BeatmapDetails) };
|
||||||
|
|
||||||
|
private ModDisplay modDisplay;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuGameBase game)
|
private void load(OsuGameBase game, RulesetStore rulesets)
|
||||||
{
|
{
|
||||||
BeatmapDetailArea detailsArea;
|
BeatmapDetailArea detailsArea;
|
||||||
Add(detailsArea = new BeatmapDetailArea
|
Add(detailsArea = new BeatmapDetailArea
|
||||||
@ -31,6 +36,16 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Size = new Vector2(550f, 450f),
|
Size = new Vector2(550f, 450f),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Add(modDisplay = new ModDisplay
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Position = new Vector2(0, 25),
|
||||||
|
});
|
||||||
|
|
||||||
|
modDisplay.Current.BindTo(SelectedMods);
|
||||||
|
|
||||||
AddStep("all metrics", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
AddStep("all metrics", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
BeatmapInfo =
|
BeatmapInfo =
|
||||||
@ -163,6 +178,60 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
AddStep("null beatmap", () => detailsArea.Beatmap = null);
|
AddStep("null beatmap", () => detailsArea.Beatmap = null);
|
||||||
|
|
||||||
|
Ruleset ruleset = rulesets.AvailableRulesets.First().CreateInstance();
|
||||||
|
|
||||||
|
AddStep("with EZ mod", () =>
|
||||||
|
{
|
||||||
|
detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
BeatmapInfo =
|
||||||
|
{
|
||||||
|
Version = "Has Easy Mod",
|
||||||
|
Metadata = new BeatmapMetadata
|
||||||
|
{
|
||||||
|
Source = "osu!lazer",
|
||||||
|
Tags = "this beatmap has the easy mod enabled",
|
||||||
|
},
|
||||||
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
|
{
|
||||||
|
CircleSize = 3,
|
||||||
|
DrainRate = 3,
|
||||||
|
OverallDifficulty = 3,
|
||||||
|
ApproachRate = 3,
|
||||||
|
},
|
||||||
|
StarDifficulty = 1f,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModEasy) };
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("with HR mod", () =>
|
||||||
|
{
|
||||||
|
detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
BeatmapInfo =
|
||||||
|
{
|
||||||
|
Version = "Has Hard Rock Mod",
|
||||||
|
Metadata = new BeatmapMetadata
|
||||||
|
{
|
||||||
|
Source = "osu!lazer",
|
||||||
|
Tags = "this beatmap has the hard rock mod enabled",
|
||||||
|
},
|
||||||
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
|
{
|
||||||
|
CircleSize = 3,
|
||||||
|
DrainRate = 3,
|
||||||
|
OverallDifficulty = 3,
|
||||||
|
ApproachRate = 3,
|
||||||
|
},
|
||||||
|
StarDifficulty = 1f,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModHardRock) };
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
private class DummyRulesetInfo : RulesetInfo
|
private class DummyRulesetInfo : RulesetInfo
|
||||||
{
|
{
|
||||||
public override Ruleset CreateInstance() => new DummyRuleset(this);
|
public override Ruleset CreateInstance() => new DummyRuleset();
|
||||||
|
|
||||||
private class DummyRuleset : Ruleset
|
private class DummyRuleset : Ruleset
|
||||||
{
|
{
|
||||||
@ -70,11 +70,6 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public override string ShortName => "dummy";
|
public override string ShortName => "dummy";
|
||||||
|
|
||||||
public DummyRuleset(RulesetInfo rulesetInfo = null)
|
|
||||||
: base(rulesetInfo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DummyBeatmapConverter : IBeatmapConverter
|
private class DummyBeatmapConverter : IBeatmapConverter
|
||||||
{
|
{
|
||||||
public event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
|
public event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
|
||||||
|
@ -10,6 +10,7 @@ using osu.Game.Beatmaps.Timing;
|
|||||||
using osu.Game.Rulesets.Objects.Legacy;
|
using osu.Game.Rulesets.Objects.Legacy;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
|
using osu.Game.Beatmaps.Legacy;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Formats
|
namespace osu.Game.Beatmaps.Formats
|
||||||
{
|
{
|
||||||
@ -293,22 +294,22 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
string[] split = line.Split(',');
|
string[] split = line.Split(',');
|
||||||
|
|
||||||
if (!Enum.TryParse(split[0], out EventType type))
|
if (!Enum.TryParse(split[0], out LegacyEventType type))
|
||||||
throw new InvalidDataException($@"Unknown event type: {split[0]}");
|
throw new InvalidDataException($@"Unknown event type: {split[0]}");
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case EventType.Background:
|
case LegacyEventType.Background:
|
||||||
string bgFilename = split[2].Trim('"');
|
string bgFilename = split[2].Trim('"');
|
||||||
beatmap.BeatmapInfo.Metadata.BackgroundFile = bgFilename.ToStandardisedPath();
|
beatmap.BeatmapInfo.Metadata.BackgroundFile = bgFilename.ToStandardisedPath();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EventType.Video:
|
case LegacyEventType.Video:
|
||||||
string videoFilename = split[2].Trim('"');
|
string videoFilename = split[2].Trim('"');
|
||||||
beatmap.BeatmapInfo.Metadata.VideoFile = videoFilename.ToStandardisedPath();
|
beatmap.BeatmapInfo.Metadata.VideoFile = videoFilename.ToStandardisedPath();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EventType.Break:
|
case LegacyEventType.Break:
|
||||||
double start = getOffsetTime(Parsing.ParseDouble(split[1]));
|
double start = getOffsetTime(Parsing.ParseDouble(split[1]));
|
||||||
|
|
||||||
var breakEvent = new BreakPeriod
|
var breakEvent = new BreakPeriod
|
||||||
@ -358,9 +359,9 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
if (split.Length >= 8)
|
if (split.Length >= 8)
|
||||||
{
|
{
|
||||||
EffectFlags effectFlags = (EffectFlags)Parsing.ParseInt(split[7]);
|
LegacyEffectFlags effectFlags = (LegacyEffectFlags)Parsing.ParseInt(split[7]);
|
||||||
kiaiMode = effectFlags.HasFlag(EffectFlags.Kiai);
|
kiaiMode = effectFlags.HasFlag(LegacyEffectFlags.Kiai);
|
||||||
omitFirstBarSignature = effectFlags.HasFlag(EffectFlags.OmitFirstBarLine);
|
omitFirstBarSignature = effectFlags.HasFlag(LegacyEffectFlags.OmitFirstBarLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
string stringSampleSet = sampleSet.ToString().ToLowerInvariant();
|
string stringSampleSet = sampleSet.ToString().ToLowerInvariant();
|
||||||
@ -448,13 +449,5 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0);
|
private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0);
|
||||||
|
|
||||||
protected virtual TimingControlPoint CreateTimingControlPoint() => new TimingControlPoint();
|
protected virtual TimingControlPoint CreateTimingControlPoint() => new TimingControlPoint();
|
||||||
|
|
||||||
[Flags]
|
|
||||||
internal enum EffectFlags
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Kiai = 1,
|
|
||||||
OmitFirstBarLine = 8
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
410
osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
Normal file
410
osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
Normal file
@ -0,0 +1,410 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Beatmaps.Legacy;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Formats
|
||||||
|
{
|
||||||
|
public class LegacyBeatmapEncoder
|
||||||
|
{
|
||||||
|
public const int LATEST_VERSION = 128;
|
||||||
|
|
||||||
|
private readonly IBeatmap beatmap;
|
||||||
|
|
||||||
|
public LegacyBeatmapEncoder(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
this.beatmap = beatmap;
|
||||||
|
|
||||||
|
if (beatmap.BeatmapInfo.RulesetID < 0 || beatmap.BeatmapInfo.RulesetID > 3)
|
||||||
|
throw new ArgumentException("Only beatmaps in the osu, taiko, catch, or mania rulesets can be encoded to the legacy beatmap format.", nameof(beatmap));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Encode(TextWriter writer)
|
||||||
|
{
|
||||||
|
writer.WriteLine($"osu file format v{LATEST_VERSION}");
|
||||||
|
|
||||||
|
writer.WriteLine();
|
||||||
|
handleGeneral(writer);
|
||||||
|
|
||||||
|
writer.WriteLine();
|
||||||
|
handleEditor(writer);
|
||||||
|
|
||||||
|
writer.WriteLine();
|
||||||
|
handleMetadata(writer);
|
||||||
|
|
||||||
|
writer.WriteLine();
|
||||||
|
handleDifficulty(writer);
|
||||||
|
|
||||||
|
writer.WriteLine();
|
||||||
|
handleEvents(writer);
|
||||||
|
|
||||||
|
writer.WriteLine();
|
||||||
|
handleTimingPoints(writer);
|
||||||
|
|
||||||
|
writer.WriteLine();
|
||||||
|
handleHitObjects(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleGeneral(TextWriter writer)
|
||||||
|
{
|
||||||
|
writer.WriteLine("[General]");
|
||||||
|
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"AudioFilename: {Path.GetFileName(beatmap.Metadata.AudioFile)}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"AudioLeadIn: {beatmap.BeatmapInfo.AudioLeadIn}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}"));
|
||||||
|
// Todo: Not all countdown types are supported by lazer yet
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"Countdown: {(beatmap.BeatmapInfo.Countdown ? '1' : '0')}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank(beatmap.ControlPointInfo.SamplePoints[0].SampleBank)}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.BeatmapInfo.StackLeniency}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"Mode: {beatmap.BeatmapInfo.RulesetID}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.BeatmapInfo.LetterboxInBreaks ? '1' : '0')}"));
|
||||||
|
// if (beatmap.BeatmapInfo.UseSkinSprites)
|
||||||
|
// writer.WriteLine(@"UseSkinSprites: 1");
|
||||||
|
// if (b.AlwaysShowPlayfield)
|
||||||
|
// writer.WriteLine(@"AlwaysShowPlayfield: 1");
|
||||||
|
// if (b.OverlayPosition != OverlayPosition.NoChange)
|
||||||
|
// writer.WriteLine(@"OverlayPosition: " + b.OverlayPosition);
|
||||||
|
// if (!string.IsNullOrEmpty(b.SkinPreference))
|
||||||
|
// writer.WriteLine(@"SkinPreference:" + b.SkinPreference);
|
||||||
|
// if (b.EpilepsyWarning)
|
||||||
|
// writer.WriteLine(@"EpilepsyWarning: 1");
|
||||||
|
// if (b.CountdownOffset > 0)
|
||||||
|
// writer.WriteLine(@"CountdownOffset: " + b.CountdownOffset.ToString());
|
||||||
|
if (beatmap.BeatmapInfo.RulesetID == 3)
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"SpecialStyle: {(beatmap.BeatmapInfo.SpecialStyle ? '1' : '0')}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"WidescreenStoryboard: {(beatmap.BeatmapInfo.WidescreenStoryboard ? '1' : '0')}"));
|
||||||
|
// if (b.SamplesMatchPlaybackRate)
|
||||||
|
// writer.WriteLine(@"SamplesMatchPlaybackRate: 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleEditor(TextWriter writer)
|
||||||
|
{
|
||||||
|
writer.WriteLine("[Editor]");
|
||||||
|
|
||||||
|
if (beatmap.BeatmapInfo.Bookmarks.Length > 0)
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"Bookmarks: {string.Join(',', beatmap.BeatmapInfo.Bookmarks)}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"DistanceSpacing: {beatmap.BeatmapInfo.DistanceSpacing}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"BeatDivisor: {beatmap.BeatmapInfo.BeatDivisor}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"GridSize: {beatmap.BeatmapInfo.GridSize}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"TimelineZoom: {beatmap.BeatmapInfo.TimelineZoom}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleMetadata(TextWriter writer)
|
||||||
|
{
|
||||||
|
writer.WriteLine("[Metadata]");
|
||||||
|
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"Title: {beatmap.Metadata.Title}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"TitleUnicode: {beatmap.Metadata.TitleUnicode}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"Artist: {beatmap.Metadata.Artist}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"ArtistUnicode: {beatmap.Metadata.ArtistUnicode}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"Creator: {beatmap.Metadata.AuthorString}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"Version: {beatmap.BeatmapInfo.Version}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"Source: {beatmap.Metadata.Source}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"Tags: {beatmap.Metadata.Tags}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"BeatmapID: {beatmap.BeatmapInfo.OnlineBeatmapID ?? 0}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"BeatmapSetID: {beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID ?? -1}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleDifficulty(TextWriter writer)
|
||||||
|
{
|
||||||
|
writer.WriteLine("[Difficulty]");
|
||||||
|
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"HPDrainRate: {beatmap.BeatmapInfo.BaseDifficulty.DrainRate}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"CircleSize: {beatmap.BeatmapInfo.BaseDifficulty.CircleSize}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"OverallDifficulty: {beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"ApproachRate: {beatmap.BeatmapInfo.BaseDifficulty.ApproachRate}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier}"));
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"SliderTickRate: {beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleEvents(TextWriter writer)
|
||||||
|
{
|
||||||
|
writer.WriteLine("[Events]");
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.BackgroundFile))
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Background},0,\"{beatmap.BeatmapInfo.Metadata.BackgroundFile}\",0,0"));
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.VideoFile))
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Video},0,\"{beatmap.BeatmapInfo.Metadata.VideoFile}\",0,0"));
|
||||||
|
|
||||||
|
foreach (var b in beatmap.Breaks)
|
||||||
|
writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Break},{b.StartTime},{b.EndTime}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleTimingPoints(TextWriter writer)
|
||||||
|
{
|
||||||
|
if (beatmap.ControlPointInfo.Groups.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
writer.WriteLine("[TimingPoints]");
|
||||||
|
|
||||||
|
foreach (var group in beatmap.ControlPointInfo.Groups)
|
||||||
|
{
|
||||||
|
var timingPoint = group.ControlPoints.OfType<TimingControlPoint>().FirstOrDefault();
|
||||||
|
var difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(group.Time);
|
||||||
|
var samplePoint = beatmap.ControlPointInfo.SamplePointAt(group.Time);
|
||||||
|
var effectPoint = beatmap.ControlPointInfo.EffectPointAt(group.Time);
|
||||||
|
|
||||||
|
// Convert beat length the legacy format
|
||||||
|
double beatLength;
|
||||||
|
if (timingPoint != null)
|
||||||
|
beatLength = timingPoint.BeatLength;
|
||||||
|
else
|
||||||
|
beatLength = -100 / difficultyPoint.SpeedMultiplier;
|
||||||
|
|
||||||
|
// Apply the control point to a hit sample to uncover legacy properties (e.g. suffix)
|
||||||
|
HitSampleInfo tempHitSample = samplePoint.ApplyTo(new HitSampleInfo());
|
||||||
|
|
||||||
|
// Convert effect flags to the legacy format
|
||||||
|
LegacyEffectFlags effectFlags = LegacyEffectFlags.None;
|
||||||
|
if (effectPoint.KiaiMode)
|
||||||
|
effectFlags |= LegacyEffectFlags.Kiai;
|
||||||
|
if (effectPoint.OmitFirstBarLine)
|
||||||
|
effectFlags |= LegacyEffectFlags.OmitFirstBarLine;
|
||||||
|
|
||||||
|
writer.Write(FormattableString.Invariant($"{group.Time},"));
|
||||||
|
writer.Write(FormattableString.Invariant($"{beatLength},"));
|
||||||
|
writer.Write(FormattableString.Invariant($"{(int)beatmap.ControlPointInfo.TimingPointAt(group.Time).TimeSignature},"));
|
||||||
|
writer.Write(FormattableString.Invariant($"{(int)toLegacySampleBank(tempHitSample.Bank)},"));
|
||||||
|
writer.Write(FormattableString.Invariant($"{toLegacyCustomSampleBank(tempHitSample.Suffix)},"));
|
||||||
|
writer.Write(FormattableString.Invariant($"{tempHitSample.Volume},"));
|
||||||
|
writer.Write(FormattableString.Invariant($"{(timingPoint != null ? '1' : '0')},"));
|
||||||
|
writer.Write(FormattableString.Invariant($"{(int)effectFlags}"));
|
||||||
|
writer.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleHitObjects(TextWriter writer)
|
||||||
|
{
|
||||||
|
if (beatmap.HitObjects.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
writer.WriteLine("[HitObjects]");
|
||||||
|
|
||||||
|
switch (beatmap.BeatmapInfo.RulesetID)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
foreach (var h in beatmap.HitObjects)
|
||||||
|
handleOsuHitObject(writer, h);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
foreach (var h in beatmap.HitObjects)
|
||||||
|
handleTaikoHitObject(writer, h);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
foreach (var h in beatmap.HitObjects)
|
||||||
|
handleCatchHitObject(writer, h);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
foreach (var h in beatmap.HitObjects)
|
||||||
|
handleManiaHitObject(writer, h);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleOsuHitObject(TextWriter writer, HitObject hitObject)
|
||||||
|
{
|
||||||
|
var positionData = (IHasPosition)hitObject;
|
||||||
|
|
||||||
|
writer.Write(FormattableString.Invariant($"{positionData.X},"));
|
||||||
|
writer.Write(FormattableString.Invariant($"{positionData.Y},"));
|
||||||
|
writer.Write(FormattableString.Invariant($"{hitObject.StartTime},"));
|
||||||
|
writer.Write(FormattableString.Invariant($"{(int)getObjectType(hitObject)},"));
|
||||||
|
|
||||||
|
writer.Write(hitObject is IHasCurve
|
||||||
|
? FormattableString.Invariant($"0,")
|
||||||
|
: FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},"));
|
||||||
|
|
||||||
|
if (hitObject is IHasCurve curveData)
|
||||||
|
{
|
||||||
|
addCurveData(writer, curveData, positionData);
|
||||||
|
writer.Write(getSampleBank(hitObject.Samples, zeroBanks: true));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (hitObject is IHasEndTime endTimeData)
|
||||||
|
writer.Write(FormattableString.Invariant($"{endTimeData.EndTime},"));
|
||||||
|
writer.Write(getSampleBank(hitObject.Samples));
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LegacyHitObjectType getObjectType(HitObject hitObject)
|
||||||
|
{
|
||||||
|
var comboData = (IHasCombo)hitObject;
|
||||||
|
|
||||||
|
var type = (LegacyHitObjectType)(comboData.ComboOffset << 4);
|
||||||
|
|
||||||
|
if (comboData.NewCombo) type |= LegacyHitObjectType.NewCombo;
|
||||||
|
|
||||||
|
switch (hitObject)
|
||||||
|
{
|
||||||
|
case IHasCurve _:
|
||||||
|
type |= LegacyHitObjectType.Slider;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IHasEndTime _:
|
||||||
|
type |= LegacyHitObjectType.Spinner | LegacyHitObjectType.NewCombo;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
type |= LegacyHitObjectType.Circle;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCurveData(TextWriter writer, IHasCurve curveData, IHasPosition positionData)
|
||||||
|
{
|
||||||
|
PathType? lastType = null;
|
||||||
|
|
||||||
|
for (int i = 0; i < curveData.Path.ControlPoints.Count; i++)
|
||||||
|
{
|
||||||
|
PathControlPoint point = curveData.Path.ControlPoints[i];
|
||||||
|
|
||||||
|
if (point.Type.Value != null)
|
||||||
|
{
|
||||||
|
if (point.Type.Value != lastType)
|
||||||
|
{
|
||||||
|
switch (point.Type.Value)
|
||||||
|
{
|
||||||
|
case PathType.Bezier:
|
||||||
|
writer.Write("B|");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PathType.Catmull:
|
||||||
|
writer.Write("C|");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PathType.PerfectCurve:
|
||||||
|
writer.Write("P|");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PathType.Linear:
|
||||||
|
writer.Write("L|");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastType = point.Type.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// New segment with the same type - duplicate the control point
|
||||||
|
writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}|"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i != 0)
|
||||||
|
{
|
||||||
|
writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}"));
|
||||||
|
writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Write(FormattableString.Invariant($"{curveData.RepeatCount + 1},"));
|
||||||
|
writer.Write(FormattableString.Invariant($"{curveData.Path.Distance},"));
|
||||||
|
|
||||||
|
for (int i = 0; i < curveData.NodeSamples.Count; i++)
|
||||||
|
{
|
||||||
|
writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(curveData.NodeSamples[i])}"));
|
||||||
|
writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < curveData.NodeSamples.Count; i++)
|
||||||
|
{
|
||||||
|
writer.Write(getSampleBank(curveData.NodeSamples[i], true));
|
||||||
|
writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleTaikoHitObject(TextWriter writer, HitObject hitObject) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
private void handleCatchHitObject(TextWriter writer, HitObject hitObject) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
private void handleManiaHitObject(TextWriter writer, HitObject hitObject) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
private string getSampleBank(IList<HitSampleInfo> samples, bool banksOnly = false, bool zeroBanks = false)
|
||||||
|
{
|
||||||
|
LegacySampleBank normalBank = toLegacySampleBank(samples.SingleOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank);
|
||||||
|
LegacySampleBank addBank = toLegacySampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name) && s.Name != HitSampleInfo.HIT_NORMAL)?.Bank);
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.Append(FormattableString.Invariant($"{(zeroBanks ? 0 : (int)normalBank)}:"));
|
||||||
|
sb.Append(FormattableString.Invariant($"{(zeroBanks ? 0 : (int)addBank)}"));
|
||||||
|
|
||||||
|
if (!banksOnly)
|
||||||
|
{
|
||||||
|
string customSampleBank = toLegacyCustomSampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name))?.Suffix);
|
||||||
|
string sampleFilename = samples.FirstOrDefault(s => string.IsNullOrEmpty(s.Name))?.LookupNames.First() ?? string.Empty;
|
||||||
|
int volume = samples.FirstOrDefault()?.Volume ?? 100;
|
||||||
|
|
||||||
|
sb.Append(":");
|
||||||
|
sb.Append(FormattableString.Invariant($"{customSampleBank}:"));
|
||||||
|
sb.Append(FormattableString.Invariant($"{volume}:"));
|
||||||
|
sb.Append(FormattableString.Invariant($"{sampleFilename}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private LegacyHitSoundType toLegacyHitSoundType(IList<HitSampleInfo> samples)
|
||||||
|
{
|
||||||
|
LegacyHitSoundType type = LegacyHitSoundType.None;
|
||||||
|
|
||||||
|
foreach (var sample in samples)
|
||||||
|
{
|
||||||
|
switch (sample.Name)
|
||||||
|
{
|
||||||
|
case HitSampleInfo.HIT_WHISTLE:
|
||||||
|
type |= LegacyHitSoundType.Whistle;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HitSampleInfo.HIT_FINISH:
|
||||||
|
type |= LegacyHitSoundType.Finish;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HitSampleInfo.HIT_CLAP:
|
||||||
|
type |= LegacyHitSoundType.Clap;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LegacySampleBank toLegacySampleBank(string sampleBank)
|
||||||
|
{
|
||||||
|
switch (sampleBank?.ToLowerInvariant())
|
||||||
|
{
|
||||||
|
case "normal":
|
||||||
|
return LegacySampleBank.Normal;
|
||||||
|
|
||||||
|
case "soft":
|
||||||
|
return LegacySampleBank.Soft;
|
||||||
|
|
||||||
|
case "drum":
|
||||||
|
return LegacySampleBank.Drum;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return LegacySampleBank.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string toLegacyCustomSampleBank(string sampleSuffix) => string.IsNullOrEmpty(sampleSuffix) ? "0" : sampleSuffix;
|
||||||
|
}
|
||||||
|
}
|
@ -148,47 +148,6 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
Fonts
|
Fonts
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum LegacySampleBank
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Normal = 1,
|
|
||||||
Soft = 2,
|
|
||||||
Drum = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum EventType
|
|
||||||
{
|
|
||||||
Background = 0,
|
|
||||||
Video = 1,
|
|
||||||
Break = 2,
|
|
||||||
Colour = 3,
|
|
||||||
Sprite = 4,
|
|
||||||
Sample = 5,
|
|
||||||
Animation = 6
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum LegacyOrigins
|
|
||||||
{
|
|
||||||
TopLeft,
|
|
||||||
Centre,
|
|
||||||
CentreLeft,
|
|
||||||
TopRight,
|
|
||||||
BottomCentre,
|
|
||||||
TopCentre,
|
|
||||||
Custom,
|
|
||||||
CentreRight,
|
|
||||||
BottomLeft,
|
|
||||||
BottomRight
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum StoryLayer
|
|
||||||
{
|
|
||||||
Background = 0,
|
|
||||||
Fail = 1,
|
|
||||||
Pass = 2,
|
|
||||||
Foreground = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class LegacyDifficultyControlPoint : DifficultyControlPoint
|
internal class LegacyDifficultyControlPoint : DifficultyControlPoint
|
||||||
{
|
{
|
||||||
public LegacyDifficultyControlPoint()
|
public LegacyDifficultyControlPoint()
|
||||||
|
@ -12,6 +12,7 @@ using osu.Framework.Extensions;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.Storyboards;
|
using osu.Game.Storyboards;
|
||||||
|
using osu.Game.Beatmaps.Legacy;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Formats
|
namespace osu.Game.Beatmaps.Formats
|
||||||
{
|
{
|
||||||
@ -83,12 +84,12 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
storyboardSprite = null;
|
storyboardSprite = null;
|
||||||
|
|
||||||
if (!Enum.TryParse(split[0], out EventType type))
|
if (!Enum.TryParse(split[0], out LegacyEventType type))
|
||||||
throw new InvalidDataException($@"Unknown event type: {split[0]}");
|
throw new InvalidDataException($@"Unknown event type: {split[0]}");
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case EventType.Sprite:
|
case LegacyEventType.Sprite:
|
||||||
{
|
{
|
||||||
var layer = parseLayer(split[1]);
|
var layer = parseLayer(split[1]);
|
||||||
var origin = parseOrigin(split[2]);
|
var origin = parseOrigin(split[2]);
|
||||||
@ -100,7 +101,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case EventType.Animation:
|
case LegacyEventType.Animation:
|
||||||
{
|
{
|
||||||
var layer = parseLayer(split[1]);
|
var layer = parseLayer(split[1]);
|
||||||
var origin = parseOrigin(split[2]);
|
var origin = parseOrigin(split[2]);
|
||||||
@ -115,7 +116,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case EventType.Sample:
|
case LegacyEventType.Sample:
|
||||||
{
|
{
|
||||||
var time = double.Parse(split[1], CultureInfo.InvariantCulture);
|
var time = double.Parse(split[1], CultureInfo.InvariantCulture);
|
||||||
var layer = parseLayer(split[2]);
|
var layer = parseLayer(split[2]);
|
||||||
@ -176,7 +177,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||||
timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue));
|
timelineGroup?.Scale.Add(easing, startTime, endTime, startValue, endValue);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +187,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
var startY = float.Parse(split[5], CultureInfo.InvariantCulture);
|
var startY = float.Parse(split[5], CultureInfo.InvariantCulture);
|
||||||
var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX;
|
var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX;
|
||||||
var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY;
|
var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY;
|
||||||
timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY));
|
timelineGroup?.VectorScale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,7 +272,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string parseLayer(string value) => Enum.Parse(typeof(StoryLayer), value).ToString();
|
private string parseLayer(string value) => Enum.Parse(typeof(LegacyStoryLayer), value).ToString();
|
||||||
|
|
||||||
private Anchor parseOrigin(string value)
|
private Anchor parseOrigin(string value)
|
||||||
{
|
{
|
||||||
|
15
osu.Game/Beatmaps/Legacy/LegacyEffectFlags.cs
Normal file
15
osu.Game/Beatmaps/Legacy/LegacyEffectFlags.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Legacy
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
internal enum LegacyEffectFlags
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Kiai = 1,
|
||||||
|
OmitFirstBarLine = 8
|
||||||
|
}
|
||||||
|
}
|
16
osu.Game/Beatmaps/Legacy/LegacyEventType.cs
Normal file
16
osu.Game/Beatmaps/Legacy/LegacyEventType.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Legacy
|
||||||
|
{
|
||||||
|
internal enum LegacyEventType
|
||||||
|
{
|
||||||
|
Background = 0,
|
||||||
|
Video = 1,
|
||||||
|
Break = 2,
|
||||||
|
Colour = 3,
|
||||||
|
Sprite = 4,
|
||||||
|
Sample = 5,
|
||||||
|
Animation = 6
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects.Legacy
|
namespace osu.Game.Beatmaps.Legacy
|
||||||
{
|
{
|
||||||
[Flags]
|
[Flags]
|
||||||
internal enum ConvertHitObjectType
|
internal enum LegacyHitObjectType
|
||||||
{
|
{
|
||||||
Circle = 1,
|
Circle = 1,
|
||||||
Slider = 1 << 1,
|
Slider = 1 << 1,
|
17
osu.Game/Beatmaps/Legacy/LegacyHitSoundType.cs
Normal file
17
osu.Game/Beatmaps/Legacy/LegacyHitSoundType.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Legacy
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
internal enum LegacyHitSoundType
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Normal = 1,
|
||||||
|
Whistle = 2,
|
||||||
|
Finish = 4,
|
||||||
|
Clap = 8
|
||||||
|
}
|
||||||
|
}
|
19
osu.Game/Beatmaps/Legacy/LegacyOrigins.cs
Normal file
19
osu.Game/Beatmaps/Legacy/LegacyOrigins.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Legacy
|
||||||
|
{
|
||||||
|
internal enum LegacyOrigins
|
||||||
|
{
|
||||||
|
TopLeft,
|
||||||
|
Centre,
|
||||||
|
CentreLeft,
|
||||||
|
TopRight,
|
||||||
|
BottomCentre,
|
||||||
|
TopCentre,
|
||||||
|
Custom,
|
||||||
|
CentreRight,
|
||||||
|
BottomLeft,
|
||||||
|
BottomRight
|
||||||
|
}
|
||||||
|
}
|
13
osu.Game/Beatmaps/Legacy/LegacySampleBank.cs
Normal file
13
osu.Game/Beatmaps/Legacy/LegacySampleBank.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Legacy
|
||||||
|
{
|
||||||
|
internal enum LegacySampleBank
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Normal = 1,
|
||||||
|
Soft = 2,
|
||||||
|
Drum = 3
|
||||||
|
}
|
||||||
|
}
|
13
osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs
Normal file
13
osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Legacy
|
||||||
|
{
|
||||||
|
internal enum LegacyStoryLayer
|
||||||
|
{
|
||||||
|
Background = 0,
|
||||||
|
Fail = 1,
|
||||||
|
Pass = 2,
|
||||||
|
Foreground = 3
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,11 @@ namespace osu.Game.Graphics.Containers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public double TimeSinceLastBeat { get; private set; }
|
public double TimeSinceLastBeat { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How many beats per beatlength to trigger. Defaults to 1.
|
||||||
|
/// </summary>
|
||||||
|
public int Divisor { get; set; } = 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default length of a beat in milliseconds. Used whenever there is no beatmap or track playing.
|
/// Default length of a beat in milliseconds. Used whenever there is no beatmap or track playing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -42,6 +47,8 @@ namespace osu.Game.Graphics.Containers
|
|||||||
private EffectControlPoint defaultEffect;
|
private EffectControlPoint defaultEffect;
|
||||||
private TrackAmplitudes defaultAmplitudes;
|
private TrackAmplitudes defaultAmplitudes;
|
||||||
|
|
||||||
|
protected bool IsBeatSyncedWithTrack { get; private set; }
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
Track track = null;
|
Track track = null;
|
||||||
@ -65,26 +72,34 @@ namespace osu.Game.Graphics.Containers
|
|||||||
effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
|
effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
|
||||||
|
|
||||||
if (timingPoint.BeatLength == 0)
|
if (timingPoint.BeatLength == 0)
|
||||||
|
{
|
||||||
|
IsBeatSyncedWithTrack = false;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsBeatSyncedWithTrack = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
IsBeatSyncedWithTrack = false;
|
||||||
currentTrackTime = Clock.CurrentTime;
|
currentTrackTime = Clock.CurrentTime;
|
||||||
timingPoint = defaultTiming;
|
timingPoint = defaultTiming;
|
||||||
effectPoint = defaultEffect;
|
effectPoint = defaultEffect;
|
||||||
}
|
}
|
||||||
|
|
||||||
int beatIndex = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength);
|
double beatLength = timingPoint.BeatLength / Divisor;
|
||||||
|
|
||||||
|
int beatIndex = (int)((currentTrackTime - timingPoint.Time) / beatLength) - (effectPoint.OmitFirstBarLine ? 1 : 0);
|
||||||
|
|
||||||
// The beats before the start of the first control point are off by 1, this should do the trick
|
// The beats before the start of the first control point are off by 1, this should do the trick
|
||||||
if (currentTrackTime < timingPoint.Time)
|
if (currentTrackTime < timingPoint.Time)
|
||||||
beatIndex--;
|
beatIndex--;
|
||||||
|
|
||||||
TimeUntilNextBeat = (timingPoint.Time - currentTrackTime) % timingPoint.BeatLength;
|
TimeUntilNextBeat = (timingPoint.Time - currentTrackTime) % beatLength;
|
||||||
if (TimeUntilNextBeat < 0)
|
if (TimeUntilNextBeat < 0)
|
||||||
TimeUntilNextBeat += timingPoint.BeatLength;
|
TimeUntilNextBeat += beatLength;
|
||||||
|
|
||||||
TimeSinceLastBeat = timingPoint.BeatLength - TimeUntilNextBeat;
|
TimeSinceLastBeat = beatLength - TimeUntilNextBeat;
|
||||||
|
|
||||||
if (timingPoint.Equals(lastTimingPoint) && beatIndex == lastBeat)
|
if (timingPoint.Equals(lastTimingPoint) && beatIndex == lastBeat)
|
||||||
return;
|
return;
|
||||||
|
@ -153,6 +153,10 @@ namespace osu.Game.Online.API
|
|||||||
userReq.Success += u =>
|
userReq.Success += u =>
|
||||||
{
|
{
|
||||||
LocalUser.Value = u;
|
LocalUser.Value = u;
|
||||||
|
|
||||||
|
// todo: save/pull from settings
|
||||||
|
LocalUser.Value.Status.Value = new UserStatusOnline();
|
||||||
|
|
||||||
failureCount = 0;
|
failureCount = 0;
|
||||||
|
|
||||||
//we're connected!
|
//we're connected!
|
||||||
|
157
osu.Game/Overlays/News/NewsArticleCover.cs
Normal file
157
osu.Game/Overlays/News/NewsArticleCover.cs
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.News
|
||||||
|
{
|
||||||
|
public class NewsArticleCover : Container
|
||||||
|
{
|
||||||
|
public NewsArticleCover(ArticleInfo info)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
Masking = true;
|
||||||
|
CornerRadius = 4;
|
||||||
|
|
||||||
|
NewsBackground bg;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.2f), OsuColour.Gray(0.1f))
|
||||||
|
},
|
||||||
|
new DelayedLoadWrapper(bg = new NewsBackground(info.CoverUrl)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
FillMode = FillMode.Fill,
|
||||||
|
Alpha = 0
|
||||||
|
})
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.6f)),
|
||||||
|
Alpha = 1f,
|
||||||
|
},
|
||||||
|
new DateContainer(info.Time)
|
||||||
|
{
|
||||||
|
Margin = new MarginPadding
|
||||||
|
{
|
||||||
|
Right = 20,
|
||||||
|
Top = 20,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Margin = new MarginPadding
|
||||||
|
{
|
||||||
|
Left = 25,
|
||||||
|
Bottom = 50,
|
||||||
|
},
|
||||||
|
Font = OsuFont.GetFont(Typeface.Exo, 24, FontWeight.Bold),
|
||||||
|
Text = info.Title,
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Margin = new MarginPadding
|
||||||
|
{
|
||||||
|
Left = 25,
|
||||||
|
Bottom = 30,
|
||||||
|
},
|
||||||
|
Font = OsuFont.GetFont(Typeface.Exo, 16, FontWeight.Bold),
|
||||||
|
Text = "by " + info.Author
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bg.OnLoadComplete += d => d.FadeIn(250, Easing.In);
|
||||||
|
}
|
||||||
|
|
||||||
|
[LongRunningLoad]
|
||||||
|
private class NewsBackground : Sprite
|
||||||
|
{
|
||||||
|
private readonly string url;
|
||||||
|
|
||||||
|
public NewsBackground(string coverUrl)
|
||||||
|
{
|
||||||
|
url = coverUrl ?? "Headers/news";
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(LargeTextureStore store)
|
||||||
|
{
|
||||||
|
Texture = store.Get(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DateContainer : Container, IHasTooltip
|
||||||
|
{
|
||||||
|
private readonly DateTime date;
|
||||||
|
|
||||||
|
public DateContainer(DateTime date)
|
||||||
|
{
|
||||||
|
this.date = date;
|
||||||
|
|
||||||
|
Anchor = Anchor.TopRight;
|
||||||
|
Origin = Anchor.TopRight;
|
||||||
|
Masking = true;
|
||||||
|
CornerRadius = 4;
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.Black.Opacity(0.5f),
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Font = OsuFont.GetFont(Typeface.Exo, 12, FontWeight.Black, false, false),
|
||||||
|
Text = date.ToString("d MMM yyy").ToUpper(),
|
||||||
|
Margin = new MarginPadding
|
||||||
|
{
|
||||||
|
Vertical = 4,
|
||||||
|
Horizontal = 8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TooltipText => date.ToString("dddd dd MMMM yyyy hh:mm:ss UTCz").ToUpper();
|
||||||
|
}
|
||||||
|
|
||||||
|
//fake API data struct to use for now as a skeleton for data, as there is no API struct for news article info for now
|
||||||
|
public class ArticleInfo
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string CoverUrl { get; set; }
|
||||||
|
public DateTime Time { get; set; }
|
||||||
|
public string Author { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Threading;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -16,7 +17,6 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
private NewsHeader header;
|
private NewsHeader header;
|
||||||
|
|
||||||
//ReSharper disable NotAccessedField.Local
|
|
||||||
private Container<NewsContent> content;
|
private Container<NewsContent> content;
|
||||||
|
|
||||||
public readonly Bindable<string> Current = new Bindable<string>(null);
|
public readonly Bindable<string> Current = new Bindable<string>(null);
|
||||||
@ -59,6 +59,21 @@ namespace osu.Game.Overlays
|
|||||||
Current.TriggerChange();
|
Current.TriggerChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CancellationTokenSource loadContentCancellation;
|
||||||
|
|
||||||
|
protected void LoadAndShowContent(NewsContent newContent)
|
||||||
|
{
|
||||||
|
content.FadeTo(0.2f, 300, Easing.OutQuint);
|
||||||
|
|
||||||
|
loadContentCancellation?.Cancel();
|
||||||
|
|
||||||
|
LoadComponentAsync(newContent, c =>
|
||||||
|
{
|
||||||
|
content.Child = c;
|
||||||
|
content.FadeIn(300, Easing.OutQuint);
|
||||||
|
}, (loadContentCancellation = new CancellationTokenSource()).Token);
|
||||||
|
}
|
||||||
|
|
||||||
public void ShowFrontPage()
|
public void ShowFrontPage()
|
||||||
{
|
{
|
||||||
Current.Value = null;
|
Current.Value = null;
|
||||||
|
@ -1,15 +1,25 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
{
|
{
|
||||||
public abstract class ModNightcore : ModDoubleTime
|
public abstract class ModNightcore<TObject> : ModDoubleTime, IApplicableToDrawableRuleset<TObject>
|
||||||
|
where TObject : HitObject
|
||||||
{
|
{
|
||||||
public override string Name => "Nightcore";
|
public override string Name => "Nightcore";
|
||||||
public override string Acronym => "NC";
|
public override string Acronym => "NC";
|
||||||
@ -34,5 +44,105 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust);
|
track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust);
|
||||||
track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust);
|
track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ApplyToDrawableRuleset(DrawableRuleset<TObject> drawableRuleset)
|
||||||
|
{
|
||||||
|
drawableRuleset.Overlays.Add(new NightcoreBeatContainer());
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NightcoreBeatContainer : BeatSyncedContainer
|
||||||
|
{
|
||||||
|
private SkinnableSound hatSample;
|
||||||
|
private SkinnableSound clapSample;
|
||||||
|
private SkinnableSound kickSample;
|
||||||
|
private SkinnableSound finishSample;
|
||||||
|
|
||||||
|
private int? firstBeat;
|
||||||
|
|
||||||
|
public NightcoreBeatContainer()
|
||||||
|
{
|
||||||
|
Divisor = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
hatSample = new SkinnableSound(new SampleInfo("nightcore-hat")),
|
||||||
|
clapSample = new SkinnableSound(new SampleInfo("nightcore-clap")),
|
||||||
|
kickSample = new SkinnableSound(new SampleInfo("nightcore-kick")),
|
||||||
|
finishSample = new SkinnableSound(new SampleInfo("nightcore-finish")),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private const int bars_per_segment = 4;
|
||||||
|
|
||||||
|
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
|
||||||
|
{
|
||||||
|
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
|
||||||
|
|
||||||
|
int beatsPerBar = (int)timingPoint.TimeSignature;
|
||||||
|
int segmentLength = beatsPerBar * Divisor * bars_per_segment;
|
||||||
|
|
||||||
|
if (!IsBeatSyncedWithTrack)
|
||||||
|
{
|
||||||
|
firstBeat = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!firstBeat.HasValue || beatIndex < firstBeat)
|
||||||
|
// decide on a good starting beat index if once has not yet been decided.
|
||||||
|
firstBeat = beatIndex < 0 ? 0 : (beatIndex / segmentLength + 1) * segmentLength;
|
||||||
|
|
||||||
|
if (beatIndex >= firstBeat)
|
||||||
|
playBeatFor(beatIndex % segmentLength, timingPoint.TimeSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playBeatFor(int beatIndex, TimeSignatures signature)
|
||||||
|
{
|
||||||
|
if (beatIndex == 0)
|
||||||
|
finishSample?.Play();
|
||||||
|
|
||||||
|
switch (signature)
|
||||||
|
{
|
||||||
|
case TimeSignatures.SimpleTriple:
|
||||||
|
switch (beatIndex % 6)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
kickSample?.Play();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
clapSample?.Play();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
hatSample?.Play();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TimeSignatures.SimpleQuadruple:
|
||||||
|
switch (beatIndex % 4)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
kickSample?.Play();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
clapSample?.Play();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
hatSample?.Play();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ using osu.Game.Audio;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Game.Beatmaps.Legacy;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects.Legacy
|
namespace osu.Game.Rulesets.Objects.Legacy
|
||||||
{
|
{
|
||||||
@ -46,27 +47,27 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
double startTime = Parsing.ParseDouble(split[2]) + Offset;
|
double startTime = Parsing.ParseDouble(split[2]) + Offset;
|
||||||
|
|
||||||
ConvertHitObjectType type = (ConvertHitObjectType)Parsing.ParseInt(split[3]);
|
LegacyHitObjectType type = (LegacyHitObjectType)Parsing.ParseInt(split[3]);
|
||||||
|
|
||||||
int comboOffset = (int)(type & ConvertHitObjectType.ComboOffset) >> 4;
|
int comboOffset = (int)(type & LegacyHitObjectType.ComboOffset) >> 4;
|
||||||
type &= ~ConvertHitObjectType.ComboOffset;
|
type &= ~LegacyHitObjectType.ComboOffset;
|
||||||
|
|
||||||
bool combo = type.HasFlag(ConvertHitObjectType.NewCombo);
|
bool combo = type.HasFlag(LegacyHitObjectType.NewCombo);
|
||||||
type &= ~ConvertHitObjectType.NewCombo;
|
type &= ~LegacyHitObjectType.NewCombo;
|
||||||
|
|
||||||
var soundType = (LegacySoundType)Parsing.ParseInt(split[4]);
|
var soundType = (LegacyHitSoundType)Parsing.ParseInt(split[4]);
|
||||||
var bankInfo = new SampleBankInfo();
|
var bankInfo = new SampleBankInfo();
|
||||||
|
|
||||||
HitObject result = null;
|
HitObject result = null;
|
||||||
|
|
||||||
if (type.HasFlag(ConvertHitObjectType.Circle))
|
if (type.HasFlag(LegacyHitObjectType.Circle))
|
||||||
{
|
{
|
||||||
result = CreateHit(pos, combo, comboOffset);
|
result = CreateHit(pos, combo, comboOffset);
|
||||||
|
|
||||||
if (split.Length > 5)
|
if (split.Length > 5)
|
||||||
readCustomSampleBanks(split[5], bankInfo);
|
readCustomSampleBanks(split[5], bankInfo);
|
||||||
}
|
}
|
||||||
else if (type.HasFlag(ConvertHitObjectType.Slider))
|
else if (type.HasFlag(LegacyHitObjectType.Slider))
|
||||||
{
|
{
|
||||||
PathType pathType = PathType.Catmull;
|
PathType pathType = PathType.Catmull;
|
||||||
double? length = null;
|
double? length = null;
|
||||||
@ -157,7 +158,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Populate node sound types with the default hit object sound type
|
// Populate node sound types with the default hit object sound type
|
||||||
var nodeSoundTypes = new List<LegacySoundType>();
|
var nodeSoundTypes = new List<LegacyHitSoundType>();
|
||||||
for (int i = 0; i < nodes; i++)
|
for (int i = 0; i < nodes; i++)
|
||||||
nodeSoundTypes.Add(soundType);
|
nodeSoundTypes.Add(soundType);
|
||||||
|
|
||||||
@ -172,7 +173,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
int.TryParse(adds[i], out var sound);
|
int.TryParse(adds[i], out var sound);
|
||||||
nodeSoundTypes[i] = (LegacySoundType)sound;
|
nodeSoundTypes[i] = (LegacyHitSoundType)sound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +187,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
// The samples are played when the slider ends, which is the last node
|
// The samples are played when the slider ends, which is the last node
|
||||||
result.Samples = nodeSamples[^1];
|
result.Samples = nodeSamples[^1];
|
||||||
}
|
}
|
||||||
else if (type.HasFlag(ConvertHitObjectType.Spinner))
|
else if (type.HasFlag(LegacyHitObjectType.Spinner))
|
||||||
{
|
{
|
||||||
double endTime = Math.Max(startTime, Parsing.ParseDouble(split[5]) + Offset);
|
double endTime = Math.Max(startTime, Parsing.ParseDouble(split[5]) + Offset);
|
||||||
|
|
||||||
@ -195,7 +196,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
if (split.Length > 6)
|
if (split.Length > 6)
|
||||||
readCustomSampleBanks(split[6], bankInfo);
|
readCustomSampleBanks(split[6], bankInfo);
|
||||||
}
|
}
|
||||||
else if (type.HasFlag(ConvertHitObjectType.Hold))
|
else if (type.HasFlag(LegacyHitObjectType.Hold))
|
||||||
{
|
{
|
||||||
// Note: Hold is generated by BMS converts
|
// Note: Hold is generated by BMS converts
|
||||||
|
|
||||||
@ -231,8 +232,8 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
string[] split = str.Split(':');
|
string[] split = str.Split(':');
|
||||||
|
|
||||||
var bank = (LegacyBeatmapDecoder.LegacySampleBank)Parsing.ParseInt(split[0]);
|
var bank = (LegacySampleBank)Parsing.ParseInt(split[0]);
|
||||||
var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Parsing.ParseInt(split[1]);
|
var addbank = (LegacySampleBank)Parsing.ParseInt(split[1]);
|
||||||
|
|
||||||
string stringBank = bank.ToString().ToLowerInvariant();
|
string stringBank = bank.ToString().ToLowerInvariant();
|
||||||
if (stringBank == @"none")
|
if (stringBank == @"none")
|
||||||
@ -333,7 +334,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
/// <param name="endTime">The hold end time.</param>
|
/// <param name="endTime">The hold end time.</param>
|
||||||
protected abstract HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime);
|
protected abstract HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime);
|
||||||
|
|
||||||
private List<HitSampleInfo> convertSoundType(LegacySoundType type, SampleBankInfo bankInfo)
|
private List<HitSampleInfo> convertSoundType(LegacyHitSoundType type, SampleBankInfo bankInfo)
|
||||||
{
|
{
|
||||||
// Todo: This should return the normal SampleInfos if the specified sample file isn't found, but that's a pretty edge-case scenario
|
// Todo: This should return the normal SampleInfos if the specified sample file isn't found, but that's a pretty edge-case scenario
|
||||||
if (!string.IsNullOrEmpty(bankInfo.Filename))
|
if (!string.IsNullOrEmpty(bankInfo.Filename))
|
||||||
@ -359,7 +360,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (type.HasFlag(LegacySoundType.Finish))
|
if (type.HasFlag(LegacyHitSoundType.Finish))
|
||||||
{
|
{
|
||||||
soundTypes.Add(new LegacyHitSampleInfo
|
soundTypes.Add(new LegacyHitSampleInfo
|
||||||
{
|
{
|
||||||
@ -370,7 +371,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type.HasFlag(LegacySoundType.Whistle))
|
if (type.HasFlag(LegacyHitSoundType.Whistle))
|
||||||
{
|
{
|
||||||
soundTypes.Add(new LegacyHitSampleInfo
|
soundTypes.Add(new LegacyHitSampleInfo
|
||||||
{
|
{
|
||||||
@ -381,7 +382,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type.HasFlag(LegacySoundType.Clap))
|
if (type.HasFlag(LegacyHitSoundType.Clap))
|
||||||
{
|
{
|
||||||
soundTypes.Add(new LegacyHitSampleInfo
|
soundTypes.Add(new LegacyHitSampleInfo
|
||||||
{
|
{
|
||||||
@ -430,15 +431,5 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
Path.ChangeExtension(Filename, null)
|
Path.ChangeExtension(Filename, null)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
|
||||||
private enum LegacySoundType
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Normal = 1,
|
|
||||||
Whistle = 2,
|
|
||||||
Finish = 4,
|
|
||||||
Clap = 8
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,9 +49,9 @@ namespace osu.Game.Rulesets
|
|||||||
|
|
||||||
public virtual ISkin CreateLegacySkinProvider(ISkinSource source) => null;
|
public virtual ISkin CreateLegacySkinProvider(ISkinSource source) => null;
|
||||||
|
|
||||||
protected Ruleset(RulesetInfo rulesetInfo = null)
|
protected Ruleset()
|
||||||
{
|
{
|
||||||
RulesetInfo = rulesetInfo ?? createRulesetInfo();
|
RulesetInfo = createRulesetInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets
|
|||||||
{
|
{
|
||||||
if (!Available) return null;
|
if (!Available) return null;
|
||||||
|
|
||||||
return (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this);
|
return (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo;
|
public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo;
|
||||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Rulesets
|
|||||||
{
|
{
|
||||||
var context = usage.Context;
|
var context = usage.Context;
|
||||||
|
|
||||||
var instances = loadedAssemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, (RulesetInfo)null)).ToList();
|
var instances = loadedAssemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r)).ToList();
|
||||||
|
|
||||||
//add all legacy modes in correct order
|
//add all legacy modes in correct order
|
||||||
foreach (var r in instances.Where(r => r.LegacyID != null).OrderBy(r => r.LegacyID))
|
foreach (var r in instances.Where(r => r.LegacyID != null).OrderBy(r => r.LegacyID))
|
||||||
@ -87,7 +87,7 @@ namespace osu.Game.Rulesets
|
|||||||
// this allows for debug builds to successfully load rulesets (even though debug rulesets have a 0.0.0 version).
|
// this allows for debug builds to successfully load rulesets (even though debug rulesets have a 0.0.0 version).
|
||||||
asm.Version = null;
|
asm.Version = null;
|
||||||
return Assembly.Load(asm);
|
return Assembly.Load(asm);
|
||||||
}, null), (RulesetInfo)null)).RulesetInfo;
|
}, null))).RulesetInfo;
|
||||||
|
|
||||||
r.Name = instanceInfo.Name;
|
r.Name = instanceInfo.Name;
|
||||||
r.ShortName = instanceInfo.ShortName;
|
r.ShortName = instanceInfo.ShortName;
|
||||||
|
@ -32,6 +32,6 @@ namespace osu.Game.Screens.Edit.Compose
|
|||||||
return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(composer));
|
return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(composer));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Drawable CreateTimelineContent() => new TimelineHitObjectDisplay(composer.EditorBeatmap);
|
protected override Drawable CreateTimelineContent() => composer == null ? base.CreateTimelineContent() : new TimelineHitObjectDisplay(composer.EditorBeatmap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,18 @@ using osu.Game.Graphics.Sprites;
|
|||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using System;
|
using System;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Select.Details
|
namespace osu.Game.Screens.Select.Details
|
||||||
{
|
{
|
||||||
public class AdvancedStats : Container
|
public class AdvancedStats : Container
|
||||||
{
|
{
|
||||||
|
[Resolved]
|
||||||
|
private IBindable<IReadOnlyList<Mod>> mods { get; set; }
|
||||||
|
|
||||||
private readonly StatisticRow firstValue, hpDrain, accuracy, approachRate, starDifficulty;
|
private readonly StatisticRow firstValue, hpDrain, accuracy, approachRate, starDifficulty;
|
||||||
|
|
||||||
private BeatmapInfo beatmap;
|
private BeatmapInfo beatmap;
|
||||||
@ -30,22 +37,7 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
|
|
||||||
beatmap = value;
|
beatmap = value;
|
||||||
|
|
||||||
//mania specific
|
updateStatistics();
|
||||||
if ((Beatmap?.Ruleset?.ID ?? 0) == 3)
|
|
||||||
{
|
|
||||||
firstValue.Title = "Key Amount";
|
|
||||||
firstValue.Value = (int)MathF.Round(Beatmap?.BaseDifficulty?.CircleSize ?? 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
firstValue.Title = "Circle Size";
|
|
||||||
firstValue.Value = Beatmap?.BaseDifficulty?.CircleSize ?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
hpDrain.Value = Beatmap?.BaseDifficulty?.DrainRate ?? 0;
|
|
||||||
accuracy.Value = Beatmap?.BaseDifficulty?.OverallDifficulty ?? 0;
|
|
||||||
approachRate.Value = Beatmap?.BaseDifficulty?.ApproachRate ?? 0;
|
|
||||||
starDifficulty.Value = (float)(Beatmap?.StarDifficulty ?? 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +65,45 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
starDifficulty.AccentColour = colours.Yellow;
|
starDifficulty.AccentColour = colours.Yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
mods.BindValueChanged(_ => updateStatistics(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateStatistics()
|
||||||
|
{
|
||||||
|
BeatmapDifficulty baseDifficulty = Beatmap?.BaseDifficulty;
|
||||||
|
BeatmapDifficulty adjustedDifficulty = null;
|
||||||
|
|
||||||
|
if (baseDifficulty != null && mods.Value.Any(m => m is IApplicableToDifficulty))
|
||||||
|
{
|
||||||
|
adjustedDifficulty = baseDifficulty.Clone();
|
||||||
|
|
||||||
|
foreach (var mod in mods.Value.OfType<IApplicableToDifficulty>())
|
||||||
|
mod.ApplyToDifficulty(adjustedDifficulty);
|
||||||
|
}
|
||||||
|
|
||||||
|
//mania specific
|
||||||
|
if ((Beatmap?.Ruleset?.ID ?? 0) == 3)
|
||||||
|
{
|
||||||
|
firstValue.Title = "Key Amount";
|
||||||
|
firstValue.Value = ((int)MathF.Round(baseDifficulty?.CircleSize ?? 0), (int)MathF.Round(adjustedDifficulty?.CircleSize ?? 0));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
firstValue.Title = "Circle Size";
|
||||||
|
firstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
starDifficulty.Value = ((float)(Beatmap?.StarDifficulty ?? 0), null);
|
||||||
|
|
||||||
|
hpDrain.Value = (baseDifficulty?.DrainRate ?? 0, adjustedDifficulty?.DrainRate);
|
||||||
|
accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty);
|
||||||
|
approachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate);
|
||||||
|
}
|
||||||
|
|
||||||
private class StatisticRow : Container, IHasAccentColour
|
private class StatisticRow : Container, IHasAccentColour
|
||||||
{
|
{
|
||||||
private const float value_width = 25;
|
private const float value_width = 25;
|
||||||
@ -80,8 +111,11 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
|
|
||||||
private readonly float maxValue;
|
private readonly float maxValue;
|
||||||
private readonly bool forceDecimalPlaces;
|
private readonly bool forceDecimalPlaces;
|
||||||
private readonly OsuSpriteText name, value;
|
private readonly OsuSpriteText name, valueText;
|
||||||
private readonly Bar bar;
|
private readonly Bar bar, modBar;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; }
|
||||||
|
|
||||||
public string Title
|
public string Title
|
||||||
{
|
{
|
||||||
@ -89,16 +123,29 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
set => name.Text = value;
|
set => name.Text = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float difficultyValue;
|
private (float baseValue, float? adjustedValue) value;
|
||||||
|
|
||||||
public float Value
|
public (float baseValue, float? adjustedValue) Value
|
||||||
{
|
{
|
||||||
get => difficultyValue;
|
get => value;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
difficultyValue = value;
|
if (value == this.value)
|
||||||
bar.Length = value / maxValue;
|
return;
|
||||||
this.value.Text = value.ToString(forceDecimalPlaces ? "0.00" : "0.##");
|
|
||||||
|
this.value = value;
|
||||||
|
|
||||||
|
bar.Length = value.baseValue / maxValue;
|
||||||
|
|
||||||
|
valueText.Text = (value.adjustedValue ?? value.baseValue).ToString(forceDecimalPlaces ? "0.00" : "0.##");
|
||||||
|
modBar.Length = (value.adjustedValue ?? 0) / maxValue;
|
||||||
|
|
||||||
|
if (value.adjustedValue > value.baseValue)
|
||||||
|
modBar.AccentColour = valueText.Colour = colours.Red;
|
||||||
|
else if (value.adjustedValue < value.baseValue)
|
||||||
|
modBar.AccentColour = valueText.Colour = colours.BlueDark;
|
||||||
|
else
|
||||||
|
modBar.AccentColour = valueText.Colour = Color4.White;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,13 +182,22 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
BackgroundColour = Color4.White.Opacity(0.5f),
|
BackgroundColour = Color4.White.Opacity(0.5f),
|
||||||
Padding = new MarginPadding { Left = name_width + 10, Right = value_width + 10 },
|
Padding = new MarginPadding { Left = name_width + 10, Right = value_width + 10 },
|
||||||
},
|
},
|
||||||
|
modBar = new Bar
|
||||||
|
{
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Alpha = 0.5f,
|
||||||
|
Height = 5,
|
||||||
|
Padding = new MarginPadding { Left = name_width + 10, Right = value_width + 10 },
|
||||||
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
Width = value_width,
|
Width = value_width,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Child = value = new OsuSpriteText
|
Child = valueText = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -16,7 +16,8 @@ namespace osu.Game.Storyboards
|
|||||||
{
|
{
|
||||||
public CommandTimeline<float> X = new CommandTimeline<float>();
|
public CommandTimeline<float> X = new CommandTimeline<float>();
|
||||||
public CommandTimeline<float> Y = new CommandTimeline<float>();
|
public CommandTimeline<float> Y = new CommandTimeline<float>();
|
||||||
public CommandTimeline<Vector2> Scale = new CommandTimeline<Vector2>();
|
public CommandTimeline<float> Scale = new CommandTimeline<float>();
|
||||||
|
public CommandTimeline<Vector2> VectorScale = new CommandTimeline<Vector2>();
|
||||||
public CommandTimeline<float> Rotation = new CommandTimeline<float>();
|
public CommandTimeline<float> Rotation = new CommandTimeline<float>();
|
||||||
public CommandTimeline<Color4> Colour = new CommandTimeline<Color4>();
|
public CommandTimeline<Color4> Colour = new CommandTimeline<Color4>();
|
||||||
public CommandTimeline<float> Alpha = new CommandTimeline<float>();
|
public CommandTimeline<float> Alpha = new CommandTimeline<float>();
|
||||||
|
@ -8,21 +8,71 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Animations;
|
using osu.Framework.Graphics.Animations;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Storyboards.Drawables
|
namespace osu.Game.Storyboards.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableStoryboardAnimation : TextureAnimation, IFlippable
|
public class DrawableStoryboardAnimation : TextureAnimation, IFlippable, IVectorScalable
|
||||||
{
|
{
|
||||||
public StoryboardAnimation Animation { get; private set; }
|
public StoryboardAnimation Animation { get; private set; }
|
||||||
|
|
||||||
public bool FlipH { get; set; }
|
private bool flipH;
|
||||||
public bool FlipV { get; set; }
|
|
||||||
|
public bool FlipH
|
||||||
|
{
|
||||||
|
get => flipH;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (flipH == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
flipH = value;
|
||||||
|
Invalidate(Invalidation.MiscGeometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool flipV;
|
||||||
|
|
||||||
|
public bool FlipV
|
||||||
|
{
|
||||||
|
get => flipV;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (flipV == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
flipV = value;
|
||||||
|
Invalidate(Invalidation.MiscGeometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2 vectorScale = Vector2.One;
|
||||||
|
|
||||||
|
public Vector2 VectorScale
|
||||||
|
{
|
||||||
|
get => vectorScale;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (Math.Abs(value.X) < Precision.FLOAT_EPSILON)
|
||||||
|
value.X = Precision.FLOAT_EPSILON;
|
||||||
|
if (Math.Abs(value.Y) < Precision.FLOAT_EPSILON)
|
||||||
|
value.Y = Precision.FLOAT_EPSILON;
|
||||||
|
|
||||||
|
if (vectorScale == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Validation.IsFinite(value)) throw new ArgumentException($@"{nameof(VectorScale)} must be finite, but is {value}.");
|
||||||
|
|
||||||
|
vectorScale = value;
|
||||||
|
Invalidate(Invalidation.MiscGeometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override bool RemoveWhenNotAlive => false;
|
public override bool RemoveWhenNotAlive => false;
|
||||||
|
|
||||||
protected override Vector2 DrawScale
|
protected override Vector2 DrawScale
|
||||||
=> new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y);
|
=> new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y) * VectorScale;
|
||||||
|
|
||||||
public override Anchor Origin
|
public override Anchor Origin
|
||||||
{
|
{
|
||||||
|
@ -8,21 +8,71 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Storyboards.Drawables
|
namespace osu.Game.Storyboards.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableStoryboardSprite : Sprite, IFlippable
|
public class DrawableStoryboardSprite : Sprite, IFlippable, IVectorScalable
|
||||||
{
|
{
|
||||||
public StoryboardSprite Sprite { get; private set; }
|
public StoryboardSprite Sprite { get; private set; }
|
||||||
|
|
||||||
public bool FlipH { get; set; }
|
private bool flipH;
|
||||||
public bool FlipV { get; set; }
|
|
||||||
|
public bool FlipH
|
||||||
|
{
|
||||||
|
get => flipH;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (flipH == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
flipH = value;
|
||||||
|
Invalidate(Invalidation.MiscGeometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool flipV;
|
||||||
|
|
||||||
|
public bool FlipV
|
||||||
|
{
|
||||||
|
get => flipV;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (flipV == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
flipV = value;
|
||||||
|
Invalidate(Invalidation.MiscGeometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2 vectorScale = Vector2.One;
|
||||||
|
|
||||||
|
public Vector2 VectorScale
|
||||||
|
{
|
||||||
|
get => vectorScale;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (Math.Abs(value.X) < Precision.FLOAT_EPSILON)
|
||||||
|
value.X = Precision.FLOAT_EPSILON;
|
||||||
|
if (Math.Abs(value.Y) < Precision.FLOAT_EPSILON)
|
||||||
|
value.Y = Precision.FLOAT_EPSILON;
|
||||||
|
|
||||||
|
if (vectorScale == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Validation.IsFinite(value)) throw new ArgumentException($@"{nameof(VectorScale)} must be finite, but is {value}.");
|
||||||
|
|
||||||
|
vectorScale = value;
|
||||||
|
Invalidate(Invalidation.MiscGeometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override bool RemoveWhenNotAlive => false;
|
public override bool RemoveWhenNotAlive => false;
|
||||||
|
|
||||||
protected override Vector2 DrawScale
|
protected override Vector2 DrawScale
|
||||||
=> new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y);
|
=> new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y) * VectorScale;
|
||||||
|
|
||||||
public override Anchor Origin
|
public override Anchor Origin
|
||||||
{
|
{
|
||||||
|
@ -6,13 +6,13 @@ using osu.Framework.Graphics.Transforms;
|
|||||||
|
|
||||||
namespace osu.Game.Storyboards.Drawables
|
namespace osu.Game.Storyboards.Drawables
|
||||||
{
|
{
|
||||||
public interface IFlippable : ITransformable
|
internal interface IFlippable : ITransformable
|
||||||
{
|
{
|
||||||
bool FlipH { get; set; }
|
bool FlipH { get; set; }
|
||||||
bool FlipV { get; set; }
|
bool FlipV { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TransformFlipH : Transform<bool, IFlippable>
|
internal class TransformFlipH : Transform<bool, IFlippable>
|
||||||
{
|
{
|
||||||
private bool valueAt(double time)
|
private bool valueAt(double time)
|
||||||
=> time < EndTime ? StartValue : EndValue;
|
=> time < EndTime ? StartValue : EndValue;
|
||||||
@ -23,7 +23,7 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
protected override void ReadIntoStartValue(IFlippable d) => StartValue = d.FlipH;
|
protected override void ReadIntoStartValue(IFlippable d) => StartValue = d.FlipH;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TransformFlipV : Transform<bool, IFlippable>
|
internal class TransformFlipV : Transform<bool, IFlippable>
|
||||||
{
|
{
|
||||||
private bool valueAt(double time)
|
private bool valueAt(double time)
|
||||||
=> time < EndTime ? StartValue : EndValue;
|
=> time < EndTime ? StartValue : EndValue;
|
||||||
@ -34,7 +34,7 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
protected override void ReadIntoStartValue(IFlippable d) => StartValue = d.FlipV;
|
protected override void ReadIntoStartValue(IFlippable d) => StartValue = d.FlipV;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class FlippableExtensions
|
internal static class FlippableExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adjusts <see cref="IFlippable.FlipH"/> after a delay.
|
/// Adjusts <see cref="IFlippable.FlipH"/> after a delay.
|
||||||
|
21
osu.Game/Storyboards/Drawables/IVectorScalable.cs
Normal file
21
osu.Game/Storyboards/Drawables/IVectorScalable.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Transforms;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Storyboards.Drawables
|
||||||
|
{
|
||||||
|
internal interface IVectorScalable : ITransformable
|
||||||
|
{
|
||||||
|
Vector2 VectorScale { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class VectorScalableExtensions
|
||||||
|
{
|
||||||
|
public static TransformSequence<T> VectorScaleTo<T>(this T target, Vector2 newVectorScale, double duration = 0, Easing easing = Easing.None)
|
||||||
|
where T : class, IVectorScalable
|
||||||
|
=> target.TransformTo(nameof(IVectorScalable.VectorScale), newVectorScale, duration, easing);
|
||||||
|
}
|
||||||
|
}
|
@ -65,20 +65,30 @@ namespace osu.Game.Storyboards
|
|||||||
{
|
{
|
||||||
applyCommands(drawable, getCommands(g => g.X, triggeredGroups), (d, value) => d.X = value, (d, value, duration, easing) => d.MoveToX(value, duration, easing));
|
applyCommands(drawable, getCommands(g => g.X, triggeredGroups), (d, value) => d.X = value, (d, value, duration, easing) => d.MoveToX(value, duration, easing));
|
||||||
applyCommands(drawable, getCommands(g => g.Y, triggeredGroups), (d, value) => d.Y = value, (d, value, duration, easing) => d.MoveToY(value, duration, easing));
|
applyCommands(drawable, getCommands(g => g.Y, triggeredGroups), (d, value) => d.Y = value, (d, value, duration, easing) => d.MoveToY(value, duration, easing));
|
||||||
applyCommands(drawable, getCommands(g => g.Scale, triggeredGroups), (d, value) => d.Scale = value, (d, value, duration, easing) => d.ScaleTo(value, duration, easing));
|
applyCommands(drawable, getCommands(g => g.Scale, triggeredGroups), (d, value) => d.Scale = new Vector2(value), (d, value, duration, easing) => d.ScaleTo(value, duration, easing));
|
||||||
applyCommands(drawable, getCommands(g => g.Rotation, triggeredGroups), (d, value) => d.Rotation = value, (d, value, duration, easing) => d.RotateTo(value, duration, easing));
|
applyCommands(drawable, getCommands(g => g.Rotation, triggeredGroups), (d, value) => d.Rotation = value, (d, value, duration, easing) => d.RotateTo(value, duration, easing));
|
||||||
applyCommands(drawable, getCommands(g => g.Colour, triggeredGroups), (d, value) => d.Colour = value, (d, value, duration, easing) => d.FadeColour(value, duration, easing));
|
applyCommands(drawable, getCommands(g => g.Colour, triggeredGroups), (d, value) => d.Colour = value, (d, value, duration, easing) => d.FadeColour(value, duration, easing));
|
||||||
applyCommands(drawable, getCommands(g => g.Alpha, triggeredGroups), (d, value) => d.Alpha = value, (d, value, duration, easing) => d.FadeTo(value, duration, easing));
|
applyCommands(drawable, getCommands(g => g.Alpha, triggeredGroups), (d, value) => d.Alpha = value, (d, value, duration, easing) => d.FadeTo(value, duration, easing));
|
||||||
applyCommands(drawable, getCommands(g => g.BlendingParameters, triggeredGroups), (d, value) => d.Blending = value, (d, value, duration, easing) => d.TransformBlendingMode(value, duration), false);
|
applyCommands(drawable, getCommands(g => g.BlendingParameters, triggeredGroups), (d, value) => d.Blending = value, (d, value, duration, easing) => d.TransformBlendingMode(value, duration),
|
||||||
|
false);
|
||||||
|
|
||||||
|
if (drawable is IVectorScalable vectorScalable)
|
||||||
|
{
|
||||||
|
applyCommands(drawable, getCommands(g => g.VectorScale, triggeredGroups), (d, value) => vectorScalable.VectorScale = value,
|
||||||
|
(d, value, duration, easing) => vectorScalable.VectorScaleTo(value, duration, easing));
|
||||||
|
}
|
||||||
|
|
||||||
if (drawable is IFlippable flippable)
|
if (drawable is IFlippable flippable)
|
||||||
{
|
{
|
||||||
applyCommands(drawable, getCommands(g => g.FlipH, triggeredGroups), (d, value) => flippable.FlipH = value, (d, value, duration, easing) => flippable.TransformFlipH(value, duration), false);
|
applyCommands(drawable, getCommands(g => g.FlipH, triggeredGroups), (d, value) => flippable.FlipH = value, (d, value, duration, easing) => flippable.TransformFlipH(value, duration),
|
||||||
applyCommands(drawable, getCommands(g => g.FlipV, triggeredGroups), (d, value) => flippable.FlipV = value, (d, value, duration, easing) => flippable.TransformFlipV(value, duration), false);
|
false);
|
||||||
|
applyCommands(drawable, getCommands(g => g.FlipV, triggeredGroups), (d, value) => flippable.FlipV = value, (d, value, duration, easing) => flippable.TransformFlipV(value, duration),
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyCommands<T>(Drawable drawable, IEnumerable<CommandTimeline<T>.TypedCommand> commands, DrawablePropertyInitializer<T> initializeProperty, DrawableTransformer<T> transform, bool alwaysInitialize = true)
|
private void applyCommands<T>(Drawable drawable, IEnumerable<CommandTimeline<T>.TypedCommand> commands, DrawablePropertyInitializer<T> initializeProperty, DrawableTransformer<T> transform,
|
||||||
|
bool alwaysInitialize = true)
|
||||||
where T : struct
|
where T : struct
|
||||||
{
|
{
|
||||||
var initialized = false;
|
var initialized = false;
|
||||||
|
@ -26,9 +26,9 @@ namespace osu.Game.Users
|
|||||||
[JsonProperty(@"country")]
|
[JsonProperty(@"country")]
|
||||||
public Country Country;
|
public Country Country;
|
||||||
|
|
||||||
public Bindable<UserStatus> Status = new Bindable<UserStatus>();
|
public readonly Bindable<UserStatus> Status = new Bindable<UserStatus>();
|
||||||
|
|
||||||
public IBindable<UserActivity> Activity = new Bindable<UserActivity>();
|
public readonly Bindable<UserActivity> Activity = new Bindable<UserActivity>();
|
||||||
|
|
||||||
//public Team Team;
|
//public Team Team;
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ namespace osu.Game.Users
|
|||||||
|
|
||||||
public class InLobby : UserActivity
|
public class InLobby : UserActivity
|
||||||
{
|
{
|
||||||
public override string Status => @"In a Multiplayer Lobby";
|
public override string Status => @"In a multiplayer lobby";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user