1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-13 08:32:57 +08:00

Merge branch 'master' into storyboards

This commit is contained in:
Damnae 2017-09-11 12:23:32 +02:00
commit 47cd1d486b
75 changed files with 1320 additions and 616 deletions

@ -1 +1 @@
Subproject commit 4e7ea6af4f59f21f6afc522fb063c05417e1a5fe
Subproject commit cb48c7bef8c5909472c70e09059ee6ea5bf6fc8a

@ -1 +1 @@
Subproject commit f6042e1cb37cfad6c879d0e1245f7880c7fcd5f5
Subproject commit a4418111f8ed2350a6fd46fe69258884f0757745

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Desktop.Deploy</RootNamespace>
<AssemblyName>osu.Desktop.Deploy</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>

View File

@ -12,50 +12,104 @@ namespace osu.Desktop.Tests.Visual
{
public override string Description => "BeatmapDetails tab of BeatmapDetailArea";
private readonly BeatmapDetails details;
public TestCaseBeatmapDetails()
{
BeatmapDetails details;
Add(details = new BeatmapDetails
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(150),
Beatmap = new BeatmapInfo
});
AddStep("beatmap all metrics", () => details.Beatmap = new BeatmapInfo
{
Version = "All Metrics",
Metadata = new BeatmapMetadata
{
Version = "VisualTest",
Metadata = new BeatmapMetadata
{
Source = "Some guy",
Tags = "beatmap metadata example with a very very long list of tags and not much creativity",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 7,
ApproachRate = 3.5f,
OverallDifficulty = 5.7f,
DrainRate = 1,
},
StarDifficulty = 5.3f,
Metrics = new BeatmapMetrics
{
Ratings = Enumerable.Range(0, 10),
Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6),
Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6),
},
Source = "osu!lazer",
Tags = "this beatmap has all the metrics",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 7,
DrainRate = 1,
OverallDifficulty = 5.7f,
ApproachRate = 3.5f,
},
StarDifficulty = 5.3f,
Metrics = new BeatmapMetrics
{
Ratings = Enumerable.Range(0, 10),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
},
});
AddRepeatStep("fail values", newRetryAndFailValues, 10);
}
AddStep("beatmap ratings", () => details.Beatmap = new BeatmapInfo
{
Version = "Only Ratings",
Metadata = new BeatmapMetadata
{
Source = "osu!lazer",
Tags = "this beatmap has ratings metrics but not retries or fails",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 6,
DrainRate = 9,
OverallDifficulty = 6,
ApproachRate = 6,
},
StarDifficulty = 4.8f,
Metrics = new BeatmapMetrics
{
Ratings = Enumerable.Range(0, 10),
},
});
private int lastRange = 1;
AddStep("beatmap fails retries", () => details.Beatmap = new BeatmapInfo
{
Version = "Only Retries and Fails",
Metadata = new BeatmapMetadata
{
Source = "osu!lazer",
Tags = "this beatmap has retries and fails but no ratings",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 3.7f,
DrainRate = 6,
OverallDifficulty = 6,
ApproachRate = 7,
},
StarDifficulty = 2.91f,
Metrics = new BeatmapMetrics
{
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
},
});
private void newRetryAndFailValues()
{
details.Beatmap.Metrics.Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6);
details.Beatmap.Metrics.Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6);
details.Beatmap = details.Beatmap;
lastRange += 100;
AddStep("beatmap no metrics", () => details.Beatmap = new BeatmapInfo
{
Version = "No Metrics",
Metadata = new BeatmapMetadata
{
Source = "osu!lazer",
Tags = "this beatmap has no metrics",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 5,
DrainRate = 5,
OverallDifficulty = 5.5f,
ApproachRate = 6.5f,
},
StarDifficulty = 1.97f,
Metrics = new BeatmapMetrics(),
});
AddStep("null beatmap", () => details.Beatmap = null);
}
}
}
}

View File

@ -41,12 +41,14 @@ namespace osu.Desktop.Tests.Visual
{
new BeatmapSetInfo
{
OnlineBeatmapSetID = 578332,
Metadata = new BeatmapMetadata
{
Title = @"OrVid",
Artist = @"An",
Author = @"RLC",
Source = @"",
Tags = @"acuticnotes an-fillnote revid tear tearvid encrpted encryption axi axivid quad her hervid recoll",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@ -71,12 +73,14 @@ namespace osu.Desktop.Tests.Visual
},
new BeatmapSetInfo
{
OnlineBeatmapSetID = 599627,
Metadata = new BeatmapMetadata
{
Title = @"tiny lamp",
Artist = @"fhana",
Author = @"Sotarks",
Source = @"ぎんぎつね",
Tags = @"lantis junichi sato yuxuki waga kevin mitsunaga towana gingitsune opening op full ver version kalibe collab collaboration",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@ -101,12 +105,14 @@ namespace osu.Desktop.Tests.Visual
},
new BeatmapSetInfo
{
OnlineBeatmapSetID = 513268,
Metadata = new BeatmapMetadata
{
Title = @"At Gwanghwamun",
Artist = @"KYUHYUN",
Author = @"Cerulean Veyron",
Source = @"",
Tags = @"soul ballad kh super junior sj suju 슈퍼주니어 kt뮤직 sm엔터테인먼트 s.m.entertainment kt music 1st mini album ep",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@ -146,12 +152,14 @@ namespace osu.Desktop.Tests.Visual
},
new BeatmapSetInfo
{
OnlineBeatmapSetID = 586841,
Metadata = new BeatmapMetadata
{
Title = @"RHAPSODY OF BLUE SKY",
Artist = @"fhana",
Author = @"[Kamiya]",
Source = @"小林さんちのメイドラゴン",
Tags = @"kobayashi san chi no maidragon aozora no opening anime maid dragon oblivion karen dynamix imoutosan pata-mon gxytcgxytc",
},
OnlineInfo = new BeatmapSetOnlineInfo
{

View File

@ -41,8 +41,20 @@ namespace osu.Desktop.Tests.Visual
RelativeChildSize = new Vector2(1, 10000),
Children = new[]
{
new DrawableNote(new Note { StartTime = 5000 }, ManiaAction.Key1) { AccentColour = Color4.Red },
new DrawableNote(new Note { StartTime = 6000 }, ManiaAction.Key1) { AccentColour = Color4.Red }
new DrawableNote(new Note(), ManiaAction.Key1)
{
Y = 5000,
LifetimeStart = double.MinValue,
LifetimeEnd = double.MaxValue,
AccentColour = Color4.Red
},
new DrawableNote(new Note(), ManiaAction.Key1)
{
Y = 6000,
LifetimeStart = double.MinValue,
LifetimeEnd = double.MaxValue,
AccentColour = Color4.Red
}
}
}
}
@ -63,11 +75,14 @@ namespace osu.Desktop.Tests.Visual
RelativeChildSize = new Vector2(1, 10000),
Children = new[]
{
new DrawableHoldNote(new HoldNote
new DrawableHoldNote(new HoldNote(), ManiaAction.Key1)
{
StartTime = 5000,
Duration = 1000
}, ManiaAction.Key1) { AccentColour = Color4.Red }
Y = 5000,
Height = 1000,
LifetimeStart = double.MinValue,
LifetimeEnd = double.MaxValue,
AccentColour = Color4.Red
}
}
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
@ -13,6 +14,8 @@ using osu.Game.Rulesets.Mania.Timing;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Timing;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Desktop.Tests.Visual
{
@ -29,6 +32,8 @@ namespace osu.Desktop.Tests.Visual
public TestCaseManiaPlayfield()
{
var rng = new Random(1337);
AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal));
AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal));
AddStep("Left special style", () => createPlayfield(4, SpecialColumnPosition.Left));
@ -43,6 +48,21 @@ namespace osu.Desktop.Tests.Visual
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(false, true));
AddStep("Notes with gravity", () => createPlayfieldWithNotes(true));
AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true, true));
AddStep("Hit explosion", () =>
{
var playfield = createPlayfield(4, SpecialColumnPosition.Normal);
int col = rng.Next(0, 4);
var note = new DrawableNote(new Note { Column = col }, ManiaAction.Key1)
{
Judgement = new ManiaJudgement { Result = HitResult.Hit },
AccentColour = playfield.Columns.ElementAt(col).AccentColour
};
playfield.OnJudgement(note);
});
}
[BackgroundDependencyLoader]
@ -56,7 +76,7 @@ namespace osu.Desktop.Tests.Visual
TimingPoint = { BeatLength = 1000 }
}, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic);
private void createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false)
private ManiaPlayfield createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false)
{
Clear();
@ -72,6 +92,8 @@ namespace osu.Desktop.Tests.Visual
});
playfield.Inverted.Value = inverted;
return playfield;
}
private void createPlayfieldWithNotes(bool gravity, bool inverted = false)

