1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-07 00:42:54 +08:00

Merge branch 'master' into mania-column-background-skinning

This commit is contained in:
Dean Herbert 2020-03-31 21:29:37 +09:00 committed by GitHub
commit fe35f696c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 187 additions and 46 deletions

View File

@ -5,6 +5,6 @@
"version": "3.1.100"
},
"msbuild-sdks": {
"Microsoft.Build.Traversal": "2.0.24"
"Microsoft.Build.Traversal": "2.0.32"
}
}

View File

@ -52,6 +52,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.327.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.331.0" />
</ItemGroup>
</Project>

View File

@ -72,10 +72,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
protected override Skill[] CreateSkills(IBeatmap beatmap)
{
using (var catcher = new Catcher(beatmap.BeatmapInfo.BaseDifficulty))
{
halfCatcherWidth = catcher.CatchWidth * 0.5f;
halfCatcherWidth *= 0.8f; // We're only using 80% of the catcher's width to simulate imperfect gameplay.
}
return new Skill[]
{

View File

@ -37,10 +37,15 @@ namespace osu.Game.Rulesets.Catch.UI
public CatcherAnimationState CurrentState { get; private set; }
/// <summary>
/// The width of the catcher which can receive fruit. Equivalent to "catchMargin" in osu-stable.
/// </summary>
private const float allowed_catch_range = 0.8f;
/// <summary>
/// Width of the area that can be used to attempt catches during gameplay.
/// </summary>
internal float CatchWidth => CatcherArea.CATCHER_SIZE * Math.Abs(Scale.X);
internal float CatchWidth => CatcherArea.CATCHER_SIZE * Math.Abs(Scale.X) * allowed_catch_range;
protected bool Dashing
{

View File

@ -0,0 +1,41 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.UI;
namespace osu.Game.Rulesets.Mania.Skinning
{
/// <summary>
/// A <see cref="CompositeDrawable"/> which is placed somewhere within a <see cref="Column"/>.
/// </summary>
public class LegacyManiaColumnElement : CompositeDrawable
{
[Resolved(CanBeNull = true)]
[CanBeNull]
protected ManiaStage Stage { get; private set; }
[Resolved]
protected Column Column { get; private set; }
/// <summary>
/// The column index to use for texture lookups, in the case of no user-provided configuration.
/// </summary>
protected int FallbackColumnIndex { get; private set; }
[BackgroundDependencyLoader]
private void load()
{
if (Stage == null)
FallbackColumnIndex = Column.Index % 2 + 1;
else
{
int dist = Math.Min(Column.Index, Stage.Columns.Count - Column.Index - 1);
FallbackColumnIndex = dist % 2 + 1;
}
}
}
}

View File

@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Mods
void handleHitCircle(DrawableHitCircle circle)
{
if (!circle.IsHovered)
if (!circle.HitArea.IsHovered)
return;
Debug.Assert(circle.HitObject.HitWindows != null);

View File

@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
/// <summary>
/// A single follow point positioned between two adjacent <see cref="DrawableOsuHitObject"/>s.
/// </summary>
public class FollowPoint : Container
public class FollowPoint : Container, IAnimationTimeReference
{
private const float width = 8;
@ -45,5 +45,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
}
}, confineMode: ConfineMode.NoScaling);
}
public double AnimationStartTime { get; set; }
}
}

View File

@ -116,6 +116,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
int point = 0;
ClearInternal();
for (int d = (int)(spacing * 1.5); d < distance - spacing; d += spacing)
{
float fraction = (float)d / distance;
@ -126,13 +128,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
FollowPoint fp;
if (InternalChildren.Count > point)
{
fp = (FollowPoint)InternalChildren[point];
fp.ClearTransforms();
}
else
AddInternal(fp = new FollowPoint());
AddInternal(fp = new FollowPoint());
fp.Position = pointStartPosition;
fp.Rotation = rotation;
@ -142,6 +138,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
if (firstTransformStartTime == null)
firstTransformStartTime = fadeInTime;
fp.AnimationStartTime = fadeInTime;
using (fp.BeginAbsoluteSequence(fadeInTime))
{
fp.FadeIn(osuEnd.TimeFadeIn);

View File

@ -62,6 +62,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
}
};
bool overlayAboveNumber = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true;
if (!overlayAboveNumber)
ChangeInternalChildDepth(hitCircleText, -float.MaxValue);
state.BindTo(drawableObject.State);
state.BindValueChanged(updateState, true);

View File

