1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 06:47:24 +08:00

Merge branch 'master' into beatmap-info

This commit is contained in:
C0D3 M4513R 2022-11-09 14:47:08 +00:00 committed by GitHub
commit a57c824092
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 300 additions and 141 deletions

View File

@ -16,13 +16,13 @@ namespace osu.Game.Rulesets.Taiko.Tests
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
[TestCase(3.1098944660126882d, 200, "diffcalc-test")]
[TestCase(3.1098944660126882d, 200, "diffcalc-test-strong")]
[TestCase(3.0920212594351191d, 200, "diffcalc-test")]
[TestCase(3.0920212594351191d, 200, "diffcalc-test-strong")]
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
=> base.Test(expectedStarRating, expectedMaxCombo, name);
[TestCase(4.0974106752474251d, 200, "diffcalc-test")]
[TestCase(4.0974106752474251d, 200, "diffcalc-test-strong")]
[TestCase(4.0789820318081444d, 200, "diffcalc-test")]
[TestCase(4.0789820318081444d, 200, "diffcalc-test-strong")]
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
=> Test(expectedStarRating, expectedMaxCombo, name, new TaikoModDoubleTime());

View File

@ -54,11 +54,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
TaikoDifficultyHitObjectColour colour = ((TaikoDifficultyHitObject)hitObject).Colour;
double difficulty = 0.0d;
if (colour.MonoStreak != null) // Difficulty for MonoStreak
if (colour.MonoStreak?.FirstHitObject == hitObject) // Difficulty for MonoStreak
difficulty += EvaluateDifficultyOf(colour.MonoStreak);
if (colour.AlternatingMonoPattern != null) // Difficulty for AlternatingMonoPattern
if (colour.AlternatingMonoPattern?.FirstHitObject == hitObject) // Difficulty for AlternatingMonoPattern
difficulty += EvaluateDifficultyOf(colour.AlternatingMonoPattern);
if (colour.RepeatingHitPattern != null) // Difficulty for RepeatingHitPattern
if (colour.RepeatingHitPattern?.FirstHitObject == hitObject) // Difficulty for RepeatingHitPattern
difficulty += EvaluateDifficultyOf(colour.RepeatingHitPattern);
return difficulty;

View File

