1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-03 08:49:57 +08:00

Compare commits

...

317 Commits

155 changed files with 4617 additions and 1717 deletions
+42 -35
View File
@@ -2,63 +2,70 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"command": "msbuild",
"type": "shell",
"suppressTaskName": true,
"args": [
"/property:GenerateFullPaths=true",
"/property:DebugType=portable",
"/verbosity:minimal",
"/m" //parallel compiling support.
],
"tasks": [{
"taskName": "Build (Debug)",
"label": "Build (Debug)",
"type": "shell",
"command": "msbuild",
"args": [
"/p:GenerateFullPaths=true",
"/p:DebugType=portable",
"/m",
"/v:m"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [
"$msCompile"
]
"problemMatcher": "$msCompile"
},
{
"taskName": "Build (Release)",
"label": "Build (Release)",
"type": "shell",
"command": "msbuild",
"args": [
"/p:Configuration=Release",
"/p:DebugType=portable",
"/p:GenerateFullPaths=true",
"/m",
"/v:m"
],
"group": "build",
"args": [
"/property:Configuration=Release"
],
"problemMatcher": [
"$msCompile"
]
"problemMatcher": "$msCompile"
},
{
"taskName": "Clean (Debug)",
"label": "Clean (Debug)",
"type": "shell",
"command": "msbuild",
"args": [
"/target:Clean"
"/p:DebugType=portable",
"/p:GenerateFullPaths=true",
"/m",
"/t:Clean",
"/v:m"
],
"problemMatcher": [
"$msCompile"
]
"problemMatcher": "$msCompile"
},
{
"taskName": "Clean (Release)",
"label": "Clean (Release)",
"type": "shell",
"command": "msbuild",
"args": [
"/target:Clean",
"/property:Configuration=Release"
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/p:DebugType=portable",
"/m",
"/t:Clean",
"/v:m"
],
"problemMatcher": [
"$msCompile"
]
"problemMatcher": "$msCompile"
},
{
"taskName": "Clean All",
"label": "Clean All",
"dependsOn": [
"Clean (Debug)",
"Clean (Release)"
],
"problemMatcher": [
"$msCompile"
]
"problemMatcher": "$msCompile"
}
]
}
+2
View File
@@ -93,6 +93,8 @@ namespace osu.Game.Rulesets.Catch
public override string Description => "osu!catch";
public override string ShortName => "fruits";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap);
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
[Ignore("getting CI working")]
internal class TestCaseCatcherArea : OsuTestCase
public class TestCaseCatcherArea : OsuTestCase
{
private RulesetInfo catchRuleset;
private TestCatcherArea catcherArea;
@@ -90,6 +90,9 @@
<Private>True</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 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.
+2
View File
@@ -105,6 +105,8 @@ namespace osu.Game.Rulesets.Mania
public override string Description => "osu!mania";
public override string ShortName => "mania";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap);
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Tests
{
[TestFixture]
[Ignore("getting CI working")]
internal class TestCaseManiaHitObjects : OsuTestCase
public class TestCaseManiaHitObjects : OsuTestCase
{
public TestCaseManiaHitObjects()
{
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Tests
{
[TestFixture]
[Ignore("getting CI working")]
internal class TestCaseManiaPlayfield : OsuTestCase
public class TestCaseManiaPlayfield : OsuTestCase
{
private const double start_time = 500;
private const double duration = 500;
@@ -114,6 +114,9 @@
<Private>True</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 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.
@@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Framework.Graphics.Primitives;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -165,6 +166,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
public Drawable ProxiedLayer => initialCircle.ApproachCircle;
public override Vector2 SelectionPoint => ToScreenSpace(body.Position);
public override Quad SelectionQuad => body.PathDrawQuad;
}
internal interface ISliderProgress
@@ -14,6 +14,7 @@ using osu.Game.Configuration;
using OpenTK;
using OpenTK.Graphics.ES30;
using OpenTK.Graphics;
using osu.Framework.Graphics.Primitives;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@@ -49,6 +50,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}
}
public Quad PathDrawQuad => container.ScreenSpaceDrawQuad;
private int textureWidth => (int)PathWidth * 2;
private readonly Slider slider;
@@ -182,4 +185,4 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
SetRange(start, end);
}
}
}
}
@@ -93,6 +93,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
float approxFollowCircleRadius = (float)(slider.Radius * 3);
var computeVertex = new Action<double>(t =>
{
// ReSharper disable once PossibleInvalidOperationException (bugged in current r# version)
var diff = slider.PositionAt(t) - slider.LazyEndPosition.Value;
float dist = diff.Length;
+31 -14
View File
@@ -5,7 +5,6 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.OsuDifficulty;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.UI;
@@ -18,6 +17,8 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Osu.Edit;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Osu
{
@@ -33,21 +34,35 @@ namespace osu.Game.Rulesets.Osu
new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
};
public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new[]
public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap)
{
new BeatmapStatistic
IEnumerable<HitObject> hitObjects = beatmap.Beatmap.HitObjects;
IEnumerable<HitObject> circles = hitObjects.Where(c => !(c is IHasEndTime));
IEnumerable<HitObject> sliders = hitObjects.Where(s => s is IHasCurve);
IEnumerable<HitObject> spinners = hitObjects.Where(s => s is IHasEndTime && !(s is IHasCurve));
return new[]
{
Name = @"Circle count",
Content = beatmap.Beatmap.HitObjects.Count(h => h is HitCircle).ToString(),
Icon = FontAwesome.fa_dot_circle_o
},
new BeatmapStatistic
{
Name = @"Slider count",
Content = beatmap.Beatmap.HitObjects.Count(h => h is Slider).ToString(),
Icon = FontAwesome.fa_circle_o
}
};
new BeatmapStatistic
{
Name = @"Circle Count",
Content = circles.Count().ToString(),
Icon = FontAwesome.fa_circle_o
},
new BeatmapStatistic
{
Name = @"Slider Count",
Content = sliders.Count().ToString(),
Icon = FontAwesome.fa_circle
},
new BeatmapStatistic
{
Name = @"Spinner Count",
Content = spinners.Count().ToString(),
Icon = FontAwesome.fa_circle
}
};
}
public override IEnumerable<Mod> GetModsFor(ModType type)
{
@@ -124,6 +139,8 @@ namespace osu.Game.Rulesets.Osu
public override string Description => "osu!";
public override string ShortName => "osu";
public override SettingsSubsection CreateSettings() => new OsuSettings();
public override int LegacyID => 0;
@@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
[Ignore("getting CI working")]
internal class TestCaseHitObjects : OsuTestCase
public class TestCaseHitObjects : OsuTestCase
{
private FramedClock framedClock;
@@ -20,8 +20,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
{
internal class CursorTrail : Drawable
{
public override bool HandleInput => true;
private int currentIndex;
private Shader shader;
+3
View File
@@ -31,6 +31,9 @@ namespace osu.Game.Rulesets.Osu.UI
{
get
{
if (Parent == null)
return Vector2.Zero;
var parentSize = Parent.DrawSize;
var aspectSize = parentSize.X * 0.75f < parentSize.Y ? new Vector2(parentSize.X, parentSize.X * 0.75f) : new Vector2(parentSize.Y * 4f / 3f, parentSize.Y);
@@ -123,7 +123,9 @@
<Private>True</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 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.
+2
View File
@@ -95,6 +95,8 @@ namespace osu.Game.Rulesets.Taiko
public override string Description => "osu!taiko";
public override string ShortName => "taiko";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap);
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
{
[TestFixture]
[Ignore("getting CI working")]
internal class TestCaseTaikoPlayfield : OsuTestCase
public class TestCaseTaikoPlayfield : OsuTestCase
{
private const double default_duration = 1000;
private const float scroll_time = 1000;
@@ -111,6 +111,9 @@
<Private>True</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 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.
@@ -17,7 +17,7 @@ using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
{
internal class TestCaseBeatSyncedContainer : OsuTestCase
public class TestCaseBeatSyncedContainer : OsuTestCase
{
private readonly MusicController mc;
@@ -0,0 +1,342 @@
// 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 System.IO;
using System.Linq;
using System.Text;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
namespace osu.Game.Tests.Visual
{
public class TestCaseBeatmapCarousel : OsuTestCase
{
private TestBeatmapCarousel carousel;
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(CarouselItem),
typeof(CarouselGroup),
typeof(CarouselGroupEagerSelect),
typeof(CarouselBeatmap),
typeof(CarouselBeatmapSet),
typeof(DrawableCarouselItem),
typeof(CarouselItemState),
typeof(DrawableCarouselBeatmap),
typeof(DrawableCarouselBeatmapSet),
};
private readonly Stack<BeatmapSetInfo> selectedSets = new Stack<BeatmapSetInfo>();
private BeatmapInfo currentSelection;
private const int set_count = 5;
[BackgroundDependencyLoader]
private void load()
{
Add(carousel = new TestBeatmapCarousel
{
RelativeSizeAxes = Axes.Both,
});
List<BeatmapSetInfo> beatmapSets = new List<BeatmapSetInfo>();
for (int i = 1; i <= set_count; i++)
beatmapSets.Add(createTestBeatmapSet(i));
carousel.SelectionChanged = s => currentSelection = s;
AddStep("Load Beatmaps", () => { carousel.BeatmapSets = beatmapSets; });
AddUntilStep(() => carousel.BeatmapSets.Any(), "Wait for load");
testTraversal();
testFiltering();
testRandom();
testAddRemove();
testSorting();
testRemoveAll();
}
private void ensureRandomFetchSuccess() =>
AddAssert("ensure prev random fetch worked", () => selectedSets.Peek() == carousel.SelectedBeatmapSet);
private void checkSelected(int set, int? diff = null) =>
AddAssert($"selected is set{set}{(diff.HasValue ? $" diff{diff.Value}" : "")}", () =>
{
if (diff != null)
return carousel.SelectedBeatmap == carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff.Value - 1).First();
return carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Contains(carousel.SelectedBeatmap);
});
private void setSelected(int set, int diff) =>
AddStep($"select set{set} diff{diff}", () =>
carousel.SelectBeatmap(carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff - 1).First()));
private void advanceSelection(bool diff, int direction = 1, int count = 1)
{
if (count == 1)
AddStep($"select {(direction > 0 ? "next" : "prev")} {(diff ? "diff" : "set")}", () =>
carousel.SelectNext(direction, !diff));
else
{
AddRepeatStep($"select {(direction > 0 ? "next" : "prev")} {(diff ? "diff" : "set")}", () =>
carousel.SelectNext(direction, !diff), count);
}
}
private void checkVisibleItemCount(bool diff, int count) =>
AddAssert($"{count} {(diff ? "diffs" : "sets")} visible", () =>
carousel.Items.Count(s => (diff ? s.Item is CarouselBeatmap : s.Item is CarouselBeatmapSet) && s.Item.Visible) == count);
private void nextRandom() =>
AddStep("select random next", () =>
{
carousel.RandomAlgorithm.Value = RandomSelectAlgorithm.RandomPermutation;
if (!selectedSets.Any() && carousel.SelectedBeatmap != null)
selectedSets.Push(carousel.SelectedBeatmapSet);
carousel.SelectNextRandom();
selectedSets.Push(carousel.SelectedBeatmapSet);
});
private void ensureRandomDidntRepeat() =>
AddAssert("ensure no repeats", () => selectedSets.Distinct().Count() == selectedSets.Count);
private void prevRandom() => AddStep("select random last", () =>
{
carousel.SelectPreviousRandom();
selectedSets.Pop();
});
/// <summary>
/// Test keyboard traversal
/// </summary>
private void testTraversal()
{
advanceSelection(direction: 1, diff: false);
checkSelected(1, 1);
advanceSelection(direction: 1, diff: true);
checkSelected(1, 2);
advanceSelection(direction: -1, diff: false);
checkSelected(set_count, 1);
advanceSelection(direction: -1, diff: true);
checkSelected(set_count - 1, 3);
advanceSelection(diff: false);
advanceSelection(diff: false);
checkSelected(1, 2);
advanceSelection(direction: -1, diff: true);
advanceSelection(direction: -1, diff: true);
checkSelected(set_count, 3);
}
/// <summary>
/// Test filtering
/// </summary>
private void testFiltering()
{
// basic filtering
setSelected(1, 1);
AddStep("Filter", () => carousel.Filter(new FilterCriteria { SearchText = "set #3!" }, false));
checkVisibleItemCount(diff: false, count: 1);
checkVisibleItemCount(diff: true, count: 3);
checkSelected(3, 1);
advanceSelection(diff: true, count: 4);
checkSelected(3, 2);
AddStep("Un-filter (debounce)", () => carousel.Filter(new FilterCriteria()));
AddUntilStep(() => !carousel.PendingFilterTask, "Wait for debounce");
checkVisibleItemCount(diff: false, count: set_count);
checkVisibleItemCount(diff: true, count: 3);
// test filtering some difficulties (and keeping current beatmap set selected).
setSelected(1, 2);
AddStep("Filter some difficulties", () => carousel.Filter(new FilterCriteria { SearchText = "Normal" }, false));
checkSelected(1, 1);
AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false));
checkSelected(1, 1);
AddStep("Filter all", () => carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false));
checkVisibleItemCount(false, 0);
checkVisibleItemCount(true, 0);
AddAssert("Selection is null", () => currentSelection == null);
AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false));
AddAssert("Selection is non-null", () => currentSelection != null);
}
/// <summary>
/// Test random non-repeating algorithm
/// </summary>
private void testRandom()
{
setSelected(1, 1);
nextRandom();
ensureRandomDidntRepeat();
nextRandom();
ensureRandomDidntRepeat();
nextRandom();
ensureRandomDidntRepeat();
prevRandom();
ensureRandomFetchSuccess();
prevRandom();
ensureRandomFetchSuccess();
nextRandom();
ensureRandomDidntRepeat();
nextRandom();
ensureRandomDidntRepeat();
nextRandom();
AddAssert("ensure repeat", () => selectedSets.Contains(carousel.SelectedBeatmapSet));
}
/// <summary>
/// Test adding and removing beatmap sets
/// </summary>
private void testAddRemove()
{
AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 1)));
AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 2)));
checkVisibleItemCount(false, set_count + 2);
AddStep("Remove set", () => carousel.RemoveBeatmapSet(createTestBeatmapSet(set_count + 2)));
checkVisibleItemCount(false, set_count + 1);
setSelected(set_count + 1, 1);
AddStep("Remove set", () => carousel.RemoveBeatmapSet(createTestBeatmapSet(set_count + 1)));
checkVisibleItemCount(false, set_count);
checkSelected(set_count);
}
/// <summary>
/// Test sorting
/// </summary>
private void testSorting()
{
AddStep("Sort by author", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Author }, false));
AddAssert("Check zzzzz is at bottom", () => carousel.BeatmapSets.Last().Metadata.AuthorString == "zzzzz");
AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false));
AddAssert($"Check #{set_count} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Title.EndsWith($"#{set_count}!"));
}
private void testRemoveAll()
{
setSelected(2, 1);
AddAssert("Selection is non-null", () => currentSelection != null);
AddStep("Remove selected", () => carousel.RemoveBeatmapSet(carousel.SelectedBeatmapSet));
checkSelected(2);
AddStep("Remove first", () => carousel.RemoveBeatmapSet(carousel.BeatmapSets.First()));
AddStep("Remove first", () => carousel.RemoveBeatmapSet(carousel.BeatmapSets.First()));
checkSelected(1);
AddUntilStep(() =>
{
if (!carousel.BeatmapSets.Any()) return true;
carousel.RemoveBeatmapSet(carousel.BeatmapSets.Last());
return false;
}, "Remove all");
AddAssert("Selection is null", () => currentSelection == null);
}
private BeatmapSetInfo createTestBeatmapSet(int i)
{
return new BeatmapSetInfo
{
ID = i,
OnlineBeatmapSetID = i,
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
OnlineBeatmapSetID = i,
// Create random metadata, then we can check if sorting works based on these
Artist = $"peppy{i.ToString().PadLeft(6, '0')}",
Title = $"test set #{i}!",
AuthorString = string.Concat(Enumerable.Repeat((char)('z' - Math.Min(25, i - 1)), 5))
},
Beatmaps = new List<BeatmapInfo>(new[]
{
new BeatmapInfo
{
OnlineBeatmapID = i * 10,
Path = "normal.osu",
Version = "Normal",
StarDifficulty = 2,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = 3.5f,
}
},
new BeatmapInfo
{
OnlineBeatmapID = i * 10 + 1,
Path = "hard.osu",
Version = "Hard",
StarDifficulty = 5,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = 5,
}
},
new BeatmapInfo
{
OnlineBeatmapID = i * 10 + 2,
Path = "insane.osu",
Version = "Insane",
StarDifficulty = 6,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = 7,
}
},
}),
};
}
private class TestBeatmapCarousel : BeatmapCarousel
{
public new List<DrawableCarouselItem> Items => base.Items;
public bool PendingFilterTask => FilterTask != null;
}
}
}
@@ -10,7 +10,7 @@ namespace osu.Game.Tests.Visual
{
[TestFixture]
[System.ComponentModel.Description("PlaySongSelect leaderboard/details area")]
internal class TestCaseBeatmapDetailArea : OsuTestCase
public class TestCaseBeatmapDetailArea : OsuTestCase
{
public TestCaseBeatmapDetailArea()
{
@@ -10,7 +10,7 @@ using osu.Game.Screens.Select;
namespace osu.Game.Tests.Visual
{
[Description("PlaySongSelect beatmap details")]
internal class TestCaseBeatmapDetails : OsuTestCase
public class TestCaseBeatmapDetails : OsuTestCase
{
public TestCaseBeatmapDetails()
{
@@ -0,0 +1,69 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Screens.Select;
namespace osu.Game.Tests.Visual
{
public class TestCaseBeatmapInfoWedge : OsuTestCase
{
private BeatmapManager beatmaps;
private readonly Random random;
private readonly BeatmapInfoWedge infoWedge;
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
public TestCaseBeatmapInfoWedge()
{
random = new Random(0123);
Add(infoWedge = new BeatmapInfoWedge
{
Size = new Vector2(0.5f, 245),
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding
{
Top = 20,
},
});
AddStep("show", () =>
{
Content.FadeInFromZero(250);
infoWedge.State = Visibility.Visible;
infoWedge.UpdateBeatmap(beatmap);
});
AddStep("hide", () =>
{
infoWedge.State = Visibility.Hidden;
Content.FadeOut(100);
});
AddStep("random beatmap", randomBeatmap);
AddStep("null beatmap", () => infoWedge.UpdateBeatmap(beatmap.Default));
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game, BeatmapManager beatmaps)
{
this.beatmaps = beatmaps;
beatmap.BindTo(game.Beatmap);
}
private void randomBeatmap()
{
var sets = beatmaps.GetAllUsableBeatmapSets();
if (sets.Count == 0)
return;
var b = sets[random.Next(0, sets.Count)].Beatmaps[0];
beatmap.Value = beatmaps.GetWorkingBeatmap(b);
infoWedge.UpdateBeatmap(beatmap);
}
}
}
@@ -10,7 +10,7 @@ using OpenTK.Input;
namespace osu.Game.Tests.Visual
{
[Description("bottom beatmap details")]
internal class TestCaseBeatmapOptionsOverlay : OsuTestCase
public class TestCaseBeatmapOptionsOverlay : OsuTestCase
{
public TestCaseBeatmapOptionsOverlay()
{
@@ -12,7 +12,7 @@ using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
internal class TestCaseBeatmapSetOverlay : OsuTestCase
public class TestCaseBeatmapSetOverlay : OsuTestCase
{
private readonly BeatmapSetOverlay overlay;
+1 -1
View File
@@ -6,7 +6,7 @@ using osu.Game.Graphics.UserInterface;
namespace osu.Game.Tests.Visual
{
internal class TestCaseBreadcrumbs : OsuTestCase
public class TestCaseBreadcrumbs : OsuTestCase
{
public TestCaseBreadcrumbs()
{
@@ -8,7 +8,7 @@ using System.Collections.Generic;
namespace osu.Game.Tests.Visual
{
internal class TestCaseBreakOverlay : OsuTestCase
public class TestCaseBreakOverlay : OsuTestCase
{
private readonly BreakOverlay breakOverlay;
@@ -9,7 +9,7 @@ using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
{
internal class TestCaseButtonSystem : OsuTestCase
public class TestCaseButtonSystem : OsuTestCase
{
public TestCaseButtonSystem()
{
+1 -1
View File
@@ -8,7 +8,7 @@ using osu.Game.Overlays;
namespace osu.Game.Tests.Visual
{
[Description("Testing chat api and overlay")]
internal class TestCaseChatDisplay : OsuTestCase
public class TestCaseChatDisplay : OsuTestCase
{
public TestCaseChatDisplay()
{
+1 -1
View File
@@ -13,7 +13,7 @@ using osu.Game.Graphics.Cursor;
namespace osu.Game.Tests.Visual
{
internal class TestCaseContextMenu : OsuTestCase
public class TestCaseContextMenu : OsuTestCase
{
private const int start_time = 0;
private const int duration = 1000;
@@ -7,7 +7,7 @@ using osu.Game.Overlays.Dialog;
namespace osu.Game.Tests.Visual
{
internal class TestCaseDialogOverlay : OsuTestCase
public class TestCaseDialogOverlay : OsuTestCase
{
public TestCaseDialogOverlay()
{
@@ -12,7 +12,7 @@ using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
internal class TestCaseDrawableRoom : OsuTestCase
public class TestCaseDrawableRoom : OsuTestCase
{
private RulesetStore rulesets;
+1 -1
View File
@@ -9,7 +9,7 @@ using osu.Game.Screens.Tournament.Teams;
namespace osu.Game.Tests.Visual
{
[Description("for tournament use")]
internal class TestCaseDrawings : OsuTestCase
public class TestCaseDrawings : OsuTestCase
{
public TestCaseDrawings()
{
@@ -0,0 +1,54 @@
// 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 osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
using osu.Game.Rulesets.Edit.Layers.Selection;
using osu.Game.Rulesets.Osu.Edit;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Tests.Visual
{
public class TestCaseEditorSelectionLayer : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(SelectionLayer) };
public TestCaseEditorSelectionLayer()
{
var playfield = new OsuEditPlayfield
{
new DrawableHitCircle(new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }),
new DrawableHitCircle(new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }),
new DrawableSlider(new Slider
{
ControlPoints = new List<Vector2>
{
new Vector2(128, 256),
new Vector2(344, 256),
},
Distance = 400,
Position = new Vector2(128, 256),
Velocity = 1,
TickDistance = 100,
Scale = 0.5f
})
};
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Clock = new FramedClock(new StopwatchClock()),
Child = playfield
},
new SelectionLayer(playfield)
};
}
}
}
@@ -14,7 +14,7 @@ using osu.Framework.Configuration;
namespace osu.Game.Tests.Visual
{
internal class TestCaseEditorSummaryTimeline : OsuTestCase
public class TestCaseEditorSummaryTimeline : OsuTestCase
{
private const int length = 60000;
private readonly Random random;
+1 -1
View File
@@ -5,7 +5,7 @@ using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Tests.Visual
{
internal class TestCaseGamefield : OsuTestCase
public class TestCaseGamefield : OsuTestCase
{
protected override void LoadComplete()
{
@@ -0,0 +1,258 @@
// 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 System.ComponentModel;
using System.Linq;
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Logging;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{
[Description("player pause/fail screens")]
public class TestCaseGameplayMenuOverlay : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer) };
private FailOverlay failOverlay;
private PauseContainer.PauseOverlay pauseOverlay;
[BackgroundDependencyLoader]
private void load()
{
Add(pauseOverlay = new PauseContainer.PauseOverlay
{
OnResume = () => Logger.Log(@"Resume"),
OnRetry = () => Logger.Log(@"Retry"),
OnQuit = () => Logger.Log(@"Quit"),
});
Add(failOverlay = new FailOverlay
{
OnRetry = () => Logger.Log(@"Retry"),
OnQuit = () => Logger.Log(@"Quit"),
});
var retryCount = 0;
AddStep("Add retry", () =>
{
retryCount++;
pauseOverlay.Retries = failOverlay.Retries = retryCount;
});
AddToggleStep("Toggle pause overlay", t => pauseOverlay.ToggleVisibility());
AddToggleStep("Toggle fail overlay", t => failOverlay.ToggleVisibility());
testHideResets();
testEnterWithoutSelection();
testKeyUpFromInitial();
testKeyDownFromInitial();
testKeyUpWrapping();
testKeyDownWrapping();
testMouseSelectionAfterKeySelection();
testKeySelectionAfterMouseSelection();
testMouseDeselectionResets();
testClickSelection();
testEnterKeySelection();
}
/// <summary>
/// Test that hiding the overlay after hovering a button will reset the overlay to the initial state with no buttons selected.
/// </summary>
private void testHideResets()
{
AddStep("Show overlay", () => failOverlay.Show());
AddStep("Hover first button", () => failOverlay.Buttons.First().TriggerOnHover(null));
AddStep("Hide overlay", () => failOverlay.Hide());
AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected));
}
/// <summary>
/// Tests that pressing enter after an overlay shows doesn't trigger an event because a selection hasn't occurred.
/// </summary>
private void testEnterWithoutSelection()
{
AddStep("Show overlay", () => pauseOverlay.Show());
AddStep("Press enter", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter }));
AddAssert("Overlay still open", () => pauseOverlay.State == Visibility.Visible);
AddStep("Hide overlay", () => pauseOverlay.Hide());
}
/// <summary>
/// Tests that pressing the up arrow from the initial state selects the last button.
/// </summary>
private void testKeyUpFromInitial()
{
AddStep("Show overlay", () => pauseOverlay.Show());
AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected);
AddStep("Hide overlay", () => pauseOverlay.Hide());
}
/// <summary>
/// Tests that pressing the down arrow from the initial state selects the first button.
/// </summary>
private void testKeyDownFromInitial()
{
AddStep("Show overlay", () => pauseOverlay.Show());
AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected);
AddStep("Hide overlay", () => pauseOverlay.Hide());
}
/// <summary>
/// Tests that pressing the up arrow repeatedly causes the selected button to wrap correctly.
/// </summary>
private void testKeyUpWrapping()
{
AddStep("Show overlay", () => failOverlay.Show());
AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
AddStep("Hide overlay", () => failOverlay.Hide());
}
/// <summary>
/// Tests that pressing the down arrow repeatedly causes the selected button to wrap correctly.
/// </summary>
private void testKeyDownWrapping()
{
AddStep("Show overlay", () => failOverlay.Show());
AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
AddStep("Hide overlay", () => failOverlay.Hide());
}
/// <summary>
/// Tests that hovering a button that was previously selected with the keyboard correctly selects the new button and deselects the previous button.
/// </summary>
private void testMouseSelectionAfterKeySelection()
{
AddStep("Show overlay", () => pauseOverlay.Show());
var secondButton = pauseOverlay.Buttons.Skip(1).First();
AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
AddStep("Hover second button", () => secondButton.TriggerOnHover(null));
AddAssert("First button not selected", () => !pauseOverlay.Buttons.First().Selected);
AddAssert("Second button selected", () => secondButton.Selected);
AddStep("Hide overlay", () => pauseOverlay.Hide());
}
/// <summary>
/// Tests that pressing a key after selecting a button with a hover event correctly selects a new button and deselects the previous button.
/// </summary>
private void testKeySelectionAfterMouseSelection()
{
AddStep("Show overlay", () => pauseOverlay.Show());
var secondButton = pauseOverlay.Buttons.Skip(1).First();
AddStep("Hover second button", () => secondButton.TriggerOnHover(null));
AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
AddAssert("Second button not selected", () => !secondButton.Selected);
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected);
AddStep("Hide overlay", () => pauseOverlay.Hide());
}
/// <summary>
/// Tests that deselecting with the mouse by losing hover will reset the overlay to the initial state.
/// </summary>
private void testMouseDeselectionResets()
{
AddStep("Show overlay", () => pauseOverlay.Show());
var secondButton = pauseOverlay.Buttons.Skip(1).First();
AddStep("Hover second button", () => secondButton.TriggerOnHover(null));
AddStep("Unhover second button", () => secondButton.TriggerOnHoverLost(null));
AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); // Initial state condition
AddStep("Hide overlay", () => pauseOverlay.Hide());
}
/// <summary>
/// Tests that clicking on a button correctly causes a click event for that button.
/// </summary>
private void testClickSelection()
{
AddStep("Show overlay", () => pauseOverlay.Show());
var retryButton = pauseOverlay.Buttons.Skip(1).First();
bool triggered = false;
AddStep("Click retry button", () =>
{
var lastAction = pauseOverlay.OnRetry;
pauseOverlay.OnRetry = () => triggered = true;
retryButton.TriggerOnClick();
pauseOverlay.OnRetry = lastAction;
});
AddAssert("Action was triggered", () => triggered);
AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden);
}
/// <summary>
/// Tests that pressing the enter key with a button selected correctly causes a click event for that button.
/// </summary>
private void testEnterKeySelection()
{
AddStep("Show overlay", () => pauseOverlay.Show());
AddStep("Select second button", () =>
{
pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down });
pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down });
});
var retryButton = pauseOverlay.Buttons.Skip(1).First();
bool triggered = false;
AddStep("Press enter", () =>
{
var lastAction = pauseOverlay.OnRetry;
pauseOverlay.OnRetry = () => triggered = true;
retryButton.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter });
pauseOverlay.OnRetry = lastAction;
});
AddAssert("Action was triggered", () => triggered);
AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden);
}
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ using OpenTK;
namespace osu.Game.Tests.Visual
{
internal class TestCaseGraph : OsuTestCase
public class TestCaseGraph : OsuTestCase
{
public TestCaseGraph()
{
@@ -0,0 +1,47 @@
// 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.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Overlays.Profile.Sections;
using osu.Game.Overlays.Profile.Sections.Historical;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
public class TestCaseHistoricalSection : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes =>
new[]
{
typeof(HistoricalSection),
typeof(PaginatedMostPlayedBeatmapContainer),
typeof(DrawableMostPlayedRow),
typeof(DrawableProfileRow)
};
public TestCaseHistoricalSection()
{
HistoricalSection section;
Add(new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f)
});
Add(new ScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = section = new HistoricalSection(),
});
AddStep("Show peppy", () => section.User.Value = new User { Id = 2 });
AddStep("Show WubWoofWolf", () => section.User.Value = new User { Id = 39828 });
}
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ using OpenTK.Input;
namespace osu.Game.Tests.Visual
{
internal class TestCaseKeyCounter : OsuTestCase
public class TestCaseKeyCounter : OsuTestCase
{
public TestCaseKeyCounter()
{
+67 -10
View File
@@ -6,14 +6,44 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
using osu.Framework.Allocation;
using OpenTK;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
namespace osu.Game.Tests.Visual
{
[Description("PlaySongSelect leaderboard")]
internal class TestCaseLeaderboard : OsuTestCase
public class TestCaseLeaderboard : OsuTestCase
{
private readonly Leaderboard leaderboard;
private RulesetStore rulesets;
private readonly FailableLeaderboard leaderboard;
public TestCaseLeaderboard()
{
Add(leaderboard = new FailableLeaderboard
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f),
Scope = LeaderboardScope.Global,
});
AddStep(@"New Scores", newScores);
AddStep(@"Empty Scores", () => leaderboard.SetRetrievalState(PlaceholderState.NoScores));
AddStep(@"Network failure", () => leaderboard.SetRetrievalState(PlaceholderState.NetworkFailure));
AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter));
AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn));
AddStep(@"Real beatmap", realBeatmap);
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}
private void newScores()
{
@@ -204,17 +234,44 @@ namespace osu.Game.Tests.Visual
leaderboard.Scores = scores;
}
public TestCaseLeaderboard()
private void realBeatmap()
{
Add(leaderboard = new Leaderboard
leaderboard.Beatmap = new BeatmapInfo
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f),
});
StarDifficulty = 1.36,
Version = @"BASIC",
OnlineBeatmapID = 1113057,
Ruleset = rulesets.GetRuleset(0),
BaseDifficulty = new BeatmapDifficulty
{
CircleSize = 4,
DrainRate = 6.5f,
OverallDifficulty = 6.5f,
ApproachRate = 5,
},
OnlineInfo = new BeatmapOnlineInfo
{
Length = 115000,
CircleCount = 265,
SliderCount = 71,
PlayCount = 47906,
PassCount = 19899,
},
Metrics = new BeatmapMetrics
{
Ratings = Enumerable.Range(0, 11),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
},
};
}
AddStep(@"New Scores", newScores);
newScores();
private class FailableLeaderboard : Leaderboard
{
public void SetRetrievalState(PlaceholderState state)
{
PlaceholderState = state;
}
}
}
}
@@ -9,7 +9,7 @@ using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
internal class TestCaseMedalOverlay : OsuTestCase
public class TestCaseMedalOverlay : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@@ -1,57 +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 System.ComponentModel;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{
[Description("player pause/fail screens")]
internal class TestCaseMenuOverlays : OsuTestCase
{
public TestCaseMenuOverlays()
{
FailOverlay failOverlay;
PauseContainer.PauseOverlay pauseOverlay;
var retryCount = 0;
Add(pauseOverlay = new PauseContainer.PauseOverlay
{
OnResume = () => Logger.Log(@"Resume"),
OnRetry = () => Logger.Log(@"Retry"),
OnQuit = () => Logger.Log(@"Quit"),
});
Add(failOverlay = new FailOverlay
{
OnRetry = () => Logger.Log(@"Retry"),
OnQuit = () => Logger.Log(@"Quit"),
});
AddStep(@"Pause", delegate
{
if (failOverlay.State == Visibility.Visible)
{
failOverlay.Hide();
}
pauseOverlay.Show();
});
AddStep("Fail", delegate
{
if (pauseOverlay.State == Visibility.Visible)
{
pauseOverlay.Hide();
}
failOverlay.Show();
});
AddStep("Add Retry", delegate
{
retryCount++;
pauseOverlay.Retries = retryCount;
failOverlay.Retries = retryCount;
});
}
}
}
+162 -7
View File
@@ -8,17 +8,25 @@ using osu.Game.Overlays.Mods;
using osu.Game.Rulesets;
using osu.Game.Screens.Play.HUD;
using OpenTK;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using System.Linq;
using System.Collections.Generic;
using osu.Game.Rulesets.Osu;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
{
[Description("mod select and icon display")]
internal class TestCaseMods : OsuTestCase
public class TestCaseMods : OsuTestCase
{
private ModSelectOverlay modSelect;
private ModDisplay modDisplay;
private const string unranked_suffix = " (Unranked)";
private RulesetStore rulesets;
private ModDisplay modDisplay;
private TestModSelectOverlay modSelect;
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
@@ -30,7 +38,7 @@ namespace osu.Game.Tests.Visual
{
base.LoadComplete();
Add(modSelect = new ModSelectOverlay
Add(modSelect = new TestModSelectOverlay
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomCentre,
@@ -48,9 +56,156 @@ namespace osu.Game.Tests.Visual
modDisplay.Current.BindTo(modSelect.SelectedMods);
AddStep("Toggle", modSelect.ToggleVisibility);
AddStep("Hide", modSelect.Hide);
AddStep("Show", modSelect.Show);
foreach (var ruleset in rulesets.AvailableRulesets)
AddStep(ruleset.CreateInstance().Description, () => modSelect.Ruleset.Value = ruleset);
foreach (var rulesetInfo in rulesets.AvailableRulesets)
{
Ruleset ruleset = rulesetInfo.CreateInstance();
AddStep($"switch to {ruleset.Description}", () => modSelect.Ruleset.Value = rulesetInfo);
switch (ruleset) {
case OsuRuleset or:
testOsuMods(or);
break;
}
}
}
private void testOsuMods(OsuRuleset ruleset)
{
var easierMods = ruleset.GetModsFor(ModType.DifficultyReduction);
var harderMods = ruleset.GetModsFor(ModType.DifficultyIncrease);
var assistMods = ruleset.GetModsFor(ModType.Special);
var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail);
var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden);
var doubleTimeMod = harderMods.OfType<MultiMod>().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime));
var autoPilotMod = assistMods.FirstOrDefault(m => m is OsuModAutopilot);
testSingleMod(noFailMod);
testMultiMod(doubleTimeMod);
testIncompatibleMods(noFailMod, autoPilotMod);
testDeselectAll(easierMods.Where(m => !(m is MultiMod)));
testMultiplierTextColour(noFailMod, modSelect.LowMultiplierColour);
testMultiplierTextColour(hiddenMod, modSelect.HighMultiplierColour);
testMultiplierTextUnranked(autoPilotMod);
}
private void testSingleMod(Mod mod)
{
selectNext(mod);
checkSelected(mod);
selectPrevious(mod);
checkNotSelected(mod);
selectNext(mod);
selectNext(mod);
checkNotSelected(mod);
selectPrevious(mod);
selectPrevious(mod);
checkNotSelected(mod);
}
private void testMultiMod(MultiMod multiMod)
{
foreach (var mod in multiMod.Mods)
{
selectNext(mod);
checkSelected(mod);
}
for (int index = multiMod.Mods.Length - 1; index >= 0; index--)
selectPrevious(multiMod.Mods[index]);
foreach (var mod in multiMod.Mods)
checkNotSelected(mod);
}
private void testIncompatibleMods(Mod modA, Mod modB)
{
selectNext(modA);
checkSelected(modA);
checkNotSelected(modB);
selectNext(modB);
checkSelected(modB);
checkNotSelected(modA);
selectPrevious(modB);
checkNotSelected(modA);
checkNotSelected(modB);
}
private void testDeselectAll(IEnumerable<Mod> mods)
{
foreach (var mod in mods)
selectNext(mod);
AddAssert("check for any selection", () => modSelect.SelectedMods.Value.Any());
AddStep("deselect all", modSelect.DeselectAllButton.Action.Invoke);
AddAssert("check for no selection", () => !modSelect.SelectedMods.Value.Any());
}
private void testMultiplierTextColour(Mod mod, Color4 colour)
{
checkLabelColor(Color4.White);
selectNext(mod);
AddWaitStep(1, "wait for changing colour");
checkLabelColor(colour);
selectPrevious(mod);
AddWaitStep(1, "wait for changing colour");
checkLabelColor(Color4.White);
}
private void testMultiplierTextUnranked(Mod mod)
{
AddAssert("check for ranked", () => !modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix));
selectNext(mod);
AddAssert("check for unranked", () => modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix));
selectPrevious(mod);
AddAssert("check for ranked", () => !modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix));
}
private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext());
private void selectPrevious(Mod mod) => AddStep($"right click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectPrevious());
private void checkSelected(Mod mod)
{
AddAssert($"check {mod.Name} is selected", () =>
{
var button = modSelect.GetModButton(mod);
return modSelect.SelectedMods.Value.Single(m => m.Name == mod.Name) != null && button.SelectedMod.GetType() == mod.GetType() && button.Selected;
});
}
private void checkNotSelected(Mod mod)
{
AddAssert($"check {mod.Name} is not selected", () =>
{
var button = modSelect.GetModButton(mod);
return modSelect.SelectedMods.Value.All(m => m.GetType() != mod.GetType()) && button.SelectedMod?.GetType() != mod.GetType();
});
}
private void checkLabelColor(Color4 color) => AddAssert("check label has expected colour", () => modSelect.MultiplierLabel.Colour.AverageColour == color);
private class TestModSelectOverlay : ModSelectOverlay
{
public ModButton GetModButton(Mod mod)
{
var section = ModSectionsContainer.Children.Single(s => s.ModType == mod.Type);
return section.ButtonsContainer.OfType<ModButton>().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType()));
}
public new OsuSpriteText MultiplierLabel => base.MultiplierLabel;
public new TriangleButton DeselectAllButton => base.DeselectAllButton;
public new Color4 LowMultiplierColour => base.LowMultiplierColour;
public new Color4 HighMultiplierColour => base.HighMultiplierColour;
}
}
}
@@ -11,7 +11,7 @@ using osu.Game.Overlays;
namespace osu.Game.Tests.Visual
{
internal class TestCaseMusicController : OsuTestCase
public class TestCaseMusicController : OsuTestCase
{
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
@@ -13,7 +13,7 @@ using osu.Game.Overlays.Notifications;
namespace osu.Game.Tests.Visual
{
[TestFixture]
internal class TestCaseNotificationOverlay : OsuTestCase
public class TestCaseNotificationOverlay : OsuTestCase
{
private readonly NotificationOverlay manager;
@@ -82,7 +82,11 @@ namespace osu.Game.Tests.Visual
private void sendProgress2()
{
var n = new ProgressNotification { Text = @"Downloading Haitai..." };
var n = new ProgressNotification
{
Text = @"Downloading Haitai...",
CompletionText = "Downloaded Haitai!",
};
manager.Post(n);
progressingNotifications.Add(n);
}
@@ -91,7 +95,11 @@ namespace osu.Game.Tests.Visual
private void sendProgress1()
{
var n = new ProgressNotification { Text = @"Uploading to BSS..." };
var n = new ProgressNotification
{
Text = @"Uploading to BSS...",
CompletionText = "Uploaded to BSS!",
};
manager.Post(n);
progressingNotifications.Add(n);
}
@@ -7,7 +7,7 @@ using osu.Game.Overlays;
namespace osu.Game.Tests.Visual
{
internal class TestCaseOnScreenDisplay : OsuTestCase
public class TestCaseOnScreenDisplay : OsuTestCase
{
private FrameworkConfigManager config;
private Bindable<FrameSync> frameSyncMode;
@@ -13,12 +13,13 @@ using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
using osu.Game.Tests.Platform;
namespace osu.Game.Tests.Visual
{
internal class TestCasePlaySongSelect : OsuTestCase
public class TestCasePlaySongSelect : OsuTestCase
{
private BeatmapManager manager;
@@ -26,10 +27,28 @@ namespace osu.Game.Tests.Visual
private DependencyContainer dependencies;
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(SongSelect),
typeof(BeatmapCarousel),
typeof(CarouselItem),
typeof(CarouselGroup),
typeof(CarouselGroupEagerSelect),
typeof(CarouselBeatmap),
typeof(CarouselBeatmapSet),
typeof(DrawableCarouselItem),
typeof(CarouselItemState),
typeof(DrawableCarouselBeatmap),
typeof(DrawableCarouselBeatmapSet),
};
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent);
[BackgroundDependencyLoader]
private void load()
private void load(BeatmapManager baseManager)
{
PlaySongSelect songSelect;
@@ -43,7 +62,10 @@ namespace osu.Game.Tests.Visual
Func<OsuDbContext> contextFactory = () => context;
dependencies.Cache(rulesets = new RulesetStore(contextFactory));
dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null));
dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null)
{
DefaultBeatmap = baseManager.GetWorkingBeatmap(null)
});
for (int i = 0; i < 100; i += 10)
manager.Import(createTestBeatmapSet(i));
+1 -1
View File
@@ -8,7 +8,7 @@ using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{
internal class TestCaseReplay : TestCasePlayer
public class TestCaseReplay : TestCasePlayer
{
protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
{
@@ -8,7 +8,7 @@ using osu.Game.Screens.Play.ReplaySettings;
namespace osu.Game.Tests.Visual
{
internal class TestCaseReplaySettingsOverlay : OsuTestCase
public class TestCaseReplaySettingsOverlay : OsuTestCase
{
public TestCaseReplaySettingsOverlay()
{
+1 -1
View File
@@ -11,7 +11,7 @@ using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
internal class TestCaseResults : OsuTestCase
public class TestCaseResults : OsuTestCase
{
private BeatmapManager beatmaps;
@@ -11,7 +11,7 @@ using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
internal class TestCaseRoomInspector : OsuTestCase
public class TestCaseRoomInspector : OsuTestCase
{
private RulesetStore rulesets;
@@ -10,7 +10,7 @@ using OpenTK;
namespace osu.Game.Tests.Visual
{
internal class TestCaseScoreCounter : OsuTestCase
public class TestCaseScoreCounter : OsuTestCase
{
public TestCaseScoreCounter()
{
+21 -5
View File
@@ -1,23 +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.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual
{
internal class TestCaseSettings : OsuTestCase
public class TestCaseSettings : OsuTestCase
{
private readonly SettingsOverlay settings;
private readonly DialogOverlay dialogOverlay;
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent);
public TestCaseSettings()
{
Children = new[] { settings = new MainSettings() };
settings = new MainSettings
{
State = Visibility.Visible
};
Add(dialogOverlay = new DialogOverlay
{
Depth = -1
});
}
protected override void LoadComplete()
[BackgroundDependencyLoader]
private void load()
{
base.LoadComplete();
settings.ToggleVisibility();
dependencies.Cache(dialogOverlay);
Add(settings);
}
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{
internal class TestCaseSkipButton : OsuTestCase
public class TestCaseSkipButton : OsuTestCase
{
protected override void LoadComplete()
{
@@ -10,7 +10,7 @@ using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{
internal class TestCaseSongProgress : OsuTestCase
public class TestCaseSongProgress : OsuTestCase
{
private readonly SongProgress progress;
private readonly SongProgressGraph graph;
+1 -1
View File
@@ -14,7 +14,7 @@ using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
{
internal class TestCaseStoryboard : OsuTestCase
public class TestCaseStoryboard : OsuTestCase
{
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
+1 -1
View File
@@ -10,7 +10,7 @@ using OpenTK;
namespace osu.Game.Tests.Visual
{
internal class TestCaseTextAwesome : OsuTestCase
public class TestCaseTextAwesome : OsuTestCase
{
public TestCaseTextAwesome()
{
@@ -7,7 +7,7 @@ using osu.Game.Graphics.UserInterface;
namespace osu.Game.Tests.Visual
{
[Description("mostly back button")]
internal class TestCaseTwoLayerButton : OsuTestCase
public class TestCaseTwoLayerButton : OsuTestCase
{
public TestCaseTwoLayerButton()
{
+1 -1
View File
@@ -8,7 +8,7 @@ using OpenTK;
namespace osu.Game.Tests.Visual
{
internal class TestCaseUserPanel : OsuTestCase
public class TestCaseUserPanel : OsuTestCase
{
public TestCaseUserPanel()
{
+1 -1
View File
@@ -8,7 +8,7 @@ using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
internal class TestCaseUserProfile : OsuTestCase
public class TestCaseUserProfile : OsuTestCase
{
public TestCaseUserProfile()
{
+2 -2
View File
@@ -13,9 +13,9 @@ using System.Collections.Generic;
namespace osu.Game.Tests.Visual
{
internal class TestCaseUserRanks : OsuTestCase
public class TestCaseUserRanks : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(DrawableScore), typeof(RanksSection) };
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(DrawableProfileScore), typeof(RanksSection) };
public TestCaseUserRanks()
{
+1 -1
View File
@@ -15,7 +15,7 @@ using osu.Game.Screens.Edit.Screens.Compose.Timeline;
namespace osu.Game.Tests.Visual
{
internal class TestCaseWaveform : OsuTestCase
public class TestCaseWaveform : OsuTestCase
{
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
+5 -1
View File
@@ -88,8 +88,10 @@
<Compile Include="Beatmaps\IO\ImportBeatmapTest.cs" />
<Compile Include="Resources\Resource.cs" />
<Compile Include="Beatmaps\Formats\LegacyBeatmapDecoderTest.cs" />
<Compile Include="Visual\TestCaseBeatmapCarousel.cs" />
<Compile Include="Visual\TestCaseBeatmapDetailArea.cs" />
<Compile Include="Visual\TestCaseBeatmapDetails.cs" />
<Compile Include="Visual\TestCaseBeatmapInfoWedge.cs" />
<Compile Include="Visual\TestCaseBeatmapOptionsOverlay.cs" />
<Compile Include="Visual\TestCaseBeatmapScoresContainer.cs" />
<Compile Include="Visual\TestCaseBeatmapSetOverlay.cs" />
@@ -108,8 +110,10 @@
<Compile Include="Visual\TestCaseEditorComposeTimeline.cs" />
<Compile Include="Visual\TestCaseEditorMenuBar.cs" />
<Compile Include="Visual\TestCaseEditorSummaryTimeline.cs" />
<Compile Include="Visual\TestCaseEditorSelectionLayer.cs" />
<Compile Include="Visual\TestCaseGamefield.cs" />
<Compile Include="Visual\TestCaseGraph.cs" />
<Compile Include="Visual\TestCaseHistoricalSection.cs" />
<Compile Include="Visual\TestCaseIconButton.cs" />
<Compile Include="Visual\TestCaseIntroSequence.cs" />
<Compile Include="Visual\TestCaseKeyConfiguration.cs" />
@@ -117,7 +121,7 @@
<Compile Include="Visual\TestCaseLeaderboard.cs" />
<Compile Include="Visual\TestCaseMedalOverlay.cs" />
<Compile Include="Visual\TestCaseButtonSystem.cs" />
<Compile Include="Visual\TestCaseMenuOverlays.cs" />
<Compile Include="Visual\TestCaseGameplayMenuOverlay.cs" />
<Compile Include="Visual\TestCaseMods.cs" />
<Compile Include="Visual\TestCaseMusicController.cs" />
<Compile Include="Visual\TestCaseNotificationOverlay.cs" />
+3
View File
@@ -115,8 +115,11 @@ namespace osu.Game.Beatmaps
// Metadata
public string Version { get; set; }
[JsonProperty("difficulty_rating")]
public double StarDifficulty { get; set; }
public override string ToString() => $"{Metadata} [{Version}]";
public bool Equals(BeatmapInfo other)
{
if (ID == 0 || other?.ID == 0)
+78 -16
View File
@@ -134,6 +134,7 @@ namespace osu.Game.Beatmaps
var notification = new ProgressNotification
{
Text = "Beatmap import is initialising...",
CompletionText = "Import successful!",
Progress = 0,
State = ProgressNotificationState.Active,
};
@@ -245,8 +246,9 @@ namespace osu.Game.Beatmaps
return;
}
ProgressNotification downloadNotification = new ProgressNotification
var downloadNotification = new ProgressNotification
{
CompletionText = $"Imported {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}!",
Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
};
@@ -339,6 +341,61 @@ namespace osu.Game.Beatmaps
}
}
public void UndeleteAll()
{
var deleteMaps = QueryBeatmapSets(bs => bs.DeletePending).ToList();
if (!deleteMaps.Any()) return;
var notification = new ProgressNotification
{
CompletionText = "Restored all deleted beatmaps!",
Progress = 0,
State = ProgressNotificationState.Active,
};
PostNotification?.Invoke(notification);
int i = 0;
foreach (var bs in deleteMaps)
{
if (notification.State == ProgressNotificationState.Cancelled)
// user requested abort
return;
notification.Text = $"Restoring ({i} of {deleteMaps.Count})";
notification.Progress = (float)++i / deleteMaps.Count;
Undelete(bs);
}
notification.State = ProgressNotificationState.Completed;
}
public void Undelete(BeatmapSetInfo beatmapSet)
{
if (beatmapSet.Protected)
return;
lock (importContext)
{
var context = importContext.Value;
using (var transaction = context.BeginTransaction())
{
context.ChangeTracker.AutoDetectChangesEnabled = false;
var iFiles = new FileStore(() => context, storage);
var iBeatmaps = createBeatmapStore(() => context);
undelete(iBeatmaps, iFiles, beatmapSet);
context.ChangeTracker.AutoDetectChangesEnabled = true;
context.SaveChanges(transaction);
}
}
}
/// <summary>
/// Delete a beatmap difficulty.
/// </summary>
@@ -374,12 +431,9 @@ namespace osu.Game.Beatmaps
/// <returns>A <see cref="WorkingBeatmap"/> instance correlating to the provided <see cref="BeatmapInfo"/>.</returns>
public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null)
{
if (beatmapInfo == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo)
if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo)
return DefaultBeatmap;
if (beatmapInfo.BeatmapSet == null)
throw new InvalidOperationException($@"Beatmap set {beatmapInfo.BeatmapSetInfoID} is not in the local database.");
if (beatmapInfo.Metadata == null)
beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata;
@@ -497,15 +551,21 @@ namespace osu.Game.Beatmaps
using (var stream = new StreamReader(reader.GetStream(mapName)))
metadata = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata;
// check if a set already exists with the same online id.
beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID) ?? new BeatmapSetInfo
{
OnlineBeatmapSetID = metadata.OnlineBeatmapSetID,
Beatmaps = new List<BeatmapInfo>(),
Hash = hash,
Files = fileInfos,
Metadata = metadata
};
if (metadata.OnlineBeatmapSetID != null)
beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID);
if (beatmapSet == null)
beatmapSet = new BeatmapSetInfo
{
OnlineBeatmapSetID = metadata.OnlineBeatmapSetID,
Beatmaps = new List<BeatmapInfo>(),
Hash = hash,
Files = fileInfos,
Metadata = metadata
};
var mapNames = reader.Filenames.Where(f => f.EndsWith(".osu"));
@@ -525,12 +585,13 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID);
var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || beatmap.BeatmapInfo.OnlineBeatmapID != null && b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID);
if (existing == null)
{
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary
beatmap.BeatmapInfo.Metadata = null;
// Exclude beatmap-metadata if it's equal to beatmapset-metadata
if (metadata.Equals(beatmap.Metadata))
beatmap.BeatmapInfo.Metadata = null;
RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
@@ -661,6 +722,7 @@ namespace osu.Game.Beatmaps
var notification = new ProgressNotification
{
Progress = 0,
CompletionText = "Deleted all beatmaps!",
State = ProgressNotificationState.Active,
};
+22 -1
View File
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
@@ -9,7 +10,7 @@ using osu.Game.Users;
namespace osu.Game.Beatmaps
{
public class BeatmapMetadata
public class BeatmapMetadata : IEquatable<BeatmapMetadata>
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
@@ -56,6 +57,8 @@ namespace osu.Game.Beatmaps
public string AudioFile { get; set; }
public string BackgroundFile { get; set; }
public override string ToString() => $"{Artist} - {Title} ({Author})";
public string[] SearchableTerms => new[]
{
Author?.Username,
@@ -66,5 +69,23 @@ namespace osu.Game.Beatmaps
Source,
Tags
}.Where(s => !string.IsNullOrEmpty(s)).ToArray();
public bool Equals(BeatmapMetadata other)
{
if (other == null)
return false;
return onlineBeatmapSetID == other.onlineBeatmapSetID
&& Title == other.Title
&& TitleUnicode == other.TitleUnicode
&& Artist == other.Artist
&& ArtistUnicode == other.ArtistUnicode
&& AuthorString == other.AuthorString
&& Source == other.Source
&& Tags == other.Tags
&& PreviewTime == other.PreviewTime
&& AudioFile == other.AudioFile
&& BackgroundFile == other.BackgroundFile;
}
}
}
+2
View File
@@ -33,6 +33,8 @@ namespace osu.Game.Beatmaps
public List<BeatmapSetFileInfo> Files { get; set; }
public override string ToString() => Metadata.ToString();
public bool Protected { get; set; }
}
}
+12
View File
@@ -32,6 +32,18 @@ namespace osu.Game.Beatmaps
{
var context = GetContext();
foreach (var beatmap in beatmapSet.Beatmaps.Where(b => b.Metadata != null))
{
// If we detect a new metadata object it'll be attached to the current context so it can be reused
// to prevent duplicate entries when persisting. To accomplish this we look in the cache (.Local)
// of the corresponding table (.Set<BeatmapMetadata>()) for matching entries to our criteria.
var contextMetadata = context.Set<BeatmapMetadata>().Local.SingleOrDefault(e => e.Equals(beatmap.Metadata));
if (contextMetadata != null)
beatmap.Metadata = contextMetadata;
else
context.BeatmapMetadata.Attach(beatmap.Metadata);
}
context.BeatmapSetInfo.Attach(beatmapSet);
context.SaveChanges();
-138
View File
@@ -1,138 +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 System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework;
using osu.Framework.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
public class BeatmapGroup : IStateful<BeatmapGroupState>
{
public event Action<BeatmapGroupState> StateChanged;
public BeatmapPanel SelectedPanel;
/// <summary>
/// Fires when one of our difficulties was selected. Will fire on first expand.
/// </summary>
public Action<BeatmapGroup, BeatmapPanel> SelectionChanged;
/// <summary>
/// Fires when one of our difficulties is clicked when already selected. Should start playing the map.
/// </summary>
public Action<BeatmapInfo> StartRequested;
public Action<BeatmapSetInfo> DeleteRequested;
public Action<BeatmapSetInfo> RestoreHiddenRequested;
public Action<BeatmapInfo> HideDifficultyRequested;
public Action<BeatmapInfo> EditRequested;
public BeatmapSetHeader Header;
public List<BeatmapPanel> BeatmapPanels;
public BeatmapSetInfo BeatmapSet;
private BeatmapGroupState state;
public BeatmapGroupState State
{
get { return state; }
set
{
state = value;
switch (value)
{
case BeatmapGroupState.Expanded:
Header.State = PanelSelectedState.Selected;
foreach (BeatmapPanel panel in BeatmapPanels)
panel.State = panel == SelectedPanel ? PanelSelectedState.Selected : PanelSelectedState.NotSelected;
break;
case BeatmapGroupState.Collapsed:
Header.State = PanelSelectedState.NotSelected;
foreach (BeatmapPanel panel in BeatmapPanels)
panel.State = PanelSelectedState.Hidden;
break;
case BeatmapGroupState.Hidden:
Header.State = PanelSelectedState.Hidden;
foreach (BeatmapPanel panel in BeatmapPanels)
panel.State = PanelSelectedState.Hidden;
break;
}
StateChanged?.Invoke(state);
}
}
public BeatmapGroup(BeatmapSetInfo beatmapSet, BeatmapManager manager)
{
if (beatmapSet == null)
throw new ArgumentNullException(nameof(beatmapSet));
if (manager == null)
throw new ArgumentNullException(nameof(manager));
BeatmapSet = beatmapSet;
WorkingBeatmap beatmap = manager.GetWorkingBeatmap(BeatmapSet.Beatmaps.FirstOrDefault());
Header = new BeatmapSetHeader(beatmap)
{
GainedSelection = headerGainedSelection,
DeleteRequested = b => DeleteRequested(b),
RestoreHiddenRequested = b => RestoreHiddenRequested(b),
RelativeSizeAxes = Axes.X,
};
BeatmapPanels = BeatmapSet.Beatmaps.Where(b => !b.Hidden).OrderBy(b => b.StarDifficulty).Select(b => new BeatmapPanel(b)
{
Alpha = 0,
GainedSelection = panelGainedSelection,
HideRequested = p => HideDifficultyRequested?.Invoke(p),
StartRequested = p => StartRequested?.Invoke(p.Beatmap),
EditRequested = p => EditRequested?.Invoke(p.Beatmap),
RelativeSizeAxes = Axes.X,
}).ToList();
Header.AddDifficultyIcons(BeatmapPanels);
}
private void headerGainedSelection(BeatmapSetHeader panel)
{
State = BeatmapGroupState.Expanded;
//we want to make sure one of our children is selected in the case none have been selected yet.
if (SelectedPanel == null)
BeatmapPanels.First().State = PanelSelectedState.Selected;
}
private void panelGainedSelection(BeatmapPanel panel)
{
try
{
if (SelectedPanel == panel) return;
if (SelectedPanel != null)
SelectedPanel.State = PanelSelectedState.NotSelected;
SelectedPanel = panel;
}
finally
{
State = BeatmapGroupState.Expanded;
SelectionChanged?.Invoke(this, SelectedPanel);
}
}
}
public enum BeatmapGroupState
{
Collapsed,
Expanded,
Hidden,
}
}
+25 -2
View File
@@ -11,21 +11,44 @@ namespace osu.Game.Beatmaps.Drawables
public class BeatmapSetCover : Sprite
{
private readonly BeatmapSetInfo set;
public BeatmapSetCover(BeatmapSetInfo set)
private readonly BeatmapSetCoverType type;
public BeatmapSetCover(BeatmapSetInfo set, BeatmapSetCoverType type = BeatmapSetCoverType.Cover)
{
if (set == null)
throw new ArgumentNullException(nameof(set));
this.set = set;
this.type = type;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
string resource = set.OnlineInfo.Covers.Cover;
string resource = null;
switch (type)
{
case BeatmapSetCoverType.Cover:
resource = set.OnlineInfo.Covers.Cover;
break;
case BeatmapSetCoverType.Card:
resource = set.OnlineInfo.Covers.Card;
break;
case BeatmapSetCoverType.List:
resource = set.OnlineInfo.Covers.List;
break;
}
if (resource != null)
Texture = textures.Get(resource);
}
}
public enum BeatmapSetCoverType
{
Cover,
Card,
List,
}
}
+2
View File
@@ -63,6 +63,8 @@ namespace osu.Game.Beatmaps
public override string Description => "dummy";
public override string ShortName => "dummy";
public DummyRuleset(RulesetInfo rulesetInfo)
: base(rulesetInfo)
{
+2 -2
View File
@@ -20,7 +20,7 @@ namespace osu.Game.Configuration
Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1);
Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10, 0.1);
Set(OsuSetting.SelectionRandomType, SelectionRandomType.RandomPermutation);
Set(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation);
Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1);
@@ -108,7 +108,7 @@ namespace osu.Game.Configuration
SaveUsername,
DisplayStarsMinimum,
DisplayStarsMaximum,
SelectionRandomType,
RandomSelectAlgorithm,
SnakingInSliders,
SnakingOutSliders,
ShowFpsDisplay,
@@ -5,11 +5,11 @@ using System.ComponentModel;
namespace osu.Game.Configuration
{
public enum SelectionRandomType
public enum RandomSelectAlgorithm
{
[Description("Never repeat")]
RandomPermutation,
[Description("Random")]
Random
}
}
}
+1
View File
@@ -93,6 +93,7 @@ namespace osu.Game.Database
modelBuilder.Entity<FileInfo>().HasIndex(b => b.ReferenceCount);
modelBuilder.Entity<RulesetInfo>().HasIndex(b => b.Available);
modelBuilder.Entity<RulesetInfo>().HasIndex(b => b.ShortName).IsUnique();
modelBuilder.Entity<BeatmapInfo>().HasOne(b => b.BaseDifficulty);
}
+15 -14
View File
@@ -15,14 +15,19 @@ namespace osu.Game.Graphics
{
public class SpriteIcon : CompositeDrawable
{
private readonly Sprite spriteShadow;
private readonly Sprite spriteMain;
private Sprite spriteShadow;
private Sprite spriteMain;
private Cached layout = new Cached();
private readonly Container shadowVisibility;
private Container shadowVisibility;
public SpriteIcon()
private FontStore store;
[BackgroundDependencyLoader]
private void load(FontStore store)
{
this.store = store;
InternalChildren = new Drawable[]
{
shadowVisibility = new Container
@@ -39,7 +44,7 @@ namespace osu.Game.Graphics
Y = 2,
Colour = new Color4(0f, 0f, 0f, 0.2f),
},
Alpha = 0,
Alpha = shadow ? 1 : 0,
},
spriteMain = new Sprite
{
@@ -49,14 +54,7 @@ namespace osu.Game.Graphics
FillMode = FillMode.Fit
},
};
}
private FontStore store;
[BackgroundDependencyLoader]
private void load(FontStore store)
{
this.store = store;
updateTexture();
}
@@ -105,12 +103,15 @@ namespace osu.Game.Graphics
}
}
private bool shadow;
public bool Shadow
{
get { return spriteShadow.IsPresent; }
get { return shadow; }
set
{
shadowVisibility.Alpha = value ? 1 : 0;
shadow = value;
if (shadowVisibility != null)
shadowVisibility.Alpha = value ? 1 : 0;
}
}
+132 -121
View File
@@ -12,6 +12,8 @@ using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Graphics.Containers;
using osu.Framework.Configuration;
using osu.Framework.Input;
namespace osu.Game.Graphics.UserInterface
{
@@ -22,62 +24,7 @@ namespace osu.Game.Graphics.UserInterface
private const float glow_fade_duration = 250;
private const float click_duration = 200;
private Color4 buttonColour;
public Color4 ButtonColour
{
get
{
return buttonColour;
}
set
{
buttonColour = value;
updateGlow();
colourContainer.Colour = value;
}
}
private Color4 backgroundColour = OsuColour.Gray(34);
public Color4 BackgroundColour
{
get
{
return backgroundColour;
}
set
{
backgroundColour = value;
background.Colour = value;
}
}
private string text;
public string Text
{
get
{
return text;
}
set
{
text = value;
spriteText.Text = Text;
}
}
private float textSize = 28;
public float TextSize
{
get
{
return textSize;
}
set
{
textSize = value;
spriteText.TextSize = value;
}
}
public readonly BindableBool Selected = new BindableBool();
private readonly Container backgroundContainer;
private readonly Container colourContainer;
@@ -89,71 +36,6 @@ namespace osu.Game.Graphics.UserInterface
private readonly SpriteText spriteText;
private Vector2 hoverSpacing => new Vector2(3f, 0f);
private bool didClick; // Used for making sure that the OnMouseDown animation can call instead of OnHoverLost's when clicking
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceiveMouseInputAt(screenSpacePos);
protected override bool OnClick(Framework.Input.InputState state)
{
didClick = true;
colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In);
flash();
this.Delay(click_duration).Schedule(delegate
{
colourContainer.ResizeTo(new Vector2(0.8f, 1f));
spriteText.Spacing = Vector2.Zero;
glowContainer.FadeOut();
});
return base.OnClick(state);
}
protected override bool OnHover(Framework.Input.InputState state)
{
spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic);
colourContainer.ResizeTo(new Vector2(hover_width, 1f), hover_duration, Easing.OutElastic);
glowContainer.FadeIn(glow_fade_duration, Easing.Out);
base.OnHover(state);
return true;
}
protected override void OnHoverLost(Framework.Input.InputState state)
{
if (!didClick)
{
colourContainer.ResizeTo(new Vector2(0.8f, 1f), hover_duration, Easing.OutElastic);
spriteText.TransformSpacingTo(Vector2.Zero, hover_duration, Easing.OutElastic);
glowContainer.FadeOut(glow_fade_duration, Easing.Out);
}
didClick = false;
}
private void flash()
{
var flash = new Box
{
RelativeSizeAxes = Axes.Both
};
colourContainer.Add(flash);
flash.Colour = ButtonColour;
flash.Blending = BlendingMode.Additive;
flash.Alpha = 0.3f;
flash.FadeOutFromOne(click_duration);
flash.Expire();
}
private void updateGlow()
{
leftGlow.Colour = ColourInfo.GradientHorizontal(new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f), ButtonColour);
centerGlow.Colour = ButtonColour;
rightGlow.Colour = ColourInfo.GradientHorizontal(ButtonColour, new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f));
}
public DialogButton()
{
RelativeSizeAxes = Axes.X;
@@ -268,6 +150,135 @@ namespace osu.Game.Graphics.UserInterface
};
updateGlow();
Selected.ValueChanged += selectionChanged;
}
private Color4 buttonColour;
public Color4 ButtonColour
{
get
{
return buttonColour;
}
set
{
buttonColour = value;
updateGlow();
colourContainer.Colour = value;
}
}
private Color4 backgroundColour = OsuColour.Gray(34);
public Color4 BackgroundColour
{
get
{
return backgroundColour;
}
set
{
backgroundColour = value;
background.Colour = value;
}
}
private string text;
public string Text
{
get
{
return text;
}
set
{
text = value;
spriteText.Text = Text;
}
}
private float textSize = 28;
public float TextSize
{
get
{
return textSize;
}
set
{
textSize = value;
spriteText.TextSize = value;
}
}
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceiveMouseInputAt(screenSpacePos);
protected override bool OnClick(InputState state)
{
colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In);
flash();
this.Delay(click_duration).Schedule(delegate
{
colourContainer.ResizeTo(new Vector2(0.8f, 1f));
spriteText.Spacing = Vector2.Zero;
glowContainer.FadeOut();
});
return base.OnClick(state);
}
protected override bool OnHover(InputState state)
{
base.OnHover(state);
Selected.Value = true;
return true;
}
protected override void OnHoverLost(InputState state)
{
base.OnHoverLost(state);
Selected.Value = false;
}
private void selectionChanged(bool isSelected)
{
if (isSelected)
{
spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic);
colourContainer.ResizeTo(new Vector2(hover_width, 1f), hover_duration, Easing.OutElastic);
glowContainer.FadeIn(glow_fade_duration, Easing.Out);
}
else
{
colourContainer.ResizeTo(new Vector2(0.8f, 1f), hover_duration, Easing.OutElastic);
spriteText.TransformSpacingTo(Vector2.Zero, hover_duration, Easing.OutElastic);
glowContainer.FadeOut(glow_fade_duration, Easing.Out);
}
}
private void flash()
{
var flash = new Box
{
RelativeSizeAxes = Axes.Both
};
colourContainer.Add(flash);
flash.Colour = ButtonColour;
flash.Blending = BlendingMode.Additive;
flash.Alpha = 0.3f;
flash.FadeOutFromOne(click_duration);
flash.Expire();
}
private void updateGlow()
{
leftGlow.Colour = ColourInfo.GradientHorizontal(new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f), ButtonColour);
centerGlow.Colour = ButtonColour;
rightGlow.Colour = ColourInfo.GradientHorizontal(ButtonColour, new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f));
}
}
}
+7 -16
View File
@@ -6,6 +6,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
using System;
using System.Linq;
namespace osu.Game.Graphics.UserInterface
{
@@ -72,16 +73,9 @@ namespace osu.Game.Graphics.UserInterface
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(star_spacing),
ChildrenEnumerable = Enumerable.Range(0, StarCount).Select(i => new Star { Alpha = minStarAlpha })
}
};
for (int i = 0; i < StarCount; i++)
{
stars.Add(new Star
{
Alpha = minStarAlpha,
});
}
}
protected override void LoadComplete()
@@ -147,15 +141,12 @@ namespace osu.Game.Graphics.UserInterface
{
Size = new Vector2(star_size);
Children = new[]
Child = Icon = new SpriteIcon
{
Icon = new SpriteIcon
{
Size = new Vector2(star_size),
Icon = FontAwesome.fa_star,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
Size = new Vector2(star_size),
Icon = FontAwesome.fa_star,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
}
}
@@ -5,11 +5,12 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
namespace osu.Game.Input.Bindings
{
public class GlobalKeyBindingInputManager : DatabasedKeyBindingInputManager<GlobalAction>
public class GlobalKeyBindingInputManager : DatabasedKeyBindingInputManager<GlobalAction>, IHandleGlobalInput
{
private readonly Drawable handler;
@@ -0,0 +1,307 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using osu.Game.Database;
using System;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20171209034410_AddRulesetInfoShortName")]
partial class AddRulesetInfoShortName
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<float>("SliderMultiplier");
b.Property<float>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("AudioLeadIn");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MD5Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.Property<string>("ShortName");
b.HasKey("ID");
b.HasIndex("Available");
b.HasIndex("ShortName")
.IsUnique();
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace osu.Game.Migrations
{
public partial class AddRulesetInfoShortName : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "ShortName",
table: "RulesetInfo",
type: "TEXT",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_RulesetInfo_ShortName",
table: "RulesetInfo",
column: "ShortName",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_RulesetInfo_ShortName",
table: "RulesetInfo");
migrationBuilder.DropColumn(
name: "ShortName",
table: "RulesetInfo");
}
}
}
@@ -247,10 +247,15 @@ namespace osu.Game.Migrations
b.Property<string>("Name");
b.Property<string>("ShortName");
b.HasKey("ID");
b.HasIndex("Available");
b.HasIndex("ShortName")
.IsUnique();
b.ToTable("RulesetInfo");
});
@@ -10,7 +10,7 @@ using System;
namespace osu.Game.Online.API.Requests
{
public class GetBeatmapSetsResponse : BeatmapMetadata // todo: this is a bit wrong...
public class APIResponseBeatmapSet : BeatmapMetadata // todo: this is a bit wrong...
{
[JsonProperty(@"covers")]
private BeatmapSetOnlineCovers covers { get; set; }
@@ -45,7 +45,7 @@ namespace osu.Game.Online.API.Requests
}
[JsonProperty(@"beatmaps")]
private IEnumerable<GetBeatmapSetsBeatmapResponse> beatmaps { get; set; }
private IEnumerable<APIResponseBeatmap> beatmaps { get; set; }
public BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets)
{
@@ -65,11 +65,11 @@ namespace osu.Game.Online.API.Requests
Ranked = ranked,
LastUpdated = lastUpdated,
},
Beatmaps = beatmaps.Select(b => b.ToBeatmap(rulesets)).ToList(),
Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(),
};
}
private class GetBeatmapSetsBeatmapResponse : BeatmapMetadata
private class APIResponseBeatmap : BeatmapMetadata
{
[JsonProperty(@"id")]
private int onlineBeatmapID { get; set; }
@@ -3,7 +3,7 @@
namespace osu.Game.Online.API.Requests
{
public class GetBeatmapSetRequest : APIRequest<GetBeatmapSetsResponse>
public class GetBeatmapSetRequest : APIRequest<APIResponseBeatmapSet>
{
private readonly int beatmapSetId;
@@ -10,19 +10,28 @@ using osu.Game.Rulesets;
using osu.Game.Users;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osu.Framework.IO.Network;
namespace osu.Game.Online.API.Requests
{
public class GetScoresRequest : APIRequest<GetScoresResponse>
{
private readonly BeatmapInfo beatmap;
private readonly LeaderboardScope scope;
private readonly RulesetInfo ruleset;
public GetScoresRequest(BeatmapInfo beatmap)
public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, LeaderboardScope scope = LeaderboardScope.Global)
{
if (!beatmap.OnlineBeatmapID.HasValue)
throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}.");
if (scope == LeaderboardScope.Local)
throw new InvalidOperationException("Should not attempt to request online scores for a local scoped leaderboard");
this.beatmap = beatmap;
this.scope = scope;
this.ruleset = ruleset ?? throw new ArgumentNullException(nameof(ruleset));
Success += onSuccess;
}
@@ -33,6 +42,17 @@ namespace osu.Game.Online.API.Requests
score.ApplyBeatmap(beatmap);
}
protected override WebRequest CreateWebRequest()
{
var req = base.CreateWebRequest();
req.Timeout = 30000;
req.AddParameter(@"type", scope.ToString().ToLowerInvariant());
req.AddParameter(@"mode", ruleset.ShortName);
return req;
}
protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores";
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace osu.Game.Online.API.Requests
{
public class GetUserBeatmapsRequest : APIRequest<List<GetBeatmapSetsResponse>>
public class GetUserBeatmapsRequest : APIRequest<List<APIResponseBeatmapSet>>
{
private readonly long userId;
private readonly int offset;
@@ -24,7 +24,6 @@ namespace osu.Game.Online.API.Requests
public enum BeatmapSetType
{
MostPlayed,
Favourite,
RankedAndApproved,
Unranked,
@@ -0,0 +1,48 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using System.Collections.Generic;
namespace osu.Game.Online.API.Requests
{
public class GetUserMostPlayedBeatmapsRequest : APIRequest<List<MostPlayedBeatmap>>
{
private readonly long userId;
private readonly int offset;
public GetUserMostPlayedBeatmapsRequest(long userId, int offset = 0)
{
this.userId = userId;
this.offset = offset;
}
protected override string Target => $@"users/{userId}/beatmapsets/most_played?offset={offset}";
}
public class MostPlayedBeatmap
{
[JsonProperty("beatmap_id")]
public int BeatmapID;
[JsonProperty("count")]
public int PlayCount;
[JsonProperty]
private BeatmapInfo beatmap;
[JsonProperty]
private APIResponseBeatmapSet beatmapSet;
public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets)
{
BeatmapSetInfo setInfo = beatmapSet.ToBeatmapSet(rulesets);
beatmap.BeatmapSet = setInfo;
beatmap.OnlineBeatmapSetID = setInfo.OnlineBeatmapSetID;
beatmap.Metadata = setInfo.Metadata;
return beatmap;
}
}
}
@@ -9,7 +9,7 @@ using osu.Game.Rulesets;
namespace osu.Game.Online.API.Requests
{
public class SearchBeatmapSetsRequest : APIRequest<IEnumerable<GetBeatmapSetsResponse>>
public class SearchBeatmapSetsRequest : APIRequest<IEnumerable<APIResponseBeatmapSet>>
{
private readonly string query;
private readonly RulesetInfo ruleset;
+1 -1
View File
@@ -104,7 +104,7 @@ namespace osu.Game.Overlays
scores.IsLoading = true;
getScoresRequest = new GetScoresRequest(beatmap);
getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
getScoresRequest.Success += r =>
{
scores.Scores = r.Scores;
+1 -1
View File
@@ -117,7 +117,7 @@ namespace osu.Game.Overlays.Direct
{
base.Update();
if (PreviewPlaying && Preview != null)
if (PreviewPlaying && Preview != null && Preview.IsLoaded)
{
PreviewBar.Width = (float)(Preview.CurrentTime / Preview.Length);
}
@@ -105,6 +105,7 @@ namespace osu.Game.Overlays.Direct
public enum DirectSortCriteria
{
Relevance,
Title,
Artist,
Creator,
+22 -4
View File
@@ -43,6 +43,7 @@ namespace osu.Game.Overlays
protected override SearchableListFilterControl<DirectSortCriteria, RankStatus> CreateFilterControl() => new FilterControl();
private IEnumerable<BeatmapSetInfo> beatmapSets;
public IEnumerable<BeatmapSetInfo> BeatmapSets
{
get { return beatmapSets; }
@@ -69,6 +70,7 @@ namespace osu.Game.Overlays
}
private ResultCounts resultAmounts;
public ResultCounts ResultAmounts
{
get { return resultAmounts; }
@@ -115,7 +117,23 @@ namespace osu.Game.Overlays
},
};
Filter.Search.Current.ValueChanged += text => { if (text != string.Empty) Header.Tabs.Current.Value = DirectTab.Search; };
Filter.Search.Current.ValueChanged += text =>
{
if (text != string.Empty)
{
Header.Tabs.Current.Value = DirectTab.Search;
if (Filter.Tabs.Current.Value == DirectSortCriteria.Ranked)
Filter.Tabs.Current.Value = DirectSortCriteria.Relevance;
}
else
{
Header.Tabs.Current.Value = DirectTab.NewestMaps;
if (Filter.Tabs.Current.Value == DirectSortCriteria.Relevance)
Filter.Tabs.Current.Value = DirectSortCriteria.Ranked;
}
};
((FilterControl)Filter).Ruleset.ValueChanged += ruleset => Scheduler.AddOnce(updateSearch);
Filter.DisplayStyleControl.DisplayStyle.ValueChanged += recreatePanels;
Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += rankStatus => Scheduler.AddOnce(updateSearch);
@@ -271,9 +289,9 @@ namespace osu.Game.Overlays
if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty)) return;
getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value ?? string.Empty,
((FilterControl)Filter).Ruleset.Value,
Filter.DisplayStyleControl.Dropdown.Current.Value,
Filter.Tabs.Current.Value); //todo: sort direction (?)
((FilterControl)Filter).Ruleset.Value,
Filter.DisplayStyleControl.Dropdown.Current.Value,
Filter.Tabs.Current.Value); //todo: sort direction (?)
getSetsRequest.Success += response =>
{
+61 -43
View File
@@ -17,22 +17,23 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Mods
{
public class ModSelectOverlay : WaveOverlayContainer
{
private const int button_duration = 700;
private const int ranked_multiplier_duration = 700;
private const float content_width = 0.8f;
private Color4 lowMultiplierColour, highMultiplierColour;
protected Color4 LowMultiplierColour, HighMultiplierColour;
private readonly OsuSpriteText rankedLabel;
private readonly OsuSpriteText multiplierLabel;
private readonly FillFlowContainer rankedMultiplerContainer;
protected readonly TriangleButton DeselectAllButton;
protected readonly OsuSpriteText MultiplierLabel;
private readonly FillFlowContainer footerContainer;
private readonly FillFlowContainer<ModSection> modSectionsContainer;
protected override bool BlockPassThroughKeyboard => false;
protected readonly FillFlowContainer<ModSection> ModSectionsContainer;
public readonly Bindable<IEnumerable<Mod>> SelectedMods = new Bindable<IEnumerable<Mod>>();
@@ -42,7 +43,7 @@ namespace osu.Game.Overlays.Mods
{
var instance = newRuleset.CreateInstance();
foreach (ModSection section in modSectionsContainer.Children)
foreach (ModSection section in ModSectionsContainer.Children)
section.Mods = instance.GetModsFor(section.ModType);
refreshSelectedMods();
}
@@ -50,8 +51,8 @@ namespace osu.Game.Overlays.Mods
[BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets)
{
lowMultiplierColour = colours.Red;
highMultiplierColour = colours.Green;
LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green;
if (osu != null)
Ruleset.BindTo(osu.Ruleset);
@@ -66,14 +67,14 @@ namespace osu.Game.Overlays.Mods
{
base.PopOut();
rankedMultiplerContainer.MoveToX(rankedMultiplerContainer.DrawSize.X, APPEAR_DURATION, Easing.InSine);
rankedMultiplerContainer.FadeOut(APPEAR_DURATION, Easing.InSine);
footerContainer.MoveToX(footerContainer.DrawSize.X, DISAPPEAR_DURATION, Easing.InSine);
footerContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine);
foreach (ModSection section in modSectionsContainer.Children)
foreach (ModSection section in ModSectionsContainer.Children)
{
section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), APPEAR_DURATION, Easing.InSine);
section.ButtonsContainer.MoveToX(100f, APPEAR_DURATION, Easing.InSine);
section.ButtonsContainer.FadeOut(APPEAR_DURATION, Easing.InSine);
section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), DISAPPEAR_DURATION, Easing.InSine);
section.ButtonsContainer.MoveToX(100f, DISAPPEAR_DURATION, Easing.InSine);
section.ButtonsContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine);
}
}
@@ -81,27 +82,28 @@ namespace osu.Game.Overlays.Mods
{
base.PopIn();
rankedMultiplerContainer.MoveToX(0, ranked_multiplier_duration, Easing.OutQuint);
rankedMultiplerContainer.FadeIn(ranked_multiplier_duration, Easing.OutQuint);
footerContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint);
footerContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint);
foreach (ModSection section in modSectionsContainer.Children)
foreach (ModSection section in ModSectionsContainer.Children)
{
section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), button_duration, Easing.OutQuint);
section.ButtonsContainer.MoveToX(0, button_duration, Easing.OutQuint);
section.ButtonsContainer.FadeIn(button_duration, Easing.OutQuint);
section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), APPEAR_DURATION, Easing.OutQuint);
section.ButtonsContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint);
section.ButtonsContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint);
}
}
public void DeselectAll()
{
foreach (ModSection section in modSectionsContainer.Children)
foreach (ModSection section in ModSectionsContainer.Children)
section.DeselectAll();
refreshSelectedMods();
}
public void DeselectTypes(Type[] modTypes)
{
if (modTypes.Length == 0) return;
foreach (ModSection section in modSectionsContainer.Children)
foreach (ModSection section in ModSectionsContainer.Children)
section.DeselectTypes(modTypes);
}
@@ -114,7 +116,7 @@ namespace osu.Game.Overlays.Mods
private void refreshSelectedMods()
{
SelectedMods.Value = modSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
double multiplier = 1.0;
bool ranked = true;
@@ -129,16 +131,16 @@ namespace osu.Game.Overlays.Mods
// 1.05x
// 1.20x
multiplierLabel.Text = $"{multiplier:N2}x";
string rankedString = ranked ? "Ranked" : "Unranked";
rankedLabel.Text = $@"{rankedString}, Score Multiplier: ";
MultiplierLabel.Text = $"{multiplier:N2}x";
if (!ranked)
MultiplierLabel.Text += " (Unranked)";
if (multiplier > 1.0)
multiplierLabel.FadeColour(highMultiplierColour, 200);
MultiplierLabel.FadeColour(HighMultiplierColour, 200);
else if (multiplier < 1.0)
multiplierLabel.FadeColour(lowMultiplierColour, 200);
MultiplierLabel.FadeColour(LowMultiplierColour, 200);
else
multiplierLabel.FadeColour(Color4.White, 200);
MultiplierLabel.FadeColour(Color4.White, 200);
}
public ModSelectOverlay()
@@ -232,7 +234,7 @@ namespace osu.Game.Overlays.Mods
},
new OsuSpriteText
{
Text = @"Others are just for fun",
Text = @"Others are just for fun.",
TextSize = 18,
Shadow = true,
},
@@ -241,7 +243,7 @@ namespace osu.Game.Overlays.Mods
},
},
// Body
modSectionsContainer = new FillFlowContainer<ModSection>
ModSectionsContainer = new FillFlowContainer<ModSection>
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
@@ -289,7 +291,7 @@ namespace osu.Game.Overlays.Mods
Colour = new Color4(172, 20, 116, 255),
Alpha = 0.5f,
},
rankedMultiplerContainer = new FillFlowContainer
footerContainer = new FillFlowContainer
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
@@ -299,26 +301,42 @@ namespace osu.Game.Overlays.Mods
Direction = FillDirection.Horizontal,
Padding = new MarginPadding
{
Top = 20,
Bottom = 20,
Vertical = 15
},
Children = new Drawable[]
{
rankedLabel = new OsuSpriteText
DeselectAllButton = new TriangleButton
{
Text = @"Ranked, Score Multiplier: ",
Width = 180,
Text = "Deselect All",
Action = DeselectAll,
Margin = new MarginPadding
{
Right = 20
}
},
new OsuSpriteText
{
Text = @"Score Multiplier: ",
TextSize = 30,
Shadow = true,
Margin = new MarginPadding
{
Top = 5
}
},
multiplierLabel = new OsuSpriteText
MultiplierLabel = new OsuSpriteText
{
Font = @"Exo2.0-Bold",
Text = @"1.00x",
TextSize = 30,
Shadow = true,
},
},
},
Margin = new MarginPadding
{
Top = 5
}
}
}
}
},
},
},
@@ -13,7 +13,6 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
using System.Threading;
namespace osu.Game.Overlays.Music
{
@@ -153,11 +152,6 @@ namespace osu.Game.Overlays.Music
var track = beatmapBacking.Value.Track;
track.Restart();
// this is temporary until we have blocking (async.Wait()) audio component methods.
// then we can call RestartAsync().Wait() or the blocking version above.
while (!track.IsRunning)
Thread.Sleep(1);
}
}
+1 -1
View File
@@ -251,7 +251,7 @@ namespace osu.Game.Overlays
playButton.Icon = track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o;
if (track.HasCompleted && !beatmapBacking.Disabled && playlist.BeatmapSets.Any())
if (track.HasCompleted && !track.Looping && !beatmapBacking.Disabled && playlist.BeatmapSets.Any())
next();
}
else
@@ -22,6 +22,8 @@ namespace osu.Game.Overlays.Notifications
}
}
public string CompletionText { get; set; } = "Task has completed!";
public float Progress
{
get { return progressBar.Progress; }
@@ -87,7 +89,7 @@ namespace osu.Game.Overlays.Notifications
protected virtual Notification CreateCompletionNotification() => new ProgressCompletionNotification
{
Activated = CompletionClickAction,
Text = "Task has completed!"
Text = CompletionText
};
protected virtual void Completed()
+4 -4
View File
@@ -53,6 +53,7 @@ namespace osu.Game.Overlays.Profile
{
RelativeSizeAxes = Axes.X,
Height = cover_height,
Masking = true,
Children = new Drawable[]
{
new Box
@@ -324,8 +325,7 @@ namespace osu.Game.Overlays.Profile
FillMode = FillMode.Fill,
OnLoadComplete = d => d.FadeInFromZero(200),
Depth = float.MaxValue,
},
coverContainer.Add);
}, coverContainer.Add);
if (user.IsSupporter) supporterTag.Show();
@@ -382,7 +382,7 @@ namespace osu.Game.Overlays.Profile
}
tryAddInfoRightLine(FontAwesome.fa_map_marker, user.Location);
tryAddInfoRightLine(FontAwesome.fa_heart_o, user.Intrerests);
tryAddInfoRightLine(FontAwesome.fa_heart_o, user.Interests);
tryAddInfoRightLine(FontAwesome.fa_suitcase, user.Occupation);
infoTextRight.NewParagraph();
if (!string.IsNullOrEmpty(user.Twitter))
@@ -514,7 +514,7 @@ namespace osu.Game.Overlays.Profile
{
set
{
if(value != null)
if (value != null)
content.Action = () => Process.Start(value);
}
}
+3 -3
View File
@@ -81,12 +81,12 @@ namespace osu.Game.Overlays.Profile
{
rankText.Text = user.Statistics.Rank > 0 ? $"#{user.Statistics.Rank:#,0}" : "no rank";
performanceText.Text = user.Statistics.PP != null ? $"{user.Statistics.PP:#,0}pp" : string.Empty;
relativeText.Text = $"{user.Country?.FullName} #{user.CountryRank:#,0}";
relativeText.Text = user.CountryRank > 0 ? $"{user.Country?.FullName} #{user.CountryRank:#,0}" : $"{user.Country?.FullName}";
}
private void showHistoryRankTexts(int dayIndex)
{
rankText.Text = $"#{ranks[dayIndex]:#,0}";
rankText.Text = ranks[dayIndex] > 0 ? $"#{ranks[dayIndex]:#,0}" : "no rank";
dayIndex++;
relativeText.Text = dayIndex == ranks.Length ? "Now" : $"{ranks.Length - dayIndex} days ago";
//plural should be handled in a general way
@@ -99,7 +99,7 @@ namespace osu.Game.Overlays.Profile
{
graph.Colour = colours.Yellow;
// use logarithmic coordinates
graph.Values = ranks.Select(x => -(float)Math.Log(x));
graph.Values = ranks.Select(x => x == 0 ? float.MinValue : -(float)Math.Log(x));
graph.SetStaticBallPosition();
}

Some files were not shown because too many files have changed in this diff Show More