View File

@ -32,7 +32,7 @@ namespace osu.Desktop.Tests.Visual
backingDatabase.CreateTable<StoreVersion>();
rulesets = new RulesetStore(backingDatabase);
manager = new BeatmapManager(storage, null, backingDatabase, rulesets);
manager = new BeatmapManager(storage, null, backingDatabase, rulesets, null);
for (int i = 0; i < 100; i += 10)
manager.Import(createTestBeatmapSet(i));

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Desktop.Tests</RootNamespace>
<AssemblyName>osu.Desktop.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

View File

@ -22,7 +22,7 @@
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
<SignAssembly>false</SignAssembly>
<TargetZone>LocalIntranet</TargetZone>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>

View File

@ -22,7 +22,7 @@
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
<SignAssembly>false</SignAssembly>
<TargetZone>LocalIntranet</TargetZone>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>

View File

@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Catch.UI
var additive = createCatcherSprite();
additive.RelativePositionAxes = Axes.Both;
additive.BlendingMode = BlendingMode.Additive;
additive.Blending = BlendingMode.Additive;
additive.Position = Position;
additive.Scale = Scale;

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game.Rulesets.Catch</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Catch</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
@ -9,6 +10,7 @@ using OpenTK;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
@ -23,6 +25,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
private readonly BodyPiece bodyPiece;
private readonly Container<DrawableHoldNoteTick> tickContainer;
private readonly Container glowContainer;
/// <summary>
/// Time at which the user started holding this hold note. Null if the user is not holding this hold note.
@ -42,13 +45,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
AddRange(new Drawable[]
{
// For now the body piece covers the entire height of the container
// whereas possibly in the future we don't want to extend under the head/tail.
// This will be fixed when new designs are given or the current design is finalized.
bodyPiece = new BodyPiece
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
},
tickContainer = new Container<DrawableHoldNoteTick>
{
@ -65,6 +66,19 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre
},
// The hit object itself cannot be used for the glow because the tail overshoots it
// So a specialized container that is updated to contain the tail height is used
glowContainer = new Container
{
RelativeSizeAxes = Axes.X,
Masking = true,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
});
@ -83,6 +97,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
AddNested(tail);
}
protected override void LoadComplete()
{
base.LoadComplete();
updateGlow();
}
public override Color4 AccentColour
{
get { return base.AccentColour; }
@ -97,13 +118,42 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
bodyPiece.AccentColour = value;
head.AccentColour = value;
tail.AccentColour = value;
updateGlow();
}
}
private void updateGlow()
{
if (!IsLoaded)
return;
glowContainer.EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = AccentColour.Opacity(0.5f),
Radius = 10,
Hollow = true
};
}
protected override void UpdateState(ArmedState state)
{
}
protected override void Update()
{
base.Update();
// Make the body piece not lie under the head note
bodyPiece.Y = head.Height;
bodyPiece.Height = DrawHeight - head.Height;
// Make the glowContainer "contain" the height of the tail note, keeping in mind
// that the tail note overshoots the height of this hit object
glowContainer.Height = DrawHeight + tail.Height;
}
public bool OnPressed(ManiaAction action)
{
// Make sure the action happened within the body of the hold note
@ -153,6 +203,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativePositionAxes = Axes.None;
Y = 0;
// Life time managed by the parent DrawableHoldNote
LifetimeStart = double.MinValue;
LifetimeEnd = double.MaxValue;
ApplyGlow = false;
}
public override bool OnPressed(ManiaAction action)
@ -190,6 +246,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativePositionAxes = Axes.None;
Y = 0;
// Life time managed by the parent DrawableHoldNote
LifetimeStart = double.MinValue;
LifetimeEnd = double.MaxValue;
ApplyGlow = false;
}
protected override ManiaJudgement CreateJudgement() => new HoldNoteTailJudgement();

View File

@ -23,11 +23,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// </summary>
public Func<double?> HoldStartTime;
/// <summary>
/// References whether the user is currently holding the hold note.
/// </summary>
public Func<bool> IsHolding;
private readonly Container glowContainer;
public DrawableHoldNoteTick(HoldNoteTick hitObject)
@ -36,9 +31,15 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
Y = (float)HitObject.StartTime;
RelativeSizeAxes = Axes.X;
Size = new Vector2(1);
// Life time managed by the parent DrawableHoldNote
LifetimeStart = double.MinValue;
LifetimeEnd = double.MaxValue;
Children = new[]
{
glowContainer = new CircularContainer
@ -58,9 +59,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
};
// Set the default glow
AccentColour = Color4.White;
}
public override Color4 AccentColour
@ -112,7 +110,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (Judgement.Result != HitResult.None)
return;
if (IsHolding?.Invoke() != true)
if (HoldStartTime?.Invoke() == null)
return;
UpdateJudgement(true);

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using OpenTK.Graphics;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
@ -20,6 +21,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null)
: base(hitObject)
{
RelativePositionAxes = Axes.Y;
HitObject = hitObject;
if (action != null)

View File

@ -2,8 +2,10 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Extensions.Color4Extensions;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
@ -16,13 +18,19 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// </summary>
public class DrawableNote : DrawableManiaHitObject<Note>, IKeyBindingHandler<ManiaAction>
{
/// <summary>
/// Gets or sets whether this <see cref="DrawableNote"/> should apply glow to itself.
/// </summary>
protected bool ApplyGlow = true;
private readonly NotePiece headPiece;
public DrawableNote(Note hitObject, ManiaAction action)
: base(hitObject, action)
{
RelativeSizeAxes = Axes.X;
Height = 100;
AutoSizeAxes = Axes.Y;
Masking = true;
Add(headPiece = new NotePiece
{
@ -31,6 +39,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
});
}
protected override void LoadComplete()
{
base.LoadComplete();
updateGlow();
}
public override Color4 AccentColour
{
get { return base.AccentColour; }
@ -41,9 +56,28 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
base.AccentColour = value;
headPiece.AccentColour = value;
updateGlow();
}
}
private void updateGlow()
{
if (!IsLoaded)
return;
if (!ApplyGlow)
return;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = AccentColour.Opacity(0.5f),
Radius = 10,
Hollow = true
};
}
protected override void CheckJudgement(bool userTriggered)
{
if (!userTriggered)
@ -71,12 +105,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected override void UpdateState(ArmedState state)
{
switch (State)
{
case ArmedState.Hit:
Colour = Color4.Green;
break;
}
}
public virtual bool OnPressed(ManiaAction action)

View File

@ -1,7 +1,10 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Caching;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@ -14,22 +17,61 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
/// </summary>
internal class BodyPiece : Container, IHasAccentColour
{
private readonly Box box;
private readonly Container subtractionLayer;
private readonly Drawable background;
private readonly BufferedContainer foreground;
private readonly BufferedContainer subtractionContainer;
public BodyPiece()
{
RelativeSizeAxes = Axes.Both;
Blending = BlendingMode.Additive;
Children = new[]
{
box = new Box
background = new Box { RelativeSizeAxes = Axes.Both },
foreground = new BufferedContainer
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.3f
CacheDrawnFrameBuffer = true,
Children = new Drawable[]
{
new Box { RelativeSizeAxes = Axes.Both },
subtractionContainer = new BufferedContainer
{
RelativeSizeAxes = Axes.Both,
// This is needed because we're blending with another object
BackgroundColour = Color4.White.Opacity(0),
CacheDrawnFrameBuffer = true,
// The 'hole' is achieved by subtracting the result of this container with the parent
Blending = new BlendingParameters { AlphaEquation = BlendingEquation.ReverseSubtract },
Child = subtractionLayer = new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
// Height computed in Update
Width = 1,
Masking = true,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
}
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
updateAccentColour();
}
private Color4 accentColour;
public Color4 AccentColour
{
@ -40,8 +82,51 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
return;
accentColour = value;
box.Colour = accentColour;
updateAccentColour();
}
}
private Cached subtractionCache = new Cached();
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & Invalidation.DrawSize) > 0)
subtractionCache.Invalidate();
return base.Invalidate(invalidation, source, shallPropagate);
}
protected override void Update()
{
base.Update();
if (!subtractionCache.IsValid)
{
subtractionLayer.Width = 5;
subtractionLayer.Height = Math.Max(0, DrawHeight - DrawWidth);
subtractionLayer.EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.White,
Type = EdgeEffectType.Glow,
Radius = DrawWidth
};
foreground.ForceRedraw();
subtractionContainer.ForceRedraw();
subtractionCache.Validate();
}
}
private void updateAccentColour()
{
if (!IsLoaded)
return;
foreground.Colour = AccentColour.Opacity(0.4f);
background.Colour = AccentColour.Opacity(0.2f);
subtractionCache.Invalidate();
}
}
}