@ -11,22 +11,43 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
public class StaminaEvaluator
{
/// <summary>
/// Applies a speed bonus dependent on the time since the last hit performed using this key.
/// Applies a speed bonus dependent on the time since the last hit performed using this finger.
/// </summary>
/// <param name="interval">The interval between the current and previous note hit using the same key.</param>
/// <param name="interval">The interval between the current and previous note hit using the same finger.</param>
private static double speedBonus(double interval)
{
// Cap to 600bpm 1/4, 25ms note interval, 50ms key interval
// Interval will be capped at a very small value to avoid infinite/negative speed bonuses.
// TODO - This is a temporary measure as we need to implement methods of detecting playstyle-abuse of SpeedBonus.
interval = Math.Max(interval, 50);
// Interval is capped at a very small value to prevent infinite values.
interval = Math.Max(interval, 1);
return 30 / interval;
}
/// <summary>
/// Determines the number of fingers available to hit the current <see cref="TaikoDifficultyHitObject"/>.
/// Any mono notes that is more than 300ms apart from a colour change will be considered to have more than 2
/// fingers available, since players can hit the same key with multiple fingers.
/// </summary>
private static int availableFingersFor(TaikoDifficultyHitObject hitObject)
{
DifficultyHitObject? previousColourChange = hitObject.Colour.PreviousColourChange;
DifficultyHitObject? nextColourChange = hitObject.Colour.NextColourChange;
if (previousColourChange != null && hitObject.StartTime - previousColourChange.StartTime < 300)
{
return 2;
}
if (nextColourChange != null && nextColourChange.StartTime - hitObject.StartTime < 300)
{
return 2;
}
return 4;
}
/// <summary>
/// Evaluates the minimum mechanical stamina required to play the current object. This is calculated using the
/// maximum possible interval between two hits using the same key, by alternating 2 keys for each colour.
/// maximum possible interval between two hits using the same key, by alternating available fingers for each colour.
/// </summary>
public static double EvaluateDifficultyOf(DifficultyHitObject current)
{
@ -35,13 +56,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
return 0.0;
}
// Find the previous hit object hit by the current key, which is two notes of the same colour prior.
// Find the previous hit object hit by the current finger, which is n notes prior, n being the number of
// available fingers.
TaikoDifficultyHitObject taikoCurrent = (TaikoDifficultyHitObject)current;
TaikoDifficultyHitObject? keyPrevious = taikoCurrent.PreviousMono(1);
TaikoDifficultyHitObject? keyPrevious = taikoCurrent.PreviousMono(availableFingersFor(taikoCurrent) - 1);
if (keyPrevious == null)
{
// There is no previous hit object hit by the current key
// There is no previous hit object hit by the current finger
return 0.0;
}

View File

@ -33,6 +33,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
/// </summary>
public TaikoDifficultyHitObject FirstHitObject => HitObjects[0];
/// <summary>
/// The last <see cref="TaikoDifficultyHitObject"/> in this <see cref="MonoStreak"/>.
/// </summary>
public TaikoDifficultyHitObject LastHitObject => HitObjects[^1];
/// <summary>
/// The hit type of all objects encoded within this <see cref="MonoStreak"/>
/// </summary>

View File

@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
public readonly List<AlternatingMonoPattern> AlternatingMonoPatterns = new List<AlternatingMonoPattern>();
/// <summary>
/// The parent <see cref="TaikoDifficultyHitObject"/> in this <see cref="RepeatingHitPatterns"/>
/// The first <see cref="TaikoDifficultyHitObject"/> in this <see cref="RepeatingHitPatterns"/>
/// </summary>
public TaikoDifficultyHitObject FirstHitObject => AlternatingMonoPatterns[0].FirstHitObject;

View File

@ -15,19 +15,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour
{
/// <summary>
/// Processes and encodes a list of <see cref="TaikoDifficultyHitObject"/>s into a list of <see cref="TaikoDifficultyHitObjectColour"/>s,
/// assigning the appropriate <see cref="TaikoDifficultyHitObjectColour"/>s to each <see cref="TaikoDifficultyHitObject"/>,
/// and pre-evaluating colour difficulty of each <see cref="TaikoDifficultyHitObject"/>.
/// assigning the appropriate <see cref="TaikoDifficultyHitObjectColour"/>s to each <see cref="TaikoDifficultyHitObject"/>.
/// </summary>
public static void ProcessAndAssign(List<DifficultyHitObject> hitObjects)
{
List<RepeatingHitPatterns> hitPatterns = encode(hitObjects);
// Assign indexing and encoding data to all relevant objects. Only the first note of each encoding type is
// assigned with the relevant encodings.
// Assign indexing and encoding data to all relevant objects.
foreach (var repeatingHitPattern in hitPatterns)
{
repeatingHitPattern.FirstHitObject.Colour.RepeatingHitPattern = repeatingHitPattern;
// The outermost loop is kept a ForEach loop since it doesn't need index information, and we want to
// keep i and j for AlternatingMonoPattern's and MonoStreak's index respectively, to keep it in line with
// documentation.
@ -36,14 +32,19 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour
AlternatingMonoPattern monoPattern = repeatingHitPattern.AlternatingMonoPatterns[i];
monoPattern.Parent = repeatingHitPattern;
monoPattern.Index = i;
monoPattern.FirstHitObject.Colour.AlternatingMonoPattern = monoPattern;
for (int j = 0; j < monoPattern.MonoStreaks.Count; ++j)
{
MonoStreak monoStreak = monoPattern.MonoStreaks[j];
monoStreak.Parent = monoPattern;
monoStreak.Index = j;
monoStreak.FirstHitObject.Colour.MonoStreak = monoStreak;
foreach (var hitObject in monoStreak.HitObjects)
{
hitObject.Colour.RepeatingHitPattern = repeatingHitPattern;
hitObject.Colour.AlternatingMonoPattern = monoPattern;
hitObject.Colour.MonoStreak = monoStreak;
}
}
}
}

View File

@ -11,18 +11,28 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour
public class TaikoDifficultyHitObjectColour
{
/// <summary>
/// The <see cref="MonoStreak"/> that encodes this note, only present if this is the first note within a <see cref="MonoStreak"/>
/// The <see cref="MonoStreak"/> that encodes this note.
/// </summary>
public MonoStreak? MonoStreak;
/// <summary>
/// The <see cref="AlternatingMonoPattern"/> that encodes this note, only present if this is the first note within a <see cref="AlternatingMonoPattern"/>
/// The <see cref="AlternatingMonoPattern"/> that encodes this note.
/// </summary>
public AlternatingMonoPattern? AlternatingMonoPattern;
/// <summary>
/// The <see cref="RepeatingHitPattern"/> that encodes this note, only present if this is the first note within a <see cref="RepeatingHitPattern"/>
/// The <see cref="RepeatingHitPattern"/> that encodes this note.
/// </summary>
public RepeatingHitPatterns? RepeatingHitPattern;
/// <summary>
/// The closest past <see cref="TaikoDifficultyHitObject"/> that's not the same colour.
/// </summary>
public TaikoDifficultyHitObject? PreviousColourChange => MonoStreak?.FirstHitObject.PreviousNote(0);
/// <summary>
/// The closest future <see cref="TaikoDifficultyHitObject"/> that's not the same colour.
/// </summary>
public TaikoDifficultyHitObject? NextColourChange => MonoStreak?.LastHitObject.NextNote(0);
}
}

View File

@ -13,9 +13,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
/// <summary>
/// Calculates the stamina coefficient of taiko difficulty.
/// </summary>
/// <remarks>
/// The reference play style chosen uses two hands, with full alternating (the hand changes after every hit).
/// </remarks>
public class Stamina : StrainDecaySkill
{
protected override double SkillMultiplier => 1.1;

View File

@ -83,15 +83,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
double combinedRating = combined.DifficultyValue() * difficulty_multiplier;
double starRating = rescale(combinedRating * 1.4);
// TODO: This is temporary measure as we don't detect abuse of multiple-input playstyles of converts within the current system.
if (beatmap.BeatmapInfo.Ruleset.OnlineID == 0)
{
starRating *= 0.925;
// For maps with low colour variance and high stamina requirement, multiple inputs are more likely to be abused.
if (colourRating < 2 && staminaRating > 8)
starRating *= 0.80;
}
HitWindows hitWindows = new TaikoHitWindows();
hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);

View File

@ -43,6 +43,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (totalSuccessfulHits > 0)
effectiveMissCount = Math.Max(1.0, 1000.0 / totalSuccessfulHits) * countMiss;
// TODO: The detection of rulesets is temporary until the leftover old skills have been reworked.
bool isConvert = score.BeatmapInfo.Ruleset.OnlineID != 1;
double multiplier = 1.13;
if (score.Mods.Any(m => m is ModHidden))
@ -51,8 +54,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (score.Mods.Any(m => m is ModEasy))
multiplier *= 0.975;
double difficultyValue = computeDifficultyValue(score, taikoAttributes);
double accuracyValue = computeAccuracyValue(score, taikoAttributes);
double difficultyValue = computeDifficultyValue(score, taikoAttributes, isConvert);
double accuracyValue = computeAccuracyValue(score, taikoAttributes, isConvert);
double totalValue =
Math.Pow(
Math.Pow(difficultyValue, 1.1) +
@ -68,7 +71,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
};
}
private double computeDifficultyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
private double computeDifficultyValue(ScoreInfo score, TaikoDifficultyAttributes attributes, bool isConvert)
{
double difficultyValue = Math.Pow(5 * Math.Max(1.0, attributes.StarRating / 0.115) - 4.0, 2.25) / 1150.0;
@ -80,7 +83,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (score.Mods.Any(m => m is ModEasy))
difficultyValue *= 0.985;
if (score.Mods.Any(m => m is ModHidden))
if (score.Mods.Any(m => m is ModHidden) && !isConvert)
difficultyValue *= 1.025;
if (score.Mods.Any(m => m is ModHardRock))
@ -92,7 +95,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
return difficultyValue * Math.Pow(accuracy, 2.0);
}
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes, bool isConvert)
{
if (attributes.GreatHitWindow <= 0)
return 0;
@ -102,9 +105,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
accuracyValue *= lengthBonus;
// Slight HDFL Bonus for accuracy. A clamp is used to prevent against negative values
if (score.Mods.Any(m => m is ModFlashlight<TaikoHitObject>) && score.Mods.Any(m => m is ModHidden))
accuracyValue *= Math.Max(1.050, 1.075 * lengthBonus);
// Slight HDFL Bonus for accuracy. A clamp is used to prevent against negative values.
if (score.Mods.Any(m => m is ModFlashlight<TaikoHitObject>) && score.Mods.Any(m => m is ModHidden) && !isConvert)
accuracyValue *= Math.Max(1.0, 1.1 * lengthBonus);
return accuracyValue;
}

