18 Commits

Author SHA1 Message Date
f5ca5083d6 implement pp for legacy song select 2025-11-16 21:21:12 +03:00
1d7c77d8d6 add performance points to selectv2 beatmap info wedge 2025-11-16 20:49:27 +03:00
774e52fbd6 slight result screen score panel redesign 2025-11-16 18:52:04 +03:00
a9d7a9d5d5 score panel is now tinted, plus some other changes 2025-11-16 02:48:41 +03:00
f7069b1009 minor fixes for some mods 2025-11-16 02:47:38 +03:00
c3ce5dc787 add argon-style longest combo counter 2025-11-15 19:24:49 +03:00
98076e2092 add skinnable online status and star rating components 2025-11-15 19:03:14 +03:00
b7d1092f90 Merge branch 'master' of https://gitea.jvnko.boats/jvnkosu/client 2025-11-15 17:02:04 +03:00
08db90c278 some minor hud changes
- argon-style cps counter
- keybinds in key counters (for now, only default)
2025-11-15 16:45:14 +03:00
b7e36164c3 update issue templates 2025-11-14 22:22:43 +01:00
0f5f13858d exit game option for fail condition mods (SD, PF, AC) 2025-11-13 21:41:09 +03:00
89a0c75156 all user-playable mods are now always ranked 2025-11-12 20:53:07 +03:00
ab7e5c94f1 make autoupdates work, at last 2025-11-11 19:11:36 +03:00
8dc9ea4553 add startup disclaimer 2025-11-11 18:14:38 +03:00
dcf553c252 Revert "Remove disclaimer screen completely"
This reverts commit bd0e2b4dde.
2025-11-11 16:45:04 +03:00
43f3a506ea new icon + default logo color 2025-11-11 00:04:18 +03:00
ab51579c27 added quit w/ replay button to pause menu; minor visual changes for ranks 2025-11-10 22:39:10 +03:00
d8e977c05f minor changes to mod scoring, all mods are ranked now
Probably all user-playable mods are ranked by default now,
Mania key mods were reverted to 1.0x score multiplier
2025-11-10 18:52:07 +03:00
71 changed files with 958 additions and 156 deletions

View File

@@ -1,12 +1,6 @@
blank_issues_enabled: false
blank_issues_enabled: true
contact_links:
- name: Help
url: https://github.com/ppy/osu/discussions/categories/q-a
about: osu! not working or performing as you'd expect? Not sure it's a bug? Check the Q&A section!
- name: Suggestions or feature request
url: https://github.com/ppy/osu/discussions/categories/ideas
about: Got something you think should change or be added? Search for or start a new discussion!
- name: osu!stable issues
url: https://github.com/ppy/osu-stable-issues
about: For osu!(stable) - ie. the current "live" game version, check out the dedicated repository. Note that this is for serious bug reports only, not tech support.
url: https://t.me/jvnkosu_chat
about: Your jvnkosu! is not working right? Please contact us using our Telegram chat

View File

@@ -115,12 +115,10 @@ namespace osu.Desktop
if (IsFirstRun)
LocalConfig.SetValue(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
// if (IsPackageManaged)
// return new NoActionUpdateManager();
if (IsPackageManaged)
return new NoActionUpdateManager();
// return new VelopackUpdateManager();
return new NoActionUpdateManager(); // for now, APIs are useless for actually downloading the releases. TODO: adapt UpdateManager for gitea
return new VelopackUpdateManager(); // yay
}
public override bool RestartAppWhenExited()

View File

@@ -208,9 +208,10 @@ namespace osu.Desktop
[SupportedOSPlatform("windows")]
private static void configureWindows(VelopackApp app)
{
app.OnFirstRun(_ => WindowsAssociationManager.InstallAssociations());
app.OnAfterUpdateFastCallback(_ => WindowsAssociationManager.UpdateAssociations());
app.OnBeforeUninstallFastCallback(_ => WindowsAssociationManager.UninstallAssociations());
// we do not want associations here, as that breaks official lazer's associations
// app.OnFirstRun(_ => WindowsAssociationManager.InstallAssociations());
// app.OnAfterUpdateFastCallback(_ => WindowsAssociationManager.UpdateAssociations());
// app.OnBeforeUninstallFastCallback(_ => WindowsAssociationManager.UninstallAssociations());
}
}
}

View File

@@ -55,7 +55,7 @@ namespace osu.Desktop.Updater
try
{
IUpdateSource updateSource = new GithubSource(@"https://github.com/ppy/osu", null, ReleaseStream.Value == Game.Configuration.ReleaseStream.Tachyon);
IUpdateSource updateSource = new GiteaSource(@"https://gitea.jvnko.boats/jvnkosu/client", null, ReleaseStream.Value == Game.Configuration.ReleaseStream.Tachyon);
Velopack.UpdateManager updateManager = new Velopack.UpdateManager(updateSource, new UpdateOptions
{
AllowVersionDowngrade = true

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 401 KiB

View File

@@ -14,8 +14,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Acronym => Name;
public abstract int KeyCount { get; }
public override ModType Type => ModType.Conversion;
public override double ScoreMultiplier => 0.9;
public override bool Ranked => UsesDefaultConfiguration;
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
public void ApplyToBeatmapConverter(IBeatmapConverter beatmapConverter)
{

View File

@@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Mods
typeof(ManiaModFadeIn)
}).ToArray();
public override bool Ranked => false;
public override bool Ranked => true;
public override bool ValidForFreestyleAsRequiredMod => false;

View File

@@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModHardRock : ModHardRock, IApplicableToHitObject
{
public override double ScoreMultiplier => 1;
public override bool Ranked => false;
public override bool Ranked => true;
public const double HIT_WINDOW_DIFFICULTY_MULTIPLIER = 1.4;

View File

@@ -14,6 +14,5 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Acronym => "1K";
public override IconUsage? Icon => OsuIcon.ModOneKey;
public override LocalisableString Description => @"Play with one key.";
public override bool Ranked => false;
}
}

View File

@@ -14,6 +14,5 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Acronym => "10K";
public override IconUsage? Icon => OsuIcon.ModTenKeys;
public override LocalisableString Description => @"Play with ten keys.";
public override bool Ranked => false;
}
}

View File

@@ -14,6 +14,5 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Acronym => "2K";
public override IconUsage? Icon => OsuIcon.ModTwoKeys;
public override LocalisableString Description => @"Play with two keys.";
public override bool Ranked => false;
}
}

View File

@@ -14,6 +14,5 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Acronym => "3K";
public override IconUsage? Icon => OsuIcon.ModThreeKeys;
public override LocalisableString Description => @"Play with three keys.";
public override bool Ranked => false;
}
}

View File

@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModMirror : ModMirror, IApplicableToBeatmap
{
public override LocalisableString Description => "Notes are flipped horizontally.";
public override bool Ranked => UsesDefaultConfiguration;
public override bool Ranked => true;
public void ApplyToBeatmap(IBeatmap beatmap)
{

View File

@@ -0,0 +1,16 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Game.Configuration;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModRateAdjustConcrete : ModRateAdjustConcrete
{
}
}

View File

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

View File

@@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModTouchDevice : ModTouchDevice
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModBloom) }).ToArray();
public override bool Ranked => UsesDefaultConfiguration;
public override bool Ranked => true;
}
}

