Merge branch 'master' into argon-spinner-sides

This commit is contained in:
Dean Herbert
2022-11-03 13:21:22 +09:00
committed by GitHub
178 changed files with 3374 additions and 1358 deletions

View File

@ -40,16 +40,23 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
body.BorderColour = colours.Yellow;
}
private int? lastVersion;
public override void UpdateFrom(Slider hitObject)
{
base.UpdateFrom(hitObject);
body.PathRadius = hitObject.Scale * OsuHitObject.OBJECT_RADIUS;
var vertices = new List<Vector2>();
hitObject.Path.GetPathToProgress(vertices, 0, 1);
if (lastVersion != hitObject.Path.Version.Value)
{
lastVersion = hitObject.Path.Version.Value;
body.SetVertices(vertices);
var vertices = new List<Vector2>();
hitObject.Path.GetPathToProgress(vertices, 0, 1);
body.SetVertices(vertices);
}
Size = body.Size;
OriginPosition = body.PathOffset;

View File

@ -59,6 +59,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
private readonly BindableList<PathControlPoint> controlPoints = new BindableList<PathControlPoint>();
private readonly IBindable<int> pathVersion = new Bindable<int>();
private readonly BindableList<HitObject> selectedObjects = new BindableList<HitObject>();
public SliderSelectionBlueprint(Slider slider)
: base(slider)
@ -86,6 +87,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
pathVersion.BindValueChanged(_ => editorBeatmap?.Update(HitObject));
BodyPiece.UpdateFrom(HitObject);
if (editorBeatmap != null)
selectedObjects.BindTo(editorBeatmap.SelectedHitObjects);
selectedObjects.BindCollectionChanged((_, _) => updateVisualDefinition(), true);
}
public override bool HandleQuickDeletion()
@ -100,6 +105,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
return true;
}
private bool hasSingleObjectSelected => selectedObjects.Count == 1;
protected override void Update()
{
base.Update();
@ -108,14 +115,25 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
BodyPiece.UpdateFrom(HitObject);
}
protected override bool OnHover(HoverEvent e)
{
updateVisualDefinition();
// In the case more than a single object is selected, block hover from arriving at sliders behind this one.
// Without doing this, the path visualisers of potentially hundreds of sliders will render, which is not only
// visually noisy but also functionally useless.
return !hasSingleObjectSelected;
}
protected override void OnHoverLost(HoverLostEvent e)
{
updateVisualDefinition();
base.OnHoverLost(e);
}
protected override void OnSelected()
{
AddInternal(ControlPointVisualiser = new PathControlPointVisualiser(HitObject, true)
{
RemoveControlPointsRequested = removeControlPoints,
SplitControlPointsRequested = splitControlPoints
});
updateVisualDefinition();
base.OnSelected();
}
@ -123,13 +141,31 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
{
base.OnDeselected();
// throw away frame buffers on deselection.
ControlPointVisualiser?.Expire();
ControlPointVisualiser = null;
updateVisualDefinition();
BodyPiece.RecyclePath();
}
private void updateVisualDefinition()
{
// To reduce overhead of drawing these blueprints, only add extra detail when hovered or when only this slider is selected.
if (IsSelected && (hasSingleObjectSelected || IsHovered))
{
if (ControlPointVisualiser == null)
{
AddInternal(ControlPointVisualiser = new PathControlPointVisualiser(HitObject, true)
{
RemoveControlPointsRequested = removeControlPoints,
SplitControlPointsRequested = splitControlPoints
});
}
}
else
{
ControlPointVisualiser?.Expire();
ControlPointVisualiser = null;
}
}
private Vector2 rightClickPosition;
protected override bool OnMouseDown(MouseDownEvent e)

View File

@ -5,19 +5,17 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Skinning.Default;
using osuTK;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components
{
public class SpinnerPiece : BlueprintPiece<Spinner>
{
private readonly CircularContainer circle;
private readonly RingPiece ring;
private readonly Circle circle;
private readonly Circle ring;
public SpinnerPiece()
{
@ -25,18 +23,21 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components
RelativeSizeAxes = Axes.Both;
FillMode = FillMode.Fit;
Size = new Vector2(1.3f);
Size = new Vector2(1);
InternalChildren = new Drawable[]
{
circle = new CircularContainer
circle = new Circle
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Alpha = 0.5f,
Child = new Box { RelativeSizeAxes = Axes.Both }
},
ring = new RingPiece()
ring = new Circle
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(OsuHitObject.OBJECT_RADIUS),
},
};
}

