mirror of
https://github.com/ppy/osu
synced 2024-12-14 19:06:07 +00:00
Merge branch 'master' into daily-challenge/better-messaging
This commit is contained in:
commit
d75c170ba1
@ -25,6 +25,9 @@ namespace osu.Game.Tests.Editing
|
||||
new object?[] { "1:02:3000", false, null, null },
|
||||
new object?[] { "1:02:300 ()", false, null, null },
|
||||
new object?[] { "1:02:300 (1,2,3)", true, new TimeSpan(0, 0, 1, 2, 300), "1,2,3" },
|
||||
new object?[] { "1:02:300 (1,2,3) - ", true, new TimeSpan(0, 0, 1, 2, 300), "1,2,3" },
|
||||
new object?[] { "1:02:300 (1,2,3) - following mod", true, new TimeSpan(0, 0, 1, 2, 300), "1,2,3" },
|
||||
new object?[] { "1:02:300 (1,2,3) - following mod\nwith newlines", true, new TimeSpan(0, 0, 1, 2, 300), "1,2,3" },
|
||||
};
|
||||
|
||||
[TestCaseSource(nameof(test_cases))]
|
||||
|
@ -8,6 +8,8 @@ using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Tests.Mods
|
||||
@ -105,9 +107,6 @@ namespace osu.Game.Tests.Mods
|
||||
testMod.ResetSettingsToDefaults();
|
||||
|
||||
Assert.That(testMod.DrainRate.Value, Is.Null);
|
||||
|
||||
// ReSharper disable once HeuristicUnreachableCode
|
||||
// see https://youtrack.jetbrains.com/issue/RIDER-70159.
|
||||
Assert.That(testMod.OverallDifficulty.Value, Is.Null);
|
||||
|
||||
var applied = applyDifficulty(new BeatmapDifficulty
|
||||
@ -119,6 +118,48 @@ namespace osu.Game.Tests.Mods
|
||||
Assert.That(applied.OverallDifficulty, Is.EqualTo(10));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDeserializeIncorrectRange()
|
||||
{
|
||||
var apiMod = new APIMod
|
||||
{
|
||||
Acronym = @"DA",
|
||||
Settings = new Dictionary<string, object>
|
||||
{
|
||||
[@"circle_size"] = -727,
|
||||
[@"approach_rate"] = -727,
|
||||
}
|
||||
};
|
||||
var ruleset = new OsuRuleset();
|
||||
|
||||
var mod = (OsuModDifficultyAdjust)apiMod.ToMod(ruleset);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(mod.CircleSize.Value, Is.GreaterThanOrEqualTo(0).And.LessThanOrEqualTo(11));
|
||||
Assert.That(mod.ApproachRate.Value, Is.GreaterThanOrEqualTo(-10).And.LessThanOrEqualTo(11));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDeserializeNegativeApproachRate()
|
||||
{
|
||||
var apiMod = new APIMod
|
||||
{
|
||||
Acronym = @"DA",
|
||||
Settings = new Dictionary<string, object>
|
||||
{
|
||||
[@"approach_rate"] = -9,
|
||||
}
|
||||
};
|
||||
var ruleset = new OsuRuleset();
|
||||
|
||||
var mod = (OsuModDifficultyAdjust)apiMod.ToMod(ruleset);
|
||||
|
||||
Assert.That(mod.ApproachRate.Value, Is.GreaterThanOrEqualTo(-10).And.LessThanOrEqualTo(11));
|
||||
Assert.That(mod.ApproachRate.Value, Is.EqualTo(-9));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a <see cref="BeatmapDifficulty"/> to the mod and returns a new <see cref="BeatmapDifficulty"/>
|
||||
/// representing the result if the mod were applied to a fresh <see cref="BeatmapDifficulty"/> instance.
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
@ -20,11 +21,11 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
[Cached]
|
||||
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum);
|
||||
|
||||
[Test]
|
||||
public void TestBasicAppearance()
|
||||
{
|
||||
DailyChallengeScoreBreakdown breakdown = null!;
|
||||
private DailyChallengeScoreBreakdown breakdown = null!;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("create content", () => Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
@ -50,7 +51,14 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
breakdown.Height = height;
|
||||
});
|
||||
|
||||
AddToggleStep("toggle visible", v => breakdown.Alpha = v ? 1 : 0);
|
||||
|
||||
AddStep("set initial data", () => breakdown.SetInitialCounts([1, 4, 9, 16, 25, 36, 49, 36, 25, 16, 9, 4, 1]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBasicAppearance()
|
||||
{
|
||||
AddStep("add new score", () =>
|
||||
{
|
||||
var ev = new NewScoreEvent(1, new APIUser
|
||||
@ -65,5 +73,24 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
AddStep("set user score", () => breakdown.UserBestScore.Value = new MultiplayerScore { TotalScore = RNG.Next(1_000_000) });
|
||||
AddStep("unset user score", () => breakdown.UserBestScore.Value = null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMassAdd()
|
||||
{
|
||||
AddStep("add 1000 scores at once", () =>
|
||||
{
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
var ev = new NewScoreEvent(1, new APIUser
|
||||
{
|
||||
Id = 2,
|
||||
Username = "peppy",
|
||||
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||
}, RNG.Next(1_000_000), null);
|
||||
|
||||
breakdown.AddNewScore(ev);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,8 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
if (ring.IsNotNull())
|
||||
ring.Height = height;
|
||||
});
|
||||
AddToggleStep("toggle visible", v => ring.Alpha = v ? 1 : 0);
|
||||
|
||||
AddStep("just started", () =>
|
||||
{
|
||||
room.Value.StartDate.Value = DateTimeOffset.Now.AddMinutes(-1);
|
||||
|
@ -49,6 +49,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
if (totals.IsNotNull())
|
||||
totals.Height = height;
|
||||
});
|
||||
AddToggleStep("toggle visible", v => totals.Alpha = v ? 1 : 0);
|
||||
|
||||
AddStep("set counts", () => totals.SetInitialCounts(totalPassCount: 9650, cumulativeTotalScore: 10_000_000_000));
|
||||
|
||||
|
@ -40,6 +40,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
Id = 3,
|
||||
Username = "LocalUser"
|
||||
};
|
||||
|
||||
string uuid = Guid.NewGuid().ToString();
|
||||
AddStep("add local echo message", () => channel.AddLocalEcho(new LocalEchoMessage
|
||||
{
|
||||
@ -83,5 +84,40 @@ namespace osu.Game.Tests.Visual.Online
|
||||
AddUntilStep("three day separators present", () => drawableChannel.ChildrenOfType<DaySeparator>().Count() == 3);
|
||||
AddAssert("last day separator is from correct day", () => drawableChannel.ChildrenOfType<DaySeparator>().Last().Date.Date == new DateTime(2022, 11, 22));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBackgroundAlternating()
|
||||
{
|
||||
var localUser = new APIUser
|
||||
{
|
||||
Id = 3,
|
||||
Username = "LocalUser"
|
||||
};
|
||||
|
||||
int messageCount = 1;
|
||||
|
||||
AddRepeatStep("add messages", () =>
|
||||
{
|
||||
channel.AddNewMessages(new Message(messageCount)
|
||||
{
|
||||
Sender = localUser,
|
||||
Content = "Hi there all!",
|
||||
Timestamp = new DateTimeOffset(2022, 11, 21, 20, messageCount, 13, TimeSpan.Zero),
|
||||
Uuid = Guid.NewGuid().ToString(),
|
||||
});
|
||||
messageCount++;
|
||||
}, 10);
|
||||
|
||||
AddUntilStep("10 message present", () => drawableChannel.ChildrenOfType<ChatLine>().Count() == 10);
|
||||
|
||||
int checkCount = 0;
|
||||
|
||||
AddRepeatStep("check background", () =>
|
||||
{
|
||||
// +1 because the day separator take one index
|
||||
Assert.AreEqual((checkCount + 1) % 2 == 0, drawableChannel.ChildrenOfType<ChatLine>().ToList()[checkCount].AlternatingBackground);
|
||||
checkCount++;
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOutOfRangeValueStillApplied()
|
||||
public void TestValueAboveRangeStillApplied()
|
||||
{
|
||||
AddStep("set override cs to 11", () => modDifficultyAdjust.CircleSize.Value = 11);
|
||||
|
||||
@ -91,6 +91,28 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
checkBindableAtValue("Circle Size", 11);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestValueBelowRangeStillApplied()
|
||||
{
|
||||
AddStep("set override cs to -5", () => modDifficultyAdjust.ApproachRate.Value = -5);
|
||||
|
||||
checkSliderAtValue("Approach Rate", -5);
|
||||
checkBindableAtValue("Approach Rate", -5);
|
||||
|
||||
// this is a no-op, just showing that it won't reset the value during deserialisation.
|
||||
setExtendedLimits(false);
|
||||
|
||||
checkSliderAtValue("Approach Rate", -5);
|
||||
checkBindableAtValue("Approach Rate", -5);
|
||||
|
||||
// setting extended limits will reset the serialisation exception.
|
||||
// this should be fine as the goal is to allow, at most, the value of extended limits.
|
||||
setExtendedLimits(true);
|
||||
|
||||
checkSliderAtValue("Approach Rate", -5);
|
||||
checkBindableAtValue("Approach Rate", -5);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExtendedLimits()
|
||||
{
|
||||
@ -109,6 +131,11 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
checkSliderAtValue("Circle Size", 11);
|
||||
checkBindableAtValue("Circle Size", 11);
|
||||
|
||||
setSliderValue("Approach Rate", -5);
|
||||
|
||||
checkSliderAtValue("Approach Rate", -5);
|
||||
checkBindableAtValue("Approach Rate", -5);
|
||||
|
||||
setExtendedLimits(false);
|
||||
|
||||
checkSliderAtValue("Circle Size", 10);
|
||||
|
@ -118,12 +118,11 @@ namespace osu.Game.Online.API
|
||||
u.OldValue?.Activity.UnbindFrom(activity);
|
||||
u.NewValue.Activity.BindTo(activity);
|
||||
|
||||
if (u.OldValue != null)
|
||||
localUserStatus.UnbindFrom(u.OldValue.Status);
|
||||
localUserStatus.BindTo(u.NewValue.Status);
|
||||
u.OldValue?.Status.UnbindFrom(localUserStatus);
|
||||
u.NewValue.Status.BindTo(localUserStatus);
|
||||
}, true);
|
||||
|
||||
localUserStatus.BindValueChanged(val => configStatus.Value = val.NewValue);
|
||||
localUserStatus.BindTo(configStatus);
|
||||
|
||||
var thread = new Thread(run)
|
||||
{
|
||||
@ -600,6 +599,7 @@ namespace osu.Game.Online.API
|
||||
password = null;
|
||||
SecondFactorCode = null;
|
||||
authentication.Clear();
|
||||
configStatus.Value = UserStatus.Online;
|
||||
|
||||
// Scheduled prior to state change such that the state changed event is invoked with the correct user and their friends present
|
||||
Schedule(() =>
|
||||
|
@ -63,7 +63,6 @@ using osu.Game.Screens;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Footer;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.DailyChallenge;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.Play;
|
||||
@ -757,11 +756,13 @@ namespace osu.Game
|
||||
// As a special case, if the beatmap and ruleset already match, allow immediately displaying the score from song select.
|
||||
// This is guaranteed to not crash, and feels better from a user's perspective (ie. if they are clicking a score in the
|
||||
// song select leaderboard).
|
||||
// Similar exemptions are made here for online flows where there are good chances that beatmap and ruleset match
|
||||
// (playlists / multiplayer / daily challenge).
|
||||
// Similar exemptions are made here for daily challenge where it is guaranteed that beatmap and ruleset match.
|
||||
// `OnlinePlayScreen` is excluded because when resuming back to it,
|
||||
// `RoomSubScreen` changes the global beatmap to the next playlist item on resume,
|
||||
// which may not match the score, and thus crash.
|
||||
IEnumerable<Type> validScreens =
|
||||
Beatmap.Value.BeatmapInfo.Equals(databasedBeatmap) && Ruleset.Value.Equals(databasedScore.ScoreInfo.Ruleset)
|
||||
? new[] { typeof(SongSelect), typeof(OnlinePlayScreen), typeof(DailyChallenge) }
|
||||
? new[] { typeof(SongSelect), typeof(DailyChallenge) }
|
||||
: Array.Empty<Type>();
|
||||
|
||||
PerformFromScreen(screen =>
|
||||
|
@ -21,7 +21,6 @@ using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Chat;
|
||||
using osuTK.Graphics;
|
||||
using Message = osu.Game.Online.Chat.Message;
|
||||
|
||||
namespace osu.Game.Overlays.Chat
|
||||
{
|
||||
@ -69,6 +68,23 @@ namespace osu.Game.Overlays.Chat
|
||||
|
||||
private Container? highlight;
|
||||
|
||||
private Drawable? background;
|
||||
|
||||
private bool alternatingBackground;
|
||||
|
||||
public bool AlternatingBackground
|
||||
{
|
||||
get => alternatingBackground;
|
||||
set
|
||||
{
|
||||
if (alternatingBackground == value)
|
||||
return;
|
||||
|
||||
alternatingBackground = value;
|
||||
updateBackground();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The colour used to paint the author's username.
|
||||
/// </summary>
|
||||
@ -102,48 +118,73 @@ namespace osu.Game.Overlays.Chat
|
||||
configManager.BindWith(OsuSetting.Prefer24HourTime, prefer24HourTime);
|
||||
prefer24HourTime.BindValueChanged(_ => updateTimestamp());
|
||||
|
||||
InternalChild = new GridContainer
|
||||
Padding = new MarginPadding { Right = 5 };
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
|
||||
ColumnDimensions = new[]
|
||||
background = new Container
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(GridSizeMode.Absolute, Spacing + UsernameWidth + Spacing),
|
||||
new Dimension(),
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
Masking = true,
|
||||
CornerRadius = 4,
|
||||
Alpha = 0,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Child = new Box
|
||||
{
|
||||
drawableTimestamp = new OsuSpriteText
|
||||
{
|
||||
Shadow = false,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(size: FontSize * 0.75f, weight: FontWeight.SemiBold, fixedWidth: true),
|
||||
AlwaysPresent = true,
|
||||
},
|
||||
drawableUsername = new DrawableChatUsername(message.Sender)
|
||||
{
|
||||
Width = UsernameWidth,
|
||||
FontSize = FontSize,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Origin = Anchor.TopRight,
|
||||
Anchor = Anchor.TopRight,
|
||||
Margin = new MarginPadding { Horizontal = Spacing },
|
||||
AccentColour = UsernameColour,
|
||||
Inverted = !string.IsNullOrEmpty(message.Sender.Colour),
|
||||
},
|
||||
drawableContentFlow = new LinkFlowContainer(styleMessageContent)
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
}
|
||||
Colour = Colour4.FromHex("#3b3234"),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
},
|
||||
new GridContainer
|
||||
{
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Horizontal = 2,
|
||||
Vertical = 2,
|
||||
},
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(GridSizeMode.Absolute, Spacing + UsernameWidth + Spacing),
|
||||
new Dimension(),
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
drawableTimestamp = new OsuSpriteText
|
||||
{
|
||||
Shadow = false,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(size: FontSize * 0.75f, weight: FontWeight.SemiBold, fixedWidth: true),
|
||||
AlwaysPresent = true,
|
||||
},
|
||||
drawableUsername = new DrawableChatUsername(message.Sender)
|
||||
{
|
||||
Width = UsernameWidth,
|
||||
FontSize = FontSize,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Origin = Anchor.TopRight,
|
||||
Anchor = Anchor.TopRight,
|
||||
Margin = new MarginPadding { Horizontal = Spacing },
|
||||
AccentColour = UsernameColour,
|
||||
Inverted = !string.IsNullOrEmpty(message.Sender.Colour),
|
||||
},
|
||||
drawableContentFlow = new LinkFlowContainer(styleMessageContent)
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
updateBackground();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -258,5 +299,11 @@ namespace osu.Game.Overlays.Chat
|
||||
Color4Extensions.FromHex("812a96"),
|
||||
Color4Extensions.FromHex("992861"),
|
||||
};
|
||||
|
||||
private void updateBackground()
|
||||
{
|
||||
if (background != null)
|
||||
background.Alpha = alternatingBackground ? 0.2f : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,17 @@ namespace osu.Game.Overlays.Chat
|
||||
highlightedMessage.BindValueChanged(_ => processMessageHighlighting(), true);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
for (int i = 0; i < ChatLineFlow.Count; i++)
|
||||
{
|
||||
if (ChatLineFlow[i] is ChatLine chatline)
|
||||
chatline.AlternatingBackground = i % 2 == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes any pending message in <see cref="highlightedMessage"/>.
|
||||
/// </summary>
|
||||
|
@ -157,6 +157,7 @@ namespace osu.Game.Overlays.Login
|
||||
},
|
||||
};
|
||||
|
||||
updateDropdownCurrent(status.Value);
|
||||
dropdown.Current.BindValueChanged(action =>
|
||||
{
|
||||
switch (action.NewValue)
|
||||
|
@ -7,7 +7,6 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osuTK;
|
||||
|
||||
@ -19,7 +18,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
private const double transition_duration = 200;
|
||||
|
||||
private readonly OsuSpriteText descriptionText;
|
||||
private readonly TextFlowContainer descriptionText;
|
||||
|
||||
public ModPresetTooltip(OverlayColourProvider colourProvider)
|
||||
{
|
||||
@ -44,11 +43,15 @@ namespace osu.Game.Overlays.Mods
|
||||
Spacing = new Vector2(7),
|
||||
Children = new[]
|
||||
{
|
||||
descriptionText = new OsuSpriteText
|
||||
descriptionText = new TextFlowContainer(f =>
|
||||
{
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular),
|
||||
Colour = colourProvider.Content1,
|
||||
},
|
||||
f.Font = OsuFont.GetFont(weight: FontWeight.Regular);
|
||||
f.Colour = colourProvider.Content1;
|
||||
})
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -11,7 +11,8 @@ namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
/// <summary>
|
||||
/// Used for parsing in contexts where we don't want e.g. normal times of day to be parsed as timestamps (e.g. chat)
|
||||
/// Original osu-web regex: https://github.com/ppy/osu-web/blob/3b1698639244cfdaf0b41c68bfd651ea729ec2e3/resources/js/utils/beatmapset-discussion-helper.ts#L78
|
||||
/// Original osu-web regex:
|
||||
/// https://github.com/ppy/osu-web/blob/3b1698639244cfdaf0b41c68bfd651ea729ec2e3/resources/js/utils/beatmapset-discussion-helper.ts#L78
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// 00:00:000 (...) - test
|
||||
@ -32,7 +33,10 @@ namespace osu.Game.Rulesets.Edit
|
||||
/// <item>1:02:300 (1,2,3) - parses to 01:02:300 with selection</item>
|
||||
/// </list>
|
||||
/// </example>
|
||||
private static readonly Regex time_regex_lenient = new Regex(@"^(((?<minutes>\d{1,3}):(?<seconds>([0-5]?\d))([:.](?<milliseconds>\d{0,3}))?)(?<selection>\s\([^)]+\))?)$", RegexOptions.Compiled);
|
||||
private static readonly Regex time_regex_lenient = new Regex(
|
||||
@"^(((?<minutes>\d{1,3}):(?<seconds>([0-5]?\d))([:.](?<milliseconds>\d{0,3}))?)(?<selection>\s\([^)]+\))?)(?<suffix>\s-.*)?$",
|
||||
RegexOptions.Compiled | RegexOptions.Singleline
|
||||
);
|
||||
|
||||
public static bool TryParse(string timestamp, [NotNullWhen(true)] out TimeSpan? parsedTime, out string? parsedSelection)
|
||||
{
|
||||
|
@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
public float MinValue
|
||||
{
|
||||
get => minValue;
|
||||
set
|
||||
{
|
||||
if (value == minValue)
|
||||
@ -52,6 +53,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
public float MaxValue
|
||||
{
|
||||
get => maxValue;
|
||||
set
|
||||
{
|
||||
if (value == maxValue)
|
||||
@ -69,6 +71,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
/// </summary>
|
||||
public float? ExtendedMinValue
|
||||
{
|
||||
get => extendedMinValue;
|
||||
set
|
||||
{
|
||||
if (value == extendedMinValue)
|
||||
@ -86,6 +89,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
/// </summary>
|
||||
public float? ExtendedMaxValue
|
||||
{
|
||||
get => extendedMaxValue;
|
||||
set
|
||||
{
|
||||
if (value == extendedMaxValue)
|
||||
@ -114,9 +118,14 @@ namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
// Ensure that in the case serialisation runs in the wrong order (and limit extensions aren't applied yet) the deserialised value is still propagated.
|
||||
if (value != null)
|
||||
CurrentNumber.MaxValue = MathF.Max(CurrentNumber.MaxValue, value.Value);
|
||||
{
|
||||
CurrentNumber.MinValue = Math.Clamp(MathF.Min(CurrentNumber.MinValue, value.Value), ExtendedMinValue ?? MinValue, MinValue);
|
||||
CurrentNumber.MaxValue = Math.Clamp(MathF.Max(CurrentNumber.MaxValue, value.Value), MaxValue, ExtendedMaxValue ?? MaxValue);
|
||||
|
||||
base.Value = value;
|
||||
base.Value = Math.Clamp(value.Value, CurrentNumber.MinValue, CurrentNumber.MaxValue);
|
||||
}
|
||||
else
|
||||
base.Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,6 +147,8 @@ namespace osu.Game.Rulesets.Mods
|
||||
// the following max value copies are only safe as long as these values are effectively constants.
|
||||
otherDifficultyBindable.MaxValue = maxValue;
|
||||
otherDifficultyBindable.ExtendedMaxValue = extendedMaxValue;
|
||||
otherDifficultyBindable.MinValue = minValue;
|
||||
otherDifficultyBindable.ExtendedMinValue = extendedMinValue;
|
||||
}
|
||||
|
||||
public override void BindTo(Bindable<float?> them)
|
||||
|
@ -58,7 +58,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
{
|
||||
case IHasPosition pos:
|
||||
AddHeader("Position");
|
||||
AddValue($"x:{pos.X:#,0.##} y:{pos.Y:#,0.##}");
|
||||
AddValue($"x:{pos.X:#,0.##}");
|
||||
AddValue($"y:{pos.Y:#,0.##}");
|
||||
break;
|
||||
|
||||
case IHasXPosition x:
|
||||
|
@ -473,6 +473,9 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
{
|
||||
base.OnResuming(e);
|
||||
applyLoopingToTrack();
|
||||
// re-apply mods as they may have been changed by a child screen
|
||||
// (one known instance of this is showing a replay).
|
||||
updateMods();
|
||||
}
|
||||
|
||||
public override void OnSuspending(ScreenTransitionEvent e)
|
||||
|
@ -50,7 +50,6 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
{
|
||||
drawable.RelativeSizeAxes = Axes.Both;
|
||||
drawable.Size = Vector2.One;
|
||||
drawable.AlwaysPresent = true;
|
||||
drawable.Alpha = 0;
|
||||
|
||||
base.Add(drawable);
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -27,9 +28,6 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
|
||||
private FillFlowContainer<Bar> barsContainer = null!;
|
||||
|
||||
// we're always present so that we can update while hidden, but we don't want tooltips to be displayed, therefore directly use alpha comparison here.
|
||||
public override bool PropagatePositionalInputSubTree => base.PropagatePositionalInputSubTree && Alpha > 0;
|
||||
|
||||
private const int bin_count = MultiplayerPlaylistItemStats.TOTAL_SCORE_DISTRIBUTION_BINS;
|
||||
private long[] bins = new long[bin_count];
|
||||
|
||||
@ -70,34 +68,61 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
});
|
||||
}
|
||||
|
||||
private readonly Queue<NewScoreEvent> newScores = new Queue<NewScoreEvent>();
|
||||
|
||||
public void AddNewScore(NewScoreEvent newScoreEvent)
|
||||
{
|
||||
int targetBin = (int)Math.Clamp(Math.Floor((float)newScoreEvent.TotalScore / 100000), 0, bin_count - 1);
|
||||
bins[targetBin] += 1;
|
||||
updateCounts();
|
||||
newScores.Enqueue(newScoreEvent);
|
||||
|
||||
var text = new OsuSpriteText
|
||||
// ensure things don't get too out-of-hand.
|
||||
if (newScores.Count > 25)
|
||||
{
|
||||
Text = newScoreEvent.TotalScore.ToString(@"N0"),
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Font = OsuFont.Default.With(size: 30),
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = (targetBin + 0.5f) / bin_count - 0.5f,
|
||||
Alpha = 0,
|
||||
};
|
||||
AddInternal(text);
|
||||
bins[getTargetBin(newScores.Dequeue())] += 1;
|
||||
Scheduler.AddOnce(updateCounts);
|
||||
}
|
||||
}
|
||||
|
||||
Scheduler.AddDelayed(() =>
|
||||
private double lastScoreDisplay;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (Time.Current - lastScoreDisplay > 150 && newScores.TryDequeue(out var newScore))
|
||||
{
|
||||
float startY = ToLocalSpace(barsContainer[targetBin].CircularBar.ScreenSpaceDrawQuad.TopLeft).Y;
|
||||
text.FadeInFromZero()
|
||||
.ScaleTo(new Vector2(0.8f), 500, Easing.OutElasticHalf)
|
||||
.MoveToY(startY)
|
||||
.MoveToOffset(new Vector2(0, -50), 2500, Easing.OutQuint)
|
||||
.FadeOut(2500, Easing.OutQuint)
|
||||
.Expire();
|
||||
}, 150);
|
||||
if (lastScoreDisplay < Time.Current)
|
||||
lastScoreDisplay = Time.Current;
|
||||
|
||||
int targetBin = getTargetBin(newScore);
|
||||
bins[targetBin] += 1;
|
||||
|
||||
updateCounts();
|
||||
|
||||
var text = new OsuSpriteText
|
||||
{
|
||||
Text = newScore.TotalScore.ToString(@"N0"),
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Font = OsuFont.Default.With(size: 30),
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = (targetBin + 0.5f) / bin_count - 0.5f,
|
||||
Alpha = 0,
|
||||
};
|
||||
AddInternal(text);
|
||||
|
||||
Scheduler.AddDelayed(() =>
|
||||
{
|
||||
float startY = ToLocalSpace(barsContainer[targetBin].CircularBar.ScreenSpaceDrawQuad.TopLeft).Y;
|
||||
text.FadeInFromZero()
|
||||
.ScaleTo(new Vector2(0.8f), 500, Easing.OutElasticHalf)
|
||||
.MoveToY(startY)
|
||||
.MoveToOffset(new Vector2(0, -50), 2500, Easing.OutQuint)
|
||||
.FadeOut(2500, Easing.OutQuint)
|
||||
.Expire();
|
||||
}, 150);
|
||||
|
||||
lastScoreDisplay = Time.Current;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetInitialCounts(long[] counts)
|
||||
@ -109,6 +134,9 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
updateCounts();
|
||||
}
|
||||
|
||||
private static int getTargetBin(NewScoreEvent score) =>
|
||||
(int)Math.Clamp(Math.Floor((float)score.TotalScore / 100000), 0, bin_count - 1);
|
||||
|
||||
private void updateCounts()
|
||||
{
|
||||
long max = Math.Max(bins.Max(), 1);
|
||||
|
Loading…
Reference in New Issue
Block a user