mirror of
https://github.com/ppy/osu.git
synced 2025-03-21 13:57:45 +08:00
Merge branch 'master' into pp-counter-alloc-reduction
This commit is contained in:
commit
6fb06d69cc
@ -52,7 +52,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.304.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.308.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.314.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
|
||||
public override ISkin CreateLegacySkinProvider(ISkin skin, IBeatmap beatmap) => new CatchLegacySkinTransformer(skin);
|
||||
|
||||
public override PerformanceCalculator CreatePerformanceCalculator() => new CatchPerformanceCalculator(this);
|
||||
public override PerformanceCalculator CreatePerformanceCalculator() => new CatchPerformanceCalculator();
|
||||
|
||||
public int LegacyID => 2;
|
||||
|
||||
|
@ -19,8 +19,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
private int tinyTicksMissed;
|
||||
private int misses;
|
||||
|
||||
public CatchPerformanceCalculator(Ruleset ruleset)
|
||||
: base(ruleset)
|
||||
public CatchPerformanceCalculator()
|
||||
: base(new CatchRuleset())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
private int countMeh;
|
||||
private int countMiss;
|
||||
|
||||
public ManiaPerformanceCalculator(Ruleset ruleset)
|
||||
: base(ruleset)
|
||||
public ManiaPerformanceCalculator()
|
||||
: base(new ManiaRuleset())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
|
||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap, this);
|
||||
|
||||
public override PerformanceCalculator CreatePerformanceCalculator() => new ManiaPerformanceCalculator(this);
|
||||
public override PerformanceCalculator CreatePerformanceCalculator() => new ManiaPerformanceCalculator();
|
||||
|
||||
public const string SHORT_NAME = "mania";
|
||||
|
||||
|
@ -22,8 +22,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
private double effectiveMissCount;
|
||||
|
||||
public OsuPerformanceCalculator(Ruleset ruleset)
|
||||
: base(ruleset)
|
||||
public OsuPerformanceCalculator()
|
||||
: base(new OsuRuleset())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -2,23 +2,22 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osuTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Skinning;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
@ -126,18 +125,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
}
|
||||
|
||||
Samples.Samples = HitObject.TailSamples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
||||
|
||||
var slidingSamples = new List<ISampleInfo>();
|
||||
|
||||
var normalSample = HitObject.Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL);
|
||||
if (normalSample != null)
|
||||
slidingSamples.Add(HitObject.SampleControlPoint.ApplyTo(normalSample).With("sliderslide"));
|
||||
|
||||
var whistleSample = HitObject.Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_WHISTLE);
|
||||
if (whistleSample != null)
|
||||
slidingSamples.Add(HitObject.SampleControlPoint.ApplyTo(whistleSample).With("sliderwhistle"));
|
||||
|
||||
slidingSample.Samples = slidingSamples.ToArray();
|
||||
slidingSample.Samples = HitObject.CreateSlidingSamples().Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
||||
}
|
||||
|
||||
public override void StopAllSamples()
|
||||
|
@ -74,6 +74,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue));
|
||||
}
|
||||
|
||||
protected override void LoadSamples()
|
||||
{
|
||||
// Tail models don't actually get samples, as the playback is handled by DrawableSlider.
|
||||
// This override is only here for visibility in explaining this weird flow.
|
||||
}
|
||||
|
||||
public override void PlaySamples()
|
||||
{
|
||||
// Tail models don't actually get samples, as the playback is handled by DrawableSlider.
|
||||
// This override is only here for visibility in explaining this weird flow.
|
||||
}
|
||||
|
||||
protected override void UpdateInitialTransforms()
|
||||
{
|
||||
base.UpdateInitialTransforms();
|
||||
|
@ -121,15 +121,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
base.LoadSamples();
|
||||
|
||||
var firstSample = HitObject.Samples.FirstOrDefault();
|
||||
|
||||
if (firstSample != null)
|
||||
{
|
||||
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample).With("spinnerspin");
|
||||
|
||||
spinningSample.Samples = new ISampleInfo[] { clone };
|
||||
spinningSample.Frequency.Value = spinning_sample_initial_frequency;
|
||||
}
|
||||
spinningSample.Samples = HitObject.CreateSpinningSamples().Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
||||
spinningSample.Frequency.Value = spinning_sample_initial_frequency;
|
||||
}
|
||||
|
||||
private void updateSpinningSample(ValueChangedEvent<bool> tracking)
|
||||
|
@ -29,6 +29,23 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed.
|
||||
}
|
||||
|
||||
public override IList<HitSampleInfo> AuxiliarySamples => CreateSlidingSamples().Concat(TailSamples).ToArray();
|
||||
|
||||
public IList<HitSampleInfo> CreateSlidingSamples()
|
||||
{
|
||||
var slidingSamples = new List<HitSampleInfo>();
|
||||
|
||||
var normalSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL);
|
||||
if (normalSample != null)
|
||||
slidingSamples.Add(normalSample.With("sliderslide"));
|
||||
|
||||
var whistleSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_WHISTLE);
|
||||
if (whistleSample != null)
|
||||
slidingSamples.Add(whistleSample.With("sliderwhistle"));
|
||||
|
||||
return slidingSamples;
|
||||
}
|
||||
|
||||
private readonly Cached<Vector2> endPositionCache = new Cached<Vector2>();
|
||||
|
||||
public override Vector2 EndPosition => endPositionCache.IsValid ? endPositionCache.Value : endPositionCache.Value = Position + this.CurvePositionAt(1);
|
||||
|
@ -1,7 +1,11 @@
|
||||
// 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 System.Threading;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
@ -73,5 +77,20 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
public override Judgement CreateJudgement() => new OsuJudgement();
|
||||
|
||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||
|
||||
public override IList<HitSampleInfo> AuxiliarySamples => CreateSpinningSamples();
|
||||
|
||||
public HitSampleInfo[] CreateSpinningSamples()
|
||||
{
|
||||
var referenceSample = Samples.FirstOrDefault();
|
||||
|
||||
if (referenceSample == null)
|
||||
return Array.Empty<HitSampleInfo>();
|
||||
|
||||
return new[]
|
||||
{
|
||||
SampleControlPoint.ApplyTo(referenceSample).With("spinnerspin")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -213,7 +213,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new OsuDifficultyCalculator(RulesetInfo, beatmap);
|
||||
|
||||
public override PerformanceCalculator CreatePerformanceCalculator() => new OsuPerformanceCalculator(this);
|
||||
public override PerformanceCalculator CreatePerformanceCalculator() => new OsuPerformanceCalculator();
|
||||
|
||||
public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this);
|
||||
|
||||
|
@ -19,8 +19,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
private int countMeh;
|
||||
private int countMiss;
|
||||
|
||||
public TaikoPerformanceCalculator(Ruleset ruleset)
|
||||
: base(ruleset)
|
||||
public TaikoPerformanceCalculator()
|
||||
: base(new TaikoRuleset())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Taiko
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new TaikoDifficultyCalculator(RulesetInfo, beatmap);
|
||||
|
||||
public override PerformanceCalculator CreatePerformanceCalculator() => new TaikoPerformanceCalculator(this);
|
||||
public override PerformanceCalculator CreatePerformanceCalculator() => new TaikoPerformanceCalculator();
|
||||
|
||||
public int LegacyID => 1;
|
||||
|
||||
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
@ -52,6 +53,49 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("skinnable sprite has correct size", () => sprites.Any(s => Precision.AlmostEquals(s.ChildrenOfType<SkinnableSprite>().Single().Size, new Vector2(128, 128))));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFlippedSprite()
|
||||
{
|
||||
const string lookup_name = "hitcircleoverlay";
|
||||
|
||||
AddStep("allow skin lookup", () => storyboard.UseSkinSprites = true);
|
||||
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
||||
AddStep("flip sprites", () => sprites.ForEach(s =>
|
||||
{
|
||||
s.FlipH = true;
|
||||
s.FlipV = true;
|
||||
}));
|
||||
AddAssert("origin flipped", () => sprites.All(s => s.Origin == Anchor.BottomRight));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNegativeScale()
|
||||
{
|
||||
const string lookup_name = "hitcircleoverlay";
|
||||
|
||||
AddStep("allow skin lookup", () => storyboard.UseSkinSprites = true);
|
||||
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
||||
AddStep("scale sprite", () => sprites.ForEach(s => s.VectorScale = new Vector2(-1)));
|
||||
AddAssert("origin flipped", () => sprites.All(s => s.Origin == Anchor.BottomRight));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNegativeScaleWithFlippedSprite()
|
||||
{
|
||||
const string lookup_name = "hitcircleoverlay";
|
||||
|
||||
AddStep("allow skin lookup", () => storyboard.UseSkinSprites = true);
|
||||
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
||||
AddStep("scale sprite", () => sprites.ForEach(s => s.VectorScale = new Vector2(-1)));
|
||||
AddAssert("origin flipped", () => sprites.All(s => s.Origin == Anchor.BottomRight));
|
||||
AddStep("flip sprites", () => sprites.ForEach(s =>
|
||||
{
|
||||
s.FlipH = true;
|
||||
s.FlipV = true;
|
||||
}));
|
||||
AddAssert("origin back", () => sprites.All(s => s.Origin == Anchor.TopLeft));
|
||||
}
|
||||
|
||||
private DrawableStoryboardSprite createSprite(string lookupName, Anchor origin, Vector2 initialPosition)
|
||||
=> new DrawableStoryboardSprite(
|
||||
new StoryboardSprite(lookupName, origin, initialPosition)
|
||||
|
@ -48,7 +48,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
AddStep("create display", () => recreateDisplay(new OsuHitWindows(), 5));
|
||||
|
||||
AddRepeatStep("New random judgement", () => newJudgement(), 40);
|
||||
AddRepeatStep("New random judgement", () =>
|
||||
{
|
||||
double offset = RNG.Next(-150, 150);
|
||||
newJudgement(offset, drawableRuleset.HitWindows.ResultFor(offset));
|
||||
}, 400);
|
||||
|
||||
AddRepeatStep("New max negative", () => newJudgement(-drawableRuleset.HitWindows.WindowFor(HitResult.Meh)), 20);
|
||||
AddRepeatStep("New max positive", () => newJudgement(drawableRuleset.HitWindows.WindowFor(HitResult.Meh)), 20);
|
||||
|
@ -6,6 +6,8 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Layout;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Screens;
|
||||
@ -66,7 +68,7 @@ namespace osu.Game.Graphics.Containers
|
||||
this.targetMode = targetMode;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChild = sizableContainer = new AlwaysInputContainer
|
||||
InternalChild = sizableContainer = new SizeableAlwaysInputContainer(targetMode == ScalingMode.Everything)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
@ -209,13 +211,50 @@ namespace osu.Game.Graphics.Containers
|
||||
}
|
||||
}
|
||||
|
||||
private class AlwaysInputContainer : Container
|
||||
private class SizeableAlwaysInputContainer : Container
|
||||
{
|
||||
[Resolved]
|
||||
private GameHost host { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private ISafeArea safeArea { get; set; }
|
||||
|
||||
private readonly bool confineHostCursor;
|
||||
private readonly LayoutValue cursorRectCache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||
|
||||
public AlwaysInputContainer()
|
||||
/// <summary>
|
||||
/// Container used for sizing/positioning purposes in <see cref="ScalingContainer"/>. Always receives mouse input.
|
||||
/// </summary>
|
||||
/// <param name="confineHostCursor">Whether to confine the host cursor to the draw area of this container.</param>
|
||||
/// <remarks>Cursor confinement will abide by the <see cref="OsuSetting.ConfineMouseMode"/> setting.</remarks>
|
||||
public SizeableAlwaysInputContainer(bool confineHostCursor)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
this.confineHostCursor = confineHostCursor;
|
||||
|
||||
if (confineHostCursor)
|
||||
AddLayout(cursorRectCache);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (confineHostCursor && !cursorRectCache.IsValid)
|
||||
{
|
||||
updateHostCursorConfineRect();
|
||||
cursorRectCache.Validate();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateHostCursorConfineRect()
|
||||
{
|
||||
if (host.Window == null) return;
|
||||
|
||||
bool coversWholeScreen = Size == Vector2.One && safeArea.SafeAreaPadding.Value.Total == Vector2.Zero;
|
||||
host.Window.CursorConfineRect = coversWholeScreen ? (RectangleF?)null : ToScreenSpace(DrawRectangle).AABBFloat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1140,10 +1140,8 @@ namespace osu.Game
|
||||
MenuCursorContainer.CanShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false;
|
||||
}
|
||||
|
||||
protected virtual void ScreenChanged(IScreen current, IScreen newScreen)
|
||||
private void screenChanged(IScreen current, IScreen newScreen)
|
||||
{
|
||||
skinEditor.Reset();
|
||||
|
||||
switch (newScreen)
|
||||
{
|
||||
case IntroScreen intro:
|
||||
@ -1185,13 +1183,15 @@ namespace osu.Game
|
||||
else
|
||||
BackButton.Hide();
|
||||
}
|
||||
|
||||
skinEditor.SetTarget((Screen)newScreen);
|
||||
}
|
||||
|
||||
private void screenPushed(IScreen lastScreen, IScreen newScreen) => ScreenChanged(lastScreen, newScreen);
|
||||
private void screenPushed(IScreen lastScreen, IScreen newScreen) => screenChanged(lastScreen, newScreen);
|
||||
|
||||
private void screenExited(IScreen lastScreen, IScreen newScreen)
|
||||
{
|
||||
ScreenChanged(lastScreen, newScreen);
|
||||
screenChanged(lastScreen, newScreen);
|
||||
|
||||
if (newScreen == null)
|
||||
Exit();
|
||||
|
@ -69,7 +69,7 @@ namespace osu.Game
|
||||
/// </summary>
|
||||
private const double global_track_volume_adjust = 0.8;
|
||||
|
||||
public bool UseDevelopmentServer { get; }
|
||||
public virtual bool UseDevelopmentServer => DebugUtils.IsDebugBuild;
|
||||
|
||||
public virtual Version AssemblyVersion => Assembly.GetEntryAssembly()?.GetName().Version ?? new Version();
|
||||
|
||||
@ -177,7 +177,6 @@ namespace osu.Game
|
||||
|
||||
public OsuGameBase()
|
||||
{
|
||||
UseDevelopmentServer = DebugUtils.IsDebugBuild;
|
||||
Name = @"osu!";
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Threading;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
@ -67,6 +68,12 @@ namespace osu.Game.Rulesets.Objects
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Any samples which may be used by this hit object that are non-standard.
|
||||
/// This is used only to preload these samples ahead of time.
|
||||
/// </summary>
|
||||
public virtual IList<HitSampleInfo> AuxiliarySamples => ImmutableList<HitSampleInfo>.Empty;
|
||||
|
||||
public SampleControlPoint SampleControlPoint = SampleControlPoint.DEFAULT;
|
||||
public DifficultyControlPoint DifficultyControlPoint = DifficultyControlPoint.DEFAULT;
|
||||
|
||||
|
@ -500,12 +500,12 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
=> new LegacyHitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newVolume.GetOr(Volume), newCustomSampleBank.GetOr(CustomSampleBank), newIsLayered.GetOr(IsLayered));
|
||||
|
||||
public bool Equals(LegacyHitSampleInfo? other)
|
||||
=> base.Equals(other) && CustomSampleBank == other.CustomSampleBank && IsLayered == other.IsLayered;
|
||||
=> base.Equals(other) && CustomSampleBank == other.CustomSampleBank;
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
=> obj is LegacyHitSampleInfo other && Equals(other);
|
||||
|
||||
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), CustomSampleBank, IsLayered);
|
||||
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), CustomSampleBank);
|
||||
}
|
||||
|
||||
private class FileHitSampleInfo : LegacyHitSampleInfo, IEquatable<FileHitSampleInfo>
|
||||
|
@ -3,22 +3,22 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
@ -264,10 +264,25 @@ namespace osu.Game.Rulesets.UI
|
||||
var entry = CreateLifetimeEntry(hitObject);
|
||||
lifetimeEntryMap[entry.HitObject] = entry;
|
||||
|
||||
preloadSamples(hitObject);
|
||||
|
||||
HitObjectContainer.Add(entry);
|
||||
OnHitObjectAdded(entry.HitObject);
|
||||
}
|
||||
|
||||
private void preloadSamples(HitObject hitObject)
|
||||
{
|
||||
// prepare sample pools ahead of time so we're not initialising at runtime.
|
||||
foreach (var sample in hitObject.Samples)
|
||||
prepareSamplePool(hitObject.SampleControlPoint.ApplyTo(sample));
|
||||
|
||||
foreach (var sample in hitObject.AuxiliarySamples)
|
||||
prepareSamplePool(hitObject.SampleControlPoint.ApplyTo(sample));
|
||||
|
||||
foreach (var nestedObject in hitObject.NestedHitObjects)
|
||||
preloadSamples(nestedObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a <see cref="HitObjectLifetimeEntry"/> for a pooled <see cref="HitObject"/> from this <see cref="Playfield"/>.
|
||||
/// </summary>
|
||||
@ -330,22 +345,7 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
DrawableHitObject IPooledHitObjectProvider.GetPooledDrawableRepresentation(HitObject hitObject, DrawableHitObject parent)
|
||||
{
|
||||
var lookupType = hitObject.GetType();
|
||||
|
||||
IDrawablePool pool;
|
||||
|
||||
// Tests may add derived hitobject instances for which pools don't exist. Try to find any applicable pool and dynamically assign the type if the pool exists.
|
||||
if (!pools.TryGetValue(lookupType, out pool))
|
||||
{
|
||||
foreach (var (t, p) in pools)
|
||||
{
|
||||
if (!t.IsInstanceOfType(hitObject))
|
||||
continue;
|
||||
|
||||
pools[lookupType] = pool = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var pool = prepareDrawableHitObjectPool(hitObject);
|
||||
|
||||
return (DrawableHitObject)pool?.Get(d =>
|
||||
{
|
||||
@ -372,14 +372,39 @@ namespace osu.Game.Rulesets.UI
|
||||
});
|
||||
}
|
||||
|
||||
private IDrawablePool prepareDrawableHitObjectPool(HitObject hitObject)
|
||||
{
|
||||
var lookupType = hitObject.GetType();
|
||||
|
||||
IDrawablePool pool;
|
||||
|
||||
// Tests may add derived hitobject instances for which pools don't exist. Try to find any applicable pool and dynamically assign the type if the pool exists.
|
||||
if (!pools.TryGetValue(lookupType, out pool))
|
||||
{
|
||||
foreach (var (t, p) in pools)
|
||||
{
|
||||
if (!t.IsInstanceOfType(hitObject))
|
||||
continue;
|
||||
|
||||
pools[lookupType] = pool = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
private readonly Dictionary<ISampleInfo, DrawablePool<PoolableSkinnableSample>> samplePools = new Dictionary<ISampleInfo, DrawablePool<PoolableSkinnableSample>>();
|
||||
|
||||
public PoolableSkinnableSample GetPooledSample(ISampleInfo sampleInfo)
|
||||
{
|
||||
if (!samplePools.TryGetValue(sampleInfo, out var existingPool))
|
||||
AddInternal(samplePools[sampleInfo] = existingPool = new DrawableSamplePool(sampleInfo, 1));
|
||||
public PoolableSkinnableSample GetPooledSample(ISampleInfo sampleInfo) => prepareSamplePool(sampleInfo).Get();
|
||||
|
||||
return existingPool.Get();
|
||||
private DrawablePool<PoolableSkinnableSample> prepareSamplePool(ISampleInfo sampleInfo)
|
||||
{
|
||||
if (samplePools.TryGetValue(sampleInfo, out var pool)) return pool;
|
||||
|
||||
AddInternal(samplePools[sampleInfo] = pool = new DrawableSamplePool(sampleInfo, 1));
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
private class DrawableSamplePool : DrawablePool<PoolableSkinnableSample>
|
||||
|
@ -13,14 +13,17 @@ using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using Realms;
|
||||
@ -55,7 +58,8 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
private const int exit_delay = 3000;
|
||||
|
||||
private Sample seeya;
|
||||
private SkinnableSound skinnableSeeya;
|
||||
private ISample seeya;
|
||||
|
||||
protected virtual string SeeyaSampleName => "Intro/seeya";
|
||||
|
||||
@ -90,14 +94,18 @@ namespace osu.Game.Screens.Menu
|
||||
private BeatmapManager beatmaps { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config, Framework.Game game, RealmAccess realm)
|
||||
private void load(OsuConfigManager config, Framework.Game game, RealmAccess realm, IAPIProvider api)
|
||||
{
|
||||
// prevent user from changing beatmap while the intro is still running.
|
||||
beatmap = Beatmap.BeginLease(false);
|
||||
|
||||
MenuVoice = config.GetBindable<bool>(OsuSetting.MenuVoice);
|
||||
MenuMusic = config.GetBindable<bool>(OsuSetting.MenuMusic);
|
||||
seeya = audio.Samples.Get(SeeyaSampleName);
|
||||
|
||||
if (api.LocalUser.Value.IsSupporter)
|
||||
AddInternal(skinnableSeeya = new SkinnableSound(new SampleInfo(SeeyaSampleName)));
|
||||
else
|
||||
seeya = audio.Samples.Get(SeeyaSampleName);
|
||||
|
||||
// if the user has requested not to play theme music, we should attempt to find a random beatmap from their collection.
|
||||
if (!MenuMusic.Value)
|
||||
@ -201,7 +209,15 @@ namespace osu.Game.Screens.Menu
|
||||
// we also handle the exit transition.
|
||||
if (MenuVoice.Value)
|
||||
{
|
||||
seeya.Play();
|
||||
if (skinnableSeeya != null)
|
||||
{
|
||||
// resuming a screen (i.e. calling OnResume) happens before the screen itself becomes alive,
|
||||
// therefore skinnable samples may not be updated yet with the recently selected skin.
|
||||
// schedule after children to ensure skinnable samples have processed skin changes before playing.
|
||||
ScheduleAfterChildren(() => skinnableSeeya.Play());
|
||||
}
|
||||
else
|
||||
seeya.Play();
|
||||
|
||||
// if playing the outro voice, we have more time to have fun with the background track.
|
||||
// initially fade to almost silent then ramp out over the remaining time.
|
||||
|
@ -13,7 +13,10 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
@ -23,8 +26,11 @@ namespace osu.Game.Screens.Menu
|
||||
protected override string BeatmapHash => "64e00d7022195959bfa3109d09c2e2276c8f12f486b91fcf6175583e973b48f2";
|
||||
protected override string BeatmapFile => "welcome.osz";
|
||||
private const double delay_step_two = 2142;
|
||||
private Sample welcome;
|
||||
private Sample pianoReverb;
|
||||
|
||||
private SkinnableSound skinnableWelcome;
|
||||
private ISample welcome;
|
||||
|
||||
private ISample pianoReverb;
|
||||
protected override string SeeyaSampleName => "Intro/Welcome/seeya";
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => background = new BackgroundScreenDefault(false)
|
||||
@ -40,10 +46,15 @@ namespace osu.Game.Screens.Menu
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
private void load(AudioManager audio, IAPIProvider api)
|
||||
{
|
||||
if (MenuVoice.Value)
|
||||
welcome = audio.Samples.Get(@"Intro/Welcome/welcome");
|
||||
{
|
||||
if (api.LocalUser.Value.IsSupporter)
|
||||
AddInternal(skinnableWelcome = new SkinnableSound(new SampleInfo(@"Intro/Welcome/welcome")));
|
||||
else
|
||||
welcome = audio.Samples.Get(@"Intro/Welcome/welcome");
|
||||
}
|
||||
|
||||
pianoReverb = audio.Samples.Get(@"Intro/Welcome/welcome_piano");
|
||||
}
|
||||
@ -65,7 +76,10 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
AddInternal(intro);
|
||||
|
||||
welcome?.Play();
|
||||
if (skinnableWelcome != null)
|
||||
skinnableWelcome.Play();
|
||||
else
|
||||
welcome?.Play();
|
||||
|
||||
var reverbChannel = pianoReverb?.Play();
|
||||
if (reverbChannel != null)
|
||||
@ -100,13 +114,13 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
private class WelcomeIntroSequence : Container
|
||||
{
|
||||
private Sprite welcomeText;
|
||||
private Drawable welcomeText;
|
||||
private Container scaleContainer;
|
||||
|
||||
public LogoVisualisation LogoVisualisation { get; private set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
private void load(TextureStore textures, IAPIProvider api)
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
Anchor = Anchor.Centre;
|
||||
@ -135,15 +149,17 @@ namespace osu.Game.Screens.Menu
|
||||
Size = new Vector2(480),
|
||||
Colour = Color4.Black
|
||||
},
|
||||
welcomeText = new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Texture = textures.Get(@"Intro/Welcome/welcome_text")
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if (api.LocalUser.Value.IsSupporter)
|
||||
scaleContainer.Add(welcomeText = new SkinnableSprite(@"Intro/Welcome/welcome_text"));
|
||||
else
|
||||
scaleContainer.Add(welcomeText = new Sprite { Texture = textures.Get(@"Intro/Welcome/welcome_text") });
|
||||
|
||||
welcomeText.Anchor = Anchor.Centre;
|
||||
welcomeText.Origin = Anchor.Centre;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
@ -334,6 +334,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
// ID must be unset as we use this as a marker for whether this is a client-side (not-yet-created) room or not.
|
||||
r.RoomID.Value = null;
|
||||
|
||||
// Null out dates because end date is not supported client-side and the settings overlay will populate a duration.
|
||||
r.EndDate.Value = null;
|
||||
r.Duration.Value = null;
|
||||
|
||||
Open(r);
|
||||
|
||||
joiningRoomOperation?.Dispose();
|
||||
|
@ -13,23 +13,13 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
{
|
||||
public class BarHitErrorMeter : HitErrorMeter
|
||||
{
|
||||
private const int arrow_move_duration = 400;
|
||||
|
||||
private const int judgement_line_width = 6;
|
||||
|
||||
private const int bar_height = 200;
|
||||
|
||||
private const int bar_width = 2;
|
||||
|
||||
private const int spacing = 2;
|
||||
|
||||
private const float chevron_size = 8;
|
||||
private const int judgement_line_width = 14;
|
||||
private const int judgement_line_height = 4;
|
||||
|
||||
private SpriteIcon arrow;
|
||||
private SpriteIcon iconEarly;
|
||||
@ -50,17 +40,94 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChild = new FillFlowContainer
|
||||
const int centre_marker_size = 8;
|
||||
const int bar_height = 200;
|
||||
const int bar_width = 2;
|
||||
const float chevron_size = 8;
|
||||
const float icon_size = 14;
|
||||
|
||||
var hitWindows = HitWindows.GetAllAvailableWindows().ToArray();
|
||||
|
||||
InternalChild = new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = bar_height,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(spacing, 0),
|
||||
Margin = new MarginPadding(2),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
colourBars = new Container
|
||||
{
|
||||
Name = "colour axis",
|
||||
X = chevron_size,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Width = judgement_line_width,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
iconEarly = new SpriteIcon
|
||||
{
|
||||
Y = -10,
|
||||
Size = new Vector2(icon_size),
|
||||
Icon = FontAwesome.Solid.ShippingFast,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
iconLate = new SpriteIcon
|
||||
{
|
||||
Y = 10,
|
||||
Size = new Vector2(icon_size),
|
||||
Icon = FontAwesome.Solid.Bicycle,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
colourBarsEarly = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Width = bar_width,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Height = 0.5f,
|
||||
Scale = new Vector2(1, -1),
|
||||
},
|
||||
colourBarsLate = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Width = bar_width,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Height = 0.5f,
|
||||
},
|
||||
new Circle
|
||||
{
|
||||
Name = "middle marker behind",
|
||||
Colour = GetColourForHitResult(hitWindows.Last().result),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(centre_marker_size),
|
||||
},
|
||||
judgementsContainer = new Container
|
||||
{
|
||||
Name = "judgements",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = judgement_line_width,
|
||||
},
|
||||
new Circle
|
||||
{
|
||||
Name = "middle marker in front",
|
||||
Colour = GetColourForHitResult(hitWindows.Last().result).Darken(0.3f),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(centre_marker_size),
|
||||
Scale = new Vector2(0.5f),
|
||||
},
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Name = "average chevron",
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Width = chevron_size,
|
||||
@ -75,58 +142,10 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
Size = new Vector2(chevron_size),
|
||||
}
|
||||
},
|
||||
colourBars = new Container
|
||||
{
|
||||
Width = bar_width,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
colourBarsEarly = new Container
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.5f,
|
||||
Scale = new Vector2(1, -1),
|
||||
},
|
||||
colourBarsLate = new Container
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.5f,
|
||||
},
|
||||
iconEarly = new SpriteIcon
|
||||
{
|
||||
Y = -10,
|
||||
Size = new Vector2(10),
|
||||
Icon = FontAwesome.Solid.ShippingFast,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
iconLate = new SpriteIcon
|
||||
{
|
||||
Y = 10,
|
||||
Size = new Vector2(10),
|
||||
Icon = FontAwesome.Solid.Bicycle,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
}
|
||||
},
|
||||
judgementsContainer = new Container
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Width = judgement_line_width,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
createColourBars();
|
||||
createColourBars(hitWindows);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -149,10 +168,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
iconLate.Rotation = -Rotation;
|
||||
}
|
||||
|
||||
private void createColourBars()
|
||||
private void createColourBars((HitResult result, double length)[] windows)
|
||||
{
|
||||
var windows = HitWindows.GetAllAvailableWindows().ToArray();
|
||||
|
||||
// max to avoid div-by-zero.
|
||||
maxHitWindow = Math.Max(1, windows.First().length);
|
||||
|
||||
@ -166,17 +183,11 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
colourBarsLate.Add(createColourBar(result, hitWindow, i == 0));
|
||||
}
|
||||
|
||||
// a little nub to mark the centre point.
|
||||
var centre = createColourBar(windows.Last().result, 0.01f);
|
||||
centre.Anchor = centre.Origin = Anchor.CentreLeft;
|
||||
centre.Width = 2.5f;
|
||||
colourBars.Add(centre);
|
||||
|
||||
Drawable createColourBar(HitResult result, float height, bool first = false)
|
||||
Drawable createColourBar(HitResult result, float height, bool requireGradient = false)
|
||||
{
|
||||
var colour = GetColourForHitResult(result);
|
||||
|
||||
if (first)
|
||||
if (requireGradient)
|
||||
{
|
||||
// the first bar needs gradient rendering.
|
||||
const float gradient_start = 0.8f;
|
||||
@ -220,6 +231,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
|
||||
protected override void OnNewJudgement(JudgementResult judgement)
|
||||
{
|
||||
const int arrow_move_duration = 800;
|
||||
|
||||
if (!judgement.IsHit || judgement.HitObject.HitWindows?.WindowFor(HitResult.Miss) == 0)
|
||||
return;
|
||||
|
||||
@ -243,46 +256,52 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
judgementsContainer.Add(new JudgementLine
|
||||
{
|
||||
Y = getRelativeJudgementPosition(judgement.TimeOffset),
|
||||
Origin = Anchor.CentreLeft,
|
||||
Colour = GetColourForHitResult(judgement.Type),
|
||||
});
|
||||
|
||||
arrow.MoveToY(
|
||||
getRelativeJudgementPosition(floatingAverage = floatingAverage * 0.9 + judgement.TimeOffset * 0.1)
|
||||
, arrow_move_duration, Easing.Out);
|
||||
, arrow_move_duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private float getRelativeJudgementPosition(double value) => Math.Clamp((float)((value / maxHitWindow) + 1) / 2, 0, 1);
|
||||
|
||||
internal class JudgementLine : CompositeDrawable
|
||||
{
|
||||
private const int judgement_fade_duration = 5000;
|
||||
|
||||
public JudgementLine()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
RelativePositionAxes = Axes.Y;
|
||||
Height = 3;
|
||||
Height = judgement_line_height;
|
||||
|
||||
InternalChild = new CircularContainer
|
||||
Blending = BlendingParameters.Additive;
|
||||
|
||||
Origin = Anchor.Centre;
|
||||
Anchor = Anchor.TopCentre;
|
||||
|
||||
InternalChild = new Circle
|
||||
{
|
||||
Masking = true,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.White,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
const int judgement_fade_in_duration = 100;
|
||||
const int judgement_fade_out_duration = 5000;
|
||||
|
||||
base.LoadComplete();
|
||||
|
||||
Alpha = 0;
|
||||
Width = 0;
|
||||
|
||||
this.ResizeWidthTo(1, 200, Easing.OutElasticHalf);
|
||||
this.FadeTo(0.8f, 150).Then().FadeOut(judgement_fade_duration).Expire();
|
||||
this
|
||||
.FadeTo(0.6f, judgement_fade_in_duration, Easing.OutQuint)
|
||||
.ResizeWidthTo(1, judgement_fade_in_duration, Easing.OutQuint)
|
||||
.Then()
|
||||
.FadeOut(judgement_fade_out_duration)
|
||||
.ResizeWidthTo(0, judgement_fade_out_duration, Easing.InQuint)
|
||||
.Expire();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
protected override bool StartHidden => true;
|
||||
|
||||
private readonly Drawable targetScreen;
|
||||
private Drawable targetScreen;
|
||||
|
||||
private OsuTextFlowContainer headerText;
|
||||
|
||||
@ -42,11 +42,13 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
private bool hasBegunMutating;
|
||||
|
||||
private Container content;
|
||||
|
||||
public SkinEditor(Drawable targetScreen)
|
||||
{
|
||||
this.targetScreen = targetScreen;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
UpdateTargetScreen(targetScreen);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -113,13 +115,9 @@ namespace osu.Game.Skinning.Editor
|
||||
Origin = Anchor.CentreLeft,
|
||||
RequestPlacement = placeComponent
|
||||
},
|
||||
new Container
|
||||
content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SkinBlueprintContainer(targetScreen),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -147,6 +145,21 @@ namespace osu.Game.Skinning.Editor
|
||||
}, true);
|
||||
}
|
||||
|
||||
public void UpdateTargetScreen(Drawable targetScreen)
|
||||
{
|
||||
this.targetScreen = targetScreen;
|
||||
|
||||
Scheduler.AddOnce(loadBlueprintContainer);
|
||||
|
||||
void loadBlueprintContainer()
|
||||
{
|
||||
content.Children = new Drawable[]
|
||||
{
|
||||
new SkinBlueprintContainer(targetScreen),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void skinChanged()
|
||||
{
|
||||
headerText.Clear();
|
||||
@ -171,7 +184,12 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
private void placeComponent(Type type)
|
||||
{
|
||||
var targetContainer = getTarget(SkinnableTarget.MainHUDComponents);
|
||||
var target = availableTargets.FirstOrDefault()?.Target;
|
||||
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
var targetContainer = getTarget(target.Value);
|
||||
|
||||
if (targetContainer == null)
|
||||
return;
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Diagnostics;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -8,6 +9,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Input.Bindings;
|
||||
|
||||
@ -114,15 +116,39 @@ namespace osu.Game.Skinning.Editor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exit any existing skin editor due to the game state changing.
|
||||
/// Set a new target screen which will be used to find skinnable components.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
public void SetTarget(Screen screen)
|
||||
{
|
||||
skinEditor?.Save();
|
||||
skinEditor?.Hide();
|
||||
skinEditor?.Expire();
|
||||
if (skinEditor == null) return;
|
||||
|
||||
skinEditor = null;
|
||||
skinEditor.Save();
|
||||
|
||||
// AddOnce with parameter will ensure the newest target is loaded if there is any overlap.
|
||||
Scheduler.AddOnce(setTarget, screen);
|
||||
}
|
||||
|
||||
private void setTarget(Screen target)
|
||||
{
|
||||
Debug.Assert(skinEditor != null);
|
||||
|
||||
if (!target.IsCurrentScreen())
|
||||
return;
|
||||
|
||||
if (!target.IsLoaded)
|
||||
{
|
||||
Scheduler.AddOnce(setTarget, target);
|
||||
return;
|
||||
}
|
||||
|
||||
if (skinEditor.State.Value == Visibility.Visible)
|
||||
skinEditor.UpdateTargetScreen(target);
|
||||
else
|
||||
{
|
||||
skinEditor.Hide();
|
||||
skinEditor.Expire();
|
||||
skinEditor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Animations;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
@ -73,31 +72,7 @@ namespace osu.Game.Storyboards.Drawables
|
||||
protected override Vector2 DrawScale
|
||||
=> new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y) * VectorScale;
|
||||
|
||||
public override Anchor Origin
|
||||
{
|
||||
get
|
||||
{
|
||||
var origin = base.Origin;
|
||||
|
||||
if (FlipH)
|
||||
{
|
||||
if (origin.HasFlagFast(Anchor.x0))
|
||||
origin = Anchor.x2 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
|
||||
else if (origin.HasFlagFast(Anchor.x2))
|
||||
origin = Anchor.x0 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
|
||||
}
|
||||
|
||||
if (FlipV)
|
||||
{
|
||||
if (origin.HasFlagFast(Anchor.y0))
|
||||
origin = Anchor.y2 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
|
||||
else if (origin.HasFlagFast(Anchor.y2))
|
||||
origin = Anchor.y0 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
|
||||
}
|
||||
|
||||
return origin;
|
||||
}
|
||||
}
|
||||
public override Anchor Origin => StoryboardExtensions.AdjustOrigin(base.Origin, VectorScale, FlipH, FlipV);
|
||||
|
||||
public override bool IsPresent
|
||||
=> !float.IsNaN(DrawPosition.X) && !float.IsNaN(DrawPosition.Y) && base.IsPresent;
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
@ -73,31 +72,7 @@ namespace osu.Game.Storyboards.Drawables
|
||||
protected override Vector2 DrawScale
|
||||
=> new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y) * VectorScale;
|
||||
|
||||
public override Anchor Origin
|
||||
{
|
||||
get
|
||||
{
|
||||
var origin = base.Origin;
|
||||
|
||||
if (FlipH)
|
||||
{
|
||||
if (origin.HasFlagFast(Anchor.x0))
|
||||
origin = Anchor.x2 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
|
||||
else if (origin.HasFlagFast(Anchor.x2))
|
||||
origin = Anchor.x0 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
|
||||
}
|
||||
|
||||
if (FlipV)
|
||||
{
|
||||
if (origin.HasFlagFast(Anchor.y0))
|
||||
origin = Anchor.y2 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
|
||||
else if (origin.HasFlagFast(Anchor.y2))
|
||||
origin = Anchor.y0 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
|
||||
}
|
||||
|
||||
return origin;
|
||||
}
|
||||
}
|
||||
public override Anchor Origin => StoryboardExtensions.AdjustOrigin(base.Origin, VectorScale, FlipH, FlipV);
|
||||
|
||||
public override bool IsPresent
|
||||
=> !float.IsNaN(DrawPosition.X) && !float.IsNaN(DrawPosition.Y) && base.IsPresent;
|
||||
|
43
osu.Game/Storyboards/StoryboardExtensions.cs
Normal file
43
osu.Game/Storyboards/StoryboardExtensions.cs
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Storyboards
|
||||
{
|
||||
public static class StoryboardExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Given an origin and a set of properties, adjust the origin to display the sprite/animation correctly.
|
||||
/// </summary>
|
||||
/// <param name="origin">The current origin.</param>
|
||||
/// <param name="vectorScale">The vector scale.</param>
|
||||
/// <param name="flipH">Whether the element is flipped horizontally.</param>
|
||||
/// <param name="flipV">Whether the element is flipped vertically.</param>
|
||||
/// <returns>The adjusted origin.</returns>
|
||||
public static Anchor AdjustOrigin(Anchor origin, Vector2 vectorScale, bool flipH, bool flipV)
|
||||
{
|
||||
// Either flip horizontally or negative X scale, but not both.
|
||||
if (flipH ^ (vectorScale.X < 0))
|
||||
{
|
||||
if (origin.HasFlagFast(Anchor.x0))
|
||||
origin = Anchor.x2 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
|
||||
else if (origin.HasFlagFast(Anchor.x2))
|
||||
origin = Anchor.x0 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
|
||||
}
|
||||
|
||||
// Either flip vertically or negative Y scale, but not both.
|
||||
if (flipV ^ (vectorScale.Y < 0))
|
||||
{
|
||||
if (origin.HasFlagFast(Anchor.y0))
|
||||
origin = Anchor.y2 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
|
||||
else if (origin.HasFlagFast(Anchor.y2))
|
||||
origin = Anchor.y0 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
|
||||
}
|
||||
|
||||
return origin;
|
||||
}
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.10.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.308.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.314.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.304.0" />
|
||||
<PackageReference Include="Sentry" Version="3.14.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
||||
|
@ -61,7 +61,7 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.308.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.314.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.304.0" />
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
|
||||
@ -84,7 +84,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.14" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.14" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.308.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.314.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user