View File

@@ -230,6 +230,14 @@ namespace osu.Game.Rulesets.Osu
new ModScoreV2(),
};
case ModType.Special:
#if DEBUG
return new Mod[]
{
new OsuModRateAdjustConcrete(),
};
#endif
default:
return Array.Empty<Mod>();
}

View File

@@ -0,0 +1,29 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Screens.Menu;
namespace osu.Game.Tests.Visual.Menus
{
public partial class TestSceneDisclaimer : ScreenTestScene
{
[BackgroundDependencyLoader]
private void load()
{
AddStep("load disclaimer", () => LoadScreen(new Disclaimer()));
AddStep("toggle support", () =>
{
((DummyAPIAccess)API).LocalUser.Value = new APIUser
{
Username = API.LocalUser.Value.Username,
Id = API.LocalUser.Value.Id + 1,
IsSupporter = !API.LocalUser.Value.IsSupporter,
};
});
}
}
}

View File

@@ -19,7 +19,7 @@ namespace osu.Game.Beatmaps
LocallyModified = -4,
[LocalisableDescription(typeof(SongSelectStrings), nameof(SongSelectStrings.StatusUnknown))]
[Description("Unknown")]
[Description("Offline")]
None = -3,
[LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusGraveyard))]

View File

@@ -33,7 +33,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards
Status = beatmapSet.Status,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
TextSize = 13f
TextSize = 13f,
ShowUnknownStatus = true
},
new DifficultySpectrumDisplay
{

View File

@@ -42,7 +42,7 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.Ruleset, string.Empty);
SetDefault(OsuSetting.Skin, SkinInfo.ARGON_SKIN.ToString());
SetDefault(OsuSetting.MenuCookieColor, Colour4.FromHex(@"ff66ba"));
SetDefault(OsuSetting.MenuCookieColor, Colour4.FromHex(@"8400FF"));
SetDefault(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Local);
SetDefault(OsuSetting.BeatmapLeaderboardSortMode, LeaderboardSortMode.Score);

View File

@@ -129,7 +129,7 @@ namespace osu.Game.Graphics
switch (status)
{
case BeatmapOnlineStatus.None:
return Color4.RosyBrown;
return Color4.AliceBlue;
case BeatmapOnlineStatus.LocallyModified:
return Color4.OrangeRed;
@@ -183,6 +183,9 @@ namespace osu.Game.Graphics
case ModType.System:
return Yellow;
case ModType.Special:
return Orange2;
default:
throw new ArgumentOutOfRangeException(nameof(modType), modType, "Unknown mod type");
}

View File

@@ -40,9 +40,9 @@ namespace osu.Game.Localisation
public static LocalisableString LocallyModifiedTooltip => new TranslatableString(getKey(@"locally_modified_tooltip"), @"Has been locally modified");
/// <summary>
/// "Unknown"
/// "Offline"
/// </summary>
public static LocalisableString StatusUnknown => new TranslatableString(getKey(@"status_unknown"), @"Unknown");
public static LocalisableString StatusUnknown => new TranslatableString(getKey(@"status_unknown"), @"Offline");
/// <summary>
/// "Total Plays"

View File

@@ -53,7 +53,7 @@ namespace osu.Game.Online.Leaderboards
Spacing = new Vector2(-3, 0),
Padding = new MarginPadding { Top = -5 },
Colour = GetRankLetterColour(rank),
Font = OsuFont.TorusAlternate.With(size: 42, weight: FontWeight.SemiBold),
Font = OsuFont.TorusAlternate.With(size: 40, weight: FontWeight.Bold),
Text = GetRankLetter(rank),
ShadowColour = Color4.Black.Opacity(0.3f),
ShadowOffset = new Vector2(0, 0.08f),
@@ -72,12 +72,14 @@ namespace osu.Game.Online.Leaderboards
switch (rank)
{
case ScoreRank.SH:
return @"S";
return @"S+";
case ScoreRank.X:
case ScoreRank.XH:
return @"SS";
case ScoreRank.XH:
return @"SS+";
default:
return rank.ToString();
}

View File

@@ -932,7 +932,7 @@ namespace osu.Game
protected virtual Loader CreateLoader() => new Loader();
protected virtual UpdateManager CreateUpdateManager() => new NoActionUpdateManager();
protected virtual UpdateManager CreateUpdateManager() => new UpdateManager();
/// <summary>
/// Adjust the globally applied <see cref="DrawSizePreservingFillContainer.TargetDrawSize"/> in every <see cref="ScalingContainer"/>.
@@ -1729,12 +1729,13 @@ namespace osu.Game
{
case IntroScreen intro:
introScreen = intro;
SimpleNotification notification = new SimpleNotification
{
Text = ButtonSystemStrings.GreetingNotification,
Transient = true,
};
Notifications?.Post(notification);
// SimpleNotification notification = new SimpleNotification
// {
// Text = ButtonSystemStrings.GreetingNotification,
// Transient = true,
// PopInSampleName = "",
// };
// Notifications?.Post(notification);
devBuildBanner?.Show();
break;

View File

@@ -191,7 +191,8 @@ namespace osu.Game.Overlays.BeatmapSet
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
TextSize = 14,
TextPadding = new MarginPadding { Horizontal = 35, Vertical = 10 }
TextPadding = new MarginPadding { Horizontal = 35, Vertical = 10 },
ShowUnknownStatus = true
},
storyboardIconPill = new StoryboardIconPill
{

View File

@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
@@ -9,6 +10,7 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays
{
@@ -26,30 +28,42 @@ namespace osu.Game.Overlays
AddRange(new Drawable[]
{
new OsuSpriteText
new FillFlowContainer
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Font = OsuFont.Torus.With(size: 12),
Colour = colours.GrayF,
Text = $@"jvnkosu! " + game.Version,
Y = -12,
},
new OsuSpriteText
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 15),
Colour = colours.Yellow,
Text = "Experimental version",
},
new Sprite
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Texture = textures.Get(@"Menu/dev-build-footer"),
Scale = new Vector2(0.4f, 1),
Y = 2,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Children = new Drawable[]
{
new OsuSpriteText
{
Font = OsuFont.GetFont(weight: FontWeight.Bold),
Text = game.Name
},
new OsuSpriteText
{
Colour = DebugUtils.IsDebugBuild ? colours.Red : Color4.White,
Text = game.Version
},
}
},
new Sprite
{
// Anchor = Anchor.BottomCentre,
// Origin = Anchor.BottomCentre,
Texture = textures.Get(@"Menu/dev-build-footer"),
Scale = new Vector2(0.4f, 1),
Y = 2,
}
},
},
});
}

View File

@@ -329,6 +329,7 @@ namespace osu.Game.Overlays.Mods
yield return createModColumnContent(ModType.Automation);
yield return createModColumnContent(ModType.Conversion);
yield return createModColumnContent(ModType.Fun);
yield return createModColumnContent(ModType.Special);
}
private ColumnDimContainer createModColumnContent(ModType modType)