View File

@ -37,6 +37,17 @@ namespace osu.Game.Rulesets.Mania.Objects
}
}
public override int Column
{
get { return base.Column; }
set
{
base.Column = value;
Head.Column = value;
Tail.Column = value;
}
}
/// <summary>
/// The head note of the hold.
/// </summary>
@ -80,7 +91,8 @@ namespace osu.Game.Rulesets.Mania.Objects
{
ret.Add(new HoldNoteTick
{
StartTime = t
StartTime = t,
Column = Column
});
}

View File

@ -8,6 +8,6 @@ namespace osu.Game.Rulesets.Mania.Objects
{
public abstract class ManiaHitObject : HitObject, IHasColumn
{
public int Column { get; set; }
public virtual int Column { get; set; }
}
}

View File

@ -36,6 +36,9 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly Container hitTargetBar;
private readonly Container keyIcon;
internal readonly Container TopLevelContainer;
private readonly Container explosionContainer;
protected override Container<Drawable> Content => content;
private readonly Container<Drawable> content;
@ -98,6 +101,11 @@ namespace osu.Game.Rulesets.Mania.UI
{
Pressed = onPressed,
Released = onReleased
},
explosionContainer = new Container
{
Name = "Hit explosions",
RelativeSizeAxes = Axes.Both
}
}
},
@ -136,8 +144,11 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
}
}
},
TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
};
TopLevelContainer.Add(explosionContainer.CreateProxy());
}
public override Axes RelativeSizeAxes => Axes.Y;
@ -194,6 +205,14 @@ namespace osu.Game.Rulesets.Mania.UI
HitObjects.Add(hitObject);
}
public override void OnJudgement(DrawableHitObject<ManiaHitObject, ManiaJudgement> judgedObject)
{
if (judgedObject.Judgement.Result != HitResult.Hit)
return;
explosionContainer.Add(new HitExplosion(judgedObject));
}
private bool onPressed(ManiaAction action)
{
if (action == Action)

View File

@ -0,0 +1,66 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.UI
{
internal class HitExplosion : CompositeDrawable
{
private readonly Box inner;
public HitExplosion(DrawableHitObject<ManiaHitObject, ManiaJudgement> judgedObject)
{
bool isTick = judgedObject is DrawableHoldNoteTick;
Anchor = Anchor.TopCentre;
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
Size = new Vector2(isTick ? 0.5f : 1);
FillMode = FillMode.Fit;
Blending = BlendingMode.Additive;
Color4 accent = isTick ? Color4.White : judgedObject.AccentColour;
InternalChild = new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = 1,
BorderColour = accent,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = accent,
Radius = 10,
Hollow = true
},
Child = inner = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = accent,
Alpha = 1,
AlwaysPresent = true,
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
this.ScaleTo(2f, 600, Easing.OutQuint).FadeOut(500);
inner.FadeOut(250);
}
}
}

View File

@ -65,21 +65,21 @@ namespace osu.Game.Rulesets.Mania.UI
Inverted.Value = true;
Container topLevelContainer;
InternalChildren = new Drawable[]
{
new Container
{
Name = "Playfield elements",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Masking = true,
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Children = new Drawable[]
{
new Container
{
Name = "Masked elements",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Name = "Columns mask",
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Masking = true,
@ -87,6 +87,7 @@ namespace osu.Game.Rulesets.Mania.UI
{
new Box
{
Name = "Background",
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
},
@ -98,27 +99,28 @@ namespace osu.Game.Rulesets.Mania.UI
Direction = FillDirection.Horizontal,
Padding = new MarginPadding { Left = 1, Right = 1 },
Spacing = new Vector2(1, 0)
}
},
}
},
new Container
{
Name = "Barlines mask",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = HIT_TARGET_POSITION },
Children = new[]
RelativeSizeAxes = Axes.Y,
Width = 1366, // Bar lines should only be masked on the vertical axis
BypassAutoSizeAxes = Axes.Both,
Masking = true,
Child = content = new Container
{
content = new Container
{
Name = "Bar lines",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
// Width is set in the Update method
}
Name = "Bar lines",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
}
}
},
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
}
}
};
@ -132,6 +134,8 @@ namespace osu.Game.Rulesets.Mania.UI
c.IsSpecial = isSpecialColumn(i);
c.Action = c.IsSpecial ? ManiaAction.Special : currentAction++;
topLevelContainer.Add(c.TopLevelContainer.CreateProxy());
columns.Add(c);
AddNested(c);
}
@ -177,6 +181,8 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
public override void OnJudgement(DrawableHitObject<ManiaHitObject, ManiaJudgement> judgedObject) => columns[judgedObject.HitObject.Column].OnJudgement(judgedObject);
/// <summary>
/// Whether the column index is a special column for this playfield.
/// </summary>

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game.Rulesets.Mania</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Mania</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@ -83,6 +83,7 @@
<Compile Include="Timing\GravityScrollingContainer.cs" />
<Compile Include="Timing\ScrollingAlgorithm.cs" />
<Compile Include="UI\Column.cs" />
<Compile Include="UI\HitExplosion.cs" />
<Compile Include="UI\ManiaRulesetContainer.cs" />
<Compile Include="UI\ManiaPlayfield.cs" />
<Compile Include="ManiaRuleset.cs" />

View File

@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
new Box
{
Size = new Vector2(width),
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Alpha = 0.5f,

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
new TrianglesPiece
{
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Alpha = 0.5f,
}
};

View File

@ -16,14 +16,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
BlendingMode = BlendingMode.Additive;
Blending = BlendingMode.Additive;
Alpha = 0;
Children = new Drawable[]
{
new TrianglesPiece
{
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f,
}

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
BlendingMode = BlendingMode.Additive;
Blending = BlendingMode.Additive;
Alpha = 0;
Children = new Drawable[]

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Alpha = 0.5f
}
};

View File

@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
this.slider = slider;
Masking = true;
AutoSizeAxes = Axes.Both;
BlendingMode = BlendingMode.Additive;
Blending = BlendingMode.Additive;
Origin = Anchor.Centre;
BorderThickness = 10;
BorderColour = Color4.Orange;

View File

@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
path = new Path
{
BlendingMode = BlendingMode.None,
Blending = BlendingMode.None,
},
}
},

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
this.isEnd = isEnd;
AutoSizeAxes = Axes.Both;
BlendingMode = BlendingMode.Additive;
Blending = BlendingMode.Additive;
Origin = Anchor.Centre;
Children = new Drawable[]

View File

@ -51,6 +51,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
foreach (OsuDifficultyHitObject h in onScreen)
{
// ReSharper disable once PossibleNullReferenceException (resharper not smart enough to understand IEnumerator.MoveNext())
h.TimeUntilHit -= latest.DeltaTime;
// Calculate reading strain here
}

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game.Rulesets.Osu</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Osu</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>

View File

@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Origin = Anchor.Centre,
Alpha = 0,
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Masking = true,
Children = new[]
{
@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = target_ring_thick_border,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Children = new Drawable[]
{
new Box

View File

@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Alpha = 0,
AlwaysPresent = true
}

View File

@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Taiko.UI
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 0,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
},
centre = new Sprite
{
@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Taiko.UI
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.7f),
Alpha = 0,
BlendingMode = BlendingMode.Additive
Blending = BlendingMode.Additive
}
};
}

View File

@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.UI
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
},
new HitTarget
{
@ -126,14 +126,14 @@ namespace osu.Game.Rulesets.Taiko.UI
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
BlendingMode = BlendingMode.Additive
Blending = BlendingMode.Additive
},
judgementContainer = new Container<DrawableTaikoJudgement>
{
Name = "Judgements",
RelativeSizeAxes = Axes.Y,
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
BlendingMode = BlendingMode.Additive
Blending = BlendingMode.Additive
},
}
},

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game.Rulesets.Taiko</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Taiko</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

View File

@ -7,7 +7,7 @@
<OutputType>Library</OutputType>
<RootNamespace>osu.Game.Tests</RootNamespace>
<AssemblyName>osu.Game.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>

View File

