mirror of
https://github.com/ppy/osu.git
synced 2025-02-11 23:13:06 +08:00
Merge branch 'master' into hover-open-mod-customise
This commit is contained in:
commit
e6d5389608
@ -6,6 +6,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
@ -28,11 +29,15 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
|
||||
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
||||
{
|
||||
if (lookup is SkinComponentsContainerLookup containerLookup)
|
||||
switch (lookup)
|
||||
{
|
||||
switch (containerLookup.Target)
|
||||
{
|
||||
case SkinComponentsContainerLookup.TargetArea.MainHUDComponents:
|
||||
case SkinComponentsContainerLookup containerLookup:
|
||||
if (containerLookup.Target != SkinComponentsContainerLookup.TargetArea.MainHUDComponents)
|
||||
return base.GetDrawableComponent(lookup);
|
||||
|
||||
// Modifications for global components.
|
||||
if (containerLookup.Ruleset == null)
|
||||
{
|
||||
var components = base.GetDrawableComponent(lookup) as Container;
|
||||
|
||||
if (providesComboCounter && components != null)
|
||||
@ -44,60 +49,84 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lookup is CatchSkinComponentLookup catchSkinComponent)
|
||||
{
|
||||
switch (catchSkinComponent.Component)
|
||||
{
|
||||
case CatchSkinComponents.Fruit:
|
||||
if (hasPear)
|
||||
return new LegacyFruitPiece();
|
||||
// Skin has configuration.
|
||||
if (base.GetDrawableComponent(lookup) is Drawable d)
|
||||
return d;
|
||||
|
||||
return null;
|
||||
// Our own ruleset components default.
|
||||
return new DefaultSkinComponentsContainer(container =>
|
||||
{
|
||||
var keyCounter = container.OfType<LegacyKeyCounterDisplay>().FirstOrDefault();
|
||||
|
||||
case CatchSkinComponents.Banana:
|
||||
if (GetTexture("fruit-bananas") != null)
|
||||
return new LegacyBananaPiece();
|
||||
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.Droplet:
|
||||
if (GetTexture("fruit-drop") != null)
|
||||
return new LegacyDropletPiece();
|
||||
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.Catcher:
|
||||
decimal version = GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value ?? 1;
|
||||
|
||||
if (version < 2.3m)
|
||||
if (keyCounter != null)
|
||||
{
|
||||
if (hasOldStyleCatcherSprite())
|
||||
return new LegacyCatcherOld();
|
||||
// set the anchor to top right so that it won't squash to the return button to the top
|
||||
keyCounter.Anchor = Anchor.CentreRight;
|
||||
keyCounter.Origin = Anchor.CentreRight;
|
||||
keyCounter.X = 0;
|
||||
// 340px is the default height inherit from stable
|
||||
keyCounter.Y = container.ToLocalSpace(new Vector2(0, container.ScreenSpaceDrawQuad.Centre.Y - 340f)).Y;
|
||||
}
|
||||
})
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new LegacyKeyCounterDisplay(),
|
||||
}
|
||||
};
|
||||
|
||||
if (hasNewStyleCatcherSprite())
|
||||
return new LegacyCatcherNew();
|
||||
case CatchSkinComponentLookup catchSkinComponent:
|
||||
switch (catchSkinComponent.Component)
|
||||
{
|
||||
case CatchSkinComponents.Fruit:
|
||||
if (hasPear)
|
||||
return new LegacyFruitPiece();
|
||||
|
||||
return null;
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.CatchComboCounter:
|
||||
if (providesComboCounter)
|
||||
return new LegacyCatchComboCounter();
|
||||
case CatchSkinComponents.Banana:
|
||||
if (GetTexture("fruit-bananas") != null)
|
||||
return new LegacyBananaPiece();
|
||||
|
||||
return null;
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.HitExplosion:
|
||||
if (hasOldStyleCatcherSprite() || hasNewStyleCatcherSprite())
|
||||
return new LegacyHitExplosion();
|
||||
case CatchSkinComponents.Droplet:
|
||||
if (GetTexture("fruit-drop") != null)
|
||||
return new LegacyDropletPiece();
|
||||
|
||||
return null;
|
||||
return null;
|
||||
|
||||
default:
|
||||
throw new UnsupportedSkinComponentException(lookup);
|
||||
}
|
||||
case CatchSkinComponents.Catcher:
|
||||
decimal version = GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value ?? 1;
|
||||
|
||||
if (version < 2.3m)
|
||||
{
|
||||
if (hasOldStyleCatcherSprite())
|
||||
return new LegacyCatcherOld();
|
||||
}
|
||||
|
||||
if (hasNewStyleCatcherSprite())
|
||||
return new LegacyCatcherNew();
|
||||
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.CatchComboCounter:
|
||||
if (providesComboCounter)
|
||||
return new LegacyCatchComboCounter();
|
||||
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.HitExplosion:
|
||||
if (hasOldStyleCatcherSprite() || hasNewStyleCatcherSprite())
|
||||
return new LegacyHitExplosion();
|
||||
|
||||
return null;
|
||||
|
||||
default:
|
||||
throw new UnsupportedSkinComponentException(lookup);
|
||||
}
|
||||
}
|
||||
|
||||
return base.GetDrawableComponent(lookup);
|
||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
});
|
||||
|
||||
moveMouseToHitObject(1);
|
||||
AddAssert("merge option available", () => selectionHandler.ContextMenuItems?.Any(o => o.Text.Value == "Merge selection") == true);
|
||||
AddAssert("merge option available", () => selectionHandler.ContextMenuItems.Any(o => o.Text.Value == "Merge selection"));
|
||||
|
||||
mergeSelection();
|
||||
|
||||
@ -198,7 +198,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
});
|
||||
|
||||
moveMouseToHitObject(1);
|
||||
AddAssert("merge option not available", () => selectionHandler.ContextMenuItems?.Length > 0 && selectionHandler.ContextMenuItems.All(o => o.Text.Value != "Merge selection"));
|
||||
AddAssert("merge option not available", () => selectionHandler.ContextMenuItems.Length > 0 && selectionHandler.ContextMenuItems.All(o => o.Text.Value != "Merge selection"));
|
||||
mergeSelection();
|
||||
AddAssert("circles not merged", () => circle1 is not null && circle2 is not null
|
||||
&& EditorBeatmap.HitObjects.Contains(circle1) && EditorBeatmap.HitObjects.Contains(circle2));
|
||||
@ -222,7 +222,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
});
|
||||
|
||||
moveMouseToHitObject(1);
|
||||
AddAssert("merge option available", () => selectionHandler.ContextMenuItems?.Any(o => o.Text.Value == "Merge selection") == true);
|
||||
AddAssert("merge option available", () => selectionHandler.ContextMenuItems.Any(o => o.Text.Value == "Merge selection"));
|
||||
|
||||
mergeSelection();
|
||||
|
||||
|
@ -61,12 +61,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
flashlightRating *= 0.7;
|
||||
}
|
||||
|
||||
double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000;
|
||||
double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000;
|
||||
double baseAimPerformance = OsuStrainSkill.DifficultyToPerformance(aimRating);
|
||||
double baseSpeedPerformance = OsuStrainSkill.DifficultyToPerformance(speedRating);
|
||||
double baseFlashlightPerformance = 0.0;
|
||||
|
||||
if (mods.Any(h => h is OsuModFlashlight))
|
||||
baseFlashlightPerformance = Math.Pow(flashlightRating, 2.0) * 25.0;
|
||||
baseFlashlightPerformance = Flashlight.DifficultyToPerformance(flashlightRating);
|
||||
|
||||
double basePerformance =
|
||||
Math.Pow(
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Osu.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
@ -86,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
private double computeAimValue(ScoreInfo score, OsuDifficultyAttributes attributes)
|
||||
{
|
||||
double aimValue = Math.Pow(5.0 * Math.Max(1.0, attributes.AimDifficulty / 0.0675) - 4.0, 3.0) / 100000.0;
|
||||
double aimValue = OsuStrainSkill.DifficultyToPerformance(attributes.AimDifficulty);
|
||||
|
||||
double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
|
||||
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
|
||||
@ -139,7 +140,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
if (score.Mods.Any(h => h is OsuModRelax))
|
||||
return 0.0;
|
||||
|
||||
double speedValue = Math.Pow(5.0 * Math.Max(1.0, attributes.SpeedDifficulty / 0.0675) - 4.0, 3.0) / 100000.0;
|
||||
double speedValue = OsuStrainSkill.DifficultyToPerformance(attributes.SpeedDifficulty);
|
||||
|
||||
double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
|
||||
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
|
||||
@ -226,7 +227,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
if (!score.Mods.Any(h => h is OsuModFlashlight))
|
||||
return 0.0;
|
||||
|
||||
double flashlightValue = Math.Pow(attributes.FlashlightDifficulty, 2.0) * 25.0;
|
||||
double flashlightValue = Flashlight.DifficultyToPerformance(attributes.FlashlightDifficulty);
|
||||
|
||||
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
||||
if (effectiveMissCount > 0)
|
||||
|
@ -42,5 +42,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||
}
|
||||
|
||||
public override double DifficultyValue() => GetCurrentStrainPeaks().Sum() * OsuStrainSkill.DEFAULT_DIFFICULTY_MULTIPLIER;
|
||||
|
||||
public static double DifficultyToPerformance(double difficulty) => 25 * Math.Pow(difficulty, 2);
|
||||
}
|
||||
}
|
||||
|
@ -67,5 +67,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||
|
||||
return difficulty * DifficultyMultiplier;
|
||||
}
|
||||
|
||||
public static double DifficultyToPerformance(double difficulty) => Math.Pow(5.0 * Math.Max(1.0, difficulty / 0.0675) - 4.0, 3.0) / 100000.0;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@ -41,139 +42,178 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
||||
{
|
||||
if (lookup is OsuSkinComponentLookup osuComponent)
|
||||
switch (lookup)
|
||||
{
|
||||
switch (osuComponent.Component)
|
||||
{
|
||||
case OsuSkinComponents.FollowPoint:
|
||||
return this.GetAnimation("followpoint", true, true, true, startAtCurrentTime: false, maxSize: new Vector2(OsuHitObject.OBJECT_RADIUS * 2, OsuHitObject.OBJECT_RADIUS));
|
||||
case SkinComponentsContainerLookup containerLookup:
|
||||
// Only handle per ruleset defaults here.
|
||||
if (containerLookup.Ruleset == null)
|
||||
return base.GetDrawableComponent(lookup);
|
||||
|
||||
case OsuSkinComponents.SliderScorePoint:
|
||||
return this.GetAnimation("sliderscorepoint", false, false, maxSize: OsuHitObject.OBJECT_DIMENSIONS);
|
||||
// Skin has configuration.
|
||||
if (base.GetDrawableComponent(lookup) is Drawable d)
|
||||
return d;
|
||||
|
||||
case OsuSkinComponents.SliderFollowCircle:
|
||||
var followCircleContent = this.GetAnimation("sliderfollowcircle", true, true, true, maxSize: MAX_FOLLOW_CIRCLE_AREA_SIZE);
|
||||
if (followCircleContent != null)
|
||||
return new LegacyFollowCircle(followCircleContent);
|
||||
// Our own ruleset components default.
|
||||
switch (containerLookup.Target)
|
||||
{
|
||||
case SkinComponentsContainerLookup.TargetArea.MainHUDComponents:
|
||||
return new DefaultSkinComponentsContainer(container =>
|
||||
{
|
||||
var keyCounter = container.OfType<LegacyKeyCounterDisplay>().FirstOrDefault();
|
||||
|
||||
return null;
|
||||
if (keyCounter != null)
|
||||
{
|
||||
// set the anchor to top right so that it won't squash to the return button to the top
|
||||
keyCounter.Anchor = Anchor.CentreRight;
|
||||
keyCounter.Origin = Anchor.CentreRight;
|
||||
keyCounter.X = 0;
|
||||
// 340px is the default height inherit from stable
|
||||
keyCounter.Y = container.ToLocalSpace(new Vector2(0, container.ScreenSpaceDrawQuad.Centre.Y - 340f)).Y;
|
||||
}
|
||||
})
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new LegacyKeyCounterDisplay(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case OsuSkinComponents.SliderBall:
|
||||
if (GetTexture("sliderb") != null || GetTexture("sliderb0") != null)
|
||||
return new LegacySliderBall(this);
|
||||
return null;
|
||||
|
||||
return null;
|
||||
case OsuSkinComponentLookup osuComponent:
|
||||
switch (osuComponent.Component)
|
||||
{
|
||||
case OsuSkinComponents.FollowPoint:
|
||||
return this.GetAnimation("followpoint", true, true, true, startAtCurrentTime: false, maxSize: new Vector2(OsuHitObject.OBJECT_RADIUS * 2, OsuHitObject.OBJECT_RADIUS));
|
||||
|
||||
case OsuSkinComponents.SliderBody:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacySliderBody();
|
||||
case OsuSkinComponents.SliderScorePoint:
|
||||
return this.GetAnimation("sliderscorepoint", false, false, maxSize: OsuHitObject.OBJECT_DIMENSIONS);
|
||||
|
||||
return null;
|
||||
case OsuSkinComponents.SliderFollowCircle:
|
||||
var followCircleContent = this.GetAnimation("sliderfollowcircle", true, true, true, maxSize: MAX_FOLLOW_CIRCLE_AREA_SIZE);
|
||||
if (followCircleContent != null)
|
||||
return new LegacyFollowCircle(followCircleContent);
|
||||
|
||||
case OsuSkinComponents.SliderTailHitCircle:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacyMainCirclePiece("sliderendcircle", false);
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.SliderHeadHitCircle:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacySliderHeadHitCircle();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.ReverseArrow:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacyReverseArrow();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.HitCircle:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacyMainCirclePiece();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.Cursor:
|
||||
if (GetTexture("cursor") != null)
|
||||
return new LegacyCursor(this);
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorTrail:
|
||||
if (GetTexture("cursortrail") != null)
|
||||
return new LegacyCursorTrail(this);
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorRipple:
|
||||
if (GetTexture("cursor-ripple") != null)
|
||||
{
|
||||
var ripple = this.GetAnimation("cursor-ripple", false, false);
|
||||
|
||||
// In stable this element was scaled down to 50% and opacity 20%, but this makes the elements WAY too big and inflexible.
|
||||
// If anyone complains about these not being applied, this can be uncommented.
|
||||
//
|
||||
// But if no one complains I'd rather fix this in lazer. Wiki documentation doesn't mention size,
|
||||
// so we might be okay.
|
||||
//
|
||||
// if (ripple != null)
|
||||
// {
|
||||
// ripple.Scale = new Vector2(0.5f);
|
||||
// ripple.Alpha = 0.2f;
|
||||
// }
|
||||
|
||||
return ripple;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorParticles:
|
||||
if (GetTexture("star2") != null)
|
||||
return new LegacyCursorParticles();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorSmoke:
|
||||
if (GetTexture("cursor-smoke") != null)
|
||||
return new LegacySmokeSegment();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.HitCircleText:
|
||||
if (!this.HasFont(LegacyFont.HitCircle))
|
||||
return null;
|
||||
|
||||
const float hitcircle_text_scale = 0.8f;
|
||||
return new LegacySpriteText(LegacyFont.HitCircle)
|
||||
{
|
||||
// stable applies a blanket 0.8x scale to hitcircle fonts
|
||||
Scale = new Vector2(hitcircle_text_scale),
|
||||
MaxSizePerGlyph = OsuHitObject.OBJECT_DIMENSIONS * 2 / hitcircle_text_scale,
|
||||
};
|
||||
case OsuSkinComponents.SliderBall:
|
||||
if (GetTexture("sliderb") != null || GetTexture("sliderb0") != null)
|
||||
return new LegacySliderBall(this);
|
||||
|
||||
case OsuSkinComponents.SpinnerBody:
|
||||
bool hasBackground = GetTexture("spinner-background") != null;
|
||||
return null;
|
||||
|
||||
if (GetTexture("spinner-top") != null && !hasBackground)
|
||||
return new LegacyNewStyleSpinner();
|
||||
else if (hasBackground)
|
||||
return new LegacyOldStyleSpinner();
|
||||
case OsuSkinComponents.SliderBody:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacySliderBody();
|
||||
|
||||
return null;
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.ApproachCircle:
|
||||
if (GetTexture(@"approachcircle") != null)
|
||||
return new LegacyApproachCircle();
|
||||
case OsuSkinComponents.SliderTailHitCircle:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacyMainCirclePiece("sliderendcircle", false);
|
||||
|
||||
return null;
|
||||
return null;
|
||||
|
||||
default:
|
||||
throw new UnsupportedSkinComponentException(lookup);
|
||||
}
|
||||
case OsuSkinComponents.SliderHeadHitCircle:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacySliderHeadHitCircle();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.ReverseArrow:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacyReverseArrow();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.HitCircle:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacyMainCirclePiece();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.Cursor:
|
||||
if (GetTexture("cursor") != null)
|
||||
return new LegacyCursor(this);
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorTrail:
|
||||
if (GetTexture("cursortrail") != null)
|
||||
return new LegacyCursorTrail(this);
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorRipple:
|
||||
if (GetTexture("cursor-ripple") != null)
|
||||
{
|
||||
var ripple = this.GetAnimation("cursor-ripple", false, false);
|
||||
|
||||
// In stable this element was scaled down to 50% and opacity 20%, but this makes the elements WAY too big and inflexible.
|
||||
// If anyone complains about these not being applied, this can be uncommented.
|
||||
//
|
||||
// But if no one complains I'd rather fix this in lazer. Wiki documentation doesn't mention size,
|
||||
// so we might be okay.
|
||||
//
|
||||
// if (ripple != null)
|
||||
// {
|
||||
// ripple.Scale = new Vector2(0.5f);
|
||||
// ripple.Alpha = 0.2f;
|
||||
// }
|
||||
|
||||
return ripple;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorParticles:
|
||||
if (GetTexture("star2") != null)
|
||||
return new LegacyCursorParticles();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorSmoke:
|
||||
if (GetTexture("cursor-smoke") != null)
|
||||
return new LegacySmokeSegment();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.HitCircleText:
|
||||
if (!this.HasFont(LegacyFont.HitCircle))
|
||||
return null;
|
||||
|
||||
const float hitcircle_text_scale = 0.8f;
|
||||
return new LegacySpriteText(LegacyFont.HitCircle)
|
||||
{
|
||||
// stable applies a blanket 0.8x scale to hitcircle fonts
|
||||
Scale = new Vector2(hitcircle_text_scale),
|
||||
MaxSizePerGlyph = OsuHitObject.OBJECT_DIMENSIONS * 2 / hitcircle_text_scale,
|
||||
};
|
||||
|
||||
case OsuSkinComponents.SpinnerBody:
|
||||
bool hasBackground = GetTexture("spinner-background") != null;
|
||||
|
||||
if (GetTexture("spinner-top") != null && !hasBackground)
|
||||
return new LegacyNewStyleSpinner();
|
||||
else if (hasBackground)
|
||||
return new LegacyOldStyleSpinner();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.ApproachCircle:
|
||||
if (GetTexture(@"approachcircle") != null)
|
||||
return new LegacyApproachCircle();
|
||||
|
||||
return null;
|
||||
|
||||
default:
|
||||
throw new UnsupportedSkinComponentException(lookup);
|
||||
}
|
||||
|
||||
default:
|
||||
return base.GetDrawableComponent(lookup);
|
||||
}
|
||||
|
||||
return base.GetDrawableComponent(lookup);
|
||||
}
|
||||
|
||||
public override IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
|
@ -52,10 +52,7 @@ namespace osu.Game.Tests.Editing
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
composer = new TestHitObjectComposer()
|
||||
};
|
||||
Child = composer = new TestHitObjectComposer();
|
||||
|
||||
BeatDivisor.Value = 1;
|
||||
|
||||
|
BIN
osu.Game.Tests/Resources/Archives/modified-classic-20240724.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/modified-classic-20240724.osk
Normal file
Binary file not shown.
@ -65,7 +65,9 @@ namespace osu.Game.Tests.Skins
|
||||
// Covers default rank display
|
||||
"Archives/modified-default-20230809.osk",
|
||||
// Covers legacy rank display
|
||||
"Archives/modified-classic-20230809.osk"
|
||||
"Archives/modified-classic-20230809.osk",
|
||||
// Covers legacy key counter
|
||||
"Archives/modified-classic-20240724.osk"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -9,7 +9,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Containers;
|
||||
@ -45,7 +44,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
// best way to check without exposing.
|
||||
private Drawable hideTarget => hudOverlay.ChildrenOfType<SkinComponentsContainer>().First();
|
||||
private Drawable keyCounterFlow => hudOverlay.ChildrenOfType<KeyCounterDisplay>().First().ChildrenOfType<FillFlowContainer<KeyCounter>>().Single();
|
||||
private Drawable keyCounterContent => hudOverlay.ChildrenOfType<KeyCounterDisplay>().First().ChildrenOfType<Drawable>().Skip(1).First();
|
||||
|
||||
public TestSceneHUDOverlay()
|
||||
{
|
||||
@ -79,7 +78,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("showhud is set", () => hudOverlay.ShowHud.Value);
|
||||
|
||||
AddAssert("hidetarget is visible", () => hideTarget.Alpha, () => Is.GreaterThan(0));
|
||||
AddAssert("key counter flow is visible", () => keyCounterFlow.IsPresent);
|
||||
AddAssert("key counter flow is visible", () => keyCounterContent.IsPresent);
|
||||
AddAssert("pause button is visible", () => hudOverlay.HoldToQuit.IsPresent);
|
||||
}
|
||||
|
||||
@ -104,7 +103,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("pause button is still visible", () => hudOverlay.HoldToQuit.IsPresent);
|
||||
|
||||
// Key counter flow container should not be affected by this, only the key counter display will be hidden as checked above.
|
||||
AddAssert("key counter flow not affected", () => keyCounterFlow.IsPresent);
|
||||
AddAssert("key counter flow not affected", () => keyCounterContent.IsPresent);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -150,11 +149,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
|
||||
AddUntilStep("hidetarget is hidden", () => hideTarget.Alpha, () => Is.LessThanOrEqualTo(0));
|
||||
AddUntilStep("key counters hidden", () => !keyCounterFlow.IsPresent);
|
||||
AddUntilStep("key counters hidden", () => !keyCounterContent.IsPresent);
|
||||
|
||||
AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true);
|
||||
AddUntilStep("hidetarget is visible", () => hideTarget.Alpha, () => Is.GreaterThan(0));
|
||||
AddUntilStep("key counters still hidden", () => !keyCounterFlow.IsPresent);
|
||||
AddUntilStep("key counters still hidden", () => !keyCounterContent.IsPresent);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -56,6 +57,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Anchor = Anchor.Centre,
|
||||
Scale = new Vector2(1, -1)
|
||||
},
|
||||
new LegacyKeyCounterDisplay
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
@ -89,6 +95,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Anchor = Anchor.Centre,
|
||||
Rotation = 90,
|
||||
},
|
||||
new LegacyKeyCounterDisplay
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Rotation = 90,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -16,12 +16,13 @@ using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Gameplay;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
@ -91,10 +92,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
SetContents(_ =>
|
||||
{
|
||||
hudOverlay = new HUDOverlay(null, Array.Empty<Mod>());
|
||||
|
||||
// Add any key just to display the key counter visually.
|
||||
hudOverlay.InputCountController.Add(new KeyCounterKeyboardTrigger(Key.Space));
|
||||
hudOverlay = new HUDOverlay(new DrawableOsuRuleset(new OsuRuleset(), new OsuBeatmap()), Array.Empty<Mod>());
|
||||
|
||||
action?.Invoke(hudOverlay);
|
||||
|
||||
|
@ -0,0 +1,42 @@
|
||||
// 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.Graphics;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public partial class TestSceneSkinnableKeyCounter : SkinnableHUDComponentTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly InputCountController controller = new InputCountController();
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
AddStep("create dependencies", () =>
|
||||
{
|
||||
Add(controller);
|
||||
controller.Add(new KeyCounterKeyboardTrigger(Key.Z));
|
||||
controller.Add(new KeyCounterKeyboardTrigger(Key.X));
|
||||
controller.Add(new KeyCounterKeyboardTrigger(Key.C));
|
||||
controller.Add(new KeyCounterKeyboardTrigger(Key.V));
|
||||
|
||||
foreach (var trigger in controller.Triggers)
|
||||
Add(trigger);
|
||||
});
|
||||
base.SetUpSteps();
|
||||
}
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
|
||||
protected override Drawable CreateDefaultImplementation() => new ArgonKeyCounterDisplay();
|
||||
|
||||
protected override Drawable CreateLegacyImplementation() => new LegacyKeyCounterDisplay();
|
||||
}
|
||||
}
|
@ -33,6 +33,34 @@ namespace osu.Game.Tests.Visual.Online
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMention()
|
||||
{
|
||||
AddStep("add normal message", () => channel.AddNewMessages(
|
||||
new Message(1)
|
||||
{
|
||||
Sender = new APIUser
|
||||
{
|
||||
Id = 2,
|
||||
Username = "TestUser2"
|
||||
},
|
||||
Content = "Hello how are you today?",
|
||||
Timestamp = new DateTimeOffset(2021, 12, 11, 13, 33, 24, TimeSpan.Zero)
|
||||
}));
|
||||
|
||||
AddStep("add mention", () => channel.AddNewMessages(
|
||||
new Message(2)
|
||||
{
|
||||
Sender = new APIUser
|
||||
{
|
||||
Id = 2,
|
||||
Username = "TestUser2"
|
||||
},
|
||||
Content = $"Hello {API.LocalUser.Value.Username} how are you today?",
|
||||
Timestamp = new DateTimeOffset(2021, 12, 11, 13, 33, 25, TimeSpan.Zero)
|
||||
}));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDaySeparators()
|
||||
{
|
||||
|
@ -93,14 +93,8 @@ namespace osu.Game.Beatmaps.Formats
|
||||
return line;
|
||||
}
|
||||
|
||||
protected void HandleColours<TModel>(TModel output, string line, bool allowAlpha)
|
||||
private Color4 convertSettingStringToColor4(string[] split, bool allowAlpha, KeyValuePair<string, string> pair)
|
||||
{
|
||||
var pair = SplitKeyVal(line);
|
||||
|
||||
bool isCombo = pair.Key.StartsWith(@"Combo", StringComparison.Ordinal);
|
||||
|
||||
string[] split = pair.Value.Split(',');
|
||||
|
||||
if (split.Length != 3 && split.Length != 4)
|
||||
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B or R,G,B,A): {pair.Value}");
|
||||
|
||||
@ -116,6 +110,18 @@ namespace osu.Game.Beatmaps.Formats
|
||||
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
|
||||
}
|
||||
|
||||
return colour;
|
||||
}
|
||||
|
||||
protected void HandleColours<TModel>(TModel output, string line, bool allowAlpha)
|
||||
{
|
||||
var pair = SplitKeyVal(line);
|
||||
|
||||
string[] split = pair.Value.Split(',');
|
||||
Color4 colour = convertSettingStringToColor4(split, allowAlpha, pair);
|
||||
|
||||
bool isCombo = pair.Key.StartsWith(@"Combo", StringComparison.Ordinal);
|
||||
|
||||
if (isCombo)
|
||||
{
|
||||
if (!(output is IHasComboColours tHasComboColours)) return;
|
||||
|
@ -67,13 +67,7 @@ namespace osu.Game.Configuration
|
||||
SetDefault(OsuSetting.Username, string.Empty);
|
||||
SetDefault(OsuSetting.Token, string.Empty);
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
// this default set MUST remain despite the setting being deprecated, because `SetDefault()` calls are implicitly used to declare the type returned for the lookup.
|
||||
// if this is removed, the setting will be interpreted as a string, and `Migrate()` will fail due to cast failure.
|
||||
// can be removed 20240618
|
||||
SetDefault(OsuSetting.AutomaticallyDownloadWhenSpectating, false);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
SetDefault(OsuSetting.AutomaticallyDownloadMissingBeatmaps, false);
|
||||
SetDefault(OsuSetting.AutomaticallyDownloadMissingBeatmaps, true);
|
||||
|
||||
SetDefault(OsuSetting.SavePassword, true).ValueChanged += enabled =>
|
||||
{
|
||||
@ -244,12 +238,6 @@ namespace osu.Game.Configuration
|
||||
|
||||
// migrations can be added here using a condition like:
|
||||
// if (combined < 20220103) { performMigration() }
|
||||
if (combined < 20230918)
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
SetValue(OsuSetting.AutomaticallyDownloadMissingBeatmaps, Get<bool>(OsuSetting.AutomaticallyDownloadWhenSpectating)); // can be removed 20240618
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
}
|
||||
|
||||
public override TrackedSettings CreateTrackedSettings()
|
||||
@ -424,9 +412,6 @@ namespace osu.Game.Configuration
|
||||
EditorAutoSeekOnPlacement,
|
||||
DiscordRichPresence,
|
||||
|
||||
[Obsolete($"Use {nameof(AutomaticallyDownloadMissingBeatmaps)} instead.")] // can be removed 20240318
|
||||
AutomaticallyDownloadWhenSpectating,
|
||||
|
||||
ShowOnlineExplicitContent,
|
||||
LastProcessedMetadataId,
|
||||
SafeAreaConsiderations,
|
||||
|
@ -8,16 +8,24 @@ using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Graphics.Cursor
|
||||
{
|
||||
[Cached(typeof(OsuContextMenuContainer))]
|
||||
public partial class OsuContextMenuContainer : ContextMenuContainer
|
||||
{
|
||||
[Cached]
|
||||
private OsuContextMenuSamples samples = new OsuContextMenuSamples();
|
||||
|
||||
private OsuContextMenu menu = null!;
|
||||
|
||||
public OsuContextMenuContainer()
|
||||
{
|
||||
AddInternal(samples);
|
||||
}
|
||||
|
||||
protected override Menu CreateMenu() => new OsuContextMenu(true);
|
||||
protected override Menu CreateMenu() => menu = new OsuContextMenu(true);
|
||||
|
||||
public void CloseMenu()
|
||||
{
|
||||
menu.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Chat;
|
||||
using osuTK;
|
||||
@ -104,6 +105,8 @@ namespace osu.Game.Overlays.Chat
|
||||
}
|
||||
}
|
||||
|
||||
private bool isMention;
|
||||
|
||||
/// <summary>
|
||||
/// The colour used to paint the author's username.
|
||||
/// </summary>
|
||||
@ -255,12 +258,21 @@ namespace osu.Game.Overlays.Chat
|
||||
private void styleMessageContent(SpriteText text)
|
||||
{
|
||||
text.Shadow = false;
|
||||
text.Font = text.Font.With(size: FontSize, italics: Message.IsAction);
|
||||
text.Font = text.Font.With(size: FontSize, italics: Message.IsAction, weight: isMention ? FontWeight.SemiBold : FontWeight.Medium);
|
||||
|
||||
bool messageHasColour = Message.IsAction && !string.IsNullOrEmpty(message.Sender.Colour);
|
||||
text.Colour = messageHasColour ? Color4Extensions.FromHex(message.Sender.Colour) : colourProvider?.Content1 ?? Colour4.White;
|
||||
Color4 messageColour = colourProvider?.Content1 ?? Colour4.White;
|
||||
|
||||
if (isMention)
|
||||
messageColour = colourProvider?.Highlight1 ?? Color4.Orange;
|
||||
else if (Message.IsAction && !string.IsNullOrEmpty(message.Sender.Colour))
|
||||
messageColour = Color4Extensions.FromHex(message.Sender.Colour);
|
||||
|
||||
text.Colour = messageColour;
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
private void updateMessageContent()
|
||||
{
|
||||
this.FadeTo(message is LocalEchoMessage ? 0.4f : 1.0f, 500, Easing.OutQuint);
|
||||
@ -280,6 +292,8 @@ namespace osu.Game.Overlays.Chat
|
||||
// remove non-existent channels from the link list
|
||||
message.Links.RemoveAll(link => link.Action == LinkAction.OpenChannel && chatManager?.AvailableChannels.Any(c => c.Name == link.Argument.ToString()) != true);
|
||||
|
||||
isMention = MessageNotifier.CheckContainsUsername(message.DisplayContent, api.LocalUser.Value.Username);
|
||||
|
||||
drawableContentFlow.Clear();
|
||||
drawableContentFlow.AddLinks(message.DisplayContent, message.Links);
|
||||
}
|
||||
|
@ -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 System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -16,6 +14,7 @@ using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
@ -50,14 +49,17 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
|
||||
private readonly List<SelectionBlueprint<T>> selectedBlueprints;
|
||||
|
||||
protected SelectionBox SelectionBox { get; private set; }
|
||||
protected SelectionBox SelectionBox { get; private set; } = null!;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
protected IEditorChangeHandler ChangeHandler { get; private set; }
|
||||
protected IEditorChangeHandler? ChangeHandler { get; private set; }
|
||||
|
||||
public SelectionRotationHandler RotationHandler { get; private set; }
|
||||
public SelectionRotationHandler RotationHandler { get; private set; } = null!;
|
||||
|
||||
public SelectionScaleHandler ScaleHandler { get; private set; }
|
||||
public SelectionScaleHandler ScaleHandler { get; private set; } = null!;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
protected OsuContextMenuContainer? ContextMenuContainer { get; private set; }
|
||||
|
||||
protected SelectionHandler()
|
||||
{
|
||||
@ -230,7 +232,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// <summary>
|
||||
/// Deselect all selected items.
|
||||
/// </summary>
|
||||
protected void DeselectAll() => SelectedItems.Clear();
|
||||
protected void DeselectAll()
|
||||
{
|
||||
SelectedItems.Clear();
|
||||
ContextMenuContainer?.CloseMenu();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a blueprint becoming selected.
|
||||
@ -243,6 +249,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
SelectedItems.Add(blueprint.Item);
|
||||
|
||||
selectedBlueprints.Add(blueprint);
|
||||
|
||||
ContextMenuContainer?.CloseMenu();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -6,6 +6,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework;
|
||||
@ -159,7 +160,7 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private string lastSavedHash;
|
||||
|
||||
private Container<EditorScreen> screenContainer;
|
||||
private ScreenContainer screenContainer;
|
||||
|
||||
[CanBeNull]
|
||||
private readonly EditorLoader loader;
|
||||
@ -329,7 +330,7 @@ namespace osu.Game.Screens.Edit
|
||||
Name = "Screen container",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = 40, Bottom = 50 },
|
||||
Child = screenContainer = new Container<EditorScreen>
|
||||
Child = screenContainer = new ScreenContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
@ -422,6 +423,7 @@ namespace osu.Game.Screens.Edit
|
||||
MutationTracker,
|
||||
}
|
||||
});
|
||||
|
||||
changeHandler?.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true);
|
||||
changeHandler?.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true);
|
||||
|
||||
@ -1007,7 +1009,7 @@ namespace osu.Game.Screens.Edit
|
||||
throw new InvalidOperationException("Editor menu bar switched to an unsupported mode");
|
||||
}
|
||||
|
||||
LoadComponentAsync(currentScreen, newScreen =>
|
||||
screenContainer.LoadComponentAsync(currentScreen, newScreen =>
|
||||
{
|
||||
if (newScreen == currentScreen)
|
||||
{
|
||||
@ -1385,5 +1387,12 @@ namespace osu.Game.Screens.Edit
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private partial class ScreenContainer : Container<EditorScreen>
|
||||
{
|
||||
public new Task LoadComponentAsync<TLoadable>([NotNull] TLoadable component, Action<TLoadable> onLoaded = null, CancellationToken cancellation = default, Scheduler scheduler = null)
|
||||
where TLoadable : Drawable
|
||||
=> base.LoadComponentAsync(component, onLoaded, cancellation, scheduler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,11 +14,10 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public ArgonKeyCounterDisplay()
|
||||
{
|
||||
InternalChild = KeyFlow = new FillFlowContainer<KeyCounter>
|
||||
Child = KeyFlow = new FillFlowContainer<KeyCounter>
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
Spacing = new Vector2(2),
|
||||
};
|
||||
}
|
||||
|
@ -16,11 +16,10 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
public DefaultKeyCounterDisplay()
|
||||
{
|
||||
InternalChild = KeyFlow = new FillFlowContainer<KeyCounter>
|
||||
Child = KeyFlow = new FillFlowContainer<KeyCounter>
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
/// <summary>
|
||||
/// A flowing display of all gameplay keys. Individual keys can be added using <see cref="InputTrigger"/> implementations.
|
||||
/// </summary>
|
||||
public abstract partial class KeyCounterDisplay : CompositeDrawable, ISerialisableDrawable
|
||||
public abstract partial class KeyCounterDisplay : Container, ISerialisableDrawable
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the key counter should be visible regardless of the configuration value.
|
||||
@ -29,25 +29,22 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
private readonly IBindableList<InputTrigger> triggers = new BindableList<InputTrigger>();
|
||||
|
||||
protected override Container<Drawable> Content { get; } = new Container
|
||||
{
|
||||
Alpha = 0,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
};
|
||||
|
||||
[Resolved]
|
||||
private InputCountController controller { get; set; } = null!;
|
||||
|
||||
private const int duration = 100;
|
||||
|
||||
protected void UpdateVisibility()
|
||||
protected KeyCounterDisplay()
|
||||
{
|
||||
bool visible = AlwaysVisible.Value || ConfigVisibility.Value;
|
||||
|
||||
// Isolate changing visibility of the key counters from fading this component.
|
||||
KeyFlow.FadeTo(visible ? 1 : 0, duration);
|
||||
|
||||
// Ensure a valid size is immediately obtained even if partially off-screen
|
||||
// See https://github.com/ppy/osu/issues/14793.
|
||||
KeyFlow.AlwaysPresent = visible;
|
||||
AddInternal(Content);
|
||||
}
|
||||
|
||||
protected abstract KeyCounter CreateCounter(InputTrigger trigger);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config, DrawableRuleset? drawableRuleset)
|
||||
{
|
||||
@ -70,6 +67,20 @@ namespace osu.Game.Screens.Play.HUD
|
||||
ConfigVisibility.BindValueChanged(_ => UpdateVisibility(), true);
|
||||
}
|
||||
|
||||
protected void UpdateVisibility()
|
||||
{
|
||||
bool visible = AlwaysVisible.Value || ConfigVisibility.Value;
|
||||
|
||||
// Isolate changing visibility of the key counters from fading this component.
|
||||
Content.FadeTo(visible ? 1 : 0, duration);
|
||||
|
||||
// Ensure a valid size is immediately obtained even if partially off-screen
|
||||
// See https://github.com/ppy/osu/issues/14793.
|
||||
Content.AlwaysPresent = visible;
|
||||
}
|
||||
|
||||
protected abstract KeyCounter CreateCounter(InputTrigger trigger);
|
||||
|
||||
private void triggersChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
KeyFlow.Clear();
|
||||
|
@ -47,6 +47,9 @@ namespace osu.Game.Skinning
|
||||
/// <summary>
|
||||
/// Retrieve a configuration value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that while this returns a bindable value, it is not actually updated.
|
||||
/// Until the API is fixed, just use the received bindable's <see cref="IBindable{TValue}.Value"/> immediately.</remarks>
|
||||
/// <param name="lookup">The requested configuration value.</param>
|
||||
/// <returns>A matching value boxed in an <see cref="IBindable{TValue}"/>, or null if unavailable.</returns>
|
||||
IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
|
101
osu.Game/Skinning/LegacyKeyCounter.cs
Normal file
101
osu.Game/Skinning/LegacyKeyCounter.cs
Normal file
@ -0,0 +1,101 @@
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public partial class LegacyKeyCounter : KeyCounter
|
||||
{
|
||||
private const float transition_duration = 160;
|
||||
|
||||
public Colour4 ActiveColour { get; set; }
|
||||
|
||||
private Colour4 textColour;
|
||||
|
||||
public Colour4 TextColour
|
||||
{
|
||||
get => textColour;
|
||||
set
|
||||
{
|
||||
textColour = value;
|
||||
overlayKeyText.Colour = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Container keyContainer;
|
||||
private readonly OsuSpriteText overlayKeyText;
|
||||
private readonly Sprite keySprite;
|
||||
|
||||
public LegacyKeyCounter(InputTrigger trigger)
|
||||
: base(trigger)
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
Anchor = Anchor.Centre;
|
||||
Child = keyContainer = new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
keySprite = new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
new UprightAspectMaintainingContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Child = overlayKeyText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = trigger.Name,
|
||||
Colour = textColour,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.SemiBold),
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// matches longest dimension of default skin asset
|
||||
Height = Width = 46;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource source)
|
||||
{
|
||||
Texture? keyTexture = source.GetTexture(@"inputoverlay-key");
|
||||
|
||||
if (keyTexture != null)
|
||||
keySprite.Texture = keyTexture;
|
||||
}
|
||||
|
||||
protected override void Activate(bool forwardPlayback = true)
|
||||
{
|
||||
base.Activate(forwardPlayback);
|
||||
keyContainer.ScaleTo(0.75f, transition_duration, Easing.Out);
|
||||
keySprite.Colour = ActiveColour;
|
||||
overlayKeyText.Text = CountPresses.Value.ToString();
|
||||
overlayKeyText.Font = overlayKeyText.Font.With(weight: FontWeight.SemiBold);
|
||||
}
|
||||
|
||||
protected override void Deactivate(bool forwardPlayback = true)
|
||||
{
|
||||
base.Deactivate(forwardPlayback);
|
||||
keyContainer.ScaleTo(1f, transition_duration, Easing.Out);
|
||||
keySprite.Colour = Colour4.White;
|
||||
}
|
||||
}
|
||||
}
|
92
osu.Game/Skinning/LegacyKeyCounterDisplay.cs
Normal file
92
osu.Game/Skinning/LegacyKeyCounterDisplay.cs
Normal file
@ -0,0 +1,92 @@
|
||||
// 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.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public partial class LegacyKeyCounterDisplay : KeyCounterDisplay
|
||||
{
|
||||
private static readonly Colour4 active_colour_top = Colour4.FromHex(@"#ffde00");
|
||||
private static readonly Colour4 active_colour_bottom = Colour4.FromHex(@"#f8009e");
|
||||
|
||||
protected override FillFlowContainer<KeyCounter> KeyFlow { get; }
|
||||
|
||||
private readonly Sprite backgroundSprite;
|
||||
|
||||
public LegacyKeyCounterDisplay()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
backgroundSprite = new Sprite
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopLeft,
|
||||
Scale = new Vector2(1.05f, 1),
|
||||
Rotation = 90,
|
||||
},
|
||||
KeyFlow = new FillFlowContainer<KeyCounter>
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
X = -1.5f,
|
||||
Y = 7,
|
||||
Spacing = new Vector2(1.8f),
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private ISkinSource source { get; set; } = null!;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
KeyTextColor = source.GetConfig<SkinCustomColourLookup, Color4>(new SkinCustomColourLookup(SkinConfiguration.LegacySetting.InputOverlayText))?.Value ?? Color4.Black;
|
||||
|
||||
Texture? backgroundTexture = source.GetTexture(@"inputoverlay-background");
|
||||
|
||||
if (backgroundTexture != null)
|
||||
backgroundSprite.Texture = backgroundTexture;
|
||||
|
||||
for (int i = 0; i < KeyFlow.Count; ++i)
|
||||
{
|
||||
((LegacyKeyCounter)KeyFlow[i]).ActiveColour = i < 2 ? active_colour_top : active_colour_bottom;
|
||||
}
|
||||
}
|
||||
|
||||
protected override KeyCounter CreateCounter(InputTrigger trigger) => new LegacyKeyCounter(trigger)
|
||||
{
|
||||
TextColour = keyTextColor,
|
||||
};
|
||||
|
||||
private Colour4 keyTextColor = Colour4.White;
|
||||
|
||||
public Colour4 KeyTextColor
|
||||
{
|
||||
get => keyTextColor;
|
||||
set
|
||||
{
|
||||
if (value != keyTextColor)
|
||||
{
|
||||
keyTextColor = value;
|
||||
foreach (var child in KeyFlow.Cast<LegacyKeyCounter>())
|
||||
child.TextColour = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -23,7 +23,6 @@ using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
@ -310,6 +309,9 @@ namespace osu.Game.Skinning
|
||||
case SkinConfiguration.LegacySetting.Version:
|
||||
return SkinUtils.As<TValue>(new Bindable<decimal>(Configuration.LegacyVersion ?? SkinConfiguration.LATEST_VERSION));
|
||||
|
||||
case SkinConfiguration.LegacySetting.InputOverlayText:
|
||||
return SkinUtils.As<TValue>(new Bindable<Colour4>(Configuration.CustomColours.TryGetValue(@"InputOverlayText", out var colour) ? colour : Colour4.Black));
|
||||
|
||||
default:
|
||||
return genericLookup<SkinConfiguration.LegacySetting, TValue>(legacySetting);
|
||||
}
|
||||
@ -381,22 +383,12 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
|
||||
var hitError = container.OfType<HitErrorMeter>().FirstOrDefault();
|
||||
var keyCounter = container.OfType<DefaultKeyCounterDisplay>().FirstOrDefault();
|
||||
|
||||
if (hitError != null)
|
||||
{
|
||||
hitError.Anchor = Anchor.BottomCentre;
|
||||
hitError.Origin = Anchor.CentreLeft;
|
||||
hitError.Rotation = -90;
|
||||
|
||||
if (keyCounter != null)
|
||||
{
|
||||
const float padding = 10;
|
||||
|
||||
keyCounter.Anchor = Anchor.BottomRight;
|
||||
keyCounter.Origin = Anchor.BottomRight;
|
||||
keyCounter.Position = new Vector2(-padding, -(padding + hitError.Width));
|
||||
}
|
||||
}
|
||||
})
|
||||
{
|
||||
@ -408,7 +400,6 @@ namespace osu.Game.Skinning
|
||||
new LegacySongProgress(),
|
||||
new LegacyHealthDisplay(),
|
||||
new BarHitErrorMeter(),
|
||||
new DefaultKeyCounterDisplay()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ namespace osu.Game.Skinning
|
||||
AnimationFramerate,
|
||||
LayeredHitSounds,
|
||||
AllowSliderBallTint,
|
||||
InputOverlayText,
|
||||
}
|
||||
|
||||
public static List<Color4> DefaultComboColours { get; } = new List<Color4>
|
||||
|
Loading…
Reference in New Issue
Block a user