View File

@@ -0,0 +1,22 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// Represents a mod which can override a fail and quit the game instead.
/// </summary>
public interface IApplicableFailExit : IApplicableMod
{
/// <summary>
/// Whether we should allow failing at the current point in time.
/// </summary>
/// <returns>Whether the fail should be allowed to proceed. Return false to block.</returns>
bool PerformFail();
/// <summary>
/// Whether we want to exit the game on fail. Only used if <see cref="PerformFail"/> returns true.
/// </summary>
bool ExitOnFail { get; }
}
}

View File

@@ -1,19 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// An interface for mods that apply changes to the <see cref="OsuGameBase"/>.
/// This is really stupid and f%%king dangerous, possibly disasterous even.
/// </summary>
public interface IApplicableToOsuGameBase : IApplicableMod
{
/// <summary>
/// Provide a <see cref="OsuGameBase"/>. Called once on initialisation of a play instance.
/// </summary>
void ApplyToOsuGameBase(OsuGameBase game);
}
}

View File

@@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Mods
public virtual bool RequiresConfiguration => false;
[JsonIgnore]
public virtual bool Ranked => false;
public virtual bool Ranked => true;
/// <summary>
/// The mods this mod cannot be enabled with.
@@ -127,7 +127,9 @@ namespace osu.Game.Rulesets.Mods
/// The settings are returned in ascending key order as per <see cref="SettingsMap"/>.
/// The ordering is intentionally enforced manually, as ordering of <see cref="Dictionary{TKey,TValue}.Values"/> is unspecified.
/// </remarks>
internal IEnumerable<IBindable> SettingsBindables => SettingsMap.OrderBy(pair => pair.Key).Select(pair => pair.Value);
internal IEnumerable<IBindable> SettingsBindables => SettingsMap.OrderBy(pair => pair.Key)
.Select(pair => pair.Value)
.Where(x => !x.GetType().GetCustomAttributes(typeof(JsonIgnoreAttribute)).Any());
/// <summary>
/// Provides mapping of names to <see cref="IBindable"/>s of all settings within this mod.

View File

@@ -23,6 +23,8 @@ namespace osu.Game.Rulesets.Mods
public sealed override bool UserPlayable => false;
public sealed override bool ValidForMultiplayer => false;
public sealed override bool ValidForMultiplayerAsFreeMod => false;
public override bool Ranked => false;
public override Type[] IncompatibleMods => new[] { typeof(ModCinema), typeof(ModRelax), typeof(ModAdaptiveSpeed), typeof(ModTouchDevice) };

View File

@@ -31,6 +31,7 @@ namespace osu.Game.Rulesets.Mods
public override LocalisableString Description => "Watch the video without visual distractions.";
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModFailCondition) }).ToArray();
public override bool Ranked => false;
public void ApplyToHUD(HUDOverlay overlay)
{

View File

@@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods
/// - Hit windows differ (https://github.com/ppy/osu/issues/11311).
/// - Sliders always gives combo for slider end, even on miss (https://github.com/ppy/osu/issues/11769).
/// </summary>
public sealed override bool Ranked => false;
public sealed override bool Ranked => true;
public sealed override bool ValidForFreestyleAsRequiredMod => false;
}

View File

@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage? Icon => OsuIcon.ModDaycore;
public override ModType Type => ModType.DifficultyReduction;
public override LocalisableString Description => "Whoaaaaa...";
public override bool Ranked => UsesDefaultConfiguration;
public override bool Ranked => true;
[SettingSource("Speed decrease", "The actual decrease to apply", SettingControlType = typeof(MultiplierSettingsSlider))]
public override BindableNumber<double> SpeedChange { get; } = new BindableDouble(0.75)

View File

@@ -19,7 +19,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 => SpeedChange.IsDefault;
public override bool Ranked => true;
[SettingSource("Speed increase", "The actual increase to apply", SettingControlType = typeof(MultiplierSettingsSlider))]
public override BindableNumber<double> SpeedChange { get; } = new BindableDouble(1.5)

View File

@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mods
public override ModType Type => ModType.DifficultyReduction;
public override double ScoreMultiplier => 0.5;
public override Type[] IncompatibleMods => new[] { typeof(ModHardRock), typeof(ModDifficultyAdjust) };
public override bool Ranked => UsesDefaultConfiguration;
public override bool Ranked => true;
public override bool ValidForFreestyleAsRequiredMod => true;
protected const float ADJUST_RATIO = 0.5f;

View File

@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Game.Configuration;
using osu.Game.Rulesets.Judgements;
@@ -9,17 +10,23 @@ using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mods
{
public abstract class ModFailCondition : Mod, IApplicableToHealthProcessor, IApplicableFailOverride
public abstract class ModFailCondition : Mod, IApplicableToHealthProcessor, IApplicableFailOverride, IApplicableFailExit
{
public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModCinema) };
[SettingSource("Restart on fail", "Automatically restarts when failed.")]
public BindableBool Restart { get; } = new BindableBool();
[SettingSource("Exit game on fail", "Automatically exits the game when failed."), JsonIgnore]
public BindableBool Exit { get; } = new BindableBool();
public virtual bool PerformFail() => true;
public virtual bool RestartOnFail => Restart.Value;
[JsonIgnore]
public virtual bool ExitOnFail => Exit.Value;
private Action? triggerFailureDelegate;
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
@@ -39,7 +46,7 @@ namespace osu.Game.Rulesets.Mods
/// </summary>
/// <param name="healthProcessor">The loaded <see cref="HealthProcessor"/>.</param>
/// <param name="result">The latest <see cref="JudgementResult"/>.</param>
/// <returns>Whether the fail condition has been met.</returns>
/// <returns>Whether the fail condition has been met.</returns>z
/// <remarks>
/// This method should only be used to trigger failures based on <paramref name="result"/>.
/// Using outside values to evaluate failure may introduce event ordering discrepancies, use

View File

@@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage? Icon => OsuIcon.ModFlashlight;
public override ModType Type => ModType.DifficultyIncrease;
public override LocalisableString Description => "Restricted view area.";
public override bool Ranked => UsesDefaultConfiguration;
public override bool Ranked => true;
[SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")]
public abstract BindableFloat SizeMultiplier { get; }

View File

@@ -20,7 +20,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 => SpeedChange.IsDefault;
public override bool Ranked => true;
[SettingSource("Speed decrease", "The actual decrease to apply", SettingControlType = typeof(MultiplierSettingsSlider))]
public override BindableNumber<double> SpeedChange { get; } = new BindableDouble(0.75)

View File

@@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mods
public override ModType Type => ModType.DifficultyIncrease;
public override LocalisableString Description => "Everything just got a bit harder...";
public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModDifficultyAdjust) };
public override bool Ranked => UsesDefaultConfiguration;
public override bool Ranked => true;
public override bool ValidForFreestyleAsRequiredMod => true;
protected const float ADJUST_RATIO = 1.4f;

View File