View File

@ -60,8 +60,9 @@ namespace osu.Game.Rulesets.Taiko.UI
/// </remarks>
private BarLinePlayfield barLinePlayfield;
private Container playfieldContent;
private Container playfieldOverlay;
private Container barLineContent;
private Container hitObjectContent;
private Container overlayContent;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
@ -121,22 +122,20 @@ namespace osu.Game.Rulesets.Taiko.UI
}
}
},
new Container
barLineContent = new Container
{
Name = "Bar line content",
RelativeSizeAxes = Axes.Both,
Child = barLinePlayfield = new BarLinePlayfield(),
},
hitObjectContent = new Container
{
Name = "Masked hit objects content",
RelativeSizeAxes = Axes.Both,
Masking = true,
Child = playfieldContent = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
barLinePlayfield = new BarLinePlayfield(),
HitObjectContainer,
}
}
Child = HitObjectContainer,
},
playfieldOverlay = new Container
overlayContent = new Container
{
Name = "Elements after hit objects",
RelativeSizeAxes = Axes.Both,
@ -215,8 +214,9 @@ namespace osu.Game.Rulesets.Taiko.UI
// Padding is required to be updated for elements which are based on "absolute" X sized elements.
// This is basically allowing for correct alignment as relative pieces move around them.
rightArea.Padding = new MarginPadding { Left = inputDrum.Width };
playfieldContent.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 };
playfieldOverlay.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 };
barLineContent.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 };
hitObjectContent.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 };
overlayContent.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 };
mascot.Scale = new Vector2(DrawHeight / DEFAULT_HEIGHT);
}

