1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 23:12:56 +08:00

Merge branch 'master' into fix-mania-conversion

This commit is contained in:
Dan Balasescu 2018-03-09 16:09:14 +09:00 committed by GitHub
commit ac9de336db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 1369 additions and 381 deletions

@ -1 +1 @@
Subproject commit 71900dc350bcebbb60d912d4023a1d2a6bbbc3c1
Subproject commit 6372fb22c1c85f600921a139849b8dedf71026d5

View File

@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Catch
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap);
public override int LegacyID => 2;
public override int? LegacyID => 2;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();

View File

@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Mania
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap);
public override int LegacyID => 3;
public override int? LegacyID => 3;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();

View File

@ -50,10 +50,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdatePreemptState()
{
this.Animate(
d => d.FadeIn(ANIM_DURATION),
d => d.ScaleTo(0.5f).ScaleTo(1f, ANIM_DURATION * 4, Easing.OutElasticHalf)
);
this.FadeOut().FadeIn(ANIM_DURATION);
this.ScaleTo(0.5f).ScaleTo(1f, ANIM_DURATION * 4, Easing.OutElasticHalf);
}
protected override void UpdateCurrentState(ArmedState state)
@ -64,12 +62,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
this.Delay(HitObject.TimePreempt).FadeOut();
break;
case ArmedState.Miss:
this.FadeOut(ANIM_DURATION)
.FadeColour(Color4.Red, ANIM_DURATION / 2);
this.FadeOut(ANIM_DURATION);
this.FadeColour(Color4.Red, ANIM_DURATION / 2);
break;
case ArmedState.Hit:
this.FadeOut(ANIM_DURATION, Easing.OutQuint)
.ScaleTo(Scale * 1.5f, ANIM_DURATION, Easing.Out);
this.FadeOut(ANIM_DURATION, Easing.OutQuint);
this.ScaleTo(Scale * 1.5f, ANIM_DURATION, Easing.Out);
break;
}
}

View File

@ -1,6 +1,7 @@
// 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.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.ControlPoints;
@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Objects
SpinsRequired = (int)(Duration / 1000 * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5));
// spinning doesn't match 1:1 with stable, so let's fudge them easier for the time being.
SpinsRequired = (int)(SpinsRequired * 0.6);
SpinsRequired = (int)Math.Max(1, SpinsRequired * 0.6);
}
}
}

View File

@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Osu
public override SettingsSubsection CreateSettings() => new OsuSettings();
public override int LegacyID => 0;
public override int? LegacyID => 0;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame();

View File

@ -68,6 +68,8 @@ namespace osu.Game.Rulesets.Osu.Scoring
score.Statistics[HitResult.Miss] = scoreResultCounts.GetOrDefault(HitResult.Miss);
}
private const double harshness = 0.01;
protected override void OnNewJudgement(Judgement judgement)
{
base.OnNewJudgement(judgement);
@ -83,15 +85,15 @@ namespace osu.Game.Rulesets.Osu.Scoring
switch (judgement.Result)
{
case HitResult.Great:
Health.Value += (10.2 - hpDrainRate) * 0.02;
Health.Value += (10.2 - hpDrainRate) * harshness;
break;
case HitResult.Good:
Health.Value += (8 - hpDrainRate) * 0.02;
Health.Value += (8 - hpDrainRate) * harshness;
break;
case HitResult.Meh:
Health.Value += (4 - hpDrainRate) * 0.02;
Health.Value += (4 - hpDrainRate) * harshness;
break;
/*case HitResult.SliderTick:
@ -99,7 +101,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
break;*/
case HitResult.Miss:
Health.Value -= hpDrainRate * 0.04;
Health.Value -= hpDrainRate * (harshness * 2);
break;
}
}

View File

@ -101,16 +101,16 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
// The duration of the taiko hit object
double taikoDuration = distance / taikoVelocity;
// For some reason, old osu! always uses speedAdjustment to determine the taiko velocity, but
// only uses it to determine osu! velocity if beatmap version < 8. Let's account for that here.
if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
speedAdjustedBeatLength *= speedAdjustment;
// The velocity of the osu! hit object - calculated as the velocity of a slider
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
// The duration of the osu! hit object
double osuDuration = distance / osuVelocity;
// osu-stable always uses the speed-adjusted beatlength to determine the velocities, but
// only uses it for tick rate if beatmap version < 8
if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
speedAdjustedBeatLength *= speedAdjustment;
// If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans);

View File