@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods
public override string Acronym => "HD";
public override IconUsage? Icon => OsuIcon.ModHidden;
public override ModType Type => ModType.DifficultyIncrease;
public override bool Ranked => UsesDefaultConfiguration;
public override bool Ranked => true;
public virtual void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
{

View File

@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage? Icon => OsuIcon.ModNightcore;
public override ModType Type => ModType.DifficultyIncrease;
public override LocalisableString Description => "Uguuuuuuuu...";
public override bool Ranked => UsesDefaultConfiguration;
public override bool Ranked => true;
[SettingSource("Speed increase", "The actual increase to apply", SettingControlType = typeof(MultiplierSettingsSlider))]
public override BindableNumber<double> SpeedChange { get; } = new BindableDouble(1.5)

View File

@@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mods
public override LocalisableString Description => "You can't fail, no matter what.";
public override double ScoreMultiplier => 0.5;
public override Type[] IncompatibleMods => new[] { typeof(ModFailCondition), typeof(ModCinema) };
public override bool Ranked => UsesDefaultConfiguration;
public override bool Ranked => true;
public override bool ValidForFreestyleAsRequiredMod => true;
private readonly Bindable<bool> showHealthBar = new Bindable<bool>();

View File

@@ -0,0 +1,47 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Localisation;
using osu.Game.Configuration;
using osu.Game.Overlays.Settings;
namespace osu.Game.Rulesets.Mods
{
public abstract class ModRateAdjustConcrete : ModRateAdjust
{
public override string Name => "Rate Adjust";
public override LocalisableString Description => "[DEBUG BUILDS ONLY] Set any speed";
public override string Acronym => "_R";
private readonly RateAdjustModHelper rateAdjustHelper;
[SettingSource("Speed decrease", "The actual decrease to apply", SettingControlType = typeof(MultiplierSettingsSlider))]
public override BindableNumber<double> SpeedChange { get; } = new BindableDouble(0.75)
{
MinValue = 0.1, // BASS breaks at lower rates
MaxValue = 10,
Precision = 0.01
};
[SettingSource("Adjust pitch", "Should pitch be adjusted with speed")]
public virtual BindableBool AdjustPitch { get; } = new BindableBool();
protected ModRateAdjustConcrete()
{
rateAdjustHelper = new RateAdjustModHelper(SpeedChange);
rateAdjustHelper.HandleAudioAdjustments(AdjustPitch);
}
public override double ScoreMultiplier => 1.0;
public override void ApplyToTrack(IAdjustableAudioComponent track)
{
rateAdjustHelper.ApplyToTrack(track);
}
public override bool Ranked => false;
public override ModType Type => ModType.Special;
}
}

View File

@@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
Conversion,
Automation,
Fun,
System
System,
Special
}
}

View File

@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mods
public readonly string OriginalAcronym;
public override string Name => $"Unknown mod ({OriginalAcronym})";
public override string Acronym => $"{OriginalAcronym}??";
public override string Acronym => $"{OriginalAcronym}!";
public override LocalisableString Description => "This mod could not be resolved by the game.";
public override double ScoreMultiplier => 0;

View File

@@ -2,6 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -32,6 +34,8 @@ namespace osu.Game.Rulesets.UI
public readonly KeyBindingContainer<T> KeyBindingContainer;
private readonly RulesetKeyBindingContainer rulesetKeyBindingContainer;
[Resolved]
private ScoreProcessor? scoreProcessor { get; set; }
@@ -64,8 +68,10 @@ namespace osu.Game.Rulesets.UI
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
{
rulesetKeyBindingContainer = createRulesetKeyBindingContainer(ruleset, variant, unique);
InternalChild = KeyBindingContainer =
CreateKeyBindingContainer(ruleset, variant, unique)
rulesetKeyBindingContainer
.WithChild(content = new Container { RelativeSizeAxes = Axes.Both });
}
@@ -174,11 +180,10 @@ namespace osu.Game.Rulesets.UI
public void Attach(InputCountController inputCountController)
{
var triggers = KeyBindingContainer.DefaultKeyBindings
.Select(b => b.GetAction<T>())
.Distinct()
.Select(action => new KeyCounterActionTrigger<T>(action))
.ToArray();
var bindings = rulesetKeyBindingContainer.DefaultKeyBindings;
var triggers = bindings.Select(b => new KeyCounterBindingTrigger<T>(b, b.GetAction<T>()))
.DistinctBy(b => b.Action)
.ToArray();
KeyBindingContainer.AddRange(triggers);
inputCountController.AddRange(triggers);
@@ -215,6 +220,9 @@ namespace osu.Game.Rulesets.UI
protected virtual KeyBindingContainer<T> CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
=> new RulesetKeyBindingContainer(ruleset, variant, unique);
private RulesetKeyBindingContainer createRulesetKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
=> new RulesetKeyBindingContainer(ruleset, variant, unique);
public partial class RulesetKeyBindingContainer : DatabasedKeyBindingContainer<T>
{
protected override bool HandleRepeats => false;

View File

@@ -23,6 +23,8 @@ namespace osu.Game.Screens
{
public partial class Loader : StartupScreen
{
private bool showDisclaimer;
public Loader()
{
ValidForResume = false;
@@ -35,7 +37,13 @@ namespace osu.Game.Screens
private LoadingSpinner spinner;
private ScheduledDelegate spinnerShow;
protected virtual OsuScreen CreateLoadableScreen() => getIntroSequence();
protected virtual OsuScreen CreateLoadableScreen()
{
if (showDisclaimer)
return new Disclaimer(getIntroSequence());
return getIntroSequence();
}
private IntroScreen getIntroSequence()
{
@@ -106,8 +114,9 @@ namespace osu.Game.Screens
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
private void load(OsuGameBase game, OsuConfigManager config)
{
showDisclaimer = game.IsDeployedBuild || !DebugUtils.IsDebugBuild;
introSequence = config.Get<IntroSequence>(OsuSetting.IntroSequence);
}

View File

@@ -0,0 +1,161 @@
// 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.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Screens;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Menu
{
public partial class Disclaimer : StartupScreen
{
private SpriteIcon icon;
private Color4 iconColour;
private LinkFlowContainer textFlow;
private const float icon_y = -85;
private const float icon_size = 30;
private readonly OsuScreen nextScreen;
private readonly Bindable<APIUser> currentUser = new Bindable<APIUser>();
private FillFlowContainer fill;
private readonly List<ITextPart> expendableText = new List<ITextPart>();
public Disclaimer(OsuScreen nextScreen = null)
{
this.nextScreen = nextScreen;
ValidForResume = false;
}
[Resolved]
private IAPIProvider api { get; set; }
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
InternalChildren = new Drawable[]
{
icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.Solid.ExclamationTriangle,
Size = new Vector2(icon_size),
Y = icon_y,
},
fill = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Y = icon_y,
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
Children = new Drawable[]
{
textFlow = new LinkFlowContainer
{
Width = 680,
AutoSizeAxes = Axes.Y,
TextAnchor = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Spacing = new Vector2(0, 2),
},
}
},
};
textFlow.AddText("Disclaimer", t => t.Font = t.Font.With(Typeface.Torus, 30, FontWeight.Regular));
static void formatRegular(SpriteText t) => t.Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular);
static void formatSemiBold(SpriteText t) => t.Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold);
textFlow.NewParagraph();
textFlow.AddText("This is ", formatRegular);
textFlow.AddText("jvnkosu!", formatSemiBold);
textFlow.AddText(", an unofficial osu!(lazer) server based on official source code.", formatRegular);
textFlow.NewParagraph();
textFlow.AddText("We are not in any way affiliated with, or endorsed by, the osu! team.", formatSemiBold);
textFlow.NewParagraph();
textFlow.NewParagraph();
textFlow.AddText("Thank you, and have fun!", formatRegular);
iconColour = colours.Yellow;
}
protected override void LoadComplete()
{
base.LoadComplete();
if (nextScreen != null)
LoadComponentAsync(nextScreen);
((IBindable<APIUser>)currentUser).BindTo(api.LocalUser);
}
public override void OnSuspending(ScreenTransitionEvent e)
{
base.OnSuspending(e);
// Once this screen has finished being displayed, we don't want to unnecessarily handle user change events.
currentUser.UnbindAll();
}
public override void OnEntering(ScreenTransitionEvent e)
{
base.OnEntering(e);
// icon.RotateTo(10);
icon.FadeOut();
icon.ScaleTo(0.5f);
icon.Delay(500).FadeIn(50).ScaleTo(1, 500, Easing.OutQuint);
fill.MoveToOffset(new Vector2(0, 15), 0, Easing.OutQuart);
using (BeginDelayedSequence(3000))
{
icon.MoveToY(icon_y, 0, Easing.InQuart)
.FadeColour(Color4.White, 160)
.Then()
.FadeColour(iconColour, 200, Easing.OutQuint);
Schedule(() => expendableText.SelectMany(t => t.Drawables).ForEach(t =>
{
t.FadeOut(100);
t.ScaleTo(new Vector2(0, 1), 100, Easing.OutQuart);
}));
}
double delay = 500;
foreach (var c in textFlow.Children)
c.FadeTo(0.001f).Delay(delay += 20).FadeIn(500);
this
.FadeInFromZero(500)
.Then(5500)
.FadeOut(250)
.ScaleTo(0.9f, 250, Easing.InQuint)
.Finally(_ =>
{
if (nextScreen != null)
this.Push(nextScreen);
});
}
}
}

View File

@@ -20,7 +20,7 @@ namespace osu.Game.Screens.Play.Break
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.Numeric.With(size: 33),
Font = OsuFont.TorusAlternate.With(size: 64, weight: FontWeight.SemiBold),
};
}