View File

@ -7,10 +7,12 @@ using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
@ -21,7 +23,6 @@ using osu.Game.Screens.Edit.GameplayTest;
using osu.Game.Screens.Play;
using osu.Game.Storyboards;
using osu.Game.Tests.Beatmaps.IO;
using osuTK.Graphics;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Editing
@ -40,6 +41,14 @@ namespace osu.Game.Tests.Visual.Editing
private BeatmapSetInfo importedBeatmapSet;
private Bindable<float> editorDim;
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
editorDim = config.GetBindable<float>(OsuSetting.EditorDim);
}
public override void SetUpSteps()
{
AddStep("import test beatmap", () => importedBeatmapSet = BeatmapImportHelper.LoadOszIntoOsu(game).GetResultSafely());
@ -77,7 +86,7 @@ namespace osu.Game.Tests.Visual.Editing
// this test cares about checking the background belonging to the editor specifically, so check that using reference equality
// (as `.Equals()` cannot discern between the two, as they technically share the same database GUID).
var background = this.ChildrenOfType<BackgroundScreenBeatmap>().Single(b => ReferenceEquals(b.Beatmap.BeatmapInfo, EditorBeatmap.BeatmapInfo));
return background.Colour == Color4.DarkGray && background.BlurAmount.Value == 0;
return background.DimWhenUserSettingsIgnored.Value == editorDim.Value && background.BlurAmount.Value == 0;
});
AddAssert("no mods selected", () => SelectedMods.Value.Count == 0);
}
@ -110,7 +119,7 @@ namespace osu.Game.Tests.Visual.Editing
// this test cares about checking the background belonging to the editor specifically, so check that using reference equality
// (as `.Equals()` cannot discern between the two, as they technically share the same database GUID).
var background = this.ChildrenOfType<BackgroundScreenBeatmap>().Single(b => ReferenceEquals(b.Beatmap.BeatmapInfo, EditorBeatmap.BeatmapInfo));
return background.Colour == Color4.DarkGray && background.BlurAmount.Value == 0;
return background.DimWhenUserSettingsIgnored.Value == editorDim.Value && background.BlurAmount.Value == 0;
});
AddStep("start track", () => EditorClock.Start());

