mirror of
https://github.com/osukey/osukey.git
synced 2025-08-03 14:46:38 +09:00
Merge branch 'master' into fix-blinds-barrel-roll-compatibility
This commit is contained in:
@ -103,22 +103,26 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
double approachRateTotalHitsFactor = 1.0 / (1.0 + Math.Exp(-(0.007 * (totalHits - 400))));
|
||||
|
||||
aimValue *= 1.0 + (0.03 + 0.37 * approachRateTotalHitsFactor) * approachRateFactor;
|
||||
double approachRateBonus = 1.0 + (0.03 + 0.37 * approachRateTotalHitsFactor) * approachRateFactor;
|
||||
|
||||
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
|
||||
if (mods.Any(h => h is OsuModHidden))
|
||||
aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate);
|
||||
|
||||
double flashlightBonus = 1.0;
|
||||
|
||||
if (mods.Any(h => h is OsuModFlashlight))
|
||||
{
|
||||
// Apply object-based bonus for flashlight.
|
||||
aimValue *= 1.0 + 0.35 * Math.Min(1.0, totalHits / 200.0) +
|
||||
(totalHits > 200
|
||||
? 0.3 * Math.Min(1.0, (totalHits - 200) / 300.0) +
|
||||
(totalHits > 500 ? (totalHits - 500) / 1200.0 : 0.0)
|
||||
: 0.0);
|
||||
flashlightBonus = 1.0 + 0.35 * Math.Min(1.0, totalHits / 200.0) +
|
||||
(totalHits > 200
|
||||
? 0.3 * Math.Min(1.0, (totalHits - 200) / 300.0) +
|
||||
(totalHits > 500 ? (totalHits - 500) / 1200.0 : 0.0)
|
||||
: 0.0);
|
||||
}
|
||||
|
||||
aimValue *= Math.Max(flashlightBonus, approachRateBonus);
|
||||
|
||||
// Scale the aim value with accuracy _slightly_
|
||||
aimValue *= 0.5 + accuracy / 2.0;
|
||||
// It is important to also consider accuracy difficulty when doing that
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@ -13,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||
/// <summary>
|
||||
/// Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
|
||||
/// </summary>
|
||||
public class Aim : StrainSkill
|
||||
public class Aim : OsuStrainSkill
|
||||
{
|
||||
private const double angle_bonus_begin = Math.PI / 3;
|
||||
private const double timing_threshold = 107;
|
||||
@ -47,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||
Math.Max(osuPrevious.JumpDistance - scale, 0)
|
||||
* Math.Pow(Math.Sin(osuCurrent.Angle.Value - angle_bonus_begin), 2)
|
||||
* Math.Max(osuCurrent.JumpDistance - scale, 0));
|
||||
result = 1.5 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime);
|
||||
result = 1.4 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
61
osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
Normal file
61
osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using System.Linq;
|
||||
using osu.Framework.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||
{
|
||||
public abstract class OsuStrainSkill : StrainSkill
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of sections with the highest strains, which the peak strain reductions will apply to.
|
||||
/// This is done in order to decrease their impact on the overall difficulty of the map for this skill.
|
||||
/// </summary>
|
||||
protected virtual int ReducedSectionCount => 10;
|
||||
|
||||
/// <summary>
|
||||
/// The baseline multiplier applied to the section with the biggest strain.
|
||||
/// </summary>
|
||||
protected virtual double ReducedStrainBaseline => 0.75;
|
||||
|
||||
/// <summary>
|
||||
/// The final multiplier to be applied to <see cref="DifficultyValue"/> after all other calculations.
|
||||
/// </summary>
|
||||
protected virtual double DifficultyMultiplier => 1.06;
|
||||
|
||||
protected OsuStrainSkill(Mod[] mods)
|
||||
: base(mods)
|
||||
{
|
||||
}
|
||||
|
||||
public override double DifficultyValue()
|
||||
{
|
||||
double difficulty = 0;
|
||||
double weight = 1;
|
||||
|
||||
List<double> strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList();
|
||||
|
||||
// We are reducing the highest strains first to account for extreme difficulty spikes
|
||||
for (int i = 0; i < Math.Min(strains.Count, ReducedSectionCount); i++)
|
||||
{
|
||||
double scale = Math.Log10(Interpolation.Lerp(1, 10, Math.Clamp((float)i / ReducedSectionCount, 0, 1)));
|
||||
strains[i] *= Interpolation.Lerp(ReducedStrainBaseline, 1.0, scale);
|
||||
}
|
||||
|
||||
// Difficulty is the weighted sum of the highest strains from every section.
|
||||
// We're sorting from highest to lowest strain.
|
||||
foreach (double strain in strains.OrderByDescending(d => d))
|
||||
{
|
||||
difficulty += strain * weight;
|
||||
weight *= DecayWeight;
|
||||
}
|
||||
|
||||
return difficulty * DifficultyMultiplier;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@ -13,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||
/// <summary>
|
||||
/// Represents the skill required to press keys with regards to keeping up with the speed at which objects need to be hit.
|
||||
/// </summary>
|
||||
public class Speed : StrainSkill
|
||||
public class Speed : OsuStrainSkill
|
||||
{
|
||||
private const double single_spacing_threshold = 125;
|
||||
|
||||
@ -23,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||
|
||||
protected override double SkillMultiplier => 1400;
|
||||
protected override double StrainDecayBase => 0.3;
|
||||
protected override int ReducedSectionCount => 5;
|
||||
protected override double DifficultyMultiplier => 1.04;
|
||||
|
||||
private const double min_speed_bonus = 75; // ~200BPM
|
||||
private const double max_speed_bonus = 45; // ~330BPM
|
||||
|
@ -41,6 +41,11 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
protected override GameplayCursorContainer CreateCursor() => null;
|
||||
|
||||
public OsuEditorPlayfield()
|
||||
{
|
||||
HitPolicy = new AnyOrderHitPolicy();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
|
@ -76,32 +76,8 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
if (h is Slider slider)
|
||||
{
|
||||
var points = slider.Path.ControlPoints.ToArray();
|
||||
Vector2 endPos = points.Last().Position.Value;
|
||||
|
||||
slider.Path.ControlPoints.Clear();
|
||||
|
||||
slider.Position += endPos;
|
||||
|
||||
PathType? lastType = null;
|
||||
|
||||
for (var i = 0; i < points.Length; i++)
|
||||
{
|
||||
var p = points[i];
|
||||
p.Position.Value -= endPos;
|
||||
|
||||
// propagate types forwards to last null type
|
||||
if (i == points.Length - 1)
|
||||
p.Type.Value = lastType;
|
||||
else if (p.Type.Value != null)
|
||||
{
|
||||
var newType = p.Type.Value;
|
||||
p.Type.Value = lastType;
|
||||
lastType = newType;
|
||||
}
|
||||
|
||||
slider.Path.ControlPoints.Insert(0, p);
|
||||
}
|
||||
slider.Path.Reverse(out Vector2 offset);
|
||||
slider.Position += offset;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
// 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.Extensions.IEnumerableExtensions;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osuTK;
|
||||
using osu.Game.Rulesets.Osu.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
@ -15,23 +14,13 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModMirror)).ToArray();
|
||||
|
||||
public void ApplyToHitObject(HitObject hitObject)
|
||||
{
|
||||
var osuObject = (OsuHitObject)hitObject;
|
||||
|
||||
osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y);
|
||||
|
||||
if (!(hitObject is Slider slider))
|
||||
return;
|
||||
|
||||
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||
|
||||
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
|
||||
foreach (var point in controlPoints)
|
||||
point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y);
|
||||
|
||||
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
|
||||
OsuHitObjectGenerationUtils.ReflectVertically(osuObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
50
osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs
Normal file
50
osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs
Normal file
@ -0,0 +1,50 @@
|
||||
// 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.Bindables;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModMirror : ModMirror, IApplicableToHitObject
|
||||
{
|
||||
public override string Description => "Flip objects on the chosen axes.";
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) };
|
||||
|
||||
[SettingSource("Mirrored axes", "Choose which axes objects are mirrored over.")]
|
||||
public Bindable<MirrorType> Reflection { get; } = new Bindable<MirrorType>();
|
||||
|
||||
public void ApplyToHitObject(HitObject hitObject)
|
||||
{
|
||||
var osuObject = (OsuHitObject)hitObject;
|
||||
|
||||
switch (Reflection.Value)
|
||||
{
|
||||
case MirrorType.Horizontal:
|
||||
OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject);
|
||||
break;
|
||||
|
||||
case MirrorType.Vertical:
|
||||
OsuHitObjectGenerationUtils.ReflectVertically(osuObject);
|
||||
break;
|
||||
|
||||
case MirrorType.Both:
|
||||
OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject);
|
||||
OsuHitObjectGenerationUtils.ReflectVertically(osuObject);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public enum MirrorType
|
||||
{
|
||||
Horizontal,
|
||||
Vertical,
|
||||
Both
|
||||
}
|
||||
}
|
||||
}
|
12
osu.Game.Rulesets.Osu/Mods/OsuModMuted.cs
Normal file
12
osu.Game.Rulesets.Osu/Mods/OsuModMuted.cs
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModMuted : ModMuted<OsuHitObject>
|
||||
{
|
||||
}
|
||||
}
|
@ -4,8 +4,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
@ -16,7 +14,6 @@ using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -29,7 +26,6 @@ using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.Osu.Utils;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -67,11 +63,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
/// </summary>
|
||||
private const float distance_cap = 380f;
|
||||
|
||||
// The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle.
|
||||
// The closer the hit objects draw to the border, the sharper the turn
|
||||
private const byte border_distance_x = 192;
|
||||
private const byte border_distance_y = 144;
|
||||
|
||||
/// <summary>
|
||||
/// The extent of rotation towards playfield centre when a circle is near the edge
|
||||
/// </summary>
|
||||
@ -341,46 +332,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||
{
|
||||
drawableRuleset.Overlays.Add(new TargetBeatContainer(drawableRuleset.Beatmap.HitObjects.First().StartTime));
|
||||
}
|
||||
|
||||
public class TargetBeatContainer : BeatSyncedContainer
|
||||
{
|
||||
private readonly double firstHitTime;
|
||||
|
||||
private PausableSkinnableSound sample;
|
||||
|
||||
public TargetBeatContainer(double firstHitTime)
|
||||
{
|
||||
this.firstHitTime = firstHitTime;
|
||||
AllowMistimedEventFiring = false;
|
||||
Divisor = 1;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana"))
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
|
||||
{
|
||||
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
|
||||
|
||||
if (!IsBeatSyncedWithTrack) return;
|
||||
|
||||
int timeSignature = (int)timingPoint.TimeSignature;
|
||||
|
||||
// play metronome from one measure before the first object.
|
||||
if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature)
|
||||
return;
|
||||
|
||||
sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f;
|
||||
sample.Play();
|
||||
}
|
||||
drawableRuleset.Overlays.Add(new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -97,6 +97,14 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
set => ComboIndexBindable.Value = value;
|
||||
}
|
||||
|
||||
public Bindable<int> ComboIndexWithOffsetsBindable { get; } = new Bindable<int>();
|
||||
|
||||
public int ComboIndexWithOffsets
|
||||
{
|
||||
get => ComboIndexWithOffsetsBindable.Value;
|
||||
set => ComboIndexWithOffsetsBindable.Value = value;
|
||||
}
|
||||
|
||||
public Bindable<bool> LastInComboBindable { get; } = new Bindable<bool>();
|
||||
|
||||
public bool LastInCombo
|
||||
|
@ -166,6 +166,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
new OsuModDifficultyAdjust(),
|
||||
new OsuModClassic(),
|
||||
new OsuModRandom(),
|
||||
new OsuModMirror(),
|
||||
};
|
||||
|
||||
case ModType.Automation:
|
||||
@ -188,6 +189,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
new OsuModTraceable(),
|
||||
new OsuModBarrelRoll(),
|
||||
new OsuModApproachDifferent(),
|
||||
new OsuModMuted(),
|
||||
};
|
||||
|
||||
case ModType.System:
|
||||
|
@ -3,9 +3,9 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Utils;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
// Roughly matches osu!stable's slider border portions.
|
||||
=> base.CalculatedBorderPortion * 0.77f;
|
||||
|
||||
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, 0.7f);
|
||||
|
||||
protected override Color4 ColourAt(float position)
|
||||
{
|
||||
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
Color4 outerColour = AccentColour.Darken(0.1f);
|
||||
Color4 innerColour = lighten(AccentColour, 0.5f);
|
||||
|
||||
return Interpolation.ValueAt(position / realGradientPortion, outerColour, innerColour, 0, 1);
|
||||
return LegacyUtils.InterpolateNonLinear(position / realGradientPortion, outerColour, innerColour, 0, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
22
osu.Game.Rulesets.Osu/UI/AnyOrderHitPolicy.cs
Normal file
22
osu.Game.Rulesets.Osu/UI/AnyOrderHitPolicy.cs
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="IHitPolicy"/> which allows hitobjects to be hit in any order.
|
||||
/// </summary>
|
||||
public class AnyOrderHitPolicy : IHitPolicy
|
||||
{
|
||||
public IHitObjectContainer HitObjectContainer { get; set; }
|
||||
|
||||
public bool IsHittable(DrawableHitObject hitObject, double time) => true;
|
||||
|
||||
public void HandleHit(DrawableHitObject hitObject)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
|
||||
private void onJudgementLoaded(DrawableOsuJudgement judgement)
|
||||
{
|
||||
judgementAboveHitObjectLayer.Add(judgement.GetProxyAboveHitObjectsContent());
|
||||
judgementAboveHitObjectLayer.Add(judgement.ProxiedAboveHitObjectsContent);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
@ -150,6 +150,10 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
DrawableOsuJudgement explosion = poolDictionary[result.Type].Get(doj => doj.Apply(result, judgedObject));
|
||||
|
||||
judgementLayer.Add(explosion);
|
||||
|
||||
// the proxied content is added to judgementAboveHitObjectLayer once, on first load, and never removed from it.
|
||||
// ensure that ordering is consistent with expectations (latest judgement should be front-most).
|
||||
judgementAboveHitObjectLayer.ChangeChildDepth(explosion.ProxiedAboveHitObjectsContent, (float)-result.TimeAbsolute);
|
||||
}
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => HitObjectContainer.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
@ -2,7 +2,11 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Utils
|
||||
@ -100,5 +104,47 @@ namespace osu.Game.Rulesets.Osu.Utils
|
||||
initial.Length * MathF.Sin(finalAngleRad)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reflects the position of the <see cref="OsuHitObject"/> in the playfield horizontally.
|
||||
/// </summary>
|
||||
/// <param name="osuObject">The object to reflect.</param>
|
||||
public static void ReflectHorizontally(OsuHitObject osuObject)
|
||||
{
|
||||
osuObject.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - osuObject.X, osuObject.Position.Y);
|
||||
|
||||
if (!(osuObject is Slider slider))
|
||||
return;
|
||||
|
||||
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
|
||||
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
|
||||
|
||||
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
|
||||
foreach (var point in controlPoints)
|
||||
point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y);
|
||||
|
||||
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reflects the position of the <see cref="OsuHitObject"/> in the playfield vertically.
|
||||
/// </summary>
|
||||
/// <param name="osuObject">The object to reflect.</param>
|
||||
public static void ReflectVertically(OsuHitObject osuObject)
|
||||
{
|
||||
osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y);
|
||||
|
||||
if (!(osuObject is Slider slider))
|
||||
return;
|
||||
|
||||
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||
|
||||
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
|
||||
foreach (var point in controlPoints)
|
||||
point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y);
|
||||
|
||||
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user