View File

@@ -18,11 +18,13 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Input.Bindings;
using osuTK;
using osuTK.Graphics;
using osu.Game.Localisation;
using osu.Game.Utils;
using System.Runtime.InteropServices;
namespace osu.Game.Screens.Play
{
@@ -40,6 +42,7 @@ namespace osu.Game.Screens.Play
public Action? OnResume { get; init; }
public Action? OnRetry { get; init; }
public Action? OnQuit { get; init; }
public Action? OnQuitReplay { get; init; }
/// <summary>
/// Action that is invoked when <see cref="GlobalAction.Back"/> is triggered.
@@ -69,6 +72,8 @@ namespace osu.Game.Screens.Play
[Resolved]
private GlobalActionContainer globalAction { get; set; } = null!;
private ShearedButton saveReplay { get; set; } = null!;
protected GameplayMenuOverlay()
{
RelativeSizeAxes = Axes.Both;
@@ -90,7 +95,7 @@ namespace osu.Game.Screens.Play
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 50),
Spacing = new Vector2(0, 25),
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new Drawable[]
@@ -118,6 +123,15 @@ namespace osu.Game.Screens.Play
Radius = 50
},
},
saveReplay = new ShearedButton
{
Text = "Quit and save replay",
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Height = 32,
Colour = colours.PurpleLight,
// Visibility = false
},
playInfoText = new OsuTextFlowContainer(cp => cp.Font = OsuFont.GetFont(size: 18))
{
Origin = Anchor.TopCentre,
@@ -138,6 +152,12 @@ namespace osu.Game.Screens.Play
if (OnQuit != null)
AddButton(GameplayMenuOverlayStrings.Quit, new Color4(170, 27, 39, 255), () => OnQuit.Invoke());
if (OnQuitReplay != null)
{
// saveReplay.Visibility = true;
saveReplay.Action = () => OnQuitReplay.Invoke();
}
State.ValueChanged += _ => InternalButtons.Deselect();
updateInfoText();

View File

@@ -0,0 +1,97 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Configuration;
using osu.Game.Localisation.SkinComponents;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Rulesets.Scoring;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Play.HUD
{
public partial class ArgonLongestComboCounter : ComboCounter
{
protected ArgonCounterTextComponent Text = null!;
protected override double RollingDuration => 250;
protected virtual bool DisplayXSymbol => true;
[SettingSource("Wireframe opacity", "Controls the opacity of the wireframes behind the digits.")]
public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.25f)
{
Precision = 0.01f,
MinValue = 0,
MaxValue = 1,
};
[SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.ShowLabel))]
public Bindable<bool> ShowLabel { get; } = new BindableBool(true);
[SettingSource("Show animation on increase", "Shows a bouncing animation when the combo increases")]
public Bindable<bool> ShowRolling { get; } = new BindableBool(true);
[BackgroundDependencyLoader]
private void load(ScoreProcessor scoreProcessor)
{
Current.BindTo(scoreProcessor.HighestCombo);
Current.BindValueChanged(combo =>
{
bool wasIncrease = combo.NewValue > combo.OldValue;
bool wasMiss = combo.OldValue > 1 && combo.NewValue == 0;
float newScale = Math.Clamp(Text.NumberContainer.Scale.X * (wasIncrease ? 1.1f : 0.8f), 0.6f, 1.4f);
float duration = ShowRolling.Value ? 500 : 0;
Text.NumberContainer
.ScaleTo(new Vector2(newScale))
.ScaleTo(Vector2.One, duration, Easing.OutQuint);
});
}
public override int DisplayedCount
{
get => base.DisplayedCount;
set
{
base.DisplayedCount = value;
updateWireframe();
}
}
private void updateWireframe()
{
int digitsRequiredForDisplayCount = getDigitsRequiredForDisplayCount();
if (digitsRequiredForDisplayCount != Text.WireframeTemplate.Length)
Text.WireframeTemplate = new string('#', digitsRequiredForDisplayCount);
}
private int getDigitsRequiredForDisplayCount()
{
// one for the single presumed starting digit, one for the "x" at the end (unless disabled).
int digitsRequired = DisplayXSymbol ? 2 : 1;
long c = DisplayedCount;
while ((c /= 10) > 0)
digitsRequired++;
return digitsRequired;
}
protected override LocalisableString FormatCount(int count) => DisplayXSymbol ? $@"{count}x" : count.ToString();
protected override IHasText CreateText() => Text = new ArgonCounterTextComponent(Anchor.TopLeft, "MAX COMBO")
{
WireframeOpacity = { BindTarget = WireframeOpacity },
ShowLabel = { BindTarget = ShowLabel },
};
}
}

View File