View File

@ -13,8 +13,11 @@ using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Utils;
using osu.Framework.Input.Events;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mods;
@ -44,12 +47,10 @@ namespace osu.Game.Rulesets.Osu.Edit
new SpinnerCompositionTool()
};
private readonly Bindable<TernaryState> distanceSnapToggle = new Bindable<TernaryState>();
private readonly Bindable<TernaryState> rectangularGridSnapToggle = new Bindable<TernaryState>();
protected override IEnumerable<TernaryButton> CreateTernaryButtons() => base.CreateTernaryButtons().Concat(new[]
{
new TernaryButton(distanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler }),
new TernaryButton(rectangularGridSnapToggle, "Grid Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Th })
});
@ -80,19 +81,7 @@ namespace osu.Game.Rulesets.Osu.Edit
placementObject = EditorBeatmap.PlacementObject.GetBoundCopy();
placementObject.ValueChanged += _ => updateDistanceSnapGrid();
distanceSnapToggle.ValueChanged += _ =>
{
updateDistanceSnapGrid();
if (distanceSnapToggle.Value == TernaryState.True)
rectangularGridSnapToggle.Value = TernaryState.False;
};
rectangularGridSnapToggle.ValueChanged += _ =>
{
if (rectangularGridSnapToggle.Value == TernaryState.True)
distanceSnapToggle.Value = TernaryState.False;
};
DistanceSnapToggle.ValueChanged += _ => updateDistanceSnapGrid();
// we may be entering the screen with a selection already active
updateDistanceSnapGrid();
@ -112,6 +101,14 @@ namespace osu.Game.Rulesets.Osu.Edit
private RectangularPositionSnapGrid rectangularPositionSnapGrid;
protected override double ReadCurrentDistanceSnap(HitObject before, HitObject after)
{
float expectedDistance = DurationToDistance(before, after.StartTime - before.GetEndTime());
float actualDistance = Vector2.Distance(((OsuHitObject)before).EndPosition, ((OsuHitObject)after).Position);
return actualDistance / expectedDistance;
}
protected override void Update()
{
base.Update();
@ -132,24 +129,46 @@ namespace osu.Game.Rulesets.Osu.Edit
public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All)
{
if (snapType.HasFlagFast(SnapType.NearbyObjects) && snapToVisibleBlueprints(screenSpacePosition, out var snapResult))
{
// In the case of snapping to nearby objects, a time value is not provided.
// This matches the stable editor (which also uses current time), but with the introduction of time-snapping distance snap
// this could result in unexpected behaviour when distance snapping is turned on and a user attempts to place an object that is
// BOTH on a valid distance snap ring, and also at the same position as a previous object.
//
// We want to ensure that in this particular case, the time-snapping component of distance snap is still applied.
// The easiest way to ensure this is to attempt application of distance snap after a nearby object is found, and copy over
// the time value if the proposed positions are roughly the same.
if (snapType.HasFlagFast(SnapType.Grids) && DistanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null)
{
(Vector2 distanceSnappedPosition, double distanceSnappedTime) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(snapResult.ScreenSpacePosition));
if (Precision.AlmostEquals(distanceSnapGrid.ToScreenSpace(distanceSnappedPosition), snapResult.ScreenSpacePosition, 1))
snapResult.Time = distanceSnappedTime;
}
return snapResult;
}
SnapResult result = base.FindSnappedPositionAndTime(screenSpacePosition, snapType);
if (snapType.HasFlagFast(SnapType.Grids))
{
if (distanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null)
if (DistanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null)
{
(Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition));
return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time, PlayfieldAtScreenSpacePosition(screenSpacePosition));
result.ScreenSpacePosition = distanceSnapGrid.ToScreenSpace(pos);
result.Time = time;
}
if (rectangularGridSnapToggle.Value == TernaryState.True)
{
Vector2 pos = rectangularPositionSnapGrid.GetSnappedPosition(rectangularPositionSnapGrid.ToLocalSpace(screenSpacePosition));
return new SnapResult(rectangularPositionSnapGrid.ToScreenSpace(pos), null, PlayfieldAtScreenSpacePosition(screenSpacePosition));
Vector2 pos = rectangularPositionSnapGrid.GetSnappedPosition(rectangularPositionSnapGrid.ToLocalSpace(result.ScreenSpacePosition));
result.ScreenSpacePosition = rectangularPositionSnapGrid.ToScreenSpace(pos);
}
}
return base.FindSnappedPositionAndTime(screenSpacePosition, snapType);
return result;
}
private bool snapToVisibleBlueprints(Vector2 screenSpacePosition, out SnapResult snapResult)
@ -202,7 +221,7 @@ namespace osu.Game.Rulesets.Osu.Edit
distanceSnapGridCache.Invalidate();
distanceSnapGrid = null;
if (distanceSnapToggle.Value != TernaryState.True)
if (DistanceSnapToggle.Value != TernaryState.True)
return;
switch (BlueprintContainer.CurrentTool)
@ -229,6 +248,42 @@ namespace osu.Game.Rulesets.Osu.Edit
}
}
protected override bool OnKeyDown(KeyDownEvent e)
{
if (e.Repeat)
return false;
handleToggleViaKey(e);
return base.OnKeyDown(e);
}
protected override void OnKeyUp(KeyUpEvent e)
{
handleToggleViaKey(e);
base.OnKeyUp(e);
}
protected override bool AdjustDistanceSpacing(GlobalAction action, float amount)
{
// To allow better visualisation, ensure that the spacing grid is visible before adjusting.
DistanceSnapToggle.Value = TernaryState.True;
return base.AdjustDistanceSpacing(action, amount);
}
private bool gridSnapMomentary;
private void handleToggleViaKey(KeyboardEvent key)
{
bool shiftPressed = key.ShiftPressed;
if (shiftPressed != gridSnapMomentary)
{
gridSnapMomentary = shiftPressed;
rectangularGridSnapToggle.Value = rectangularGridSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False;
}
}
private DistanceSnapGrid createDistanceSnapGrid(IEnumerable<HitObject> selectedHitObjects)
{
if (BlueprintContainer.CurrentTool is SpinnerCompositionTool)

View File

@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override double ScoreMultiplier => 1;
public override IconUsage? Icon { get; } = FontAwesome.Regular.Circle;
public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles) };
public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles), typeof(OsuModFreezeFrame) };
[SettingSource("Initial size", "Change the initial size of the approach circle, relative to hit circles.", 0)]
public BindableFloat Scale { get; } = new BindableFloat(4)