View File

@ -171,7 +171,8 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full);
SetDefault(OsuSetting.EditorWaveformOpacity, 0.25f);
SetDefault(OsuSetting.EditorDim, 0.25f, 0f, 0.75f, 0.25f);
SetDefault(OsuSetting.EditorWaveformOpacity, 0.25f, 0f, 1f, 0.25f);
SetDefault(OsuSetting.LastProcessedMetadataId, -1);
}
@ -288,6 +289,7 @@ namespace osu.Game.Configuration
GameplayCursorDuringTouch,
DimLevel,
BlurLevel,
EditorDim,
LightenDuringBreaks,
ShowStoryboard,
KeyOverlay,

View File

@ -111,8 +111,6 @@ namespace osu.Game.Graphics.Backgrounds
private void load(LargeTextureStore textures)
{
Sprite.Texture = textures.Get(url) ?? textures.Get(fallback_texture_name);
// ensure we're not loading in without a transition.
this.FadeInFromZero(200, Easing.InOutSine);
}
public override bool Equals(Background other)

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -46,15 +44,20 @@ namespace osu.Game.Graphics.Containers
/// </summary>
public bool ContentDisplayed { get; private set; }
protected Bindable<double> UserDimLevel { get; private set; }
protected Bindable<double> UserDimLevel { get; private set; } = null!;
protected Bindable<bool> LightenDuringBreaks { get; private set; }
/// <summary>
/// The amount of dim to be used when <see cref="IgnoreUserSettings"/> is <c>true</c>.
/// </summary>
public Bindable<float> DimWhenUserSettingsIgnored { get; set; } = new Bindable<float>();
protected Bindable<bool> ShowStoryboard { get; private set; }
protected Bindable<bool> LightenDuringBreaks { get; private set; } = null!;
protected Bindable<bool> ShowStoryboard { get; private set; } = null!;
private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0;
protected float DimLevel => Math.Max(!IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0);
protected float DimLevel => Math.Max(!IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : DimWhenUserSettingsIgnored.Value, 0);
protected override Container<Drawable> Content => dimContent;
@ -76,6 +79,7 @@ namespace osu.Game.Graphics.Containers
ShowStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
UserDimLevel.ValueChanged += _ => UpdateVisuals();
DimWhenUserSettingsIgnored.ValueChanged += _ => UpdateVisuals();
LightenDuringBreaks.ValueChanged += _ => UpdateVisuals();
IsBreakTime.ValueChanged += _ => UpdateVisuals();
ShowStoryboard.ValueChanged += _ => UpdateVisuals();

View File

@ -70,7 +70,8 @@ namespace osu.Game.Graphics.Cursor
private OsuGame? game { get; set; }
private readonly IBindable<bool> lastInputWasMouse = new BindableBool();
private readonly IBindable<bool> isIdle = new BindableBool();
private readonly IBindable<bool> gameActive = new BindableBool(true);
private readonly IBindable<bool> gameIdle = new BindableBool();
protected override void LoadComplete()
{
@ -81,8 +82,11 @@ namespace osu.Game.Graphics.Cursor
if (game != null)
{
isIdle.BindTo(game.IsIdle);
isIdle.BindValueChanged(_ => updateState());
gameIdle.BindTo(game.IsIdle);
gameIdle.BindValueChanged(_ => updateState());
gameActive.BindTo(game.IsActive);
gameActive.BindValueChanged(_ => updateState());
}
}
@ -90,7 +94,7 @@ namespace osu.Game.Graphics.Cursor
private void updateState()
{
bool combinedVisibility = State.Value == Visibility.Visible && (lastInputWasMouse.Value || !hideCursorOnNonMouseInput) && !isIdle.Value;
bool combinedVisibility = getCursorVisibility();
if (visible == combinedVisibility)
return;
@ -103,6 +107,27 @@ namespace osu.Game.Graphics.Cursor
PopOut();
}
private bool getCursorVisibility()
{
// do not display when explicitly set to hidden state.
if (State.Value == Visibility.Hidden)
return false;
// only hide cursor when game is focused, otherwise it should always be displayed.
if (gameActive.Value)
{
// do not display when last input is not mouse.
if (hideCursorOnNonMouseInput && !lastInputWasMouse.Value)
return false;
// do not display when game is idle.
if (gameIdle.Value)
return false;
}
return true;
}
protected override void Update()
{
base.Update();

View File

@ -191,7 +191,6 @@ namespace osu.Game.Online.Chat
{
protected override float TextSize => 15;
protected override float Spacing => 5;
protected override float TimestampWidth => 45;
protected override float UsernameWidth => 75;
public StandAloneMessage(Message message)

View File

@ -5,6 +5,7 @@ using System;
using System.Linq;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -12,6 +13,7 @@ using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
@ -48,8 +50,6 @@ namespace osu.Game.Overlays.Chat
protected virtual float Spacing => 15;
protected virtual float TimestampWidth => 60;
protected virtual float UsernameWidth => 130;
private Color4 usernameColour;
@ -62,6 +62,8 @@ namespace osu.Game.Overlays.Chat
private Container? highlight;
private readonly Bindable<bool> prefer24HourTime = new Bindable<bool>();
private bool senderHasColour => !string.IsNullOrEmpty(message.Sender.Colour);
private bool messageHasColour => Message.IsAction && senderHasColour;
@ -80,7 +82,7 @@ namespace osu.Game.Overlays.Chat
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider? colourProvider)
private void load(OverlayColourProvider? colourProvider, OsuConfigManager configManager)
{
usernameColour = senderHasColour
? Color4Extensions.FromHex(message.Sender.Colour)
@ -93,18 +95,13 @@ namespace osu.Game.Overlays.Chat
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
ColumnDimensions = new[]
{
new Dimension(GridSizeMode.Absolute, TimestampWidth + Spacing + UsernameWidth + Spacing),
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.Absolute, Spacing + UsernameWidth + Spacing),
new Dimension(),
},
Content = new[]
{
new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
timestamp = new OsuSpriteText
{
@ -112,8 +109,8 @@ namespace osu.Game.Overlays.Chat
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: TextSize * 0.75f, weight: FontWeight.SemiBold, fixedWidth: true),
MaxWidth = TimestampWidth,
Colour = colourProvider?.Background1 ?? Colour4.White,
AlwaysPresent = true,
},
new MessageSender(message.Sender)
{
@ -124,8 +121,6 @@ namespace osu.Game.Overlays.Chat
Child = createUsername(),
Margin = new MarginPadding { Horizontal = Spacing },
},
},
},
ContentFlow = new LinkFlowContainer(t =>
{
t.Shadow = false;
@ -139,6 +134,8 @@ namespace osu.Game.Overlays.Chat
},
}
};
configManager.BindWith(OsuSetting.Prefer24HourTime, prefer24HourTime);
}
protected override void LoadComplete()
@ -147,6 +144,8 @@ namespace osu.Game.Overlays.Chat
updateMessageContent();
FinishTransforms(true);
prefer24HourTime.BindValueChanged(_ => updateTimestamp());
}
/// <summary>
@ -176,7 +175,7 @@ namespace osu.Game.Overlays.Chat
this.FadeTo(message is LocalEchoMessage ? 0.4f : 1.0f, 500, Easing.OutQuint);
timestamp.FadeTo(message is LocalEchoMessage ? 0 : 1, 500, Easing.OutQuint);
timestamp.Text = $@"{message.Timestamp.LocalDateTime:HH:mm:ss}";
updateTimestamp();
username.Text = $@"{message.Sender.Username}";
// remove non-existent channels from the link list
@ -186,6 +185,13 @@ namespace osu.Game.Overlays.Chat
ContentFlow.AddLinks(message.DisplayContent, message.Links);
}
private void updateTimestamp()
{
timestamp.Text = prefer24HourTime.Value
? $@"{message.Timestamp.LocalDateTime:HH:mm:ss}"
: $@"{message.Timestamp.LocalDateTime:hh:mm:ss tt}";
}
private Drawable createUsername()
{
username = new OsuSpriteText

View File

@ -43,6 +43,11 @@ namespace osu.Game.Screens.Backgrounds
/// </summary>
public readonly Bindable<float> BlurAmount = new BindableFloat();
/// <summary>
/// The amount of dim to be used when <see cref="IgnoreUserSettings"/> is <c>true</c>.
/// </summary>
public readonly Bindable<float> DimWhenUserSettingsIgnored = new Bindable<float>();
internal readonly IBindable<bool> IsBreakTime = new Bindable<bool>();
private readonly DimmableBackground dimmable;
@ -58,6 +63,7 @@ namespace osu.Game.Screens.Backgrounds
dimmable.IgnoreUserSettings.BindTo(IgnoreUserSettings);
dimmable.IsBreakTime.BindTo(IsBreakTime);
dimmable.BlurAmount.BindTo(BlurAmount);
dimmable.DimWhenUserSettingsIgnored.BindTo(DimWhenUserSettingsIgnored);
StoryboardReplacesBackground.BindTo(dimmable.StoryboardReplacesBackground);
}

View File

@ -95,14 +95,14 @@ namespace osu.Game.Screens.Backgrounds
nextTask = Scheduler.AddDelayed(() =>
{
LoadComponentAsync(nextBackground, displayNext, cancellationTokenSource.Token);
}, 100);
}, 500);
return true;
}
private void displayNext(Background newBackground)
{
background?.FadeOut(800, Easing.InOutSine);
background?.FadeOut(800, Easing.OutQuint);
background?.Expire();
AddInternal(background = newBackground);

View File

@ -0,0 +1,45 @@
// 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.Collections.Generic;
using osu.Framework.Bindables;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Edit
{
internal class BackgroundDimMenuItem : MenuItem
{
private readonly Bindable<float> backgroudDim;
private readonly Dictionary<float, TernaryStateRadioMenuItem> menuItemLookup = new Dictionary<float, TernaryStateRadioMenuItem>();
public BackgroundDimMenuItem(Bindable<float> backgroudDim)
: base("Background dim")
{
Items = new[]
{
createMenuItem(0f),
createMenuItem(0.25f),
createMenuItem(0.5f),
createMenuItem(0.75f),
};
this.backgroudDim = backgroudDim;
backgroudDim.BindValueChanged(dim =>
{
foreach (var kvp in menuItemLookup)
kvp.Value.State.Value = kvp.Key == dim.NewValue ? TernaryState.True : TernaryState.False;
}, true);
}
private TernaryStateRadioMenuItem createMenuItem(float dim)
{
var item = new TernaryStateRadioMenuItem($"{dim * 100}%", MenuItemType.Standard, _ => updateOpacity(dim));
menuItemLookup[dim] = item;
return item;
}
private void updateOpacity(float dim) => backgroudDim.Value = dim;
}
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using osu.Game.Graphics.UserInterface;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
namespace osu.Game.Screens.Edit.Components.Menus
{
public class EditorMenuItemSpacer : EditorMenuItem

View File

@ -51,7 +51,6 @@ using osu.Game.Screens.Edit.Timing;
using osu.Game.Screens.Edit.Verify;
using osu.Game.Screens.Play;
using osu.Game.Users;
using osuTK.Graphics;
using osuTK.Input;
using CommonStrings = osu.Game.Resources.Localisation.Web.CommonStrings;
@ -176,6 +175,8 @@ namespace osu.Game.Screens.Edit
[Resolved(canBeNull: true)]
private OnScreenDisplay onScreenDisplay { get; set; }
private Bindable<float> editorBackgroundDim;
public Editor(EditorLoader loader = null)
{
this.loader = loader;
@ -260,6 +261,8 @@ namespace osu.Game.Screens.Edit
OsuMenuItem undoMenuItem;
OsuMenuItem redoMenuItem;
editorBackgroundDim = config.GetBindable<float>(OsuSetting.EditorDim);
AddInternal(new OsuContextMenuContainer
{
RelativeSizeAxes = Axes.Both,
@ -312,6 +315,7 @@ namespace osu.Game.Screens.Edit
Items = new MenuItem[]
{
new WaveformOpacityMenuItem(config.GetBindable<float>(OsuSetting.EditorWaveformOpacity)),
new BackgroundDimMenuItem(editorBackgroundDim),
}
}
}
@ -331,6 +335,8 @@ namespace osu.Game.Screens.Edit
changeHandler?.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true);
changeHandler?.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true);
editorBackgroundDim.BindValueChanged(_ => dimBackground());
}
[Resolved]
@ -630,10 +636,8 @@ namespace osu.Game.Screens.Edit
{
ApplyToBackground(b =>
{
// todo: temporary. we want to be applying dim using the UserDimContainer eventually.
b.FadeColour(Color4.DarkGray, 500);
b.IgnoreUserSettings.Value = true;
b.DimWhenUserSettingsIgnored.Value = editorBackgroundDim.Value;
b.BlurAmount.Value = 0;
});
}
@ -661,7 +665,11 @@ namespace osu.Game.Screens.Edit
}
}
ApplyToBackground(b => b.FadeColour(Color4.White, 500));
ApplyToBackground(b =>
{
b.DimWhenUserSettingsIgnored.Value = 0;
});
resetTrack();
refetchBeatmap();

View File

@ -770,6 +770,26 @@ namespace osu.Game.Screens.Select
{
updateItem(item);
if (!item.Item.Filtered.Value)
{
bool isSelected = item.Item.State.Value == CarouselItemState.Selected;
// Cheap way of doing animations when entering / exiting song select.
const double half_time = 50;
const float panel_x_offset_when_inactive = 200;
if (isSelected || AllowSelection)
{
item.Alpha = (float)Interpolation.DampContinuously(item.Alpha, 1, half_time, Clock.ElapsedFrameTime);
item.X = (float)Interpolation.DampContinuously(item.X, 0, half_time, Clock.ElapsedFrameTime);
}
else
{
item.Alpha = (float)Interpolation.DampContinuously(item.Alpha, 0, half_time, Clock.ElapsedFrameTime);
item.X = (float)Interpolation.DampContinuously(item.X, panel_x_offset_when_inactive, half_time, Clock.ElapsedFrameTime);
}
}
if (item is DrawableCarouselBeatmapSet set)
{
foreach (var diff in set.DrawableBeatmaps)

View File

@ -89,6 +89,8 @@ namespace osu.Game.Screens.Select
protected BeatmapCarousel Carousel { get; private set; }
private ParallaxContainer wedgeBackground;
protected Container LeftArea { get; private set; }
private BeatmapInfoWedge beatmapInfoWedge;
@ -165,10 +167,12 @@ namespace osu.Game.Screens.Select
{
new Drawable[]
{
new ParallaxContainer
wedgeBackground = new ParallaxContainer
{
ParallaxAmount = 0.005f,
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Child = new WedgeBackground
{
RelativeSizeAxes = Axes.Both,
@ -609,9 +613,15 @@ namespace osu.Game.Screens.Select
}
}
this.FadeIn(250);
LeftArea.MoveToX(0, 400, Easing.OutQuint);
LeftArea.FadeIn(100, Easing.OutQuint);
this.ScaleTo(1, 250, Easing.OutSine);
FilterControl.MoveToY(0, 400, Easing.OutQuint);
FilterControl.FadeIn(100, Easing.OutQuint);
this.FadeIn(250, Easing.OutQuint);
wedgeBackground.ScaleTo(1, 500, Easing.OutQuint);
FilterControl.Activate();
}
@ -623,17 +633,8 @@ namespace osu.Game.Screens.Select
transferRulesetValue();
ModSelect.SelectedMods.UnbindFrom(selectedMods);
ModSelect.Hide();
BeatmapOptions.Hide();
endLooping();
this.ScaleTo(1.1f, 250, Easing.InSine);
this.FadeOut(250);
FilterControl.Deactivate();
playExitingTransition();
base.OnSuspending(e);
}
@ -642,16 +643,31 @@ namespace osu.Game.Screens.Select
if (base.OnExiting(e))
return true;
beatmapInfoWedge.Hide();
playExitingTransition();
return false;
}
private void playExitingTransition()
{
ModSelect.Hide();
this.FadeOut(100);
BeatmapOptions.Hide();
FilterControl.Deactivate();
Carousel.AllowSelection = false;
endLooping();
return false;
FilterControl.MoveToY(-120, 500, Easing.OutQuint);
FilterControl.FadeOut(200, Easing.OutQuint);
LeftArea.MoveToX(-150, 1800, Easing.OutQuint);
LeftArea.FadeOut(200, Easing.OutQuint);
wedgeBackground.ScaleTo(2.4f, 400, Easing.OutQuint);
this.FadeOut(400, Easing.OutQuint);
FilterControl.Deactivate();
}
private bool isHandlingLooping;