@@ -0,0 +1,103 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Localisation.SkinComponents;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Screens.Play.HUD.ClicksPerSecond
{
public partial class ArgonClicksPerSecondCounter : RollingCounter<int>, ISerialisableDrawable
{
[Resolved]
private ClicksPerSecondController controller { get; set; } = null!;
protected override double RollingDuration => 175;
[SettingSource("Wireframe opacity", "Controls the opacity of the wireframes behind the digits.")]
public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.25f)
{
Precision = 0.01f,
MinValue = 0,
MaxValue = 1,
};
[SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.ShowLabel))]
public Bindable<bool> ShowLabel { get; } = new BindableBool(true);
public bool UsesFixedAnchor { get; set; }
public ArgonClicksPerSecondCounter()
{
Current.Value = 0;
}
protected override void Update()
{
base.Update();
Current.Value = controller.Value;
}
protected override IHasText CreateText() => new TextComponent()
{
WireframeOpacity = { BindTarget = WireframeOpacity },
ShowLabel = { BindTarget = ShowLabel },
};
private partial class TextComponent : CompositeDrawable, IHasText
{
private readonly ArgonCounterTextComponent cpsValue;
public IBindable<float> WireframeOpacity { get; } = new BindableFloat();
public Bindable<bool> ShowLabel { get; } = new BindableBool();
public LocalisableString Text
{
get => cpsValue.Text;
set => cpsValue.Text = value;
}
public TextComponent()
{
AutoSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new Container
{
AutoSizeAxes = Axes.Both,
Child = cpsValue = new ArgonCounterTextComponent(Anchor.TopLeft, "KEYS/SEC") // welp, not good
{
WireframeOpacity = { BindTarget = WireframeOpacity },
WireframeTemplate = @"##",
ShowLabel = { BindTarget = ShowLabel },
}
},
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
}
}
}
}

View File

@@ -0,0 +1,38 @@
// 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.Input.Bindings;
using osu.Framework.Input.Events;
namespace osu.Game.Screens.Play.HUD
{
public partial class KeyCounterBindingTrigger<T> : InputTrigger, IKeyBindingHandler<T>
where T : struct
{
public T Action { get; }
public KeyCounterBindingTrigger(IKeyBinding key, T action)
: base(key?.KeyCombination.Keys[0].ToString() ?? $"B{(int)(object)action + 1}")
{
Action = action;
}
public bool OnPressed(KeyBindingPressEvent<T> e)
{
if (!EqualityComparer<T>.Default.Equals(e.Action, Action))
return false;
Activate(Clock.Rate >= 0);
return false;
}
public void OnReleased(KeyBindingReleaseEvent<T> e)
{
if (!EqualityComparer<T>.Default.Equals(e.Action, Action))
return;
Deactivate(Clock.Rate >= 0);
}
}
}

View File

@@ -0,0 +1,56 @@
// 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 System.Threading;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Localisation.SkinComponents;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Skinning;
namespace osu.Game.Screens.Play.HUD
{
public partial class SkinnableBeatmapSetOnlineStatusPill : CompositeDrawable, ISerialisableDrawable
{
private BeatmapSetOnlineStatusPill beatmapSetOnlineStatusPill { get; set; } = null!;
[Resolved]
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
public SkinnableBeatmapSetOnlineStatusPill() // WARNING: this is awful
{
AutoSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
beatmapSetOnlineStatusPill = new BeatmapSetOnlineStatusPill()
{
ShowUnknownStatus = true
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
beatmap.BindValueChanged(b =>
{
beatmapSetOnlineStatusPill.Status = beatmap.Value.BeatmapSetInfo.Status;
}, true);
}
public bool UsesFixedAnchor { get; set; }
}
}

View File

@@ -0,0 +1,64 @@
// 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 System.Threading;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Localisation.SkinComponents;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Skinning;
namespace osu.Game.Screens.Play.HUD
{
public partial class SkinnableStarRatingDisplay : CompositeDrawable, ISerialisableDrawable
{
private StarRatingDisplay starRatingDisplay { get; set; } = null!;
[Resolved]
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
[Resolved]
private BeatmapDifficultyCache difficultyCache { get; set; } = null!;
private CancellationTokenSource? difficultyCancellationSource;
private IBindable<StarDifficulty>? difficultyBindable;
public SkinnableStarRatingDisplay() // WARNING: this is awful
{
AutoSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
starRatingDisplay = new StarRatingDisplay(new StarDifficulty(0.00, 0), animated: true)
};
}
protected override void LoadComplete()
{
base.LoadComplete();
beatmap.BindValueChanged(b =>
{
difficultyCancellationSource?.Cancel();
difficultyCancellationSource = new CancellationTokenSource();
difficultyBindable?.UnbindAll();
difficultyBindable = difficultyCache.GetBindableDifficulty(b.NewValue.BeatmapInfo, difficultyCancellationSource.Token);
}, true);
starRatingDisplay.Current.BindTo((Bindable<StarDifficulty>)difficultyBindable!);
}
public bool UsesFixedAnchor { get; set; }
}
}

View File

@@ -200,6 +200,8 @@ namespace osu.Game.Screens.Play
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
private OsuConfigManager config;
protected override void LoadComplete()
{
base.LoadComplete();
@@ -226,12 +228,13 @@ namespace osu.Game.Screens.Play
[BackgroundDependencyLoader(true)]
private void load(OsuConfigManager config, OsuGameBase game, CancellationToken cancellationToken)
{
this.config = config;
var gameplayMods = Mods.Value.Select(m => m.DeepClone()).ToArray();
if (gameplayMods.Any(m => m is UnknownMod))
{
Logger.Log("Gameplay was started with an unknown mod applied.", level: LogLevel.Important);
return;
// return;
}
if (Beatmap.Value is DummyWorkingBeatmap)
@@ -516,6 +519,7 @@ namespace osu.Game.Screens.Play
Retries = RestartCount,
OnRetry = () => Restart(),
OnQuit = () => PerformExitWithConfirmation(),
OnQuitReplay = () => PerformExitReplay()
},
},
};
@@ -701,6 +705,20 @@ namespace osu.Game.Screens.Play
return true;
}
// XXX: replays saved from pause screen, when played back, will continue playing past the point player quits
// unfixable because it's not possible to manually trigger a failure in a way that would be recorded (w/o using a mod)
protected void PerformExitReplay()
{
// manually triggering a failure in a messy manner to avoid score submission
GameplayClockContainer.Stop();
GameplayState.HasFailed = true;
updateGameplayState();
ConcludeFailedScore(Score);
prepareAndImportScoreAsync(true);
PerformExit();
}
private void performUserRequestedSkip()
{
// user requested skip
@@ -968,6 +986,15 @@ namespace osu.Game.Screens.Play
if (PauseOverlay.State.Value == Visibility.Visible)
PauseOverlay.Hide();
bool exitOnFail = GameplayState.Mods.OfType<IApplicableFailExit>().Any(m => m.ExitOnFail)
&& Score.ScoreInfo.User.Username == config.Get<string>(OsuSetting.Username); // TODO: do more concrete checks
if (exitOnFail)
{
// game.AttemptExit();
game.Exit();
}
bool restartOnFail = GameplayState.Mods.OfType<IApplicableFailOverride>().Any(m => m.RestartOnFail);
if (!restartOnFail)
failAnimationContainer.Start();

View File

@@ -215,6 +215,9 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
this.ScaleTo(0).Then().ScaleTo(1, APPEAR_DURATION, Easing.OutQuint);
if (!withFlair)
accuracyCircle.Colour = OsuColour.ForRank(score.Rank);
if (withFlair)
{
const double swoosh_pre_delay = 443f;
@@ -307,6 +310,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
{
var dink = badgeNum < badges.Count - 1 ? badgeTickSound : badgeMaxSound;
accuracyCircle.FadeColour(OsuColour.ForRank(badge.Rank), 100, Easing.InOutSine); // TODO: nicer animations
dink!.FrequencyTo(1 + badgeNum++ * 0.05);
dink!.Play();
});

View File

@@ -49,7 +49,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
Spacing = new Vector2(-15, 0),
Text = DrawableRank.GetRankLetter(rank),
// Font = OsuFont.Numeric.With(size: 76),
Font = OsuFont.TorusAlternate.With(size: 128, weight: FontWeight.Bold),
Font = OsuFont.TorusAlternate.With(size: 100, weight: FontWeight.Bold),
UseFullGlyphHeight = false
},
superFlash = new BufferedContainer(cachedFrameBuffer: true)
@@ -89,7 +89,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
Origin = Anchor.Centre,
Spacing = new Vector2(-15, 0),
Text = DrawableRank.GetRankLetter(rank),
Font = OsuFont.Numeric.With(size: 76),
Font = OsuFont.TorusAlternate.With(size: 100, weight: FontWeight.Bold),
UseFullGlyphHeight = false,
Shadow = false
},