View File

@ -0,0 +1,89 @@
// 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.Linq;
using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModFreezeFrame : Mod, IApplicableToDrawableHitObject, IApplicableToBeatmap
{
public override string Name => "Freeze Frame";
public override string Acronym => "FR";
public override double ScoreMultiplier => 1;
public override LocalisableString Description => "Burn the notes into your memory.";
//Alters the transforms of the approach circles, breaking the effects of these mods.
public override Type[] IncompatibleMods => new[] { typeof(OsuModApproachDifferent) };
public override ModType Type => ModType.Fun;
//mod breaks normal approach circle preempt
private double originalPreempt;
public void ApplyToBeatmap(IBeatmap beatmap)
{
var firstHitObject = beatmap.HitObjects.OfType<OsuHitObject>().FirstOrDefault();
if (firstHitObject == null)
return;
double lastNewComboTime = 0;
originalPreempt = firstHitObject.TimePreempt;
foreach (var obj in beatmap.HitObjects.OfType<OsuHitObject>())
{
if (obj.NewCombo) { lastNewComboTime = obj.StartTime; }
applyFadeInAdjustment(obj);
}
void applyFadeInAdjustment(OsuHitObject osuObject)
{
osuObject.TimePreempt += osuObject.StartTime - lastNewComboTime;
foreach (var nested in osuObject.NestedHitObjects.OfType<OsuHitObject>())
{
switch (nested)
{
//SliderRepeat wont layer correctly if preempt is changed.
case SliderRepeat:
break;
default:
applyFadeInAdjustment(nested);
break;
}
}
}
}
public void ApplyToDrawableHitObject(DrawableHitObject drawableObject)
{
drawableObject.ApplyCustomUpdateState += (drawableHitObject, _) =>
{
if (drawableHitObject is not DrawableHitCircle drawableHitCircle) return;
var hitCircle = drawableHitCircle.HitObject;
var approachCircle = drawableHitCircle.ApproachCircle;
// Reapply scale, ensuring the AR isn't changed due to the new preempt.
approachCircle.ClearTransforms(targetMember: nameof(approachCircle.Scale));
approachCircle.ScaleTo(4 * (float)(hitCircle.TimePreempt / originalPreempt));
using (drawableHitCircle.ApproachCircle.BeginAbsoluteSequence(hitCircle.StartTime - hitCircle.TimePreempt))
approachCircle.ScaleTo(1, hitCircle.TimePreempt).Then().Expire();
};
}
}
}

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override LocalisableString Description => "It never gets boring!";
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTarget)).ToArray();
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTargetPractice)).ToArray();
[SettingSource("Angle sharpness", "How sharp angles should be", SettingControlType = typeof(SettingsSlider<float>))]
public BindableFloat AngleSharpness { get; } = new BindableFloat(7)

