1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-15 14:52:53 +08:00

Compare commits

...

232 Commits

151 changed files with 3259 additions and 2256 deletions
+27 -13
View File
@@ -2,46 +2,60 @@
"version": "0.2.0",
"configurations": [
{
"name": "Launch VisualTests",
"name": "VisualTests (debug)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/osu.Desktop.VisualTests/bin/Debug/osu!.exe",
"args": [],
"cwd": "${workspaceRoot}",
"preLaunchTask": "build",
"preLaunchTask": "Build (Debug)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "Launch Desktop",
"name": "VisualTests (release)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/osu.Desktop.VisualTests/bin/Release/osu!.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "osu! (debug)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/osu.Desktop/bin/Debug/osu!.exe",
"args": [],
"cwd": "${workspaceRoot}",
"preLaunchTask": "build",
"preLaunchTask": "Build (Debug)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "Attach",
"name": "osu! (release)",
"windows": {
"type": "clr",
"request": "attach",
"processName": "osu!"
"type": "clr"
},
"type": "mono",
"request": "attach",
"address": "localhost",
"port": 55555
"request": "launch",
"program": "${workspaceRoot}/osu.Desktop/bin/Release/osu!.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
}
]
}
+38 -39
View File
@@ -1,51 +1,50 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "0.1.0",
"taskSelector": "/t:",
"version": "2.0.0",
"problemMatcher": "$msCompile",
"isShellCommand": true,
"command": "msbuild",
"suppressTaskName": true,
"showOutput": "silent",
"args": [
"/property:GenerateFullPaths=true",
"/property:DebugType=portable"
],
"windows": {
"args": [
"/property:GenerateFullPaths=true",
"/property:DebugType=portable",
"/m" //parallel compiling support. doesn't work well with mono atm
]
},
"tasks": [
{
"taskName": "build",
"isShellCommand": true,
"showOutput": "silent",
"command": "msbuild",
"args": [
"/property:GenerateFullPaths=true",
"/property:DebugType=portable"
],
"windows": {
"args": [
"/property:GenerateFullPaths=true",
"/property:DebugType=portable",
"/m" //parallel compiling support. doesn't work well with mono atm
]
},
// Use the standard MS compiler pattern to detect errors, warnings and infos
"problemMatcher": "$msCompile",
"taskName": "Build (Debug)",
"isBuildCommand": true
},
{
"taskName": "rebuild",
"isShellCommand": true,
"showOutput": "silent",
"command": "msbuild",
"taskName": "Build (Release)",
"args": [
// Ask msbuild to generate full paths for file names.
"/property:GenerateFullPaths=true",
"/property:DebugType=portable",
"/target:Clean,Build"
],
"windows": {
"args": [
"/property:GenerateFullPaths=true",
"/property:DebugType=portable",
"/target:Clean,Build",
"/m" //parallel compiling support. doesn't work well with mono atm
]
},
// Use the standard MS compiler pattern to detect errors, warnings and infos
"problemMatcher": "$msCompile",
"isBuildCommand": true
"/property:Configuration=Release"
]
},
{
"taskName": "Clean All",
"dependsOn": ["Clean (Debug)", "Clean (Release)"]
},
{
"taskName": "Clean (Debug)",
"args": [
"/target:Clean"
]
},
{
"taskName": "Clean (Release)",
"args": [
"/target:Clean",
"/property:Configuration=Release"
]
}
]
}
+2 -4
View File
@@ -1,8 +1,6 @@
# osu! [![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu) [![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu)
# osu! [![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu) [![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu) [![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](https://discord.gg/ppy)
[osu! on the web](https://osu.ppy.sh) | [dev chat](https://discord.gg/ppy)
Rhythm is just a *click* away. The future of osu! and the beginning of an open era!
Rhythm is just a *click* away. The future of [osu!](https://osu.ppy.sh) and the beginning of an open era!
# Status
@@ -5,7 +5,6 @@ using System.Collections.Generic;
using osu.Framework.Testing;
using osu.Game.Screens.Tournament;
using osu.Game.Screens.Tournament.Teams;
using osu.Game.Users;
namespace osu.Desktop.VisualTests.Tests
{
@@ -25,57 +24,57 @@ namespace osu.Desktop.VisualTests.Tests
private class TestTeamList : ITeamList
{
public IEnumerable<Country> Teams { get; } = new[]
public IEnumerable<DrawingsTeam> Teams { get; } = new[]
{
new Country
new DrawingsTeam
{
FlagName = "GB",
FullName = "United Kingdom",
Acronym = "UK"
},
new Country
new DrawingsTeam
{
FlagName = "FR",
FullName = "France",
Acronym = "FRA"
},
new Country
new DrawingsTeam
{
FlagName = "CN",
FullName = "China",
Acronym = "CHN"
},
new Country
new DrawingsTeam
{
FlagName = "AU",
FullName = "Australia",
Acronym = "AUS"
},
new Country
new DrawingsTeam
{
FlagName = "JP",
FullName = "Japan",
Acronym = "JPN"
},
new Country
new DrawingsTeam
{
FlagName = "RO",
FullName = "Romania",
Acronym = "ROM"
},
new Country
new DrawingsTeam
{
FlagName = "IT",
FullName = "Italy",
Acronym = "PIZZA"
},
new Country
new DrawingsTeam
{
FlagName = "VE",
FullName = "Venezuela",
Acronym = "VNZ"
},
new Country
new DrawingsTeam
{
FlagName = "US",
FullName = "United States of America",
@@ -0,0 +1,70 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Input;
using osu.Framework.Testing;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.UI;
using System.Linq;
using System;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseManiaPlayfield : TestCase
{
public override string Description => @"Mania playfield";
protected override double TimePerAction => 200;
public override void Reset()
{
base.Reset();
const int max_columns = 10;
Action<int, SpecialColumnPosition> createPlayfield = (cols, pos) =>
{
Clear();
Add(new ManiaPlayfield(cols)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
SpecialColumnPosition = pos
});
};
for (int i = 1; i <= max_columns; i++)
{
int tempI = i;
AddStep($"{i} column" + (i > 1 ? "s" : ""), () => createPlayfield(tempI, SpecialColumnPosition.Normal));
AddStep("Trigger keys down", () => ((ManiaPlayfield)Children.First()).Columns.Children.ForEach(triggerKeyDown));
AddStep("Trigger keys up", () => ((ManiaPlayfield)Children.First()).Columns.Children.ForEach(triggerKeyUp));
AddStep("Left special style", () => createPlayfield(tempI, SpecialColumnPosition.Left));
AddStep("Right special style", () => createPlayfield(tempI, SpecialColumnPosition.Right));
}
AddStep("Normal special style", () => createPlayfield(max_columns, SpecialColumnPosition.Normal));
}
private void triggerKeyDown(Column column)
{
column.TriggerKeyDown(new InputState(), new KeyDownEventArgs
{
Key = column.Key,
Repeat = false
});
}
private void triggerKeyUp(Column column)
{
column.TriggerKeyUp(new InputState(), new KeyUpEventArgs
{
Key = column.Key
});
}
}
}
@@ -6,16 +6,21 @@ using osu.Framework.Graphics;
using osu.Game.Overlays.Mods;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Screens.Play.HUD;
using OpenTK;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseModSelectOverlay : TestCase
internal class TestCaseMods : TestCase
{
public override string Description => @"Tests the mod select overlay";
public override string Description => @"Mod select overlay and in-game display";
private ModSelectOverlay modSelect;
private ModDisplay modDisplay;
private RulesetDatabase rulesets;
[BackgroundDependencyLoader]
private void load(RulesetDatabase rulesets)
{
@@ -33,6 +38,16 @@ namespace osu.Desktop.VisualTests.Tests
Anchor = Anchor.BottomCentre,
});
Add(modDisplay = new ModDisplay
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Position = new Vector2(0, 25),
});
modDisplay.Current.BindTo(modSelect.SelectedMods);
AddStep("Toggle", modSelect.ToggleVisibility);
foreach (var ruleset in rulesets.AllRulesets)
@@ -8,7 +8,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play.HUD;
namespace osu.Desktop.VisualTests.Tests
{
@@ -38,9 +38,9 @@ namespace osu.Desktop.VisualTests.Tests
Origin = Anchor.TopLeft,
});
AddStep("Toggle Bar", progress.ToggleBar);
AddStep("Toggle Bar", () => progress.AllowSeeking = !progress.AllowSeeking);
AddWaitStep(5);
AddStep("Toggle Bar", progress.ToggleBar);
AddStep("Toggle Bar", () => progress.AllowSeeking = !progress.AllowSeeking);
AddWaitStep(2);
AddRepeatStep("New Values", displayNewValues, 5);
+1 -1
View File
@@ -29,7 +29,7 @@ namespace osu.Desktop.VisualTests
host.DrawThread.InactiveHz = host.DrawThread.ActiveHz;
host.InputThread.InactiveHz = host.InputThread.ActiveHz;
host.Window.CursorState = CursorState.Hidden;
host.Window.CursorState |= CursorState.Hidden;
}
}
}
@@ -190,6 +190,7 @@
<Compile Include="Tests\TestCaseDrawings.cs" />
<Compile Include="Tests\TestCaseGamefield.cs" />
<Compile Include="Tests\TestCaseGraph.cs" />
<Compile Include="Tests\TestCaseManiaPlayfield.cs" />
<Compile Include="Tests\TestCaseMenuOverlays.cs" />
<Compile Include="Tests\TestCaseMusicController.cs" />
<Compile Include="Tests\TestCaseNotificationManager.cs" />
@@ -211,7 +212,7 @@
<Compile Include="Platform\TestStorage.cs" />
<Compile Include="Tests\TestCaseOptions.cs" />
<Compile Include="Tests\TestCaseSongProgress.cs" />
<Compile Include="Tests\TestCaseModSelectOverlay.cs" />
<Compile Include="Tests\TestCaseMods.cs" />
<Compile Include="Tests\TestCaseDialogOverlay.cs" />
<Compile Include="Tests\TestCaseBeatmapOptionsOverlay.cs" />
<Compile Include="Tests\TestCaseLeaderboard.cs" />
+1 -1
View File
@@ -43,7 +43,7 @@ namespace osu.Desktop
var desktopWindow = host.Window as DesktopGameWindow;
if (desktopWindow != null)
{
desktopWindow.CursorState = CursorState.Hidden;
desktopWindow.CursorState |= CursorState.Hidden;
desktopWindow.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
desktopWindow.Title = Name;
+1
View File
@@ -64,6 +64,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public override string Name => "FadeIn";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override ModType Type => ModType.DifficultyIncrease;
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
+202
View File
@@ -0,0 +1,202 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Colour;
using osu.Framework.Input;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mania.UI
{
public class Column : Container, IHasAccentColour
{
private const float key_size = 50;
private const float key_icon_size = 10;
private const float key_icon_corner_radius = 3;
private const float key_icon_border_radius = 2;
private const float hit_target_height = 10;
private const float hit_target_bar_height = 2;
private const float column_width = 45;
private const float special_column_width = 70;
public Key Key;
private readonly Box background;
private readonly Container hitTargetBar;
private readonly Container keyIcon;
public Column()
{
RelativeSizeAxes = Axes.Y;
Width = column_width;
Children = new Drawable[]
{
background = new Box
{
Name = "Foreground",
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f
},
new FillFlowContainer
{
Name = "Key + hit target",
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new[]
{
new Container
{
Name = "Key",
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Height = key_size,
Children = new Drawable[]
{
new Box
{
Name = "Key gradient",
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0)),
Alpha = 0.5f
},
keyIcon = new Container
{
Name = "Key icon",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(key_icon_size),
Masking = true,
CornerRadius = key_icon_corner_radius,
BorderThickness = 2,
BorderColour = Color4.White, // Not true
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
}
}
},
new Container
{
Name = "Hit target",
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Height = hit_target_height,
Children = new Drawable[]
{
new Box
{
Name = "Background",
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
},
hitTargetBar = new Container
{
Name = "Bar",
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Height = hit_target_bar_height,
Masking = true,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both
}
}
}
}
}
}
}
};
}
private bool isSpecial;
public bool IsSpecial
{
get { return isSpecial; }
set
{
if (isSpecial == value)
return;
isSpecial = value;
Width = isSpecial ? special_column_width : column_width;
}
}
private Color4 accentColour;
public Color4 AccentColour
{
get { return accentColour; }
set
{
if (accentColour == value)
return;
accentColour = value;
background.Colour = accentColour;
hitTargetBar.EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Radius = 5,
Colour = accentColour.Opacity(0.5f),
};
keyIcon.EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Radius = 5,
Colour = accentColour.Opacity(0.5f),
};
}
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Key == Key && !args.Repeat)
{
background.FadeTo(background.Alpha + 0.2f, 50, EasingTypes.OutQuint);
keyIcon.ScaleTo(1.4f, 50, EasingTypes.OutQuint);
}
return false;
}
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
{
if (args.Key == Key)
{
background.FadeTo(0.2f, 800, EasingTypes.OutQuart);
keyIcon.ScaleTo(1f, 400, EasingTypes.OutQuart);
}
return false;
}
}
}
+138 -14
View File
@@ -8,29 +8,153 @@ using osu.Game.Rulesets.UI;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Graphics.Containers;
using System;
using osu.Framework.Graphics.Primitives;
using osu.Game.Graphics;
using osu.Framework.Allocation;
using OpenTK.Input;
using System.Linq;
using System.Collections.Generic;
namespace osu.Game.Rulesets.Mania.UI
{
public class ManiaPlayfield : Playfield<ManiaBaseHit, ManiaJudgement>
{
public ManiaPlayfield(int columns)
/// <summary>
/// Default column keys, expanding outwards from the middle as more column are added.
/// E.g. 2 columns use FJ, 4 columns use DFJK, 6 use SDFJKL, etc...
/// </summary>
private static readonly Key[] default_keys = { Key.A, Key.S, Key.D, Key.F, Key.J, Key.K, Key.L, Key.Semicolon };
private SpecialColumnPosition specialColumnPosition;
/// <summary>
/// The style to use for the special column.
/// </summary>
public SpecialColumnPosition SpecialColumnPosition
{
Size = new Vector2(0.8f, 1f);
Anchor = Anchor.BottomCentre;
Origin = Anchor.BottomCentre;
get { return specialColumnPosition; }
set
{
if (IsLoaded)
throw new InvalidOperationException($"Setting {nameof(SpecialColumnPosition)} after the playfield is loaded requires re-creating the playfield.");
specialColumnPosition = value;
}
}
Add(new Box { RelativeSizeAxes = Axes.Both, Alpha = 0.5f });
public readonly FlowContainer<Column> Columns;
for (int i = 0; i < columns; i++)
Add(new Box
private List<Color4> normalColumnColours = new List<Color4>();
private Color4 specialColumnColour;
private readonly int columnCount;
public ManiaPlayfield(int columnCount)
{
this.columnCount = columnCount;
if (columnCount <= 0)
throw new ArgumentException("Can't have zero or fewer columns.");
Children = new Drawable[]
{
new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Size = new Vector2(2, 1),
RelativePositionAxes = Axes.Both,
Position = new Vector2((float)i / columns, 0),
Alpha = 0.5f,
Colour = Color4.Black
});
AutoSizeAxes = Axes.X,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
},
Columns = new FillFlowContainer<Column>
{
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Direction = FillDirection.Horizontal,
Padding = new MarginPadding { Left = 1, Right = 1 },
Spacing = new Vector2(1, 0)
}
}
}
};
for (int i = 0; i < columnCount; i++)
Columns.Add(new Column());
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
normalColumnColours = new List<Color4>
{
colours.RedDark,
colours.GreenDark
};
specialColumnColour = colours.BlueDark;
// Set the special column + colour + key
for (int i = 0; i < columnCount; i++)
{
Column column = Columns.Children.ElementAt(i);
column.IsSpecial = isSpecialColumn(i);
if (!column.IsSpecial)
continue;
column.Key = Key.Space;
column.AccentColour = specialColumnColour;
}
var nonSpecialColumns = Columns.Children.Where(c => !c.IsSpecial).ToList();
// We'll set the colours of the non-special columns in a separate loop, because the non-special
// column colours are mirrored across their centre and special styles mess with this
for (int i = 0; i < Math.Ceiling(nonSpecialColumns.Count / 2f); i++)
{
Color4 colour = normalColumnColours[i % normalColumnColours.Count];
nonSpecialColumns[i].AccentColour = colour;
nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
}
// We'll set the keys for non-special columns in another separate loop because it's not mirrored like the above colours
// Todo: This needs to go when we get to bindings and use Button1, ..., ButtonN instead
for (int i = 0; i < nonSpecialColumns.Count; i++)
{
Column column = nonSpecialColumns[i];
int keyOffset = default_keys.Length / 2 - nonSpecialColumns.Count / 2 + i;
if (keyOffset >= 0 && keyOffset < default_keys.Length)
column.Key = default_keys[keyOffset];
else
// There is no default key defined for this column. Let's set this to Unknown for now
// however note that this will be gone after bindings are in place
column.Key = Key.Unknown;
}
}
/// <summary>
/// Whether the column index is a special column for this playfield.
/// </summary>
/// <param name="column">The 0-based column index.</param>
/// <returns>Whether the column is a special column.</returns>
private bool isSpecialColumn(int column)
{
switch (SpecialColumnPosition)
{
default:
case SpecialColumnPosition.Normal:
return columnCount % 2 == 1 && column == columnCount / 2;
case SpecialColumnPosition.Left:
return column == 0;
case SpecialColumnPosition.Right:
return column == columnCount - 1;
}
}
}
}
}
@@ -0,0 +1,21 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Rulesets.Mania.UI
{
public enum SpecialColumnPosition
{
/// <summary>
/// The special column will lie in the center of the columns.
/// </summary>
Normal,
/// <summary>
/// The special column will lie to the left of the columns.
/// </summary>
Left,
/// <summary>
/// The special column will lie to the right of the columns.
/// </summary>
Right
}
}
@@ -56,10 +56,12 @@
<Compile Include="Objects\ManiaBaseHit.cs" />
<Compile Include="Objects\Note.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UI\Column.cs" />
<Compile Include="UI\ManiaHitRenderer.cs" />
<Compile Include="UI\ManiaPlayfield.cs" />
<Compile Include="ManiaRuleset.cs" />
<Compile Include="Mods\ManiaMod.cs" />
<Compile Include="UI\SpecialColumnPosition.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
+2 -1
View File
@@ -3,6 +3,7 @@
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
using System;
@@ -95,7 +96,7 @@ namespace osu.Game.Rulesets.Osu.Mods
protected override Score CreateReplayScore(Beatmap<OsuHitObject> beatmap) => new Score
{
Replay = new OsuAutoReplay(beatmap)
Replay = new OsuAutoGenerator(beatmap).Generate()
};
}
@@ -12,16 +12,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
{
public class FollowPoint : Container
{
public double StartTime;
public double EndTime;
public Vector2 EndPosition;
private const float width = 8;
public FollowPoint()
{
Origin = Anchor.Centre;
Alpha = 0;
Masking = true;
AutoSizeAxes = Axes.Both;
@@ -45,22 +40,5 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
},
};
}
protected override void LoadComplete()
{
base.LoadComplete();
Delay(StartTime);
FadeIn(DrawableOsuHitObject.TIME_FADEIN);
ScaleTo(1.5f);
ScaleTo(1, DrawableOsuHitObject.TIME_FADEIN, EasingTypes.Out);
MoveTo(EndPosition, DrawableOsuHitObject.TIME_FADEIN, EasingTypes.Out);
Delay(EndTime - StartTime);
FadeOut(DrawableOsuHitObject.TIME_FADEIN);
Delay(DrawableOsuHitObject.TIME_FADEIN);
Expire(true);
}
}
}
}
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using OpenTK;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
@@ -80,14 +81,28 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
double fadeOutTime = startTime + fraction * duration;
double fadeInTime = fadeOutTime - PreEmpt;
Add(new FollowPoint
FollowPoint fp;
Add(fp = new FollowPoint
{
StartTime = fadeInTime,
EndTime = fadeOutTime,
Position = pointStartPosition,
EndPosition = pointEndPosition,
Rotation = rotation,
Alpha = 0,
Scale = new Vector2(1.5f),
});
using (fp.BeginAbsoluteSequence(fadeInTime))
{
fp.FadeIn(DrawableOsuHitObject.TIME_FADEIN);
fp.ScaleTo(1, DrawableOsuHitObject.TIME_FADEIN, EasingTypes.Out);
fp.MoveTo(pointEndPosition, DrawableOsuHitObject.TIME_FADEIN, EasingTypes.Out);
fp.Delay(fadeOutTime - fadeInTime);
fp.FadeOut(DrawableOsuHitObject.TIME_FADEIN);
}
fp.Expire(true);
}
}
prevHitObject = currHitObject;
@@ -104,10 +104,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ApproachCircle.ScaleTo(1.1f, TIME_PREEMPT);
}
protected override void UpdateState(ArmedState state)
protected override void UpdateCurrentState(ArmedState state)
{
base.UpdateState(state);
ApproachCircle.FadeOut();
double endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
@@ -21,17 +21,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override OsuJudgement CreateJudgement() => new OsuJudgement { MaxScore = OsuScoreResult.Hit300 };
protected override void UpdateState(ArmedState state)
protected sealed override void UpdateState(ArmedState state)
{
Flush();
UpdateInitialState();
Delay(HitObject.StartTime - Time.Current - TIME_PREEMPT + Judgement.TimeOffset, true);
using (BeginAbsoluteSequence(HitObject.StartTime - TIME_PREEMPT, true))
{
UpdatePreemptState();
UpdatePreemptState();
using (BeginDelayedSequence(TIME_PREEMPT + Judgement.TimeOffset, true))
UpdateCurrentState(state);
}
}
Delay(TIME_PREEMPT, true);
protected virtual void UpdateCurrentState(ArmedState state)
{
}
protected virtual void UpdatePreemptState()
@@ -158,10 +158,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ball.Alpha = 0;
}
protected override void UpdateState(ArmedState state)
protected override void UpdateCurrentState(ArmedState state)
{
base.UpdateState(state);
ball.FadeIn();
Delay(slider.Duration, true);
@@ -181,4 +179,4 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
void UpdateProgress(double progress, int repeat);
}
}
}
@@ -72,10 +72,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Delay(-animIn);
}
protected override void UpdateState(ArmedState state)
protected override void UpdateCurrentState(ArmedState state)
{
base.UpdateState(state);
switch (state)
{
case ArmedState.Idle:
@@ -93,4 +91,4 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
}
}
}
@@ -132,10 +132,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
disc.FadeIn(200);
}
protected override void UpdateState(ArmedState state)
protected override void UpdateCurrentState(ArmedState state)
{
base.UpdateState(state);
Delay(spinner.Duration, true);
FadeOut(160);
+3 -3
View File
@@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Objects
set { Curve.Distance = value; }
}
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>();
public List<SampleInfoList> RepeatSamples { get; set; } = new List<SampleInfoList>();
public int RepeatCount { get; set; } = 1;
private int stackHeight;
@@ -117,12 +117,12 @@ namespace osu.Game.Rulesets.Osu.Objects
StackHeight = StackHeight,
Scale = Scale,
ComboColour = ComboColour,
Samples = Samples.Select(s => new SampleInfo
Samples = new SampleInfoList(Samples.Select(s => new SampleInfo
{
Bank = s.Bank,
Name = @"slidertick",
Volume = s.Volume
}).ToList()
}))
};
}
}
-315
View File
@@ -1,315 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays;
using osu.Game.Users;
namespace osu.Game.Rulesets.Osu
{
public class OsuAutoReplay : Replay
{
private static readonly Vector2 spinner_centre = new Vector2(256, 192);
private const float spin_radius = 50;
private readonly Beatmap<OsuHitObject> beatmap;
public OsuAutoReplay(Beatmap<OsuHitObject> beatmap)
{
this.beatmap = beatmap;
User = new User
{
Username = @"Autoplay",
};
createAutoReplay();
}
private class ReplayFrameComparer : IComparer<ReplayFrame>
{
public int Compare(ReplayFrame f1, ReplayFrame f2)
{
if (f1 == null) throw new NullReferenceException($@"{nameof(f1)} cannot be null");
if (f2 == null) throw new NullReferenceException($@"{nameof(f2)} cannot be null");
return f1.Time.CompareTo(f2.Time);
}
}
private static readonly IComparer<ReplayFrame> replay_frame_comparer = new ReplayFrameComparer();
private int findInsertionIndex(ReplayFrame frame)
{
int index = Frames.BinarySearch(frame, replay_frame_comparer);
if (index < 0)
{
index = ~index;
}
else
{
// Go to the first index which is actually bigger
while (index < Frames.Count && frame.Time == Frames[index].Time)
{
++index;
}
}
return index;
}
private void addFrameToReplay(ReplayFrame frame) => Frames.Insert(findInsertionIndex(frame), frame);
private static Vector2 circlePosition(double t, double radius) => new Vector2((float)(Math.Cos(t) * radius), (float)(Math.Sin(t) * radius));
private double applyModsToTime(double v) => v;
private double applyModsToRate(double v) => v;
public bool DelayedMovements; // ModManager.CheckActive(Mods.Relax2);
private void createAutoReplay()
{
int buttonIndex = 0;
EasingTypes preferredEasing = DelayedMovements ? EasingTypes.InOutCubic : EasingTypes.Out;
addFrameToReplay(new ReplayFrame(-100000, 256, 500, ReplayButtonState.None));
addFrameToReplay(new ReplayFrame(beatmap.HitObjects[0].StartTime - 1500, 256, 500, ReplayButtonState.None));
addFrameToReplay(new ReplayFrame(beatmap.HitObjects[0].StartTime - 1000, 256, 192, ReplayButtonState.None));
// We are using ApplyModsToRate and not ApplyModsToTime to counteract the speed up / slow down from HalfTime / DoubleTime so that we remain at a constant framerate of 60 fps.
float frameDelay = (float)applyModsToRate(1000.0 / 60.0);
// Already superhuman, but still somewhat realistic
int reactionTime = (int)applyModsToRate(100);
for (int i = 0; i < beatmap.HitObjects.Count; i++)
{
OsuHitObject h = beatmap.HitObjects[i];
//if (h.EndTime < InputManager.ReplayStartTime)
//{
// h.IsHit = true;
// continue;
//}
int endDelay = h is Spinner ? 1 : 0;
if (DelayedMovements && i > 0)
{
OsuHitObject last = beatmap.HitObjects[i - 1];
double endTime = (last as IHasEndTime)?.EndTime ?? last.StartTime;
//Make the cursor stay at a hitObject as long as possible (mainly for autopilot).
if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{
if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) addFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, ReplayButtonState.None));
}
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{
if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) addFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, ReplayButtonState.None));
}
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50)
{
if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), last.EndPosition.X, last.EndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) addFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, ReplayButtonState.None));
}
}
Vector2 targetPosition = h.Position;
EasingTypes easing = preferredEasing;
float spinnerDirection = -1;
if (h is Spinner)
{
targetPosition = Frames[Frames.Count - 1].Position;
Vector2 difference = spinner_centre - targetPosition;
float differenceLength = difference.Length;
float newLength = (float)Math.Sqrt(differenceLength * differenceLength - spin_radius * spin_radius);
if (differenceLength > spin_radius)
{
float angle = (float)Math.Asin(spin_radius / differenceLength);
if (angle > 0)
{
spinnerDirection = -1;
}
else
{
spinnerDirection = 1;
}
difference.X = difference.X * (float)Math.Cos(angle) - difference.Y * (float)Math.Sin(angle);
difference.Y = difference.X * (float)Math.Sin(angle) + difference.Y * (float)Math.Cos(angle);
difference.Normalize();
difference *= newLength;
targetPosition += difference;
easing = EasingTypes.In;
}
else if (difference.Length > 0)
{
targetPosition = spinner_centre - difference * (spin_radius / difference.Length);
}
else
{
targetPosition = spinner_centre + new Vector2(0, -spin_radius);
}
}
// Do some nice easing for cursor movements
if (Frames.Count > 0)
{
ReplayFrame lastFrame = Frames[Frames.Count - 1];
// Wait until Auto could "see and react" to the next note.
double waitTime = h.StartTime - Math.Max(0.0, DrawableOsuHitObject.TIME_PREEMPT - reactionTime);
if (waitTime > lastFrame.Time)
{
lastFrame = new ReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState);
addFrameToReplay(lastFrame);
}
Vector2 lastPosition = lastFrame.Position;
double timeDifference = applyModsToTime(h.StartTime - lastFrame.Time);
// Only "snap" to hitcircles if they are far enough apart. As the time between hitcircles gets shorter the snapping threshold goes up.
if (timeDifference > 0 && // Sanity checks
((lastPosition - targetPosition).Length > h.Radius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough
timeDifference >= 266)) // ... or the beats are slow enough to tap anyway.
{
// Perform eased movement
for (double time = lastFrame.Time + frameDelay; time < h.StartTime; time += frameDelay)
{
Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPosition, lastFrame.Time, h.StartTime, easing);
addFrameToReplay(new ReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState));
}
buttonIndex = 0;
}
else
{
buttonIndex++;
}
}
ReplayButtonState button = buttonIndex % 2 == 0 ? ReplayButtonState.Left1 : ReplayButtonState.Right1;
double hEndTime = ((h as IHasEndTime)?.EndTime ?? h.StartTime) + KEY_UP_DELAY;
ReplayFrame newFrame = new ReplayFrame(h.StartTime, targetPosition.X, targetPosition.Y, button);
ReplayFrame endFrame = new ReplayFrame(hEndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, ReplayButtonState.None);
// Decrement because we want the previous frame, not the next one
int index = findInsertionIndex(newFrame) - 1;
// Do we have a previous frame? No need to check for < replay.Count since we decremented!
if (index >= 0)
{
ReplayFrame previousFrame = Frames[index];
var previousButton = previousFrame.ButtonState;
// If a button is already held, then we simply alternate
if (previousButton != ReplayButtonState.None)
{
Debug.Assert(previousButton != (ReplayButtonState.Left1 | ReplayButtonState.Right1));
// Force alternation if we have the same button. Otherwise we can just keep the naturally to us assigned button.
if (previousButton == button)
{
button = (ReplayButtonState.Left1 | ReplayButtonState.Right1) & ~button;
newFrame.ButtonState = button;
}
// We always follow the most recent slider / spinner, so remove any other frames that occur while it exists.
int endIndex = findInsertionIndex(endFrame);
if (index < Frames.Count - 1)
Frames.RemoveRange(index + 1, Math.Max(0, endIndex - (index + 1)));
// After alternating we need to keep holding the other button in the future rather than the previous one.
for (int j = index + 1; j < Frames.Count; ++j)
{
// Don't affect frames which stop pressing a button!
if (j < Frames.Count - 1 || Frames[j].ButtonState == previousButton)
Frames[j].ButtonState = button;
}
}
}
addFrameToReplay(newFrame);
// We add intermediate frames for spinning / following a slider here.
if (h is Spinner)
{
Spinner s = h as Spinner;
Vector2 difference = targetPosition - spinner_centre;
float radius = difference.Length;
float angle = radius == 0 ? 0 : (float)Math.Atan2(difference.Y, difference.X);
double t;
for (double j = h.StartTime + frameDelay; j < s.EndTime; j += frameDelay)
{
t = applyModsToTime(j - h.StartTime) * spinnerDirection;
Vector2 pos = spinner_centre + circlePosition(t / 20 + angle, spin_radius);
addFrameToReplay(new ReplayFrame((int)j, pos.X, pos.Y, button));
}
t = applyModsToTime(s.EndTime - h.StartTime) * spinnerDirection;
Vector2 endPosition = spinner_centre + circlePosition(t / 20 + angle, spin_radius);
addFrameToReplay(new ReplayFrame(s.EndTime, endPosition.X, endPosition.Y, button));
endFrame.MouseX = endPosition.X;
endFrame.MouseY = endPosition.Y;
}
else if (h is Slider)
{
Slider s = h as Slider;
for (double j = frameDelay; j < s.Duration; j += frameDelay)
{
Vector2 pos = s.PositionAt(j / s.Duration);
addFrameToReplay(new ReplayFrame(h.StartTime + j, pos.X, pos.Y, button));
}
addFrameToReplay(new ReplayFrame(s.EndTime, s.EndPosition.X, s.EndPosition.Y, button));
}
// We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed!
if (Frames[Frames.Count - 1].Time <= endFrame.Time)
addFrameToReplay(endFrame);
}
//Player.currentScore.Replay = InputManager.ReplayScore.Replay;
//Player.currentScore.PlayerName = "osu!";
}
}
}
@@ -2,10 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Input;
using osu.Game.Configuration;
using osu.Game.Screens.Play;
using OpenTK.Input;
using KeyboardState = osu.Framework.Input.KeyboardState;
@@ -17,13 +14,6 @@ namespace osu.Game.Rulesets.Osu
{
private bool leftViaKeyboard;
private bool rightViaKeyboard;
private Bindable<bool> mouseDisabled;
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
mouseDisabled = config.GetBindable<bool>(OsuConfig.MouseDisableButtons);
}
protected override void TransformState(InputState state)
{
@@ -40,12 +30,6 @@ namespace osu.Game.Rulesets.Osu
if (mouse != null)
{
if (mouseDisabled.Value)
{
mouse.SetPressed(MouseButton.Left, false);
mouse.SetPressed(MouseButton.Right, false);
}
if (leftViaKeyboard)
mouse.SetPressed(MouseButton.Left, true);
if (rightViaKeyboard)
@@ -0,0 +1,332 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using System;
using System.Diagnostics;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Osu.Replays
{
public class OsuAutoGenerator : OsuAutoGeneratorBase
{
#region Parameters
/// <summary>
/// If delayed movements should be used, causing the cursor to stay on each hitobject for as long as possible.
/// Mainly for Autopilot.
/// </summary>
public bool DelayedMovements; // ModManager.CheckActive(Mods.Relax2);
#endregion
#region Constants
/// <summary>
/// The "reaction time" in ms between "seeing" a new hit object and moving to "react" to it.
/// </summary>
private readonly double reactionTime;
/// <summary>
/// What easing to use when moving between hitobjects
/// </summary>
private EasingTypes preferredEasing => DelayedMovements ? EasingTypes.InOutCubic : EasingTypes.Out;
#endregion
#region Construction / Initialisation
public OsuAutoGenerator(Beatmap<OsuHitObject> beatmap)
: base(beatmap)
{
// Already superhuman, but still somewhat realistic
reactionTime = ApplyModsToRate(100);
}
#endregion
#region Generator
/// <summary>
/// Which button (left or right) to use for the current hitobject.
/// Even means LMB will be used to click, odd means RMB will be used.
/// This keeps track of the button previously used for alt/singletap logic.
/// </summary>
private int buttonIndex;
public override Replay Generate()
{
buttonIndex = 0;
AddFrameToReplay(new ReplayFrame(-100000, 256, 500, ReplayButtonState.None));
AddFrameToReplay(new ReplayFrame(Beatmap.HitObjects[0].StartTime - 1500, 256, 500, ReplayButtonState.None));
AddFrameToReplay(new ReplayFrame(Beatmap.HitObjects[0].StartTime - 1000, 256, 192, ReplayButtonState.None));
for (int i = 0; i < Beatmap.HitObjects.Count; i++)
{
OsuHitObject h = Beatmap.HitObjects[i];
if (DelayedMovements && i > 0)
{
OsuHitObject prev = Beatmap.HitObjects[i - 1];
addDelayedMovements(h, prev);
}
addHitObjectReplay(h);
}
return Replay;
}
private void addDelayedMovements(OsuHitObject h, OsuHitObject prev)
{
double endTime = (prev as IHasEndTime)?.EndTime ?? prev.StartTime;
// Make the cursor stay at a hitObject as long as possible (mainly for autopilot).
if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, ReplayButtonState.None));
}
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, ReplayButtonState.None));
}
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50)
{
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, ReplayButtonState.None));
}
}
private void addHitObjectReplay(OsuHitObject h)
{
// Default values for circles/sliders
Vector2 startPosition = h.Position;
EasingTypes easing = preferredEasing;
float spinnerDirection = -1;
// The startPosition for the slider should not be its .Position, but the point on the circle whose tangent crosses the current cursor position
// We also modify spinnerDirection so it spins in the direction it enters the spin circle, to make a smooth transition.
// TODO: Shouldn't the spinner always spin in the same direction?
if (h is Spinner)
{
calcSpinnerStartPosAndDirection(Frames[Frames.Count - 1].Position, out startPosition, out spinnerDirection);
Vector2 spinCentreOffset = SPINNER_CENTRE - Frames[Frames.Count - 1].Position;
if (spinCentreOffset.Length > SPIN_RADIUS)
{
// If moving in from the outside, don't ease out (default eases out). This means auto will "start" spinning immediately after moving into position.
easing = EasingTypes.In;
}
}
// Do some nice easing for cursor movements
if (Frames.Count > 0)
{
moveToHitObject(h.StartTime, startPosition, h.Radius, easing);
}
// Add frames to click the hitobject
addHitObjectClickFrames(h, startPosition, spinnerDirection);
}
#endregion
#region Helper subroutines
private static void calcSpinnerStartPosAndDirection(Vector2 prevPos, out Vector2 startPosition, out float spinnerDirection)
{
Vector2 spinCentreOffset = SPINNER_CENTRE - prevPos;
float distFromCentre = spinCentreOffset.Length;
float distToTangentPoint = (float)Math.Sqrt(distFromCentre * distFromCentre - SPIN_RADIUS * SPIN_RADIUS);
if (distFromCentre > SPIN_RADIUS)
{
// Previous cursor position was outside spin circle, set startPosition to the tangent point.
// Angle between centre offset and tangent point offset.
float angle = (float)Math.Asin(SPIN_RADIUS / distFromCentre);
if (angle > 0)
{
spinnerDirection = -1;
}
else
{
spinnerDirection = 1;
}
// Rotate by angle so it's parallel to tangent line
spinCentreOffset.X = spinCentreOffset.X * (float)Math.Cos(angle) - spinCentreOffset.Y * (float)Math.Sin(angle);
spinCentreOffset.Y = spinCentreOffset.X * (float)Math.Sin(angle) + spinCentreOffset.Y * (float)Math.Cos(angle);
// Set length to distToTangentPoint
spinCentreOffset.Normalize();
spinCentreOffset *= distToTangentPoint;
// Move along the tangent line, now startPosition is at the tangent point.
startPosition = prevPos + spinCentreOffset;
}
else if (spinCentreOffset.Length > 0)
{
// Previous cursor position was inside spin circle, set startPosition to the nearest point on spin circle.
startPosition = SPINNER_CENTRE - spinCentreOffset * (SPIN_RADIUS / spinCentreOffset.Length);
spinnerDirection = 1;
}
else
{
// Degenerate case where cursor position is exactly at the centre of the spin circle.
startPosition = SPINNER_CENTRE + new Vector2(0, -SPIN_RADIUS);
spinnerDirection = 1;
}
}
private void moveToHitObject(double targetTime, Vector2 targetPos, double hitObjectRadius, EasingTypes easing)
{
ReplayFrame lastFrame = Frames[Frames.Count - 1];
// Wait until Auto could "see and react" to the next note.
double waitTime = targetTime - Math.Max(0.0, DrawableOsuHitObject.TIME_PREEMPT - reactionTime);
if (waitTime > lastFrame.Time)
{
lastFrame = new ReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState);
AddFrameToReplay(lastFrame);
}
Vector2 lastPosition = lastFrame.Position;
double timeDifference = ApplyModsToTime(targetTime - lastFrame.Time);
// Only "snap" to hitcircles if they are far enough apart. As the time between hitcircles gets shorter the snapping threshold goes up.
if (timeDifference > 0 && // Sanity checks
((lastPosition - targetPos).Length > hitObjectRadius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough
timeDifference >= 266)) // ... or the beats are slow enough to tap anyway.
{
// Perform eased movement
for (double time = lastFrame.Time + FrameDelay; time < targetTime; time += FrameDelay)
{
Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, targetTime, easing);
AddFrameToReplay(new ReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState));
}
buttonIndex = 0;
}
else
{
buttonIndex++;
}
}
// Add frames to click the hitobject
private void addHitObjectClickFrames(OsuHitObject h, Vector2 startPosition, float spinnerDirection)
{
// Time to insert the first frame which clicks the object
// Here we mainly need to determine which button to use
ReplayButtonState button = buttonIndex % 2 == 0 ? ReplayButtonState.Left1 : ReplayButtonState.Right1;
ReplayFrame startFrame = new ReplayFrame(h.StartTime, startPosition.X, startPosition.Y, button);
// TODO: Why do we delay 1 ms if the object is a spinner? There already is KEY_UP_DELAY from hEndTime.
double hEndTime = ((h as IHasEndTime)?.EndTime ?? h.StartTime) + KEY_UP_DELAY;
int endDelay = h is Spinner ? 1 : 0;
ReplayFrame endFrame = new ReplayFrame(hEndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, ReplayButtonState.None);
// Decrement because we want the previous frame, not the next one
int index = FindInsertionIndex(startFrame) - 1;
// If the previous frame has a button pressed, force alternation.
// If there are frames ahead, modify those to use the new button press.
// Do we have a previous frame? No need to check for < replay.Count since we decremented!
if (index >= 0)
{
ReplayFrame previousFrame = Frames[index];
var previousButton = previousFrame.ButtonState;
// If a button is already held, then we simply alternate
if (previousButton != ReplayButtonState.None)
{
Debug.Assert(previousButton != (ReplayButtonState.Left1 | ReplayButtonState.Right1), "Previous button state was not Left1 nor Right1 despite only using those two states.");
// Force alternation if we have the same button. Otherwise we can just keep the naturally to us assigned button.
if (previousButton == button)
{
button = (ReplayButtonState.Left1 | ReplayButtonState.Right1) & ~button;
startFrame.ButtonState = button;
}
// We always follow the most recent slider / spinner, so remove any other frames that occur while it exists.
int endIndex = FindInsertionIndex(endFrame);
if (index < Frames.Count - 1)
Frames.RemoveRange(index + 1, Math.Max(0, endIndex - (index + 1)));
// After alternating we need to keep holding the other button in the future rather than the previous one.
for (int j = index + 1; j < Frames.Count; ++j)
{
// Don't affect frames which stop pressing a button!
if (j < Frames.Count - 1 || Frames[j].ButtonState == previousButton)
Frames[j].ButtonState = button;
}
}
}
AddFrameToReplay(startFrame);
// We add intermediate frames for spinning / following a slider here.
if (h is Spinner)
{
Spinner s = h as Spinner;
Vector2 difference = startPosition - SPINNER_CENTRE;
float radius = difference.Length;
float angle = radius == 0 ? 0 : (float)Math.Atan2(difference.Y, difference.X);
double t;
for (double j = h.StartTime + FrameDelay; j < s.EndTime; j += FrameDelay)
{
t = ApplyModsToTime(j - h.StartTime) * spinnerDirection;
Vector2 pos = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS);
AddFrameToReplay(new ReplayFrame((int)j, pos.X, pos.Y, button));
}
t = ApplyModsToTime(s.EndTime - h.StartTime) * spinnerDirection;
Vector2 endPosition = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS);
AddFrameToReplay(new ReplayFrame(s.EndTime, endPosition.X, endPosition.Y, button));
endFrame.MouseX = endPosition.X;
endFrame.MouseY = endPosition.Y;
}
else if (h is Slider)
{
Slider s = h as Slider;
for (double j = FrameDelay; j < s.Duration; j += FrameDelay)
{
Vector2 pos = s.PositionAt(j / s.Duration);
AddFrameToReplay(new ReplayFrame(h.StartTime + j, pos.X, pos.Y, button));
}
AddFrameToReplay(new ReplayFrame(s.EndTime, s.EndPosition.X, s.EndPosition.Y, button));
}
// We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed!
if (Frames[Frames.Count - 1].Time <= endFrame.Time)
AddFrameToReplay(endFrame);
}
#endregion
}
}
@@ -0,0 +1,96 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using System;
using System.Collections.Generic;
using osu.Game.Rulesets.Replays;
using osu.Game.Users;
namespace osu.Game.Rulesets.Osu.Replays
{
public abstract class OsuAutoGeneratorBase : AutoGenerator<OsuHitObject>
{
#region Constants
/// <summary>
/// Constants (for spinners).
/// </summary>
protected static readonly Vector2 SPINNER_CENTRE = new Vector2(256, 192);
protected const float SPIN_RADIUS = 50;
/// <summary>
/// The time in ms between each ReplayFrame.
/// </summary>
protected readonly double FrameDelay;
#endregion
#region Construction / Initialisation
protected Replay Replay;
protected List<ReplayFrame> Frames => Replay.Frames;
protected OsuAutoGeneratorBase(Beatmap<OsuHitObject> beatmap)
: base(beatmap)
{
Replay = new Replay
{
User = new User
{
Username = @"Autoplay",
}
};
// We are using ApplyModsToRate and not ApplyModsToTime to counteract the speed up / slow down from HalfTime / DoubleTime so that we remain at a constant framerate of 60 fps.
FrameDelay = ApplyModsToRate(1000.0 / 60.0);
}
#endregion
#region Utilities
protected double ApplyModsToTime(double v) => v;
protected double ApplyModsToRate(double v) => v;
private class ReplayFrameComparer : IComparer<ReplayFrame>
{
public int Compare(ReplayFrame f1, ReplayFrame f2)
{
if (f1 == null) throw new NullReferenceException($@"{nameof(f1)} cannot be null");
if (f2 == null) throw new NullReferenceException($@"{nameof(f2)} cannot be null");
return f1.Time.CompareTo(f2.Time);
}
}
private static readonly IComparer<ReplayFrame> replay_frame_comparer = new ReplayFrameComparer();
protected int FindInsertionIndex(ReplayFrame frame)
{
int index = Frames.BinarySearch(frame, replay_frame_comparer);
if (index < 0)
{
index = ~index;
}
else
{
// Go to the first index which is actually bigger
while (index < Frames.Count && frame.Time == Frames[index].Time)
{
++index;
}
}
return index;
}
protected void AddFrameToReplay(ReplayFrame frame) => Frames.Insert(FindInsertionIndex(frame), frame);
protected static Vector2 CirclePosition(double t, double radius) => new Vector2((float)(Math.Cos(t) * radius), (float)(Math.Sin(t) * radius));
#endregion
}
}
@@ -69,7 +69,6 @@
<Compile Include="Objects\Drawables\Pieces\SliderBody.cs" />
<Compile Include="Objects\OsuHitObjectDifficulty.cs" />
<Compile Include="Objects\SliderTick.cs" />
<Compile Include="OsuAutoReplay.cs" />
<Compile Include="OsuDifficultyCalculator.cs" />
<Compile Include="OsuKeyConversionInputManager.cs" />
<Compile Include="Scoring\OsuScoreProcessor.cs" />
@@ -83,6 +82,8 @@
<Compile Include="Objects\Spinner.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Mods\OsuMod.cs" />
<Compile Include="Replays\OsuAutoGenerator.cs" />
<Compile Include="Replays\OsuAutoGeneratorBase.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
@@ -102,6 +103,9 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Folder Include="Replays\" />
</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.
@@ -66,9 +66,10 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
var distanceData = obj as IHasDistance;
var repeatsData = obj as IHasRepeats;
var endTimeData = obj as IHasEndTime;
var curveData = obj as IHasCurve;
// Old osu! used hit sounding to determine various hit type information
List<SampleInfo> samples = obj.Samples;
SampleInfoList samples = obj.Samples;
bool strong = samples.Any(s => s.Name == SampleInfo.HIT_FINISH);
@@ -102,16 +103,35 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
if (tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
{
List<SampleInfoList> allSamples = curveData != null ? curveData.RepeatSamples : new List<SampleInfoList>(new[] { samples });
int i = 0;
for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)
{
// Todo: This should generate different type of hits (including strongs)
// depending on hitobject sound additions (not implemented fully yet)
yield return new CentreHit
SampleInfoList currentSamples = allSamples[i];
bool isRim = currentSamples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);
strong = currentSamples.Any(s => s.Name == SampleInfo.HIT_FINISH);
if (isRim)
{
StartTime = j,
Samples = obj.Samples,
IsStrong = strong,
};
yield return new RimHit
{
StartTime = j,
Samples = currentSamples,
IsStrong = strong
};
}
else
{
yield return new CentreHit
{
StartTime = j,
Samples = currentSamples,
IsStrong = strong,
};
}
i = (i + 1) % allSamples.Count;
}
}
else
+1 -1
View File
@@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
protected override Score CreateReplayScore(Beatmap<TaikoHitObject> beatmap) => new Score
{
User = new User { Username = "mekkadosu!" },
Replay = new TaikoAutoReplay(beatmap)
Replay = new TaikoAutoGenerator(beatmap).Generate(),
};
}
}
@@ -148,9 +148,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
var completion = (float)userHits / HitObject.RequiredHits;
expandingRing.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50);
expandingRing.Delay(50);
expandingRing.FadeTo(completion / 8, 2000, EasingTypes.OutQuint);
expandingRing.DelayReset();
using (expandingRing.BeginDelayedSequence(50))
expandingRing.FadeTo(completion / 8, 2000, EasingTypes.OutQuint);
symbol.RotateTo((float)(completion * HitObject.Duration / 8), 4000, EasingTypes.OutQuint);
+2 -2
View File
@@ -82,12 +82,12 @@ namespace osu.Game.Rulesets.Taiko.Objects
TickSpacing = tickSpacing,
StartTime = t,
IsStrong = IsStrong,
Samples = Samples.Select(s => new SampleInfo
Samples = new SampleInfoList(Samples.Select(s => new SampleInfo
{
Bank = s.Bank,
Name = @"slidertick",
Volume = s.Volume
}).ToList()
}))
});
first = false;
@@ -0,0 +1,127 @@
// 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;
namespace osu.Game.Rulesets.Taiko.Objects
{
internal class TaikoHitObjectDifficulty
{
/// <summary>
/// Factor by how much individual / overall strain decays per second.
/// </summary>
/// <remarks>
/// These values are results of tweaking a lot and taking into account general feedback.
/// </remarks>
internal const double DECAY_BASE = 0.30;
private const double type_change_bonus = 0.75;
private const double rhythm_change_bonus = 1.0;
private const double rhythm_change_base_threshold = 0.2;
private const double rhythm_change_base = 2.0;
internal TaikoHitObject BaseHitObject;
/// <summary>
/// Measures note density in a way
/// </summary>
internal double Strain = 1;
private double timeElapsed;
private int sameTypeSince = 1;
private bool isRim => BaseHitObject is RimHit;
public TaikoHitObjectDifficulty(TaikoHitObject baseHitObject)
{
BaseHitObject = baseHitObject;
}
internal void CalculateStrains(TaikoHitObjectDifficulty previousHitObject, double timeRate)
{
// Rather simple, but more specialized things are inherently inaccurate due to the big difference playstyles and opinions make.
// See Taiko feedback thread.
timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
double decay = Math.Pow(DECAY_BASE, timeElapsed / 1000);
double addition = 1;
// Only if we are no slider or spinner we get an extra addition
if (previousHitObject.BaseHitObject is Hit && BaseHitObject is Hit
&& BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime < 1000) // And we only want to check out hitobjects which aren't so far in the past
{
addition += typeChangeAddition(previousHitObject);
addition += rhythmChangeAddition(previousHitObject);
}
double additionFactor = 1.0;
// Scale AdditionFactor linearly from 0.4 to 1 for TimeElapsed from 0 to 50
if (timeElapsed < 50.0)
additionFactor = 0.4 + 0.6 * timeElapsed / 50.0;
Strain = previousHitObject.Strain * decay + addition * additionFactor;
}
private TypeSwitch lastTypeSwitchEven = TypeSwitch.None;
private double typeChangeAddition(TaikoHitObjectDifficulty previousHitObject)
{
// If we don't have the same hit type, trigger a type change!
if (previousHitObject.isRim != isRim)
{
lastTypeSwitchEven = previousHitObject.sameTypeSince % 2 == 0 ? TypeSwitch.Even : TypeSwitch.Odd;
// We only want a bonus if the parity of the type switch changes!
switch (previousHitObject.lastTypeSwitchEven)
{
case TypeSwitch.Even:
if (lastTypeSwitchEven == TypeSwitch.Odd)
return type_change_bonus;
break;
case TypeSwitch.Odd:
if (lastTypeSwitchEven == TypeSwitch.Even)
return type_change_bonus;
break;
}
}
// No type change? Increment counter and keep track of last type switch
else
{
lastTypeSwitchEven = previousHitObject.lastTypeSwitchEven;
sameTypeSince = previousHitObject.sameTypeSince + 1;
}
return 0;
}
private double rhythmChangeAddition(TaikoHitObjectDifficulty previousHitObject)
{
// We don't want a division by zero if some random mapper decides to put 2 HitObjects at the same time.
if (timeElapsed == 0 || previousHitObject.timeElapsed == 0)
return 0;
double timeElapsedRatio = Math.Max(previousHitObject.timeElapsed / timeElapsed, timeElapsed / previousHitObject.timeElapsed);
if (timeElapsedRatio >= 8)
return 0;
double difference = Math.Log(timeElapsedRatio, rhythm_change_base) % 1.0;
if (isWithinChangeThreshold(difference))
return rhythm_change_bonus;
return 0;
}
private bool isWithinChangeThreshold(double value)
{
return value > rhythm_change_base_threshold && value < 1 - rhythm_change_base_threshold;
}
private enum TypeSwitch
{
None,
Even,
Odd
}
}
}
@@ -1,35 +1,45 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Replays;
using osu.Game.Users;
namespace osu.Game.Rulesets.Taiko.Replays
{
public class TaikoAutoReplay : Replay
public class TaikoAutoGenerator : AutoGenerator<TaikoHitObject>
{
private readonly Beatmap<TaikoHitObject> beatmap;
private const double swell_hit_speed = 50;
public TaikoAutoReplay(Beatmap<TaikoHitObject> beatmap)
public TaikoAutoGenerator(Beatmap<TaikoHitObject> beatmap)
: base(beatmap)
{
this.beatmap = beatmap;
createAutoReplay();
Replay = new Replay
{
User = new User
{
Username = @"Autoplay",
}
};
}
private void createAutoReplay()
protected Replay Replay;
protected List<ReplayFrame> Frames => Replay.Frames;
public override Replay Generate()
{
bool hitButton = true;
Frames.Add(new ReplayFrame(-100000, null, null, ReplayButtonState.None));
Frames.Add(new ReplayFrame(beatmap.HitObjects[0].StartTime - 1000, null, null, ReplayButtonState.None));
Frames.Add(new ReplayFrame(Beatmap.HitObjects[0].StartTime - 1000, null, null, ReplayButtonState.None));
for (int i = 0; i < beatmap.HitObjects.Count; i++)
for (int i = 0; i < Beatmap.HitObjects.Count; i++)
{
TaikoHitObject h = beatmap.HitObjects[i];
TaikoHitObject h = Beatmap.HitObjects[i];
ReplayButtonState button;
@@ -45,12 +55,13 @@ namespace osu.Game.Rulesets.Taiko.Replays
int d = 0;
int count = 0;
int req = swell.RequiredHits;
double hitRate = swell.Duration / req;
double hitRate = Math.Min(swell_hit_speed, swell.Duration / req);
for (double j = h.StartTime; j < endTime; j += hitRate)
{
switch (d)
{
default:
case 0:
button = ReplayButtonState.Left1;
break;
case 1:
@@ -66,7 +77,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
Frames.Add(new ReplayFrame(j, null, null, button));
d = (d + 1) % 4;
if (++count > req)
if (++count == req)
break;
}
}
@@ -102,15 +113,17 @@ namespace osu.Game.Rulesets.Taiko.Replays
Frames.Add(new ReplayFrame(endTime + KEY_UP_DELAY, null, null, ReplayButtonState.None));
if (i < beatmap.HitObjects.Count - 1)
if (i < Beatmap.HitObjects.Count - 1)
{
double waitTime = beatmap.HitObjects[i + 1].StartTime - 1000;
double waitTime = Beatmap.HitObjects[i + 1].StartTime - 1000;
if (waitTime > endTime)
Frames.Add(new ReplayFrame(waitTime, null, null, ReplayButtonState.None));
}
hitButton = !hitButton;
}
return Replay;
}
}
}
@@ -6,18 +6,133 @@ using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Objects;
using System.Collections.Generic;
using System.Globalization;
using System;
namespace osu.Game.Rulesets.Taiko
{
public class TaikoDifficultyCalculator : DifficultyCalculator<TaikoHitObject>
internal class TaikoDifficultyCalculator : DifficultyCalculator<TaikoHitObject>
{
public TaikoDifficultyCalculator(Beatmap beatmap) : base(beatmap)
private const double star_scaling_factor = 0.04125;
/// <summary>
/// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP.
/// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.
/// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage.
/// </summary>
private const double strain_step = 400;
/// <summary>
/// The weighting of each strain value decays to this number * it's previous value
/// </summary>
private const double decay_weight = 0.9;
/// <summary>
/// HitObjects are stored as a member variable.
/// </summary>
private readonly List<TaikoHitObjectDifficulty> difficultyHitObjects = new List<TaikoHitObjectDifficulty>();
public TaikoDifficultyCalculator(Beatmap beatmap)
: base(beatmap)
{
}
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
{
return 0;
// Fill our custom DifficultyHitObject class, that carries additional information
difficultyHitObjects.Clear();
foreach (var hitObject in Objects)
difficultyHitObjects.Add(new TaikoHitObjectDifficulty(hitObject));
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
if (!calculateStrainValues()) return 0;
double starRating = calculateDifficulty() * star_scaling_factor;
if (categoryDifficulty != null)
{
categoryDifficulty.Add("Strain", starRating.ToString("0.00", CultureInfo.InvariantCulture));
categoryDifficulty.Add("Hit window 300", (35 /*HitObjectManager.HitWindow300*/ / TimeRate).ToString("0.00", CultureInfo.InvariantCulture));
}
return starRating;
}
private bool calculateStrainValues()
{
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
using (List<TaikoHitObjectDifficulty>.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator())
{
if (!hitObjectsEnumerator.MoveNext()) return false;
TaikoHitObjectDifficulty current = hitObjectsEnumerator.Current;
// First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject.
while (hitObjectsEnumerator.MoveNext())
{
var next = hitObjectsEnumerator.Current;
next?.CalculateStrains(current, TimeRate);
current = next;
}
return true;
}
}
private double calculateDifficulty()
{
double actualStrainStep = strain_step * TimeRate;
// Find the highest strain value within each strain step
List<double> highestStrains = new List<double>();
double intervalEndTime = actualStrainStep;
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
TaikoHitObjectDifficulty previousHitObject = null;
foreach (var hitObject in difficultyHitObjects)
{
// While we are beyond the current interval push the currently available maximum to our strain list
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
{
highestStrains.Add(maximumStrain);
// The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
// until the beginning of the next interval.
if (previousHitObject == null)
{
maximumStrain = 0;
}
else
{
double decay = Math.Pow(TaikoHitObjectDifficulty.DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
maximumStrain = previousHitObject.Strain * decay;
}
// Go to the next time interval
intervalEndTime += actualStrainStep;
}
// Obtain maximum strain
maximumStrain = Math.Max(hitObject.Strain, maximumStrain);
previousHitObject = hitObject;
}
// Build the weighted sum over the highest strains for each interval
double difficulty = 0;
double weight = 1;
highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
foreach (double strain in highestStrains)
{
difficulty += weight * strain;
weight *= decay_weight;
}
return difficulty;
}
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter();
@@ -79,8 +79,9 @@
<Compile Include="Objects\RimHit.cs" />
<Compile Include="Objects\Swell.cs" />
<Compile Include="Replays\TaikoFramedReplayInputHandler.cs" />
<Compile Include="Replays\TaikoAutoReplay.cs" />
<Compile Include="Replays\TaikoAutoGenerator.cs" />
<Compile Include="Objects\TaikoHitObject.cs" />
<Compile Include="Objects\TaikoHitObjectDifficulty.cs" />
<Compile Include="TaikoDifficultyCalculator.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Scoring\TaikoScoreProcessor.cs" />
+18 -18
View File
@@ -26,15 +26,15 @@ namespace osu.Game.Tests.Beatmaps.IO
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new HeadlessGameHost())
{
var osu = loadOsu(host);
loadOsu(host);
var temp = prepareTempCopy(osz_path);
Assert.IsTrue(File.Exists(temp));
osu.Dependencies.Get<BeatmapDatabase>().Import(temp);
host.Dependencies.Get<BeatmapDatabase>().Import(temp);
ensureLoaded(osu);
ensureLoaded(host);
Assert.IsFalse(File.Exists(temp));
}
@@ -49,7 +49,7 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.IsTrue(host.IsPrimaryInstance);
Assert.IsTrue(!client.IsPrimaryInstance);
var osu = loadOsu(host);
loadOsu(host);
var temp = prepareTempCopy(osz_path);
@@ -59,7 +59,7 @@ namespace osu.Game.Tests.Beatmaps.IO
if (!importer.ImportAsync(temp).Wait(5000))
Assert.Fail(@"IPC took too long to send");
ensureLoaded(osu);
ensureLoaded(host);
Assert.IsFalse(File.Exists(temp));
}
@@ -71,16 +71,16 @@ namespace osu.Game.Tests.Beatmaps.IO
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new HeadlessGameHost())
{
var osu = loadOsu(host);
loadOsu(host);
var temp = prepareTempCopy(osz_path);
Assert.IsTrue(File.Exists(temp));
using (File.OpenRead(temp))
osu.Dependencies.Get<BeatmapDatabase>().Import(temp);
host.Dependencies.Get<BeatmapDatabase>().Import(temp);
ensureLoaded(osu);
ensureLoaded(host);
Assert.IsTrue(File.Exists(temp));
@@ -105,19 +105,19 @@ namespace osu.Game.Tests.Beatmaps.IO
Thread.Sleep(1);
//reset beatmap database (sqlite and storage backing)
osu.Dependencies.Get<RulesetDatabase>().Reset();
osu.Dependencies.Get<BeatmapDatabase>().Reset();
host.Dependencies.Get<RulesetDatabase>().Reset();
host.Dependencies.Get<BeatmapDatabase>().Reset();
return osu;
}
private void ensureLoaded(OsuGameBase osu, int timeout = 10000)
private void ensureLoaded(GameHost host, int timeout = 10000)
{
IEnumerable<BeatmapSetInfo> resultSets = null;
Action waitAction = () =>
{
while (!(resultSets = osu.Dependencies.Get<BeatmapDatabase>()
while (!(resultSets = host.Dependencies.Get<BeatmapDatabase>()
.Query<BeatmapSetInfo>().Where(s => s.OnlineBeatmapSetID == 241526)).Any())
Thread.Sleep(50);
};
@@ -134,7 +134,7 @@ namespace osu.Game.Tests.Beatmaps.IO
//if we don't re-check here, the set will be inserted but the beatmaps won't be present yet.
waitAction = () =>
{
while ((resultBeatmaps = osu.Dependencies.Get<BeatmapDatabase>()
while ((resultBeatmaps = host.Dependencies.Get<BeatmapDatabase>()
.Query<BeatmapInfo>().Where(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0)).Count() != 12)
Thread.Sleep(50);
};
@@ -143,7 +143,7 @@ namespace osu.Game.Tests.Beatmaps.IO
@"Beatmaps did not import to the database in allocated time");
//fetch children and check we can load from the post-storage path...
var set = osu.Dependencies.Get<BeatmapDatabase>().GetChildren(resultSets.First());
var set = host.Dependencies.Get<BeatmapDatabase>().GetChildren(resultSets.First());
Assert.IsTrue(set.Beatmaps.Count == resultBeatmaps.Count(),
$@"Incorrect database beatmap count post-import ({resultBeatmaps.Count()} but should be {set.Beatmaps.Count}).");
@@ -153,16 +153,16 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.IsTrue(set.Beatmaps.Count > 0);
var beatmap = osu.Dependencies.Get<BeatmapDatabase>().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap;
var beatmap = host.Dependencies.Get<BeatmapDatabase>().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
beatmap = osu.Dependencies.Get<BeatmapDatabase>().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap;
beatmap = host.Dependencies.Get<BeatmapDatabase>().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
beatmap = osu.Dependencies.Get<BeatmapDatabase>().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap;
beatmap = host.Dependencies.Get<BeatmapDatabase>().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
beatmap = osu.Dependencies.Get<BeatmapDatabase>().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap;
beatmap = host.Dependencies.Get<BeatmapDatabase>().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
}
}
+19
View File
@@ -0,0 +1,19 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
namespace osu.Game.Audio
{
public class SampleInfoList : List<SampleInfo>
{
public SampleInfoList()
{
}
public SampleInfoList(IEnumerable<SampleInfo> elements)
{
AddRange(elements);
}
}
}
@@ -35,6 +35,10 @@ namespace osu.Game.Beatmaps
protected DifficultyCalculator(Beatmap beatmap)
{
Objects = CreateBeatmapConverter().Convert(beatmap).HitObjects;
foreach (var h in Objects)
h.ApplyDefaults(beatmap.TimingInfo, beatmap.BeatmapInfo.Difficulty);
PreprocessHitObjects();
}
+2 -4
View File
@@ -17,7 +17,7 @@ namespace osu.Game.Beatmaps.Drawables
/// <summary>
/// Fires when one of our difficulties was selected. Will fire on first expand.
/// </summary>
public Action<BeatmapGroup, BeatmapInfo> SelectionChanged;
public Action<BeatmapGroup, BeatmapPanel> SelectionChanged;
/// <summary>
/// Fires when one of our difficulties is clicked when already selected. Should start playing the map.
@@ -89,8 +89,6 @@ namespace osu.Game.Beatmaps.Drawables
//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;
else
SelectionChanged?.Invoke(this, SelectedPanel.Beatmap);
}
private void panelGainedSelection(BeatmapPanel panel)
@@ -106,7 +104,7 @@ namespace osu.Game.Beatmaps.Drawables
finally
{
State = BeatmapGroupState.Expanded;
SelectionChanged?.Invoke(this, panel.Beatmap);
SelectionChanged?.Invoke(this, SelectedPanel);
}
}
}
@@ -1,19 +1,18 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Game.Configuration;
using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
@@ -23,8 +22,6 @@ namespace osu.Game.Beatmaps.Drawables
private readonly SpriteText title;
private readonly SpriteText artist;
private Bindable<bool> preferUnicode;
private readonly WorkingBeatmap beatmap;
private readonly FillFlowContainer difficultyIcons;
@@ -82,15 +79,10 @@ namespace osu.Game.Beatmaps.Drawables
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
private void load(LocalisationEngine localisation)
{
preferUnicode = config.GetBindable<bool>(OsuConfig.ShowUnicode);
preferUnicode.ValueChanged += unicode =>
{
title.Text = unicode ? beatmap.BeatmapSetInfo.Metadata.TitleUnicode : beatmap.BeatmapSetInfo.Metadata.Title;
artist.Text = unicode ? beatmap.BeatmapSetInfo.Metadata.ArtistUnicode : beatmap.BeatmapSetInfo.Metadata.Artist;
};
preferUnicode.TriggerChange();
title.Current = localisation.GetUnicodePreference(beatmap.BeatmapSetInfo.Metadata.TitleUnicode, beatmap.BeatmapSetInfo.Metadata.Title);
artist.Current = localisation.GetUnicodePreference(beatmap.BeatmapSetInfo.Metadata.ArtistUnicode, beatmap.BeatmapSetInfo.Metadata.Artist);
}
private class PanelBackground : BufferedContainer
@@ -112,7 +104,7 @@ namespace osu.Game.Beatmaps.Drawables
Depth = -1,
Direction = FillDirection.Horizontal,
RelativeSizeAxes = Axes.Both,
// This makes the gradient not be perfectly horizontal, but diagonal at a ~40° angle
// This makes the gradient not be perfectly horizontal, but diagonal at a ~40° angle
Shear = new Vector2(0.8f, 0),
Alpha = 0.5f,
Children = new[]
+3
View File
@@ -94,6 +94,9 @@ namespace osu.Game.Beatmaps
{
if (track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
other.track = track;
if (background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo))
other.background = background;
}
public virtual void Dispose()
+49 -290
View File
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Configuration;
using osu.Framework.Platform;
using osu.Game.Screens.Select;
@@ -12,183 +11,65 @@ namespace osu.Game.Configuration
{
protected override void InitialiseDefaults()
{
#pragma warning disable CS0612 // Type or member is obsolete
// UI/selection defaults
Set(OsuConfig.Ruleset, 0, 0, int.MaxValue);
Set(OsuConfig.BeatmapDetailTab, BeatmapDetailTab.Details);
Set(OsuConfig.DisplayStarsMinimum, 0.0, 0, 10);
Set(OsuConfig.DisplayStarsMaximum, 10.0, 0, 10);
// Online settings
Set(OsuConfig.Username, string.Empty);
Set(OsuConfig.Token, string.Empty);
Set(OsuConfig.Ruleset, 0, 0, int.MaxValue);
Set(OsuConfig.SavePassword, false).ValueChanged += val =>
{
if (val) Set(OsuConfig.SaveUsername, true);
};
Set(OsuConfig.SaveUsername, true).ValueChanged += val =>
{
if (!val) Set(OsuConfig.SavePassword, false);
};
// Audio
Set(OsuConfig.AudioDevice, string.Empty);
Set(OsuConfig.SavePassword, false);
Set(OsuConfig.SaveUsername, true);
Set(OsuConfig.MenuCursorSize, 1.0, 0.5f, 2);
Set(OsuConfig.GameplayCursorSize, 1.0, 0.5f, 2);
Set(OsuConfig.DimLevel, 0.3, 0, 1);
Set(OsuConfig.MouseDisableButtons, false);
Set(OsuConfig.MouseDisableWheel, false);
Set(OsuConfig.SnakingInSliders, true);
Set(OsuConfig.SnakingOutSliders, true);
Set(OsuConfig.MenuParallax, true);
Set(OsuConfig.MenuVoice, true);
Set(OsuConfig.MenuMusic, true);
Set(OsuConfig.BeatmapDetailTab, BeatmapDetailTab.Details);
Set(OsuConfig.AudioOffset, 0, -500.0, 500.0);
// Input
Set(OsuConfig.MenuCursorSize, 1.0, 0.5f, 2);
Set(OsuConfig.GameplayCursorSize, 1.0, 0.5f, 2);
Set(OsuConfig.MouseDisableButtons, false);
Set(OsuConfig.MouseDisableWheel, false);
// Graphics
Set(OsuConfig.ShowFpsDisplay, false);
Set(OsuConfig.MenuParallax, true);
Set(OsuConfig.SnakingInSliders, true);
Set(OsuConfig.SnakingOutSliders, true);
// Gameplay
Set(OsuConfig.DimLevel, 0.3, 0, 1);
Set(OsuConfig.ShowInterface, true);
Set(OsuConfig.KeyOverlay, false);
//todo: implement all settings below this line (remove the Disabled set when doing so).
Set(OsuConfig.AudioOffset, 0, -500.0, 500.0);
// Update
Set(OsuConfig.MouseSpeed, 1.0).Disabled = true;
Set(OsuConfig.BeatmapDirectory, @"Songs").Disabled = true; // TODO: use thi.Disabled = trues
Set(OsuConfig.AllowPublicInvites, true).Disabled = true;
Set(OsuConfig.AutoChatHide, true).Disabled = true;
Set(OsuConfig.AutomaticDownload, true).Disabled = true;
Set(OsuConfig.AutomaticDownloadNoVideo, false).Disabled = true;
Set(OsuConfig.BlockNonFriendPM, false).Disabled = true;
Set(OsuConfig.Bloom, false).Disabled = true;
Set(OsuConfig.BloomSoftening, false).Disabled = true;
Set(OsuConfig.BossKeyFirstActivation, true).Disabled = true;
Set(OsuConfig.ChatAudibleHighlight, true).Disabled = true;
Set(OsuConfig.ChatChannels, string.Empty).Disabled = true;
Set(OsuConfig.ChatFilter, false).Disabled = true;
Set(OsuConfig.ChatHighlightName, true).Disabled = true;
Set(OsuConfig.ChatMessageNotification, true).Disabled = true;
Set(OsuConfig.ChatLastChannel, string.Empty).Disabled = true;
Set(OsuConfig.ChatRemoveForeign, false).Disabled = true;
//Set(OsuConfig.ChatSortMode, UserSortMode.Rank).Disabled = true;
Set(OsuConfig.ComboBurst, true).Disabled = true;
Set(OsuConfig.ComboFire, false).Disabled = true;
Set(OsuConfig.ComboFireHeight, 3).Disabled = true;
Set(OsuConfig.ConfirmExit, false).Disabled = true;
Set(OsuConfig.AutoSendNowPlaying, true).Disabled = true;
Set(OsuConfig.AutomaticCursorSizing, false).Disabled = true;
Set(OsuConfig.Display, 1).Disabled = true;
Set(OsuConfig.DisplayCityLocation, false).Disabled = true;
Set(OsuConfig.DistanceSpacingEnabled, true).Disabled = true;
Set(OsuConfig.EditorTip, 0).Disabled = true;
Set(OsuConfig.VideoEditor, true).Disabled = true;
Set(OsuConfig.EditorDefaultSkin, false).Disabled = true;
Set(OsuConfig.EditorSnakingSliders, true).Disabled = true;
Set(OsuConfig.EditorHitAnimations, false).Disabled = true;
Set(OsuConfig.EditorFollowPoints, true).Disabled = true;
Set(OsuConfig.EditorStacking, true).Disabled = true;
Set(OsuConfig.ForceSliderRendering, false).Disabled = true;
Set(OsuConfig.FpsCounter, false).Disabled = true;
Set(OsuConfig.FrameTimeDisplay, false).Disabled = true;
Set(OsuConfig.GuideTips, @"").Disabled = true;
Set(OsuConfig.CursorRipple, false).Disabled = true;
Set(OsuConfig.HighlightWords, string.Empty).Disabled = true;
Set(OsuConfig.HighResolution, false).Disabled = true;
Set(OsuConfig.HitLighting, true).Disabled = true;
Set(OsuConfig.IgnoreBarline, false).Disabled = true;
Set(OsuConfig.IgnoreBeatmapSamples, false).Disabled = true;
Set(OsuConfig.IgnoreBeatmapSkins, false).Disabled = true;
Set(OsuConfig.IgnoreList, string.Empty).Disabled = true;
Set(OsuConfig.Language, @"unknown").Disabled = true;
Set(OsuConfig.AllowNowPlayingHighlights, false).Disabled = true;
Set(OsuConfig.LastVersion, string.Empty).Disabled = true;
Set(OsuConfig.LastVersionPermissionsFailed, string.Empty).Disabled = true;
Set(OsuConfig.LoadSubmittedThread, true).Disabled = true;
Set(OsuConfig.LobbyPlayMode, -1).Disabled = true;
Set(OsuConfig.ShowInterfaceDuringRelax, false).Disabled = true;
Set(OsuConfig.LobbyShowExistingOnly, false).Disabled = true;
Set(OsuConfig.LobbyShowFriendsOnly, false).Disabled = true;
Set(OsuConfig.LobbyShowFull, false).Disabled = true;
Set(OsuConfig.LobbyShowInProgress, true).Disabled = true;
Set(OsuConfig.LobbyShowPassworded, true).Disabled = true;
Set(OsuConfig.LogPrivateMessages, false).Disabled = true;
Set(OsuConfig.LowResolution, false).Disabled = true;
//Set(OsuConfig.ManiaSpeed, SpeedMania.SPEED_DEFAULT, SpeedMania.SPEED_MIN, SpeedMania.SPEED_MAX).Disabled = true;
Set(OsuConfig.UsePerBeatmapManiaSpeed, true).Disabled = true;
Set(OsuConfig.ManiaSpeedBPMScale, true).Disabled = true;
Set(OsuConfig.MenuTip, 0).Disabled = true;
Set(OsuConfig.MouseSpeed, 1, 0.4, 6).Disabled = true;
Set(OsuConfig.ScoreMeterScale, 1, 0.5, 2).Disabled = true;
//Set(OsuConfig.ScoreMeterScale, 1, 0.5, OsuGame.Tournament ? 10 : 2).Disabled = true;
Set(OsuConfig.DistanceSpacing, 0.8, 0.1, 6).Disabled = true;
Set(OsuConfig.EditorBeatDivisor, 1, 1, 16).Disabled = true;
Set(OsuConfig.EditorGridSize, 32, 4, 32).Disabled = true;
Set(OsuConfig.EditorGridSizeDesign, 32, 4, 32).Disabled = true;
Set(OsuConfig.CustomFrameLimit, 240, 240, 999).Disabled = true;
Set(OsuConfig.MsnIntegration, false).Disabled = true;
Set(OsuConfig.MyPcSucks, false).Disabled = true;
Set(OsuConfig.NotifyFriends, true).Disabled = true;
Set(OsuConfig.NotifySubmittedThread, true).Disabled = true;
Set(OsuConfig.PopupDuringGameplay, true).Disabled = true;
Set(OsuConfig.ProgressBarType, ProgressBarType.Pie).Disabled = true;
Set(OsuConfig.RankType, RankingType.Top).Disabled = true;
Set(OsuConfig.RefreshRate, 60).Disabled = true;
Set(OsuConfig.OverrideRefreshRate, Get<int>(OsuConfig.RefreshRate) != 60).Disabled = true;
//Set(OsuConfig.ScaleMode, ScaleMode.WidescreenConservative).Disabled = true;
Set(OsuConfig.ScoreboardVisible, true).Disabled = true;
Set(OsuConfig.ScoreMeter, ScoreMeterType.Error).Disabled = true;
//Set(OsuConfig.ScoreMeter, OsuGame.Tournament ? ScoreMeterType.Colour : ScoreMeterType.Error).Disabled = true;
Set(OsuConfig.ScreenshotId, 0).Disabled = true;
Set(OsuConfig.MenuSnow, false).Disabled = true;
Set(OsuConfig.MenuTriangles, true).Disabled = true;
Set(OsuConfig.SongSelectThumbnails, true).Disabled = true;
Set(OsuConfig.ScreenshotFormat, ScreenshotFormat.Jpg).Disabled = true;
Set(OsuConfig.ShowReplayComments, true).Disabled = true;
Set(OsuConfig.ShowSpectators, true).Disabled = true;
Set(OsuConfig.ShowStoryboard, true).Disabled = true;
//Set(OsuConfig.Skin, SkinManager.DEFAULT_SKIN).Disabled = true;
Set(OsuConfig.SkinSamples, true).Disabled = true;
Set(OsuConfig.SkipTablet, false).Disabled = true;
Set(OsuConfig.Tablet, false).Disabled = true;
Set(OsuConfig.UpdatePending, false).Disabled = true;
Set(OsuConfig.UseSkinCursor, false).Disabled = true;
Set(OsuConfig.UseTaikoSkin, false).Disabled = true;
Set(OsuConfig.Video, true).Disabled = true;
Set(OsuConfig.Wiimote, false).Disabled = true;
Set(OsuConfig.YahooIntegration, false).Disabled = true;
Set(OsuConfig.ForceFrameFlush, false).Disabled = true;
Set(OsuConfig.DetectPerformanceIssues, true).Disabled = true;
Set(OsuConfig.RawInput, false).Disabled = true;
Set(OsuConfig.AbsoluteToOsuWindow, Get<bool>(OsuConfig.RawInput)).Disabled = true;
Set(OsuConfig.ShowMenuTips, true).Disabled = true;
Set(OsuConfig.HiddenShowFirstApproach, true).Disabled = true;
Set(OsuConfig.ComboColourSliderBall, true).Disabled = true;
Set(OsuConfig.AlternativeChatFont, false).Disabled = true;
Set(OsuConfig.DisplayStarsMaximum, 10.0, 0.0, 10.0).Disabled = true;
Set(OsuConfig.DisplayStarsMinimum, 0.0, 0.0, 10.0).Disabled = true;
Set(OsuConfig.ReleaseStream, ReleaseStream.Lazer).Disabled = true;
Set(OsuConfig.UpdateFailCount, 0).Disabled = true;
//Set(OsuConfig.TreeSortMode, TreeGroupMode.Show_All).Disabled = true;
//Set(OsuConfig.TreeSortMode2, TreeSortMode.Title).Disabled = true;
bool unicodeDefault = false;
switch (Get<string>(OsuConfig.Language))
{
case @"zh":
case @"ja":
case @"ko":
unicodeDefault = true;
break;
}
Set(OsuConfig.ShowUnicode, unicodeDefault);
Set(OsuConfig.PermanentSongInfo, false).Disabled = true;
Set(OsuConfig.Ticker, false).Disabled = true;
Set(OsuConfig.CompatibilityContext, false).Disabled = true;
Set(OsuConfig.CanForceOptimusCompatibility, true).Disabled = true;
Set(OsuConfig.ConfineMouse, Get<bool>(OsuConfig.ConfineMouseToFullscreen) ?
ConfineMouseMode.Fullscreen : ConfineMouseMode.Never).Disabled = true;
GetOriginalBindable<bool>(OsuConfig.SavePassword).ValueChanged += delegate
{
if (Get<bool>(OsuConfig.SavePassword)) Set(OsuConfig.SaveUsername, true);
};
GetOriginalBindable<bool>(OsuConfig.SaveUsername).ValueChanged += delegate
{
if (!Get<bool>(OsuConfig.SaveUsername)) Set(OsuConfig.SavePassword, false);
};
#pragma warning restore CS0612 // Type or member is obsolete
Set(OsuConfig.ReleaseStream, ReleaseStream.Lazer);
}
public OsuConfigManager(Storage storage) : base(storage)
@@ -198,151 +79,29 @@ namespace osu.Game.Configuration
public enum OsuConfig
{
// New osu:
Ruleset,
Token,
// Imported from old osu:
BeatmapDirectory,
AllowPublicInvites,
AutoChatHide,
AutomaticDownload,
AutomaticDownloadNoVideo,
BlockNonFriendPM,
Bloom,
BloomSoftening,
BossKeyFirstActivation,
ChatAudibleHighlight,
ChatChannels,
ChatFilter,
ChatHighlightName,
ChatMessageNotification,
ChatLastChannel,
ChatRemoveForeign,
ChatSortMode,
ComboBurst,
ComboFire,
ComboFireHeight,
ConfirmExit,
AutoSendNowPlaying,
MenuCursorSize,
GameplayCursorSize,
AutomaticCursorSizing,
DimLevel,
Display,
DisplayCityLocation,
DistanceSpacingEnabled,
EditorTip,
VideoEditor,
EditorDefaultSkin,
EditorSnakingSliders,
EditorHitAnimations,
EditorFollowPoints,
EditorStacking,
ForceSliderRendering,
FpsCounter,
FrameTimeDisplay,
GuideTips,
CursorRipple,
HighlightWords,
HighResolution,
HitLighting,
IgnoreBarline,
IgnoreBeatmapSamples,
IgnoreBeatmapSkins,
IgnoreList,
KeyOverlay,
Language,
LastPlayMode,
AllowNowPlayingHighlights,
LastVersion,
LastVersionPermissionsFailed,
LoadSubmittedThread,
LobbyPlayMode,
ShowInterface,
ShowInterfaceDuringRelax,
LobbyShowExistingOnly,
LobbyShowFriendsOnly,
LobbyShowFull,
LobbyShowInProgress,
LobbyShowPassworded,
LogPrivateMessages,
LowResolution,
ManiaSpeed,
UsePerBeatmapManiaSpeed,
ManiaSpeedBPMScale,
MenuTip,
MouseDisableButtons,
MouseDisableWheel,
MouseSpeed,
AudioOffset,
ScoreMeterScale,
DistanceSpacing,
EditorBeatDivisor,
EditorGridSize,
EditorGridSizeDesign,
CustomFrameLimit,
MsnIntegration,
MyPcSucks,
NotifyFriends,
NotifySubmittedThread,
PopupDuringGameplay,
ProgressBarType,
RankType,
RefreshRate,
OverrideRefreshRate,
ScaleMode,
ScoreboardVisible,
ScoreMeter,
ScreenshotId,
MenuSnow,
MenuTriangles,
SongSelectThumbnails,
ScreenshotFormat,
ShowReplayComments,
ShowSpectators,
ShowStoryboard,
Skin,
SkinSamples,
SkipTablet,
SnakingInSliders,
SnakingOutSliders,
Tablet,
UpdatePending,
UserFilter,
UseSkinCursor,
UseTaikoSkin,
Video,
Wiimote,
YahooIntegration,
ForceFrameFlush,
DetectPerformanceIssues,
MenuMusic,
MenuVoice,
MenuParallax,
BeatmapDetailTab,
RawInput,
AbsoluteToOsuWindow,
ConfineMouse,
[Obsolete]
ConfineMouseToFullscreen,
ShowMenuTips,
HiddenShowFirstApproach,
ComboColourSliderBall,
AlternativeChatFont,
Username,
DisplayStarsMaximum,
DisplayStarsMinimum,
AudioDevice,
ReleaseStream,
UpdateFailCount,
SavePassword,
SaveUsername,
TreeSortMode,
TreeSortMode2,
ShowUnicode,
PermanentSongInfo,
Ticker,
CompatibilityContext,
CanForceOptimusCompatibility,
DisplayStarsMinimum,
DisplayStarsMaximum,
SnakingInSliders,
SnakingOutSliders,
ShowFpsDisplay
}
}
-18
View File
@@ -1,18 +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;
namespace osu.Game.Configuration
{
public enum ProgressBarType
{
Off,
Pie,
[Description("Top Right")]
TopRight,
[Description("Bottom Right")]
BottomRight,
Bottom
}
}
+4
View File
@@ -93,5 +93,9 @@ namespace osu.Game.Database
public bool AudioEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null &&
BeatmapSet.Path == other.BeatmapSet.Path &&
(Metadata ?? BeatmapSet.Metadata).AudioFile == (other.Metadata ?? other.BeatmapSet.Metadata).AudioFile;
public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null &&
BeatmapSet.Path == other.BeatmapSet.Path &&
(Metadata ?? BeatmapSet.Metadata).BackgroundFile == (other.Metadata ?? other.BeatmapSet.Metadata).BackgroundFile;
}
}
+11
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.Linq;
using SQLite.Net.Attributes;
namespace osu.Game.Database
@@ -22,5 +23,15 @@ namespace osu.Game.Database
public int PreviewTime { get; set; }
public string AudioFile { get; set; }
public string BackgroundFile { get; set; }
public string[] SearchableTerms => new[]
{
Artist,
ArtistUnicode,
Title,
TitleUnicode,
Source,
Tags
}.Where(s => !string.IsNullOrEmpty(s)).ToArray();
}
}
+19 -12
View File
@@ -14,6 +14,7 @@ using OpenTK.Graphics.ES30;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Colour;
using osu.Framework.Timing;
using System.Diagnostics;
namespace osu.Game.Graphics.Cursor
{
@@ -39,6 +40,8 @@ namespace osu.Game.Graphics.Cursor
private Vector2? lastPosition;
private readonly InputResampler resampler = new InputResampler();
protected override DrawNode CreateDrawNode() => new TrailDrawNode();
protected override void ApplyDrawNode(DrawNode node)
@@ -116,22 +119,26 @@ namespace osu.Game.Graphics.Cursor
if (lastPosition == null)
{
lastPosition = state.Mouse.NativeState.Position;
resampler.AddPosition(lastPosition.Value);
return base.OnMouseMove(state);
}
Vector2 pos1 = lastPosition.Value;
Vector2 pos2 = state.Mouse.NativeState.Position;
Vector2 diff = pos2 - pos1;
float distance = diff.Length;
Vector2 direction = diff / distance;
float interval = size.X / 2 * 0.9f;
for (float d = interval; d < distance; d += interval)
foreach (Vector2 pos2 in resampler.AddPosition(state.Mouse.NativeState.Position))
{
lastPosition = pos1 + direction * d;
addPosition(lastPosition.Value);
Trace.Assert(lastPosition.HasValue);
Vector2 pos1 = lastPosition.Value;
Vector2 diff = pos2 - pos1;
float distance = diff.Length;
Vector2 direction = diff / distance;
float interval = size.X / 2 * 0.9f;
for (float d = interval; d < distance; d += interval)
{
lastPosition = pos1 + direction * d;
addPosition(lastPosition.Value);
}
}
return base.OnMouseMove(state);
+15 -13
View File
@@ -123,6 +123,8 @@ namespace osu.Game.Graphics.UserInterface
set { label.Text = value; }
}
protected readonly TextAwesome Icon;
private Color4? accentColour;
public virtual Color4 AccentColour
{
@@ -145,19 +147,19 @@ namespace osu.Game.Graphics.UserInterface
Foreground.Children = new Drawable[]
{
label = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
new TextAwesome
{
Icon = FontAwesome.fa_chevron_down,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Margin = new MarginPadding { Right = 4 },
TextSize = 20
}
label = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
Icon = new TextAwesome
{
Icon = FontAwesome.fa_chevron_down,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Margin = new MarginPadding { Right = 4 },
TextSize = 20
}
};
}
@@ -131,14 +131,14 @@ namespace osu.Game.Graphics.UserInterface
foreach (var star in stars.Children)
{
star.ClearTransforms(true);
if (count <= newValue)
star.Delay(Math.Max(i - count, 0) * animationDelay, true);
else
star.Delay(Math.Max(count - 1 - i, 0) * animationDelay, true);
star.FadeTo(i < newValue ? 1.0f : minStarAlpha, fadingDuration);
star.Icon.ScaleTo(getStarScale(i, newValue), scalingDuration, scalingEasing);
star.DelayReset();
var delay = (count <= newValue ? Math.Max(i - count, 0) : Math.Max(count - 1 - i, 0)) * animationDelay;
using (BeginDelayedSequence(delay, true))
{
star.FadeTo(i < newValue ? 1.0f : minStarAlpha, fadingDuration);
star.Icon.ScaleTo(getStarScale(i, newValue), scalingDuration, scalingEasing);
}
i++;
}
@@ -6,7 +6,7 @@ using osu.Game.Database;
namespace osu.Game.Online.API.Requests
{
public class GetBeatmapDetailsRequest : APIRequest<GetBeatmapDeatilsResponse>
public class GetBeatmapDetailsRequest : APIRequest<GetBeatmapDetailsResponse>
{
private readonly BeatmapInfo beatmap;
@@ -20,7 +20,7 @@ namespace osu.Game.Online.API.Requests
protected override string Target => $@"beatmaps/{lookupString}";
}
public class GetBeatmapDeatilsResponse : BeatmapMetrics
public class GetBeatmapDetailsResponse : BeatmapMetrics
{
//the online API returns some metrics as a nested object.
[JsonProperty(@"failtimes")]
+12
View File
@@ -19,6 +19,7 @@ using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.Processing;
using osu.Game.Online.API;
using SQLite.Net;
using osu.Framework.Graphics.Performance;
namespace osu.Game
{
@@ -44,6 +45,8 @@ namespace osu.Game
public readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
private Bindable<bool> fpsDisplayVisible;
protected AssemblyName AssemblyName => Assembly.GetEntryAssembly()?.GetName() ?? new AssemblyName { Version = new Version() };
public bool IsDeployedBuild => AssemblyName.Version.Major > 0;
@@ -160,6 +163,15 @@ namespace osu.Game
},
}
});
// TODO: This is temporary until we reimplement the local FPS display.
// It's just to allow end-users to access the framework FPS display without knowing the shortcut key.
fpsDisplayVisible = LocalConfig.GetBindable<bool>(OsuConfig.ShowFpsDisplay);
fpsDisplayVisible.ValueChanged += val =>
{
FrameStatisticsMode = val ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None;
};
fpsDisplayVisible.TriggerChange();
}
public override void SetHost(GameHost host)
@@ -16,7 +16,6 @@ namespace osu.Game.Overlays.Mods
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
ButtonColour = colours.Blue;
SelectedColour = colours.BlueLight;
}
@@ -16,7 +16,6 @@ namespace osu.Game.Overlays.Mods
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
ButtonColour = colours.Yellow;
SelectedColour = colours.YellowLight;
}
@@ -16,7 +16,6 @@ namespace osu.Game.Overlays.Mods
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
ButtonColour = colours.Green;
SelectedColour = colours.GreenLight;
}
+13 -40
View File
@@ -19,7 +19,11 @@ using System.Linq;
namespace osu.Game.Overlays.Mods
{
public class ModButton : FillFlowContainer
/// <summary>
/// Represents a clickable button which can cycle through one of more mods.
/// </summary>
public class ModButton : ModButtonEmpty
{
private ModIcon foregroundIcon { get; set; }
private readonly SpriteText text;
@@ -51,7 +55,7 @@ namespace osu.Game.Overlays.Mods
iconsContainer.RotateTo(Selected ? 5f : 0f, 300, EasingTypes.OutElastic);
iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, EasingTypes.OutElastic);
foregroundIcon.Colour = Selected ? SelectedColour : ButtonColour;
foregroundIcon.Highlighted = Selected;
if (mod != null)
displayMod(SelectedMod ?? Mods[0]);
@@ -60,23 +64,6 @@ namespace osu.Game.Overlays.Mods
public bool Selected => selectedIndex != -1;
private Color4 buttonColour;
public Color4 ButtonColour
{
get
{
return buttonColour;
}
set
{
if (value == buttonColour) return;
buttonColour = value;
foreach (ModIcon icon in iconsContainer.Children)
{
icon.Colour = value;
}
}
}
private Color4 selectedColour;
public Color4 SelectedColour
@@ -127,7 +114,7 @@ namespace osu.Game.Overlays.Mods
// the mods from Mod, only multiple if Mod is a MultiMod
public Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex);
public override Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex);
[BackgroundDependencyLoader]
private void load(AudioManager audio)
@@ -180,50 +167,35 @@ namespace osu.Game.Overlays.Mods
{
iconsContainer.Add(new[]
{
new ModIcon
new ModIcon(Mods[0])
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Position = new Vector2(1.5f),
Colour = ButtonColour
},
foregroundIcon = new ModIcon
foregroundIcon = new ModIcon(Mods[0])
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Position = new Vector2(-1.5f),
Colour = ButtonColour
},
});
}
else
{
iconsContainer.Add(foregroundIcon = new ModIcon
iconsContainer.Add(foregroundIcon = new ModIcon(Mod)
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Colour = ButtonColour
});
}
}
protected override void LoadComplete()
public ModButton(Mod mod)
{
base.LoadComplete();
foreach (ModIcon icon in iconsContainer.Children)
icon.Colour = ButtonColour;
}
public ModButton(Mod m)
{
Direction = FillDirection.Vertical;
Spacing = new Vector2(0f, -5f);
Size = new Vector2(100f);
AlwaysPresent = true;
Children = new Drawable[]
{
new Container
@@ -243,13 +215,14 @@ namespace osu.Game.Overlays.Mods
},
text = new OsuSpriteText
{
Y = 75,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
TextSize = 18,
},
};
Mod = m;
Mod = mod;
}
}
}
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Overlays.Mods
{
/// <summary>
/// A mod button used exclusively for providing an empty space the size of a mod button.
/// </summary>
public class ModButtonEmpty : Container
{
public virtual Mod SelectedMod => null;
public ModButtonEmpty()
{
Size = new Vector2(100f);
AlwaysPresent = true;
}
}
}
+40 -47
View File
@@ -11,6 +11,8 @@ using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using System;
using System.Linq;
using System.Collections.Generic;
namespace osu.Game.Overlays.Mods
{
@@ -18,7 +20,7 @@ namespace osu.Game.Overlays.Mods
{
private readonly OsuSpriteText headerLabel;
public FillFlowContainer<ModButton> ButtonsContainer { get; }
public FillFlowContainer<ModButtonEmpty> ButtonsContainer { get; }
public Action<Mod> Action;
protected abstract Key[] ToggleKeys { get; }
@@ -36,47 +38,30 @@ namespace osu.Game.Overlays.Mods
}
}
public IEnumerable<Mod> SelectedMods => buttons.Select(b => b.SelectedMod).Where(m => m != null);
public IEnumerable<Mod> Mods
{
set
{
var modContainers = value.Select(m =>
{
if (m == null)
return new ModButtonEmpty();
else
return new ModButton(m)
{
SelectedColour = selectedColour,
Action = Action,
};
}).ToArray();
ButtonsContainer.Children = modContainers;
buttons = modContainers.OfType<ModButton>().ToArray();
}
}
private ModButton[] buttons = { };
public ModButton[] Buttons
{
get
{
return buttons;
}
set
{
if (value == buttons) return;
buttons = value;
foreach (ModButton button in value)
{
button.ButtonColour = ButtonColour;
button.SelectedColour = selectedColour;
button.Action = Action;
}
ButtonsContainer.Children = value;
}
}
private Color4 buttonsBolour = Color4.White;
public Color4 ButtonColour
{
get
{
return buttonsBolour;
}
set
{
if (value == buttonsBolour) return;
buttonsBolour = value;
foreach (ModButton button in buttons)
{
button.ButtonColour = value;
}
}
}
private Color4 selectedColour = Color4.White;
public Color4 SelectedColour
@@ -91,17 +76,15 @@ namespace osu.Game.Overlays.Mods
selectedColour = value;
foreach (ModButton button in buttons)
{
button.SelectedColour = value;
}
}
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
var index = Array.IndexOf(ToggleKeys, args.Key);
if (index > -1 && index < Buttons.Length)
Buttons[index].SelectNext();
if (index > -1 && index < buttons.Length)
buttons[index].SelectNext();
return base.OnKeyDown(state, args);
}
@@ -109,8 +92,18 @@ namespace osu.Game.Overlays.Mods
public void DeselectAll()
{
foreach (ModButton button in buttons)
{
button.Deselect();
}
public void DeselectTypes(Type[] modTypes)
{
foreach (var button in buttons)
{
Mod selected = button.SelectedMod;
if (selected == null) continue;
foreach (Type type in modTypes)
if (type.IsInstanceOfType(selected))
button.Deselect();
}
}
@@ -127,7 +120,7 @@ namespace osu.Game.Overlays.Mods
Position = new Vector2(0f, 0f),
Font = @"Exo2.0-Bold"
},
ButtonsContainer = new FillFlowContainer<ModButton>
ButtonsContainer = new FillFlowContainer<ModButtonEmpty>
{
AutoSizeAxes = Axes.Both,
Origin = Anchor.BottomLeft,
+3 -10
View File
@@ -44,7 +44,7 @@ namespace osu.Game.Overlays.Mods
var instance = newRuleset.CreateInstance();
foreach (ModSection section in modSectionsContainer.Children)
section.Buttons = instance.GetModsFor(section.ModType).Select(m => new ModButton(m)).ToArray();
section.Mods = instance.GetModsFor(section.ModType);
refreshSelectedMods();
}
@@ -103,14 +103,7 @@ namespace osu.Game.Overlays.Mods
{
if (modTypes.Length == 0) return;
foreach (ModSection section in modSectionsContainer.Children)
foreach (ModButton button in section.Buttons)
{
Mod selected = button.SelectedMod;
if (selected == null) continue;
foreach (Type type in modTypes)
if (type.IsInstanceOfType(selected))
button.Deselect();
}
section.DeselectTypes(modTypes);
}
private void modButtonPressed(Mod selectedMod)
@@ -122,7 +115,7 @@ namespace osu.Game.Overlays.Mods
private void refreshSelectedMods()
{
SelectedMods.Value = modSectionsContainer.Children.SelectMany(s => s.Buttons.Select(x => x.SelectedMod).Where(x => x != null)).ToArray();
SelectedMods.Value = modSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
double multiplier = 1.0;
bool ranked = true;
+137
View File
@@ -0,0 +1,137 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Select;
using OpenTK;
using OpenTK.Graphics;
using System;
namespace osu.Game.Overlays.Music
{
internal class FilterControl : Container
{
public readonly FilterTextBox Search;
public FilterControl()
{
Children = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
Search = new FilterTextBox
{
RelativeSizeAxes = Axes.X,
Height = 40,
Exit = () => ExitRequested?.Invoke(),
},
new CollectionsDropdown<PlaylistCollection>
{
RelativeSizeAxes = Axes.X,
Items = new[] { new KeyValuePair<string, PlaylistCollection>(@"All", PlaylistCollection.All) },
}
},
},
};
Search.Current.ValueChanged += current_ValueChanged;
}
private void current_ValueChanged(string newValue) => FilterChanged?.Invoke(newValue);
public Action ExitRequested;
public Action<string> FilterChanged;
public class FilterTextBox : SearchTextBox
{
private Color4 backgroundColour;
protected override Color4 BackgroundUnfocused => backgroundColour;
protected override Color4 BackgroundFocused => backgroundColour;
public FilterTextBox()
{
Masking = true;
CornerRadius = 5;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
backgroundColour = colours.Gray2;
}
}
private class CollectionsDropdown<T> : OsuDropdown<T>
{
protected override DropdownHeader CreateHeader() => new CollectionsHeader { AccentColour = AccentColour };
protected override Menu CreateMenu() => new CollectionsMenu();
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AccentColour = colours.Gray6;
}
private class CollectionsHeader : OsuDropdownHeader
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = colours.Gray4;
}
public CollectionsHeader()
{
CornerRadius = 5;
Height = 30;
Icon.TextSize = 14;
Icon.Margin = new MarginPadding(0);
Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 10, Right = 10 };
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.3f),
Radius = 3,
Offset = new Vector2(0f, 1f),
};
}
}
private class CollectionsMenu : OsuMenu
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Background.Colour = colours.Gray4;
}
public CollectionsMenu()
{
CornerRadius = 5;
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.3f),
Radius = 3,
Offset = new Vector2(0f, 1f),
};
}
}
}
}
}
+137
View File
@@ -0,0 +1,137 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Localisation;
namespace osu.Game.Overlays.Music
{
internal class PlaylistItem : Container, IFilterable
{
private const float fade_duration = 100;
private Color4 hoverColour;
private TextAwesome handle;
private OsuSpriteText title;
public readonly BeatmapSetInfo BeatmapSetInfo;
public Action<BeatmapSetInfo> OnSelect;
private bool selected;
public bool Selected
{
get { return selected; }
set
{
if (value == selected) return;
selected = value;
Flush(true);
title.FadeColour(Selected ? hoverColour : Color4.White, fade_duration);
}
}
public PlaylistItem(BeatmapSetInfo setInfo)
{
BeatmapSetInfo = setInfo;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Top = 3, Bottom = 3 };
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationEngine localisation)
{
BeatmapMetadata metadata = BeatmapSetInfo.Metadata;
FilterTerms = metadata.SearchableTerms;
Children = new Drawable[]
{
handle = new TextAwesome
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
TextSize = 12,
Colour = colours.Gray5,
Icon = FontAwesome.fa_bars,
Alpha = 0f,
Margin = new MarginPadding { Left = 5 },
Padding = new MarginPadding { Top = 2 },
},
new FillFlowContainer<OsuSpriteText>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Left = 20 },
Spacing = new Vector2(5f, 0f),
Children = new []
{
title = new OsuSpriteText
{
TextSize = 16,
Font = @"Exo2.0-Regular",
Current = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title),
},
new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-Bold",
Colour = colours.Gray9,
Padding = new MarginPadding { Top = 1 },
Current = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist),
}
}
},
};
hoverColour = colours.Yellow;
}
protected override bool OnHover(Framework.Input.InputState state)
{
handle.FadeIn(fade_duration);
return base.OnHover(state);
}
protected override void OnHoverLost(Framework.Input.InputState state)
{
handle.FadeOut(fade_duration);
}
protected override bool OnClick(Framework.Input.InputState state)
{
OnSelect?.Invoke(BeatmapSetInfo);
return true;
}
public string[] FilterTerms { get; private set; }
private bool matching = true;
public bool MatchingCurrentFilter
{
set
{
if (matching == value) return;
matching = value;
FadeTo(matching ? 1 : 0, 200);
}
}
}
}
+94
View File
@@ -0,0 +1,94 @@
// 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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Database;
namespace osu.Game.Overlays.Music
{
internal class PlaylistList : Container
{
private readonly FillFlowContainer<PlaylistItem> items;
public IEnumerable<BeatmapSetInfo> BeatmapSets
{
set
{
items.Children = value.Select(item => new PlaylistItem(item) { OnSelect = itemSelected }).ToList();
}
}
private void itemSelected(BeatmapSetInfo b)
{
OnSelect?.Invoke(b);
}
public Action<BeatmapSetInfo> OnSelect;
private readonly SearchContainer search;
public void Filter(string searchTerm) => search.SearchTerm = searchTerm;
public BeatmapSetInfo SelectedItem
{
get { return items.Children.FirstOrDefault(i => i.Selected)?.BeatmapSetInfo; }
set
{
foreach (PlaylistItem s in items.Children)
s.Selected = s.BeatmapSetInfo.ID == value?.ID;
}
}
public PlaylistList()
{
Children = new Drawable[]
{
new ScrollContainer
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
search = new SearchContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
items = new ItemSearchContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
},
}
}
},
},
};
}
private class ItemSearchContainer : FillFlowContainer<PlaylistItem>, IHasFilterableChildren
{
public string[] FilterTerms => new string[] { };
public bool MatchingCurrentFilter
{
set
{
if (value)
InvalidateLayout();
}
}
public IEnumerable<IFilterable> FilterableChildren => Children;
public ItemSearchContainer()
{
LayoutDuration = 200;
LayoutEasing = EasingTypes.OutQuint;
}
}
}
}
+164
View File
@@ -0,0 +1,164 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions;
namespace osu.Game.Overlays.Music
{
public class PlaylistOverlay : OverlayContainer
{
private const float transition_duration = 600;
private const float playlist_height = 510;
private FilterControl filter;
private PlaylistList list;
private TrackManager trackManager;
private BeatmapDatabase beatmaps;
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
public IEnumerable<BeatmapSetInfo> BeatmapSets;
[BackgroundDependencyLoader]
private void load(OsuGameBase game, BeatmapDatabase beatmaps, OsuColour colours)
{
this.beatmaps = beatmaps;
trackManager = game.Audio.Track;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
CornerRadius = 5,
Masking = true,
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(40),
Radius = 5,
},
Children = new Drawable[]
{
new Box
{
Colour = colours.Gray3,
RelativeSizeAxes = Axes.Both,
},
list = new PlaylistList
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 95, Bottom = 10, Right = 10 },
OnSelect = itemSelected,
},
filter = new FilterControl
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
ExitRequested = () => State = Visibility.Hidden,
FilterChanged = search => list.Filter(search),
Padding = new MarginPadding(10),
},
},
},
};
list.BeatmapSets = BeatmapSets = beatmaps.GetAllWithChildren<BeatmapSetInfo>().ToList();
beatmapBacking.BindTo(game.Beatmap);
}
protected override void LoadComplete()
{
base.LoadComplete();
beatmapBacking.ValueChanged += b => list.SelectedItem = b?.BeatmapSetInfo;
beatmapBacking.TriggerChange();
}
protected override void PopIn()
{
filter.Search.HoldFocus = true;
Schedule(() => filter.Search.TriggerFocus());
ResizeTo(new Vector2(1, playlist_height), transition_duration, EasingTypes.OutQuint);
FadeIn(transition_duration, EasingTypes.OutQuint);
}
protected override void PopOut()
{
filter.Search.HoldFocus = false;
ResizeTo(new Vector2(1, 0), transition_duration, EasingTypes.OutQuint);
FadeOut(transition_duration);
}
private void itemSelected(BeatmapSetInfo set)
{
if (set.ID == (beatmapBacking.Value?.BeatmapSetInfo?.ID ?? -1))
{
beatmapBacking.Value?.Track?.Seek(0);
return;
}
playSpecified(set.Beatmaps[0]);
}
public void PlayPrevious()
{
var currentID = beatmapBacking.Value?.BeatmapSetInfo.ID ?? -1;
var available = BeatmapSets.Reverse();
var playable = available.SkipWhile(b => b.ID != currentID).Skip(1).FirstOrDefault() ?? available.FirstOrDefault();
if (playable != null)
playSpecified(playable.Beatmaps[0]);
}
public void PlayNext()
{
var currentID = beatmapBacking.Value?.BeatmapSetInfo.ID ?? -1;
var available = BeatmapSets;
var playable = available.SkipWhile(b => b.ID != currentID).Skip(1).FirstOrDefault() ?? available.FirstOrDefault();
if (playable != null)
playSpecified(playable.Beatmaps[0]);
}
private void playSpecified(BeatmapInfo info)
{
beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking);
Task.Run(() =>
{
var track = beatmapBacking.Value.Track;
trackManager.SetExclusive(track);
track.Start();
}).ContinueWith(task => Schedule(task.ThrowIfFaulted), TaskContinuationOptions.OnlyOnFaulted);
}
}
//todo: placeholder
public enum PlaylistCollection
{
All
}
}
+193 -190
View File
@@ -2,60 +2,60 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Game.Graphics.Sprites;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Threading;
using osu.Game.Overlays.Music;
namespace osu.Game.Overlays
{
public class MusicController : FocusedOverlayContainer
{
private Drawable currentBackground;
private DragBar progress;
private Button playButton;
private SpriteText title, artist;
private const float player_height = 130;
private List<BeatmapSetInfo> playList;
private readonly List<BeatmapInfo> playHistory = new List<BeatmapInfo>();
private int playListIndex;
private int playHistoryIndex = -1;
private TrackManager trackManager;
private Bindable<WorkingBeatmap> beatmapSource;
private Bindable<bool> preferUnicode;
private WorkingBeatmap current;
private BeatmapDatabase beatmaps;
private Container dragContainer;
private const float transition_length = 800;
private const float progress_height = 10;
private const float bottom_black_area_height = 55;
private Drawable currentBackground;
private DragBar progressBar;
private Button playButton;
private Button playlistButton;
private SpriteText title, artist;
private PlaylistOverlay playlist;
private LocalisationEngine localisation;
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
private Container dragContainer;
private Container playerContainer;
public MusicController()
{
Width = 400;
Height = 130;
Margin = new MarginPadding(10);
}
@@ -81,247 +81,247 @@ namespace osu.Game.Overlays
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game, OsuConfigManager config, BeatmapDatabase beatmaps, OsuColour colours)
private void load(OsuGameBase game, OsuColour colours, LocalisationEngine localisation)
{
this.localisation = localisation;
Children = new Drawable[]
{
dragContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Masking = true,
CornerRadius = 5,
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(40),
Radius = 5,
},
RelativeSizeAxes = Axes.Both,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
title = new OsuSpriteText
playlist = new PlaylistOverlay
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.TopCentre,
Position = new Vector2(0, 40),
TextSize = 25,
Colour = Color4.White,
Text = @"Nothing to play",
Font = @"Exo2.0-MediumItalic"
},
artist = new OsuSpriteText
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Position = new Vector2(0, 45),
TextSize = 15,
Colour = Color4.White,
Text = @"Nothing to play",
Font = @"Exo2.0-BoldItalic"
},
new Container
{
Padding = new MarginPadding { Bottom = progress_height },
Height = bottom_black_area_height,
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Children = new Drawable[]
Y = player_height + 10,
},
playerContainer = new Container
{
RelativeSizeAxes = Axes.X,
Height = player_height,
Masking = true,
CornerRadius = 5,
EdgeEffect = new EdgeEffect
{
new FillFlowContainer<Button>
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(40),
Radius = 5,
},
Children = new[]
{
currentBackground = new Background(),
title = new OsuSpriteText
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new[]
Origin = Anchor.BottomCentre,
Anchor = Anchor.TopCentre,
Position = new Vector2(0, 40),
TextSize = 25,
Colour = Color4.White,
Text = @"Nothing to play",
Font = @"Exo2.0-MediumItalic"
},
artist = new OsuSpriteText
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Position = new Vector2(0, 45),
TextSize = 15,
Colour = Color4.White,
Text = @"Nothing to play",
Font = @"Exo2.0-BoldItalic"
},
new Container
{
Padding = new MarginPadding { Bottom = progress_height },
Height = bottom_black_area_height,
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Children = new Drawable[]
{
new Button
new FillFlowContainer<Button>
{
Action = prev,
Icon = FontAwesome.fa_step_backward,
},
playButton = new Button
{
Scale = new Vector2(1.4f),
IconScale = new Vector2(1.4f),
Action = () =>
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new[]
{
if (current?.Track == null) return;
if (current.Track.IsRunning)
current.Track.Stop();
else
current.Track.Start();
},
Icon = FontAwesome.fa_play_circle_o,
new Button
{
Action = prev,
Icon = FontAwesome.fa_step_backward,
},
playButton = new Button
{
Scale = new Vector2(1.4f),
IconScale = new Vector2(1.4f),
Action = play,
Icon = FontAwesome.fa_play_circle_o,
},
new Button
{
Action = next,
Icon = FontAwesome.fa_step_forward,
},
}
},
new Button
playlistButton = new Button
{
Action = next,
Icon = FontAwesome.fa_step_forward,
Origin = Anchor.Centre,
Anchor = Anchor.CentreRight,
Position = new Vector2(-bottom_black_area_height / 2, 0),
Icon = FontAwesome.fa_bars,
Action = () => playlist.ToggleVisibility(),
},
}
},
new Button
progressBar = new DragBar
{
Origin = Anchor.Centre,
Anchor = Anchor.CentreRight,
Position = new Vector2(-bottom_black_area_height / 2, 0),
Icon = FontAwesome.fa_bars,
},
}
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Height = progress_height,
Colour = colours.Yellow,
SeekRequested = seek
}
},
},
progress = new DragBar
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Height = progress_height,
Colour = colours.Yellow,
SeekRequested = seek
}
}
}
};
this.beatmaps = beatmaps;
trackManager = game.Audio.Track;
preferUnicode = config.GetBindable<bool>(OsuConfig.ShowUnicode);
preferUnicode.ValueChanged += unicode => updateDisplay(current, TransformDirection.None);
beatmapBacking.BindTo(game.Beatmap);
beatmapSource = game.Beatmap ?? new Bindable<WorkingBeatmap>();
playList = beatmaps.GetAllWithChildren<BeatmapSetInfo>();
currentBackground = new MusicControllerBackground();
dragContainer.Add(currentBackground);
playlist.StateChanged += (c, s) => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, EasingTypes.OutQuint);
}
protected override void LoadComplete()
{
beatmapSource.ValueChanged += workingChanged;
beatmapSource.TriggerChange();
beatmapBacking.ValueChanged += beatmapChanged;
beatmapBacking.TriggerChange();
base.LoadComplete();
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
Height = dragContainer.Height;
}
protected override void Update()
{
base.Update();
if (pendingBeatmapSwitch != null)
{
pendingBeatmapSwitch();
pendingBeatmapSwitch = null;
}
if (current?.TrackLoaded ?? false)
{
progress.UpdatePosition(current.Track.Length == 0 ? 0 : (float)(current.Track.CurrentTime / current.Track.Length));
playButton.Icon = current.Track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o;
var track = current.Track;
if (current.Track.HasCompleted && !current.Track.Looping) next();
progressBar.UpdatePosition(track.Length == 0 ? 0 : (float)(track.CurrentTime / track.Length));
playButton.Icon = track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o;
if (track.HasCompleted && !track.Looping) next();
}
else
playButton.Icon = FontAwesome.fa_play_circle_o;
}
private void workingChanged(WorkingBeatmap beatmap)
private void play()
{
progress.IsEnabled = beatmap != null;
if (beatmap == current) return;
bool audioEquals = current?.BeatmapInfo?.AudioEquals(beatmap?.BeatmapInfo) ?? false;
current = beatmap;
updateDisplay(current, audioEquals ? TransformDirection.None : TransformDirection.Next);
appendToHistory(current?.BeatmapInfo);
}
var track = current?.Track;
private void appendToHistory(BeatmapInfo beatmap)
{
if (beatmap == null) return;
if (playHistoryIndex >= 0)
if (track == null)
{
if (beatmap.AudioEquals(playHistory[playHistoryIndex]))
return;
if (playHistoryIndex < playHistory.Count - 1)
playHistory.RemoveRange(playHistoryIndex + 1, playHistory.Count - playHistoryIndex - 1);
playlist.PlayNext();
return;
}
playHistory.Insert(++playHistoryIndex, beatmap);
if (track.IsRunning)
track.Stop();
else
track.Start();
}
private void prev()
{
if (playHistoryIndex > 0)
play(playHistory[--playHistoryIndex], false);
queuedDirection = TransformDirection.Prev;
playlist.PlayPrevious();
}
private void next()
{
if (playHistoryIndex < playHistory.Count - 1)
play(playHistory[++playHistoryIndex], true);
queuedDirection = TransformDirection.Next;
playlist.PlayNext();
}
private WorkingBeatmap current;
private TransformDirection? queuedDirection;
private void beatmapChanged(WorkingBeatmap beatmap)
{
progressBar.IsEnabled = beatmap != null;
bool audioEquals = beatmapBacking.Value?.BeatmapInfo?.AudioEquals(current?.BeatmapInfo) ?? false;
TransformDirection direction;
if (audioEquals)
direction = TransformDirection.None;
else if (queuedDirection.HasValue)
{
direction = queuedDirection.Value;
queuedDirection = null;
}
else
{
if (playList.Count == 0) return;
if (current != null && playList.Count == 1) return;
//shuffle
BeatmapInfo nextToPlay;
do
{
int j = RNG.Next(playListIndex, playList.Count);
if (j != playListIndex)
{
BeatmapSetInfo temp = playList[playListIndex];
playList[playListIndex] = playList[j];
playList[j] = temp;
}
//figure out the best direction based on order in playlist.
var last = current == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo.ID).Count();
var next = beatmapBacking.Value == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmapBacking.Value.BeatmapSetInfo.ID).Count();
nextToPlay = playList[playListIndex++].Beatmaps[0];
if (playListIndex == playList.Count) playListIndex = 0;
}
while (nextToPlay.AudioEquals(current?.BeatmapInfo));
play(nextToPlay, true);
appendToHistory(nextToPlay);
direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
}
current = beatmapBacking.Value;
updateDisplay(beatmapBacking, direction);
queuedDirection = null;
}
private void play(BeatmapInfo info, bool isNext)
{
current = beatmaps.GetWorkingBeatmap(info, current);
Task.Run(() =>
{
trackManager.SetExclusive(current.Track);
current.Track.Start();
beatmapSource.Value = current;
}).ContinueWith(task => Schedule(task.ThrowIfFaulted), TaskContinuationOptions.OnlyOnFaulted);
updateDisplay(current, isNext ? TransformDirection.Next : TransformDirection.Prev);
}
private Action pendingBeatmapSwitch;
private ScheduledDelegate pendingBeatmapSwitch;
private void updateDisplay(WorkingBeatmap beatmap, TransformDirection direction)
{
//we might be off-screen when this update comes in.
//rather than Scheduling, manually handle this to avoid possible memory contention.
pendingBeatmapSwitch = () =>
pendingBeatmapSwitch?.Cancel();
pendingBeatmapSwitch = Schedule(delegate
{
Task.Run(() =>
{
if (beatmap?.Beatmap == null)
if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists
{
title.Current = null;
title.Text = @"Nothing to play";
artist.Current = null;
artist.Text = @"Nothing to play";
}
else
{
BeatmapMetadata metadata = beatmap.Beatmap.BeatmapInfo.Metadata;
title.Text = preferUnicode ? metadata.TitleUnicode : metadata.Title;
artist.Text = preferUnicode ? metadata.ArtistUnicode : metadata.Artist;
title.Current = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title);
artist.Current = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist);
}
});
dragContainer.Add(new AsyncLoadWrapper(new MusicControllerBackground(beatmap)
playerContainer.Add(new AsyncLoadWrapper(new Background(beatmap)
{
OnLoadComplete = d =>
{
@@ -345,17 +345,15 @@ namespace osu.Game.Overlays
{
Depth = float.MaxValue,
});
};
});
}
private void seek(float position)
{
current?.Track?.Seek(current.Track.Length * position);
current?.Track?.Start();
var track = current?.Track;
track?.Seek(track.Length * position);
}
private const float transition_length = 800;
protected override void PopIn()
{
base.PopIn();
@@ -372,14 +370,19 @@ namespace osu.Game.Overlays
dragContainer.ScaleTo(0.9f, transition_length, EasingTypes.OutQuint);
}
private enum TransformDirection { None, Next, Prev }
private enum TransformDirection
{
None,
Next,
Prev
}
private class MusicControllerBackground : BufferedContainer
private class Background : BufferedContainer
{
private readonly Sprite sprite;
private readonly WorkingBeatmap beatmap;
public MusicControllerBackground(WorkingBeatmap beatmap = null)
public Background(WorkingBeatmap beatmap = null)
{
this.beatmap = beatmap;
CacheDrawnFrameBuffer = true;
@@ -0,0 +1,21 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options
{
public class OptionCheckbox : OptionItem<bool>
{
private OsuCheckbox checkbox;
protected override Drawable CreateControl() => checkbox = new OsuCheckbox();
public override string LabelText
{
get { return checkbox.LabelText; }
set { checkbox.LabelText = value; }
}
}
}
+8 -53
View File
@@ -2,44 +2,18 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options
{
public class OptionDropdown<T> : FillFlowContainer
public class OptionDropdown<T> : OptionItem<T>
{
private readonly Dropdown<T> dropdown;
private readonly SpriteText text;
private Dropdown<T> dropdown;
public string LabelText
{
get { return text.Text; }
set
{
text.Text = value;
}
}
public Bindable<T> Bindable
{
get { return bindable; }
set
{
bindable = value;
dropdown.Current.BindTo(bindable);
}
}
private Bindable<T> bindable;
private IEnumerable<KeyValuePair<string, T>> items;
private IEnumerable<KeyValuePair<string, T>> items = new KeyValuePair<string, T>[] { };
public IEnumerable<KeyValuePair<string, T>> Items
{
get
@@ -54,30 +28,11 @@ namespace osu.Game.Overlays.Options
}
}
public OptionDropdown()
protected override Drawable CreateControl() => dropdown = new OsuDropdown<T>
{
Items = new KeyValuePair<string, T>[0];
Direction = FillDirection.Vertical;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
text = new OsuSpriteText {
Alpha = 0,
},
dropdown = new OsuDropdown<T>
{
Margin = new MarginPadding { Top = 5 },
RelativeSizeAxes = Axes.X,
Items = Items,
}
};
dropdown.Current.DisabledChanged += disabled =>
{
Alpha = disabled ? 0.3f : 1;
};
}
Margin = new MarginPadding { Top = 5 },
RelativeSizeAxes = Axes.X,
Items = Items,
};
}
}
+82
View File
@@ -0,0 +1,82 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Overlays.Options
{
public abstract class OptionItem<T> : FillFlowContainer, IFilterable
{
protected abstract Drawable CreateControl();
protected Drawable Control { get; }
private IHasCurrentValue<T> controlWithCurrent => Control as IHasCurrentValue<T>;
private SpriteText text;
public virtual string LabelText
{
get { return text?.Text ?? string.Empty; }
set
{
if (text == null)
{
// construct lazily for cases where the label is not needed (may be provided by the Control).
Add(text = new OsuSpriteText() { Depth = 1 });
}
text.Text = value;
}
}
// hold a reference to the provided bindable so we don't have to in every options section.
private Bindable<T> bindable;
public Bindable<T> Bindable
{
get
{
return bindable;
}
set
{
bindable = value;
controlWithCurrent?.Current.BindTo(bindable);
}
}
public string[] FilterTerms => new[] { LabelText };
public bool MatchingCurrentFilter
{
set
{
// probably needs a better transition.
FadeTo(value ? 1 : 0);
}
}
protected OptionItem()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Right = 5 };
if ((Control = CreateControl()) != null)
{
if (controlWithCurrent != null)
controlWithCurrent.Current.DisabledChanged += disabled => { Colour = disabled ? Color4.Gray : Color4.White; };
Add(Control);
}
}
}
}
+4 -2
View File
@@ -2,13 +2,15 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Overlays.Options
{
internal class OptionLabel : OsuSpriteText
internal class OptionLabel : OptionItem<string>
{
protected override Drawable CreateControl() => null;
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
+9 -48
View File
@@ -1,65 +1,26 @@
// 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.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options
{
public class OptionSlider<T> : OptionSlider<T, OsuSliderBar<T>> where T: struct
public class OptionSlider<T> : OptionSlider<T, OsuSliderBar<T>>
where T : struct
{
}
public class OptionSlider<T, U> : FillFlowContainer where T : struct where U : SliderBar<T>, new()
public class OptionSlider<T, U> : OptionItem<T>
where T : struct
where U : SliderBar<T>, new()
{
private readonly SliderBar<T> slider;
private readonly SpriteText text;
public string LabelText
protected override Drawable CreateControl() => new U()
{
get { return text.Text; }
set
{
text.Text = value;
text.Alpha = string.IsNullOrEmpty(value) ? 0 : 1;
}
}
private Bindable<T> bindable;
public Bindable<T> Bindable
{
set
{
bindable = value;
slider.Current.BindTo(bindable);
}
}
public OptionSlider()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Right = 5 };
Children = new Drawable[]
{
text = new OsuSpriteText
{
Alpha = 0,
},
slider = new U()
{
Margin = new MarginPadding { Top = 5, Bottom = 5 },
RelativeSizeAxes = Axes.X
}
};
}
Margin = new MarginPadding { Top = 5, Bottom = 5 },
RelativeSizeAxes = Axes.X
};
}
}
+3 -12
View File
@@ -1,22 +1,13 @@
// 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.Configuration;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options
{
public class OptionTextBox : OsuTextBox
public class OptionTextBox : OptionItem<string>
{
private Bindable<string> bindable;
public Bindable<string> Bindable
{
set
{
bindable = value;
Current.BindTo(bindable);
}
}
protected override Drawable CreateControl() => new OsuTextBox();
}
}
@@ -2,33 +2,30 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options.Sections.Input
namespace osu.Game.Overlays.Options.Sections.Audio
{
public class OtherInputOptions : OptionsSubsection
public class MainMenuOptions : OptionsSubsection
{
protected override string Header => "Other";
protected override string Header => "Main Menu";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
Children = new Drawable[]
Children = new[]
{
new OsuCheckbox
new OptionCheckbox
{
LabelText = "OS TabletPC support",
Bindable = config.GetBindable<bool>(OsuConfig.Tablet)
LabelText = "Interface voices",
Bindable = config.GetBindable<bool>(OsuConfig.MenuVoice)
},
new OsuCheckbox
new OptionCheckbox
{
LabelText = "Wiimote/TaTaCon Drum Support",
Bindable = config.GetBindable<bool>(OsuConfig.Wiimote)
LabelText = "osu! music theme",
Bindable = config.GetBindable<bool>(OsuConfig.MenuMusic)
},
};
}
}
}
@@ -4,8 +4,6 @@
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options.Sections.Audio
{
@@ -14,18 +12,13 @@ namespace osu.Game.Overlays.Options.Sections.Audio
protected override string Header => "Volume";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, AudioManager audio)
private void load(AudioManager audio)
{
Children = new Drawable[]
{
new OptionSlider<double> { LabelText = "Master", Bindable = audio.Volume },
new OptionSlider<double> { LabelText = "Effect", Bindable = audio.VolumeSample },
new OptionSlider<double> { LabelText = "Music", Bindable = audio.VolumeTrack },
new OsuCheckbox
{
LabelText = "Ignore beatmap hitsounds",
Bindable = config.GetBindable<bool>(OsuConfig.IgnoreBeatmapSamples)
}
};
}
}
@@ -1,7 +1,6 @@
// 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;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Overlays.Options.Sections.Audio;
@@ -17,9 +16,10 @@ namespace osu.Game.Overlays.Options.Sections
{
Children = new Drawable[]
{
new AudioDevicesOptions { Alpha = RuntimeInfo.IsWindows ? 1 : 0 },
new AudioDevicesOptions(),
new VolumeOptions(),
new OffsetOptions(),
new MainMenuOptions(),
};
}
}
@@ -4,7 +4,6 @@
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options.Sections.Debug
{
@@ -17,7 +16,7 @@ namespace osu.Game.Overlays.Options.Sections.Debug
{
Children = new Drawable[]
{
new OsuCheckbox
new OptionCheckbox
{
LabelText = "Bypass caching",
Bindable = config.GetBindable<bool>(FrameworkDebugConfig.BypassCaching)
@@ -1,58 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using OpenTK;
namespace osu.Game.Overlays.Options.Sections
{
public class EditorSection : OptionsSection
{
public override string Header => "Editor";
public override FontAwesome Icon => FontAwesome.fa_pencil;
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
FlowContent.Spacing = new Vector2(0, 5);
Children = new Drawable[]
{
new OsuCheckbox
{
LabelText = "Background video",
Bindable = config.GetBindable<bool>(OsuConfig.VideoEditor)
},
new OsuCheckbox
{
LabelText = "Always use default skin",
Bindable = config.GetBindable<bool>(OsuConfig.EditorDefaultSkin)
},
new OsuCheckbox
{
LabelText = "Snaking sliders",
Bindable = config.GetBindable<bool>(OsuConfig.EditorSnakingSliders)
},
new OsuCheckbox
{
LabelText = "Hit animations",
Bindable = config.GetBindable<bool>(OsuConfig.EditorHitAnimations)
},
new OsuCheckbox
{
LabelText = "Follow points",
Bindable = config.GetBindable<bool>(OsuConfig.EditorFollowPoints)
},
new OsuCheckbox
{
LabelText = "Stacking",
Bindable = config.GetBindable<bool>(OsuConfig.EditorStacking)
},
};
}
}
}
@@ -4,7 +4,6 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options.Sections.Gameplay
{
@@ -22,46 +21,16 @@ namespace osu.Game.Overlays.Options.Sections.Gameplay
LabelText = "Background dim",
Bindable = config.GetBindable<double>(OsuConfig.DimLevel)
},
new OptionEnumDropdown<ProgressBarType>
{
LabelText = "Progress display",
Bindable = config.GetBindable<ProgressBarType>(OsuConfig.ProgressBarType)
},
new OptionEnumDropdown<ScoreMeterType>
{
LabelText = "Score meter type",
Bindable = config.GetBindable<ScoreMeterType>(OsuConfig.ScoreMeter)
},
new OptionSlider<double>
{
LabelText = "Score meter size",
Bindable = config.GetBindable<double>(OsuConfig.ScoreMeterScale)
},
new OsuCheckbox
new OptionCheckbox
{
LabelText = "Show score overlay",
Bindable = config.GetBindable<bool>(OsuConfig.ShowInterface)
},
new OsuCheckbox
new OptionCheckbox
{
LabelText = "Always show key overlay",
Bindable = config.GetBindable<bool>(OsuConfig.KeyOverlay)
},
new OsuCheckbox
{
LabelText = "Show approach circle on first \"Hidden\" object",
Bindable = config.GetBindable<bool>(OsuConfig.HiddenShowFirstApproach)
},
new OsuCheckbox
{
LabelText = "Scale osu!mania scroll speed with BPM",
Bindable = config.GetBindable<bool>(OsuConfig.ManiaSpeedBPMScale)
},
new OsuCheckbox
{
LabelText = "Remember osu!mania scroll speed per beatmap",
Bindable = config.GetBindable<bool>(OsuConfig.UsePerBeatmapManiaSpeed)
},
};
}
}
@@ -2,9 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options.Sections.General
{
@@ -13,20 +12,14 @@ namespace osu.Game.Overlays.Options.Sections.General
protected override string Header => "Language";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
private void load(FrameworkConfigManager frameworkConfig)
{
Children = new Drawable[]
{
new OptionLabel { Text = "TODO: Dropdown" },
new OsuCheckbox
new OptionCheckbox
{
LabelText = "Prefer metadata in original language",
Bindable = config.GetBindable<bool>(OsuConfig.ShowUnicode)
},
new OsuCheckbox
{
LabelText = "Use alternative font for chat display",
Bindable = config.GetBindable<bool>(OsuConfig.AlternativeChatFont)
Bindable = frameworkConfig.GetBindable<bool>(FrameworkConfig.ShowUnicode)
},
};
}
@@ -132,12 +132,12 @@ namespace osu.Game.Overlays.Options.Sections.General
TabbableContentContainer = this,
OnCommit = (sender, newText) => performLogin()
},
new OsuCheckbox
new OptionCheckbox
{
LabelText = "Remember username",
Bindable = config.GetBindable<bool>(OsuConfig.SaveUsername),
},
new OsuCheckbox
new OptionCheckbox
{
LabelText = "Stay logged in",
Bindable = config.GetBindable<bool>(OsuConfig.SavePassword),
@@ -23,7 +23,6 @@ namespace osu.Game.Overlays.Options.Sections.General
LabelText = "Release stream",
Bindable = config.GetBindable<ReleaseStream>(OsuConfig.ReleaseStream),
},
new OptionLabel { Text = "Your osu! is up to date" }, // TODO: map this to reality
new OsuButton
{
RelativeSizeAxes = Axes.X,
@@ -4,7 +4,6 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options.Sections.Graphics
{
@@ -17,52 +16,17 @@ namespace osu.Game.Overlays.Options.Sections.Graphics
{
Children = new Drawable[]
{
new OsuCheckbox
new OptionCheckbox
{
LabelText = "Snaking in sliders",
Bindable = config.GetBindable<bool>(OsuConfig.SnakingInSliders)
},
new OsuCheckbox
new OptionCheckbox
{
LabelText = "Snaking out sliders",
Bindable = config.GetBindable<bool>(OsuConfig.SnakingOutSliders)
},
new OsuCheckbox
{
LabelText = "Background video",
Bindable = config.GetBindable<bool>(OsuConfig.Video)
},
new OsuCheckbox
{
LabelText = "Storyboards",
Bindable = config.GetBindable<bool>(OsuConfig.ShowStoryboard)
},
new OsuCheckbox
{
LabelText = "Combo bursts",
Bindable = config.GetBindable<bool>(OsuConfig.ComboBurst)
},
new OsuCheckbox
{
LabelText = "Hit lighting",
Bindable = config.GetBindable<bool>(OsuConfig.HitLighting)
},
new OsuCheckbox
{
LabelText = "Shaders",
Bindable = config.GetBindable<bool>(OsuConfig.Bloom)
},
new OsuCheckbox
{
LabelText = "Softening filter",
Bindable = config.GetBindable<bool>(OsuConfig.BloomSoftening)
},
new OptionEnumDropdown<ScreenshotFormat>
{
LabelText = "Screenshot",
Bindable = config.GetBindable<ScreenshotFormat>(OsuConfig.ScreenshotFormat)
}
};
}
}
}
}
@@ -4,7 +4,6 @@
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options.Sections.Graphics
{
@@ -24,13 +23,12 @@ namespace osu.Game.Overlays.Options.Sections.Graphics
Children = new Drawable[]
{
new OptionLabel { Text = "Resolution: TODO dropdown" },
new OptionEnumDropdown<WindowMode>
{
LabelText = "Screen mode",
Bindable = config.GetBindable<WindowMode>(FrameworkConfig.WindowMode),
},
new OsuCheckbox
new OptionCheckbox
{
LabelText = "Letterboxing",
Bindable = letterboxing,
@@ -65,4 +63,4 @@ namespace osu.Game.Overlays.Options.Sections.Graphics
}
}
}
}
}
@@ -3,45 +3,24 @@
using osu.Framework.Allocation;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options.Sections.Graphics
{
public class MainMenuOptions : OptionsSubsection
{
protected override string Header => "Main Menu";
protected override string Header => "User Interface";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
Children = new[]
{
new OsuCheckbox
{
LabelText = "Snow",
Bindable = config.GetBindable<bool>(OsuConfig.MenuSnow)
},
new OsuCheckbox
new OptionCheckbox
{
LabelText = "Parallax",
Bindable = config.GetBindable<bool>(OsuConfig.MenuParallax)
},
new OsuCheckbox
{
LabelText = "Menu tips",
Bindable = config.GetBindable<bool>(OsuConfig.ShowMenuTips)
},
new OsuCheckbox
{
LabelText = "Interface voices",
Bindable = config.GetBindable<bool>(OsuConfig.MenuVoice)
},
new OsuCheckbox
{
LabelText = "osu! music theme",
Bindable = config.GetBindable<bool>(OsuConfig.MenuMusic)
},
};
}
}
}
}
@@ -5,7 +5,6 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options.Sections.Graphics
{
@@ -14,7 +13,7 @@ namespace osu.Game.Overlays.Options.Sections.Graphics
protected override string Header => "Renderer";
[BackgroundDependencyLoader]
private void load(OsuConfigManager osuConfig, FrameworkConfigManager config)
private void load(FrameworkConfigManager config, OsuConfigManager osuConfig)
{
// NOTE: Compatability mode omitted
Children = new Drawable[]
@@ -25,22 +24,12 @@ namespace osu.Game.Overlays.Options.Sections.Graphics
LabelText = "Frame limiter",
Bindable = config.GetBindable<FrameSync>(FrameworkConfig.FrameSync)
},
new OsuCheckbox
new OptionCheckbox
{
LabelText = "Show FPS counter",
Bindable = osuConfig.GetBindable<bool>(OsuConfig.FpsCounter),
},
new OsuCheckbox
{
LabelText = "Reduce dropped frames",
Bindable = osuConfig.GetBindable<bool>(OsuConfig.ForceFrameFlush),
},
new OsuCheckbox
{
LabelText = "Detect performance issues",
Bindable = osuConfig.GetBindable<bool>(OsuConfig.DetectPerformanceIssues),
LabelText = "Show FPS",
Bindable = osuConfig.GetBindable<bool>(OsuConfig.ShowFpsDisplay)
},
};
}
}
}
}
@@ -1,27 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options.Sections.Graphics
{
public class SongSelectGraphicsOptions : OptionsSubsection
{
protected override string Header => "Song Select";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
Children = new[]
{
new OsuCheckbox
{
LabelText = "Show thumbnails",
Bindable = config.GetBindable<bool>(OsuConfig.SongSelectThumbnails)
}
};
}
}
}
@@ -20,7 +20,6 @@ namespace osu.Game.Overlays.Options.Sections
new LayoutOptions(),
new DetailOptions(),
new MainMenuOptions(),
new SongSelectGraphicsOptions(),
};
}
}
@@ -17,13 +17,8 @@ namespace osu.Game.Overlays.Options.Sections.Input
new OsuButton
{
RelativeSizeAxes = Axes.X,
Text = "Change keyboard bindings"
Text = "Key Configuration"
},
new OsuButton
{
RelativeSizeAxes = Axes.X,
Text = "osu!mania layout"
}
};
}
}
@@ -2,7 +2,9 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
@@ -13,44 +15,24 @@ namespace osu.Game.Overlays.Options.Sections.Input
protected override string Header => "Mouse";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
private void load(OsuConfigManager osuConfig, FrameworkConfigManager config)
{
Children = new Drawable[]
{
new OptionSlider<double, SensitivitySlider>
{
LabelText = "Sensitivity",
Bindable = config.GetBindable<double>(OsuConfig.MouseSpeed)
},
new OsuCheckbox
{
LabelText = "Raw input",
Bindable = config.GetBindable<bool>(OsuConfig.RawInput)
},
new OsuCheckbox
{
LabelText = "Map absolute raw input to the osu! window",
Bindable = config.GetBindable<bool>(OsuConfig.AbsoluteToOsuWindow)
},
new OptionEnumDropdown<ConfineMouseMode>
{
LabelText = "Confine mouse cursor",
Bindable = config.GetBindable<ConfineMouseMode>(OsuConfig.ConfineMouse),
Bindable = config.GetBindable<ConfineMouseMode>(FrameworkConfig.ConfineMouseMode),
},
new OsuCheckbox
new OptionCheckbox
{
LabelText = "Disable mouse wheel in play mode",
Bindable = config.GetBindable<bool>(OsuConfig.MouseDisableWheel)
LabelText = "Disable mouse wheel during gameplay",
Bindable = osuConfig.GetBindable<bool>(OsuConfig.MouseDisableWheel)
},
new OsuCheckbox
new OptionCheckbox
{
LabelText = "Disable mouse buttons in play mode",
Bindable = config.GetBindable<bool>(OsuConfig.MouseDisableButtons)
},
new OsuCheckbox
{
LabelText = "Cursor ripples",
Bindable = config.GetBindable<bool>(OsuConfig.CursorRipple)
LabelText = "Disable mouse buttons during gameplay",
Bindable = osuConfig.GetBindable<bool>(OsuConfig.MouseDisableButtons)
},
};
}
@@ -18,7 +18,6 @@ namespace osu.Game.Overlays.Options.Sections
{
new MouseOptions(),
new KeyboardOptions(),
new OtherInputOptions(),
};
}
}
@@ -3,7 +3,6 @@
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using OpenTK;
namespace osu.Game.Overlays.Options.Sections
@@ -18,27 +17,7 @@ namespace osu.Game.Overlays.Options.Sections
FlowContent.Spacing = new Vector2(0, 5);
Children = new Drawable[]
{
new OsuButton
{
RelativeSizeAxes = Axes.X,
Text = "Delete all unranked maps",
},
new OsuButton
{
RelativeSizeAxes = Axes.X,
Text = "Repair folder permissions",
},
new OsuButton
{
RelativeSizeAxes = Axes.X,
Text = "Mark all maps as played",
},
new OsuButton
{
RelativeSizeAxes = Axes.X,
Text = "Run osu! updater",
},
};
}
}
}
}
@@ -1,53 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options.Sections.Online
{
public class InGameChatOptions : OptionsSubsection
{
protected override string Header => "Chat";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
Children = new Drawable[]
{
new OsuCheckbox
{
LabelText = "Filter offensive words",
Bindable = config.GetBindable<bool>(OsuConfig.ChatFilter)
},
new OsuCheckbox
{
LabelText = "Filter foreign characters",
Bindable = config.GetBindable<bool>(OsuConfig.ChatRemoveForeign)
},
new OsuCheckbox
{
LabelText = "Log private messages",
Bindable = config.GetBindable<bool>(OsuConfig.LogPrivateMessages)
},
new OsuCheckbox
{
LabelText = "Block private messages from non-friends",
Bindable = config.GetBindable<bool>(OsuConfig.BlockNonFriendPM)
},
new OptionLabel { Text = "Chat ignore list (space-seperated list)" },
new OptionTextBox {
RelativeSizeAxes = Axes.X,
Bindable = config.GetBindable<string>(OsuConfig.IgnoreList)
},
new OptionLabel { Text = "Chat highlight words (space-seperated list)" },
new OptionTextBox {
RelativeSizeAxes = Axes.X,
Bindable = config.GetBindable<string>(OsuConfig.HighlightWords)
},
};
}
}
}
@@ -1,43 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options.Sections.Online
{
public class IntegrationOptions : OptionsSubsection
{
protected override string Header => "Integration";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
Children = new Drawable[]
{
new OsuCheckbox
{
LabelText = "Integrate with Yahoo! status display",
Bindable = config.GetBindable<bool>(OsuConfig.YahooIntegration)
},
new OsuCheckbox
{
LabelText = "Integrate with MSN Live status display",
Bindable = config.GetBindable<bool>(OsuConfig.MsnIntegration)
},
new OsuCheckbox
{
LabelText = "Automatically start osu!direct downloads",
Bindable = config.GetBindable<bool>(OsuConfig.AutomaticDownload)
},
new OsuCheckbox
{
LabelText = "Prefer no-video downloads",
Bindable = config.GetBindable<bool>(OsuConfig.AutomaticDownloadNoVideo)
},
};
}
}
}
@@ -1,53 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options.Sections.Online
{
public class NotificationsOptions : OptionsSubsection
{
protected override string Header => "Notifications";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
Children = new Drawable[]
{
new OsuCheckbox
{
LabelText = "Enable chat ticker",
Bindable = config.GetBindable<bool>(OsuConfig.Ticker)
},
new OsuCheckbox
{
LabelText = "Show a notification popup when someone says your name",
Bindable = config.GetBindable<bool>(OsuConfig.ChatHighlightName)
},
new OsuCheckbox
{
LabelText = "Show chat message notifications",
Bindable = config.GetBindable<bool>(OsuConfig.ChatMessageNotification)
},
new OsuCheckbox
{
LabelText = "Play a sound when someone says your name",
Bindable = config.GetBindable<bool>(OsuConfig.ChatAudibleHighlight)
},
new OsuCheckbox
{
LabelText = "Show notification popups instantly during gameplay",
Bindable = config.GetBindable<bool>(OsuConfig.PopupDuringGameplay)
},
new OsuCheckbox
{
LabelText = "Show notification popups when friends change status",
Bindable = config.GetBindable<bool>(OsuConfig.NotifyFriends)
},
};
}
}
}
@@ -1,33 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Options.Sections.Online
{
public class PrivacyOptions : OptionsSubsection
{
protected override string Header => "Privacy";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
Children = new Drawable[]
{
new OsuCheckbox
{
LabelText = "Share your city location with others",
Bindable = config.GetBindable<bool>(OsuConfig.DisplayCityLocation)
},
new OsuCheckbox
{
LabelText = "Allow multiplayer game invites from all users",
Bindable = config.GetBindable<bool>(OsuConfig.AllowPublicInvites)
},
};
}
}
}
@@ -3,7 +3,6 @@
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Overlays.Options.Sections.Online;
namespace osu.Game.Overlays.Options.Sections
{
@@ -16,10 +15,6 @@ namespace osu.Game.Overlays.Options.Sections
{
Children = new Drawable[]
{
new InGameChatOptions(),
new PrivacyOptions(),
new NotificationsOptions(),
new IntegrationOptions(),
};
}
}
@@ -21,43 +21,6 @@ namespace osu.Game.Overlays.Options.Sections
FlowContent.Spacing = new Vector2(0, 5);
Children = new Drawable[]
{
new OptionLabel { Text = "TODO: Skin preview textures" },
new OptionLabel { Text = "Current skin: TODO dropdown" },
new OsuButton
{
RelativeSizeAxes = Axes.X,
Text = "Preview gameplay",
},
new OsuButton
{
RelativeSizeAxes = Axes.X,
Text = "Open skin folder",
},
new OsuButton
{
RelativeSizeAxes = Axes.X,
Text = "Export as .osk",
},
new OsuCheckbox
{
LabelText = "Ignore all beatmap skins",
Bindable = config.GetBindable<bool>(OsuConfig.IgnoreBeatmapSkins)
},
new OsuCheckbox
{
LabelText = "Use skin's sound samples",
Bindable = config.GetBindable<bool>(OsuConfig.SkinSamples)
},
new OsuCheckbox
{
LabelText = "Use Taiko skin for Taiko mode",
Bindable = config.GetBindable<bool>(OsuConfig.UseTaikoSkin)
},
new OsuCheckbox
{
LabelText = "Always use skin cursor",
Bindable = config.GetBindable<bool>(OsuConfig.UseSkinCursor)
},
new OptionSlider<double, SizeSlider>
{
LabelText = "Menu cursor size",
@@ -68,11 +31,6 @@ namespace osu.Game.Overlays.Options.Sections
LabelText = "Gameplay cursor size",
Bindable = config.GetBindable<double>(OsuConfig.GameplayCursorSize)
},
new OsuCheckbox
{
LabelText = "Automatic cursor size",
Bindable = config.GetBindable<bool>(OsuConfig.AutomaticCursorSizing)
},
};
}
-1
View File
@@ -51,7 +51,6 @@ namespace osu.Game.Overlays
new AudioSection(),
new SkinSection(),
new InputSection(),
new EditorSection(),
new OnlineSection(),
new MaintenanceSection(),
new DebugSection(),
+5
View File
@@ -21,6 +21,11 @@ namespace osu.Game.Rulesets.Mods
/// </summary>
public virtual FontAwesome Icon => FontAwesome.fa_question;
/// <summary>
/// The type of this mod.
/// </summary>
public virtual ModType Type => ModType.Special;
/// <summary>
/// The user readable description of this mod.
/// </summary>
+1
View File
@@ -11,6 +11,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Double Time";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_doubletime;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Zoooooooooom";
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModHalfTime) };

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