mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 21:02:54 +08:00
Merge branch 'master' into profile
This commit is contained in:
commit
283b6b2b5e
@ -20,4 +20,4 @@ build:
|
|||||||
verbosity: minimal
|
verbosity: minimal
|
||||||
after_build:
|
after_build:
|
||||||
- cmd: inspectcode /o="inspectcodereport.xml" /caches-home="inspectcode" osu.sln
|
- cmd: inspectcode /o="inspectcodereport.xml" /caches-home="inspectcode" osu.sln
|
||||||
- cmd: NVika parsereport "inspectcodereport.xml"
|
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
|
@ -1 +1 @@
|
|||||||
Subproject commit 925bbe42bab95078b9d33189205b5b1b76bf8e01
|
Subproject commit 7131c54ea0a76ea03565ca93680487930c1c158a
|
@ -1 +1 @@
|
|||||||
Subproject commit a5199500cc3ba96101fd858e0f78f36e538697b1
|
Subproject commit b348c1e540edbb3325a8da9bca452c9dce2938d6
|
@ -41,7 +41,7 @@ namespace osu.Desktop.VisualTests.Tests
|
|||||||
StarDifficulty = 5.3f,
|
StarDifficulty = 5.3f,
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0,10),
|
Ratings = Enumerable.Range(0, 10),
|
||||||
Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6),
|
Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6),
|
||||||
Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6),
|
||||||
},
|
},
|
||||||
|
@ -36,12 +36,12 @@ namespace osu.Desktop.VisualTests.Tests
|
|||||||
});
|
});
|
||||||
|
|
||||||
first.Room.Name.Value = @"Great Room Right Here";
|
first.Room.Name.Value = @"Great Room Right Here";
|
||||||
first.Room.Host.Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" }};
|
first.Room.Host.Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" } };
|
||||||
first.Room.Status.Value = new RoomStatusOpen();
|
first.Room.Status.Value = new RoomStatusOpen();
|
||||||
first.Room.Beatmap.Value = new BeatmapMetadata { Title = @"Seiryu", Artist = @"Critical Crystal" };
|
first.Room.Beatmap.Value = new BeatmapMetadata { Title = @"Seiryu", Artist = @"Critical Crystal" };
|
||||||
|
|
||||||
second.Room.Name.Value = @"Relax It's The Weekend";
|
second.Room.Name.Value = @"Relax It's The Weekend";
|
||||||
second.Room.Host.Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" }};
|
second.Room.Host.Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" } };
|
||||||
second.Room.Status.Value = new RoomStatusPlaying();
|
second.Room.Status.Value = new RoomStatusPlaying();
|
||||||
second.Room.Beatmap.Value = new BeatmapMetadata { Title = @"ZAQ", Artist = @"Serendipity" };
|
second.Room.Beatmap.Value = new BeatmapMetadata { Title = @"ZAQ", Artist = @"Serendipity" };
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Desktop.VisualTests.Tests
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1,10).Select(i => (float)i));
|
AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Select(i => (float)i));
|
||||||
AddStep("values from 1-100", () => graph.Values = Enumerable.Range(1, 100).Select(i => (float)i));
|
AddStep("values from 1-100", () => graph.Values = Enumerable.Range(1, 100).Select(i => (float)i));
|
||||||
AddStep("reversed values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Reverse().Select(i => (float)i));
|
AddStep("reversed values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Reverse().Select(i => (float)i));
|
||||||
AddStep("Bottom to top", () => graph.Direction = BarDirection.BottomToTop);
|
AddStep("Bottom to top", () => graph.Direction = BarDirection.BottomToTop);
|
||||||
|
@ -54,7 +54,7 @@ namespace osu.Desktop.VisualTests.Tests
|
|||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new SpriteText { Text = "FadeTime" },
|
new SpriteText { Text = "FadeTime" },
|
||||||
sliderBar =new TestSliderBar<int>
|
sliderBar = new TestSliderBar<int>
|
||||||
{
|
{
|
||||||
Width = 150,
|
Width = 150,
|
||||||
Height = 10,
|
Height = 10,
|
||||||
|
@ -40,7 +40,7 @@ namespace osu.Desktop.VisualTests.Tests
|
|||||||
{
|
{
|
||||||
Name = "Timing section",
|
Name = "Timing section",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
RelativeCoordinateSpace = new Vector2(1, 10000),
|
RelativeChildSize = new Vector2(1, 10000),
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new DrawableNote(new Note { StartTime = 5000 }) { AccentColour = Color4.Red },
|
new DrawableNote(new Note { StartTime = 5000 }) { AccentColour = Color4.Red },
|
||||||
@ -62,7 +62,7 @@ namespace osu.Desktop.VisualTests.Tests
|
|||||||
{
|
{
|
||||||
Name = "Timing section",
|
Name = "Timing section",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
RelativeCoordinateSpace = new Vector2(1, 10000),
|
RelativeChildSize = new Vector2(1, 10000),
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new DrawableHoldNote(new HoldNote
|
new DrawableHoldNote(new HoldNote
|
||||||
|
@ -35,7 +35,7 @@ namespace osu.Desktop.VisualTests.Tests
|
|||||||
});
|
});
|
||||||
|
|
||||||
AddStep(@"Pause", delegate {
|
AddStep(@"Pause", delegate {
|
||||||
if(failOverlay.State == Visibility.Visible)
|
if (failOverlay.State == Visibility.Visible)
|
||||||
{
|
{
|
||||||
failOverlay.Hide();
|
failOverlay.Hide();
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,8 @@ namespace osu.Desktop.VisualTests.Tests
|
|||||||
{
|
{
|
||||||
score.Current.Value += 300 + (ulong)(300.0 * (comboCounter.Current > 0 ? comboCounter.Current - 1 : 0) / 25.0);
|
score.Current.Value += 300 + (ulong)(300.0 * (comboCounter.Current > 0 ? comboCounter.Current - 1 : 0) / 25.0);
|
||||||
comboCounter.Increment();
|
comboCounter.Increment();
|
||||||
numerator++; denominator++;
|
numerator++;
|
||||||
|
denominator++;
|
||||||
accuracyCounter.SetFraction(numerator, denominator);
|
accuracyCounter.SetFraction(numerator, denominator);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
85
osu.Desktop.VisualTests/Tests/TestCaseSocial.cs
Normal file
85
osu.Desktop.VisualTests/Tests/TestCaseSocial.cs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// 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.Testing;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Desktop.VisualTests.Tests
|
||||||
|
{
|
||||||
|
public class TestCaseSocial : TestCase
|
||||||
|
{
|
||||||
|
public override string Description => @"social browser overlay";
|
||||||
|
|
||||||
|
public override void Reset()
|
||||||
|
{
|
||||||
|
base.Reset();
|
||||||
|
|
||||||
|
SocialOverlay s = new SocialOverlay
|
||||||
|
{
|
||||||
|
Users = new[]
|
||||||
|
{
|
||||||
|
new User
|
||||||
|
{
|
||||||
|
Username = @"flyte",
|
||||||
|
Id = 3103765,
|
||||||
|
Country = new Country { FlagName = @"JP" },
|
||||||
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg",
|
||||||
|
},
|
||||||
|
new User
|
||||||
|
{
|
||||||
|
Username = @"Cookiezi",
|
||||||
|
Id = 124493,
|
||||||
|
Country = new Country { FlagName = @"KR" },
|
||||||
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg",
|
||||||
|
},
|
||||||
|
new User
|
||||||
|
{
|
||||||
|
Username = @"Angelsim",
|
||||||
|
Id = 1777162,
|
||||||
|
Country = new Country { FlagName = @"KR" },
|
||||||
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||||
|
},
|
||||||
|
new User
|
||||||
|
{
|
||||||
|
Username = @"Rafis",
|
||||||
|
Id = 2558286,
|
||||||
|
Country = new Country { FlagName = @"PL" },
|
||||||
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c4.jpg",
|
||||||
|
},
|
||||||
|
new User
|
||||||
|
{
|
||||||
|
Username = @"hvick225",
|
||||||
|
Id = 50265,
|
||||||
|
Country = new Country { FlagName = @"TW" },
|
||||||
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c5.jpg",
|
||||||
|
},
|
||||||
|
new User
|
||||||
|
{
|
||||||
|
Username = @"peppy",
|
||||||
|
Id = 2,
|
||||||
|
Country = new Country { FlagName = @"AU" },
|
||||||
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
|
||||||
|
},
|
||||||
|
new User
|
||||||
|
{
|
||||||
|
Username = @"filsdelama",
|
||||||
|
Id = 2831793,
|
||||||
|
Country = new Country { FlagName = @"FR" },
|
||||||
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c7.jpg"
|
||||||
|
},
|
||||||
|
new User
|
||||||
|
{
|
||||||
|
Username = @"_index",
|
||||||
|
Id = 652457,
|
||||||
|
Country = new Country { FlagName = @"RU" },
|
||||||
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c8.jpg"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Add(s);
|
||||||
|
|
||||||
|
AddStep(@"toggle", s.ToggleVisibility);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -225,6 +225,7 @@
|
|||||||
<Compile Include="Tests\TestCaseDrawableRoom.cs" />
|
<Compile Include="Tests\TestCaseDrawableRoom.cs" />
|
||||||
<Compile Include="Tests\TestCaseUserPanel.cs" />
|
<Compile Include="Tests\TestCaseUserPanel.cs" />
|
||||||
<Compile Include="Tests\TestCaseDirect.cs" />
|
<Compile Include="Tests\TestCaseDirect.cs" />
|
||||||
|
<Compile Include="Tests\TestCaseSocial.cs" />
|
||||||
<Compile Include="Tests\TestCaseBreadcrumbs.cs" />
|
<Compile Include="Tests\TestCaseBreadcrumbs.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup />
|
<ItemGroup />
|
||||||
|
@ -448,7 +448,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
return curveData.RepeatSamples[index];
|
return curveData.RepeatSamples[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs and adds a note to a pattern.
|
/// Constructs and adds a note to a pattern.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -480,7 +479,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
Tail = { Samples = sampleInfoListAt(endTime) }
|
Tail = { Samples = sampleInfoListAt(endTime) }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
newObject = holdNote;
|
newObject = holdNote;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +87,5 @@ namespace osu.Game.Rulesets.Mania.MathUtils
|
|||||||
bitIndex++;
|
bitIndex++;
|
||||||
return ((bitBuffer >>= 1) & 1) == 1;
|
return ((bitBuffer >>= 1) & 1) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
tickContainer = new Container<DrawableHoldNoteTick>
|
tickContainer = new Container<DrawableHoldNoteTick>
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
RelativeCoordinateSpace = new Vector2(1, (float)HitObject.Duration)
|
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
|
||||||
},
|
},
|
||||||
head = new DrawableHeadNote(this, key)
|
head = new DrawableHeadNote(this, key)
|
||||||
{
|
{
|
||||||
|
@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Mania.Timing
|
|||||||
|
|
||||||
// Adjust our height to account for the speed changes
|
// Adjust our height to account for the speed changes
|
||||||
Height = (float)(1000 / timingChange.BeatLength / timingChange.SpeedMultiplier);
|
Height = (float)(1000 / timingChange.BeatLength / timingChange.SpeedMultiplier);
|
||||||
RelativeCoordinateSpace = new Vector2(1, (float)parent.TimeSpan);
|
RelativeChildSize = new Vector2(1, (float)parent.TimeSpan);
|
||||||
|
|
||||||
// Scroll the content
|
// Scroll the content
|
||||||
content.Y = (float)(timingChange.Time - Time.Current);
|
content.Y = (float)(timingChange.Time - Time.Current);
|
||||||
@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Mania.Timing
|
|||||||
public override void InvalidateFromChild(Invalidation invalidation)
|
public override void InvalidateFromChild(Invalidation invalidation)
|
||||||
{
|
{
|
||||||
// We only want to re-compute our size when a child's size or position has changed
|
// We only want to re-compute our size when a child's size or position has changed
|
||||||
if ((invalidation & Invalidation.Geometry) == 0)
|
if ((invalidation & Invalidation.RequiredParentSizeToFit) == 0)
|
||||||
{
|
{
|
||||||
base.InvalidateFromChild(invalidation);
|
base.InvalidateFromChild(invalidation);
|
||||||
return;
|
return;
|
||||||
@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Mania.Timing
|
|||||||
float height = Children.Select(child => child.Y + child.Height).Max();
|
float height = Children.Select(child => child.Y + child.Height).Max();
|
||||||
|
|
||||||
Height = height;
|
Height = height;
|
||||||
RelativeCoordinateSpace = new Vector2(1, height);
|
RelativeChildSize = new Vector2(1, height);
|
||||||
|
|
||||||
base.InvalidateFromChild(invalidation);
|
base.InvalidateFromChild(invalidation);
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
Name = "Hit target + hit objects",
|
Name = "Hit target + hit objects",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Top = ManiaPlayfield.HIT_TARGET_POSITION},
|
Padding = new MarginPadding { Top = ManiaPlayfield.HIT_TARGET_POSITION },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
|
@ -1,201 +0,0 @@
|
|||||||
// 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 System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects
|
|
||||||
{
|
|
||||||
internal class OsuHitObjectDifficulty
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Factor by how much speed / aim strain decays per second.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// These values are results of tweaking a lot and taking into account general feedback.
|
|
||||||
/// Opinionated observation: Speed is easier to maintain than accurate jumps.
|
|
||||||
/// </remarks>
|
|
||||||
internal static readonly double[] DECAY_BASE = { 0.3, 0.15 };
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pseudo threshold values to distinguish between "singles" and "streams"
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Of course the border can not be defined clearly, therefore the algorithm has a smooth transition between those values.
|
|
||||||
/// They also are based on tweaking and general feedback.
|
|
||||||
/// </remarks>
|
|
||||||
private const double stream_spacing_threshold = 110,
|
|
||||||
single_spacing_threshold = 125;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Scaling values for weightings to keep aim and speed difficulty in balance.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Found from testing a very large map pool (containing all ranked maps) and keeping the average values the same.
|
|
||||||
/// </remarks>
|
|
||||||
private static readonly double[] spacing_weight_scaling = { 1400, 26.25 };
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Almost the normed diameter of a circle (104 osu pixel). That is -after- position transforming.
|
|
||||||
/// </summary>
|
|
||||||
private const double almost_diameter = 90;
|
|
||||||
|
|
||||||
internal OsuHitObject BaseHitObject;
|
|
||||||
internal double[] Strains = { 1, 1 };
|
|
||||||
|
|
||||||
internal int MaxCombo = 1;
|
|
||||||
|
|
||||||
private readonly float scalingFactor;
|
|
||||||
private float lazySliderLength;
|
|
||||||
|
|
||||||
private readonly Vector2 startPosition;
|
|
||||||
private readonly Vector2 endPosition;
|
|
||||||
|
|
||||||
internal OsuHitObjectDifficulty(OsuHitObject baseHitObject)
|
|
||||||
{
|
|
||||||
BaseHitObject = baseHitObject;
|
|
||||||
float circleRadius = baseHitObject.Scale * 64;
|
|
||||||
|
|
||||||
Slider slider = BaseHitObject as Slider;
|
|
||||||
if (slider != null)
|
|
||||||
MaxCombo += slider.Ticks.Count();
|
|
||||||
|
|
||||||
// We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
|
|
||||||
scalingFactor = 52.0f / circleRadius;
|
|
||||||
if (circleRadius < 30)
|
|
||||||
{
|
|
||||||
float smallCircleBonus = Math.Min(30.0f - circleRadius, 5.0f) / 50.0f;
|
|
||||||
scalingFactor *= 1.0f + smallCircleBonus;
|
|
||||||
}
|
|
||||||
|
|
||||||
lazySliderLength = 0;
|
|
||||||
startPosition = baseHitObject.StackedPosition;
|
|
||||||
|
|
||||||
// Calculate approximation of lazy movement on the slider
|
|
||||||
if (slider != null)
|
|
||||||
{
|
|
||||||
float sliderFollowCircleRadius = circleRadius * 3; // Not sure if this is correct, but here we do not need 100% exact values. This comes pretty darn close in my tests.
|
|
||||||
|
|
||||||
// For simplifying this step we use actual osu! coordinates and simply scale the length, that we obtain by the ScalingFactor later
|
|
||||||
Vector2 cursorPos = startPosition;
|
|
||||||
|
|
||||||
Action<Vector2> addSliderVertex = delegate (Vector2 pos)
|
|
||||||
{
|
|
||||||
Vector2 difference = pos - cursorPos;
|
|
||||||
float distance = difference.Length;
|
|
||||||
|
|
||||||
// Did we move away too far?
|
|
||||||
if (distance > sliderFollowCircleRadius)
|
|
||||||
{
|
|
||||||
// Yep, we need to move the cursor
|
|
||||||
difference.Normalize(); // Obtain the direction of difference. We do no longer need the actual difference
|
|
||||||
distance -= sliderFollowCircleRadius;
|
|
||||||
cursorPos += difference * distance; // We move the cursor just as far as needed to stay in the follow circle
|
|
||||||
lazySliderLength += distance;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Actual computation of the first lazy curve
|
|
||||||
foreach (var tick in slider.Ticks)
|
|
||||||
addSliderVertex(tick.StackedPosition);
|
|
||||||
|
|
||||||
addSliderVertex(baseHitObject.StackedEndPosition);
|
|
||||||
|
|
||||||
lazySliderLength *= scalingFactor;
|
|
||||||
endPosition = cursorPos;
|
|
||||||
}
|
|
||||||
// We have a normal HitCircle or a spinner
|
|
||||||
else
|
|
||||||
endPosition = startPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void CalculateStrains(OsuHitObjectDifficulty previousHitObject, double timeRate)
|
|
||||||
{
|
|
||||||
calculateSpecificStrain(previousHitObject, OsuDifficultyCalculator.DifficultyType.Speed, timeRate);
|
|
||||||
calculateSpecificStrain(previousHitObject, OsuDifficultyCalculator.DifficultyType.Aim, timeRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Caution: The subjective values are strong with this one
|
|
||||||
private static double spacingWeight(double distance, OsuDifficultyCalculator.DifficultyType type)
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case OsuDifficultyCalculator.DifficultyType.Speed:
|
|
||||||
if (distance > single_spacing_threshold)
|
|
||||||
return 2.5;
|
|
||||||
else if (distance > stream_spacing_threshold)
|
|
||||||
return 1.6 + 0.9 * (distance - stream_spacing_threshold) / (single_spacing_threshold - stream_spacing_threshold);
|
|
||||||
else if (distance > almost_diameter)
|
|
||||||
return 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter);
|
|
||||||
else if (distance > almost_diameter / 2)
|
|
||||||
return 0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2);
|
|
||||||
else
|
|
||||||
return 0.95;
|
|
||||||
|
|
||||||
case OsuDifficultyCalculator.DifficultyType.Aim:
|
|
||||||
return Math.Pow(distance, 0.99);
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.Assert(false, "Invalid osu difficulty hit object type.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void calculateSpecificStrain(OsuHitObjectDifficulty previousHitObject, OsuDifficultyCalculator.DifficultyType type, double timeRate)
|
|
||||||
{
|
|
||||||
double addition = 0;
|
|
||||||
double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
|
|
||||||
double decay = Math.Pow(DECAY_BASE[(int)type], timeElapsed / 1000);
|
|
||||||
|
|
||||||
if (BaseHitObject is Spinner)
|
|
||||||
{
|
|
||||||
// Do nothing for spinners
|
|
||||||
}
|
|
||||||
else if (BaseHitObject is Slider)
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case OsuDifficultyCalculator.DifficultyType.Speed:
|
|
||||||
|
|
||||||
// For speed strain we treat the whole slider as a single spacing entity, since "Speed" is about how hard it is to click buttons fast.
|
|
||||||
// The spacing weight exists to differentiate between being able to easily alternate or having to single.
|
|
||||||
addition =
|
|
||||||
spacingWeight(previousHitObject.lazySliderLength +
|
|
||||||
DistanceTo(previousHitObject), type) *
|
|
||||||
spacing_weight_scaling[(int)type];
|
|
||||||
|
|
||||||
break;
|
|
||||||
case OsuDifficultyCalculator.DifficultyType.Aim:
|
|
||||||
|
|
||||||
// For Aim strain we treat each slider segment and the jump after the end of the slider as separate jumps, since movement-wise there is no difference
|
|
||||||
// to multiple jumps.
|
|
||||||
addition =
|
|
||||||
(
|
|
||||||
spacingWeight(previousHitObject.lazySliderLength, type) +
|
|
||||||
spacingWeight(DistanceTo(previousHitObject), type)
|
|
||||||
) *
|
|
||||||
spacing_weight_scaling[(int)type];
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (BaseHitObject is HitCircle)
|
|
||||||
{
|
|
||||||
addition = spacingWeight(DistanceTo(previousHitObject), type) * spacing_weight_scaling[(int)type];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scale addition by the time, that elapsed. Filter out HitObjects that are too close to be played anyway to avoid crazy values by division through close to zero.
|
|
||||||
// You will never find maps that require this amongst ranked maps.
|
|
||||||
addition /= Math.Max(timeElapsed, 50);
|
|
||||||
|
|
||||||
Strains[(int)type] = previousHitObject.Strains[(int)type] * decay + addition;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal double DistanceTo(OsuHitObjectDifficulty other)
|
|
||||||
{
|
|
||||||
// Scale the distance by circle size.
|
|
||||||
return (startPosition - other.endPosition).Length * scalingFactor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,73 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Osu.OsuDifficulty.Skills;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.OsuDifficulty
|
||||||
|
{
|
||||||
|
public class OsuDifficultyCalculator : DifficultyCalculator<OsuHitObject>
|
||||||
|
{
|
||||||
|
private const int section_length = 400;
|
||||||
|
private const double difficulty_multiplier = 0.0675;
|
||||||
|
|
||||||
|
public OsuDifficultyCalculator(Beatmap beatmap) : base(beatmap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PreprocessHitObjects()
|
||||||
|
{
|
||||||
|
foreach (OsuHitObject h in Objects)
|
||||||
|
(h as Slider)?.Curve?.Calculate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
|
||||||
|
{
|
||||||
|
OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Objects);
|
||||||
|
Skill[] skills =
|
||||||
|
{
|
||||||
|
new Aim(),
|
||||||
|
new Speed()
|
||||||
|
};
|
||||||
|
|
||||||
|
double sectionEnd = section_length / TimeRate;
|
||||||
|
foreach (OsuDifficultyHitObject h in beatmap)
|
||||||
|
{
|
||||||
|
while (h.BaseObject.StartTime > sectionEnd)
|
||||||
|
{
|
||||||
|
foreach (Skill s in skills)
|
||||||
|
{
|
||||||
|
s.SaveCurrentPeak();
|
||||||
|
s.StartNewSectionFrom(sectionEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
sectionEnd += section_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Skill s in skills)
|
||||||
|
s.Process(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
|
||||||
|
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
||||||
|
|
||||||
|
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
|
||||||
|
|
||||||
|
if (categoryDifficulty != null)
|
||||||
|
{
|
||||||
|
categoryDifficulty.Add("Aim", aimRating.ToString("0.00"));
|
||||||
|
categoryDifficulty.Add("Speed", speedRating.ToString("0.00"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return starRating;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
// 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.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An enumerable container wrapping <see cref="OsuHitObject"/> input as <see cref="OsuDifficultyHitObject"/>
|
||||||
|
/// which contains extra data required for difficulty calculation.
|
||||||
|
/// </summary>
|
||||||
|
public class OsuDifficultyBeatmap : IEnumerable<OsuDifficultyHitObject>
|
||||||
|
{
|
||||||
|
private readonly IEnumerator<OsuDifficultyHitObject> difficultyObjects;
|
||||||
|
private readonly Queue<OsuDifficultyHitObject> onScreen = new Queue<OsuDifficultyHitObject>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an enumerator, which preprocesses a list of <see cref="OsuHitObject"/>s recieved as input, wrapping them as
|
||||||
|
/// <see cref="OsuDifficultyHitObject"/> which contains extra data required for difficulty calculation.
|
||||||
|
/// </summary>
|
||||||
|
public OsuDifficultyBeatmap(List<OsuHitObject> objects)
|
||||||
|
{
|
||||||
|
// Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases.
|
||||||
|
// This should probably happen before the objects reach the difficulty calculator.
|
||||||
|
objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime));
|
||||||
|
difficultyObjects = createDifficultyObjectEnumerator(objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an enumerator that enumerates all <see cref="OsuDifficultyHitObject"/>s in the <see cref="OsuDifficultyBeatmap"/>.
|
||||||
|
/// The inner loop adds objects that appear on screen into a queue until we need to hit the next object.
|
||||||
|
/// The outer loop returns objects from this queue one at a time, only after they had to be hit, and should no longer be on screen.
|
||||||
|
/// This means that we can loop through every object that is on screen at the time when a new one appears,
|
||||||
|
/// allowing us to determine a reading strain for the object that just appeared.
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerator<OsuDifficultyHitObject> GetEnumerator()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// Add upcoming objects to the queue until we have at least one object that had been hit and can be dequeued.
|
||||||
|
// This means there is always at least one object in the queue unless we reached the end of the map.
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!difficultyObjects.MoveNext())
|
||||||
|
break; // New objects can't be added anymore, but we still need to dequeue and return the ones already on screen.
|
||||||
|
|
||||||
|
OsuDifficultyHitObject latest = difficultyObjects.Current;
|
||||||
|
// Calculate flow values here
|
||||||
|
|
||||||
|
foreach (OsuDifficultyHitObject h in onScreen)
|
||||||
|
{
|
||||||
|
h.TimeUntilHit -= latest.DeltaTime;
|
||||||
|
// Calculate reading strain here
|
||||||
|
}
|
||||||
|
|
||||||
|
onScreen.Enqueue(latest);
|
||||||
|
}
|
||||||
|
while (onScreen.Peek().TimeUntilHit > 0); // Keep adding new objects on screen while there is still time before we have to hit the next one.
|
||||||
|
|
||||||
|
if (onScreen.Count == 0) break; // We have reached the end of the map and enumerated all the objects.
|
||||||
|
yield return onScreen.Dequeue(); // Remove and return objects one by one that had to be hit before the latest one appeared.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
private IEnumerator<OsuDifficultyHitObject> createDifficultyObjectEnumerator(List<OsuHitObject> objects)
|
||||||
|
{
|
||||||
|
// We will process OsuHitObjects in groups of three to form a triangle, so we can calculate an angle for each object.
|
||||||
|
OsuHitObject[] triangle = new OsuHitObject[3];
|
||||||
|
|
||||||
|
// OsuDifficultyHitObject construction requires three components, an extra copy of the first OsuHitObject is used at the beginning.
|
||||||
|
if (objects.Count > 1)
|
||||||
|
{
|
||||||
|
triangle[1] = objects[0]; // This copy will get shifted to the last spot in the triangle.
|
||||||
|
triangle[0] = objects[0]; // This component corresponds to the real first OsuHitOject.
|
||||||
|
}
|
||||||
|
|
||||||
|
// The final component of the first triangle will be the second OsuHitOject of the map, which forms the first jump.
|
||||||
|
// If the map has less than two OsuHitObjects, the enumerator will not return anything.
|
||||||
|
for (int i = 1; i < objects.Count; ++i)
|
||||||
|
{
|
||||||
|
triangle[2] = triangle[1];
|
||||||
|
triangle[1] = triangle[0];
|
||||||
|
triangle[0] = objects[i];
|
||||||
|
|
||||||
|
yield return new OsuDifficultyHitObject(triangle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
// 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.Game.Rulesets.Osu.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A wrapper around <see cref="OsuHitObject"/> extending it with additional data required for difficulty calculation.
|
||||||
|
/// </summary>
|
||||||
|
public class OsuDifficultyHitObject
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="OsuHitObject"/> this <see cref="OsuDifficultyHitObject"/> refers to.
|
||||||
|
/// </summary>
|
||||||
|
public OsuHitObject BaseObject { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Normalized distance from the <see cref="OsuHitObject.StackedPosition"/> of the previous <see cref="OsuDifficultyHitObject"/>.
|
||||||
|
/// </summary>
|
||||||
|
public double Distance { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Milliseconds elapsed since the StartTime of the previous <see cref="OsuDifficultyHitObject"/>.
|
||||||
|
/// </summary>
|
||||||
|
public double DeltaTime { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of milliseconds until the <see cref="OsuDifficultyHitObject"/> has to be hit.
|
||||||
|
/// </summary>
|
||||||
|
public double TimeUntilHit { get; set; }
|
||||||
|
|
||||||
|
private const int normalized_radius = 52;
|
||||||
|
|
||||||
|
private readonly OsuHitObject[] t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the object calculating extra data required for difficulty calculation.
|
||||||
|
/// </summary>
|
||||||
|
public OsuDifficultyHitObject(OsuHitObject[] triangle)
|
||||||
|
{
|
||||||
|
t = triangle;
|
||||||
|
BaseObject = t[0];
|
||||||
|
setDistances();
|
||||||
|
setTimingValues();
|
||||||
|
// Calculate angle here
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDistances()
|
||||||
|
{
|
||||||
|
// We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps.
|
||||||
|
double scalingFactor = normalized_radius / BaseObject.Radius;
|
||||||
|
if (BaseObject.Radius < 30)
|
||||||
|
{
|
||||||
|
double smallCircleBonus = Math.Min(30 - BaseObject.Radius, 5) / 50;
|
||||||
|
scalingFactor *= 1 + smallCircleBonus;
|
||||||
|
}
|
||||||
|
|
||||||
|
Distance = (t[0].StackedPosition - t[1].StackedPosition).Length * scalingFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTimingValues()
|
||||||
|
{
|
||||||
|
// Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure.
|
||||||
|
DeltaTime = Math.Max(40, t[0].StartTime - t[1].StartTime);
|
||||||
|
TimeUntilHit = 450; // BaseObject.PreEmpt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Aim.cs
Normal file
19
osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Aim.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 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.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
|
||||||
|
/// </summary>
|
||||||
|
public class Aim : Skill
|
||||||
|
{
|
||||||
|
protected override double SkillMultiplier => 26.25;
|
||||||
|
protected override double StrainDecayBase => 0.15;
|
||||||
|
|
||||||
|
protected override double StrainValueOf(OsuDifficultyHitObject current) => Math.Pow(current.Distance, 0.99) / current.DeltaTime;
|
||||||
|
}
|
||||||
|
}
|
100
osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs
Normal file
100
osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Osu.OsuDifficulty.Utils;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Used to processes strain values of <see cref="OsuDifficultyHitObject"/>s, keep track of strain levels caused by the processed objects
|
||||||
|
/// and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class Skill
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other.
|
||||||
|
/// </summary>
|
||||||
|
protected abstract double SkillMultiplier { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines how quickly strain decays for the given skill.
|
||||||
|
/// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second.
|
||||||
|
/// </summary>
|
||||||
|
protected abstract double StrainDecayBase { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="OsuDifficultyHitObject"/>s that were processed previously. They can affect the strain values of the following objects.
|
||||||
|
/// </summary>
|
||||||
|
protected readonly History<OsuDifficultyHitObject> Previous = new History<OsuDifficultyHitObject>(2); // Contained objects not used yet
|
||||||
|
|
||||||
|
private double currentStrain = 1; // We keep track of the strain level at all times throughout the beatmap.
|
||||||
|
private double currentSectionPeak = 1; // We also keep track of the peak strain level in the current section.
|
||||||
|
private readonly List<double> strainPeaks = new List<double>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Process an <see cref="OsuDifficultyHitObject"/> and update current strain values accordingly.
|
||||||
|
/// </summary>
|
||||||
|
public void Process(OsuDifficultyHitObject current)
|
||||||
|
{
|
||||||
|
currentStrain *= strainDecay(current.DeltaTime);
|
||||||
|
if (!(current.BaseObject is Spinner))
|
||||||
|
currentStrain += StrainValueOf(current) * SkillMultiplier;
|
||||||
|
|
||||||
|
currentSectionPeak = Math.Max(currentStrain, currentSectionPeak);
|
||||||
|
|
||||||
|
Previous.Push(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
|
||||||
|
/// </summary>
|
||||||
|
public void SaveCurrentPeak()
|
||||||
|
{
|
||||||
|
if (Previous.Count > 0)
|
||||||
|
strainPeaks.Add(currentSectionPeak);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the initial strain level for a new section.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">The beginning of the new section in milliseconds</param>
|
||||||
|
public void StartNewSectionFrom(double offset)
|
||||||
|
{
|
||||||
|
// The maximum strain of the new section is not zero by default, strain decays as usual regardless of section boundaries.
|
||||||
|
// This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level.
|
||||||
|
if (Previous.Count > 0)
|
||||||
|
currentSectionPeak = currentStrain * strainDecay(offset - Previous[0].BaseObject.StartTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the calculated difficulty value representing all processed <see cref="OsuDifficultyHitObject"/>s.
|
||||||
|
/// </summary>
|
||||||
|
public double DifficultyValue()
|
||||||
|
{
|
||||||
|
strainPeaks.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
|
||||||
|
|
||||||
|
double difficulty = 0;
|
||||||
|
double weight = 1;
|
||||||
|
|
||||||
|
// Difficulty is the weighted sum of the highest strains from every section.
|
||||||
|
foreach (double strain in strainPeaks)
|
||||||
|
{
|
||||||
|
difficulty += strain * weight;
|
||||||
|
weight *= 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
return difficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the strain value of an <see cref="OsuDifficultyHitObject"/>. This value is affected by previously processed objects.
|
||||||
|
/// </summary>
|
||||||
|
protected abstract double StrainValueOf(OsuDifficultyHitObject current);
|
||||||
|
|
||||||
|
private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000);
|
||||||
|
}
|
||||||
|
}
|
39
osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Speed.cs
Normal file
39
osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Speed.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// 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.Rulesets.Osu.OsuDifficulty.Preprocessing;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the skill required to press keys with regards to keeping up with the speed at which objects need to be hit.
|
||||||
|
/// </summary>
|
||||||
|
public class Speed : Skill
|
||||||
|
{
|
||||||
|
protected override double SkillMultiplier => 1400;
|
||||||
|
protected override double StrainDecayBase => 0.3;
|
||||||
|
|
||||||
|
private const double single_spacing_threshold = 125;
|
||||||
|
private const double stream_spacing_threshold = 110;
|
||||||
|
private const double almost_diameter = 90;
|
||||||
|
|
||||||
|
protected override double StrainValueOf(OsuDifficultyHitObject current)
|
||||||
|
{
|
||||||
|
double distance = current.Distance;
|
||||||
|
|
||||||
|
double speedValue;
|
||||||
|
if (distance > single_spacing_threshold)
|
||||||
|
speedValue = 2.5;
|
||||||
|
else if (distance > stream_spacing_threshold)
|
||||||
|
speedValue = 1.6 + 0.9 * (distance - stream_spacing_threshold) / (single_spacing_threshold - stream_spacing_threshold);
|
||||||
|
else if (distance > almost_diameter)
|
||||||
|
speedValue = 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter);
|
||||||
|
else if (distance > almost_diameter / 2)
|
||||||
|
speedValue = 0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2);
|
||||||
|
else
|
||||||
|
speedValue = 0.95;
|
||||||
|
|
||||||
|
return speedValue / current.DeltaTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
86
osu.Game.Rulesets.Osu/OsuDifficulty/Utils/History.cs
Normal file
86
osu.Game.Rulesets.Osu/OsuDifficulty/Utils/History.cs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// 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.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An indexed stack with Push() only, which disposes items at the bottom after the capacity is full.
|
||||||
|
/// Indexing starts at the top of the stack.
|
||||||
|
/// </summary>
|
||||||
|
public class History<T> : IEnumerable<T>
|
||||||
|
{
|
||||||
|
public int Count { get; private set; }
|
||||||
|
|
||||||
|
private readonly T[] array;
|
||||||
|
private readonly int capacity;
|
||||||
|
private int marker; // Marks the position of the most recently added item.
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the History class that is empty and has the specified capacity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="capacity">The number of items the History can hold.</param>
|
||||||
|
public History(int capacity)
|
||||||
|
{
|
||||||
|
if (capacity < 0)
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
|
||||||
|
this.capacity = capacity;
|
||||||
|
array = new T[capacity];
|
||||||
|
marker = capacity; // Set marker to the end of the array, outside of the indexed range by one.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The most recently added item is returned at index 0.
|
||||||
|
/// </summary>
|
||||||
|
public T this[int i]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (i < 0 || i > Count - 1)
|
||||||
|
throw new IndexOutOfRangeException();
|
||||||
|
|
||||||
|
i += marker;
|
||||||
|
if (i > capacity - 1)
|
||||||
|
i -= capacity;
|
||||||
|
|
||||||
|
return array[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the item as the most recent one in the history.
|
||||||
|
/// The oldest item is disposed if the history is full.
|
||||||
|
/// </summary>
|
||||||
|
public void Push(T item) // Overwrite the oldest item instead of shifting every item by one with every addition.
|
||||||
|
{
|
||||||
|
if (marker == 0)
|
||||||
|
marker = capacity - 1;
|
||||||
|
else
|
||||||
|
--marker;
|
||||||
|
|
||||||
|
array[marker] = item;
|
||||||
|
|
||||||
|
if (Count < capacity)
|
||||||
|
++Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an enumerator which enumerates items in the history starting from the most recently added one.
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerator<T> GetEnumerator()
|
||||||
|
{
|
||||||
|
for (int i = marker; i < capacity; ++i)
|
||||||
|
yield return array[i];
|
||||||
|
|
||||||
|
if (Count == capacity)
|
||||||
|
for (int i = 0; i < marker; ++i)
|
||||||
|
yield return array[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
@ -1,192 +0,0 @@
|
|||||||
// 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 osu.Game.Rulesets.Beatmaps;
|
|
||||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu
|
|
||||||
{
|
|
||||||
public class OsuDifficultyCalculator : DifficultyCalculator<OsuHitObject>
|
|
||||||
{
|
|
||||||
private const double star_scaling_factor = 0.0675;
|
|
||||||
private const double extreme_scaling_factor = 0.5;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// HitObjects are stored as a member variable.
|
|
||||||
/// </summary>
|
|
||||||
internal List<OsuHitObjectDifficulty> DifficultyHitObjects = new List<OsuHitObjectDifficulty>();
|
|
||||||
|
|
||||||
public OsuDifficultyCalculator(Beatmap beatmap) : base(beatmap)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PreprocessHitObjects()
|
|
||||||
{
|
|
||||||
foreach (var h in Objects)
|
|
||||||
(h as Slider)?.Curve?.Calculate();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
|
|
||||||
{
|
|
||||||
// Fill our custom DifficultyHitObject class, that carries additional information
|
|
||||||
DifficultyHitObjects.Clear();
|
|
||||||
|
|
||||||
foreach (var hitObject in Objects)
|
|
||||||
DifficultyHitObjects.Add(new OsuHitObjectDifficulty(hitObject));
|
|
||||||
|
|
||||||
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
|
|
||||||
DifficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
|
|
||||||
|
|
||||||
if (!CalculateStrainValues()) return 0;
|
|
||||||
|
|
||||||
double speedDifficulty = CalculateDifficulty(DifficultyType.Speed);
|
|
||||||
double aimDifficulty = CalculateDifficulty(DifficultyType.Aim);
|
|
||||||
|
|
||||||
// OverallDifficulty is not considered in this algorithm and neither is HpDrainRate. That means, that in this form the algorithm determines how hard it physically is
|
|
||||||
// to play the map, assuming, that too much of an error will not lead to a death.
|
|
||||||
// It might be desirable to include OverallDifficulty into map difficulty, but in my personal opinion it belongs more to the weighting of the actual peformance
|
|
||||||
// and is superfluous in the beatmap difficulty rating.
|
|
||||||
// If it were to be considered, then I would look at the hit window of normal HitCircles only, since Sliders and Spinners are (almost) "free" 300s and take map length
|
|
||||||
// into account as well.
|
|
||||||
|
|
||||||
// The difficulty can be scaled by any desired metric.
|
|
||||||
// In osu!tp it gets squared to account for the rapid increase in difficulty as the limit of a human is approached. (Of course it also gets scaled afterwards.)
|
|
||||||
// It would not be suitable for a star rating, therefore:
|
|
||||||
|
|
||||||
// The following is a proposal to forge a star rating from 0 to 5. It consists of taking the square root of the difficulty, since by simply scaling the easier
|
|
||||||
// 5-star maps would end up with one star.
|
|
||||||
double speedStars = Math.Sqrt(speedDifficulty) * star_scaling_factor;
|
|
||||||
double aimStars = Math.Sqrt(aimDifficulty) * star_scaling_factor;
|
|
||||||
|
|
||||||
if (categoryDifficulty != null)
|
|
||||||
{
|
|
||||||
categoryDifficulty.Add("Aim", aimStars.ToString("0.00"));
|
|
||||||
categoryDifficulty.Add("Speed", speedStars.ToString("0.00"));
|
|
||||||
|
|
||||||
double hitWindow300 = 30/*HitObjectManager.HitWindow300*/ / TimeRate;
|
|
||||||
double preEmpt = 450/*HitObjectManager.PreEmpt*/ / TimeRate;
|
|
||||||
|
|
||||||
categoryDifficulty.Add("OD", (-(hitWindow300 - 80.0) / 6.0).ToString("0.00"));
|
|
||||||
categoryDifficulty.Add("AR", (preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0).ToString("0.00"));
|
|
||||||
|
|
||||||
int maxCombo = 0;
|
|
||||||
foreach (OsuHitObjectDifficulty hitObject in DifficultyHitObjects)
|
|
||||||
maxCombo += hitObject.MaxCombo;
|
|
||||||
|
|
||||||
categoryDifficulty.Add("Max combo", maxCombo.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Again, from own observations and from the general opinion of the community a map with high speed and low aim (or vice versa) difficulty is harder,
|
|
||||||
// than a map with mediocre difficulty in both. Therefore we can not just add both difficulties together, but will introduce a scaling that favors extremes.
|
|
||||||
double starRating = speedStars + aimStars + Math.Abs(speedStars - aimStars) * extreme_scaling_factor;
|
|
||||||
// Another approach to this would be taking Speed and Aim separately to a chosen power, which again would be equivalent. This would be more convenient if
|
|
||||||
// the hit window size is to be considered as well.
|
|
||||||
|
|
||||||
// Note: The star rating is tuned extremely tight! Airman (/b/104229) and Freedom Dive (/b/126645), two of the hardest ranked maps, both score ~4.66 stars.
|
|
||||||
// Expect the easier kind of maps that officially get 5 stars to obtain around 2 by this metric. The tutorial still scores about half a star.
|
|
||||||
// Tune by yourself as you please. ;)
|
|
||||||
|
|
||||||
return starRating;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected bool CalculateStrainValues()
|
|
||||||
{
|
|
||||||
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
|
|
||||||
using (List<OsuHitObjectDifficulty>.Enumerator hitObjectsEnumerator = DifficultyHitObjects.GetEnumerator())
|
|
||||||
{
|
|
||||||
|
|
||||||
if (!hitObjectsEnumerator.MoveNext()) return false;
|
|
||||||
|
|
||||||
OsuHitObjectDifficulty current = hitObjectsEnumerator.Current;
|
|
||||||
|
|
||||||
// First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject.
|
|
||||||
while (hitObjectsEnumerator.MoveNext())
|
|
||||||
{
|
|
||||||
var next = hitObjectsEnumerator.Current;
|
|
||||||
next?.CalculateStrains(current, TimeRate);
|
|
||||||
current = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP.
|
|
||||||
/// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.
|
|
||||||
/// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage.
|
|
||||||
/// </summary>
|
|
||||||
protected const double STRAIN_STEP = 400;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The weighting of each strain value decays to this number * it's previous value
|
|
||||||
/// </summary>
|
|
||||||
protected const double DECAY_WEIGHT = 0.9;
|
|
||||||
|
|
||||||
protected double CalculateDifficulty(DifficultyType type)
|
|
||||||
{
|
|
||||||
double actualStrainStep = STRAIN_STEP * TimeRate;
|
|
||||||
|
|
||||||
// Find the highest strain value within each strain step
|
|
||||||
List<double> highestStrains = new List<double>();
|
|
||||||
double intervalEndTime = actualStrainStep;
|
|
||||||
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
|
|
||||||
|
|
||||||
OsuHitObjectDifficulty previousHitObject = null;
|
|
||||||
foreach (OsuHitObjectDifficulty hitObject in DifficultyHitObjects)
|
|
||||||
{
|
|
||||||
// While we are beyond the current interval push the currently available maximum to our strain list
|
|
||||||
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
|
|
||||||
{
|
|
||||||
highestStrains.Add(maximumStrain);
|
|
||||||
|
|
||||||
// The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
|
|
||||||
// until the beginning of the next interval.
|
|
||||||
if (previousHitObject == null)
|
|
||||||
{
|
|
||||||
maximumStrain = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
double decay = Math.Pow(OsuHitObjectDifficulty.DECAY_BASE[(int)type], (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
|
|
||||||
maximumStrain = previousHitObject.Strains[(int)type] * decay;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go to the next time interval
|
|
||||||
intervalEndTime += actualStrainStep;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtain maximum strain
|
|
||||||
maximumStrain = Math.Max(hitObject.Strains[(int)type], maximumStrain);
|
|
||||||
|
|
||||||
previousHitObject = hitObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the weighted sum over the highest strains for each interval
|
|
||||||
double difficulty = 0;
|
|
||||||
double weight = 1;
|
|
||||||
highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
|
|
||||||
|
|
||||||
foreach (double strain in highestStrains)
|
|
||||||
{
|
|
||||||
difficulty += weight * strain;
|
|
||||||
weight *= DECAY_WEIGHT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return difficulty;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
|
|
||||||
|
|
||||||
// Those values are used as array indices. Be careful when changing them!
|
|
||||||
public enum DifficultyType
|
|
||||||
{
|
|
||||||
Speed = 0,
|
|
||||||
Aim,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,6 +7,7 @@ using osu.Game.Graphics;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.OsuDifficulty;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
@ -68,9 +68,14 @@
|
|||||||
<Compile Include="Objects\Drawables\Pieces\TrianglesPiece.cs" />
|
<Compile Include="Objects\Drawables\Pieces\TrianglesPiece.cs" />
|
||||||
<Compile Include="Objects\Drawables\Pieces\SliderBall.cs" />
|
<Compile Include="Objects\Drawables\Pieces\SliderBall.cs" />
|
||||||
<Compile Include="Objects\Drawables\Pieces\SliderBody.cs" />
|
<Compile Include="Objects\Drawables\Pieces\SliderBody.cs" />
|
||||||
<Compile Include="Objects\OsuHitObjectDifficulty.cs" />
|
|
||||||
<Compile Include="Objects\SliderTick.cs" />
|
<Compile Include="Objects\SliderTick.cs" />
|
||||||
<Compile Include="OsuDifficultyCalculator.cs" />
|
<Compile Include="OsuDifficulty\OsuDifficultyCalculator.cs" />
|
||||||
|
<Compile Include="OsuDifficulty\Preprocessing\OsuDifficultyBeatmap.cs" />
|
||||||
|
<Compile Include="OsuDifficulty\Preprocessing\OsuDifficultyHitObject.cs" />
|
||||||
|
<Compile Include="OsuDifficulty\Skills\Aim.cs" />
|
||||||
|
<Compile Include="OsuDifficulty\Skills\Skill.cs" />
|
||||||
|
<Compile Include="OsuDifficulty\Skills\Speed.cs" />
|
||||||
|
<Compile Include="OsuDifficulty\Utils\History.cs" />
|
||||||
<Compile Include="OsuKeyConversionInputManager.cs" />
|
<Compile Include="OsuKeyConversionInputManager.cs" />
|
||||||
<Compile Include="Scoring\OsuScoreProcessor.cs" />
|
<Compile Include="Scoring\OsuScoreProcessor.cs" />
|
||||||
<Compile Include="UI\OsuHitRenderer.cs" />
|
<Compile Include="UI\OsuHitRenderer.cs" />
|
||||||
|
@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER),
|
Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER),
|
||||||
BlendingMode = BlendingMode.Additive,
|
BlendingMode = BlendingMode.Additive,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
Children = new []
|
Children = new[]
|
||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Children = new []
|
Children = new[]
|
||||||
{
|
{
|
||||||
symbol = new SwellSymbolPiece()
|
symbol = new SwellSymbolPiece()
|
||||||
}
|
}
|
||||||
|
@ -229,7 +229,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
|
|
||||||
if (judgedObject.HitObject.Kiai)
|
if (judgedObject.HitObject.Kiai)
|
||||||
kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject.Judgement, isRim));
|
kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject.Judgement, isRim));
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
hitExplosionContainer.Children.FirstOrDefault(e => e.Judgement == judgedObject.Judgement)?.VisualiseSecondHit();
|
hitExplosionContainer.Children.FirstOrDefault(e => e.Judgement == judgedObject.Judgement)?.VisualiseSecondHit();
|
||||||
|
@ -166,4 +166,3 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,4 +86,3 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ using OpenTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Beatmaps.Drawables
|
namespace osu.Game.Beatmaps.Drawables
|
||||||
{
|
{
|
||||||
|
|
||||||
public class DifficultyIcon : DifficultyColouredContainer
|
public class DifficultyIcon : DifficultyColouredContainer
|
||||||
{
|
{
|
||||||
private readonly BeatmapInfo beatmap;
|
private readonly BeatmapInfo beatmap;
|
||||||
|
@ -13,7 +13,6 @@ namespace osu.Game.Configuration
|
|||||||
protected override void InitialiseDefaults()
|
protected override void InitialiseDefaults()
|
||||||
{
|
{
|
||||||
// UI/selection defaults
|
// UI/selection defaults
|
||||||
|
|
||||||
Set(OsuSetting.Ruleset, 0, 0, int.MaxValue);
|
Set(OsuSetting.Ruleset, 0, 0, int.MaxValue);
|
||||||
Set(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Details);
|
Set(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Details);
|
||||||
|
|
||||||
@ -25,7 +24,6 @@ namespace osu.Game.Configuration
|
|||||||
Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1);
|
Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1);
|
||||||
|
|
||||||
// Online settings
|
// Online settings
|
||||||
|
|
||||||
Set(OsuSetting.Username, string.Empty);
|
Set(OsuSetting.Username, string.Empty);
|
||||||
Set(OsuSetting.Token, string.Empty);
|
Set(OsuSetting.Token, string.Empty);
|
||||||
|
|
||||||
@ -40,14 +38,12 @@ namespace osu.Game.Configuration
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
|
|
||||||
Set(OsuSetting.MenuVoice, true);
|
Set(OsuSetting.MenuVoice, true);
|
||||||
Set(OsuSetting.MenuMusic, true);
|
Set(OsuSetting.MenuMusic, true);
|
||||||
|
|
||||||
Set(OsuSetting.AudioOffset, 0, -500.0, 500.0);
|
Set(OsuSetting.AudioOffset, 0, -500.0, 500.0);
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
|
|
||||||
Set(OsuSetting.MenuCursorSize, 1.0, 0.5f, 2);
|
Set(OsuSetting.MenuCursorSize, 1.0, 0.5f, 2);
|
||||||
Set(OsuSetting.GameplayCursorSize, 1.0, 0.5f, 2);
|
Set(OsuSetting.GameplayCursorSize, 1.0, 0.5f, 2);
|
||||||
Set(OsuSetting.AutoCursorSize, false);
|
Set(OsuSetting.AutoCursorSize, false);
|
||||||
@ -56,7 +52,6 @@ namespace osu.Game.Configuration
|
|||||||
Set(OsuSetting.MouseDisableWheel, false);
|
Set(OsuSetting.MouseDisableWheel, false);
|
||||||
|
|
||||||
// Graphics
|
// Graphics
|
||||||
|
|
||||||
Set(OsuSetting.ShowFpsDisplay, false);
|
Set(OsuSetting.ShowFpsDisplay, false);
|
||||||
|
|
||||||
Set(OsuSetting.MenuParallax, true);
|
Set(OsuSetting.MenuParallax, true);
|
||||||
@ -65,7 +60,6 @@ namespace osu.Game.Configuration
|
|||||||
Set(OsuSetting.SnakingOutSliders, true);
|
Set(OsuSetting.SnakingOutSliders, true);
|
||||||
|
|
||||||
// Gameplay
|
// Gameplay
|
||||||
|
|
||||||
Set(OsuSetting.DimLevel, 0.3, 0, 1);
|
Set(OsuSetting.DimLevel, 0.3, 0, 1);
|
||||||
|
|
||||||
Set(OsuSetting.ShowInterface, true);
|
Set(OsuSetting.ShowInterface, true);
|
||||||
@ -75,7 +69,6 @@ namespace osu.Game.Configuration
|
|||||||
Set(OsuSetting.PlaybackSpeed, 1.0, 0.5f, 2);
|
Set(OsuSetting.PlaybackSpeed, 1.0, 0.5f, 2);
|
||||||
|
|
||||||
// Update
|
// Update
|
||||||
|
|
||||||
Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
|
Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,4 +39,3 @@ namespace osu.Game.Database
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,4 +36,3 @@ namespace osu.Game.Database
|
|||||||
public string StoryboardFile { get; set; }
|
public string StoryboardFile { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
background = new Box
|
background = new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = new Color4(0,0,0,0)
|
Colour = new Color4(0, 0, 0, 0)
|
||||||
},
|
},
|
||||||
bar = new Box
|
bar = new Box
|
||||||
{
|
{
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
throw new InvalidOperationException("OsuEnumDropdown only supports enums as the generic type argument");
|
throw new InvalidOperationException("OsuEnumDropdown only supports enums as the generic type argument");
|
||||||
|
|
||||||
List<KeyValuePair<string, T>> items = new List<KeyValuePair<string, T>>();
|
List<KeyValuePair<string, T>> items = new List<KeyValuePair<string, T>>();
|
||||||
foreach(var val in (T[])Enum.GetValues(typeof(T)))
|
foreach (var val in (T[])Enum.GetValues(typeof(T)))
|
||||||
{
|
{
|
||||||
var field = typeof(T).GetField(Enum.GetName(typeof(T), val));
|
var field = typeof(T).GetField(Enum.GetName(typeof(T), val));
|
||||||
items.Add(
|
items.Add(
|
||||||
|
@ -10,22 +10,20 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Direct
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
public class SortTabControl : OsuTabControl<SortCriteria>
|
public class PageTabControl<T> : OsuTabControl<T>
|
||||||
{
|
{
|
||||||
protected override TabItem<SortCriteria> CreateTabItem(SortCriteria value) => new SortTabItem(value);
|
protected override TabItem<T> CreateTabItem(T value) => new PageTabItem(value);
|
||||||
|
|
||||||
public SortTabControl()
|
public PageTabControl()
|
||||||
{
|
{
|
||||||
Height = 30;
|
Height = 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SortTabItem : TabItem<SortCriteria>
|
private class PageTabItem : TabItem<T>
|
||||||
{
|
{
|
||||||
private const float transition_duration = 100;
|
private const float transition_duration = 100;
|
||||||
|
|
||||||
@ -46,7 +44,7 @@ namespace osu.Game.Overlays.Direct
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SortTabItem(SortCriteria value) : base(value)
|
public PageTabItem(T value) : base(value)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.X;
|
AutoSizeAxes = Axes.X;
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
@ -104,14 +102,4 @@ namespace osu.Game.Overlays.Direct
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SortCriteria
|
|
||||||
{
|
|
||||||
Title,
|
|
||||||
Artist,
|
|
||||||
Creator,
|
|
||||||
Difficulty,
|
|
||||||
Ranked,
|
|
||||||
Rating,
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -90,7 +90,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Offset = new Vector2(2, 0),
|
Offset = new Vector2(2, 0),
|
||||||
Radius = 2,
|
Radius = 2,
|
||||||
},
|
},
|
||||||
Children = new [] {
|
Children = new[] {
|
||||||
IconLayer = new Box
|
IconLayer = new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -124,7 +124,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Offset = new Vector2(2, 0),
|
Offset = new Vector2(2, 0),
|
||||||
Radius = 2,
|
Radius = 2,
|
||||||
},
|
},
|
||||||
Children = new [] {
|
Children = new[] {
|
||||||
TextLayer = new Box
|
TextLayer = new Box
|
||||||
{
|
{
|
||||||
Origin = Anchor.TopLeft,
|
Origin = Anchor.TopLeft,
|
||||||
|
@ -59,7 +59,6 @@ namespace osu.Game.Online.API
|
|||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
osu.Game/Online/API/Requests/GetUsersRequest.cs
Normal file
20
osu.Game/Online/API/Requests/GetUsersRequest.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.API.Requests
|
||||||
|
{
|
||||||
|
public class GetUsersRequest : APIRequest<List<RankingEntry>>
|
||||||
|
{
|
||||||
|
protected override string Target => @"rankings/osu/performance";
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RankingEntry
|
||||||
|
{
|
||||||
|
[JsonProperty]
|
||||||
|
public User User;
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
using osu.Framework.Lists;
|
using osu.Framework.Lists;
|
||||||
|
|
||||||
namespace osu.Game.Online.Chat
|
namespace osu.Game.Online.Chat
|
||||||
@ -25,7 +26,7 @@ namespace osu.Game.Online.Chat
|
|||||||
|
|
||||||
public readonly SortedList<Message> Messages = new SortedList<Message>(Comparer<Message>.Default);
|
public readonly SortedList<Message> Messages = new SortedList<Message>(Comparer<Message>.Default);
|
||||||
|
|
||||||
//internal bool Joined;
|
public Bindable<bool> Joined = new Bindable<bool>();
|
||||||
|
|
||||||
public bool ReadOnly => Name != "#lazer";
|
public bool ReadOnly => Name != "#lazer";
|
||||||
|
|
||||||
|
@ -43,6 +43,8 @@ namespace osu.Game
|
|||||||
|
|
||||||
private DirectOverlay direct;
|
private DirectOverlay direct;
|
||||||
|
|
||||||
|
private SocialOverlay social;
|
||||||
|
|
||||||
private Intro intro
|
private Intro intro
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -148,7 +150,7 @@ namespace osu.Game
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
volume = new VolumeControl(),
|
volume = new VolumeControl(),
|
||||||
overlayContent = new Container{ RelativeSizeAxes = Axes.Both },
|
overlayContent = new Container { RelativeSizeAxes = Axes.Both },
|
||||||
new OnScreenDisplay(),
|
new OnScreenDisplay(),
|
||||||
new GlobalHotkeys //exists because UserInputManager is at a level below us.
|
new GlobalHotkeys //exists because UserInputManager is at a level below us.
|
||||||
{
|
{
|
||||||
@ -165,6 +167,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
//overlay elements
|
//overlay elements
|
||||||
LoadComponentAsync(direct = new DirectOverlay { Depth = -1 }, mainContent.Add);
|
LoadComponentAsync(direct = new DirectOverlay { Depth = -1 }, mainContent.Add);
|
||||||
|
LoadComponentAsync(social = new SocialOverlay { Depth = -1 }, mainContent.Add);
|
||||||
LoadComponentAsync(chat = new ChatOverlay { Depth = -1 }, mainContent.Add);
|
LoadComponentAsync(chat = new ChatOverlay { Depth = -1 }, mainContent.Add);
|
||||||
LoadComponentAsync(settings = new SettingsOverlay { Depth = -1 }, overlayContent.Add);
|
LoadComponentAsync(settings = new SettingsOverlay { Depth = -1 }, overlayContent.Add);
|
||||||
LoadComponentAsync(musicController = new MusicController
|
LoadComponentAsync(musicController = new MusicController
|
||||||
@ -198,11 +201,16 @@ namespace osu.Game
|
|||||||
};
|
};
|
||||||
|
|
||||||
Dependencies.Cache(settings);
|
Dependencies.Cache(settings);
|
||||||
|
Dependencies.Cache(social);
|
||||||
Dependencies.Cache(chat);
|
Dependencies.Cache(chat);
|
||||||
Dependencies.Cache(musicController);
|
Dependencies.Cache(musicController);
|
||||||
Dependencies.Cache(notificationManager);
|
Dependencies.Cache(notificationManager);
|
||||||
Dependencies.Cache(dialogOverlay);
|
Dependencies.Cache(dialogOverlay);
|
||||||
|
|
||||||
|
// ensure both overlays aren't presented at the same time
|
||||||
|
chat.StateChanged += (container, state) => social.State = state == Visibility.Visible ? Visibility.Hidden : social.State;
|
||||||
|
social.StateChanged += (container, state) => chat.State = state == Visibility.Visible ? Visibility.Hidden : chat.State;
|
||||||
|
|
||||||
LoadComponentAsync(Toolbar = new Toolbar
|
LoadComponentAsync(Toolbar = new Toolbar
|
||||||
{
|
{
|
||||||
Depth = -3,
|
Depth = -3,
|
||||||
@ -234,6 +242,9 @@ namespace osu.Game
|
|||||||
case Key.F8:
|
case Key.F8:
|
||||||
chat.ToggleVisibility();
|
chat.ToggleVisibility();
|
||||||
return true;
|
return true;
|
||||||
|
case Key.F9:
|
||||||
|
social.ToggleVisibility();
|
||||||
|
return true;
|
||||||
case Key.PageUp:
|
case Key.PageUp:
|
||||||
case Key.PageDown:
|
case Key.PageDown:
|
||||||
var swClock = (Clock as ThrottledFrameClock)?.Source as StopwatchClock;
|
var swClock = (Clock as ThrottledFrameClock)?.Source as StopwatchClock;
|
||||||
@ -292,6 +303,7 @@ namespace osu.Game
|
|||||||
musicController.State = Visibility.Hidden;
|
musicController.State = Visibility.Hidden;
|
||||||
chat.State = Visibility.Hidden;
|
chat.State = Visibility.Hidden;
|
||||||
direct.State = Visibility.Hidden;
|
direct.State = Visibility.Hidden;
|
||||||
|
social.State = Visibility.Hidden;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
188
osu.Game/Overlays/Chat/ChannelListItem.cs
Normal file
188
osu.Game/Overlays/Chat/ChannelListItem.cs
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
// 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 OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Online.Chat;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Chat
|
||||||
|
{
|
||||||
|
public class ChannelListItem : ClickableContainer, IFilterable
|
||||||
|
{
|
||||||
|
private const float width_padding = 5;
|
||||||
|
private const float channel_width = 150;
|
||||||
|
private const float text_size = 15;
|
||||||
|
private const float transition_duration = 100;
|
||||||
|
|
||||||
|
private readonly Channel channel;
|
||||||
|
|
||||||
|
private readonly Bindable<bool> joinedBind = new Bindable<bool>();
|
||||||
|
private readonly OsuSpriteText name;
|
||||||
|
private readonly OsuSpriteText topic;
|
||||||
|
private readonly TextAwesome joinedCheckmark;
|
||||||
|
|
||||||
|
private Color4 joinedColour;
|
||||||
|
private Color4 topicColour;
|
||||||
|
private Color4 hoverColour;
|
||||||
|
|
||||||
|
public string[] FilterTerms => new[] { channel.Name };
|
||||||
|
public bool MatchingFilter
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
FadeTo(value ? 1f : 0f, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action<Channel> OnRequestJoin;
|
||||||
|
public Action<Channel> OnRequestLeave;
|
||||||
|
|
||||||
|
public ChannelListItem(Channel channel)
|
||||||
|
{
|
||||||
|
this.channel = channel;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
Action = () => { (channel.Joined ? OnRequestLeave : OnRequestJoin)?.Invoke(channel); };
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
joinedCheckmark = new TextAwesome
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
Icon = FontAwesome.fa_check_circle,
|
||||||
|
TextSize = text_size,
|
||||||
|
Shadow = false,
|
||||||
|
Margin = new MarginPadding { Right = 10f },
|
||||||
|
Alpha = 0f,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Width = channel_width,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
name = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = channel.ToString(),
|
||||||
|
TextSize = text_size,
|
||||||
|
Font = @"Exo2.0-Bold",
|
||||||
|
Shadow = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Width = 0.7f,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Margin = new MarginPadding { Left = width_padding },
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
topic = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = channel.Topic,
|
||||||
|
TextSize = text_size,
|
||||||
|
Font = @"Exo2.0-SemiBold",
|
||||||
|
Shadow = false,
|
||||||
|
Alpha = 0.8f,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Margin = new MarginPadding { Left = width_padding },
|
||||||
|
Spacing = new Vector2(3f, 0f),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new TextAwesome
|
||||||
|
{
|
||||||
|
Icon = FontAwesome.fa_user,
|
||||||
|
TextSize = text_size - 2,
|
||||||
|
Shadow = false,
|
||||||
|
Margin = new MarginPadding { Top = 1 },
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = @"0",
|
||||||
|
TextSize = text_size,
|
||||||
|
Font = @"Exo2.0-SemiBold",
|
||||||
|
Shadow = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
topicColour = colours.Gray9;
|
||||||
|
joinedColour = colours.Blue;
|
||||||
|
hoverColour = colours.Yellow;
|
||||||
|
|
||||||
|
joinedBind.ValueChanged += updateColour;
|
||||||
|
joinedBind.BindTo(channel.Joined);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(InputState state)
|
||||||
|
{
|
||||||
|
if (!channel.Joined.Value)
|
||||||
|
name.FadeColour(hoverColour, 50, EasingTypes.OutQuint);
|
||||||
|
|
||||||
|
return base.OnHover(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(InputState state)
|
||||||
|
{
|
||||||
|
if (!channel.Joined.Value)
|
||||||
|
name.FadeColour(Color4.White, transition_duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateColour(bool joined)
|
||||||
|
{
|
||||||
|
if (joined)
|
||||||
|
{
|
||||||
|
name.FadeColour(Color4.White, transition_duration);
|
||||||
|
joinedCheckmark.FadeTo(1f, transition_duration);
|
||||||
|
topic.FadeTo(0.8f, transition_duration);
|
||||||
|
topic.FadeColour(Color4.White, transition_duration);
|
||||||
|
FadeColour(joinedColour, transition_duration);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
joinedCheckmark.FadeTo(0f, transition_duration);
|
||||||
|
topic.FadeTo(1f, transition_duration);
|
||||||
|
topic.FadeColour(topicColour, transition_duration);
|
||||||
|
FadeColour(Color4.White, transition_duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
osu.Game/Overlays/Chat/ChannelSection.cs
Normal file
63
osu.Game/Overlays/Chat/ChannelSection.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using OpenTK;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Online.Chat;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Chat
|
||||||
|
{
|
||||||
|
public class ChannelSection : Container, IHasFilterableChildren
|
||||||
|
{
|
||||||
|
private readonly OsuSpriteText header;
|
||||||
|
|
||||||
|
public readonly FillFlowContainer<ChannelListItem> ChannelFlow;
|
||||||
|
|
||||||
|
public IEnumerable<IFilterable> FilterableChildren => ChannelFlow.Children;
|
||||||
|
public string[] FilterTerms => new[] { Header };
|
||||||
|
public bool MatchingFilter
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
FadeTo(value ? 1f : 0f, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Header
|
||||||
|
{
|
||||||
|
get { return header.Text; }
|
||||||
|
set { header.Text = value.ToUpper(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Channel> Channels
|
||||||
|
{
|
||||||
|
set { ChannelFlow.Children = value.Select(c => new ChannelListItem(c)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelSection()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
header = new OsuSpriteText
|
||||||
|
{
|
||||||
|
TextSize = 15,
|
||||||
|
Font = @"Exo2.0-Bold",
|
||||||
|
},
|
||||||
|
ChannelFlow = new FillFlowContainer<ChannelListItem>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Margin = new MarginPadding { Top = 25 },
|
||||||
|
Spacing = new Vector2(0f, 5f),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
184
osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs
Normal file
184
osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
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.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Backgrounds;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online.Chat;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Chat
|
||||||
|
{
|
||||||
|
public class ChannelSelectionOverlay : FocusedOverlayContainer
|
||||||
|
{
|
||||||
|
public static readonly float WIDTH_PADDING = 170;
|
||||||
|
|
||||||
|
private const float transition_duration = 500;
|
||||||
|
|
||||||
|
private readonly Box bg;
|
||||||
|
private readonly Triangles triangles;
|
||||||
|
private readonly Box headerBg;
|
||||||
|
private readonly SearchTextBox search;
|
||||||
|
private readonly SearchContainer<ChannelSection> sectionsFlow;
|
||||||
|
|
||||||
|
public Action<Channel> OnRequestJoin;
|
||||||
|
public Action<Channel> OnRequestLeave;
|
||||||
|
|
||||||
|
public IEnumerable<ChannelSection> Sections
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
sectionsFlow.Children = value;
|
||||||
|
|
||||||
|
foreach (ChannelSection s in sectionsFlow.Children)
|
||||||
|
{
|
||||||
|
foreach (ChannelListItem c in s.ChannelFlow.Children)
|
||||||
|
{
|
||||||
|
c.OnRequestJoin = channel => { OnRequestJoin?.Invoke(channel); };
|
||||||
|
c.OnRequestLeave = channel => { OnRequestLeave?.Invoke(channel); };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelSelectionOverlay()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
bg = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
triangles = new Triangles
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
TriangleScale = 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Top = 85, Right = WIDTH_PADDING },
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new ScrollContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
sectionsFlow = new SearchContainer<ChannelSection>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
LayoutDuration = 200,
|
||||||
|
LayoutEasing = EasingTypes.OutQuint,
|
||||||
|
Spacing = new Vector2(0f, 20f),
|
||||||
|
Padding = new MarginPadding { Vertical = 20, Left = WIDTH_PADDING },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
headerBg = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(0f, 10f),
|
||||||
|
Padding = new MarginPadding { Top = 10f, Bottom = 10f, Left = WIDTH_PADDING, Right = WIDTH_PADDING },
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = @"Chat Channels",
|
||||||
|
TextSize = 20,
|
||||||
|
Shadow = false,
|
||||||
|
},
|
||||||
|
search = new HeaderSearchTextBox
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
PlaceholderText = @"Search",
|
||||||
|
Exit = Hide,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
search.Current.ValueChanged += newValue => sectionsFlow.SearchTerm = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
bg.Colour = colours.Gray3;
|
||||||
|
triangles.ColourDark = colours.Gray3;
|
||||||
|
triangles.ColourLight = OsuColour.FromHex(@"353535");
|
||||||
|
|
||||||
|
headerBg.Colour = colours.Gray2.Opacity(0.75f);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFocus(InputState state)
|
||||||
|
{
|
||||||
|
InputManager.ChangeFocus(search);
|
||||||
|
base.OnFocus(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopIn()
|
||||||
|
{
|
||||||
|
if (Alpha == 0) MoveToY(DrawHeight);
|
||||||
|
|
||||||
|
FadeIn(transition_duration, EasingTypes.OutQuint);
|
||||||
|
MoveToY(0, transition_duration, EasingTypes.OutQuint);
|
||||||
|
|
||||||
|
search.HoldFocus = true;
|
||||||
|
base.PopIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopOut()
|
||||||
|
{
|
||||||
|
FadeOut(transition_duration, EasingTypes.InSine);
|
||||||
|
MoveToY(DrawHeight, transition_duration, EasingTypes.InSine);
|
||||||
|
|
||||||
|
search.HoldFocus = false;
|
||||||
|
base.PopOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class HeaderSearchTextBox : SearchTextBox
|
||||||
|
{
|
||||||
|
protected override Color4 BackgroundFocused => Color4.Black.Opacity(0.2f);
|
||||||
|
protected override Color4 BackgroundUnfocused => Color4.Black.Opacity(0.2f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -207,11 +207,15 @@ namespace osu.Game.Overlays.Chat
|
|||||||
{
|
{
|
||||||
public override bool Active
|
public override bool Active
|
||||||
{
|
{
|
||||||
get { return base.Active; }
|
get { return false; }
|
||||||
|
// ReSharper disable once ValueParameterNotUsed
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
activeBindable.Value = value;
|
// we basically never want this tab to become active.
|
||||||
base.Active = value;
|
// this allows us to become a "toggle" tab.
|
||||||
|
// is a bit hacky, to say the least.
|
||||||
|
activeBindable.Value = !activeBindable.Value;
|
||||||
|
base.Active = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,6 +224,9 @@ namespace osu.Game.Overlays.Chat
|
|||||||
public ChannelSelectorTabItem(Channel value, Bindable<bool> active) : base(value)
|
public ChannelSelectorTabItem(Channel value, Bindable<bool> active) : base(value)
|
||||||
{
|
{
|
||||||
activeBindable = active;
|
activeBindable = active;
|
||||||
|
activeBindable.ValueChanged += v => selectorUpdateState();
|
||||||
|
|
||||||
|
|
||||||
Depth = float.MaxValue;
|
Depth = float.MaxValue;
|
||||||
Width = 45;
|
Width = 45;
|
||||||
|
|
||||||
@ -235,6 +242,26 @@ namespace osu.Game.Overlays.Chat
|
|||||||
backgroundInactive = colour.Gray2;
|
backgroundInactive = colour.Gray2;
|
||||||
backgroundActive = colour.Gray3;
|
backgroundActive = colour.Gray3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
selectorUpdateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(InputState state)
|
||||||
|
{
|
||||||
|
selectorUpdateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void selectorUpdateState()
|
||||||
|
{
|
||||||
|
if (activeBindable.Value)
|
||||||
|
fadeActive();
|
||||||
|
else
|
||||||
|
fadeInactive();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,6 @@ namespace osu.Game.Overlays.Chat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scrollToEnd() => Scheduler.AddDelayed(() => scroll.ScrollToEnd(), 50);
|
private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ namespace osu.Game.Overlays
|
|||||||
public class ChatOverlay : FocusedOverlayContainer, IOnlineComponent
|
public class ChatOverlay : FocusedOverlayContainer, IOnlineComponent
|
||||||
{
|
{
|
||||||
private const float textbox_height = 60;
|
private const float textbox_height = 60;
|
||||||
|
private const float channel_selection_min_height = 0.3f;
|
||||||
|
|
||||||
private ScheduledDelegate messageRequest;
|
private ScheduledDelegate messageRequest;
|
||||||
|
|
||||||
@ -48,16 +49,21 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private readonly ChatTabControl channelTabs;
|
private readonly ChatTabControl channelTabs;
|
||||||
|
|
||||||
|
private readonly Container chatContainer;
|
||||||
private readonly Box chatBackground;
|
private readonly Box chatBackground;
|
||||||
private readonly Box tabBackground;
|
private readonly Box tabBackground;
|
||||||
|
|
||||||
private Bindable<double> chatHeight;
|
private Bindable<double> chatHeight;
|
||||||
|
|
||||||
|
private readonly Container channelSelectionContainer;
|
||||||
|
private readonly ChannelSelectionOverlay channelSelection;
|
||||||
|
|
||||||
|
protected override bool InternalContains(Vector2 screenSpacePos) => chatContainer.Contains(screenSpacePos) || channelSelection.State == Visibility.Visible && channelSelection.Contains(screenSpacePos);
|
||||||
|
|
||||||
public ChatOverlay()
|
public ChatOverlay()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
RelativePositionAxes = Axes.Both;
|
RelativePositionAxes = Axes.Both;
|
||||||
Size = new Vector2(1, DEFAULT_HEIGHT);
|
|
||||||
Anchor = Anchor.BottomLeft;
|
Anchor = Anchor.BottomLeft;
|
||||||
Origin = Anchor.BottomLeft;
|
Origin = Anchor.BottomLeft;
|
||||||
|
|
||||||
@ -65,74 +71,120 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
channelSelectionContainer = new Container
|
||||||
{
|
{
|
||||||
Name = @"chat area",
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Top = TAB_AREA_HEIGHT },
|
Height = 1f - DEFAULT_HEIGHT,
|
||||||
Children = new Drawable[]
|
Masking = true,
|
||||||
|
Children = new[]
|
||||||
{
|
{
|
||||||
chatBackground = new Box
|
channelSelection = new ChannelSelectionOverlay
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
currentChannelContainer = new Container
|
},
|
||||||
|
},
|
||||||
|
chatContainer = new Container
|
||||||
|
{
|
||||||
|
Name = @"chat container",
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Height = DEFAULT_HEIGHT,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
{
|
{
|
||||||
|
Name = @"chat area",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding
|
Padding = new MarginPadding { Top = TAB_AREA_HEIGHT },
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
Bottom = textbox_height + padding
|
chatBackground = new Box
|
||||||
},
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
currentChannelContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Bottom = textbox_height + padding
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = textbox_height,
|
||||||
|
Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Top = padding * 2,
|
||||||
|
Bottom = padding * 2,
|
||||||
|
Left = ChatLine.LEFT_PADDING + padding * 2,
|
||||||
|
Right = padding * 2,
|
||||||
|
},
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
inputTextBox = new FocusedTextBox
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Height = 1,
|
||||||
|
PlaceholderText = "type your message",
|
||||||
|
Exit = () => State = Visibility.Hidden,
|
||||||
|
OnCommit = postMessage,
|
||||||
|
ReleaseFocusOnCommit = false,
|
||||||
|
HoldFocus = true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
Name = @"tabs area",
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = textbox_height,
|
Height = TAB_AREA_HEIGHT,
|
||||||
Padding = new MarginPadding
|
|
||||||
{
|
|
||||||
Top = padding * 2,
|
|
||||||
Bottom = padding * 2,
|
|
||||||
Left = ChatLine.LEFT_PADDING + padding * 2,
|
|
||||||
Right = padding * 2,
|
|
||||||
},
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
inputTextBox = new FocusedTextBox
|
tabBackground = new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Height = 1,
|
Colour = Color4.Black,
|
||||||
PlaceholderText = "type your message",
|
},
|
||||||
Exit = () => State = Visibility.Hidden,
|
channelTabs = new ChatTabControl
|
||||||
OnCommit = postMessage,
|
{
|
||||||
HoldFocus = true,
|
RelativeSizeAxes = Axes.Both,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Name = @"tabs area",
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = TAB_AREA_HEIGHT,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
tabBackground = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = Color4.Black,
|
|
||||||
},
|
},
|
||||||
channelTabs = new ChatTabControl
|
},
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
channelTabs.Current.ValueChanged += newChannel => CurrentChannel = newChannel;
|
channelTabs.Current.ValueChanged += newChannel => CurrentChannel = newChannel;
|
||||||
|
channelTabs.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden;
|
||||||
|
channelSelection.StateChanged += (overlay, state) =>
|
||||||
|
{
|
||||||
|
channelTabs.ChannelSelectorActive.Value = state == Visibility.Visible;
|
||||||
|
|
||||||
|
if (state == Visibility.Visible)
|
||||||
|
{
|
||||||
|
inputTextBox.HoldFocus = false;
|
||||||
|
if (1f - chatHeight.Value < channel_selection_min_height)
|
||||||
|
{
|
||||||
|
chatContainer.ResizeHeightTo(1f - channel_selection_min_height, 800, EasingTypes.OutQuint);
|
||||||
|
channelSelectionContainer.ResizeHeightTo(channel_selection_min_height, 800, EasingTypes.OutQuint);
|
||||||
|
channelSelection.Show();
|
||||||
|
chatHeight.Value = 1f - channel_selection_min_height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inputTextBox.HoldFocus = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private double startDragChatHeight;
|
private double startDragChatHeight;
|
||||||
@ -205,8 +257,9 @@ namespace osu.Game.Overlays
|
|||||||
chatHeight = config.GetBindable<double>(OsuSetting.ChatDisplayHeight);
|
chatHeight = config.GetBindable<double>(OsuSetting.ChatDisplayHeight);
|
||||||
chatHeight.ValueChanged += h =>
|
chatHeight.ValueChanged += h =>
|
||||||
{
|
{
|
||||||
Height = (float)h;
|
chatContainer.Height = (float)h;
|
||||||
tabBackground.FadeTo(Height == 1 ? 1 : 0.8f, 200);
|
channelSelectionContainer.Height = 1f - (float)h;
|
||||||
|
tabBackground.FadeTo(h == 1 ? 1 : 0.8f, 200);
|
||||||
};
|
};
|
||||||
chatHeight.TriggerChange();
|
chatHeight.TriggerChange();
|
||||||
|
|
||||||
@ -243,6 +296,16 @@ namespace osu.Game.Overlays
|
|||||||
addChannel(channels.Find(c => c.Name == @"#lazer"));
|
addChannel(channels.Find(c => c.Name == @"#lazer"));
|
||||||
addChannel(channels.Find(c => c.Name == @"#osu"));
|
addChannel(channels.Find(c => c.Name == @"#osu"));
|
||||||
addChannel(channels.Find(c => c.Name == @"#lobby"));
|
addChannel(channels.Find(c => c.Name == @"#lobby"));
|
||||||
|
|
||||||
|
channelSelection.OnRequestJoin = addChannel;
|
||||||
|
channelSelection.Sections = new[]
|
||||||
|
{
|
||||||
|
new ChannelSection
|
||||||
|
{
|
||||||
|
Header = "All Channels",
|
||||||
|
Channels = channels,
|
||||||
|
},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
messageRequest = Scheduler.AddDelayed(fetchNewMessages, 1000, true);
|
messageRequest = Scheduler.AddDelayed(fetchNewMessages, 1000, true);
|
||||||
@ -262,9 +325,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (currentChannel == value) return;
|
if (currentChannel == value || value == null) return;
|
||||||
|
|
||||||
if (channelTabs.ChannelSelectorActive) return;
|
|
||||||
|
|
||||||
currentChannel = value;
|
currentChannel = value;
|
||||||
|
|
||||||
@ -306,7 +367,6 @@ namespace osu.Game.Overlays
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
careChannels.Add(channel);
|
careChannels.Add(channel);
|
||||||
channelTabs.AddItem(channel);
|
channelTabs.AddItem(channel);
|
||||||
}
|
}
|
||||||
@ -316,6 +376,8 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
if (CurrentChannel == null)
|
if (CurrentChannel == null)
|
||||||
CurrentChannel = channel;
|
CurrentChannel = channel;
|
||||||
|
|
||||||
|
channel.Joined.Value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchInitialMessages(Channel channel)
|
private void fetchInitialMessages(Channel channel)
|
||||||
|
@ -5,117 +5,35 @@ using OpenTK;
|
|||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Overlays.SearchableList;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Direct
|
namespace osu.Game.Overlays.Direct
|
||||||
{
|
{
|
||||||
public class FilterControl : Container
|
public class FilterControl : SearchableListFilterControl<DirectSortCritera, RankStatus>
|
||||||
{
|
{
|
||||||
public static readonly float HEIGHT = 35 + 32 + 30 + padding * 2; // search + mode toggle buttons + sort tabs + padding
|
private FillFlowContainer<RulesetToggleButton> modeButtons;
|
||||||
|
|
||||||
private const float padding = 10;
|
protected override Color4 BackgroundColour => OsuColour.FromHex(@"384552");
|
||||||
|
protected override DirectSortCritera DefaultTab => DirectSortCritera.Title;
|
||||||
private readonly Box tabStrip;
|
protected override Drawable CreateSupplementaryControls()
|
||||||
private readonly FillFlowContainer<RulesetToggleButton> modeButtons;
|
|
||||||
|
|
||||||
public readonly SearchTextBox Search;
|
|
||||||
public readonly SortTabControl SortTabs;
|
|
||||||
public readonly OsuEnumDropdown<RankStatus> RankStatusDropdown;
|
|
||||||
public readonly Bindable<DirectOverlay.PanelDisplayStyle> DisplayStyle = new Bindable<DirectOverlay.PanelDisplayStyle>();
|
|
||||||
|
|
||||||
protected override bool InternalContains(Vector2 screenSpacePos) => base.InternalContains(screenSpacePos) || RankStatusDropdown.Contains(screenSpacePos);
|
|
||||||
|
|
||||||
public FilterControl()
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
modeButtons = new FillFlowContainer<RulesetToggleButton>
|
||||||
Height = HEIGHT;
|
|
||||||
DisplayStyle.Value = DirectOverlay.PanelDisplayStyle.Grid;
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
new Box
|
AutoSizeAxes = Axes.Both,
|
||||||
{
|
Spacing = new Vector2(10f, 0f),
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = OsuColour.FromHex(@"384552"),
|
|
||||||
Alpha = 0.9f,
|
|
||||||
},
|
|
||||||
tabStrip = new Box
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.TopLeft,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = 1,
|
|
||||||
},
|
|
||||||
new FillFlowContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Padding = new MarginPadding { Left = DirectOverlay.WIDTH_PADDING, Right = DirectOverlay.WIDTH_PADDING },
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
Search = new DirectSearchTextBox
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Margin = new MarginPadding { Top = padding },
|
|
||||||
},
|
|
||||||
modeButtons = new FillFlowContainer<RulesetToggleButton>
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Spacing = new Vector2(padding, 0f),
|
|
||||||
Margin = new MarginPadding { Top = padding },
|
|
||||||
},
|
|
||||||
SortTabs = new SortTabControl
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new FillFlowContainer
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Anchor = Anchor.TopRight,
|
|
||||||
Origin = Anchor.TopRight,
|
|
||||||
Spacing = new Vector2(10f, 0f),
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Margin = new MarginPadding { Top = HEIGHT - SlimEnumDropdown<DirectTab>.HEIGHT - padding, Right = DirectOverlay.WIDTH_PADDING },
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new FillFlowContainer
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Spacing = new Vector2(5f, 0f),
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
new DisplayStyleToggleButton(FontAwesome.fa_th_large, DirectOverlay.PanelDisplayStyle.Grid, DisplayStyle),
|
|
||||||
new DisplayStyleToggleButton(FontAwesome.fa_list_ul, DirectOverlay.PanelDisplayStyle.List, DisplayStyle),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RankStatusDropdown = new SlimEnumDropdown<RankStatus>
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.None,
|
|
||||||
Width = 160f,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
RankStatusDropdown.Current.Value = RankStatus.RankedApproved;
|
return modeButtons;
|
||||||
SortTabs.Current.Value = SortCriteria.Title;
|
|
||||||
SortTabs.Current.TriggerChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(OsuGame game, RulesetDatabase rulesets, OsuColour colours)
|
private void load(OsuGame game, RulesetDatabase rulesets, OsuColour colours)
|
||||||
{
|
{
|
||||||
tabStrip.Colour = colours.Yellow;
|
DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark;
|
||||||
RankStatusDropdown.AccentColour = colours.BlueDark;
|
|
||||||
|
|
||||||
var b = new Bindable<RulesetInfo>(); //backup bindable incase the game is null
|
var b = new Bindable<RulesetInfo>(); //backup bindable incase the game is null
|
||||||
foreach (var r in rulesets.AllRulesets)
|
foreach (var r in rulesets.AllRulesets)
|
||||||
@ -124,20 +42,6 @@ namespace osu.Game.Overlays.Direct
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DirectSearchTextBox : SearchTextBox
|
|
||||||
{
|
|
||||||
protected override Color4 BackgroundUnfocused => backgroundColour;
|
|
||||||
protected override Color4 BackgroundFocused => backgroundColour;
|
|
||||||
|
|
||||||
private Color4 backgroundColour;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
|
||||||
backgroundColour = colours.Gray2.Opacity(0.9f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class RulesetToggleButton : ClickableContainer
|
private class RulesetToggleButton : ClickableContainer
|
||||||
{
|
{
|
||||||
private readonly TextAwesome icon;
|
private readonly TextAwesome icon;
|
||||||
@ -188,46 +92,15 @@ namespace osu.Game.Overlays.Direct
|
|||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class DisplayStyleToggleButton : ClickableContainer
|
public enum DirectSortCritera
|
||||||
{
|
{
|
||||||
private readonly TextAwesome icon;
|
Title,
|
||||||
private readonly DirectOverlay.PanelDisplayStyle style;
|
Artist,
|
||||||
private readonly Bindable<DirectOverlay.PanelDisplayStyle> bindable;
|
Creator,
|
||||||
|
Difficulty,
|
||||||
public DisplayStyleToggleButton(FontAwesome icon, DirectOverlay.PanelDisplayStyle style, Bindable<DirectOverlay.PanelDisplayStyle> bindable)
|
Ranked,
|
||||||
{
|
Rating,
|
||||||
this.bindable = bindable;
|
|
||||||
this.style = style;
|
|
||||||
Size = new Vector2(SlimEnumDropdown<DirectTab>.HEIGHT);
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
this.icon = new TextAwesome
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Icon = icon,
|
|
||||||
TextSize = 18,
|
|
||||||
UseFullGlyphHeight = false,
|
|
||||||
Alpha = 0.5f,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
bindable.ValueChanged += Bindable_ValueChanged;
|
|
||||||
Bindable_ValueChanged(bindable.Value);
|
|
||||||
Action = () => bindable.Value = this.style;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Bindable_ValueChanged(DirectOverlay.PanelDisplayStyle style)
|
|
||||||
{
|
|
||||||
icon.FadeTo(style == this.style ? 1.0f : 0.5f, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
|
||||||
{
|
|
||||||
bindable.ValueChanged -= Bindable_ValueChanged;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,113 +2,28 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using OpenTK;
|
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Graphics.UserInterface;
|
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Overlays.SearchableList;
|
||||||
|
|
||||||
using Container = osu.Framework.Graphics.Containers.Container;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Direct
|
namespace osu.Game.Overlays.Direct
|
||||||
{
|
{
|
||||||
public class Header : Container
|
public class Header : SearchableListHeader<DirectTab>
|
||||||
{
|
{
|
||||||
public static readonly float HEIGHT = 90;
|
protected override Color4 BackgroundColour => OsuColour.FromHex(@"252f3a");
|
||||||
|
protected override float TabStripWidth => 298;
|
||||||
|
|
||||||
private readonly Box tabStrip;
|
protected override DirectTab DefaultTab => DirectTab.Search;
|
||||||
|
protected override Drawable CreateHeaderText() => new OsuSpriteText { Text = @"osu!direct", TextSize = 25 };
|
||||||
public readonly OsuTabControl<DirectTab> Tabs;
|
protected override FontAwesome Icon => FontAwesome.fa_osu_chevron_down_o;
|
||||||
|
|
||||||
public Header()
|
public Header()
|
||||||
{
|
{
|
||||||
Height = HEIGHT;
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = OsuColour.FromHex(@"252f3a"),
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Padding = new MarginPadding { Left = DirectOverlay.WIDTH_PADDING, Right = DirectOverlay.WIDTH_PADDING },
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new FillFlowContainer
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Position = new Vector2(-35f, 5f),
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Spacing = new Vector2(10f, 0f),
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new TextAwesome
|
|
||||||
{
|
|
||||||
TextSize = 25,
|
|
||||||
Icon = FontAwesome.fa_osu_chevron_down_o,
|
|
||||||
},
|
|
||||||
new OsuSpriteText
|
|
||||||
{
|
|
||||||
TextSize = 25,
|
|
||||||
Text = @"osu!direct",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
tabStrip = new Box
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Width = 282, //todo: make this actually match the tab control's width instead of hardcoding
|
|
||||||
Height = 1,
|
|
||||||
},
|
|
||||||
Tabs = new DirectTabControl
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Tabs.Current.Value = DirectTab.Search;
|
Tabs.Current.Value = DirectTab.Search;
|
||||||
Tabs.Current.TriggerChange();
|
Tabs.Current.TriggerChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
|
||||||
tabStrip.Colour = colours.Green;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DirectTabControl : OsuTabControl<DirectTab>
|
|
||||||
{
|
|
||||||
protected override TabItem<DirectTab> CreateTabItem(DirectTab value) => new DirectTabItem(value);
|
|
||||||
|
|
||||||
public DirectTabControl()
|
|
||||||
{
|
|
||||||
Height = 25;
|
|
||||||
AccentColour = Color4.White;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DirectTabItem : OsuTabItem
|
|
||||||
{
|
|
||||||
public DirectTabItem(DirectTab value) : base(value)
|
|
||||||
{
|
|
||||||
Text.TextSize = 15;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum DirectTab
|
public enum DirectTab
|
||||||
|
@ -7,28 +7,30 @@ using OpenTK;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Input;
|
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Backgrounds;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Overlays.Direct;
|
using osu.Game.Overlays.Direct;
|
||||||
|
using osu.Game.Overlays.SearchableList;
|
||||||
using Container = osu.Framework.Graphics.Containers.Container;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
public class DirectOverlay : WaveOverlayContainer
|
public class DirectOverlay : SearchableListOverlay<DirectTab, DirectSortCritera, RankStatus>
|
||||||
{
|
{
|
||||||
public static readonly int WIDTH_PADDING = 80;
|
|
||||||
private const float panel_padding = 10f;
|
private const float panel_padding = 10f;
|
||||||
|
|
||||||
private readonly FilterControl filter;
|
|
||||||
private readonly FillFlowContainer resultCountsContainer;
|
private readonly FillFlowContainer resultCountsContainer;
|
||||||
private readonly OsuSpriteText resultCountsText;
|
private readonly OsuSpriteText resultCountsText;
|
||||||
private readonly FillFlowContainer<DirectPanel> panels;
|
private readonly FillFlowContainer<DirectPanel> panels;
|
||||||
|
|
||||||
|
protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74");
|
||||||
|
protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71");
|
||||||
|
protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"3f5265");
|
||||||
|
|
||||||
|
protected override SearchableListHeader<DirectTab> CreateHeader() => new Header();
|
||||||
|
protected override SearchableListFilterControl<DirectSortCritera, RankStatus> CreateFilterControl() => new FilterControl();
|
||||||
|
|
||||||
private IEnumerable<BeatmapSetInfo> beatmapSets;
|
private IEnumerable<BeatmapSetInfo> beatmapSets;
|
||||||
public IEnumerable<BeatmapSetInfo> BeatmapSets
|
public IEnumerable<BeatmapSetInfo> BeatmapSets
|
||||||
{
|
{
|
||||||
@ -38,7 +40,7 @@ namespace osu.Game.Overlays
|
|||||||
if (beatmapSets?.Equals(value) ?? false) return;
|
if (beatmapSets?.Equals(value) ?? false) return;
|
||||||
beatmapSets = value;
|
beatmapSets = value;
|
||||||
|
|
||||||
recreatePanels(filter.DisplayStyle.Value);
|
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,96 +68,39 @@ namespace osu.Game.Overlays
|
|||||||
ThirdWaveColour = OsuColour.FromHex(@"005774");
|
ThirdWaveColour = OsuColour.FromHex(@"005774");
|
||||||
FourthWaveColour = OsuColour.FromHex(@"003a4e");
|
FourthWaveColour = OsuColour.FromHex(@"003a4e");
|
||||||
|
|
||||||
Header header;
|
ScrollFlow.Children = new Drawable[]
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
new Box
|
resultCountsContainer = new FillFlowContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Colour = OsuColour.FromHex(@"485e74"),
|
Direction = FillDirection.Horizontal,
|
||||||
},
|
Margin = new MarginPadding { Top = 5 },
|
||||||
new Container
|
Children = new Drawable[]
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Masking = true,
|
|
||||||
Children = new[]
|
|
||||||
{
|
{
|
||||||
new Triangles
|
new OsuSpriteText
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
Text = "Found ",
|
||||||
TriangleScale = 5,
|
TextSize = 15,
|
||||||
ColourLight = OsuColour.FromHex(@"465b71"),
|
|
||||||
ColourDark = OsuColour.FromHex(@"3f5265"),
|
|
||||||
},
|
},
|
||||||
},
|
resultCountsText = new OsuSpriteText
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Padding = new MarginPadding { Top = Header.HEIGHT + FilterControl.HEIGHT },
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
new ScrollContainer
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
TextSize = 15,
|
||||||
ScrollbarVisible = false,
|
Font = @"Exo2.0-Bold",
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new FillFlowContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
resultCountsContainer = new FillFlowContainer
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Margin = new MarginPadding { Left = WIDTH_PADDING, Top = 6 },
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new OsuSpriteText
|
|
||||||
{
|
|
||||||
Text = "Found ",
|
|
||||||
TextSize = 15,
|
|
||||||
},
|
|
||||||
resultCountsText = new OsuSpriteText
|
|
||||||
{
|
|
||||||
TextSize = 15,
|
|
||||||
Font = @"Exo2.0-Bold",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
panels = new FillFlowContainer<DirectPanel>
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Padding = new MarginPadding { Top = panel_padding, Bottom = panel_padding, Left = WIDTH_PADDING, Right = WIDTH_PADDING },
|
|
||||||
Spacing = new Vector2(panel_padding),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
filter = new FilterControl
|
panels = new FillFlowContainer<DirectPanel>
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Margin = new MarginPadding { Top = Header.HEIGHT },
|
|
||||||
},
|
|
||||||
header = new Header
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Spacing = new Vector2(panel_padding),
|
||||||
|
Margin = new MarginPadding { Top = 10 },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
header.Tabs.Current.ValueChanged += tab => { if (tab != DirectTab.Search) filter.Search.Current.Value = string.Empty; };
|
Header.Tabs.Current.ValueChanged += tab => { if (tab != DirectTab.Search) Filter.Search.Text = string.Empty; };
|
||||||
|
Filter.Search.Current.ValueChanged += text => { if (text != string.Empty) Header.Tabs.Current.Value = DirectTab.Search; };
|
||||||
filter.Search.Exit = Hide;
|
Filter.DisplayStyleControl.DisplayStyle.ValueChanged += recreatePanels;
|
||||||
filter.Search.Current.ValueChanged += text => { if (text != string.Empty) header.Tabs.Current.Value = DirectTab.Search; };
|
|
||||||
filter.DisplayStyle.ValueChanged += recreatePanels;
|
|
||||||
|
|
||||||
updateResultCounts();
|
updateResultCounts();
|
||||||
}
|
}
|
||||||
@ -187,30 +132,6 @@ namespace osu.Game.Overlays
|
|||||||
panels.Children = BeatmapSets.Select(b => displayStyle == PanelDisplayStyle.Grid ? (DirectPanel)new DirectGridPanel(b) { Width = 400 } : new DirectListPanel(b));
|
panels.Children = BeatmapSets.Select(b => displayStyle == PanelDisplayStyle.Grid ? (DirectPanel)new DirectGridPanel(b) { Width = 400 } : new DirectListPanel(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool AcceptsFocus => true;
|
|
||||||
|
|
||||||
protected override bool OnClick(InputState state) => true;
|
|
||||||
|
|
||||||
protected override void OnFocus(InputState state)
|
|
||||||
{
|
|
||||||
InputManager.ChangeFocus(filter.Search);
|
|
||||||
base.OnFocus(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PopIn()
|
|
||||||
{
|
|
||||||
base.PopIn();
|
|
||||||
|
|
||||||
filter.Search.HoldFocus = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PopOut()
|
|
||||||
{
|
|
||||||
base.PopOut();
|
|
||||||
|
|
||||||
filter.Search.HoldFocus = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ResultCounts
|
public class ResultCounts
|
||||||
{
|
{
|
||||||
public readonly int Artists;
|
public readonly int Artists;
|
||||||
@ -224,11 +145,5 @@ namespace osu.Game.Overlays
|
|||||||
Tags = tags;
|
Tags = tags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PanelDisplayStyle
|
|
||||||
{
|
|
||||||
Grid,
|
|
||||||
List,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ using osu.Framework.Graphics.Cursor;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.Mods
|
namespace osu.Game.Overlays.Mods
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a clickable button which can cycle through one of more mods.
|
/// Represents a clickable button which can cycle through one of more mods.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -80,7 +79,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing);
|
backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing);
|
||||||
|
|
||||||
backgroundIcon.Icon = modAfter.Icon;
|
backgroundIcon.Icon = modAfter.Icon;
|
||||||
using (iconsContainer.BeginDelayedSequence(mod_switch_duration, true))
|
using (BeginDelayedSequence(mod_switch_duration, true))
|
||||||
{
|
{
|
||||||
foregroundIcon.RotateTo(-rotate_angle * direction);
|
foregroundIcon.RotateTo(-rotate_angle * direction);
|
||||||
foregroundIcon.RotateTo(0f, mod_switch_duration, mod_switch_easing);
|
foregroundIcon.RotateTo(0f, mod_switch_duration, mod_switch_easing);
|
||||||
@ -88,7 +87,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
backgroundIcon.RotateTo(rotate_angle * direction);
|
backgroundIcon.RotateTo(rotate_angle * direction);
|
||||||
backgroundIcon.RotateTo(0f, mod_switch_duration, mod_switch_easing);
|
backgroundIcon.RotateTo(0f, mod_switch_duration, mod_switch_easing);
|
||||||
|
|
||||||
iconsContainer.Schedule(() => displayMod(modAfter));
|
Schedule(() => displayMod(modAfter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Music
|
|||||||
private Color4 artistColour;
|
private Color4 artistColour;
|
||||||
|
|
||||||
private TextAwesome handle;
|
private TextAwesome handle;
|
||||||
private Paragraph text;
|
private TextFlowContainer text;
|
||||||
private IEnumerable<SpriteText> titleSprites;
|
private IEnumerable<SpriteText> titleSprites;
|
||||||
private UnicodeBindableString titleBind;
|
private UnicodeBindableString titleBind;
|
||||||
private UnicodeBindableString artistBind;
|
private UnicodeBindableString artistBind;
|
||||||
@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Music
|
|||||||
Margin = new MarginPadding { Left = 5 },
|
Margin = new MarginPadding { Left = 5 },
|
||||||
Padding = new MarginPadding { Top = 2 },
|
Padding = new MarginPadding { Top = 2 },
|
||||||
},
|
},
|
||||||
text = new Paragraph
|
text = new TextFlowContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
@ -137,6 +137,7 @@ namespace osu.Game.Overlays.Music
|
|||||||
|
|
||||||
public bool MatchingFilter
|
public bool MatchingFilter
|
||||||
{
|
{
|
||||||
|
get { return matching; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (matching == value) return;
|
if (matching == value) return;
|
||||||
@ -145,10 +146,6 @@ namespace osu.Game.Overlays.Music
|
|||||||
|
|
||||||
FadeTo(matching ? 1 : 0, 200);
|
FadeTo(matching ? 1 : 0, 200);
|
||||||
}
|
}
|
||||||
get
|
|
||||||
{
|
|
||||||
return matching;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,19 +46,19 @@ namespace osu.Game.Overlays
|
|||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Children = new []
|
Children = new[]
|
||||||
{
|
{
|
||||||
new NotificationSection
|
new NotificationSection
|
||||||
{
|
{
|
||||||
Title = @"Notifications",
|
Title = @"Notifications",
|
||||||
ClearText = @"Clear All",
|
ClearText = @"Clear All",
|
||||||
AcceptTypes = new [] { typeof(SimpleNotification) },
|
AcceptTypes = new[] { typeof(SimpleNotification) },
|
||||||
},
|
},
|
||||||
new NotificationSection
|
new NotificationSection
|
||||||
{
|
{
|
||||||
Title = @"Running Tasks",
|
Title = @"Running Tasks",
|
||||||
ClearText = @"Cancel All",
|
ClearText = @"Cancel All",
|
||||||
AcceptTypes = new [] { typeof(ProgressNotification) },
|
AcceptTypes = new[] { typeof(ProgressNotification) },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
102
osu.Game/Overlays/SearchableList/DisplayStyleControl.cs
Normal file
102
osu.Game/Overlays/SearchableList/DisplayStyleControl.cs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// 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.Configuration;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.SearchableList
|
||||||
|
{
|
||||||
|
public class DisplayStyleControl<T> : Container
|
||||||
|
{
|
||||||
|
public readonly SlimEnumDropdown<T> Dropdown;
|
||||||
|
public readonly Bindable<PanelDisplayStyle> DisplayStyle = new Bindable<PanelDisplayStyle>();
|
||||||
|
|
||||||
|
public DisplayStyleControl()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
Spacing = new Vector2(10f, 0f),
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Spacing = new Vector2(5f, 0f),
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new DisplayStyleToggleButton(FontAwesome.fa_th_large, PanelDisplayStyle.Grid, DisplayStyle),
|
||||||
|
new DisplayStyleToggleButton(FontAwesome.fa_list_ul, PanelDisplayStyle.List, DisplayStyle),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Dropdown = new SlimEnumDropdown<T>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.None,
|
||||||
|
Width = 160f,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
DisplayStyle.Value = PanelDisplayStyle.Grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DisplayStyleToggleButton : ClickableContainer
|
||||||
|
{
|
||||||
|
private readonly TextAwesome icon;
|
||||||
|
private readonly PanelDisplayStyle style;
|
||||||
|
private readonly Bindable<PanelDisplayStyle> bindable;
|
||||||
|
|
||||||
|
public DisplayStyleToggleButton(FontAwesome icon, PanelDisplayStyle style, Bindable<PanelDisplayStyle> bindable)
|
||||||
|
{
|
||||||
|
this.bindable = bindable;
|
||||||
|
this.style = style;
|
||||||
|
Size = new Vector2(25f);
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
this.icon = new TextAwesome
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Icon = icon,
|
||||||
|
TextSize = 18,
|
||||||
|
UseFullGlyphHeight = false,
|
||||||
|
Alpha = 0.5f,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
bindable.ValueChanged += Bindable_ValueChanged;
|
||||||
|
Bindable_ValueChanged(bindable.Value);
|
||||||
|
Action = () => bindable.Value = this.style;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Bindable_ValueChanged(PanelDisplayStyle style)
|
||||||
|
{
|
||||||
|
icon.FadeTo(style == this.style ? 1.0f : 0.5f, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
bindable.ValueChanged -= Bindable_ValueChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PanelDisplayStyle
|
||||||
|
{
|
||||||
|
Grid,
|
||||||
|
List,
|
||||||
|
}
|
||||||
|
}
|
28
osu.Game/Overlays/SearchableList/HeaderTabControl.cs
Normal file
28
osu.Game/Overlays/SearchableList/HeaderTabControl.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.SearchableList
|
||||||
|
{
|
||||||
|
public class HeaderTabControl<T> : OsuTabControl<T>
|
||||||
|
{
|
||||||
|
protected override TabItem<T> CreateTabItem(T value) => new HeaderTabItem(value);
|
||||||
|
|
||||||
|
public HeaderTabControl()
|
||||||
|
{
|
||||||
|
Height = 26;
|
||||||
|
AccentColour = Color4.White;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class HeaderTabItem : OsuTabItem
|
||||||
|
{
|
||||||
|
public HeaderTabItem(T value) : base(value)
|
||||||
|
{
|
||||||
|
Text.TextSize = 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
136
osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs
Normal file
136
osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
// 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 OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.SearchableList
|
||||||
|
{
|
||||||
|
public abstract class SearchableListFilterControl<T, U> : Container
|
||||||
|
{
|
||||||
|
private const float padding = 10;
|
||||||
|
|
||||||
|
private readonly Container filterContainer;
|
||||||
|
private readonly Box tabStrip;
|
||||||
|
|
||||||
|
public readonly SearchTextBox Search;
|
||||||
|
public readonly PageTabControl<T> Tabs;
|
||||||
|
public readonly DisplayStyleControl<U> DisplayStyleControl;
|
||||||
|
|
||||||
|
protected abstract Color4 BackgroundColour { get; }
|
||||||
|
protected abstract T DefaultTab { get; }
|
||||||
|
protected virtual Drawable CreateSupplementaryControls() => null;
|
||||||
|
|
||||||
|
protected override bool InternalContains(Vector2 screenSpacePos) => base.InternalContains(screenSpacePos) || DisplayStyleControl.Dropdown.Contains(screenSpacePos);
|
||||||
|
|
||||||
|
protected SearchableListFilterControl()
|
||||||
|
{
|
||||||
|
if (!typeof(T).IsEnum)
|
||||||
|
throw new InvalidOperationException("SearchableListFilterControl's sort tabs only support enums as the generic type argument");
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
|
var controls = CreateSupplementaryControls();
|
||||||
|
Container controlsContainer;
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
filterContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = BackgroundColour,
|
||||||
|
Alpha = 0.9f,
|
||||||
|
},
|
||||||
|
tabStrip = new Box
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = 1,
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Padding = new MarginPadding { Top = padding, Horizontal = SearchableListOverlay.WIDTH_PADDING },
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
Search = new FilterSearchTextBox
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
},
|
||||||
|
controlsContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Margin = new MarginPadding { Top = controls != null ? padding : 0 },
|
||||||
|
},
|
||||||
|
Tabs = new PageTabControl<T>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
},
|
||||||
|
new Box //keep the tab strip part of autosize, but don't put it in the flow container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = 1,
|
||||||
|
Colour = Color4.White.Opacity(0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DisplayStyleControl = new DisplayStyleControl<U>
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (controls != null) controlsContainer.Children = new[] { controls };
|
||||||
|
|
||||||
|
Tabs.Current.Value = DefaultTab;
|
||||||
|
Tabs.Current.TriggerChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
tabStrip.Colour = colours.Yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
Height = filterContainer.Height;
|
||||||
|
DisplayStyleControl.Margin = new MarginPadding { Top = filterContainer.Height - 35, Right = SearchableListOverlay.WIDTH_PADDING };
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FilterSearchTextBox : SearchTextBox
|
||||||
|
{
|
||||||
|
protected override Color4 BackgroundUnfocused => backgroundColour;
|
||||||
|
protected override Color4 BackgroundFocused => backgroundColour;
|
||||||
|
|
||||||
|
private Color4 backgroundColour;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
backgroundColour = colours.Gray2.Opacity(0.9f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
93
osu.Game/Overlays/SearchableList/SearchableListHeader.cs
Normal file
93
osu.Game/Overlays/SearchableList/SearchableListHeader.cs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// 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 OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.SearchableList
|
||||||
|
{
|
||||||
|
public abstract class SearchableListHeader<T> : Container
|
||||||
|
{
|
||||||
|
private readonly Box tabStrip;
|
||||||
|
|
||||||
|
public readonly HeaderTabControl<T> Tabs;
|
||||||
|
|
||||||
|
protected abstract Color4 BackgroundColour { get; }
|
||||||
|
protected abstract float TabStripWidth { get; } //can be removed once (if?) TabControl support auto sizing
|
||||||
|
protected abstract T DefaultTab { get; }
|
||||||
|
protected abstract Drawable CreateHeaderText();
|
||||||
|
protected abstract FontAwesome Icon { get; }
|
||||||
|
|
||||||
|
protected SearchableListHeader()
|
||||||
|
{
|
||||||
|
if (!typeof(T).IsEnum)
|
||||||
|
throw new InvalidOperationException("BrowseHeader only supports enums as the generic type argument");
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
Height = 90;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = BackgroundColour,
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Left = SearchableListOverlay.WIDTH_PADDING, Right = SearchableListOverlay.WIDTH_PADDING },
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Position = new Vector2(-35f, 5f),
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(10f, 0f),
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new TextAwesome
|
||||||
|
{
|
||||||
|
TextSize = 25,
|
||||||
|
Icon = Icon,
|
||||||
|
},
|
||||||
|
CreateHeaderText(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tabStrip = new Box
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Width = TabStripWidth,
|
||||||
|
Height = 1,
|
||||||
|
},
|
||||||
|
Tabs = new HeaderTabControl<T>
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Tabs.Current.Value = DefaultTab;
|
||||||
|
Tabs.Current.TriggerChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
tabStrip.Colour = colours.Green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
123
osu.Game/Overlays/SearchableList/SearchableListOverlay.cs
Normal file
123
osu.Game/Overlays/SearchableList/SearchableListOverlay.cs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Graphics.Backgrounds;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.SearchableList
|
||||||
|
{
|
||||||
|
public abstract class SearchableListOverlay : WaveOverlayContainer
|
||||||
|
{
|
||||||
|
public static readonly float WIDTH_PADDING = 80;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class SearchableListOverlay<T, U, S> : SearchableListOverlay
|
||||||
|
{
|
||||||
|
private readonly Container scrollContainer;
|
||||||
|
|
||||||
|
protected readonly SearchableListHeader<T> Header;
|
||||||
|
protected readonly SearchableListFilterControl<U, S> Filter;
|
||||||
|
protected readonly FillFlowContainer ScrollFlow;
|
||||||
|
|
||||||
|
protected abstract Color4 BackgroundColour { get; }
|
||||||
|
protected abstract Color4 TrianglesColourLight { get; }
|
||||||
|
protected abstract Color4 TrianglesColourDark { get; }
|
||||||
|
protected abstract SearchableListHeader<T> CreateHeader();
|
||||||
|
protected abstract SearchableListFilterControl<U, S> CreateFilterControl();
|
||||||
|
|
||||||
|
protected SearchableListOverlay()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = BackgroundColour,
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new Triangles
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
TriangleScale = 5,
|
||||||
|
ColourLight = TrianglesColourLight,
|
||||||
|
ColourDark = TrianglesColourDark,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scrollContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new ScrollContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
ScrollbarVisible = false,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
ScrollFlow = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Padding = new MarginPadding { Horizontal = WIDTH_PADDING, Bottom = 50 },
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
AlwaysReceiveInput = true,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
Header = CreateHeader(),
|
||||||
|
Filter = CreateFilterControl(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Filter.Search.Exit = Hide;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
scrollContainer.Padding = new MarginPadding { Top = Header.Height + Filter.Height };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFocus(InputState state)
|
||||||
|
{
|
||||||
|
InputManager.ChangeFocus(Filter.Search);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopIn()
|
||||||
|
{
|
||||||
|
base.PopIn();
|
||||||
|
|
||||||
|
Filter.Search.HoldFocus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopOut()
|
||||||
|
{
|
||||||
|
base.PopOut();
|
||||||
|
|
||||||
|
Filter.Search.HoldFocus = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,12 +7,10 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Direct
|
namespace osu.Game.Overlays.SearchableList
|
||||||
{
|
{
|
||||||
public class SlimEnumDropdown<T> : OsuEnumDropdown<T>
|
public class SlimEnumDropdown<T> : OsuEnumDropdown<T>
|
||||||
{
|
{
|
||||||
public const float HEIGHT = 25;
|
|
||||||
|
|
||||||
protected override DropdownHeader CreateHeader() => new SlimDropdownHeader { AccentColour = AccentColour };
|
protected override DropdownHeader CreateHeader() => new SlimDropdownHeader { AccentColour = AccentColour };
|
||||||
protected override Menu CreateMenu() => new SlimMenu();
|
protected override Menu CreateMenu() => new SlimMenu();
|
||||||
|
|
||||||
@ -20,7 +18,7 @@ namespace osu.Game.Overlays.Direct
|
|||||||
{
|
{
|
||||||
public SlimDropdownHeader()
|
public SlimDropdownHeader()
|
||||||
{
|
{
|
||||||
Height = HEIGHT;
|
Height = 25;
|
||||||
Icon.TextSize = 16;
|
Icon.TextSize = 16;
|
||||||
Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 8, Right = 4 };
|
Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 8, Right = 4 };
|
||||||
}
|
}
|
@ -41,4 +41,3 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
bounding = value;
|
bounding = value;
|
||||||
Invalidate(Invalidation.Geometry);
|
Invalidate(Invalidation.MiscGeometry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,4 +33,3 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,4 +22,3 @@ namespace osu.Game.Overlays.Settings.Sections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
|||||||
letterboxSettings.ClearTransforms();
|
letterboxSettings.ClearTransforms();
|
||||||
letterboxSettings.AutoSizeAxes = isVisible ? Axes.Y : Axes.None;
|
letterboxSettings.AutoSizeAxes = isVisible ? Axes.Y : Axes.None;
|
||||||
|
|
||||||
if(!isVisible)
|
if (!isVisible)
|
||||||
letterboxSettings.ResizeHeightTo(0, transition_duration, EasingTypes.OutQuint);
|
letterboxSettings.ResizeHeightTo(0, transition_duration, EasingTypes.OutQuint);
|
||||||
};
|
};
|
||||||
letterboxing.TriggerChange();
|
letterboxing.TriggerChange();
|
||||||
|
@ -24,4 +24,3 @@ namespace osu.Game.Overlays.Settings.Sections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,4 +22,3 @@ namespace osu.Game.Overlays.Settings.Sections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,4 +52,3 @@ namespace osu.Game.Overlays.Settings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Settings
|
|||||||
},
|
},
|
||||||
new SidebarScrollContainer
|
new SidebarScrollContainer
|
||||||
{
|
{
|
||||||
Children = new []
|
Children = new[]
|
||||||
{
|
{
|
||||||
content = new FillFlowContainer
|
content = new FillFlowContainer
|
||||||
{
|
{
|
||||||
|
31
osu.Game/Overlays/Social/FilterControl.cs
Normal file
31
osu.Game/Overlays/Social/FilterControl.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Overlays.SearchableList;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Social
|
||||||
|
{
|
||||||
|
public class FilterControl : SearchableListFilterControl<SocialSortCriteria, SortDirection>
|
||||||
|
{
|
||||||
|
protected override Color4 BackgroundColour => OsuColour.FromHex(@"47253a");
|
||||||
|
protected override SocialSortCriteria DefaultTab => SocialSortCriteria.Rank;
|
||||||
|
|
||||||
|
public FilterControl()
|
||||||
|
{
|
||||||
|
Tabs.Margin = new MarginPadding { Top = 10 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SocialSortCriteria
|
||||||
|
{
|
||||||
|
Rank,
|
||||||
|
//Location,
|
||||||
|
//[Description("Time Zone")]
|
||||||
|
//TimeZone,
|
||||||
|
//[Description("World Map")]
|
||||||
|
//WorldMap,
|
||||||
|
}
|
||||||
|
}
|
65
osu.Game/Overlays/Social/Header.cs
Normal file
65
osu.Game/Overlays/Social/Header.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// 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.Overlays.SearchableList;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Social
|
||||||
|
{
|
||||||
|
public class Header : SearchableListHeader<SocialTab>
|
||||||
|
{
|
||||||
|
private OsuSpriteText browser;
|
||||||
|
|
||||||
|
protected override Color4 BackgroundColour => OsuColour.FromHex(@"38202e");
|
||||||
|
protected override float TabStripWidth => 438;
|
||||||
|
protected override SocialTab DefaultTab => SocialTab.OnlinePlayers;
|
||||||
|
protected override FontAwesome Icon => FontAwesome.fa_users;
|
||||||
|
|
||||||
|
protected override Drawable CreateHeaderText()
|
||||||
|
{
|
||||||
|
return new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = "social ",
|
||||||
|
TextSize = 25,
|
||||||
|
},
|
||||||
|
browser = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = "browser",
|
||||||
|
TextSize = 25,
|
||||||
|
Font = @"Exo2.0-Light",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
browser.Colour = colours.Pink;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SocialTab
|
||||||
|
{
|
||||||
|
[Description("Online Players")]
|
||||||
|
OnlinePlayers,
|
||||||
|
//[Description("Online Friends")]
|
||||||
|
//OnlineFriends,
|
||||||
|
//[Description("Online Team Members")]
|
||||||
|
//OnlineTeamMembers,
|
||||||
|
//[Description("Chat Channels")]
|
||||||
|
//ChatChannels,
|
||||||
|
}
|
||||||
|
}
|
109
osu.Game/Overlays/SocialOverlay.cs
Normal file
109
osu.Game/Overlays/SocialOverlay.cs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Overlays.SearchableList;
|
||||||
|
using osu.Game.Overlays.Social;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays
|
||||||
|
{
|
||||||
|
public class SocialOverlay : SearchableListOverlay<SocialTab, SocialSortCriteria, SortDirection>, IOnlineComponent
|
||||||
|
{
|
||||||
|
private readonly FillFlowContainer<UserPanel> panelFlow;
|
||||||
|
|
||||||
|
protected override Color4 BackgroundColour => OsuColour.FromHex(@"60284b");
|
||||||
|
protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"672b51");
|
||||||
|
protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"5c2648");
|
||||||
|
|
||||||
|
protected override SearchableListHeader<SocialTab> CreateHeader() => new Header();
|
||||||
|
protected override SearchableListFilterControl<SocialSortCriteria, SortDirection> CreateFilterControl() => new FilterControl();
|
||||||
|
|
||||||
|
private IEnumerable<User> users;
|
||||||
|
public IEnumerable<User> Users
|
||||||
|
{
|
||||||
|
get { return users; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (users?.Equals(value) ?? false) return;
|
||||||
|
users = value;
|
||||||
|
|
||||||
|
if (users == null)
|
||||||
|
panelFlow.Clear();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
panelFlow.Children = users.Select(u =>
|
||||||
|
{
|
||||||
|
var p = new UserPanel(u) { Width = 300 };
|
||||||
|
p.Status.BindTo(u.Status);
|
||||||
|
return p;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SocialOverlay()
|
||||||
|
{
|
||||||
|
FirstWaveColour = OsuColour.FromHex(@"cb5fa0");
|
||||||
|
SecondWaveColour = OsuColour.FromHex(@"b04384");
|
||||||
|
ThirdWaveColour = OsuColour.FromHex(@"9b2b6e");
|
||||||
|
FourthWaveColour = OsuColour.FromHex(@"6d214d");
|
||||||
|
|
||||||
|
ScrollFlow.Children = new[]
|
||||||
|
{
|
||||||
|
panelFlow = new FillFlowContainer<UserPanel>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Margin = new MarginPadding { Top = 20 },
|
||||||
|
Spacing = new Vector2(10f),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(APIAccess api)
|
||||||
|
{
|
||||||
|
if (Users == null)
|
||||||
|
reloadUsers(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reloadUsers(APIAccess api)
|
||||||
|
{
|
||||||
|
Users = null;
|
||||||
|
|
||||||
|
// no this is not the correct data source, but it's something.
|
||||||
|
var request = new GetUsersRequest();
|
||||||
|
request.Success += res => Users = res.Select(e => e.User);
|
||||||
|
api.Queue(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void APIStateChanged(APIAccess api, APIState state)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case APIState.Online:
|
||||||
|
reloadUsers(api);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Users = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SortDirection
|
||||||
|
{
|
||||||
|
Descending,
|
||||||
|
Ascending,
|
||||||
|
}
|
||||||
|
}
|
@ -63,6 +63,7 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
AutoSizeAxes = Axes.X,
|
AutoSizeAxes = Axes.X,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
new ToolbarSocialButton(),
|
||||||
new ToolbarChatButton(),
|
new ToolbarChatButton(),
|
||||||
new ToolbarMusicButton(),
|
new ToolbarMusicButton(),
|
||||||
new ToolbarButton
|
new ToolbarButton
|
||||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
Radius = 15,
|
Radius = 15,
|
||||||
Roundness = 15,
|
Roundness = 15,
|
||||||
},
|
},
|
||||||
Children = new []
|
Children = new[]
|
||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
|
22
osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs
Normal file
22
osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// 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.Game.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Toolbar
|
||||||
|
{
|
||||||
|
internal class ToolbarSocialButton : ToolbarOverlayToggleButton
|
||||||
|
{
|
||||||
|
public ToolbarSocialButton()
|
||||||
|
{
|
||||||
|
Icon = FontAwesome.fa_users;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(SocialOverlay chat)
|
||||||
|
{
|
||||||
|
StateContainer = chat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -25,13 +25,13 @@ namespace osu.Game.Screens.Edit
|
|||||||
protected override void OnEntering(Screen last)
|
protected override void OnEntering(Screen last)
|
||||||
{
|
{
|
||||||
base.OnEntering(last);
|
base.OnEntering(last);
|
||||||
Background.Schedule(() => Background.FadeColour(Color4.DarkGray, 500));
|
Background.FadeColour(Color4.DarkGray, 500);
|
||||||
Beatmap?.Track?.Stop();
|
Beatmap?.Track?.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnExiting(Screen next)
|
protected override bool OnExiting(Screen next)
|
||||||
{
|
{
|
||||||
Background.Schedule(() => Background.FadeColour(Color4.White, 500));
|
Background.FadeColour(Color4.White, 500);
|
||||||
Beatmap?.Track?.Start();
|
Beatmap?.Track?.Start();
|
||||||
return base.OnExiting(next);
|
return base.OnExiting(next);
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
Scale = new Vector2(0, 1),
|
Scale = new Vector2(0, 1),
|
||||||
Size = boxSize,
|
Size = boxSize,
|
||||||
Shear = new Vector2(ButtonSystem.WEDGE_WIDTH / boxSize.Y, 0),
|
Shear = new Vector2(ButtonSystem.WEDGE_WIDTH / boxSize.Y, 0),
|
||||||
Children = new []
|
Children = new[]
|
||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
@ -283,9 +283,9 @@ namespace osu.Game.Screens.Menu
|
|||||||
public ButtonState State
|
public ButtonState State
|
||||||
{
|
{
|
||||||
get { return state; }
|
get { return state; }
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
|
||||||
if (state == value)
|
if (state == value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -192,10 +192,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
public MenuState State
|
public MenuState State
|
||||||
{
|
{
|
||||||
get
|
get { return state; }
|
||||||
{
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (state == value) return;
|
if (state == value) return;
|
||||||
|
@ -51,10 +51,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
public bool Triangles
|
public bool Triangles
|
||||||
{
|
{
|
||||||
set
|
set { colourAndTriangles.Alpha = value ? 1 : 0; }
|
||||||
{
|
|
||||||
colourAndTriangles.Alpha = value ? 1 : 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool InternalContains(Vector2 screenSpacePos) => logoContainer.Contains(screenSpacePos);
|
protected override bool InternalContains(Vector2 screenSpacePos) => logoContainer.Contains(screenSpacePos);
|
||||||
@ -62,10 +59,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
public bool Ripple
|
public bool Ripple
|
||||||
{
|
{
|
||||||
get { return rippleContainer.Alpha > 0; }
|
get { return rippleContainer.Alpha > 0; }
|
||||||
set
|
set { rippleContainer.Alpha = value ? 1 : 0; }
|
||||||
{
|
|
||||||
rippleContainer.Alpha = value ? 1 : 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Interactive = true;
|
public bool Interactive = true;
|
||||||
|
@ -68,7 +68,7 @@ namespace osu.Game.Screens.Multiplayer
|
|||||||
},
|
},
|
||||||
avatar = new UpdateableAvatar
|
avatar = new UpdateableAvatar
|
||||||
{
|
{
|
||||||
Size = new Vector2(Height - content_padding* 2),
|
Size = new Vector2(Height - content_padding * 2),
|
||||||
Masking = true,
|
Masking = true,
|
||||||
CornerRadius = 5f,
|
CornerRadius = 5f,
|
||||||
Margin = new MarginPadding { Left = content_padding * 2, Top = content_padding },
|
Margin = new MarginPadding { Left = content_padding * 2, Top = content_padding },
|
||||||
|
@ -24,12 +24,12 @@ namespace osu.Game.Screens.Multiplayer
|
|||||||
{
|
{
|
||||||
base.OnEntering(last);
|
base.OnEntering(last);
|
||||||
|
|
||||||
Background.Schedule(() => Background.FadeColour(Color4.DarkGray, 500));
|
Background.FadeColour(Color4.DarkGray, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnExiting(Screen next)
|
protected override bool OnExiting(Screen next)
|
||||||
{
|
{
|
||||||
Background.Schedule(() => Background.FadeColour(Color4.White, 500));
|
Background.FadeColour(Color4.White, 500);
|
||||||
return base.OnExiting(next);
|
return base.OnExiting(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,6 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Scale = new Vector2(0.6f),
|
Scale = new Vector2(0.6f),
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,21 +7,24 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Screens.Play.HUD;
|
using osu.Game.Screens.Play.HUD;
|
||||||
|
using OpenTK;
|
||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
{
|
{
|
||||||
public abstract class HUDOverlay : Container
|
public class HUDOverlay : Container
|
||||||
{
|
{
|
||||||
private const int duration = 100;
|
private const int duration = 100;
|
||||||
|
|
||||||
private readonly Container content;
|
private readonly Container content;
|
||||||
|
|
||||||
public readonly KeyCounterCollection KeyCounter;
|
public readonly KeyCounterCollection KeyCounter;
|
||||||
public readonly RollingCounter<int> ComboCounter;
|
public readonly RollingCounter<int> ComboCounter;
|
||||||
public readonly ScoreCounter ScoreCounter;
|
public readonly ScoreCounter ScoreCounter;
|
||||||
@ -35,16 +38,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private static bool hasShownNotificationOnce;
|
private static bool hasShownNotificationOnce;
|
||||||
|
|
||||||
protected abstract KeyCounterCollection CreateKeyCounter();
|
public HUDOverlay()
|
||||||
protected abstract RollingCounter<int> CreateComboCounter();
|
|
||||||
protected abstract RollingCounter<double> CreateAccuracyCounter();
|
|
||||||
protected abstract ScoreCounter CreateScoreCounter();
|
|
||||||
protected abstract HealthDisplay CreateHealthDisplay();
|
|
||||||
protected abstract SongProgress CreateProgress();
|
|
||||||
protected abstract ModDisplay CreateModsContainer();
|
|
||||||
//protected abstract ReplaySettingsOverlay CreateReplaySettingsOverlay();
|
|
||||||
|
|
||||||
protected HUDOverlay()
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
@ -67,7 +61,7 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(OsuConfigManager config, NotificationManager notificationManager)
|
private void load(OsuConfigManager config, NotificationManager notificationManager, OsuColour colours)
|
||||||
{
|
{
|
||||||
showHud = config.GetBindable<bool>(OsuSetting.ShowInterface);
|
showHud = config.GetBindable<bool>(OsuSetting.ShowInterface);
|
||||||
showHud.ValueChanged += hudVisibility => content.FadeTo(hudVisibility ? 1 : 0, duration);
|
showHud.ValueChanged += hudVisibility => content.FadeTo(hudVisibility ? 1 : 0, duration);
|
||||||
@ -82,14 +76,18 @@ namespace osu.Game.Screens.Play
|
|||||||
Text = @"The score overlay is currently disabled. You can toggle this by pressing Shift+Tab."
|
Text = @"The score overlay is currently disabled. You can toggle this by pressing Shift+Tab."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void BindProcessor(ScoreProcessor processor)
|
// todo: the stuff below should probably not be in this base implementation, but in each individual class.
|
||||||
{
|
ComboCounter.AccentColour = colours.BlueLighter;
|
||||||
ScoreCounter?.Current.BindTo(processor.TotalScore);
|
AccuracyCounter.AccentColour = colours.BlueLighter;
|
||||||
AccuracyCounter?.Current.BindTo(processor.Accuracy);
|
ScoreCounter.AccentColour = colours.BlueLighter;
|
||||||
ComboCounter?.Current.BindTo(processor.Combo);
|
|
||||||
HealthDisplay?.Current.BindTo(processor.Health);
|
var shd = HealthDisplay as StandardHealthDisplay;
|
||||||
|
if (shd != null)
|
||||||
|
{
|
||||||
|
shd.AccentColour = colours.BlueLighter;
|
||||||
|
shd.GlowColour = colours.BlueDarker;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void BindHitRenderer(HitRenderer hitRenderer)
|
public virtual void BindHitRenderer(HitRenderer hitRenderer)
|
||||||
@ -122,5 +120,82 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
return base.OnKeyDown(state, args);
|
return base.OnKeyDown(state, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual RollingCounter<double> CreateAccuracyCounter() => new PercentageCounter
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
Position = new Vector2(0, 35),
|
||||||
|
TextSize = 20,
|
||||||
|
Margin = new MarginPadding { Right = 140 },
|
||||||
|
};
|
||||||
|
|
||||||
|
protected virtual RollingCounter<int> CreateComboCounter() => new SimpleComboCounter
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopLeft,
|
||||||
|
Position = new Vector2(0, 35),
|
||||||
|
Margin = new MarginPadding { Left = 140 },
|
||||||
|
TextSize = 20,
|
||||||
|
};
|
||||||
|
|
||||||
|
protected virtual HealthDisplay CreateHealthDisplay() => new StandardHealthDisplay
|
||||||
|
{
|
||||||
|
Size = new Vector2(1, 5),
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Margin = new MarginPadding { Top = 20 }
|
||||||
|
};
|
||||||
|
|
||||||
|
protected virtual KeyCounterCollection CreateKeyCounter() => new KeyCounterCollection
|
||||||
|
{
|
||||||
|
IsCounting = true,
|
||||||
|
FadeTime = 50,
|
||||||
|
Anchor = Anchor.BottomRight,
|
||||||
|
Origin = Anchor.BottomRight,
|
||||||
|
Margin = new MarginPadding(10),
|
||||||
|
Y = -TwoLayerButton.SIZE_RETRACTED.Y,
|
||||||
|
};
|
||||||
|
|
||||||
|
protected virtual ScoreCounter CreateScoreCounter() => new ScoreCounter(6)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
TextSize = 40,
|
||||||
|
Position = new Vector2(0, 30),
|
||||||
|
};
|
||||||
|
|
||||||
|
protected virtual SongProgress CreateProgress() => new SongProgress
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
};
|
||||||
|
|
||||||
|
protected virtual ModDisplay CreateModsContainer() => new ModDisplay
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Margin = new MarginPadding { Top = 20, Right = 10 },
|
||||||
|
};
|
||||||
|
|
||||||
|
//protected virtual ReplaySettingsOverlay CreateReplaySettingsOverlay() => new ReplaySettingsOverlay
|
||||||
|
//{
|
||||||
|
// Anchor = Anchor.TopRight,
|
||||||
|
// Origin = Anchor.TopRight,
|
||||||
|
// Margin = new MarginPadding { Top = 100, Right = 10 },
|
||||||
|
//};
|
||||||
|
|
||||||
|
public virtual void BindProcessor(ScoreProcessor processor)
|
||||||
|
{
|
||||||
|
ScoreCounter?.Current.BindTo(processor.TotalScore);
|
||||||
|
AccuracyCounter?.Current.BindTo(processor.Accuracy);
|
||||||
|
ComboCounter?.Current.BindTo(processor.Combo);
|
||||||
|
HealthDisplay?.Current.BindTo(processor.Health);
|
||||||
|
|
||||||
|
var shd = HealthDisplay as StandardHealthDisplay;
|
||||||
|
if (shd != null)
|
||||||
|
processor.NewJudgement += shd.Flash;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,7 +170,7 @@ namespace osu.Game.Screens.Play
|
|||||||
HitRenderer,
|
HitRenderer,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hudOverlay = new StandardHUDOverlay
|
hudOverlay = new HUDOverlay
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre
|
Origin = Anchor.Centre
|
||||||
@ -278,7 +278,6 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
if (!pauseContainer.IsPaused)
|
if (!pauseContainer.IsPaused)
|
||||||
decoupledClock.Start();
|
decoupledClock.Start();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
pauseContainer.Alpha = 0;
|
pauseContainer.Alpha = 0;
|
||||||
|
@ -79,7 +79,7 @@ namespace osu.Game.Screens.Play.ReplaySettings
|
|||||||
{
|
{
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
Position = new Vector2(-15,0),
|
Position = new Vector2(-15, 0),
|
||||||
Icon = FontAwesome.fa_bars,
|
Icon = FontAwesome.fa_bars,
|
||||||
Scale = new Vector2(0.75f),
|
Scale = new Vector2(0.75f),
|
||||||
Action = toggleContentVisibility,
|
Action = toggleContentVisibility,
|
||||||
|
@ -232,7 +232,7 @@ namespace osu.Game.Screens.Play
|
|||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Children = new []
|
Children = new[]
|
||||||
{
|
{
|
||||||
new TextAwesome { Icon = FontAwesome.fa_chevron_right },
|
new TextAwesome { Icon = FontAwesome.fa_chevron_right },
|
||||||
new TextAwesome { Icon = FontAwesome.fa_chevron_right },
|
new TextAwesome { Icon = FontAwesome.fa_chevron_right },
|
||||||
|
@ -146,7 +146,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
double progress = ((audioClock?.CurrentTime ?? Time.Current) - firstHitTime) / (lastHitTime - firstHitTime);
|
double progress = ((audioClock?.CurrentTime ?? Time.Current) - firstHitTime) / (lastHitTime - firstHitTime);
|
||||||
|
|
||||||
if(progress < 1)
|
if (progress < 1)
|
||||||
{
|
{
|
||||||
bar.UpdatePosition((float)progress);
|
bar.UpdatePosition((float)progress);
|
||||||
graph.Progress = (int)(graph.ColumnCount * progress);
|
graph.Progress = (int)(graph.ColumnCount * progress);
|
||||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
IHasEndTime end = h as IHasEndTime;
|
IHasEndTime end = h as IHasEndTime;
|
||||||
|
|
||||||
int startRange = (int)((h.StartTime - firstHit)/ interval);
|
int startRange = (int)((h.StartTime - firstHit) / interval);
|
||||||
int endRange = (int)(((end?.EndTime > 0 ? end.EndTime : h.StartTime) - firstHit) / interval);
|
int endRange = (int)(((end?.EndTime > 0 ? end.EndTime : h.StartTime) - firstHit) / interval);
|
||||||
for (int i = startRange; i <= endRange; i++)
|
for (int i = startRange; i <= endRange; i++)
|
||||||
values[i]++;
|
values[i]++;
|
||||||
|
@ -69,7 +69,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
|
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
|
||||||
{
|
{
|
||||||
if ((invalidation & Invalidation.SizeInParentSpace) > 0)
|
if ((invalidation & Invalidation.DrawSize) > 0)
|
||||||
layout.Invalidate();
|
layout.Invalidate();
|
||||||
return base.Invalidate(invalidation, source, shallPropagate);
|
return base.Invalidate(invalidation, source, shallPropagate);
|
||||||
}
|
}
|
||||||
|
@ -1,105 +0,0 @@
|
|||||||
// 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.Game.Graphics;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osu.Game.Screens.Play.HUD;
|
|
||||||
using OpenTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
|
||||||
{
|
|
||||||
public class StandardHUDOverlay : HUDOverlay
|
|
||||||
{
|
|
||||||
protected override RollingCounter<double> CreateAccuracyCounter() => new PercentageCounter
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopRight,
|
|
||||||
Position = new Vector2(0, 35),
|
|
||||||
TextSize = 20,
|
|
||||||
Margin = new MarginPadding { Right = 140 },
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override RollingCounter<int> CreateComboCounter() => new SimpleComboCounter
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopLeft,
|
|
||||||
Position = new Vector2(0, 35),
|
|
||||||
Margin = new MarginPadding { Left = 140 },
|
|
||||||
TextSize = 20,
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override HealthDisplay CreateHealthDisplay() => new StandardHealthDisplay
|
|
||||||
{
|
|
||||||
Size = new Vector2(1, 5),
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Margin = new MarginPadding { Top = 20 }
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override KeyCounterCollection CreateKeyCounter() => new KeyCounterCollection
|
|
||||||
{
|
|
||||||
IsCounting = true,
|
|
||||||
FadeTime = 50,
|
|
||||||
Anchor = Anchor.BottomRight,
|
|
||||||
Origin = Anchor.BottomRight,
|
|
||||||
Margin = new MarginPadding(10),
|
|
||||||
Y = -TwoLayerButton.SIZE_RETRACTED.Y,
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override ScoreCounter CreateScoreCounter() => new ScoreCounter(6)
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
TextSize = 40,
|
|
||||||
Position = new Vector2(0, 30),
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override SongProgress CreateProgress() => new SongProgress
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override ModDisplay CreateModsContainer() => new ModDisplay
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopRight,
|
|
||||||
Origin = Anchor.TopRight,
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Margin = new MarginPadding { Top = 20, Right = 10 },
|
|
||||||
};
|
|
||||||
|
|
||||||
//protected override ReplaySettingsOverlay CreateReplaySettingsOverlay() => new ReplaySettingsOverlay
|
|
||||||
//{
|
|
||||||
// Anchor = Anchor.TopRight,
|
|
||||||
// Origin = Anchor.TopRight,
|
|
||||||
// Margin = new MarginPadding { Top = 100, Right = 10 },
|
|
||||||
//};
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
|
||||||
ComboCounter.AccentColour = colours.BlueLighter;
|
|
||||||
AccuracyCounter.AccentColour = colours.BlueLighter;
|
|
||||||
ScoreCounter.AccentColour = colours.BlueLighter;
|
|
||||||
|
|
||||||
var shd = HealthDisplay as StandardHealthDisplay;
|
|
||||||
if (shd != null)
|
|
||||||
{
|
|
||||||
shd.AccentColour = colours.BlueLighter;
|
|
||||||
shd.GlowColour = colours.BlueDarker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void BindProcessor(ScoreProcessor processor)
|
|
||||||
{
|
|
||||||
base.BindProcessor(processor);
|
|
||||||
|
|
||||||
var shd = HealthDisplay as StandardHealthDisplay;
|
|
||||||
if (shd != null)
|
|
||||||
processor.NewJudgement += shd.Flash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -71,7 +71,6 @@ namespace osu.Game.Screens.Ranking
|
|||||||
|
|
||||||
using (BeginDelayedSequence(transition_time * 0.25f, true))
|
using (BeginDelayedSequence(transition_time * 0.25f, true))
|
||||||
{
|
{
|
||||||
|
|
||||||
circleOuter.ScaleTo(1, transition_time, EasingTypes.OutQuint);
|
circleOuter.ScaleTo(1, transition_time, EasingTypes.OutQuint);
|
||||||
circleOuter.FadeTo(1, transition_time, EasingTypes.OutQuint);
|
circleOuter.FadeTo(1, transition_time, EasingTypes.OutQuint);
|
||||||
|
|
||||||
|
@ -64,7 +64,8 @@ namespace osu.Game.Screens.Ranking
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Box{
|
new Box
|
||||||
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
AlwaysPresent = true
|
AlwaysPresent = true
|
||||||
@ -87,6 +88,5 @@ namespace osu.Game.Screens.Ranking
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,7 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
private BeatmapGroup createGroup(BeatmapSetInfo beatmapSet)
|
private BeatmapGroup createGroup(BeatmapSetInfo beatmapSet)
|
||||||
{
|
{
|
||||||
foreach(var b in beatmapSet.Beatmaps)
|
foreach (var b in beatmapSet.Beatmaps)
|
||||||
{
|
{
|
||||||
if (b.Metadata == null)
|
if (b.Metadata == null)
|
||||||
b.Metadata = beatmapSet.Metadata;
|
b.Metadata = beatmapSet.Metadata;
|
||||||
|
@ -47,6 +47,7 @@ namespace osu.Game.Screens.Select
|
|||||||
public BeatmapInfo Beatmap
|
public BeatmapInfo Beatmap
|
||||||
{
|
{
|
||||||
get { return beatmap; }
|
get { return beatmap; }
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (beatmap == value) return;
|
if (beatmap == value) return;
|
||||||
@ -165,7 +166,7 @@ namespace osu.Game.Screens.Select
|
|||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
LayoutDuration = 200,
|
LayoutDuration = 200,
|
||||||
LayoutEasing = EasingTypes.OutQuint,
|
LayoutEasing = EasingTypes.OutQuint,
|
||||||
Children = new []
|
Children = new[]
|
||||||
{
|
{
|
||||||
description = new MetadataSegment("Description"),
|
description = new MetadataSegment("Description"),
|
||||||
source = new MetadataSegment("Source"),
|
source = new MetadataSegment("Source"),
|
||||||
@ -199,9 +200,9 @@ namespace osu.Game.Screens.Select
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Spacing = new Vector2(0,5),
|
Spacing = new Vector2(0, 5),
|
||||||
Padding = new MarginPadding(10),
|
Padding = new MarginPadding(10),
|
||||||
Children = new []
|
Children = new[]
|
||||||
{
|
{
|
||||||
circleSize = new DifficultyRow("Circle Size", 7),
|
circleSize = new DifficultyRow("Circle Size", 7),
|
||||||
drainRate = new DifficultyRow("HP Drain"),
|
drainRate = new DifficultyRow("HP Drain"),
|
||||||
@ -479,7 +480,7 @@ namespace osu.Game.Screens.Select
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Direction = FillDirection.Full,
|
Direction = FillDirection.Full,
|
||||||
Spacing = new Vector2(5,0),
|
Spacing = new Vector2(5, 0),
|
||||||
Margin = new MarginPadding { Top = header.TextSize }
|
Margin = new MarginPadding { Top = header.TextSize }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -335,7 +335,6 @@ namespace osu.Game.Screens.Tournament
|
|||||||
{
|
{
|
||||||
Logger.Error(ex, "Failed to read last drawings results.");
|
Logger.Error(ex, "Failed to read last drawings results.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -83,6 +83,7 @@ namespace osu.Game.Screens.Tournament
|
|||||||
private ScrollState scrollState
|
private ScrollState scrollState
|
||||||
{
|
{
|
||||||
get { return _scrollState; }
|
get { return _scrollState; }
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_scrollState == value)
|
if (_scrollState == value)
|
||||||
@ -329,6 +330,7 @@ namespace osu.Game.Screens.Tournament
|
|||||||
public bool Selected
|
public bool Selected
|
||||||
{
|
{
|
||||||
get { return selected; }
|
get { return selected; }
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
selected = value;
|
selected = value;
|
||||||
|
@ -48,7 +48,7 @@ namespace osu.Game.Users
|
|||||||
public override string Message => @"Solo Game";
|
public override string Message => @"Solo Game";
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UserStatusMultiplayerGame: UserStatusBusy
|
public class UserStatusMultiplayerGame : UserStatusBusy
|
||||||
{
|
{
|
||||||
public override string Message => @"Multiplaying";
|
public override string Message => @"Multiplaying";
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
|
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="SharpCompress, Version=0.15.2.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
|
<Reference Include="SharpCompress, Version=0.15.2.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
|
||||||
<HintPath>$(SolutionDir)\packages\SharpCompress.0.15.2\lib\net45\SharpCompress.dll</HintPath>
|
<HintPath>$(SolutionDir)\packages\sharpcompress.0.15.2\lib\net45\SharpCompress.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
|
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
|
||||||
@ -76,6 +76,7 @@
|
|||||||
<Compile Include="Beatmaps\DifficultyCalculator.cs" />
|
<Compile Include="Beatmaps\DifficultyCalculator.cs" />
|
||||||
<Compile Include="Graphics\UserInterface\IconButton.cs" />
|
<Compile Include="Graphics\UserInterface\IconButton.cs" />
|
||||||
<Compile Include="Configuration\SelectionRandomType.cs" />
|
<Compile Include="Configuration\SelectionRandomType.cs" />
|
||||||
|
<Compile Include="Online\API\Requests\GetUsersRequest.cs" />
|
||||||
<Compile Include="Online\API\Requests\PostMessageRequest.cs" />
|
<Compile Include="Online\API\Requests\PostMessageRequest.cs" />
|
||||||
<Compile Include="Online\Chat\ErrorMessage.cs" />
|
<Compile Include="Online\Chat\ErrorMessage.cs" />
|
||||||
<Compile Include="Overlays\Chat\ChatTabControl.cs" />
|
<Compile Include="Overlays\Chat\ChatTabControl.cs" />
|
||||||
@ -99,6 +100,7 @@
|
|||||||
<Compile Include="Users\UserProfile.cs" />
|
<Compile Include="Users\UserProfile.cs" />
|
||||||
<Compile Include="Users\Profile\ProfileHeader.cs" />
|
<Compile Include="Users\Profile\ProfileHeader.cs" />
|
||||||
<Compile Include="Users\Profile\ProfileSection.cs" />
|
<Compile Include="Users\Profile\ProfileSection.cs" />
|
||||||
|
<Compile Include="Overlays\Toolbar\ToolbarSocialButton.cs" />
|
||||||
<Compile Include="Rulesets\Beatmaps\BeatmapConverter.cs" />
|
<Compile Include="Rulesets\Beatmaps\BeatmapConverter.cs" />
|
||||||
<Compile Include="Rulesets\Beatmaps\BeatmapProcessor.cs" />
|
<Compile Include="Rulesets\Beatmaps\BeatmapProcessor.cs" />
|
||||||
<Compile Include="Beatmaps\ControlPoints\ControlPoint.cs" />
|
<Compile Include="Beatmaps\ControlPoints\ControlPoint.cs" />
|
||||||
@ -264,7 +266,6 @@
|
|||||||
<Compile Include="Screens\Play\SongProgressInfo.cs" />
|
<Compile Include="Screens\Play\SongProgressInfo.cs" />
|
||||||
<Compile Include="Screens\Play\HUD\ModDisplay.cs" />
|
<Compile Include="Screens\Play\HUD\ModDisplay.cs" />
|
||||||
<Compile Include="Screens\Play\SquareGraph.cs" />
|
<Compile Include="Screens\Play\SquareGraph.cs" />
|
||||||
<Compile Include="Screens\Play\StandardHUDOverlay.cs" />
|
|
||||||
<Compile Include="Screens\Ranking\ResultsPage.cs" />
|
<Compile Include="Screens\Ranking\ResultsPage.cs" />
|
||||||
<Compile Include="Screens\Ranking\ResultsPageRanking.cs" />
|
<Compile Include="Screens\Ranking\ResultsPageRanking.cs" />
|
||||||
<Compile Include="Screens\Ranking\ResultsPageScore.cs" />
|
<Compile Include="Screens\Ranking\ResultsPageScore.cs" />
|
||||||
@ -454,6 +455,9 @@
|
|||||||
<Compile Include="Overlays\Music\PlaylistOverlay.cs" />
|
<Compile Include="Overlays\Music\PlaylistOverlay.cs" />
|
||||||
<Compile Include="Rulesets\Replays\IAutoGenerator.cs" />
|
<Compile Include="Rulesets\Replays\IAutoGenerator.cs" />
|
||||||
<Compile Include="Rulesets\Replays\AutoGenerator.cs" />
|
<Compile Include="Rulesets\Replays\AutoGenerator.cs" />
|
||||||
|
<Compile Include="Overlays\Chat\ChannelSelectionOverlay.cs" />
|
||||||
|
<Compile Include="Overlays\Chat\ChannelListItem.cs" />
|
||||||
|
<Compile Include="Overlays\Chat\ChannelSection.cs" />
|
||||||
<Compile Include="Screens\Multiplayer\DrawableRoom.cs" />
|
<Compile Include="Screens\Multiplayer\DrawableRoom.cs" />
|
||||||
<Compile Include="Online\Multiplayer\Room.cs" />
|
<Compile Include="Online\Multiplayer\Room.cs" />
|
||||||
<Compile Include="Online\Multiplayer\RoomStatus.cs" />
|
<Compile Include="Online\Multiplayer\RoomStatus.cs" />
|
||||||
@ -462,16 +466,24 @@
|
|||||||
<Compile Include="Overlays\DirectOverlay.cs" />
|
<Compile Include="Overlays\DirectOverlay.cs" />
|
||||||
<Compile Include="Overlays\Direct\FilterControl.cs" />
|
<Compile Include="Overlays\Direct\FilterControl.cs" />
|
||||||
<Compile Include="Overlays\Direct\Header.cs" />
|
<Compile Include="Overlays\Direct\Header.cs" />
|
||||||
<Compile Include="Overlays\Direct\SortTabControl.cs" />
|
|
||||||
<Compile Include="Graphics\UserInterface\OsuEnumDropdown.cs" />
|
<Compile Include="Graphics\UserInterface\OsuEnumDropdown.cs" />
|
||||||
<Compile Include="Overlays\Direct\DirectPanel.cs" />
|
<Compile Include="Overlays\Direct\DirectPanel.cs" />
|
||||||
<Compile Include="Overlays\Direct\DirectGridPanel.cs" />
|
<Compile Include="Overlays\Direct\DirectGridPanel.cs" />
|
||||||
<Compile Include="Overlays\Direct\DirectListPanel.cs" />
|
<Compile Include="Overlays\Direct\DirectListPanel.cs" />
|
||||||
<Compile Include="Database\OnlineWorkingBeatmap.cs" />
|
<Compile Include="Database\OnlineWorkingBeatmap.cs" />
|
||||||
<Compile Include="Database\BeatmapOnlineInfo.cs" />
|
<Compile Include="Database\BeatmapOnlineInfo.cs" />
|
||||||
<Compile Include="Overlays\Direct\SlimEnumDropdown.cs" />
|
|
||||||
<Compile Include="Graphics\Containers\ReverseDepthFillFlowContainer.cs" />
|
<Compile Include="Graphics\Containers\ReverseDepthFillFlowContainer.cs" />
|
||||||
<Compile Include="Database\RankStatus.cs" />
|
<Compile Include="Database\RankStatus.cs" />
|
||||||
|
<Compile Include="Overlays\SearchableList\SearchableListHeader.cs" />
|
||||||
|
<Compile Include="Overlays\SearchableList\HeaderTabControl.cs" />
|
||||||
|
<Compile Include="Overlays\Social\FilterControl.cs" />
|
||||||
|
<Compile Include="Overlays\Social\Header.cs" />
|
||||||
|
<Compile Include="Overlays\SearchableList\SearchableListFilterControl.cs" />
|
||||||
|
<Compile Include="Overlays\SearchableList\SearchableListOverlay.cs" />
|
||||||
|
<Compile Include="Graphics\UserInterface\PageTabControl.cs" />
|
||||||
|
<Compile Include="Overlays\SocialOverlay.cs" />
|
||||||
|
<Compile Include="Overlays\SearchableList\SlimEnumDropdown.cs" />
|
||||||
|
<Compile Include="Overlays\SearchableList\DisplayStyleControl.cs" />
|
||||||
<Compile Include="Graphics\UserInterface\BreadcrumbControl.cs" />
|
<Compile Include="Graphics\UserInterface\BreadcrumbControl.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -490,11 +502,6 @@
|
|||||||
</None>
|
</None>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup />
|
|
||||||
<ItemGroup />
|
|
||||||
<ItemGroup />
|
|
||||||
<ItemGroup />
|
|
||||||
<ItemGroup />
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
Loading…
Reference in New Issue
Block a user