mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 19:03:08 +08:00
Merge branch 'master' into results-clean
This commit is contained in:
commit
f03c4a0961
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.EmptyFreeform.Objects.Drawables
|
||||
{
|
||||
if (timeOffset >= 0)
|
||||
// todo: implement judgement logic
|
||||
ApplyResult(r => r.Type = HitResult.Perfect);
|
||||
ApplyResult(HitResult.Perfect);
|
||||
}
|
||||
|
||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||
|
@ -9,7 +9,6 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -49,7 +48,12 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (timeOffset >= 0)
|
||||
ApplyResult(r => r.Type = IsHovered ? HitResult.Perfect : HitResult.Miss);
|
||||
{
|
||||
if (IsHovered)
|
||||
ApplyMaxResult();
|
||||
else
|
||||
ApplyMinResult();
|
||||
}
|
||||
}
|
||||
|
||||
protected override double InitialLifetimeOffset => time_preempt;
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -24,7 +23,7 @@ namespace osu.Game.Rulesets.EmptyScrolling.Objects.Drawables
|
||||
{
|
||||
if (timeOffset >= 0)
|
||||
// todo: implement judgement logic
|
||||
ApplyResult(r => r.Type = HitResult.Perfect);
|
||||
ApplyMaxResult();
|
||||
}
|
||||
|
||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||
|
@ -10,7 +10,6 @@ using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Pippidon.UI;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -49,7 +48,12 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (timeOffset >= 0)
|
||||
ApplyResult(r => r.Type = currentLane.Value == HitObject.Lane ? HitResult.Perfect : HitResult.Miss);
|
||||
{
|
||||
if (currentLane.Value == HitObject.Lane)
|
||||
ApplyMaxResult();
|
||||
else
|
||||
ApplyMinResult();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||
|
@ -63,7 +63,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
if (CheckPosition == null) return;
|
||||
|
||||
if (timeOffset >= 0 && Result != null)
|
||||
ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||
{
|
||||
if (CheckPosition.Invoke(HitObject))
|
||||
ApplyMaxResult();
|
||||
else
|
||||
ApplyMinResult();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
private readonly bool isForCurrentRuleset;
|
||||
private readonly double originalOverallDifficulty;
|
||||
|
||||
public override int Version => 20220902;
|
||||
public override int Version => 20230817;
|
||||
|
||||
public ManiaDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
|
@ -265,7 +265,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
if (Tail.AllJudged)
|
||||
{
|
||||
if (Tail.IsHit)
|
||||
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||
ApplyMaxResult();
|
||||
else
|
||||
MissForcefully();
|
||||
}
|
||||
|
@ -25,7 +25,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
if (AllJudged) return;
|
||||
|
||||
ApplyResult(r => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||
if (hit)
|
||||
ApplyMaxResult();
|
||||
else
|
||||
ApplyMinResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
/// <summary>
|
||||
/// Causes this <see cref="DrawableManiaHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
|
||||
/// </summary>
|
||||
public virtual void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
public virtual void MissForcefully() => ApplyMinResult();
|
||||
}
|
||||
|
||||
public abstract partial class DrawableManiaHitObject<TObject> : DrawableManiaHitObject
|
||||
|
@ -89,18 +89,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
ApplyMinResult();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var result = HitObject.HitWindows.ResultFor(timeOffset);
|
||||
|
||||
if (result == HitResult.None)
|
||||
return;
|
||||
|
||||
result = GetCappedResult(result);
|
||||
|
||||
ApplyResult(r => r.Type = result);
|
||||
ApplyResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(130)
|
||||
Size = new Vector2(300)
|
||||
}
|
||||
};
|
||||
});
|
||||
@ -85,6 +85,30 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
AddStep("return user input", () => InputManager.UseParentInput = true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAllPoints()
|
||||
{
|
||||
AddStep("add points", () =>
|
||||
{
|
||||
float minX = object1.DrawPosition.X - object1.DrawSize.X / 2;
|
||||
float maxX = object1.DrawPosition.X + object1.DrawSize.X / 2;
|
||||
|
||||
float minY = object1.DrawPosition.Y - object1.DrawSize.Y / 2;
|
||||
float maxY = object1.DrawPosition.Y + object1.DrawSize.Y / 2;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
for (float x = minX; x <= maxX; x += 0.5f)
|
||||
{
|
||||
for (float y = minY; y <= maxY; y += 0.5f)
|
||||
{
|
||||
accuracyHeatmap.AddPoint(object2.Position, object1.Position, new Vector2(x, y), RNG.NextSingle(10, 500));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
accuracyHeatmap.AddPoint(object2.Position, object1.Position, background.ToLocalSpace(e.ScreenSpaceMouseDownPosition), 50);
|
||||
|
@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
if (auto && !userTriggered && timeOffset > hitOffset && CheckHittable?.Invoke(this, Time.Current, HitResult.Great) == ClickAction.Hit)
|
||||
{
|
||||
// force success
|
||||
ApplyResult(r => r.Type = HitResult.Great);
|
||||
ApplyResult(HitResult.Great);
|
||||
}
|
||||
else
|
||||
base.CheckForResult(userTriggered, timeOffset);
|
||||
|
@ -208,7 +208,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
if (shouldHit && !userTriggered && timeOffset >= 0)
|
||||
{
|
||||
// force success
|
||||
ApplyResult(r => r.Type = HitResult.Great);
|
||||
ApplyResult(HitResult.Great);
|
||||
}
|
||||
else
|
||||
base.CheckForResult(userTriggered, timeOffset);
|
||||
|
@ -31,6 +31,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModFlashlight) };
|
||||
public override bool Ranked => true;
|
||||
|
||||
private DrawableOsuBlinds blinds = null!;
|
||||
|
||||
|
@ -75,8 +75,10 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
double time = playfield.Time.Current;
|
||||
|
||||
foreach (var drawable in playfield.HitObjectContainer.AliveObjects)
|
||||
foreach (var entry in playfield.HitObjectContainer.AliveEntries)
|
||||
{
|
||||
var drawable = entry.Value;
|
||||
|
||||
switch (drawable)
|
||||
{
|
||||
case DrawableHitCircle circle:
|
||||
|
@ -49,8 +49,10 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
var cursorPos = playfield.Cursor.AsNonNull().ActiveCursor.DrawPosition;
|
||||
|
||||
foreach (var drawable in playfield.HitObjectContainer.AliveObjects)
|
||||
foreach (var entry in playfield.HitObjectContainer.AliveEntries)
|
||||
{
|
||||
var drawable = entry.Value;
|
||||
|
||||
switch (drawable)
|
||||
{
|
||||
case DrawableHitCircle circle:
|
||||
|
@ -88,7 +88,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
if (!slider.HeadCircle.IsHit)
|
||||
handleHitCircle(slider.HeadCircle);
|
||||
|
||||
requiresHold |= slider.SliderInputManager.IsMouseInFollowArea(true);
|
||||
requiresHold |= slider.SliderInputManager.IsMouseInFollowArea(slider.Tracking.Value);
|
||||
break;
|
||||
|
||||
case DrawableSpinner spinner:
|
||||
|
@ -48,8 +48,10 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
var cursorPos = playfield.Cursor.AsNonNull().ActiveCursor.DrawPosition;
|
||||
|
||||
foreach (var drawable in playfield.HitObjectContainer.AliveObjects)
|
||||
foreach (var entry in playfield.HitObjectContainer.AliveEntries)
|
||||
{
|
||||
var drawable = entry.Value;
|
||||
|
||||
var destination = Vector2.Clamp(2 * drawable.Position - cursorPos, Vector2.Zero, OsuPlayfield.BASE_SIZE);
|
||||
|
||||
if (drawable.HitObject is Slider thisSlider)
|
||||
|
@ -155,7 +155,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
ApplyMinResult();
|
||||
|
||||
return;
|
||||
}
|
||||
@ -169,19 +169,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
if (result == HitResult.None || clickAction != ClickAction.Hit)
|
||||
return;
|
||||
|
||||
ApplyResult(r =>
|
||||
Vector2? hitPosition = null;
|
||||
|
||||
// Todo: This should also consider misses, but they're a little more interesting to handle, since we don't necessarily know the position at the time of a miss.
|
||||
if (result.IsHit())
|
||||
{
|
||||
var localMousePosition = ToLocalSpace(inputManager.CurrentState.Mouse.Position);
|
||||
hitPosition = HitObject.StackedPosition + (localMousePosition - DrawSize / 2);
|
||||
}
|
||||
|
||||
ApplyResult<(HitResult result, Vector2? position)>((r, state) =>
|
||||
{
|
||||
var circleResult = (OsuHitCircleJudgementResult)r;
|
||||
|
||||
// Todo: This should also consider misses, but they're a little more interesting to handle, since we don't necessarily know the position at the time of a miss.
|
||||
if (result.IsHit())
|
||||
{
|
||||
var localMousePosition = ToLocalSpace(inputManager.CurrentState.Mouse.Position);
|
||||
circleResult.CursorPositionAtHit = HitObject.StackedPosition + (localMousePosition - DrawSize / 2);
|
||||
}
|
||||
|
||||
circleResult.Type = result;
|
||||
});
|
||||
circleResult.Type = state.result;
|
||||
circleResult.CursorPositionAtHit = state.position;
|
||||
}, (result, hitPosition));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -100,12 +100,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
/// <summary>
|
||||
/// Causes this <see cref="DrawableOsuHitObject"/> to get hit, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
|
||||
/// </summary>
|
||||
public void HitForcefully() => ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||
public void HitForcefully() => ApplyMaxResult();
|
||||
|
||||
/// <summary>
|
||||
/// Causes this <see cref="DrawableOsuHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
|
||||
/// </summary>
|
||||
public void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
public void MissForcefully() => ApplyMinResult();
|
||||
|
||||
private RectangleF parentScreenSpaceRectangle => ((DrawableOsuHitObject)ParentHitObject)?.parentScreenSpaceRectangle ?? Parent!.ScreenSpaceDrawQuad.AABBFloat;
|
||||
|
||||
|
@ -292,10 +292,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
if (HitObject.ClassicSliderBehaviour)
|
||||
{
|
||||
// Classic behaviour means a slider is judged proportionally to the number of nested hitobjects hit. This is the classic osu!stable scoring.
|
||||
ApplyResult(r =>
|
||||
ApplyResult(static (r, hitObject) =>
|
||||
{
|
||||
int totalTicks = NestedHitObjects.Count;
|
||||
int hitTicks = NestedHitObjects.Count(h => h.IsHit);
|
||||
int totalTicks = hitObject.NestedHitObjects.Count;
|
||||
int hitTicks = hitObject.NestedHitObjects.Count(h => h.IsHit);
|
||||
|
||||
if (hitTicks == totalTicks)
|
||||
r.Type = HitResult.Great;
|
||||
@ -312,7 +312,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
// If only the nested hitobjects are judged, then the slider's own judgement is ignored for scoring purposes.
|
||||
// But the slider needs to still be judged with a reasonable hit/miss result for visual purposes (hit/miss transforms, etc).
|
||||
ApplyResult(r => r.Type = NestedHitObjects.Any(h => h.Result.IsHit) ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||
ApplyResult(static (r, hitObject) =>
|
||||
{
|
||||
r.Type = hitObject.NestedHitObjects.Any(h => h.Result.IsHit) ? r.Judgement.MaxResult : r.Judgement.MinResult;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,15 +258,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
foreach (var tick in ticks.Where(t => !t.Result.HasResult))
|
||||
tick.TriggerResult(false);
|
||||
|
||||
ApplyResult(r =>
|
||||
ApplyResult(static (r, hitObject) =>
|
||||
{
|
||||
if (Progress >= 1)
|
||||
var spinner = (DrawableSpinner)hitObject;
|
||||
if (spinner.Progress >= 1)
|
||||
r.Type = HitResult.Great;
|
||||
else if (Progress > .9)
|
||||
else if (spinner.Progress > .9)
|
||||
r.Type = HitResult.Ok;
|
||||
else if (Progress > .75)
|
||||
else if (spinner.Progress > .75)
|
||||
r.Type = HitResult.Meh;
|
||||
else if (Time.Current >= HitObject.EndTime)
|
||||
else if (spinner.Time.Current >= spinner.HitObject.EndTime)
|
||||
r.Type = r.Judgement.MinResult;
|
||||
});
|
||||
}
|
||||
|
@ -35,6 +35,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
/// Apply a judgement result.
|
||||
/// </summary>
|
||||
/// <param name="hit">Whether this tick was reached.</param>
|
||||
internal void TriggerResult(bool hit) => ApplyResult(r => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||
internal void TriggerResult(bool hit)
|
||||
{
|
||||
if (hit)
|
||||
ApplyMaxResult();
|
||||
else
|
||||
ApplyMinResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -191,7 +191,7 @@ namespace osu.Game.Rulesets.Osu.Statistics
|
||||
|
||||
for (int c = 0; c < points_per_dimension; c++)
|
||||
{
|
||||
HitPointType pointType = Vector2.Distance(new Vector2(c, r), centre) <= innerRadius
|
||||
HitPointType pointType = Vector2.Distance(new Vector2(c + 0.5f, r + 0.5f), centre) <= innerRadius
|
||||
? HitPointType.Hit
|
||||
: HitPointType.Miss;
|
||||
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.3 KiB |
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
{
|
||||
private const double difficulty_multiplier = 1.35;
|
||||
|
||||
public override int Version => 20220902;
|
||||
public override int Version => 20221107;
|
||||
|
||||
public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
|
@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
if (timeOffset < 0)
|
||||
return;
|
||||
|
||||
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||
ApplyMaxResult();
|
||||
}
|
||||
|
||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||
@ -192,7 +192,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
if (!ParentHitObject.Judged)
|
||||
return;
|
||||
|
||||
ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||
ApplyResult(static (r, hitObject) =>
|
||||
{
|
||||
var drumRoll = (StrongNestedHit)hitObject;
|
||||
r.Type = drumRoll.ParentHitObject!.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult;
|
||||
});
|
||||
}
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
|
||||
|
@ -49,14 +49,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (timeOffset > HitObject.HitWindow)
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
ApplyMinResult();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.Abs(timeOffset) > HitObject.HitWindow)
|
||||
return;
|
||||
|
||||
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||
ApplyMaxResult();
|
||||
}
|
||||
|
||||
public override void OnKilled()
|
||||
@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
base.OnKilled();
|
||||
|
||||
if (Time.Current > HitObject.GetEndTime() && !Judged)
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
ApplyMinResult();
|
||||
}
|
||||
|
||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||
@ -105,7 +105,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
if (!ParentHitObject.Judged)
|
||||
return;
|
||||
|
||||
ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||
ApplyResult(static (r, hitObject) =>
|
||||
{
|
||||
var nestedHit = (StrongNestedHit)hitObject;
|
||||
r.Type = nestedHit.ParentHitObject!.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult;
|
||||
});
|
||||
}
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
|
||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||
ApplyMaxResult();
|
||||
}
|
||||
|
||||
protected override void LoadSamples()
|
||||
|
@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
ApplyMinResult();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -108,9 +108,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
return;
|
||||
|
||||
if (!validActionPressed)
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
ApplyMinResult();
|
||||
else
|
||||
ApplyResult(r => r.Type = result);
|
||||
ApplyResult(result);
|
||||
}
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
|
||||
@ -209,19 +209,19 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
|
||||
if (!ParentHitObject.Result.IsHit)
|
||||
{
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
ApplyMinResult();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (timeOffset - ParentHitObject.Result.TimeOffset > SECOND_HIT_WINDOW)
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
ApplyMinResult();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.Abs(timeOffset - ParentHitObject.Result.TimeOffset) <= SECOND_HIT_WINDOW)
|
||||
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||
ApplyMaxResult();
|
||||
}
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
|
||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
// it can happen that the hit window of the nested strong hit extends past the lifetime of the parent object.
|
||||
// this is a safety to prevent such cases from causing the nested hit to never be judged and as such prevent gameplay from completing.
|
||||
if (!Judged && Time.Current > ParentHitObject?.HitObject.GetEndTime())
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
ApplyMinResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint);
|
||||
|
||||
if (numHits == HitObject.RequiredHits)
|
||||
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||
ApplyMaxResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -227,7 +227,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
tick.TriggerResult(false);
|
||||
}
|
||||
|
||||
ApplyResult(r => r.Type = numHits == HitObject.RequiredHits ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||
if (numHits == HitObject.RequiredHits)
|
||||
ApplyMaxResult();
|
||||
else
|
||||
ApplyMinResult();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
public void TriggerResult(bool hit)
|
||||
{
|
||||
HitObject.StartTime = Time.Current;
|
||||
ApplyResult(r => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||
|
||||
if (hit)
|
||||
ApplyMaxResult();
|
||||
else
|
||||
ApplyMinResult();
|
||||
}
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
|
@ -216,7 +216,7 @@ namespace osu.Game.Tests.Gameplay
|
||||
LifetimeStart = LIFETIME_ON_APPLY;
|
||||
}
|
||||
|
||||
public void MissForcefully() => ApplyResult(r => r.Type = HitResult.Miss);
|
||||
public void MissForcefully() => ApplyResult(HitResult.Miss);
|
||||
|
||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||
{
|
||||
|
@ -431,7 +431,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (timeOffset > HitObject.Duration)
|
||||
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||
ApplyMaxResult();
|
||||
}
|
||||
|
||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||
@ -468,7 +468,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public override void OnKilled()
|
||||
{
|
||||
base.OnKilled();
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
ApplyMinResult();
|
||||
}
|
||||
}
|
||||
|
||||
@ -547,7 +547,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
base.CheckForResult(userTriggered, timeOffset);
|
||||
if (timeOffset >= 0)
|
||||
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||
ApplyMaxResult();
|
||||
}
|
||||
}
|
||||
|
||||
@ -596,7 +596,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
base.CheckForResult(userTriggered, timeOffset);
|
||||
if (timeOffset >= 0)
|
||||
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||
ApplyMaxResult();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,6 +113,28 @@ Please try changing your audio device to a working setting.");
|
||||
/// </summary>
|
||||
public static LocalisableString MismatchingBeatmapForReplay => new TranslatableString(getKey(@"mismatching_beatmap_for_replay"), @"Your local copy of the beatmap for this replay appears to be different than expected. You may need to update or re-download it.");
|
||||
|
||||
/// <summary>
|
||||
/// "You are now running osu! {version}.
|
||||
/// Click to see what's new!"
|
||||
/// </summary>
|
||||
public static LocalisableString GameVersionAfterUpdate(string version) => new TranslatableString(getKey(@"game_version_after_update"), @"You are now running osu! {0}.
|
||||
Click to see what's new!", version);
|
||||
|
||||
/// <summary>
|
||||
/// "Update ready to install. Click to restart!"
|
||||
/// </summary>
|
||||
public static LocalisableString UpdateReadyToInstall => new TranslatableString(getKey(@"update_ready_to_install"), @"Update ready to install. Click to restart!");
|
||||
|
||||
/// <summary>
|
||||
/// "Downloading update..."
|
||||
/// </summary>
|
||||
public static LocalisableString DownloadingUpdate => new TranslatableString(getKey(@"downloading_update"), @"Downloading update...");
|
||||
|
||||
/// <summary>
|
||||
/// "Installing update..."
|
||||
/// </summary>
|
||||
public static LocalisableString InstallingUpdate => new TranslatableString(getKey(@"installing_update"), @"Installing update...");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Judgements
|
||||
|
||||
/// <summary>
|
||||
/// The time at which this <see cref="JudgementResult"/> occurred.
|
||||
/// Populated when this <see cref="JudgementResult"/> is applied via <see cref="DrawableHitObject.ApplyResult"/>.
|
||||
/// Populated when this <see cref="JudgementResult"/> is applied via <see cref="DrawableHitObject.ApplyResult{T}"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used instead of <see cref="TimeAbsolute"/> to check whether this <see cref="JudgementResult"/> should be reverted.
|
||||
|
@ -31,6 +31,8 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
public override bool RequiresConfiguration => false;
|
||||
|
||||
public override bool Ranked => true;
|
||||
|
||||
public override string SettingDescription => base.SettingDescription.Replace(MinimumAccuracy.ToString(), MinimumAccuracy.Value.ToString("##%", NumberFormatInfo.InvariantInfo));
|
||||
|
||||
[SettingSource("Minimum accuracy", "Trigger a failure if your accuracy goes below this value.", SettingControlType = typeof(SettingsPercentageSlider<double>))]
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override IconUsage? Icon => OsuIcon.ModDoubleTime;
|
||||
public override ModType Type => ModType.DifficultyIncrease;
|
||||
public override LocalisableString Description => "Zoooooooooom...";
|
||||
public override bool Ranked => UsesDefaultConfiguration;
|
||||
public override bool Ranked => SpeedChange.IsDefault;
|
||||
|
||||
[SettingSource("Speed increase", "The actual increase to apply", SettingControlType = typeof(MultiplierSettingsSlider))]
|
||||
public override BindableNumber<double> SpeedChange { get; } = new BindableDouble(1.5)
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override IconUsage? Icon => OsuIcon.ModHalftime;
|
||||
public override ModType Type => ModType.DifficultyReduction;
|
||||
public override LocalisableString Description => "Less zoom...";
|
||||
public override bool Ranked => UsesDefaultConfiguration;
|
||||
public override bool Ranked => SpeedChange.IsDefault;
|
||||
|
||||
[SettingSource("Speed decrease", "The actual decrease to apply", SettingControlType = typeof(MultiplierSettingsSlider))]
|
||||
public override BindableNumber<double> SpeedChange { get; } = new BindableDouble(0.75)
|
||||
|
@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override IconUsage? Icon => FontAwesome.Solid.EyeSlash;
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool Ranked => true;
|
||||
|
||||
/// <summary>
|
||||
/// Slightly higher than the cutoff for <see cref="Drawable.IsPresent"/>.
|
||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override ModType Type => ModType.DifficultyIncrease;
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override LocalisableString Description => "SS or quit.";
|
||||
public override bool Ranked => UsesDefaultConfiguration;
|
||||
public override bool Ranked => true;
|
||||
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(ModSuddenDeath), typeof(ModAccuracyChallenge) }).ToArray();
|
||||
|
||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override ModType Type => ModType.DifficultyIncrease;
|
||||
public override LocalisableString Description => "Miss and fail.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool Ranked => UsesDefaultConfiguration;
|
||||
public override bool Ranked => true;
|
||||
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModPerfect)).ToArray();
|
||||
|
||||
|
@ -683,17 +683,31 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
UpdateResult(false);
|
||||
}
|
||||
|
||||
protected void ApplyMaxResult() => ApplyResult((r, _) => r.Type = r.Judgement.MaxResult);
|
||||
protected void ApplyMinResult() => ApplyResult((r, _) => r.Type = r.Judgement.MinResult);
|
||||
|
||||
protected void ApplyResult(HitResult type) => ApplyResult(static (result, state) => result.Type = state, type);
|
||||
|
||||
[Obsolete("Use overload with state, preferrably with static delegates to avoid allocation overhead.")] // Can be removed 2024-07-26
|
||||
protected void ApplyResult(Action<JudgementResult> application) => ApplyResult((r, _) => application(r), this);
|
||||
|
||||
protected void ApplyResult(Action<JudgementResult, DrawableHitObject> application) => ApplyResult(application, this);
|
||||
|
||||
/// <summary>
|
||||
/// Applies the <see cref="Result"/> of this <see cref="DrawableHitObject"/>, notifying responders such as
|
||||
/// the <see cref="ScoreProcessor"/> of the <see cref="JudgementResult"/>.
|
||||
/// </summary>
|
||||
/// <param name="application">The callback that applies changes to the <see cref="JudgementResult"/>.</param>
|
||||
protected void ApplyResult(Action<JudgementResult> application)
|
||||
/// <param name="application">The callback that applies changes to the <see cref="JudgementResult"/>. Using a `static` delegate is recommended to avoid allocation overhead.</param>
|
||||
/// <param name="state">
|
||||
/// Use this parameter to pass any data that <paramref name="application"/> requires
|
||||
/// to apply a result, so that it can remain a `static` delegate and thus not allocate.
|
||||
/// </param>
|
||||
protected void ApplyResult<T>(Action<JudgementResult, T> application, T state)
|
||||
{
|
||||
if (Result.HasResult)
|
||||
throw new InvalidOperationException("Cannot apply result on a hitobject that already has a result.");
|
||||
|
||||
application?.Invoke(Result);
|
||||
application?.Invoke(Result, state);
|
||||
|
||||
if (!Result.HasResult)
|
||||
throw new InvalidOperationException($"{GetType().ReadableName()} applied a {nameof(JudgementResult)} but did not update {nameof(JudgementResult.Type)}.");
|
||||
@ -738,7 +752,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// Checks if a scoring result has occurred for this <see cref="DrawableHitObject"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If a scoring result has occurred, this method must invoke <see cref="ApplyResult"/> to update the result and notify responders.
|
||||
/// If a scoring result has occurred, this method must invoke <see cref="ApplyResult{T}"/> to update the result and notify responders.
|
||||
/// </remarks>
|
||||
/// <param name="userTriggered">Whether the user triggered this check.</param>
|
||||
/// <param name="timeOffset">The offset from the end time of the <see cref="HitObject"/> at which this check occurred.
|
||||
|
@ -4,9 +4,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.ListExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Performance;
|
||||
using osu.Framework.Lists;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Pooling
|
||||
{
|
||||
@ -35,7 +37,7 @@ namespace osu.Game.Rulesets.Objects.Pooling
|
||||
/// <remarks>
|
||||
/// The enumeration order is undefined.
|
||||
/// </remarks>
|
||||
public IEnumerable<(TEntry Entry, TDrawable Drawable)> AliveEntries => aliveDrawableMap.Select(x => (x.Key, x.Value));
|
||||
public readonly SlimReadOnlyDictionaryWrapper<TEntry, TDrawable> AliveEntries;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to remove an entry when clock goes backward and crossed its <see cref="LifetimeEntry.LifetimeStart"/>.
|
||||
@ -63,6 +65,8 @@ namespace osu.Game.Rulesets.Objects.Pooling
|
||||
lifetimeManager.EntryBecameAlive += entryBecameAlive;
|
||||
lifetimeManager.EntryBecameDead += entryBecameDead;
|
||||
lifetimeManager.EntryCrossedBoundary += entryCrossedBoundary;
|
||||
|
||||
AliveEntries = aliveDrawableMap.AsSlimReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.UI
|
||||
// If required, we can make this lookup more efficient by adding support to get next-future-entry in LifetimeEntryManager.
|
||||
var candidate =
|
||||
// Use alive entries first as an optimisation.
|
||||
hitObjectContainer.AliveEntries.Select(tuple => tuple.Entry).Where(e => !isAlreadyHit(e)).MinBy(e => e.HitObject.StartTime)
|
||||
hitObjectContainer.AliveEntries.Keys.Where(e => !isAlreadyHit(e)).MinBy(e => e.HitObject.StartTime)
|
||||
?? hitObjectContainer.Entries.Where(e => !isAlreadyHit(e)).MinBy(e => e.HitObject.StartTime);
|
||||
|
||||
// In the case there are no non-judged objects, the last hit object should be used instead.
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
public IEnumerable<DrawableHitObject> Objects => InternalChildren.Cast<DrawableHitObject>().OrderBy(h => h.HitObject.StartTime);
|
||||
|
||||
public IEnumerable<DrawableHitObject> AliveObjects => AliveEntries.Select(pair => pair.Drawable).OrderBy(h => h.HitObject.StartTime);
|
||||
public IEnumerable<DrawableHitObject> AliveObjects => AliveEntries.Values.OrderBy(h => h.HitObject.StartTime);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a <see cref="DrawableHitObject"/> is judged.
|
||||
|
@ -188,7 +188,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
// We are not using AliveObjects directly to avoid selection/sorting overhead since we don't care about the order at which positions will be updated.
|
||||
foreach (var entry in AliveEntries)
|
||||
{
|
||||
var obj = entry.Drawable;
|
||||
var obj = entry.Value;
|
||||
|
||||
updatePosition(obj, Time.Current);
|
||||
|
||||
|
@ -198,8 +198,10 @@ namespace osu.Game.Screens.Play
|
||||
foreach (var nested in playfield.NestedPlayfields)
|
||||
applyToPlayfield(nested);
|
||||
|
||||
foreach (DrawableHitObject obj in playfield.HitObjectContainer.AliveObjects)
|
||||
foreach (var entry in playfield.HitObjectContainer.AliveEntries)
|
||||
{
|
||||
var obj = entry.Value;
|
||||
|
||||
if (appliedObjects.Contains(obj))
|
||||
continue;
|
||||
|
||||
|
@ -58,6 +58,9 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
}
|
||||
|
||||
protected override IResourceStore<TextureUpload> CreateTextureLoaderStore(IStorageResourceProvider resources, IResourceStore<byte[]> storage)
|
||||
=> new LegacyTextureLoaderStore(base.CreateTextureLoaderStore(resources, storage));
|
||||
|
||||
protected override void ParseConfigurationStream(Stream stream)
|
||||
{
|
||||
base.ParseConfigurationStream(stream);
|
||||
|
95
osu.Game/Skinning/LegacyTextureLoaderStore.cs
Normal file
95
osu.Game/Skinning/LegacyTextureLoaderStore.cs
Normal file
@ -0,0 +1,95 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public class LegacyTextureLoaderStore : IResourceStore<TextureUpload>
|
||||
{
|
||||
private readonly IResourceStore<TextureUpload>? wrappedStore;
|
||||
|
||||
public LegacyTextureLoaderStore(IResourceStore<TextureUpload>? wrappedStore)
|
||||
{
|
||||
this.wrappedStore = wrappedStore;
|
||||
}
|
||||
|
||||
public TextureUpload Get(string name)
|
||||
{
|
||||
var textureUpload = wrappedStore?.Get(name);
|
||||
|
||||
if (textureUpload == null)
|
||||
return null!;
|
||||
|
||||
return shouldConvertToGrayscale(name)
|
||||
? convertToGrayscale(textureUpload)
|
||||
: textureUpload;
|
||||
}
|
||||
|
||||
public Task<TextureUpload> GetAsync(string name, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
var textureUpload = wrappedStore?.Get(name);
|
||||
|
||||
if (textureUpload == null)
|
||||
return null!;
|
||||
|
||||
return shouldConvertToGrayscale(name)
|
||||
? Task.Run(() => convertToGrayscale(textureUpload), cancellationToken)
|
||||
: Task.FromResult(textureUpload);
|
||||
}
|
||||
|
||||
// https://github.com/peppy/osu-stable-reference/blob/013c3010a9d495e3471a9c59518de17006f9ad89/osu!/Graphics/Textures/TextureManager.cs#L91-L96
|
||||
private static readonly string[] grayscale_sprites =
|
||||
{
|
||||
@"taiko-bar-right",
|
||||
@"taikobigcircle",
|
||||
@"taikohitcircle",
|
||||
@"taikohitcircleoverlay"
|
||||
};
|
||||
|
||||
private bool shouldConvertToGrayscale(string name)
|
||||
{
|
||||
foreach (string grayscaleSprite in grayscale_sprites)
|
||||
{
|
||||
// unfortunately at this level of lookup we can encounter `@2x` scale suffixes in the name,
|
||||
// so straight equality cannot be used.
|
||||
if (name.Equals(grayscaleSprite, StringComparison.OrdinalIgnoreCase)
|
||||
|| name.Equals($@"{grayscaleSprite}@2x", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private TextureUpload convertToGrayscale(TextureUpload textureUpload)
|
||||
{
|
||||
var image = Image.LoadPixelData(textureUpload.Data.ToArray(), textureUpload.Width, textureUpload.Height);
|
||||
|
||||
// stable uses `0.299 * r + 0.587 * g + 0.114 * b`
|
||||
// (https://github.com/peppy/osu-stable-reference/blob/013c3010a9d495e3471a9c59518de17006f9ad89/osu!/Graphics/Textures/pTexture.cs#L138-L153)
|
||||
// which matches mode BT.601 (https://en.wikipedia.org/wiki/Grayscale#Luma_coding_in_video_systems)
|
||||
image.Mutate(i => i.Grayscale(GrayscaleMode.Bt601));
|
||||
|
||||
return new TextureUpload(image);
|
||||
}
|
||||
|
||||
public Stream? GetStream(string name) => wrappedStore?.GetStream(name);
|
||||
|
||||
public IEnumerable<string> GetAvailableResources() => wrappedStore?.GetAvailableResources() ?? Array.Empty<string>();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
wrappedStore?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osuTK;
|
||||
@ -92,7 +93,7 @@ namespace osu.Game.Updater
|
||||
public UpdateCompleteNotification(string version)
|
||||
{
|
||||
this.version = version;
|
||||
Text = $"You are now running osu! {version}.\nClick to see what's new!";
|
||||
Text = NotificationsStrings.GameVersionAfterUpdate(version);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -114,7 +115,7 @@ namespace osu.Game.Updater
|
||||
{
|
||||
public UpdateApplicationCompleteNotification()
|
||||
{
|
||||
Text = @"Update ready to install. Click to restart!";
|
||||
Text = NotificationsStrings.UpdateReadyToInstall;
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,13 +167,13 @@ namespace osu.Game.Updater
|
||||
{
|
||||
State = ProgressNotificationState.Active;
|
||||
Progress = 0;
|
||||
Text = @"Downloading update...";
|
||||
Text = NotificationsStrings.DownloadingUpdate;
|
||||
}
|
||||
|
||||
public void StartInstall()
|
||||
{
|
||||
Progress = 0;
|
||||
Text = @"Installing update...";
|
||||
Text = NotificationsStrings.InstallingUpdate;
|
||||
}
|
||||
|
||||
public void FailDownload()
|
||||
|
Loading…
Reference in New Issue
Block a user