@ -20,6 +20,9 @@ using osu.Game.IPC;
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
using SQLite.Net;
using osu.Game.Online.API.Requests;
using System.Threading.Tasks;
using osu.Game.Online.API;
namespace osu.Game.Beatmaps
{
@ -63,6 +66,10 @@ namespace osu.Game.Beatmaps
private readonly BeatmapStore beatmaps;
private readonly APIAccess api;
private readonly List<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>();
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private BeatmapIPCChannel ipc;
@ -76,7 +83,7 @@ namespace osu.Game.Beatmaps
/// </summary>
public Func<Storage> GetStableStorage { private get; set; }
public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, IIpcHost importHost = null)
public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null)
{
beatmaps = new BeatmapStore(connection);
beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s);
@ -88,6 +95,7 @@ namespace osu.Game.Beatmaps
this.files = files;
this.connection = connection;
this.rulesets = rulesets;
this.api = api;
if (importHost != null)
ipc = new BeatmapIPCChannel(importHost, this);
@ -177,6 +185,74 @@ namespace osu.Game.Beatmaps
beatmaps.Add(beatmapSetInfo);
}
/// <summary>
/// Downloads a beatmap.
/// </summary>
/// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
/// <returns>A new <see cref="DownloadBeatmapSetRequest"/>, or an existing one if a download is already in progress.</returns>
public DownloadBeatmapSetRequest Download(BeatmapSetInfo beatmapSetInfo)
{
var existing = GetExistingDownload(beatmapSetInfo);
if (existing != null) return existing;
if (api == null) return null;
ProgressNotification downloadNotification = new ProgressNotification
{
Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
};
var request = new DownloadBeatmapSetRequest(beatmapSetInfo);
request.DownloadProgressed += progress =>
{
downloadNotification.State = ProgressNotificationState.Active;
downloadNotification.Progress = progress;
};
request.Success += data =>
{
downloadNotification.State = ProgressNotificationState.Completed;
using (var stream = new MemoryStream(data))
using (var archive = new OszArchiveReader(stream))
Import(archive);
currentDownloads.Remove(request);
};
request.Failure += data =>
{
downloadNotification.State = ProgressNotificationState.Completed;
Logger.Error(data, "Failed to get beatmap download information");
currentDownloads.Remove(request);
};
downloadNotification.CancelRequested += () =>
{
request.Cancel();
currentDownloads.Remove(request);
downloadNotification.State = ProgressNotificationState.Cancelled;
return true;
};
currentDownloads.Add(request);
PostNotification?.Invoke(downloadNotification);
// don't run in the main api queue as this is a long-running task.
Task.Run(() => request.Perform(api));
return request;
}
/// <summary>
/// Get an existing download request if it exists.
/// </summary>
/// <param name="beatmap">The <see cref="BeatmapSetInfo"/> whose download request is wanted.</param>
/// <returns>The <see cref="DownloadBeatmapSetRequest"/> object if it exists, or null.</returns>
public DownloadBeatmapSetRequest GetExistingDownload(BeatmapSetInfo beatmap) => currentDownloads.Find(d => d.BeatmapSet.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID);
/// <summary>
/// Delete a beatmap from the manager.
/// Is a no-op for already deleted beatmaps.

View File

@ -87,7 +87,9 @@ namespace osu.Game.Beatmaps
{
if (track != null) return track;
track = GetTrack();
// we want to ensure that we always have a track, even if it's a fake one.
track = GetTrack() ?? new TrackVirtual();
applyRateAdjustments();
return track;
}

View File

@ -131,7 +131,7 @@ namespace osu.Game.Graphics.Cursor
},
AdditiveLayer = new Sprite
{
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Colour = colour.Pink,
Alpha = 0,
Texture = textures.Get(@"Cursor/menu-cursor-additive"),

View File

@ -99,7 +99,8 @@ namespace osu.Game.Graphics.UserInterface
colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In);
flash();
this.Delay(click_duration).Schedule(delegate {
this.Delay(click_duration).Schedule(delegate
{
colourContainer.ResizeTo(new Vector2(0.8f, 1f));
spriteText.Spacing = Vector2.Zero;
glowContainer.FadeOut();
@ -140,7 +141,7 @@ namespace osu.Game.Graphics.UserInterface
colourContainer.Add(flash);
flash.Colour = ButtonColour;
flash.BlendingMode = BlendingMode.Additive;
flash.Blending = BlendingMode.Additive;
flash.Alpha = 0.3f;
flash.FadeOutFromOne(click_duration);
flash.Expire();

View File

@ -63,7 +63,7 @@ namespace osu.Game.Graphics.UserInterface
hover = new Box
{
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Colour = Color4.White.Opacity(0.1f),
Alpha = 0,
},

View File

@ -96,7 +96,7 @@ namespace osu.Game.Graphics.UserInterface
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuDropdownMenuItem(item) { AccentColour = accentColour };
#region DrawableOsuDropdownMenuItem
protected class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour
public class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour
{
private Color4? accentColour;
public Color4 AccentColour

View File

@ -15,11 +15,7 @@ namespace osu.Game.Graphics.UserInterface.Volume
{
private readonly VolumeMeter volumeMeterMaster;
private void volumeChanged(double newVolume)
{
Show();
schedulePopOut();
}
protected override bool BlockPassThroughMouse => false;
public VolumeControl()
{
@ -85,6 +81,12 @@ namespace osu.Game.Graphics.UserInterface.Volume
return false;
}
private void volumeChanged(double newVolume)
{
Show();
schedulePopOut();
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{

View File

@ -0,0 +1,24 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using System;
namespace osu.Game.Online.API.Requests
{
public class DownloadBeatmapSetRequest : APIDownloadRequest
{
public readonly BeatmapSetInfo BeatmapSet;
public Action<float> DownloadProgressed;
public DownloadBeatmapSetRequest(BeatmapSetInfo set)
{
BeatmapSet = set;
Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total);
}
protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download";
}
}

View File

@ -106,9 +106,15 @@ namespace osu.Game
connection.CreateTable<StoreVersion>();
dependencies.Cache(API = new APIAccess
{
Username = LocalConfig.Get<string>(OsuSetting.Username),
Token = LocalConfig.Get<string>(OsuSetting.Token)
});
dependencies.Cache(RulesetStore = new RulesetStore(connection));
dependencies.Cache(FileStore = new FileStore(connection, Host.Storage));
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, connection, RulesetStore, Host));
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, connection, RulesetStore, API, Host));
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, connection, Host, BeatmapManager, RulesetStore));
dependencies.Cache(KeyBindingStore = new KeyBindingStore(connection, RulesetStore));
dependencies.Cache(new OsuColour());
@ -144,12 +150,6 @@ namespace osu.Game
Beatmap = new NonNullableBindable<WorkingBeatmap>(defaultBeatmap);
BeatmapManager.DefaultBeatmap = defaultBeatmap;
dependencies.Cache(API = new APIAccess
{
Username = LocalConfig.Get<string>(OsuSetting.Username),
Token = LocalConfig.Get<string>(OsuSetting.Token)
});
Beatmap.ValueChanged += b =>
{
// compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)

View File

@ -4,8 +4,6 @@
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using System.IO;
using System.Threading.Tasks;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -20,8 +18,8 @@ using osu.Framework.Input;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Framework.Logging;
using osu.Game.Beatmaps.IO;
using osu.Game.Overlays.Notifications;
using osu.Game.Online.API.Requests;
namespace osu.Game.Overlays.Direct
{
@ -97,6 +95,11 @@ namespace osu.Game.Overlays.Direct
},
}
});
var downloadRequest = beatmaps.GetExistingDownload(SetInfo);
if (downloadRequest != null)
attachDownload(downloadRequest);
}
protected override bool OnHover(InputState state)
@ -115,23 +118,8 @@ namespace osu.Game.Overlays.Direct
base.OnHoverLost(state);
}
// this should eventually be moved to a more central place, like BeatmapManager.
private DownloadBeatmapSetRequest downloadRequest;
protected void StartDownload()
{
if (api == null) return;
// we already have an active download running.
if (downloadRequest != null)
{
content.MoveToX(-5, 50, Easing.OutSine).Then()
.MoveToX(5, 100, Easing.InOutSine).Then()
.MoveToX(-5, 100, Easing.InOutSine).Then()
.MoveToX(0, 50, Easing.InSine).Then();
return;
}
if (!api.LocalUser.Value.IsSupporter)
{
notifications.Post(new SimpleNotification
@ -142,72 +130,43 @@ namespace osu.Game.Overlays.Direct
return;
}
if (beatmaps.GetExistingDownload(SetInfo) != null)
{
// we already have an active download running.
content.MoveToX(-5, 50, Easing.OutSine).Then()
.MoveToX(5, 100, Easing.InOutSine).Then()
.MoveToX(-5, 100, Easing.InOutSine).Then()
.MoveToX(0, 50, Easing.InSine).Then();
return;
}
var request = beatmaps.Download(SetInfo);
attachDownload(request);
}
private void attachDownload(DownloadBeatmapSetRequest request)
{
progressBar.FadeIn(400, Easing.OutQuint);
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
progressBar.Current.Value = 0;
ProgressNotification downloadNotification = new ProgressNotification
{
Text = $"Downloading {SetInfo.Metadata.Artist} - {SetInfo.Metadata.Title}",
};
downloadRequest = new DownloadBeatmapSetRequest(SetInfo);
downloadRequest.Failure += e =>
request.Failure += e =>
{
progressBar.Current.Value = 0;
progressBar.FadeOut(500);
downloadNotification.State = ProgressNotificationState.Completed;
Logger.Error(e, "Failed to get beatmap download information");
downloadRequest = null;
};
downloadRequest.Progress += (current, total) =>
{
float progress = (float)current / total;
request.DownloadProgressed += progress => progressBar.Current.Value = progress;
progressBar.Current.Value = progress;
downloadNotification.State = ProgressNotificationState.Active;
downloadNotification.Progress = progress;
};
downloadRequest.Success += data =>
request.Success += data =>
{
progressBar.Current.Value = 1;
progressBar.FadeOut(500);
downloadNotification.State = ProgressNotificationState.Completed;
using (var stream = new MemoryStream(data))
using (var archive = new OszArchiveReader(stream))
beatmaps.Import(archive);
};
downloadNotification.CancelRequested += () =>
{
downloadRequest.Cancel();
downloadRequest = null;
return true;
};
notifications.Post(downloadNotification);
// don't run in the main api queue as this is a long-running task.
Task.Run(() => downloadRequest.Perform(api));
}
public class DownloadBeatmapSetRequest : APIDownloadRequest
{
private readonly BeatmapSetInfo beatmapSet;
public DownloadBeatmapSetRequest(BeatmapSetInfo beatmapSet)
{
this.beatmapSet = beatmapSet;
}
protected override string Target => $@"beatmapsets/{beatmapSet.OnlineBeatmapSetID}/download";
}
protected override void LoadComplete()

View File

@ -27,6 +27,7 @@ namespace osu.Game.Overlays
private APIAccess api;
private RulesetStore rulesets;
private BeatmapManager beatmaps;
private readonly FillFlowContainer resultCountsContainer;
private readonly OsuSpriteText resultCountsText;
@ -46,9 +47,26 @@ namespace osu.Game.Overlays
set
{
if (beatmapSets?.Equals(value) ?? false) return;
beatmapSets = value;
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
if (beatmapSets == null) return;
var artists = new List<string>();
var songs = new List<string>();
var tags = new List<string>();
foreach (var s in beatmapSets)
{
artists.Add(s.Metadata.Artist);
songs.Add(s.Metadata.Title);
tags.AddRange(s.Metadata.Tags.Split(' '));
}
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
if (beatmapSets.Any() && panels == null)
// real use case? currently only seems to be for test case
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
}
}
@ -147,6 +165,8 @@ namespace osu.Game.Overlays
{
this.api = api;
this.rulesets = rulesets;
this.beatmaps = beatmaps;
resultCountsContainer.Colour = colours.Yellow;
beatmaps.BeatmapSetAdded += setAdded;
@ -156,6 +176,7 @@ namespace osu.Game.Overlays
{
// if a new map was imported, we should remove it from search results (download completed etc.)
panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire();
BeatmapSets = BeatmapSets?.Where(b => b.OnlineBeatmapSetID != set.OnlineBeatmapSetID);
}
private void updateResultCounts()
@ -237,20 +258,11 @@ namespace osu.Game.Overlays
getSetsRequest.Success += r =>
{
BeatmapSets = r?.Select(response => response.ToBeatmapSet(rulesets));
if (BeatmapSets == null) return;
BeatmapSets = r?.
Select(response => response.ToBeatmapSet(rulesets)).
Where(b => beatmaps.QueryBeatmapSet(q => q.OnlineBeatmapSetID == b.OnlineBeatmapSetID) == null);
var artists = new List<string>();
var songs = new List<string>();
var tags = new List<string>();
foreach (var s in BeatmapSets)
{
artists.Add(s.Metadata.Artist);
songs.Add(s.Metadata.Title);
tags.AddRange(s.Metadata.Tags.Split(' '));
}
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
};
api.Queue(getSetsRequest);

View File

@ -302,8 +302,8 @@ namespace osu.Game.Overlays
else
{
//figure out the best direction based on order in playlist.
var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo.ID).Count();
var next = beatmap == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmap.BeatmapSetInfo.ID).Count();
var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count();
var next = beatmap == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmap.BeatmapSetInfo?.ID).Count();
direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
}