@ -11,6 +11,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
SliderPathRadius,
AllowSliderBallTint,
CursorExpand,
CursorRotate
CursorRotate,
HitCircleOverlayAboveNumber
}
}

View File

@ -87,7 +87,7 @@ namespace osu.Game.Beatmaps
protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osz";
protected override Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default)
protected override async Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default)
{
if (archive != null)
beatmapSet.Beatmaps = createBeatmapDifficulties(beatmapSet.Files);
@ -103,7 +103,19 @@ namespace osu.Game.Beatmaps
validateOnlineIds(beatmapSet);
return updateQueue.UpdateAsync(beatmapSet, cancellationToken);
bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0);
await updateQueue.UpdateAsync(beatmapSet, cancellationToken);
// ensure at least one beatmap was able to retrieve or keep an online ID, else drop the set ID.
if (hadOnlineBeatmapIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0))
{
if (beatmapSet.OnlineBeatmapSetID != null)
{
beatmapSet.OnlineBeatmapSetID = null;
LogForModel(beatmapSet, "Disassociating beatmap set ID due to loss of all beatmap IDs");
}
}
}
protected override void PreImport(BeatmapSetInfo beatmapSet)
@ -447,12 +459,15 @@ namespace osu.Game.Beatmaps
var res = req.Result;
beatmap.Status = res.Status;
beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
if (res != null)
{
beatmap.Status = res.Status;
beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.");
LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.");
}
}
catch (Exception e)
{

View File

@ -12,11 +12,11 @@ namespace osu.Game.Online.API
/// An API request with a well-defined response type.
/// </summary>
/// <typeparam name="T">Type of the response (used for deserialisation).</typeparam>
public abstract class APIRequest<T> : APIRequest
public abstract class APIRequest<T> : APIRequest where T : class
{
protected override WebRequest CreateWebRequest() => new OsuJsonWebRequest<T>(Uri);
public T Result => ((OsuJsonWebRequest<T>)WebRequest).ResponseObject;
public T Result => ((OsuJsonWebRequest<T>)WebRequest)?.ResponseObject;
protected APIRequest()
{

View File

@ -6,7 +6,7 @@ using osu.Game.Rulesets;
namespace osu.Game.Online.API.Requests
{
public abstract class GetRankingsRequest<TModel> : APIRequest<TModel>
public abstract class GetRankingsRequest<TModel> : APIRequest<TModel> where TModel : class
{
private readonly RulesetInfo ruleset;
private readonly int page;

View File

@ -6,7 +6,7 @@ using osu.Framework.IO.Network;
namespace osu.Game.Online.API.Requests
{
public abstract class PaginatedAPIRequest<T> : APIRequest<T>
public abstract class PaginatedAPIRequest<T> : APIRequest<T> where T : class
{
private readonly int page;
private readonly int itemsPerPage;

View File

@ -28,10 +28,11 @@ namespace osu.Game.Scoring.Legacy
{
var score = new Score
{
ScoreInfo = new ScoreInfo(),
Replay = new Replay()
};
WorkingBeatmap workingBeatmap;
using (SerializationReader sr = new SerializationReader(stream))
{
currentRuleset = GetRuleset(sr.ReadByte());
@ -41,7 +42,7 @@ namespace osu.Game.Scoring.Legacy
var version = sr.ReadInt32();
var workingBeatmap = GetBeatmap(sr.ReadString());
workingBeatmap = GetBeatmap(sr.ReadString());
if (workingBeatmap is DummyWorkingBeatmap)
throw new BeatmapNotFoundException();
@ -113,6 +114,10 @@ namespace osu.Game.Scoring.Legacy
CalculateAccuracy(score.ScoreInfo);
// before returning for database import, we must restore the database-sourced BeatmapInfo.
// if not, the clone operation in GetPlayableBeatmap will cause a dereference and subsequent database exception.
score.ScoreInfo.Beatmap = workingBeatmap.BeatmapInfo;
return score;
}

View File

@ -3,6 +3,7 @@
using osu.Framework.Screens;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
namespace osu.Game.Screens.Play
{
@ -29,6 +30,8 @@ namespace osu.Game.Screens.Play
this.Push(CreateResults(DrawableRuleset.ReplayScore.ScoreInfo));
}
protected override ResultsScreen CreateResults(ScoreInfo score) => new ResultsScreen(score, false);
protected override ScoreInfo CreateScore() => score.ScoreInfo;
}
}

View File

@ -33,16 +33,21 @@ namespace osu.Game.Screens.Ranking
public readonly ScoreInfo Score;
private readonly bool allowRetry;
private Drawable bottomPanel;
public ResultsScreen(ScoreInfo score)
public ResultsScreen(ScoreInfo score, bool allowRetry = true)
{
Score = score;
this.allowRetry = allowRetry;
}
[BackgroundDependencyLoader]
private void load()
{
FillFlowContainer buttons;
InternalChildren = new[]
{
new ResultsScrollContainer
@ -68,7 +73,7 @@ namespace osu.Game.Screens.Ranking
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#333")
},
new FillFlowContainer
buttons = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -78,15 +83,16 @@ namespace osu.Game.Screens.Ranking
Children = new Drawable[]
{
new ReplayDownloadButton(Score) { Width = 300 },
new RetryButton { Width = 300 },
}
}
}
}
};
if (player != null)
if (player != null && allowRetry)
{
buttons.Add(new RetryButton { Width = 300 });
AddInternal(new HotkeyRetryOverlay
{
Action = () =>

View File

@ -0,0 +1,25 @@
// 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.Timing;
namespace osu.Game.Skinning
{
/// <summary>
/// Denotes an object which provides a reference time to start animations from.
/// </summary>
[Cached]
public interface IAnimationTimeReference
{
/// <summary>
/// The reference clock.
/// </summary>
IFrameBasedClock Clock { get; }
/// <summary>
/// The time which animations should be started from, relative to <see cref="Clock"/>.
/// </summary>
double AnimationStartTime { get; }
}
}

View File

@ -3,10 +3,12 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Timing;
namespace osu.Game.Skinning
{
@ -22,7 +24,7 @@ namespace osu.Game.Skinning
if (textures.Length > 0)
{
var animation = new TextureAnimation
var animation = new SkinnableTextureAnimation
{
DefaultFrameLength = getFrameLength(source, applyConfigFrameRate, textures),
Repeat = looping,
@ -53,6 +55,25 @@ namespace osu.Game.Skinning
}
}
public class SkinnableTextureAnimation : TextureAnimation
{
[Resolved(canBeNull: true)]
private IAnimationTimeReference timeReference { get; set; }
public SkinnableTextureAnimation()
: base(false)
{
}
protected override void LoadComplete()
{
base.LoadComplete();
if (timeReference != null)
Clock = new FramedOffsetClock(timeReference.Clock) { Offset = -timeReference.AnimationStartTime };
}
}
private const double default_frame_time = 1000 / 60d;
private static double getFrameLength(ISkin source, bool applyConfigFrameRate, Texture[] textures)

View File

@ -50,7 +50,7 @@ namespace osu.Game.Storyboards.Drawables
AddInternal(Content = new Container<DrawableStoryboardLayer>
{
Size = new Vector2(640, 480),
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});

View File

@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers;
namespace osu.Game.Storyboards.Drawables
{
public class DrawableStoryboardLayer : LifetimeManagementContainer
public class DrawableStoryboardLayer : CompositeDrawable
{
public StoryboardLayer Layer { get; }
public bool Enabled;
@ -23,17 +23,34 @@ namespace osu.Game.Storyboards.Drawables
Origin = Anchor.Centre;
Enabled = layer.VisibleWhenPassing;
Masking = layer.Masking;
InternalChild = new LayerElementContainer(layer);
}
[BackgroundDependencyLoader]
private void load(CancellationToken? cancellationToken)
private class LayerElementContainer : LifetimeManagementContainer
{
foreach (var element in Layer.Elements)
{
cancellationToken?.ThrowIfCancellationRequested();
private readonly StoryboardLayer storyboardLayer;
if (element.IsDrawable)
AddInternal(element.CreateDrawable());
public LayerElementContainer(StoryboardLayer layer)
{
storyboardLayer = layer;
Width = 640;
Height = 480;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
}
[BackgroundDependencyLoader]
private void load(CancellationToken? cancellationToken)
{
foreach (var element in storyboardLayer.Elements)
{
cancellationToken?.ThrowIfCancellationRequested();
if (element.IsDrawable)
AddInternal(element.CreateDrawable());
}
}
}
}

View File

@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.327.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.331.0" />
<PackageReference Include="Sentry" Version="2.1.1" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />

View File

@ -71,7 +71,7 @@
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.327.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.331.0" />
</ItemGroup>
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
<ItemGroup Label="Transitive Dependencies">
@ -79,7 +79,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2020.327.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.331.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />