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

Merge branch 'master' into selection-mask-testcase

This commit is contained in:
Dean Herbert 2018-10-31 06:14:33 +09:00 committed by GitHub
commit 7e88b8b457
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 560 additions and 186 deletions

7
.gitignore vendored
View File

@ -10,6 +10,10 @@
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
### Cake ###
tools/*
!tools/cakebuild.csproj
# Build results
bin/[Dd]ebug/
[Dd]ebugPublic/
@ -98,6 +102,7 @@ $tf/
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
inspectcode
# JustCode is a .NET coding add-in
.JustCode
@ -257,3 +262,5 @@ paket-files/
__pycache__/
*.pyc
Staging/
inspectcodereport.xml

View File

@ -25,6 +25,7 @@ Build and run
- Using Visual Studio 2017, Rider or Visual Studio Code (configurations are included)
- From command line using `dotnet run --project osu.Desktop`. When building for non-development purposes, add `-c Release` to gain higher performance.
- To run with code analysis, instead use `powershell ./build.ps1` or `build.sh`. This is currently only supported under windows due to [resharper cli shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternative, you can install resharper or use rider to get inline support in your IDE of choice.
Note: If you run from command line under linux, you will need to prefix the output folder to your `LD_LIBRARY_PATH`. See `.vscode/launch.json` for an example

View File

@ -1,22 +1,8 @@
clone_depth: 1
version: '{branch}-{build}'
image: Visual Studio 2017
configuration: Debug
cache:
- C:\ProgramData\chocolatey\bin -> appveyor.yml
- C:\ProgramData\chocolatey\lib -> appveyor.yml
test: off
install:
- cmd: git submodule update --init --recursive --depth=5
- cmd: choco install resharper-clt -y
- cmd: choco install nvika -y
- cmd: dotnet tool install CodeFileSanity --version 0.0.16 --global
before_build:
- cmd: CodeFileSanity
- cmd: nuget restore -verbosity quiet
build:
project: osu.sln
parallel: true
verbosity: minimal
after_build:
- cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
build_script:
- cmd: PowerShell -Version 2.0 .\build.ps1

72
build.cake Normal file
View File

@ -0,0 +1,72 @@
#addin "nuget:?package=CodeFileSanity&version=0.0.21"
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2018.2.2"
#tool "nuget:?package=NVika.MSBuild&version=1.0.1"
///////////////////////////////////////////////////////////////////////////////
// ARGUMENTS
///////////////////////////////////////////////////////////////////////////////
var target = Argument("target", "Build");
var configuration = Argument("configuration", "Release");
var osuSolution = new FilePath("./osu.sln");
///////////////////////////////////////////////////////////////////////////////
// TASKS
///////////////////////////////////////////////////////////////////////////////
Task("Restore")
.Does(() => {
DotNetCoreRestore(osuSolution.FullPath);
});
Task("Compile")
.IsDependentOn("Restore")
.Does(() => {
DotNetCoreBuild(osuSolution.FullPath, new DotNetCoreBuildSettings {
Configuration = configuration,
NoRestore = true,
});
});
Task("Test")
.IsDependentOn("Compile")
.Does(() => {
var testAssemblies = GetFiles("**/*.Tests/bin/**/*.Tests.dll");
DotNetCoreVSTest(testAssemblies, new DotNetCoreVSTestSettings {
Logger = AppVeyor.IsRunningOnAppVeyor ? "Appveyor" : $"trx",
Parallel = true,
ToolTimeout = TimeSpan.FromMinutes(10),
});
});
// windows only because both inspectcore and nvika depend on net45
Task("InspectCode")
.WithCriteria(IsRunningOnWindows())
.IsDependentOn("Compile")
.Does(() => {
var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
InspectCode(osuSolution, new InspectCodeSettings {
CachesHome = "inspectcode",
OutputFile = "inspectcodereport.xml",
});
StartProcess(nVikaToolPath, @"parsereport ""inspectcodereport.xml"" --treatwarningsaserrors");
});
Task("CodeFileSanity")
.Does(() => {
ValidateCodeSanity(new ValidateCodeSanitySettings {
RootDirectory = ".",
IsAppveyorBuild = AppVeyor.IsRunningOnAppVeyor
});
});
Task("Build")
.IsDependentOn("CodeFileSanity")
.IsDependentOn("InspectCode")
.IsDependentOn("Test");
RunTarget(target);

79
build.ps1 Normal file
View File

@ -0,0 +1,79 @@
##########################################################################
# This is a customized Cake bootstrapper script for PowerShell.
##########################################################################
<#
.SYNOPSIS
This is a Powershell script to bootstrap a Cake build.
.DESCRIPTION
This Powershell script restores NuGet tools (including Cake)
and execute your Cake build script with the parameters you provide.
.PARAMETER Script
The build script to execute.
.PARAMETER Target
The build script target to run.
.PARAMETER Configuration
The build configuration to use.
.PARAMETER Verbosity
Specifies the amount of information to be displayed.
.PARAMETER ShowDescription
Shows description about tasks.
.PARAMETER DryRun
Performs a dry run.
.PARAMETER ScriptArgs
Remaining arguments are added here.
.LINK
https://cakebuild.net
#>
[CmdletBinding()]
Param(
[string]$Script = "build.cake",
[string]$Target,
[string]$Configuration,
[ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
[string]$Verbosity,
[switch]$ShowDescription,
[Alias("WhatIf", "Noop")]
[switch]$DryRun,
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
[string[]]$ScriptArgs
)
Write-Host "Preparing to run build script..."
# Determine the script root for resolving other paths.
if(!$PSScriptRoot){
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
}
# Resolve the paths for resources used for debugging.
$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
$CAKE_CSPROJ = Join-Path $TOOLS_DIR "cakebuild.csproj"
# Install the required tools locally.
Write-Host "Restoring cake tools..."
Invoke-Expression "dotnet restore `"$CAKE_CSPROJ`" --packages `"$TOOLS_DIR`"" | Out-Null
# Find the Cake executable
$CAKE_EXECUTABLE = (Get-ChildItem -Path ./tools/cake.coreclr/ -Filter Cake.dll -Recurse).FullName
# Build Cake arguments
$cakeArguments = @("$Script");
if ($Target) { $cakeArguments += "-target=$Target" }
if ($Configuration) { $cakeArguments += "-configuration=$Configuration" }
if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" }
if ($ShowDescription) { $cakeArguments += "-showdescription" }
if ($DryRun) { $cakeArguments += "-dryrun" }
if ($Experimental) { $cakeArguments += "-experimental" }
$cakeArguments += $ScriptArgs
# Start Cake
Write-Host "Running build script..."
Invoke-Expression "dotnet `"$CAKE_EXECUTABLE`" $cakeArguments"
exit $LASTEXITCODE

37
build.sh Normal file
View File

@ -0,0 +1,37 @@
#!/usr/bin/env bash
##########################################################################
# This is a customized Cake bootstrapper script for Shell.
##########################################################################
echo "Preparing to run build script..."
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
TOOLS_DIR=$SCRIPT_DIR/tools
CAKE_BINARY_PATH=$TOOLS_DIR/"cake.coreclr"
SCRIPT="build.cake"
CAKE_CSPROJ=$TOOLS_DIR/"cakebuild.csproj"
# Parse arguments.
CAKE_ARGUMENTS=()
for i in "$@"; do
case $1 in
-s|--script) SCRIPT="$2"; shift ;;
--) shift; CAKE_ARGUMENTS+=("$@"); break ;;
*) CAKE_ARGUMENTS+=("$1") ;;
esac
shift
done
# Install the required tools locally.
echo "Restoring cake tools..."
dotnet restore $CAKE_CSPROJ --packages $TOOLS_DIR > /dev/null 2>&1
# Search for the CakeBuild binary.
CAKE_BINARY=$(find $CAKE_BINARY_PATH -name "Cake.dll")
# Start Cake
echo "Running build script..."
dotnet "$CAKE_BINARY" $SCRIPT "${CAKE_ARGUMENTS[@]}"

5
cake.config Normal file
View File

@ -0,0 +1,5 @@
[Nuget]
Source=https://api.nuget.org/v3/index.json
UseInProcessClient=true
LoadDependencies=true

View File

@ -5,13 +5,11 @@ using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToRulesetContainer<ManiaHitObject>
public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToBeatmap<ManiaHitObject>
{
public override string Name => "Dual Stages";
public override string ShortenedName => "DS";
@ -34,22 +32,21 @@ namespace osu.Game.Rulesets.Mania.Mods
mbc.TargetColumns *= 2;
}
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
public void ApplyToBeatmap(Beatmap<ManiaHitObject> beatmap)
{
var mrc = (ManiaRulesetContainer)rulesetContainer;
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
if (isForCurrentRuleset)
return;
var maniaBeatmap = (ManiaBeatmap)beatmap;
var newDefinitions = new List<StageDefinition>();
foreach (var existing in mrc.Beatmap.Stages)
foreach (var existing in maniaBeatmap.Stages)
{
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
}
mrc.Beatmap.Stages = newDefinitions;
maniaBeatmap.Stages = newDefinitions;
}
public PlayfieldType PlayfieldType => PlayfieldType.Dual;

View File

@ -13,12 +13,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components
public class SliderBodyPiece : CompositeDrawable
{
private readonly Slider slider;
private readonly SliderBody body;
private readonly SnakingSliderBody body;
public SliderBodyPiece(Slider slider)
{
this.slider = slider;
InternalChild = body = new SliderBody(slider)
InternalChild = body = new SnakingSliderBody(slider)
{
AccentColour = Color4.Transparent,
PathWidth = slider.Scale * 64

View File

@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public readonly DrawableHitCircle HeadCircle;
public readonly DrawableSliderTail TailCircle;
public readonly SliderBody Body;
public readonly SnakingSliderBody Body;
public readonly SliderBall Ball;
public DrawableSlider(Slider s)
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
InternalChildren = new Drawable[]
{
Body = new SliderBody(s)
Body = new SnakingSliderBody(s)
{
PathWidth = s.Scale * 64,
},

View File

@ -0,0 +1,20 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
/// <summary>
/// A <see cref="SliderBody"/> with the ability to set the drawn vertices manually.
/// </summary>
public class ManualSliderBody : SliderBody
{
public new void SetVertices(IReadOnlyList<Vector2> vertices)
{
base.SetVertices(vertices);
Size = Path.Size;
}
}
}

View File

@ -4,7 +4,6 @@
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
@ -14,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class NumberPiece : Container
{
private readonly SpriteText number;
private readonly SkinnableSpriteText number;
public string Text
{
@ -41,15 +40,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
},
Child = new Box()
}, s => s.GetTexture("Play/osu/hitcircle") == null),
number = new OsuSpriteText
number = new SkinnableSpriteText("Play/osu/number-text", _ => new OsuSpriteText
{
Text = @"1",
Font = @"Venera",
UseFullGlyphHeight = false,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 40,
Alpha = 1
}, restrictSize: false)
{
Text = @"1"
}
};
}

View File

@ -1,24 +1,22 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
using OpenTK.Graphics.ES30;
using OpenTK.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.ES30;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class SliderBody : Container, ISliderProgress
public abstract class SliderBody : CompositeDrawable
{
private readonly SliderPath path;
protected Path Path => path;
private readonly BufferedContainer container;
public float PathWidth
@ -30,15 +28,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
/// <summary>
/// Offset in absolute coordinates from the start of the curve.
/// </summary>
public Vector2 PathOffset { get; private set; }
public readonly List<Vector2> CurrentCurve = new List<Vector2>();
public readonly Bindable<bool> SnakingIn = new Bindable<bool>();
public readonly Bindable<bool> SnakingOut = new Bindable<bool>();
public double? SnakedStart { get; private set; }
public double? SnakedEnd { get; private set; }
public virtual Vector2 PathOffset => path.PositionInBoundingBox(path.Vertices[0]);
/// <summary>
/// Used to colour the path.
@ -74,28 +64,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public Quad PathDrawQuad => container.ScreenSpaceDrawQuad;
private Vector2 topLeftOffset;
private readonly Slider slider;
public SliderBody(Slider s)
protected SliderBody()
{
slider = s;
Children = new Drawable[]
InternalChild = container = new BufferedContainer
{
container = new BufferedContainer
{
RelativeSizeAxes = Axes.Both,
CacheDrawnFrameBuffer = true,
Children = new Drawable[]
{
path = new SliderPath
{
Blending = BlendingMode.None,
},
}
},
RelativeSizeAxes = Axes.Both,
CacheDrawnFrameBuffer = true,
Child = path = new SliderPath { Blending = BlendingMode.None }
};
container.Attach(RenderbufferInternalFormat.DepthComponent16);
@ -103,80 +78,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos);
public void SetRange(double p0, double p1)
/// <summary>
/// Sets the vertices of the path which should be drawn by this <see cref="SliderBody"/>.
/// </summary>
/// <param name="vertices">The vertices</param>
protected void SetVertices(IReadOnlyList<Vector2> vertices)
{
if (p0 > p1)
MathHelper.Swap(ref p0, ref p1);
if (updateSnaking(p0, p1))
{
// The path is generated such that its size encloses it. This change of size causes the path
// to move around while snaking, so we need to offset it to make sure it maintains the
// same position as when it is fully snaked.
var newTopLeftOffset = path.PositionInBoundingBox(Vector2.Zero);
path.Position = topLeftOffset - newTopLeftOffset;
container.ForceRedraw();
}
}
[BackgroundDependencyLoader]
private void load()
{
computeSize();
}
private void computeSize()
{
// Generate the entire curve
slider.Curve.GetPathToProgress(CurrentCurve, 0, 1);
foreach (Vector2 p in CurrentCurve)
path.AddVertex(p);
Size = path.Size;
topLeftOffset = path.PositionInBoundingBox(Vector2.Zero);
PathOffset = path.PositionInBoundingBox(CurrentCurve[0]);
}
private bool updateSnaking(double p0, double p1)
{
if (SnakedStart == p0 && SnakedEnd == p1) return false;
SnakedStart = p0;
SnakedEnd = p1;
slider.Curve.GetPathToProgress(CurrentCurve, p0, p1);
path.ClearVertices();
foreach (Vector2 p in CurrentCurve)
path.AddVertex(p);
return true;
}
public void UpdateProgress(double completionProgress)
{
var span = slider.SpanAt(completionProgress);
var spanProgress = slider.ProgressAt(completionProgress);
double start = 0;
double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1;
if (span >= slider.SpanCount() - 1)
{
if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1)
{
start = 0;
end = SnakingOut ? spanProgress : 1;
}
else
{
start = SnakingOut ? spanProgress : 0;
}
}
SetRange(start, end);
path.Vertices = vertices;
container.ForceRedraw();
}
private class SliderPath : SmoothPath

View File

@ -0,0 +1,105 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
/// <summary>
/// A <see cref="SliderBody"/> which changes its curve depending on the snaking progress.
/// </summary>
public class SnakingSliderBody : SliderBody, ISliderProgress
{
public readonly List<Vector2> CurrentCurve = new List<Vector2>();
public readonly Bindable<bool> SnakingIn = new Bindable<bool>();
public readonly Bindable<bool> SnakingOut = new Bindable<bool>();
public double? SnakedStart { get; private set; }
public double? SnakedEnd { get; private set; }
public override Vector2 PathOffset => snakedPathOffset;
/// <summary>
/// The top-left position of the path when fully snaked.
/// </summary>
private Vector2 snakedPosition;
/// <summary>
/// The offset of the path from <see cref="snakedPosition"/> when fully snaked.
/// </summary>
private Vector2 snakedPathOffset;
private readonly Slider slider;
public SnakingSliderBody(Slider slider)
{
this.slider = slider;
}
[BackgroundDependencyLoader]
private void load()
{
// Generate the entire curve
slider.Curve.GetPathToProgress(CurrentCurve, 0, 1);
SetVertices(CurrentCurve);
// The body is sized to the full path size to avoid excessive autosize computations
Size = Path.Size;
snakedPosition = Path.PositionInBoundingBox(Vector2.Zero);
snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]);
}
public void UpdateProgress(double completionProgress)
{
var span = slider.SpanAt(completionProgress);
var spanProgress = slider.ProgressAt(completionProgress);
double start = 0;
double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1;
if (span >= slider.SpanCount() - 1)
{
if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1)
{
start = 0;
end = SnakingOut ? spanProgress : 1;
}
else
{
start = SnakingOut ? spanProgress : 0;
}
}
setRange(start, end);
}
private void setRange(double p0, double p1)
{
if (p0 > p1)
MathHelper.Swap(ref p0, ref p1);
if (SnakedStart == p0 && SnakedEnd == p1) return;
SnakedStart = p0;
SnakedEnd = p1;
slider.Curve.GetPathToProgress(CurrentCurve, p0, p1);
SetVertices(CurrentCurve);
// The bounding box of the path expands as it snakes, which in turn shifts the position of the path.
// Depending on the direction of expansion, it may appear as if the path is expanding towards the position of the slider
// rather than expanding out from the position of the slider.
// To remove this effect, the path's position is shifted towards its final snaked position
Path.Position = snakedPosition - Path.PositionInBoundingBox(Vector2.Zero);
}
}
}

View File

@ -0,0 +1,22 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// Interface for a <see cref="Mod"/> that applies changes to a <see cref="Beatmap"/>
/// after conversion and post-processing has completed.
/// </summary>
public interface IApplicableToBeatmap<TObject> : IApplicableMod
where TObject : HitObject
{
/// <summary>
/// Applies this <see cref="IApplicableToBeatmap{TObject}"/> to a <see cref="Beatmap{TObject}"/>.
/// </summary>
/// <param name="beatmap">The <see cref="Beatmap{TObject}"/> to apply to.</param>
void ApplyToBeatmap(Beatmap<TObject> beatmap);
}
}

View File

@ -238,6 +238,8 @@ namespace osu.Game.Rulesets.UI
KeyBindingInputManager = CreateInputManager();
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
applyBeatmapMods(Mods);
}
[BackgroundDependencyLoader]
@ -255,16 +257,29 @@ namespace osu.Game.Rulesets.UI
KeyBindingInputManager.Add(Cursor);
// Apply mods
applyMods(Mods, config);
applyRulesetMods(Mods, config);
loadObjects();
}
/// <summary>
/// Applies the active mods to the Beatmap.
/// </summary>
/// <param name="mods"></param>
private void applyBeatmapMods(IEnumerable<Mod> mods)
{
if (mods == null)
return;
foreach (var mod in mods.OfType<IApplicableToBeatmap<TObject>>())
mod.ApplyToBeatmap(Beatmap);
}
/// <summary>
/// Applies the active mods to this RulesetContainer.
/// </summary>
/// <param name="mods"></param>
private void applyMods(IEnumerable<Mod> mods, OsuConfigManager config)
private void applyRulesetMods(IEnumerable<Mod> mods, OsuConfigManager config)
{
if (mods == null)
return;

View File

@ -134,11 +134,13 @@ namespace osu.Game.Screens.Play
{
PlayerSettingsOverlay.Show();
ModDisplay.FadeIn(200);
KeyCounter.Margin = new MarginPadding(10) { Bottom = 30 };
}
else
{
PlayerSettingsOverlay.Hide();
ModDisplay.Delay(2000).FadeOut(200);
KeyCounter.Margin = new MarginPadding(10);
}
}

View File

@ -21,7 +21,7 @@ namespace osu.Game.Screens.Play
{
private const int bottom_bar_height = 5;
private static readonly Vector2 handle_size = new Vector2(14, 25);
private static readonly Vector2 handle_size = new Vector2(10, 18);
private const float transition_duration = 200;
@ -135,6 +135,8 @@ namespace osu.Game.Screens.Play
{
bar.FadeTo(allowSeeking ? 1 : 0, transition_duration, Easing.In);
this.MoveTo(new Vector2(0, allowSeeking ? 0 : bottom_bar_height), transition_duration, Easing.In);
info.Margin = new MarginPadding { Bottom = Height - (allowSeeking ? 0 : handle_size.Y) };
}
protected override void PopIn()

View File

@ -307,10 +307,10 @@ namespace osu.Game.Screens.Select
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Alpha = 0;
InternalChild = textContainer = new FillFlowContainer
{
Alpha = 0,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(spacing / 2),
@ -337,7 +337,7 @@ namespace osu.Game.Screens.Select
{
if (string.IsNullOrEmpty(value))
{
this.FadeOut(transition_duration);
textContainer.FadeOut(transition_duration);
return;
}
@ -345,8 +345,6 @@ namespace osu.Game.Screens.Select
}
}
public override bool IsPresent => base.IsPresent || textFlow == null; // Visibility is updated in the LoadComponentAsync callback
private void setTextAsync(string text)
{
LoadComponentAsync(new OsuTextFlowContainer(s => s.TextSize = 14)
@ -361,7 +359,7 @@ namespace osu.Game.Screens.Select
textContainer.Add(textFlow = loaded);
// fade in if we haven't yet.
this.FadeIn(transition_duration);
textContainer.FadeIn(transition_duration);
});
}
}

View File

@ -16,7 +16,6 @@ using osu.Game.Graphics;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Skinning;
@ -68,7 +67,7 @@ namespace osu.Game.Screens.Select
BeatmapOptions.AddButton(@"Edit", @"beatmap", FontAwesome.fa_pencil, colours.Yellow, () =>
{
ValidForResume = false;
Push(new Editor());
Edit();
}, Key.Number3);
if (dialogOverlay != null)

View File

@ -223,9 +223,9 @@ namespace osu.Game.Screens.Select
Carousel.LoadBeatmapSetsFromManager(this.beatmaps);
}
public void Edit(BeatmapInfo beatmap)
public void Edit(BeatmapInfo beatmap = null)
{
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap.Value);
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap ?? beatmapNoDebounce);
Push(new Editor());
}

View File

@ -12,6 +12,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
using osu.Game.Database;
using osu.Game.Graphics.Sprites;
using OpenTK;
namespace osu.Game.Skinning
@ -56,30 +57,39 @@ namespace osu.Game.Skinning
case "Play/Great":
componentName = "hit300";
break;
case "Play/osu/number-text":
return !hasFont(Configuration.HitCircleFont) ? null : new LegacySpriteText(Textures, Configuration.HitCircleFont) { Scale = new Vector2(0.96f) };
}
float ratio = 0.72f; // brings sizing roughly in-line with stable
var texture = GetTexture(componentName);
var texture = GetTexture($"{componentName}@2x");
if (texture == null)
{
ratio *= 2;
texture = GetTexture(componentName);
}
return null;
if (texture == null) return null;
return new Sprite
{
Texture = texture,
Scale = new Vector2(ratio),
};
return new Sprite { Texture = texture };
}
public override Texture GetTexture(string componentName) => Textures.Get(componentName);
public override Texture GetTexture(string componentName)
{
float ratio = 2;
var texture = Textures.Get($"{componentName}@2x");
if (texture == null)
{
ratio = 1;
texture = Textures.Get(componentName);
}
if (texture != null)
texture.ScaleAdjust = ratio / 0.72f; // brings sizing roughly in-line with stable
return texture;
}
public override SampleChannel GetSample(string sampleName) => Samples.Get(sampleName);
private bool hasFont(string fontName) => GetTexture($"{fontName}-0") != null;
protected class LegacySkinResourceStore<T> : IResourceStore<byte[]>
where T : INamedFileInfo
{
@ -142,5 +152,40 @@ namespace osu.Game.Skinning
#endregion
}
private class LegacySpriteText : OsuSpriteText
{
private readonly TextureStore textures;
private readonly string font;
public LegacySpriteText(TextureStore textures, string font)
{
this.textures = textures;
this.font = font;
Shadow = false;
UseFullGlyphHeight = false;
}
protected override Texture GetTextureForCharacter(char c)
{
string textureName = $"{font}-{c}";
// Approximate value that brings character sizing roughly in-line with stable
float ratio = 36;
var texture = textures.Get($"{textureName}@2x");
if (texture == null)
{
ratio = 18;
texture = textures.Get(textureName);
}
if (texture != null)
texture.ScaleAdjust = ratio;
return texture;
}
}
}
}

View File

@ -19,6 +19,7 @@ namespace osu.Game.Skinning
switch (section)
{
case Section.General:
{
var pair = SplitKeyVal(line);
switch (pair.Key)
@ -32,6 +33,20 @@ namespace osu.Game.Skinning
}
break;
}
case Section.Fonts:
{
var pair = SplitKeyVal(line);
switch (pair.Key)
{
case "HitCirclePrefix":
skin.HitCircleFont = pair.Value;
break;
}
break;
}
}
base.ParseLine(skin, section, line);

View File

@ -14,5 +14,7 @@ namespace osu.Game.Skinning
public List<Color4> ComboColours { get; set; } = new List<Color4>();
public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>();
public string HitCircleFont { get; set; } = "default";
}
}

View File

@ -18,6 +18,11 @@ namespace osu.Game.Skinning
public class SkinnableDrawable<T> : SkinReloadableDrawable
where T : Drawable
{
/// <summary>
/// The displayed component. May or may not be a type-<typeparamref name="T"/> member.
/// </summary>
protected Drawable Drawable { get; private set; }
private readonly Func<string, T> createDefault;
private readonly string componentName;
@ -31,7 +36,8 @@ namespace osu.Game.Skinning
/// <param name="defaultImplementation">A function to create the default skin implementation of this element.</param>
/// <param name="allowFallback">A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present.</param>
/// <param name="restrictSize">Whether a user-skin drawable should be limited to the size of our parent.</param>
public SkinnableDrawable(string name, Func<string, T> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, bool restrictSize = true) : base(allowFallback)
public SkinnableDrawable(string name, Func<string, T> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, bool restrictSize = true)
: base(allowFallback)
{
componentName = name;
createDefault = defaultImplementation;
@ -42,26 +48,27 @@ namespace osu.Game.Skinning
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
var drawable = skin.GetDrawableComponent(componentName);
if (drawable != null)
Drawable = skin.GetDrawableComponent(componentName);
if (Drawable != null)
{
if (restrictSize)
{
drawable.RelativeSizeAxes = Axes.Both;
drawable.Size = Vector2.One;
drawable.Scale = Vector2.One;
drawable.FillMode = FillMode.Fit;
Drawable.RelativeSizeAxes = Axes.Both;
Drawable.Size = Vector2.One;
Drawable.Scale = Vector2.One;
Drawable.FillMode = FillMode.Fit;
}
}
else if (allowFallback)
drawable = createDefault(componentName);
Drawable = createDefault(componentName);
if (drawable != null)
if (Drawable != null)
{
drawable.Origin = Anchor.Centre;
drawable.Anchor = Anchor.Centre;
Drawable.Origin = Anchor.Centre;
Drawable.Anchor = Anchor.Centre;
InternalChild = drawable;
InternalChild = Drawable;
}
else
ClearInternal();

View File

@ -0,0 +1,40 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics.Sprites;
namespace osu.Game.Skinning
{
public class SkinnableSpriteText : SkinnableDrawable<SpriteText>, IHasText
{
public SkinnableSpriteText(string name, Func<string, SpriteText> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, bool restrictSize = true)
: base(name, defaultImplementation, allowFallback, restrictSize)
{
}
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
if (Drawable is IHasText textDrawable)
textDrawable.Text = Text;
}
private string text;
public string Text
{
get => text;
set
{
if (text == value)
return;
text = value;
if (Drawable is IHasText textDrawable)
textDrawable.Text = value;
}
}
}
}

View File

@ -18,7 +18,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.4" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="ppy.osu.Framework" Version="2018.1018.0" />
<PackageReference Include="ppy.osu.Framework" Version="2018.1030.0" />
<PackageReference Include="SharpCompress" Version="0.22.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />

11
tools/cakebuild.csproj Normal file
View File

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<PackAsTool>true</PackAsTool>
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Cake" Version="0.30.0" />
<PackageReference Include="Cake.CoreCLR" Version="0.30.0" />
</ItemGroup>
</Project>