View File

@ -68,7 +68,7 @@ namespace osu.Game.Overlays.Settings
backgroundBox = new Box
{
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Colour = OsuColour.Gray(60),
Alpha = 0,
},

View File

@ -85,7 +85,7 @@ namespace osu.Game.Overlays.Toolbar
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(80).Opacity(180),
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Alpha = 0,
},
Flow = new FillFlowContainer

View File

@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Toolbar
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(150).Opacity(180),
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Depth = 2,
Alpha = 0,
});

View File

@ -12,17 +12,18 @@ using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
using osu.Game.Audio;
using System.Linq;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Objects.Drawables
{
public abstract class DrawableHitObject : Container
public abstract class DrawableHitObject : Container, IHasAccentColour
{
public readonly HitObject HitObject;
/// <summary>
/// The colour used for various elements of this DrawableHitObject.
/// </summary>
public virtual Color4 AccentColour { get; set; }
public virtual Color4 AccentColour { get; set; } = Color4.Gray;
protected DrawableHitObject(HitObject hitObject)
{

View File

@ -77,13 +77,6 @@ namespace osu.Game.Rulesets.UI
Ruleset = ruleset;
}
[BackgroundDependencyLoader]
private void load()
{
KeyBindingInputManager = CreateInputManager();
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
}
/// <summary>
/// Checks whether all HitObjects have been judged, and invokes OnAllJudged.
/// </summary>
@ -194,6 +187,9 @@ namespace osu.Game.Rulesets.UI
// Post-process the beatmap
processor.PostProcess(Beatmap);
KeyBindingInputManager = CreateInputManager();
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
// Add mods, should always be the last thing applied to give full control to mods
applyMods(Mods);
}

View File

@ -83,7 +83,7 @@ namespace osu.Game.Screens.Menu
{
EdgeSmoothness = new Vector2(1.5f, 0),
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Colour = Color4.White,
Alpha = 0,
},

View File

@ -52,7 +52,7 @@ namespace osu.Game.Screens.Menu
{
Alpha = 0,
Triangles = false,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Interactive = false,
Colour = Color4.DarkGray,
Ripple = false

View File

@ -73,7 +73,7 @@ namespace osu.Game.Screens.Menu
{
texture = Texture.WhitePixel;
AccentColour = new Color4(1, 1, 1, 0.2f);
BlendingMode = BlendingMode.Additive;
Blending = BlendingMode.Additive;
}
[BackgroundDependencyLoader]

View File

@ -50,7 +50,7 @@ namespace osu.Game.Screens.Menu
RelativeSizeAxes = Axes.Y,
Width = box_width,
Alpha = 0,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
},
rightBox = new Box
{
@ -59,7 +59,7 @@ namespace osu.Game.Screens.Menu
RelativeSizeAxes = Axes.Y,
Width = box_width,
Alpha = 0,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
}
};
}

View File

@ -108,7 +108,7 @@ namespace osu.Game.Screens.Menu
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Alpha = 0
}
}
@ -169,7 +169,7 @@ namespace osu.Game.Screens.Menu
flashLayer = new Box
{
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Colour = Color4.White,
Alpha = 0,
},

View File

