mirror of
https://github.com/ppy/osu.git
synced 2025-01-28 02:02:53 +08:00
Merge remote-tracking branch 'upstream/master' into reduce-handle-height
This commit is contained in:
commit
4502fa39e9
7
.gitignore
vendored
7
.gitignore
vendored
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
20
appveyor.yml
20
appveyor.yml
@ -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
|
||||
|
@ -1,30 +0,0 @@
|
||||
clone_depth: 1
|
||||
version: '{build}'
|
||||
skip_non_tags: true
|
||||
image: Visual Studio 2017
|
||||
install:
|
||||
- git clone https://github.com/ppy/osu-deploy
|
||||
before_build:
|
||||
- ps: if($env:appveyor_repo_tag -eq 'True') { Update-AppveyorBuild -Version $env:appveyor_repo_tag_name }
|
||||
- cmd: git submodule update --init --recursive --depth=5
|
||||
- cmd: nuget restore -verbosity quiet
|
||||
build_script:
|
||||
- ps: iex ((New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/appveyor/secure-file/master/install.ps1'))
|
||||
- appveyor DownloadFile https://www.dropbox.com/s/f7hv069mr5tqi2j/deanherbert.pfx.enc?dl=1 # signing certificate
|
||||
- cmd: appveyor-tools\secure-file -decrypt deanherbert.pfx.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx
|
||||
- appveyor DownloadFile https://puu.sh/A6g75/fdc6f19b04.enc # deploy configuration
|
||||
- cd osu-deploy
|
||||
- nuget restore -verbosity quiet
|
||||
- msbuild osu.Desktop.Deploy.csproj
|
||||
- cmd: ..\appveyor-tools\secure-file -decrypt ..\fdc6f19b04.enc -secret %decode_secret% -out bin\Debug\netcoreapp2.1\osu.Desktop.Deploy.dll.config
|
||||
- dotnet bin/Debug/netcoreapp2.1/osu.Desktop.Deploy.dll %code_signing_password% %APPVEYOR_REPO_TAG_NAME%
|
||||
environment:
|
||||
decode_secret:
|
||||
secure: i67IC2xj6DjjxmA6Oj2jing3+MwzLkq6CbGsjfZ7rdY=
|
||||
code_signing_password:
|
||||
secure: 34tLNqvjmmZEi97MLKfrnQ==
|
||||
artifacts:
|
||||
- path: 'osu-deploy/releases/*'
|
||||
deploy:
|
||||
- provider: Environment
|
||||
name: github
|
72
build.cake
Normal file
72
build.cake
Normal 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
79
build.ps1
Normal 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
37
build.sh
Normal 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
5
cake.config
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
[Nuget]
|
||||
Source=https://api.nuget.org/v3/index.json
|
||||
UseInProcessClient=true
|
||||
LoadDependencies=true
|
@ -27,7 +27,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="System.IO.Packaging" Version="4.5.0" />
|
||||
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.1" />
|
||||
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
|
@ -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;
|
||||
|
@ -43,7 +43,10 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
Position = positionData?.Position ?? Vector2.Zero,
|
||||
NewCombo = comboData?.NewCombo ?? false,
|
||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset
|
||||
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset,
|
||||
// prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance.
|
||||
// this results in more (or less) ticks being generated in <v8 maps for the same time duration.
|
||||
TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / beatmap.ControlPointInfo.DifficultyPointAt(original.StartTime).SpeedMultiplier : 1
|
||||
};
|
||||
}
|
||||
else if (endTimeData != null)
|
||||
|
@ -75,15 +75,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
||||
{
|
||||
computeSliderCursorPosition(lastSlider);
|
||||
lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition;
|
||||
|
||||
TravelDistance = lastSlider.LazyTravelDistance * scalingFactor;
|
||||
}
|
||||
|
||||
// Don't need to jump to reach spinners
|
||||
if (!(BaseObject is Spinner))
|
||||
JumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length;
|
||||
|
||||
// Todo: BUG!!! Last slider's travel distance is considered ONLY IF we ourselves are also a slider!
|
||||
if (BaseObject is Slider)
|
||||
TravelDistance = (lastSlider?.LazyTravelDistance ?? 0) * scalingFactor;
|
||||
}
|
||||
|
||||
private void setTimingValues()
|
||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
||||
{
|
||||
public class SliderMask : HitObjectMask
|
||||
{
|
||||
private readonly SliderBody body;
|
||||
private readonly SnakingSliderBody body;
|
||||
private readonly DrawableSlider slider;
|
||||
|
||||
public SliderMask(DrawableSlider slider)
|
||||
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
body = new SliderBody(sliderObject)
|
||||
body = new SnakingSliderBody(sliderObject)
|
||||
{
|
||||
AccentColour = Color4.Transparent,
|
||||
PathWidth = sliderObject.Scale * 64
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -92,8 +92,21 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
/// </summary>
|
||||
public double SpanDuration => Duration / this.SpanCount();
|
||||
|
||||
public double Velocity;
|
||||
public double TickDistance;
|
||||
/// <summary>
|
||||
/// Velocity of this <see cref="Slider"/>.
|
||||
/// </summary>
|
||||
public double Velocity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Spacing between <see cref="SliderTick"/>s of this <see cref="Slider"/>.
|
||||
/// </summary>
|
||||
public double TickDistance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// An extra multiplier that affects the number of <see cref="SliderTick"/>s generated by this <see cref="Slider"/>.
|
||||
/// An increase in this value increases <see cref="TickDistance"/>, which reduces the number of ticks generated.
|
||||
/// </summary>
|
||||
public double TickDistanceMultiplier = 1;
|
||||
|
||||
public HitCircle HeadCircle;
|
||||
public SliderTailCircle TailCircle;
|
||||
@ -108,7 +121,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;
|
||||
|
||||
Velocity = scoringDistance / timingPoint.BeatLength;
|
||||
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
||||
TickDistance = scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier;
|
||||
}
|
||||
|
||||
protected override void CreateNestedHitObjects()
|
||||
|
@ -8,11 +8,13 @@ using OpenTK.Graphics;
|
||||
using osu.Game.Tests.Resources;
|
||||
using System.Linq;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
@ -21,6 +23,25 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
[TestFixture]
|
||||
public class LegacyBeatmapDecoderTest
|
||||
{
|
||||
[Test]
|
||||
public void TestDecodeBeatmapVersion()
|
||||
{
|
||||
using (var resStream = Resource.OpenResource("beatmap-version.osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var decoder = Decoder.GetDecoder<Beatmap>(stream);
|
||||
|
||||
stream.BaseStream.Position = 0;
|
||||
stream.DiscardBufferedData();
|
||||
|
||||
var working = new TestWorkingBeatmap(decoder.Decode(stream));
|
||||
|
||||
Assert.AreEqual(6, working.BeatmapInfo.BeatmapVersion);
|
||||
Assert.AreEqual(6, working.Beatmap.BeatmapInfo.BeatmapVersion);
|
||||
Assert.AreEqual(6, working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapVersion);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeBeatmapGeneral()
|
||||
{
|
||||
|
1
osu.Game.Tests/Resources/beatmap-version.osu
Normal file
1
osu.Game.Tests/Resources/beatmap-version.osu
Normal file
@ -0,0 +1 @@
|
||||
osu file format v6
|
@ -49,9 +49,7 @@ namespace osu.Game.Tests.Visual
|
||||
Vector2.Zero,
|
||||
new Vector2(216, 0),
|
||||
},
|
||||
Distance = 400,
|
||||
Velocity = 1,
|
||||
TickDistance = 100,
|
||||
Distance = 216,
|
||||
Scale = 0.5f,
|
||||
}
|
||||
},
|
||||
|
@ -19,7 +19,6 @@ namespace osu.Game.Beatmaps
|
||||
[JsonIgnore]
|
||||
public int ID { get; set; }
|
||||
|
||||
//TODO: should be in database
|
||||
public int BeatmapVersion;
|
||||
|
||||
private int? onlineBeatmapID;
|
||||
|
@ -148,11 +148,12 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
/// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
|
||||
/// <param name="noVideo">Whether the beatmap should be downloaded without video. Defaults to false.</param>
|
||||
public void Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false)
|
||||
/// <returns>Downloading can happen</returns>
|
||||
public bool Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false)
|
||||
{
|
||||
var existing = GetExistingDownload(beatmapSetInfo);
|
||||
|
||||
if (existing != null || api == null) return;
|
||||
if (existing != null || api == null) return false;
|
||||
|
||||
if (!api.LocalUser.Value.IsSupporter)
|
||||
{
|
||||
@ -161,7 +162,7 @@ namespace osu.Game.Beatmaps
|
||||
Icon = FontAwesome.fa_superpowers,
|
||||
Text = "You gotta be an osu!supporter to download for now 'yo"
|
||||
});
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
var downloadNotification = new DownloadNotification
|
||||
@ -227,6 +228,7 @@ namespace osu.Game.Beatmaps
|
||||
// don't run in the main api queue as this is a long-running task.
|
||||
Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
|
||||
BeatmapDownloadBegan?.Invoke(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void PresentCompletedImport(IEnumerable<BeatmapSetInfo> imported)
|
||||
|
@ -71,9 +71,11 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
if (DownloadState.Value > DownloadStatus.NotDownloaded)
|
||||
return;
|
||||
|
||||
beatmaps.Download(set, noVideo);
|
||||
|
||||
DownloadState.Value = DownloadStatus.Downloading;
|
||||
if (beatmaps.Download(set, noVideo))
|
||||
{
|
||||
// Only change state if download can happen
|
||||
DownloadState.Value = DownloadStatus.Downloading;
|
||||
}
|
||||
}
|
||||
|
||||
private void setAdded(BeatmapSetInfo s)
|
||||
|
@ -41,8 +41,13 @@ namespace osu.Game.Beatmaps
|
||||
beatmap = new RecyclableLazy<IBeatmap>(() =>
|
||||
{
|
||||
var b = GetBeatmap() ?? new Beatmap();
|
||||
// use the database-backed info.
|
||||
|
||||
// The original beatmap version needs to be preserved as the database doesn't contain it
|
||||
BeatmapInfo.BeatmapVersion = b.BeatmapInfo.BeatmapVersion;
|
||||
|
||||
// Use the database-backed info for more up-to-date values (beatmap id, ranked status, etc)
|
||||
b.BeatmapInfo = BeatmapInfo;
|
||||
|
||||
return b;
|
||||
});
|
||||
|
||||
|
@ -1,16 +1,14 @@
|
||||
// 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 System.ComponentModel;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Direct;
|
||||
using osu.Game.Rulesets;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class SearchBeatmapSetsRequest : APIRequest<IEnumerable<APIBeatmapSet>>
|
||||
public class SearchBeatmapSetsRequest : APIRequest<SearchBeatmapSetsResponse>
|
||||
{
|
||||
private readonly string query;
|
||||
private readonly RulesetInfo ruleset;
|
||||
@ -35,6 +33,7 @@ namespace osu.Game.Online.API.Requests
|
||||
public enum BeatmapSearchCategory
|
||||
{
|
||||
Any = 7,
|
||||
|
||||
[Description("Ranked & Approved")]
|
||||
RankedApproved = 0,
|
||||
Approved = 1,
|
||||
@ -43,6 +42,7 @@ namespace osu.Game.Online.API.Requests
|
||||
Qualified = 3,
|
||||
Pending = 4,
|
||||
Graveyard = 5,
|
||||
|
||||
[Description("My Maps")]
|
||||
MyMaps = 6,
|
||||
}
|
||||
|
20
osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs
Normal file
20
osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs
Normal 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 Newtonsoft.Json;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class SearchBeatmapSetsResponse
|
||||
{
|
||||
public IEnumerable<APIBeatmapSet> BeatmapSets;
|
||||
|
||||
/// <summary>
|
||||
/// A collection of parameters which should be passed to the search endpoint to fetch the next page.
|
||||
/// </summary>
|
||||
[JsonProperty("cursor")]
|
||||
public dynamic CursorJson;
|
||||
}
|
||||
}
|
@ -288,7 +288,7 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
var sets = response.Select(r => r.ToBeatmapSet(rulesets)).ToList();
|
||||
var sets = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList();
|
||||
|
||||
// may not need scheduling; loads async internally.
|
||||
Schedule(() =>
|
||||
|
22
osu.Game/Rulesets/Mods/IApplicableToBeatmap.cs
Normal file
22
osu.Game/Rulesets/Mods/IApplicableToBeatmap.cs
Normal 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
40
osu.Game/Skinning/SkinnableSpriteText.cs
Normal file
40
osu.Game/Skinning/SkinnableSpriteText.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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.1012.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
11
tools/cakebuild.csproj
Normal 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>
|
Loading…
Reference in New Issue
Block a user