View File

@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override ModType Type => ModType.Automation;
public override LocalisableString Description => @"Spinners will be automatically completed.";
public override double ScoreMultiplier => 0.9;
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot), typeof(OsuModTarget) };
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot), typeof(OsuModTargetPractice) };
public void ApplyToDrawableHitObject(DrawableHitObject hitObject)
{

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override ModType Type => ModType.DifficultyIncrease;
public override LocalisableString Description => @"Once you start a slider, follow precisely or get a miss.";
public override double ScoreMultiplier => 1.0;
public override Type[] IncompatibleMods => new[] { typeof(ModClassic), typeof(OsuModTarget) };
public override Type[] IncompatibleMods => new[] { typeof(ModClassic), typeof(OsuModTargetPractice) };
public void ApplyToDrawableHitObject(DrawableHitObject drawable)
{

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[]
{
typeof(OsuModAutopilot),
typeof(OsuModTarget),
typeof(OsuModTargetPractice),
}).ToArray();
}
}

View File

@ -32,16 +32,15 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModTarget : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset<OsuHitObject>,
IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride,
IHasSeed, IHidesApproachCircles
public class OsuModTargetPractice : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset<OsuHitObject>,
IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, IHasSeed, IHidesApproachCircles
{
public override string Name => "Target";
public override string Name => "Target Practice";
public override string Acronym => "TP";
public override ModType Type => ModType.Conversion;
public override IconUsage? Icon => OsuIcon.ModTarget;
public override LocalisableString Description => @"Practice keeping up with the beat of the song.";
public override double ScoreMultiplier => 1;
public override double ScoreMultiplier => 0.1;
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[]
{

View File

@ -102,8 +102,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Size = HitArea.DrawSize;
PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
PositionBindable.BindValueChanged(_ => UpdatePosition());
StackHeightBindable.BindValueChanged(_ => UpdatePosition());
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue));
}
@ -134,6 +134,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
protected virtual void UpdatePosition()
{
Position = HitObject.StackedPosition;
}
public override void Shake() => shakeContainer.Shake();
protected override void CheckForResult(bool userTriggered, double timeOffset)

View File

@ -79,7 +79,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public override void ApplyTransformsAt(double time, bool propagateChildren = false)
{
// For the same reasons as above w.r.t rewinding, we shouldn't propagate to children here either.
// ReSharper disable once RedundantArgumentDefaultValue - removing the "redundant" default value triggers BaseMethodCallWithDefaultParameter
// ReSharper disable once RedundantArgumentDefaultValue
base.ApplyTransformsAt(time, false);
}

View File