@ -28,6 +28,8 @@ namespace osu.Game.Screens.Play
public override void Add(KeyCounter key)
{
if (key == null) throw new ArgumentNullException(nameof(key));
base.Add(key);
key.IsCounting = IsCounting;
key.FadeTime = FadeTime;

View File

@ -100,7 +100,7 @@ namespace osu.Game.Screens
Colour = getColourFor(GetType()),
Alpha = 0.2f,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
},
textContainer = new FillFlowContainer
{

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 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;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
@ -10,6 +11,8 @@ namespace osu.Game.Screens.Select
{
public class BeatmapDetailArea : Container
{
private const float details_padding = 10;
private readonly Container content;
protected override Container<Drawable> Content => content;
@ -66,9 +69,8 @@ namespace osu.Game.Screens.Select
Details = new BeatmapDetails
{
RelativeSizeAxes = Axes.X,
Masking = true,
Height = 352,
Alpha = 0,
Margin = new MarginPadding { Top = details_padding },
},
Leaderboard = new Leaderboard
{
@ -76,5 +78,12 @@ namespace osu.Game.Screens.Select
}
});
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
Details.Height = Math.Min(DrawHeight - details_padding * 3 - BeatmapDetailAreaTabControl.HEIGHT, 450);
}
}
}

View File

@ -9,71 +9,189 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using System.Globalization;
using System.Linq;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Framework.Threading;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Screens.Select.Details;
using osu.Game.Beatmaps;
namespace osu.Game.Screens.Select
{
public class BeatmapDetails : Container
{
private readonly MetadataSegment description;
private readonly MetadataSegment source;
private readonly MetadataSegment tags;
private const float spacing = 10;
private const float transition_duration = 250;
private readonly DifficultyRow circleSize;
private readonly DifficultyRow drainRate;
private readonly DifficultyRow overallDifficulty;
private readonly DifficultyRow approachRate;
private readonly DifficultyRow stars;
private readonly FillFlowContainer top, statsFlow;
private readonly AdvancedStats advanced;
private readonly DetailBox ratingsContainer;
private readonly UserRatings ratings;
private readonly ScrollContainer metadataScroll;
private readonly MetadataSection description, source, tags;
private readonly Container failRetryContainer;
private readonly FailRetryGraph failRetryGraph;
private readonly DimmedLoadingAnimation loading;
private readonly Container ratingsContainer;
private readonly Bar ratingsBar;
private readonly OsuSpriteText negativeRatings;
private readonly OsuSpriteText positiveRatings;
private readonly BarGraph ratingsGraph;
private readonly FillFlowContainer retryFailContainer;
private readonly BarGraph retryGraph;
private readonly BarGraph failGraph;
private APIAccess api;
private ScheduledDelegate pendingBeatmapSwitch;
private BeatmapInfo beatmap;
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
if (beatmap == value) return;
if (value == beatmap) return;
beatmap = value;
pendingBeatmapSwitch?.Cancel();
pendingBeatmapSwitch = Schedule(updateStats);
pendingBeatmapSwitch = Schedule(updateStatistics);
}
}
private void updateStats()
public BeatmapDetails()
{
if (beatmap == null) return;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = spacing },
Children = new Drawable[]
{
top = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
statsFlow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.5f,
Spacing = new Vector2(spacing),
Padding = new MarginPadding { Right = spacing / 2 },
Children = new[]
{
new DetailBox
{
Child = advanced = new AdvancedStats
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = spacing, Top = spacing * 2, Bottom = spacing },
},
},
ratingsContainer = new DetailBox
{
Child = ratings = new UserRatings
{
RelativeSizeAxes = Axes.X,
Height = 134,
Padding = new MarginPadding { Horizontal = spacing, Top = spacing },
},
},
},
},
metadataScroll = new ScrollContainer
{
RelativeSizeAxes = Axes.X,
Width = 0.5f,
ScrollbarVisible = false,
Padding = new MarginPadding { Left = spacing / 2 },
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
LayoutDuration = transition_duration,
Spacing = new Vector2(spacing * 2),
Margin = new MarginPadding { Top = spacing * 2 },
Children = new[]
{
description = new MetadataSection("Description")
{
TextColour = Color4.White.Opacity(0.75f),
},
source = new MetadataSection("Source")
{
TextColour = Color4.White.Opacity(0.75f),
},
tags = new MetadataSection("Tags"),
},
},
},
},
},
failRetryContainer = new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Children = new Drawable[]
{
new OsuSpriteText
{
Text = "Points of Failure",
Font = @"Exo2.0-Bold",
TextSize = 14,
},
failRetryGraph = new FailRetryGraph
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 14 + spacing / 2 },
},
},
},
},
},
loading = new DimmedLoadingAnimation
{
RelativeSizeAxes = Axes.Both,
},
};
}
description.Text = beatmap.Version;
source.Text = beatmap.Metadata.Source;
tags.Text = beatmap.Metadata.Tags;
[BackgroundDependencyLoader]
private void load(OsuColour colours, APIAccess api)
{
this.api = api;
tags.TextColour = colours.Yellow;
}
circleSize.Value = beatmap.Difficulty.CircleSize;
drainRate.Value = beatmap.Difficulty.DrainRate;
overallDifficulty.Value = beatmap.Difficulty.OverallDifficulty;
approachRate.Value = beatmap.Difficulty.ApproachRate;
stars.Value = (float)beatmap.StarDifficulty;
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
var requestedBeatmap = beatmap;
metadataScroll.Height = statsFlow.DrawHeight;
failRetryContainer.Height = DrawHeight - Padding.TotalVertical - (top.DrawHeight + spacing / 2);
}
private void updateStatistics()
{
if (Beatmap == null)
{
clearStats();
return;
}
ratingsContainer.FadeIn(transition_duration);
advanced.Beatmap = Beatmap;
description.Text = Beatmap.Version;
source.Text = Beatmap.Metadata.Source;
tags.Text = Beatmap.Metadata.Tags;
var requestedBeatmap = Beatmap;
if (requestedBeatmap.Metrics == null)
{
var lookup = new GetBeatmapDetailsRequest(requestedBeatmap);
@ -84,413 +202,195 @@ namespace osu.Game.Screens.Select
return;
requestedBeatmap.Metrics = res;
Schedule(() => updateMetrics(res));
Schedule(() => displayMetrics(res));
};
lookup.Failure += e => Schedule(() => updateMetrics(null));
lookup.Failure += e => Schedule(() => displayMetrics(null));
api.Queue(lookup);
loading.Show();
}
updateMetrics(requestedBeatmap.Metrics, false);
displayMetrics(requestedBeatmap.Metrics, false);
}
/// <summary>
/// Update displayed metrics.
/// </summary>
/// <param name="metrics">New metrics to overwrite the existing display. Can be null.</param>
/// <param name="failOnMissing">Whether to hide the display on null or empty metrics. If false, we will dim as if waiting for further updates.</param>
private void updateMetrics(BeatmapMetrics metrics, bool failOnMissing = true)
private void displayMetrics(BeatmapMetrics metrics, bool failOnMissing = true)
{
var hasRatings = metrics?.Ratings.Any() ?? false;
var hasRetriesFails = (metrics?.Retries.Any() ?? false) && metrics.Fails.Any();
var hasRatings = metrics?.Ratings?.Any() ?? false;
var hasRetriesFails = (metrics?.Retries?.Any() ?? false) && (metrics.Fails?.Any() ?? false);
if (failOnMissing)
loading.Hide();
if (failOnMissing) loading.Hide();
if (hasRatings)
{
var ratings = metrics.Ratings.ToList();
ratingsContainer.Show();
negativeRatings.Text = ratings.GetRange(0, ratings.Count / 2).Sum().ToString();
positiveRatings.Text = ratings.GetRange(ratings.Count / 2, ratings.Count / 2).Sum().ToString();
ratingsBar.Length = (float)ratings.GetRange(0, ratings.Count / 2).Sum() / ratings.Sum();
ratingsGraph.Values = ratings.Select(rating => (float)rating);
ratingsContainer.FadeColour(Color4.White, 500, Easing.Out);
ratings.Metrics = metrics;
ratings.FadeIn(transition_duration);
}
else if (failOnMissing)
ratingsGraph.Values = new float[10];
{
ratings.Metrics = new BeatmapMetrics
{
Ratings = new int[10],
};
}
else
ratingsContainer.FadeColour(Color4.Gray, 500, Easing.Out);
{
ratings.FadeTo(0.25f, transition_duration);
}
if (hasRetriesFails)
{
var retries = metrics.Retries;
var fails = metrics.Fails;
retryFailContainer.Show();
float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max();
failGraph.MaxValue = maxValue;
retryGraph.MaxValue = maxValue;
failGraph.Values = fails.Select(fail => (float)fail);
retryGraph.Values = retries.Zip(fails, (retry, fail) => retry + MathHelper.Clamp(fail, 0, maxValue));
retryFailContainer.FadeColour(Color4.White, 500, Easing.Out);
failRetryGraph.Metrics = metrics;
failRetryContainer.FadeIn(transition_duration);
}
else if (failOnMissing)
{
failGraph.Values = new float[100];
retryGraph.Values = new float[100];
failRetryGraph.Metrics = new BeatmapMetrics
{
Fails = new int[100],
Retries = new int[100],
};
}
else
retryFailContainer.FadeColour(Color4.Gray, 500, Easing.Out);
{
failRetryContainer.FadeTo(0.25f, transition_duration);
}
}
public BeatmapDetails()
private void clearStats()
{
Children = new Drawable[]
description.Text = null;
source.Text = null;
tags.Text = null;
advanced.Beatmap = new BeatmapInfo
{
new Box
StarDifficulty = 0,
Difficulty = new BeatmapDifficulty
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
CircleSize = 0,
DrainRate = 0,
OverallDifficulty = 0,
ApproachRate = 0,
},
new FillFlowContainer<MetadataSegment>
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.4f,
Direction = FillDirection.Vertical,
LayoutDuration = 200,
LayoutEasing = Easing.OutQuint,
Children = new[]
{
description = new MetadataSegment("Description"),
source = new MetadataSegment("Source"),
tags = new MetadataSegment("Tags")
},
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.6f,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 15),
Padding = new MarginPadding(10) { Top = 0 },
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Padding = new MarginPadding(10),
Children = new[]
{
circleSize = new DifficultyRow("Circle Size", 7),
drainRate = new DifficultyRow("HP Drain"),
overallDifficulty = new DifficultyRow("Accuracy"),
approachRate = new DifficultyRow("Approach Rate"),
stars = new DifficultyRow("Star Difficulty"),
},
},
},
},
ratingsContainer = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Alpha = 0,
AlwaysPresent = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Padding = new MarginPadding
{
Top = 25,
Left = 15,
Right = 15,
},
Children = new Drawable[]
{
new OsuSpriteText
{
Text = "User Rating",
Font = @"Exo2.0-Medium",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
ratingsBar = new Bar
{
RelativeSizeAxes = Axes.X,
Height = 5,
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new[]
{
negativeRatings = new OsuSpriteText
{
Font = @"Exo2.0-Regular",
Text = "0",
},
positiveRatings = new OsuSpriteText
{
Font = @"Exo2.0-Regular",
Text = "0",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
},
},
},
new OsuSpriteText
{
Text = "Rating Spread",
TextSize = 14,
Font = @"Exo2.0-Regular",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
ratingsGraph = new BarGraph
{
RelativeSizeAxes = Axes.X,
Height = 50,
},
},
},
},
},
retryFailContainer = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Alpha = 0,
Children = new Drawable[]
{
new OsuSpriteText
{
Text = "Points of Failure",
Font = @"Exo2.0-Regular",
},
new Container<BarGraph>
{
RelativeSizeAxes = Axes.X,
Size = new Vector2(1 / 0.6f, 50),
Children = new[]
{
retryGraph = new BarGraph
{
RelativeSizeAxes = Axes.Both,
},
failGraph = new BarGraph
{
RelativeSizeAxes = Axes.Both,
},
},
},
}
},
},
},
loading = new LoadingAnimation()
};
loading.Hide();
ratingsContainer.FadeOut(transition_duration);
failRetryContainer.FadeOut(transition_duration);
}
private APIAccess api;
private readonly LoadingAnimation loading;
[BackgroundDependencyLoader]
private void load(OsuColour colour, APIAccess api)
private class DetailBox : Container
{
this.api = api;
private readonly Container content;
protected override Container<Drawable> Content => content;
description.AccentColour = colour.GrayB;
source.AccentColour = colour.GrayB;
tags.AccentColour = colour.YellowLight;
stars.AccentColour = colour.Yellow;
ratingsBar.BackgroundColour = colour.Green;
ratingsBar.AccentColour = colour.YellowDark;
ratingsGraph.Colour = colour.BlueDark;
failGraph.Colour = colour.YellowDarker;
retryGraph.Colour = colour.Yellow;
}
private class DifficultyRow : Container, IHasAccentColour
{
private readonly OsuSpriteText name;
private readonly Bar bar;
private readonly OsuSpriteText valueText;
private readonly float maxValue;
private float difficultyValue;
public float Value
public DetailBox()
{
get
{
return difficultyValue;
}
set
{
difficultyValue = value;
bar.Length = value / maxValue;
valueText.Text = value.ToString("N1", CultureInfo.CurrentCulture);
}
}
public Color4 AccentColour
{
get
{
return bar.AccentColour;
}
set
{
bar.AccentColour = value;
}
}
public DifficultyRow(string difficultyName, float maxValue = 10)
{
this.maxValue = maxValue;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
name = new OsuSpriteText
new Box
{
Font = @"Exo2.0-Regular",
Text = difficultyName,
},
bar = new Bar
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(1, 0.35f),
Padding = new MarginPadding { Left = 100, Right = 25 },
Colour = Color4.Black.Opacity(0.5f),
},
valueText = new OsuSpriteText
content = new Container
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Font = @"Exo2.0-Regular",
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
name.Colour = colour.GrayB;
bar.BackgroundColour = colour.Gray7;
valueText.Colour = colour.GrayB;
}
}
private class MetadataSegment : Container, IHasAccentColour
private class MetadataSection : Container
{
private readonly OsuSpriteText header;
private readonly FillFlowContainer<OsuSpriteText> content;
private readonly TextFlowContainer textFlow;
public string Text
{
set
{
if (string.IsNullOrEmpty(value))
Hide();
else
{
Show();
if (header.Text == "Tags")
content.ChildrenEnumerable = value.Split(' ').Select(text => new OsuSpriteText
{
Text = text,
Font = "Exo2.0-Regular",
});
else
content.Children = new[]
{
new OsuSpriteText
{
Text = value,
Font = "Exo2.0-Regular",
}
};
this.FadeOut(transition_duration);
return;
}
this.FadeIn(transition_duration);
textFlow.Clear();
textFlow.AddText(value, s => s.TextSize = 14);
}
}
public Color4 AccentColour
public Color4 TextColour
{
get
{
return content.Colour;
}
set
{
content.Colour = value;
}
get { return textFlow.Colour; }
set { textFlow.Colour = value; }
}
public MetadataSegment(string headerText)
public MetadataSection(string title)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Margin = new MarginPadding { Top = 10 };
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(spacing / 2),
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = new OsuSpriteText
{
Text = title,
Font = @"Exo2.0-Bold",
TextSize = 14,
},
},
textFlow = new TextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
},
},
};
}
}
private class DimmedLoadingAnimation : VisibilityContainer
{
private readonly LoadingAnimation loading;
public DimmedLoadingAnimation()
{
Children = new Drawable[]
{
header = new OsuSpriteText
new Box
{
Font = @"Exo2.0-Bold",
Text = headerText,
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
content = new FillFlowContainer<OsuSpriteText>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Full,
Spacing = new Vector2(5, 0),
Margin = new MarginPadding { Top = header.TextSize }
}
loading = new LoadingAnimation(),
};
}
protected override void PopIn()
{
this.FadeIn(transition_duration, Easing.OutQuint);
loading.State = Visibility.Visible;
}
protected override void PopOut()
{
this.FadeOut(transition_duration, Easing.OutQuint);
loading.State = Visibility.Hidden;
}
}
}
}