@ -82,8 +82,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
return;
int countHit = NestedHitObjects.Count(o => o.IsHit);
if (countHit > HitObject.RequiredGoodHits)
if (countHit >= HitObject.RequiredGoodHits)
{
AddJudgement(new TaikoJudgement { Result = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good });
if (HitObject.IsStrong)

View File

@ -0,0 +1,87 @@
{
"Mappings": [{
"StartTime": 6590,
"Objects": [{
"StartTime": 6590,
"EndTime": 8320,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
},
{
"StartTime": 8436,
"Objects": [{
"StartTime": 8436,
"EndTime": 10166,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
},
{
"StartTime": 10282,
"Objects": [{
"StartTime": 10282,
"EndTime": 12012,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
},
{
"StartTime": 12128,
"Objects": [{
"StartTime": 12128,
"EndTime": 13858,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
},
{
"StartTime": 41666,
"Objects": [{
"StartTime": 41666,
"EndTime": 42589,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
},
{
"StartTime": 62666,
"Objects": [{
"StartTime": 62666,
"EndTime": 63127,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
},
{
"StartTime": 208743,
"Objects": [{
"StartTime": 208743,
"EndTime": 209204,
"IsRim": false,
"IsCentre": false,
"IsDrumRoll": true,
"IsSwell": false,
"IsStrong": false
}]
}
]
}

View File

@ -0,0 +1,25 @@
osu file format v14
[Difficulty]
HPDrainRate:6
CircleSize:4.2
OverallDifficulty:9
ApproachRate:9.8
SliderMultiplier:1.87
SliderTickRate:1
[TimingPoints]
6590,461.538461538462,4,2,2,15,1,0
6590,-200,4,2,2,15,0,0
49051,230.769230769231,4,2,1,15,1,0
62666,-200,4,2,1,60,0,0
197666,-100,4,2,1,85,0,1
[HitObjects]
88,104,6590,6,0,B|176:156|256:108|256:108|336:60|423:112,1,350.625,6|0,0:0|0:0,0:0:0:0:
396,213,8436,2,0,P|277:247|376:172,1,350.625,6|0,0:0|0:0,0:0:0:0:
472,220,10282,2,0,P|456:288|220:300,1,350.625,6|0,0:0|0:0,0:0:0:0:
277,200,12128,2,0,P|398:225|276:244,1,350.625,6|0,0:0|0:0,0:0:0:0:
268,229,41666,2,0,L|473:210,1,187,2|2,0:0|0:0,0:0:0:0:
133,342,62666,2,0,B|132:316|132:316|128:316|128:316|130:295|130:295|126:296|126:296|129:275|129:275|125:275|125:275|127:254|127:254|123:255|123:255|125:234|125:234|121:234|121:234|123:213|123:213|119:214|119:214|121:193|121:193|118:193|118:193|118:172,1,187,8|8,0:0|0:0,0:0:0:0:
481,338,208743,6,0,P|492:262|383:195,2,187,2|8|2,0:0|0:0|0:0,0:0:0:0:

View File

@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Taiko
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap);
public override int LegacyID => 1;
public override int? LegacyID => 1;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame();

View File

@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
[NonParallelizable]
[TestCase("basic", false), Ignore("See: https://github.com/ppy/osu/issues/2152")]
[TestCase("slider-generating-drumroll", false)]
public void Test(string name, bool isForCurrentRuleset)
{
this.isForCurrentRuleset = isForCurrentRuleset;

View File

@ -149,6 +149,8 @@
<ItemGroup>
<EmbeddedResource Include="Resources\Testing\Beatmaps\basic-expected-conversion.json" />
<EmbeddedResource Include="Resources\Testing\Beatmaps\basic.osu" />
<EmbeddedResource Include="Resources\Testing\Beatmaps\slider-generating-drumroll-expected-conversion.json" />
<EmbeddedResource Include="Resources\Testing\Beatmaps\slider-generating-drumroll.osu" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />

View File

@ -20,7 +20,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
[Test]
public void TestDecodeBeatmapGeneral()
{
var decoder = new LegacyBeatmapDecoder();
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
@ -102,7 +102,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(4, difficulty.CircleSize);
Assert.AreEqual(8, difficulty.OverallDifficulty);
Assert.AreEqual(9, difficulty.ApproachRate);
Assert.AreEqual(1.8f, difficulty.SliderMultiplier);
Assert.AreEqual(1.8, difficulty.SliderMultiplier);
Assert.AreEqual(2, difficulty.SliderTickRate);
}
}
@ -110,7 +110,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
[Test]
public void TestDecodeBeatmapEvents()
{
var decoder = new LegacyBeatmapDecoder();
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
@ -128,7 +128,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
[Test]
public void TestDecodeBeatmapTimingPoints()
{
var decoder = new LegacyBeatmapDecoder();
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{
@ -187,7 +187,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
[Test]
public void TestDecodeBeatmapHitObjects()
{
var decoder = new LegacyBeatmapDecoder();
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream))
{

View File

@ -85,7 +85,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(4, difficulty.CircleSize);
Assert.AreEqual(8, difficulty.OverallDifficulty);
Assert.AreEqual(9, difficulty.ApproachRate);
Assert.AreEqual(1.8f, difficulty.SliderMultiplier);
Assert.AreEqual(1.8, difficulty.SliderMultiplier);
Assert.AreEqual(2, difficulty.SliderTickRate);
}
@ -159,7 +159,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
using (var sr = new StreamReader(stream))
{
var legacyDecoded = new LegacyBeatmapDecoder().DecodeBeatmap(sr);
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.DecodeBeatmap(sr);
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms))
using (var sr2 = new StreamReader(ms))

View File

@ -58,7 +58,7 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile);
Assert.AreEqual("Deif", meta.AuthorString);
Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile);
Assert.AreEqual(164471, meta.PreviewTime);
Assert.AreEqual(164471 + LegacyBeatmapDecoder.UniversalOffset, meta.PreviewTime);
Assert.AreEqual(string.Empty, meta.Source);
Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags);
Assert.AreEqual("Renatus", meta.Title);

View File

@ -0,0 +1,161 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile.Sections;
using osu.Game.Overlays.Profile.Sections.Recent;
using System;
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseUserProfileRecentSection : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(RecentSection),
typeof(DrawableRecentActivity),
typeof(PaginatedRecentActivityContainer),
typeof(MedalIcon)
};
public TestCaseUserProfileRecentSection()
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f)
},
new ScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = new FillFlowContainer<DrawableRecentActivity>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
ChildrenEnumerable = createDummyActivities().Select(a => new DrawableRecentActivity(a))
},
}
};
}
private IEnumerable<RecentActivity> createDummyActivities()
{
var dummyBeatmap = new RecentActivity.RecentActivityBeatmap
{
Title = @"Dummy beatmap",
Url = "/b/1337",
};
var dummyUser = new RecentActivity.RecentActivityUser
{
Username = "DummyReborn",
Url = "/u/666",
PreviousUsername = "Dummy",
};
return new[]
{
new RecentActivity
{
User = dummyUser,
Type = RecentActivityType.Achievement,
Achievement = new RecentActivity.RecentActivityAchievement
{
Name = @"Feelin' It",
Slug = @"all-secret-feelinit",
},
},
new RecentActivity
{
User = dummyUser,
Type = RecentActivityType.BeatmapPlaycount,
Count = 1337,
Beatmap = dummyBeatmap,
},
new RecentActivity
{
User = dummyUser,
Type = RecentActivityType.BeatmapsetApprove,
Approval = BeatmapApproval.Qualified,
Beatmapset = dummyBeatmap,
},
new RecentActivity
{
User = dummyUser,
Type = RecentActivityType.BeatmapsetDelete,
Beatmapset = dummyBeatmap,
},
new RecentActivity
{
User = dummyUser,
Type = RecentActivityType.BeatmapsetRevive,
Beatmapset = dummyBeatmap,
},
new RecentActivity
{
User = dummyUser,
Type = RecentActivityType.BeatmapsetRevive,
Beatmapset = dummyBeatmap,
},
new RecentActivity
{
User = dummyUser,
Type = RecentActivityType.BeatmapsetUpdate,
Beatmapset = dummyBeatmap,
},
new RecentActivity
{
User = dummyUser,
Type = RecentActivityType.BeatmapsetUpload,
Beatmapset = dummyBeatmap,
},
new RecentActivity
{
User = dummyUser,
Type = RecentActivityType.Rank,
Rank = 1,
Mode = "osu!",
Beatmap = dummyBeatmap,
},
new RecentActivity
{
User = dummyUser,
Type = RecentActivityType.RankLost,
Mode = "osu!",
Beatmap = dummyBeatmap,
},
new RecentActivity
{
User = dummyUser,
Type = RecentActivityType.UsernameChange,
},
new RecentActivity
{
User = dummyUser,
Type = RecentActivityType.UserSupportAgain,
},
new RecentActivity
{
User = dummyUser,
Type = RecentActivityType.UserSupportFirst,
},
new RecentActivity
{
User = dummyUser,
Type = RecentActivityType.UserSupportGift,
},
};
}
}
}

View File

@ -0,0 +1,30 @@
// 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.Graphics;
using osu.Game.Overlays.Volume;
using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
{
public class TestCaseVolumePieces : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(VolumeMeter), typeof(MuteButton) };
protected override void LoadComplete()
{
VolumeMeter meter;
MuteButton mute;
Add(meter = new VolumeMeter("MASTER", 125, Color4.Blue));
Add(mute = new MuteButton
{
Margin = new MarginPadding { Top = 200 }
});
AddSliderStep("master volume", 0, 10, 0, i => meter.Bindable.Value = i * 0.1);
AddToggleStep("mute", b => mute.Current.Value = b);
}
}
}

View File

@ -173,7 +173,9 @@
<Compile Include="Visual\TestCaseTwoLayerButton.cs" />
<Compile Include="Visual\TestCaseUserPanel.cs" />
<Compile Include="Visual\TestCaseUserProfile.cs" />
<Compile Include="Visual\TestCaseUserProfileRecentSection.cs" />
<Compile Include="Visual\TestCaseUserRanks.cs" />
<Compile Include="Visual\TestCaseVolumePieces.cs" />
<Compile Include="Visual\TestCaseWaveform.cs" />
</ItemGroup>
<ItemGroup>

View File

@ -29,8 +29,8 @@ namespace osu.Game.Beatmaps
set => approachRate = value;
}
public float SliderMultiplier { get; set; } = 1;
public float SliderTickRate { get; set; } = 1;
public double SliderMultiplier { get; set; } = 1;
public double SliderTickRate { get; set; } = 1;
/// <summary>
/// Maps a difficulty value [0, 10] to a two-piece linear range of values.

View File

@ -41,12 +41,12 @@ namespace osu.Game.Beatmaps
foreach (var mod in Mods.OfType<IApplicableToDifficulty>())
mod.ApplyToDifficulty(Beatmap.BeatmapInfo.BaseDifficulty);
foreach (var h in Beatmap.HitObjects)
h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty);
foreach (var mod in mods.OfType<IApplicableToHitObject<T>>())
foreach (var obj in Beatmap.HitObjects)
mod.ApplyToHitObject(obj);
foreach (var h in Beatmap.HitObjects)
h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty);
}
protected virtual void PreprocessHitObjects()

View File

@ -8,6 +8,7 @@ using OpenTK.Graphics;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Beatmaps.ControlPoints;
using osu.Framework;
namespace osu.Game.Beatmaps.Formats
{
@ -21,6 +22,19 @@ namespace osu.Game.Beatmaps.Formats
private LegacySampleBank defaultSampleBank;
private int defaultSampleVolume = 100;
/// <summary>
/// lazer's audio timings in general doesn't match stable. this is the result of user testing, albeit limited.
/// This only seems to be required on windows. We need to eventually figure out why, with a bit of luck.
/// </summary>
public static int UniversalOffset => RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? -22 : 0;
/// <summary>
/// Whether or not beatmap or runtime offsets should be applied. Defaults on; only disable for testing purposes.
/// </summary>
public bool ApplyOffsets = true;
private readonly int offset = UniversalOffset;
public LegacyBeatmapDecoder()
{
}
@ -28,6 +42,9 @@ namespace osu.Game.Beatmaps.Formats
public LegacyBeatmapDecoder(string header)
{
BeatmapVersion = int.Parse(header.Substring(17));
// BeatmapVersion 4 and lower had an incorrect offset (stable has this set as 24ms off)
offset += BeatmapVersion < 5 ? 24 : 0;
}
protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap)
@ -102,7 +119,7 @@ namespace osu.Game.Beatmaps.Formats
beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value);
break;
case @"PreviewTime":
metadata.PreviewTime = int.Parse(pair.Value);
metadata.PreviewTime = getOffsetTime(int.Parse(pair.Value));
break;
case @"Countdown":
beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1;
@ -232,10 +249,10 @@ namespace osu.Game.Beatmaps.Formats
difficulty.ApproachRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
break;
case @"SliderMultiplier":
difficulty.SliderMultiplier = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
difficulty.SliderMultiplier = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
break;
case @"SliderTickRate":
difficulty.SliderTickRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
difficulty.SliderTickRate = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
break;
}
}
@ -257,8 +274,8 @@ namespace osu.Game.Beatmaps.Formats
case EventType.Break:
var breakEvent = new BreakPeriod
{
StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo),
EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo)
StartTime = getOffsetTime(double.Parse(split[1], NumberFormatInfo.InvariantInfo)),
EndTime = getOffsetTime(double.Parse(split[2], NumberFormatInfo.InvariantInfo))
};
if (!breakEvent.HasEffect)
@ -273,7 +290,7 @@ namespace osu.Game.Beatmaps.Formats
{
string[] split = line.Split(',');
double time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo);
double time = getOffsetTime(double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo));
double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo);
double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1;
@ -396,7 +413,14 @@ namespace osu.Game.Beatmaps.Formats
var obj = parser.Parse(line);
if (obj != null)
{
obj.StartTime = getOffsetTime(obj.StartTime);
beatmap.HitObjects.Add(obj);
}
}
private int getOffsetTime(int time) => time + (ApplyOffsets ? offset : 0);
private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0);
}
}