View File

@@ -4,6 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -43,45 +44,63 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new[]
Children = new Drawable[]
{
new CircularContainer
new Container
{
RelativeSizeAxes = Axes.X,
Height = 12,
AutoSizeAxes = Axes.Y,
Masking = true,
CornerRadius = 8f,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#222")
Colour = Color4Extensions.FromHex("#2222229f"),
},
HeaderText = new OsuSpriteText
new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold),
Text = header.ToUpper(),
Anchor = Anchor.TopLeft,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
HeaderText = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold),
Text = header.ToUpper(),
},
content = CreateContent().With(d =>
{
d.Origin = Anchor.TopCentre;
d.Anchor = Anchor.TopCentre;
d.Alpha = 0;
d.AlwaysPresent = true;
}),
}
}
}
},
new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both,
Children = new[]
{
content = CreateContent().With(d =>
{
d.Anchor = Anchor.TopCentre;
d.Origin = Anchor.TopCentre;
d.Alpha = 0;
d.AlwaysPresent = true;
}),
}
}
// new Container
// {
// Anchor = Anchor.TopCentre,
// Origin = Anchor.TopCentre,
// AutoSizeAxes = Axes.Both,
// Children = new[]
// {
// content = CreateContent().With(d =>
// {
// d.Anchor = Anchor.TopCentre;
// d.Origin = Anchor.TopCentre;
// d.Alpha = 0;
// d.AlwaysPresent = true;
// }),
// }
// }
}
};
}

View File

@@ -7,6 +7,7 @@ using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.ImageExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Audio;
using osu.Framework.Graphics.Colour;
@@ -14,12 +15,14 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Contracted;
using osu.Game.Screens.Ranking.Expanded;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
using ZstdSharp.Unsafe;
namespace osu.Game.Screens.Ranking
{
@@ -96,6 +99,7 @@ namespace osu.Game.Screens.Ranking
[Resolved]
private OsuGameBase game { get; set; } = null!;
private OsuColour colour { get; set; } = null!;
private AudioContainer audioContent = null!;
@@ -124,12 +128,14 @@ namespace osu.Game.Screens.Ranking
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
private void load(AudioManager audio, OsuColour colour)
{
// ScorePanel doesn't include the top extruding area in its own size.
// Adding a manual offset here allows the expanded version to take on an "acceptable" vertical centre when at 100% UI scale.
const float vertical_fudge = 20;
this.colour = colour;
InternalChild = audioContent = new AudioContainer
{
Anchor = Anchor.Centre,
@@ -239,6 +245,15 @@ namespace osu.Game.Screens.Ranking
private void updateState()
{
ColourInfo getColour(ColourInfo info)
{
var ci = info.AverageColour;
var rank = (ColourInfo)OsuColour.ForRank(Score.Rank);
(float _, float _, float v) = Color4Extensions.ToHSV(ci);
(float rh, float rs, _) = Color4Extensions.ToHSV(rank);
return Color4Extensions.FromHSV(rh, rs * 0.3f, v * 1.1f);
}
topLayerContent?.FadeOut(content_fade_duration).Expire();
middleLayerContent?.FadeOut(content_fade_duration).Expire();
@@ -247,8 +262,8 @@ namespace osu.Game.Screens.Ranking
case PanelState.Expanded:
Size = new Vector2(EXPANDED_WIDTH, expanded_height);
topLayerBackground.FadeColour(expanded_top_layer_colour, RESIZE_DURATION, Easing.OutQuint);
middleLayerBackground.FadeColour(expanded_middle_layer_colour, RESIZE_DURATION, Easing.OutQuint);
topLayerBackground.FadeColour(getColour(expanded_top_layer_colour), RESIZE_DURATION, Easing.OutQuint);
middleLayerBackground.FadeColour(getColour(expanded_middle_layer_colour), RESIZE_DURATION, Easing.OutQuint);
bool firstLoad = topLayerContent == null;
topLayerContentContainer.Add(topLayerContent = new ExpandedPanelTopContent(Score.User, firstLoad) { Alpha = 0 });
@@ -261,8 +276,8 @@ namespace osu.Game.Screens.Ranking
case PanelState.Contracted:
Size = new Vector2(CONTRACTED_WIDTH, contracted_height);
topLayerBackground.FadeColour(contracted_top_layer_colour, RESIZE_DURATION, Easing.OutQuint);
middleLayerBackground.FadeColour(contracted_middle_layer_colour, RESIZE_DURATION, Easing.OutQuint);
topLayerBackground.FadeColour(getColour(contracted_top_layer_colour), RESIZE_DURATION, Easing.OutQuint);
middleLayerBackground.FadeColour(getColour(contracted_middle_layer_colour), RESIZE_DURATION, Easing.OutQuint);
topLayerContentContainer.Add(topLayerContent = new ContractedPanelTopContent
{

View File

@@ -163,6 +163,7 @@ namespace osu.Game.Screens.Select
private FillFlowContainer infoLabelContainer;
private Container bpmLabelContainer;
private Container lengthLabelContainer;
private Container performanceLabelContainer;
private readonly WorkingBeatmap working;
private readonly RulesetInfo ruleset;
@@ -269,6 +270,7 @@ namespace osu.Game.Screens.Select
TextSize = 11,
TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 },
Status = beatmapInfo.Status,
ShowUnknownStatus = true,
Alpha = string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? 0 : 1
}
}
@@ -343,9 +345,11 @@ namespace osu.Game.Screens.Select
settingChangeTracker?.Dispose();
refreshBPMAndLengthLabel();
refreshPerformanceLabel();
settingChangeTracker = new ModSettingChangeTracker(m.NewValue);
settingChangeTracker.SettingChanged += _ => refreshBPMAndLengthLabel();
settingChangeTracker.SettingChanged += _ => refreshPerformanceLabel();
}, true);
}
@@ -384,7 +388,11 @@ namespace osu.Game.Screens.Select
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(20, 0),
Children = playableBeatmap.GetStatistics().Select(s => new InfoLabel(s)).ToArray()
}
},
performanceLabelContainer = new Container
{
AutoSizeAxes = Axes.Both
},
};
}
catch (Exception e)
@@ -428,6 +436,27 @@ namespace osu.Game.Screens.Select
});
}
private void refreshPerformanceLabel()
{
var beatmap = working.Beatmap;
if (beatmap == null || performanceLabelContainer == null)
return;
var diff = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo);
diff.BindValueChanged(d =>
{
float perf = (float?)d.NewValue.PerformanceAttributes?.Total ?? 0.0f;
string disp = $"{Math.Round(perf, 1)} pp";
performanceLabelContainer.Child = new InfoLabel(new BeatmapStatistic
{
Name = "Max PP",
CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Accuracy),
Content = disp
});
}, true);
}
private Drawable getMapper(BeatmapMetadata metadata)
{
if (string.IsNullOrEmpty(metadata.Author.Username))

View File

@@ -81,6 +81,7 @@ namespace osu.Game.Screens.Select.Carousel
Anchor = Anchor.CentreLeft,
TextSize = 11,
TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 },
ShowUnknownStatus = true,
Status = beatmapSet.Status
},
iconFlow = new FillFlowContainer<DifficultyIcon>