View File

@ -0,0 +1,152 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using System;
using osu.Game.Beatmaps;
namespace osu.Game.Screens.Select.Details
{
public class AdvancedStats : Container
{
private readonly StatisticRow firstValue, hpDrain, accuracy, approachRate, starDifficulty;
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
if (value == beatmap) return;
beatmap = value;
//mania specific
if ((Beatmap?.Ruleset?.ID ?? 0) == 3)
{
firstValue.Title = "Key Amount";
firstValue.Value = (int)Math.Round(Beatmap?.Difficulty?.CircleSize ?? 0);
}
else
{
firstValue.Title = "Circle Size";
firstValue.Value = Beatmap?.Difficulty?.CircleSize ?? 0;
}
hpDrain.Value = beatmap.Difficulty.DrainRate;
accuracy.Value = beatmap.Difficulty.OverallDifficulty;
approachRate.Value = beatmap.Difficulty.ApproachRate;
starDifficulty.Value = (float)beatmap.StarDifficulty;
}
}
public AdvancedStats()
{
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(4f),
Children = new[]
{
firstValue = new StatisticRow(), //circle size/key amount
hpDrain = new StatisticRow { Title = "HP Drain" },
accuracy = new StatisticRow { Title = "Accuracy" },
approachRate = new StatisticRow { Title = "Approach Rate" },
starDifficulty = new StatisticRow(10, true) { Title = "Star Difficulty" },
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
starDifficulty.AccentColour = colours.Yellow;
}
private class StatisticRow : Container, IHasAccentColour
{
private const float value_width = 25;
private const float name_width = 70;
private readonly float maxValue;
private readonly bool forceDecimalPlaces;
private readonly OsuSpriteText name, value;
private readonly Bar bar;
public string Title
{
get { return name.Text; }
set { name.Text = value; }
}
private float difficultyValue;
public float Value
{
get { return difficultyValue; }
set
{
difficultyValue = value;
bar.Length = value / maxValue;
this.value.Text = value.ToString(forceDecimalPlaces ? "0.00" : "0.##");
}
}
public Color4 AccentColour
{
get { return bar.AccentColour; }
set { bar.AccentColour = value; }
}
public StatisticRow(float maxValue = 10, bool forceDecimalPlaces = false)
{
this.maxValue = maxValue;
this.forceDecimalPlaces = forceDecimalPlaces;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new Container
{
Width = name_width,
AutoSizeAxes = Axes.Y,
Child = name = new OsuSpriteText
{
TextSize = 13,
},
},
bar = new Bar
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
Height = 5,
BackgroundColour = Color4.White.Opacity(0.5f),
Padding = new MarginPadding { Left = name_width + 10, Right = value_width + 10 },
},
new Container
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Width = value_width,
RelativeSizeAxes = Axes.Y,
Child = value = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 13,
},
},
};
}
}
}
}

View File

@ -0,0 +1,62 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using System.Linq;
using osu.Game.Beatmaps;
namespace osu.Game.Screens.Select.Details
{
public class FailRetryGraph : Container
{
private readonly BarGraph retryGraph, failGraph;
private BeatmapMetrics metrics;
public BeatmapMetrics Metrics
{
get { return metrics; }
set
{
if (value == metrics) return;
metrics = value;
var retries = Metrics.Retries;
var fails = Metrics.Fails;
float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max();
failGraph.MaxValue = maxValue;
retryGraph.MaxValue = maxValue;
failGraph.Values = fails.Select(f => (float)f);
retryGraph.Values = retries.Zip(fails, (retry, fail) => retry + MathHelper.Clamp(fail, 0, maxValue));
}
}
public FailRetryGraph()
{
Children = new[]
{
retryGraph = new BarGraph
{
RelativeSizeAxes = Axes.Both,
},
failGraph = new BarGraph
{
RelativeSizeAxes = Axes.Both,
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
retryGraph.Colour = colours.Yellow;
failGraph.Colour = colours.YellowDarker;
}
}
}

View File

@ -0,0 +1,122 @@
// Copyright (c) 2007-2017 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.Sprites;
using osu.Game.Graphics.UserInterface;
using System.Linq;
using osu.Game.Beatmaps;
namespace osu.Game.Screens.Select.Details
{
public class UserRatings : Container
{
private readonly FillFlowContainer header;
private readonly Bar ratingsBar;
private readonly OsuSpriteText negativeRatings, positiveRatings;
private readonly Container graphContainer;
private readonly BarGraph graph;
private BeatmapMetrics metrics;
public BeatmapMetrics Metrics
{
get { return metrics; }
set
{
if (value == metrics) return;
metrics = value;
var ratings = Metrics.Ratings.ToList();
negativeRatings.Text = ratings.GetRange(0, ratings.Count / 2).Sum().ToString();
positiveRatings.Text = ratings.GetRange(ratings.Count / 2, ratings.Count / 2).Sum().ToString();
ratingsBar.Length = (float)ratings.GetRange(0, ratings.Count / 2).Sum() / ratings.Sum();
graph.Values = Metrics.Ratings.Select(r => (float)r);
}
}
public UserRatings()
{
Children = new Drawable[]
{
header = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "User Rating",
TextSize = 13,
},
ratingsBar = new Bar
{
RelativeSizeAxes = Axes.X,
Height = 5,
Margin = new MarginPadding { Top = 5 },
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new[]
{
negativeRatings = new OsuSpriteText
{
Text = "0",
TextSize = 13,
},
positiveRatings = new OsuSpriteText
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Text = @"0",
TextSize = 13,
},
},
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Rating Spread",
TextSize = 13,
Margin = new MarginPadding { Top = 10, Bottom = 5 },
},
},
},
graphContainer = new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
Child = graph = new BarGraph
{
RelativeSizeAxes = Axes.Both,
},
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
ratingsBar.BackgroundColour = colours.Green;
ratingsBar.AccentColour = colours.Yellow;
graph.Colour = colours.BlueDark;
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
graphContainer.Padding = new MarginPadding { Top = header.DrawHeight };
}
}
}

View File

@ -304,7 +304,7 @@ namespace osu.Game.Screens.Select.Leaderboards
BlurSigma = new Vector2(4),
CacheDrawnFrameBuffer = true,
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Size = new Vector2(3f),
Children = new[]
{

View File

@ -119,7 +119,7 @@ namespace osu.Game.Screens.Select.Options
{
RelativeSizeAxes = Axes.Both,
EdgeSmoothness = new Vector2(1.5f, 0),
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Colour = Color4.White,
Alpha = 0,
},

View File

@ -19,9 +19,9 @@ namespace osu.Game.Storyboards.Drawables
private BlendingMode valueAt(double time)
=> time < EndTime ? StartValue : EndValue;
public override string TargetMember => nameof(Drawable.BlendingMode);
public override string TargetMember => nameof(Drawable.Blending);
protected override void Apply(Drawable d, double time) => d.BlendingMode = valueAt(time);
protected override void ReadIntoStartValue(Drawable d) => StartValue = d.BlendingMode;
protected override void Apply(Drawable d, double time) => d.Blending = valueAt(time);
protected override void ReadIntoStartValue(Drawable d) => StartValue = d.Blending.Mode;
}
}

View File

@ -66,7 +66,7 @@ namespace osu.Game.Storyboards
applyCommands(drawable, getCommands(g => g.Rotation, triggeredGroups), (d, value) => d.Rotation = value, (d, value, duration, easing) => d.RotateTo(value, duration, easing));
applyCommands(drawable, getCommands(g => g.Colour, triggeredGroups), (d, value) => d.Colour = value, (d, value, duration, easing) => d.FadeColour(value, duration, easing));
applyCommands(drawable, getCommands(g => g.Alpha, triggeredGroups), (d, value) => d.Alpha = value, (d, value, duration, easing) => d.FadeTo(value, duration, easing));
applyCommands(drawable, getCommands(g => g.BlendingMode, triggeredGroups), (d, value) => d.BlendingMode = value, (d, value, duration, easing) => d.TransformBlendingMode(value, duration), false);
applyCommands(drawable, getCommands(g => g.BlendingMode, triggeredGroups), (d, value) => d.Blending = value, (d, value, duration, easing) => d.TransformBlendingMode(value, duration), false);
var flippable = drawable as IFlippable;
if (flippable != null)

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game</RootNamespace>
<AssemblyName>osu.Game</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
@ -114,6 +114,7 @@
<Compile Include="Input\Bindings\GlobalKeyBindingInputManager.cs" />
<Compile Include="IO\FileStore.cs" />
<Compile Include="IO\FileInfo.cs" />
<Compile Include="Online\API\Requests\DownloadBeatmapSetRequest.cs" />
<Compile Include="Online\API\Requests\GetUsersRequest.cs" />
<Compile Include="Online\API\Requests\PostMessageRequest.cs" />
<Compile Include="Online\Chat\ErrorMessage.cs" />
@ -554,6 +555,9 @@
<Compile Include="Screens\Multiplayer\ParticipantInfo.cs" />
<Compile Include="Screens\Multiplayer\ModeTypeInfo.cs" />
<Compile Include="Beatmaps\Drawables\BeatmapSetCover.cs" />
<Compile Include="Screens\Select\Details\AdvancedStats.cs" />
<Compile Include="Screens\Select\Details\FailRetryGraph.cs" />
<Compile Include="Screens\Select\Details\UserRatings.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">

View File

@ -190,6 +190,7 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IPC/@EntryIndexedValue">IPC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LTRB/@EntryIndexedValue">LTRB</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MD/@EntryIndexedValue">MD5</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RGB/@EntryIndexedValue">RGB</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RNG/@EntryIndexedValue">RNG</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SHA/@EntryIndexedValue">SHA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SRGB/@EntryIndexedValue">SRGB</s:String>