View File

@ -90,6 +90,10 @@ namespace osu.Game.Graphics.Containers
case LinkAction.External:
Process.Start(url);
break;
case LinkAction.OpenUserProfile:
if (long.TryParse(linkArgument, out long userId))
game?.ShowUser(userId);
break;
default:
throw new NotImplementedException($"This {nameof(LinkAction)} ({linkType.ToString()}) is missing an associated action.");
}

View File

@ -66,7 +66,7 @@ namespace osu.Game.Graphics.Containers
{
Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount;
content.Position = Interpolation.ValueAt(Clock.ElapsedFrameTime, content.Position, offset, 0, 1000, Easing.OutQuint);
content.Position = Interpolation.ValueAt(MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000), content.Position, offset, 0, 1000, Easing.OutQuint);
content.Scale = new Vector2(1 + ParallaxAmount);
}

View File

@ -1,8 +1,6 @@
// 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.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -11,7 +9,5 @@ namespace osu.Game.Graphics.Containers
public class ReverseChildIDFillFlowContainer<T> : FillFlowContainer<T> where T : Drawable
{
protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y);
protected override IEnumerable<Drawable> FlowingChildren => base.FlowingChildren.Reverse();
}
}

View File

@ -18,8 +18,6 @@ namespace osu.Game.Graphics.UserInterface
public Action Exit;
public override bool HandleLeftRightArrows => false;
private bool focus;
public bool HoldFocus
{

View File

@ -9,6 +9,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
@ -56,6 +57,14 @@ namespace osu.Game.Graphics.UserInterface
}
}
protected override TabFillFlowContainer CreateTabFlow() => new OsuTabFillFlowContainer
{
Direction = FillDirection.Full,
RelativeSizeAxes = Axes.Both,
Depth = -1,
Masking = true
};
public class OsuTabItem : TabItem<T>, IHasAccentColour
{
protected readonly SpriteText Text;
@ -239,5 +248,10 @@ namespace osu.Game.Graphics.UserInterface
}
}
}
private class OsuTabFillFlowContainer : TabFillFlowContainer
{
protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y);
}
}
}

View File

@ -12,6 +12,8 @@ namespace osu.Game.Graphics.UserInterface
{
protected virtual bool AllowCommit => false;
public override bool HandleLeftRightArrows => false;
public SearchTextBox()
{
Height = 35;

View File

@ -1,108 +0,0 @@
// 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.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings;
using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface.Volume
{
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
{
private readonly Box meterFill;
public BindableDouble Bindable { get; } = new BindableDouble();
public VolumeMeter(string meterName)
{
Size = new Vector2(40, 180);
Children = new Drawable[]
{
new Box
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both
},
new Container
{
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.5f, 0.9f),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Box
{
Colour = Color4.DarkGray,
RelativeSizeAxes = Axes.Both
},
meterFill = new Box
{
Colour = Color4.White,
Scale = new Vector2(1, 0),
RelativeSizeAxes = Axes.Both,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre
}
}
},
new OsuSpriteText
{
Text = meterName,
Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre
}
};
Bindable.ValueChanged += delegate { updateFill(); };
}
protected override void LoadComplete()
{
base.LoadComplete();
updateFill();
}
public double Volume
{
get => Bindable.Value;
private set => Bindable.Value = value;
}
public void Increase()
{
Volume += 0.05f;
}
public void Decrease()
{
Volume -= 0.05f;
}
private void updateFill() => meterFill.ScaleTo(new Vector2(1, (float)Volume), 300, Easing.OutQuint);
public bool OnPressed(GlobalAction action)
{
if (!IsHovered) return false;
switch (action)
{
case GlobalAction.DecreaseVolume:
Decrease();
return true;
case GlobalAction.IncreaseVolume:
Increase();
return true;
}
return false;
}
public bool OnReleased(GlobalAction action) => false;
}
}

View File

@ -199,7 +199,7 @@ namespace osu.Game.Online.API
}
catch (WebException we)
{
HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode ?? HttpStatusCode.RequestTimeout;
HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode ?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout);
switch (statusCode)
{

View File

@ -0,0 +1,130 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
using osu.Game.Rulesets.Scoring;
using Humanizer;
using System;
using System.Collections.Generic;
namespace osu.Game.Online.API.Requests
{
public class GetUserRecentActivitiesRequest : APIRequest<List<RecentActivity>>
{
private readonly long userId;
private readonly int offset;
public GetUserRecentActivitiesRequest(long userId, int offset = 0)
{
this.userId = userId;
this.offset = offset;
}
protected override string Target => $"users/{userId}/recent_activity?offset={offset}";
}
public class RecentActivity
{
[JsonProperty("id")]
public int ID;
[JsonProperty("createdAt")]
public DateTimeOffset CreatedAt;
[JsonProperty]
private string type
{
set => Type = (RecentActivityType)Enum.Parse(typeof(RecentActivityType), value.Pascalize());
}
public RecentActivityType Type;
[JsonProperty]
private string scoreRank
{
set => ScoreRank = (ScoreRank)Enum.Parse(typeof(ScoreRank), value);
}
public ScoreRank ScoreRank;
[JsonProperty("rank")]
public int Rank;
[JsonProperty("approval")]
public BeatmapApproval Approval;
[JsonProperty("count")]
public int Count;
[JsonProperty("mode")]
public string Mode;
[JsonProperty("beatmap")]
public RecentActivityBeatmap Beatmap;
[JsonProperty("beatmapset")]
public RecentActivityBeatmap Beatmapset;
[JsonProperty("user")]
public RecentActivityUser User;
[JsonProperty("achievement")]
public RecentActivityAchievement Achievement;
public class RecentActivityBeatmap
{
[JsonProperty("title")]
public string Title;
[JsonProperty("url")]
public string Url;
}
public class RecentActivityUser
{
[JsonProperty("username")]
public string Username;
[JsonProperty("url")]
public string Url;
[JsonProperty("previousUsername")]
public string PreviousUsername;
}
public class RecentActivityAchievement
{
[JsonProperty("slug")]
public string Slug;
[JsonProperty("name")]
public string Name;
}
}
public enum RecentActivityType
{
Achievement,
BeatmapPlaycount,
BeatmapsetApprove,
BeatmapsetDelete,
BeatmapsetRevive,
BeatmapsetUpdate,
BeatmapsetUpload,
Medal,
Rank,
RankLost,
UserSupportAgain,
UserSupportFirst,
UserSupportGift,
UsernameChange,
}
public enum BeatmapApproval
{
Ranked,
Approved,
Qualified,
}
}

View File

@ -118,6 +118,8 @@ namespace osu.Game.Online.Chat
case "beatmapsets":
case "d":
return new LinkDetails(LinkAction.OpenBeatmapSet, args[3]);
case "u":
return new LinkDetails(LinkAction.OpenUserProfile, args[3]);
}
}
@ -146,6 +148,9 @@ namespace osu.Game.Online.Chat
case "spectate":
linkType = LinkAction.Spectate;
break;
case "u":
linkType = LinkAction.OpenUserProfile;
break;
default:
linkType = LinkAction.External;
break;
@ -205,6 +210,15 @@ namespace osu.Game.Online.Chat
return inputMessage;
}
public static MessageFormatterResult FormatText(string text)
{
var result = format(text);
result.Links.Sort();
return result;
}
public class MessageFormatterResult
{
public List<Link> Links = new List<Link>();
@ -239,6 +253,7 @@ namespace osu.Game.Online.Chat
OpenEditorTimestamp,
JoinMultiplayerMatch,
Spectate,
OpenUserProfile,
}
public class Link : IComparable<Link>

View File

@ -10,7 +10,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
using osu.Framework.Logging;
using osu.Game.Graphics.UserInterface.Volume;
using osu.Framework.Allocation;
using osu.Game.Overlays.Toolbar;
using osu.Game.Screens;
@ -33,6 +32,7 @@ using osu.Game.Input.Bindings;
using osu.Game.Rulesets.Mods;
using osu.Game.Skinning;
using OpenTK.Graphics;
using osu.Game.Overlays.Volume;
namespace osu.Game
{
@ -75,7 +75,7 @@ namespace osu.Game
private OsuScreen screenStack;
private VolumeControl volume;
private VolumeOverlay volume;
private OnScreenDisplay onscreenDisplay;
private Bindable<int> configRuleset;
@ -155,6 +155,12 @@ namespace osu.Game
/// <param name="setId">The set to display.</param>
public void ShowBeatmapSet(int setId) => beatmapSetOverlay.ShowBeatmapSet(setId);
/// <summary>
/// Show a user's profile as an overlay.
/// </summary>
/// <param name="userId">The user to display.</param>
public void ShowUser(long userId) => userProfile.ShowUser(userId);
protected void LoadScore(Score s)
{
scoreLoad?.Cancel();
@ -232,7 +238,7 @@ namespace osu.Game
},
}, overlayContent.Add);
loadComponentSingleFile(volume = new VolumeControl(), Add);
loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add);
loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add);
//overlay elements

View File

@ -53,9 +53,9 @@ namespace osu.Game.Overlays.Chat
protected override void AddTabItem(TabItem<Channel> item, bool addToDropdown = true)
{
if (selectorTab.Depth < float.MaxValue)
if (item != selectorTab && TabContainer.GetLayoutPosition(selectorTab) < float.MaxValue)
// performTabSort might've made selectorTab's position wonky, fix it
TabContainer.ChangeChildDepth(selectorTab, float.MaxValue);
TabContainer.SetLayoutPosition(selectorTab, float.MaxValue);
base.AddTabItem(item, addToDropdown);

View File

@ -101,11 +101,10 @@ namespace osu.Game.Overlays.Music
public void AddBeatmapSet(BeatmapSetInfo beatmapSet)
{
items.Add(new PlaylistItem(beatmapSet)
{
OnSelect = set => OnSelect?.Invoke(set),
Depth = items.Count
});
var newItem = new PlaylistItem(beatmapSet) { OnSelect = set => OnSelect?.Invoke(set) };
items.Add(newItem);
items.SetLayoutPosition(newItem, items.Count);
}
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet)
@ -197,7 +196,7 @@ namespace osu.Game.Overlays.Music
{
var itemsPos = items.ToLocalSpace(nativeDragPosition);
int srcIndex = (int)draggedItem.Depth;
int srcIndex = (int)items.GetLayoutPosition(draggedItem);
// Find the last item with position < mouse position. Note we can't directly use
// the item positions as they are being transformed
@ -219,15 +218,15 @@ namespace osu.Game.Overlays.Music
if (srcIndex < dstIndex)
{
for (int i = srcIndex + 1; i <= dstIndex; i++)
items.ChangeChildDepth(items[i], i - 1);
items.SetLayoutPosition(items[i], i - 1);
}
else
{
for (int i = dstIndex; i < srcIndex; i++)
items.ChangeChildDepth(items[i], i + 1);
items.SetLayoutPosition(items[i], i + 1);
}
items.ChangeChildDepth(draggedItem, dstIndex);
items.SetLayoutPosition(draggedItem, dstIndex);
}
private class ItemSearchContainer : FillFlowContainer<PlaylistItem>, IHasFilterableChildren
@ -243,9 +242,6 @@ namespace osu.Game.Overlays.Music
}
}
// Compare with reversed ChildID and Depth
protected override int Compare(Drawable x, Drawable y) => base.Compare(y, x);
public IEnumerable<IFilterable> FilterableChildren => Children;
public ItemSearchContainer()

View File

@ -129,7 +129,6 @@ namespace osu.Game.Overlays
public void Post(Notification notification) => postScheduler.Add(() =>
{
++runningDepth;
notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth;
notification.Closed += notificationClosed;
@ -138,7 +137,9 @@ namespace osu.Game.Overlays
hasCompletionTarget.CompletionTarget = Post;
var ourType = notification.GetType();
sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType)))?.Add(notification);
var section = sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType)));
section?.Add(notification, notification.DisplayOnTop ? -runningDepth : runningDepth);
updateCounts();
});

View File

@ -25,10 +25,13 @@ namespace osu.Game.Overlays.Notifications
private FlowContainer<Notification> notifications;
public int DisplayedCount => notifications.Count(n => !n.WasClosed);
public int UnreadCount => notifications.Count(n => !n.WasClosed && !n.Read);
public void Add(Notification notification) => notifications.Add(notification);
public void Add(Notification notification, float position)
{
notifications.Add(notification);
notifications.SetLayoutPosition(notification, position);
}
public IEnumerable<Type> AcceptTypes;

View File

@ -40,16 +40,18 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
[BackgroundDependencyLoader(true)]
private void load(OsuColour colour)
{
RightFlowContainer.Add(new OsuSpriteText
var text = new OsuSpriteText
{
Text = $"accuracy: {Score.Accuracy:P2}",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = colour.GrayA,
TextSize = 11,
Font = "Exo2.0-RegularItalic",
Depth = -1,
});
Font = "Exo2.0-RegularItalic"
};
RightFlowContainer.Add(text);
RightFlowContainer.SetLayoutPosition(text, 1);
LeftFlowContainer.Add(new BeatmapMetadataContainer(Score.Beatmap));
LeftFlowContainer.Add(new OsuSpriteText

View File

@ -0,0 +1,165 @@
// 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.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Chat;
using osu.Game.Screens.Select.Leaderboards;
namespace osu.Game.Overlays.Profile.Sections.Recent
{
public class DrawableRecentActivity : DrawableProfileRow
{
private APIAccess api;
private readonly RecentActivity activity;
private LinkFlowContainer content;
public DrawableRecentActivity(RecentActivity activity)
{
this.activity = activity;
}
[BackgroundDependencyLoader]
private void load(APIAccess api)
{
this.api = api;
LeftFlowContainer.Padding = new MarginPadding { Left = 10, Right = 160 };
LeftFlowContainer.Add(content = new LinkFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
});
RightFlowContainer.Add(new OsuSpriteText
{
Text = activity.CreatedAt.LocalDateTime.ToShortDateString(),
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Font = "Exo2.0-RegularItalic",
TextSize = 12,
Colour = OsuColour.Gray(0xAA),
});
var formatted = createMessage();
content.AddLinks(formatted.Text, formatted.Links);
}
protected override Drawable CreateLeftVisual()
{
switch (activity.Type)
{
case RecentActivityType.Rank:
return new DrawableRank(activity.ScoreRank)
{
RelativeSizeAxes = Axes.Y,
Width = 60,
FillMode = FillMode.Fit,
};
case RecentActivityType.Achievement:
return new MedalIcon(activity.Achievement.Slug)
{
RelativeSizeAxes = Axes.Y,
Width = 60,
FillMode = FillMode.Fit,
};
default:
return new Container
{
RelativeSizeAxes = Axes.Y,
Width = 60,
FillMode = FillMode.Fit,
};
}
}
private string toAbsoluteUrl(string url) => $"{api.Endpoint}{url}";
private MessageFormatter.MessageFormatterResult createMessage()
{
string userLinkTemplate() => $"[{toAbsoluteUrl(activity.User?.Url)} {activity.User?.Username}]";
string beatmapLinkTemplate() => $"[{toAbsoluteUrl(activity.Beatmap?.Url)} {activity.Beatmap?.Title}]";
string beatmapsetLinkTemplate() => $"[{toAbsoluteUrl(activity.Beatmapset?.Url)} {activity.Beatmapset?.Title}]";
string message;
switch (activity.Type)
{
case RecentActivityType.Achievement:
message = $"{userLinkTemplate()} unlocked the {activity.Achievement.Name} medal!";
break;
case RecentActivityType.BeatmapPlaycount:
message = $"{beatmapLinkTemplate()} has been played {activity.Count} times!";
break;
case RecentActivityType.BeatmapsetApprove:
message = $"{beatmapsetLinkTemplate()} has been {activity.Approval.ToString().ToLowerInvariant()}!";
break;
case RecentActivityType.BeatmapsetDelete:
message = $"{beatmapsetLinkTemplate()} has been deleted.";
break;
case RecentActivityType.BeatmapsetRevive:
message = $"{beatmapsetLinkTemplate()} has been revived from eternal slumber by {userLinkTemplate()}.";
break;
case RecentActivityType.BeatmapsetUpdate:
message = $"{userLinkTemplate()} has updated the beatmap {beatmapsetLinkTemplate()}!";
break;
case RecentActivityType.BeatmapsetUpload:
message = $"{userLinkTemplate()} has submitted a new beatmap {beatmapsetLinkTemplate()}!";
break;
case RecentActivityType.Medal:
// apparently this shouldn't exist look at achievement instead (https://github.com/ppy/osu-web/blob/master/resources/assets/coffee/react/profile-page/recent-activity.coffee#L111)
message = string.Empty;
break;
case RecentActivityType.Rank:
message = $"{userLinkTemplate()} achieved rank #{activity.Rank} on {beatmapLinkTemplate()} ({activity.Mode}!)";
break;
case RecentActivityType.RankLost:
message = $"{userLinkTemplate()} has lost first place on {beatmapLinkTemplate()} ({activity.Mode}!)";
break;
case RecentActivityType.UserSupportAgain:
message = $"{userLinkTemplate()} has once again chosen to support osu! - thanks for your generosity!";
break;
case RecentActivityType.UserSupportFirst:
message = $"{userLinkTemplate()} has become an osu! supporter - thanks for your generosity!";
break;
case RecentActivityType.UserSupportGift:
message = $"{userLinkTemplate()} has received the gift of osu! supporter!";
break;
case RecentActivityType.UsernameChange:
message = $"{activity.User?.PreviousUsername} has changed their username to {userLinkTemplate()}!";
break;
default:
message = string.Empty;
break;
}
return MessageFormatter.FormatText(message);
}
}
}

View File

@ -0,0 +1,38 @@
// 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.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
namespace osu.Game.Overlays.Profile.Sections.Recent
{
public class MedalIcon : Container
{
private readonly string slug;
private readonly Sprite sprite;
private string url => $@"https://s.ppy.sh/images/medals-client/{slug}@2x.png";
public MedalIcon(string slug)
{
this.slug = slug;
Child = sprite = new Sprite
{
Height = 40,
Width = 40,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
sprite.Texture = textures.Get(url);
}
}
}

View File

@ -0,0 +1,48 @@
// 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.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests;
using osu.Game.Users;
using System.Linq;
namespace osu.Game.Overlays.Profile.Sections.Recent
{
public class PaginatedRecentActivityContainer : PaginatedContainer
{
public PaginatedRecentActivityContainer(Bindable<User> user, string header, string missing)
: base(user, header, missing)
{
ItemsPerPage = 5;
}
protected override void ShowMore()
{
base.ShowMore();
var req = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage);
req.Success += activities =>
{
ShowMoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide();
if (!activities.Any() && VisiblePages == 1)
{
MissingText.Show();
return;
}
MissingText.Hide();
foreach (RecentActivity activity in activities)
{
ItemsContainer.Add(new DrawableRecentActivity(activity));
}
};
Api.Queue(req);
}
}
}

View File

@ -1,12 +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.Overlays.Profile.Sections.Recent;
namespace osu.Game.Overlays.Profile.Sections
{
public class RecentSection : ProfileSection
{
public override string Title => "Recent";
public override string Identifier => "recent_activities";
public override string Identifier => "recent_activity";
public RecentSection()
{
Children = new[]
{
new PaginatedRecentActivityContainer(User, null, @"This user hasn't done anything notable recently!"),
};
}
}
}

View File

@ -45,7 +45,8 @@ namespace osu.Game.Overlays.Settings
if (text == null)
{
// construct lazily for cases where the label is not needed (may be provided by the Control).
Add(text = new OsuSpriteText { Depth = 1 });
Add(text = new OsuSpriteText());
FlowContent.SetLayoutPosition(text, -1);
}
text.Text = value;

View File

@ -73,6 +73,14 @@ namespace osu.Game.Overlays
FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out);
}
public void ShowUser(long userId)
{
if (userId == Header.User.Id)
return;
ShowUser(new User { Id = userId });
}
public void ShowUser(User user, bool fetchOnline = true)
{
userReq?.Cancel();
@ -82,7 +90,7 @@ namespace osu.Game.Overlays
sections = new ProfileSection[]
{
//new AboutSection(),
//new RecentSection(),
new RecentSection(),
new RanksSection(),
//new MedalsSection(),
new HistoricalSection(),

View File

@ -0,0 +1,83 @@
// 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.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Volume
{
public class MuteButton : Container, IHasCurrentValue<bool>
{
public Bindable<bool> Current { get; } = new Bindable<bool>();
private Color4 hoveredColour, unhoveredColour;
private const float width = 100;
public const float HEIGHT = 35;
public MuteButton()
{
Masking = true;
BorderThickness = 3;
CornerRadius = HEIGHT / 2;
Size = new Vector2(width, HEIGHT);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
hoveredColour = colours.YellowDark;
BorderColour = unhoveredColour = colours.Gray1.Opacity(0.9f);
SpriteIcon icon;
AddRange(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.Gray1,
Alpha = 0.9f,
},
icon = new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(20),
}
});
Current.ValueChanged += newValue =>
{
icon.Icon = newValue ? FontAwesome.fa_volume_off : FontAwesome.fa_volume_up;
icon.Margin = new MarginPadding { Left = newValue ? width / 2 - 15 : width / 2 - 10 }; //Magic numbers to line up both icons because they're different widths
};
Current.TriggerChange();
}
protected override bool OnHover(InputState state)
{
this.TransformTo<MuteButton, SRGBColour>("BorderColour", hoveredColour, 500, Easing.OutQuint);
return true;
}
protected override void OnHoverLost(InputState state)
{
this.TransformTo<MuteButton, SRGBColour>("BorderColour", unhoveredColour, 500, Easing.OutQuint);
}
protected override bool OnClick(InputState state)
{
Current.Value = !Current.Value;
return true;
}
}
}

View File

@ -7,7 +7,7 @@ using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface.Volume
namespace osu.Game.Overlays.Volume
{
public class VolumeControlReceptor : Container, IKeyBindingHandler<GlobalAction>, IHandleGlobalInput
{

View File

@ -0,0 +1,186 @@
// 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.Globalization;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Input.Bindings;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Volume
{
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
{
private CircularProgress volumeCircle;
public BindableDouble Bindable { get; } = new BindableDouble { MinValue = 0, MaxValue = 1 };
private readonly float circleSize;
private readonly Color4 meterColour;
private readonly string name;
private OsuSpriteText text;
private BufferedContainer maxGlow;
public VolumeMeter(string name, float circleSize, Color4 meterColour)
{
this.circleSize = circleSize;
this.meterColour = meterColour;
this.name = name;
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Add(new Container
{
Size = new Vector2(120, 20),
CornerRadius = 10,
Masking = true,
Margin = new MarginPadding { Left = circleSize + 10 },
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.Gray1,
Alpha = 0.9f,
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = "Exo2.0-Bold",
Text = name
}
}
});
CircularProgress bgProgress;
Add(new CircularContainer
{
Masking = true,
Size = new Vector2(circleSize),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.Gray1,
Alpha = 0.9f,
},
bgProgress = new CircularProgress
{
RelativeSizeAxes = Axes.Both,
InnerRadius = 0.05f,
Rotation = 180,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Colour = colours.Gray2,
Size = new Vector2(0.8f)
},
(volumeCircle = new CircularProgress
{
RelativeSizeAxes = Axes.Both,
InnerRadius = 0.05f,
Rotation = 180,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(0.8f)
}).WithEffect(new GlowEffect
{
Colour = meterColour,
Strength = 2
}),
maxGlow = (text = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = "Venera",
TextSize = 0.16f * circleSize
}).WithEffect(new GlowEffect
{
Colour = Color4.Transparent,
PadExtent = true,
})
}
});
Bindable.ValueChanged += newVolume => { this.TransformTo("DisplayVolume", newVolume, 400, Easing.OutQuint); };
bgProgress.Current.Value = 0.75f;
}
protected override void LoadComplete()
{
base.LoadComplete();
Bindable.TriggerChange();
}
private double displayVolume;
protected double DisplayVolume
{
get => displayVolume;
set
{
displayVolume = value;
if (displayVolume > 0.99f)
{
text.Text = "MAX";
maxGlow.EffectColour = meterColour.Opacity(2f);
}
else
{
maxGlow.EffectColour = Color4.Transparent;
text.Text = Math.Round(displayVolume * 100).ToString(CultureInfo.CurrentCulture);
}
volumeCircle.Current.Value = displayVolume * 0.75f;
}
}
public double Volume
{
get => Bindable;
private set => Bindable.Value = value;
}
public void Increase() => Volume += 0.05f;
public void Decrease() => Volume -= 0.05f;
public bool OnPressed(GlobalAction action)
{
if (!IsHovered) return false;
switch (action)
{
case GlobalAction.DecreaseVolume:
Decrease();
return true;
case GlobalAction.IncreaseVolume:
Increase();
return true;
}
return false;
}
public bool OnReleased(GlobalAction action) => false;
}
}

View File

@ -1,57 +1,84 @@
// 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.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Threading;
using OpenTK;
using osu.Framework.Audio;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Threading;
using osu.Game.Graphics;
using osu.Game.Input.Bindings;
using osu.Game.Overlays.Volume;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Graphics.UserInterface.Volume
namespace osu.Game.Overlays
{
public class VolumeControl : OverlayContainer
public class VolumeOverlay : OverlayContainer
{
private readonly VolumeMeter volumeMeterMaster;
private readonly IconButton muteIcon;
private const float offset = 10;
private VolumeMeter volumeMeterMaster;
private VolumeMeter volumeMeterEffect;
private VolumeMeter volumeMeterMusic;
private MuteButton muteButton;
protected override bool BlockPassThroughMouse => false;
public VolumeControl()
{
AutoSizeAxes = Axes.Both;
Anchor = Anchor.BottomRight;
Origin = Anchor.BottomRight;
private readonly BindableDouble muteAdjustment = new BindableDouble();
Children = new Drawable[]
[BackgroundDependencyLoader]
private void load(AudioManager audio, OsuColour colours)
{
AutoSizeAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
AddRange(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Y,
Width = 300,
Colour = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.75f), Color4.Black.Opacity(0))
},
new FillFlowContainer
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Both,
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Margin = new MarginPadding { Left = 10, Right = 10, Top = 30, Bottom = 30 },
Spacing = new Vector2(15, 0),
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Spacing = new Vector2(0, offset),
Margin = new MarginPadding { Left = offset },
Children = new Drawable[]
{
new Container
volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker)
{
Size = new Vector2(IconButton.BUTTON_SIZE),
Child = muteIcon = new IconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.fa_volume_up,
Action = () => Adjust(GlobalAction.ToggleMute),
}
Margin = new MarginPadding { Top = 100 + MuteButton.HEIGHT } //to counter the mute button and re-center the volume meters
},
volumeMeterMaster = new VolumeMeter("Master"),
volumeMeterEffect = new VolumeMeter("Effects"),
volumeMeterMusic = new VolumeMeter("Music")
volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker),
volumeMeterMusic = new VolumeMeter("MUSIC", 125, colours.BlueDarker),
muteButton = new MuteButton
{
Margin = new MarginPadding { Top = 100 }
}
}
}
},
});
volumeMeterMaster.Bindable.BindTo(audio.Volume);
volumeMeterEffect.Bindable.BindTo(audio.VolumeSample);
volumeMeterMusic.Bindable.BindTo(audio.VolumeTrack);
muteButton.Current.ValueChanged += mute =>
{
if (mute)
audio.AddAdjustment(AdjustableProperty.Volume, muteAdjustment);
else
audio.RemoveAdjustment(AdjustableProperty.Volume, muteAdjustment);
};
}
@ -62,7 +89,13 @@ namespace osu.Game.Graphics.UserInterface.Volume
volumeMeterMaster.Bindable.ValueChanged += _ => settingChanged();
volumeMeterEffect.Bindable.ValueChanged += _ => settingChanged();
volumeMeterMusic.Bindable.ValueChanged += _ => settingChanged();
muted.ValueChanged += _ => settingChanged();
muteButton.Current.ValueChanged += _ => settingChanged();
}
private void settingChanged()
{
Show();
schedulePopOut();
}
public bool Adjust(GlobalAction action)
@ -83,50 +116,15 @@ namespace osu.Game.Graphics.UserInterface.Volume
return true;
case GlobalAction.ToggleMute:
Show();
muted.Toggle();
muteButton.Current.Value = !muteButton.Current;
return true;
}
return false;
}
private void settingChanged()
{
Show();
schedulePopOut();
}
private readonly BindableDouble muteAdjustment = new BindableDouble();
private readonly BindableBool muted = new BindableBool();
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
volumeMeterMaster.Bindable.BindTo(audio.Volume);
volumeMeterEffect.Bindable.BindTo(audio.VolumeSample);
volumeMeterMusic.Bindable.BindTo(audio.VolumeTrack);
muted.ValueChanged += mute =>
{
if (mute)
{
audio.AddAdjustment(AdjustableProperty.Volume, muteAdjustment);
muteIcon.Icon = FontAwesome.fa_volume_off;
}
else
{
audio.RemoveAdjustment(AdjustableProperty.Volume, muteAdjustment);
muteIcon.Icon = FontAwesome.fa_volume_up;
}
};
}
private ScheduledDelegate popOutDelegate;
private readonly VolumeMeter volumeMeterEffect;
private readonly VolumeMeter volumeMeterMusic;
protected override void PopIn()
{
ClearTransforms();

View File

@ -35,8 +35,9 @@ namespace osu.Game.Rulesets.Objects.Drawables
protected virtual IEnumerable<SampleInfo> GetSamples() => HitObject.Samples;
private List<DrawableHitObject> nestedHitObjects;
public IReadOnlyList<DrawableHitObject> NestedHitObjects => nestedHitObjects;
private readonly Lazy<List<DrawableHitObject>> nestedHitObjects = new Lazy<List<DrawableHitObject>>();
public bool HasNestedHitObjects => nestedHitObjects.IsValueCreated;
public IReadOnlyList<DrawableHitObject> NestedHitObjects => nestedHitObjects.Value;
public event Action<DrawableHitObject, Judgement> OnJudgement;
public event Action<DrawableHitObject, Judgement> OnJudgementRemoved;
@ -52,12 +53,12 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// <summary>
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been hit.
/// </summary>
public bool IsHit => Judgements.Any(j => j.Final && j.IsHit) && (NestedHitObjects?.All(n => n.IsHit) ?? true);
public bool IsHit => Judgements.Any(j => j.Final && j.IsHit) && (!HasNestedHitObjects || NestedHitObjects.All(n => n.IsHit));
/// <summary>
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been judged.
/// </summary>
public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (NestedHitObjects?.All(h => h.AllJudged) ?? true);
public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (!HasNestedHitObjects || NestedHitObjects.All(h => h.AllJudged));
/// <summary>
/// Whether this <see cref="DrawableHitObject"/> can be judged.
@ -160,14 +161,11 @@ namespace osu.Game.Rulesets.Objects.Drawables
protected virtual void AddNested(DrawableHitObject h)
{
if (nestedHitObjects == null)
nestedHitObjects = new List<DrawableHitObject>();
h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j);
h.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(d, j);
h.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j);
nestedHitObjects.Add(h);
nestedHitObjects.Value.Add(h);
}
/// <summary>
@ -211,7 +209,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
if (AllJudged)
return false;
if (NestedHitObjects != null)
if (HasNestedHitObjects)
foreach (var d in NestedHitObjects)
judgementOccurred |= d.UpdateJudgement(userTriggered);

View File

@ -64,7 +64,7 @@ namespace osu.Game.Rulesets
/// <summary>
/// Do not override this unless you are a legacy mode.
/// </summary>
public virtual int LegacyID => -1;
public virtual int? LegacyID => null;
/// <summary>
/// A unique short name to reference this ruleset in online requests.

View File

@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
var controlPoint = controlPointAt(obj.HitObject.StartTime);
obj.LifetimeStart = obj.HitObject.StartTime - timeRange / controlPoint.Multiplier;
if (obj.NestedHitObjects != null)
if (obj.HasNestedHitObjects)
{
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);

View File

@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
}
}
if (obj.NestedHitObjects != null)
if (obj.HasNestedHitObjects)
{
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);

View File

@ -4,8 +4,6 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using OpenTK;
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Screens.Menu
{
@ -22,8 +20,6 @@ namespace osu.Game.Screens.Menu
protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y);
protected override IEnumerable<Drawable> FlowingChildren => base.FlowingChildren.Reverse();
public override Anchor Origin => Anchor.Custom;
public override Vector2 OriginPosition

View File

@ -25,32 +25,29 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Play.BreaksOverlay;
using osu.Game.Screens.Ranking;
using osu.Game.Storyboards.Drawables;
using OpenTK;
namespace osu.Game.Screens.Play
{
public class Player : OsuScreen, IProvideCursor
public class Player : ScreenWithBeatmapBackground, IProvideCursor
{
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
protected override float BackgroundParallaxAmount => 0.1f;
public override bool ShowOverlaysOnEnter => false;
public Action RestartRequested;
public override bool AllowBeatmapRulesetChange => false;
public bool HasFailed { get; private set; }
public bool AllowPause { get; set; } = true;
public bool AllowLeadIn { get; set; } = true;
public bool AllowResults { get; set; } = true;
private Bindable<bool> mouseWheelDisabled;
private Bindable<double> userAudioOffset;
public int RestartCount;
public CursorContainer Cursor => RulesetContainer.Cursor;
@ -69,41 +66,27 @@ namespace osu.Game.Screens.Play
private APIAccess api;
private ScoreProcessor scoreProcessor;
protected RulesetContainer RulesetContainer;
#region User Settings
private Bindable<double> dimLevel;
private Bindable<double> blurLevel;
private Bindable<bool> showStoryboard;
private Bindable<bool> mouseWheelDisabled;
private Bindable<double> userAudioOffset;
private SampleChannel sampleRestart;
#endregion
private Container storyboardContainer;
private DrawableStoryboard storyboard;
private ScoreProcessor scoreProcessor;
protected RulesetContainer RulesetContainer;
private HUDOverlay hudOverlay;
private FailOverlay failOverlay;
private DrawableStoryboard storyboard;
private Container storyboardContainer;
private bool loadedSuccessfully => RulesetContainer?.Objects.Any() == true;
[BackgroundDependencyLoader]
private void load(AudioManager audio, OsuConfigManager config, APIAccess api)
private void load(AudioManager audio, APIAccess api, OsuConfigManager config)
{
this.api = api;
dimLevel = config.GetBindable<double>(OsuSetting.DimLevel);
blurLevel = config.GetBindable<double>(OsuSetting.BlurLevel);
showStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
sampleRestart = audio.Sample.Get(@"Gameplay/restart");
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
sampleRestart = audio.Sample.Get(@"Gameplay/restart");
userAudioOffset = config.GetBindable<double>(OsuSetting.AudioOffset);
WorkingBeatmap working = Beatmap.Value;
Beatmap beatmap;
@ -156,7 +139,6 @@ namespace osu.Game.Screens.Play
// the final usable gameplay clock with user-set offsets applied.
var offsetClock = new FramedOffsetClock(adjustableClock);
userAudioOffset = config.GetBindable<double>(OsuSetting.AudioOffset);
userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
userAudioOffset.TriggerChange();
@ -225,7 +207,7 @@ namespace osu.Game.Screens.Play
}
};
if (showStoryboard)
if (ShowStoryboard)
initializeStoryboard(false);
// Bind ScoreProcessor to ourselves
@ -245,19 +227,6 @@ namespace osu.Game.Screens.Play
mod.ApplyToClock(sourceClock);
}
private void initializeStoryboard(bool asyncLoad)
{
var beatmap = Beatmap.Value;
storyboard = beatmap.Storyboard.CreateDrawable(Beatmap.Value);
storyboard.Masking = true;
if (asyncLoad)
LoadComponentAsync(storyboard, storyboardContainer.Add);
else
storyboardContainer.Add(storyboard);
}
public void Restart()
{
sampleRestart?.Play();
@ -316,11 +285,6 @@ namespace osu.Game.Screens.Play
if (!loadedSuccessfully)
return;
dimLevel.ValueChanged += _ => updateBackgroundElements();
blurLevel.ValueChanged += _ => updateBackgroundElements();
showStoryboard.ValueChanged += _ => updateBackgroundElements();
updateBackgroundElements();
Content.Alpha = 0;
Content
.ScaleTo(0.7f)
@ -374,28 +338,6 @@ namespace osu.Game.Screens.Play
return true;
}
private void updateBackgroundElements()
{
if (!IsCurrentScreen) return;
const float duration = 800;
var opacity = 1 - (float)dimLevel;
if (showStoryboard && storyboard == null)
initializeStoryboard(true);
var beatmap = Beatmap.Value;
var storyboardVisible = showStoryboard && beatmap.Storyboard.HasDrawable;
storyboardContainer
.FadeColour(OsuColour.Gray(opacity), duration, Easing.OutQuint)
.FadeTo(storyboardVisible && opacity > 0 ? 1 : 0, duration, Easing.OutQuint);
(Background as BackgroundScreenBeatmap)?.BlurTo(new Vector2((float)blurLevel.Value * 25), duration, Easing.OutQuint);
Background?.FadeTo(beatmap.Background != null && (!storyboardVisible || !beatmap.Storyboard.ReplacesBackground) ? opacity : 0, duration, Easing.OutQuint);
}
private void fadeOut()
{
const float fade_out_duration = 250;
@ -409,5 +351,41 @@ namespace osu.Game.Screens.Play
}
protected override bool OnWheel(InputState state) => mouseWheelDisabled.Value && !pauseContainer.IsPaused;
private void initializeStoryboard(bool asyncLoad)
{
if (storyboardContainer == null)
return;
var beatmap = Beatmap.Value;
storyboard = beatmap.Storyboard.CreateDrawable();
storyboard.Masking = true;
if (asyncLoad)
LoadComponentAsync(storyboard, storyboardContainer.Add);
else
storyboardContainer.Add(storyboard);
}
protected override void UpdateBackgroundElements()
{
if (!IsCurrentScreen) return;
base.UpdateBackgroundElements();
if (ShowStoryboard && storyboard == null)
initializeStoryboard(true);
var beatmap = Beatmap.Value;
var storyboardVisible = ShowStoryboard && beatmap.Storyboard.HasDrawable;
storyboardContainer?
.FadeColour(OsuColour.Gray(BackgroundOpacity), BACKGROUND_FADE_DURATION, Easing.OutQuint)
.FadeTo(storyboardVisible && BackgroundOpacity > 0 ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint);
if (storyboardVisible && beatmap.Storyboard.ReplacesBackground)
Background?.FadeTo(0, BACKGROUND_FADE_DURATION, Easing.OutQuint);
}
}
}

View File

@ -9,7 +9,6 @@ using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Backgrounds;
using OpenTK;
using osu.Framework.Localisation;
using osu.Game.Screens.Menu;
@ -17,7 +16,7 @@ using osu.Game.Screens.Play.PlayerSettings;
namespace osu.Game.Screens.Play
{
public class PlayerLoader : OsuScreen
public class PlayerLoader : ScreenWithBeatmapBackground
{
private Player player;
@ -27,10 +26,6 @@ namespace osu.Game.Screens.Play
private bool showOverlays = true;
public override bool ShowOverlaysOnEnter => showOverlays;
public override bool AllowBeatmapRulesetChange => false;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
public PlayerLoader(Player player)
{
this.player = player;
@ -93,8 +88,6 @@ namespace osu.Game.Screens.Play
{
base.OnEntering(last);
Background.FadeTo(0.4f, 250);
Content.ScaleTo(0.7f);
contentIn();

View File

@ -15,7 +15,6 @@ namespace osu.Game.Screens.Play.PlayerSettings
private readonly PlayerSliderBar<double> dimSliderBar;
private readonly PlayerSliderBar<double> blurSliderBar;
private readonly PlayerCheckbox showStoryboardToggle;
private readonly PlayerCheckbox mouseWheelDisabledToggle;
public VisualSettings()
{
@ -35,8 +34,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
{
Text = "Toggles:"
},
showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" },
mouseWheelDisabledToggle = new PlayerCheckbox { LabelText = "Disable mouse wheel" }
showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" }
};
}
@ -46,7 +44,6 @@ namespace osu.Game.Screens.Play.PlayerSettings
dimSliderBar.Bindable = config.GetBindable<double>(OsuSetting.DimLevel);
blurSliderBar.Bindable = config.GetBindable<double>(OsuSetting.BlurLevel);
showStoryboardToggle.Bindable = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
mouseWheelDisabledToggle.Bindable = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
}
}
}

View File

@ -0,0 +1,57 @@
// 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.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Screens;
using osu.Game.Configuration;
using osu.Game.Screens.Backgrounds;
using OpenTK;
namespace osu.Game.Screens.Play
{
public abstract class ScreenWithBeatmapBackground : OsuScreen
{
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
public override bool AllowBeatmapRulesetChange => false;
protected const float BACKGROUND_FADE_DURATION = 800;
protected float BackgroundOpacity => 1 - (float)DimLevel;
#region User Settings
protected Bindable<double> DimLevel;
protected Bindable<double> BlurLevel;
protected Bindable<bool> ShowStoryboard;
#endregion
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
DimLevel = config.GetBindable<double>(OsuSetting.DimLevel);
BlurLevel = config.GetBindable<double>(OsuSetting.BlurLevel);
ShowStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
}
protected override void OnEntering(Screen last)
{
base.OnEntering(last);
DimLevel.ValueChanged += _ => UpdateBackgroundElements();
BlurLevel.ValueChanged += _ => UpdateBackgroundElements();
ShowStoryboard.ValueChanged += _ => UpdateBackgroundElements();
UpdateBackgroundElements();
}
protected virtual void UpdateBackgroundElements()
{
if (!IsCurrentScreen) return;
Background?.FadeTo(BackgroundOpacity, BACKGROUND_FADE_DURATION, Easing.OutQuint);
(Background as BackgroundScreenBeatmap)?.BlurTo(new Vector2((float)BlurLevel.Value * 25), BACKGROUND_FADE_DURATION, Easing.OutQuint);
}
}
}

View File