View File

@@ -393,7 +393,7 @@ namespace osu.Game.Screens.SelectV2
Origin = Anchor.Centre,
Spacing = new Vector2(-2),
Colour = DrawableRank.GetRankLetterColour(Score.Rank),
Font = OsuFont.Numeric.With(size: 14),
Font = OsuFont.TorusAlternate.With(size: 24, weight: FontWeight.Bold),
Text = DrawableRank.GetRankLetter(Score.Rank),
ShadowColour = Color4.Black.Opacity(0.3f),
ShadowOffset = new Vector2(0, 0.08f),

View File

@@ -12,6 +12,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Configuration;
@@ -233,6 +234,7 @@ namespace osu.Game.Screens.SelectV2
private void updateDisplay()
{
countStatisticsDisplay.ForceTiny = true;
cancellationSource?.Cancel();
cancellationSource = new CancellationTokenSource();
@@ -293,8 +295,22 @@ namespace osu.Game.Screens.SelectV2
Ruleset rulesetInstance = ruleset.Value.CreateInstance();
var workingBeatmap = beatmap.Value;
var diff = difficultyCache.GetBindableDifficulty(workingBeatmap.BeatmapInfo);
float perf = 0.0f;
var displayAttributes = rulesetInstance.GetBeatmapAttributesForDisplay(beatmap.Value.BeatmapInfo, mods.Value).ToList();
difficultyStatisticsDisplay.Statistics = displayAttributes.Select(a => new StatisticDifficulty.Data(a)).ToList();
difficultyStatisticsDisplay.Statistics = displayAttributes.Select(a => new StatisticDifficulty.Data(a))
.Prepend(new StatisticDifficulty.Data("Max PP", perf, perf, perf));
// at first, performance points won't be available, so we'd have to update them later
diff.BindValueChanged(d =>
{
perf = (float)Math.Round((float?)d.NewValue.PerformanceAttributes?.Total ?? 0f, 1); // yikes
var arr = difficultyStatisticsDisplay.Statistics.ToArray();
arr[0] = new StatisticDifficulty.Data("Max PP", perf, perf, perf);
difficultyStatisticsDisplay.Statistics = arr.AsEnumerable();
});
});
protected override void Update()

View File

@@ -27,9 +27,11 @@ namespace osu.Game.Screens.SelectV2
private readonly FillFlowContainer<StatisticDifficulty> statisticsFlow;
private readonly GridContainer tinyStatisticsGrid;
private IReadOnlyList<StatisticDifficulty.Data> statistics = Array.Empty<StatisticDifficulty.Data>();
public bool ForceTiny { get; set; } = false;
public IReadOnlyList<StatisticDifficulty.Data> Statistics
private IEnumerable<StatisticDifficulty.Data> statistics = Array.Empty<StatisticDifficulty.Data>();
public IEnumerable<StatisticDifficulty.Data> Statistics
{
get => statistics;
set
@@ -137,7 +139,7 @@ namespace osu.Game.Screens.SelectV2
return;
float flowWidth = statisticsFlow[0].Width * statisticsFlow.Count + statisticsFlow.Spacing.X * (statisticsFlow.Count - 1);
bool tiny = !autoSize && DrawWidth < flowWidth - 20;
bool tiny = !autoSize && DrawWidth < flowWidth - 20 || ForceTiny;
if (displayedTinyStatistics != tiny)
{
@@ -180,8 +182,8 @@ namespace osu.Game.Screens.SelectV2
if (statisticsFlow.Select(s => s.Value.Label)
.SequenceEqual(statistics.Select(s => s.Label)))
{
for (int i = 0; i < statistics.Count; i++)
statisticsFlow[i].Value = statistics[i];
for (int i = 0; i < statistics.Count(); i++)
statisticsFlow[i].Value = statistics.ToArray()[i];
}
else
{

View File

@@ -143,6 +143,7 @@ namespace osu.Game.Screens.SelectV2
TextSize = OsuFont.Style.Caption2.Size,
Margin = new MarginPadding { Right = 5f },
Animated = false,
ShowUnknownStatus = true
},
updateButton = new PanelUpdateBeatmapButton
{

View File

@@ -149,6 +149,7 @@ namespace osu.Game.Screens.SelectV2
Anchor = Anchor.BottomLeft,
TextSize = OsuFont.Style.Caption2.Size,
Margin = new MarginPadding { Right = 4f },
ShowUnknownStatus = true
},
updateButton = new PanelUpdateBeatmapButton
{

View File

@@ -42,7 +42,7 @@ namespace osu.Game.Updater
{
bool includePrerelease = stream == Configuration.ReleaseStream.Tachyon;
OsuJsonWebRequest<GitHubRelease[]> releasesRequest = new OsuJsonWebRequest<GitHubRelease[]>("https://api.github.com/repos/ppy/osu/releases?per_page=10&page=1");
OsuJsonWebRequest<GitHubRelease[]> releasesRequest = new OsuJsonWebRequest<GitHubRelease[]>("https://gitea.jvnko.boats/api/v1/repos/jvnkosu/client/releases?limit=10&page=1");
await releasesRequest.PerformAsync(cancellationToken).ConfigureAwait(false);
GitHubRelease[] releases = releasesRequest.ResponseObject;

View File

@@ -770,6 +770,9 @@ namespace osu.Game.Users
[Description("Samoa")]
WS,
[Description("Gensokyo")]
XG,
[Description("Kosovo")]
XK,