@ -6,7 +6,6 @@
using System;
using System.Diagnostics;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
@ -43,13 +42,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
}
[BackgroundDependencyLoader]
private void load()
{
PositionBindable.BindValueChanged(_ => updatePosition());
pathVersion.BindValueChanged(_ => updatePosition());
}
protected override void OnFree()
{
base.OnFree();
@ -57,6 +49,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
pathVersion.UnbindFrom(DrawableSlider.PathVersion);
}
protected override void UpdatePosition()
{
// Slider head is always drawn at (0,0).
}
protected override void OnApply()
{
base.OnApply();
@ -100,11 +97,5 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.Shake();
DrawableSlider.Shake();
}
private void updatePosition()
{
if (Slider != null)
Position = HitObject.Position - Slider.Position;
}
}
}

View File

@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Osu
yield return new OsuModSpunOut();
if (mods.HasFlagFast(LegacyMods.Target))
yield return new OsuModTarget();
yield return new OsuModTargetPractice();
if (mods.HasFlagFast(LegacyMods.TouchDevice))
yield return new OsuModTouchDevice();
@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Osu
value |= LegacyMods.SpunOut;
break;
case OsuModTarget:
case OsuModTargetPractice:
value |= LegacyMods.Target;
break;
@ -170,7 +170,7 @@ namespace osu.Game.Rulesets.Osu
case ModType.Conversion:
return new Mod[]
{
new OsuModTarget(),
new OsuModTargetPractice(),
new OsuModDifficultyAdjust(),
new OsuModClassic(),
new OsuModRandom(),
@ -201,7 +201,8 @@ namespace osu.Game.Rulesets.Osu
new OsuModMuted(),
new OsuModNoScope(),
new MultiMod(new OsuModMagnetised(), new OsuModRepel()),
new ModAdaptiveSpeed()
new ModAdaptiveSpeed(),
new OsuModFreezeFrame()
};
case ModType.System:

View File

@ -121,12 +121,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f));
flash.Colour = colour.NewValue;
updateStateTransforms(drawableObject, drawableObject.State.Value);
// Accent colour may be changed many times during a paused gameplay state.
// Schedule the change to avoid transforms piling up.
Scheduler.AddOnce(updateStateTransforms);
}, true);
drawableObject.ApplyCustomUpdateState += updateStateTransforms;
}
private void updateStateTransforms() => updateStateTransforms(drawableObject, drawableObject.State.Value);
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
{
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
@ -171,7 +175,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
// This is to give it a bomb-like effect, with the border "triggering" its animation when getting close.
using (BeginDelayedSequence(flash_in_duration / 12))
{
outerGradient.ResizeTo(outerGradient.Size * shrink_size, resize_duration, Easing.OutElasticHalf);
outerGradient.ResizeTo(OUTER_GRADIENT_SIZE * shrink_size, resize_duration, Easing.OutElasticHalf);
outerGradient
.FadeColour(Color4.White, 80)
.Then()

View File

@ -1,50 +0,0 @@
// 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.Audio.Track;
using osu.Framework.Graphics;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.Containers;
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
internal class KiaiFlashingDrawable : BeatSyncedContainer
{
private readonly Drawable flashingDrawable;
private const float flash_opacity = 0.3f;
public KiaiFlashingDrawable(Func<Drawable?> creationFunc)
{
AutoSizeAxes = Axes.Both;
Children = new[]
{
(creationFunc.Invoke() ?? Empty()).With(d =>
{
d.Anchor = Anchor.Centre;
d.Origin = Anchor.Centre;
}),
flashingDrawable = (creationFunc.Invoke() ?? Empty()).With(d =>
{
d.Anchor = Anchor.Centre;
d.Origin = Anchor.Centre;
d.Alpha = 0;
d.Blending = BlendingParameters.Additive;
})
};
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
if (!effectPoint.KiaiMode)
return;
flashingDrawable
.FadeTo(flash_opacity)
.Then()
.FadeOut(timingPoint.BeatLength * 0.75f);
}
}
}

View File

@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
InternalChildren = new[]
{
CircleSprite = new KiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(circleName) })
CircleSprite = new LegacyKiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(circleName) })
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Child = OverlaySprite = new KiaiFlashingDrawable(() => skin.GetAnimation(@$"{circleName}overlay", true, true, frameLength: 1000 / 2d))
Child = OverlaySprite = new LegacyKiaiFlashingDrawable(() => skin.GetAnimation(@$"{circleName}overlay", true, true, frameLength: 1000 / 2d))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,