@ -41,19 +41,25 @@ namespace osu.Game.Screens.Select
/// <para>Higher depth to be put on the left, and lower to be put on the right.</para>
/// <para>Notice this is different to <see cref="Options.BeatmapOptionsOverlay"/>!</para>
/// </param>
public void AddButton(string text, Color4 colour, Action action, Key? hotkey = null, float depth = 0) => buttons.Add(new FooterButton
public void AddButton(string text, Color4 colour, Action action, Key? hotkey = null, float depth = 0)
{
Text = text,
Height = play_song_select_button_height,
Width = play_song_select_button_width,
Depth = depth,
SelectedColour = colour,
DeselectedColour = colour.Opacity(0.5f),
Hotkey = hotkey,
Hovered = updateModeLight,
HoverLost = updateModeLight,
Action = action,
});
var button = new FooterButton
{
Text = text,
Height = play_song_select_button_height,
Width = play_song_select_button_width,
Depth = depth,
SelectedColour = colour,
DeselectedColour = colour.Opacity(0.5f),
Hotkey = hotkey,
Hovered = updateModeLight,
HoverLost = updateModeLight,
Action = action,
};
buttons.Add(button);
buttons.SetLayoutPosition(button, -depth);
}
private readonly List<OverlayContainer> overlays = new List<OverlayContainer>();

View File

@ -95,7 +95,7 @@ namespace osu.Game.Screens.Select.Options
/// </param>
public void AddButton(string firstLine, string secondLine, FontAwesome icon, Color4 colour, Action action, Key? hotkey = null, float depth = 0)
{
buttonsContainer.Add(new BeatmapOptionsButton
var button = new BeatmapOptionsButton
{
FirstLineText = firstLine,
SecondLineText = secondLine,
@ -108,7 +108,10 @@ namespace osu.Game.Screens.Select.Options
action?.Invoke();
},
HotKey = hotkey
});
};
buttonsContainer.Add(button);
buttonsContainer.SetLayoutPosition(button, depth);
}
}
}

View File

@ -22,16 +22,14 @@ namespace osu.Game.Skinning
public LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager)
: base(skin)
{
storage = new LegacySkinResourceStore(skin, storage);
samples = audioManager.GetSampleManager(storage);
textures = new TextureStore(new RawTextureLoaderStore(storage));
}
private string getPathForFile(string filename) =>
SkinInfo.Files.FirstOrDefault(f => string.Equals(Path.GetFileNameWithoutExtension(f.Filename), filename, StringComparison.InvariantCultureIgnoreCase))?.FileInfo.StoragePath;
public override Drawable GetDrawableComponent(string componentName)
{
var texture = textures.Get(getPathForFile(componentName.Split('/').Last()));
var texture = textures.Get(componentName);
if (texture == null) return null;
return new Sprite
@ -42,6 +40,25 @@ namespace osu.Game.Skinning
};
}
public override SampleChannel GetSample(string sampleName) => samples.Get(getPathForFile(sampleName.Split('/').Last()));
public override SampleChannel GetSample(string sampleName) => samples.Get(sampleName);
private class LegacySkinResourceStore : IResourceStore<byte[]>
{
private readonly SkinInfo skin;
private readonly IResourceStore<byte[]> underlyingStore;
private string getPathForFile(string filename) =>
skin.Files.FirstOrDefault(f => string.Equals(Path.GetFileNameWithoutExtension(f.Filename), filename.Split('/').Last(), StringComparison.InvariantCultureIgnoreCase))?.FileInfo.StoragePath;
public LegacySkinResourceStore(SkinInfo skin, IResourceStore<byte[]> underlyingStore)
{
this.skin = skin;
this.underlyingStore = underlyingStore;
}
public Stream GetStream(string name) => underlyingStore.GetStream(getPathForFile(name));
byte[] IResourceStore<byte[]>.Get(string name) => underlyingStore.Get(getPathForFile(name));
}
}
}

View File

@ -33,6 +33,8 @@ namespace osu.Game.Storyboards.Drawables
}
}
public override bool RemoveCompletedTransforms => false;
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) =>
dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));

View File

@ -17,6 +17,8 @@ namespace osu.Game.Storyboards.Drawables
public bool FlipH { get; set; }
public bool FlipV { get; set; }
public override bool RemoveWhenNotAlive => false;
protected override Vector2 DrawScale
=> new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y);

View File

@ -17,6 +17,8 @@ namespace osu.Game.Storyboards.Drawables
public bool FlipH { get; set; }
public bool FlipV { get; set; }
public override bool RemoveWhenNotAlive => false;
protected override Vector2 DrawScale
=> new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y);

View File

@ -109,10 +109,13 @@ namespace osu.Game.Tests.Beatmaps
private Beatmap getBeatmap(string name)
{
var decoder = new LegacyBeatmapDecoder();
using (var resStream = openResource($"{resource_namespace}.{name}.osu"))
using (var stream = new StreamReader(resStream))
{
var decoder = Decoder.GetDecoder(stream);
((LegacyBeatmapDecoder)decoder).ApplyOffsets = false;
return decoder.DecodeBeatmap(stream);
}
}
private Stream openResource(string name)

View File

@ -294,12 +294,16 @@
<Compile Include="Online\API\DummyAPIAccess.cs" />
<Compile Include="Online\API\IAPIProvider.cs" />
<Compile Include="Online\API\APIDownloadRequest.cs" />
<Compile Include="Online\API\Requests\GetUserRecentActivitiesRequest.cs" />
<Compile Include="Online\API\Requests\GetUserRequest.cs" />
<Compile Include="Migrations\20180125143340_Settings.cs" />
<Compile Include="Migrations\20180125143340_Settings.Designer.cs">
<DependentUpon>20180125143340_Settings.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\20180131154205_AddMuteBinding.cs" />
<Compile Include="Overlays\Profile\Sections\Recent\DrawableRecentActivity.cs" />
<Compile Include="Overlays\Profile\Sections\Recent\MedalIcon.cs" />
<Compile Include="Overlays\Profile\Sections\Recent\PaginatedRecentActivityContainer.cs" />
<Compile Include="Overlays\Profile\SupporterIcon.cs" />
<Compile Include="Online\API\Requests\GetFriendsRequest.cs" />
<Compile Include="Overlays\Settings\DangerousSettingsButton.cs" />
@ -351,6 +355,10 @@
<Compile Include="Overlays\Profile\Sections\Ranks\ScoreModsContainer.cs" />
<Compile Include="Overlays\Settings\Sections\Gameplay\ScrollingSettings.cs" />
<Compile Include="Overlays\Settings\Sections\Maintenance\DeleteAllBeatmapsDialog.cs" />
<Compile Include="Overlays\VolumeOverlay.cs" />
<Compile Include="Overlays\Volume\MuteButton.cs" />
<Compile Include="Overlays\Volume\VolumeControlReceptor.cs" />
<Compile Include="Overlays\Volume\VolumeMeter.cs" />
<Compile Include="Rulesets\Configuration\IRulesetConfigManager.cs" />
<Compile Include="Rulesets\Configuration\RulesetConfigManager.cs" />
<Compile Include="Rulesets\Edit\Layers\BorderLayer.cs" />
@ -370,10 +378,11 @@
<Compile Include="Rulesets\Replays\ReplayFrame.cs" />
<Compile Include="Rulesets\Replays\Types\IConvertibleReplayFrame.cs" />
<Compile Include="Rulesets\Scoring\Legacy\LegacyScoreParser.cs" />
<Compile Include="Rulesets\UI\ScalableContainer.cs" />
<Compile Include="Screens\Play\ScreenWithBeatmapBackground.cs" />
<Compile Include="Screens\Play\PlayerSettings\VisualSettings.cs" />
<Compile Include="Rulesets\Objects\CatmullApproximator.cs" />
<Compile Include="Rulesets\UI\HitObjectContainer.cs" />
<Compile Include="Rulesets\UI\ScalableContainer.cs" />
<Compile Include="Rulesets\UI\Scrolling\Visualisers\SequentialSpeedChangeVisualiser.cs" />
<Compile Include="Rulesets\UI\Scrolling\Visualisers\ISpeedChangeVisualiser.cs" />
<Compile Include="Rulesets\UI\Scrolling\Visualisers\OverlappingSpeedChangeVisualiser.cs" />
@ -474,9 +483,6 @@
<Compile Include="Graphics\UserInterface\SimpleComboCounter.cs" />
<Compile Include="Graphics\UserInterface\StarCounter.cs" />
<Compile Include="Graphics\UserInterface\TwoLayerButton.cs" />
<Compile Include="Graphics\UserInterface\Volume\VolumeControl.cs" />
<Compile Include="Graphics\UserInterface\Volume\VolumeControlReceptor.cs" />
<Compile Include="Graphics\UserInterface\Volume\VolumeMeter.cs" />
<Compile Include="Input\Bindings\DatabasedKeyBinding.cs" />
<Compile Include="Input\Bindings\DatabasedKeyBindingContainer.cs" />
<Compile Include="Input\Bindings\GlobalActionContainer.cs" />