mirror of
https://github.com/ppy/osu.git
synced 2026-05-16 05:32:35 +08:00
Compare commits
262 Commits
Vendored
+11
@@ -3,6 +3,9 @@
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch VisualTests",
|
||||
"windows": {
|
||||
"type": "clr"
|
||||
},
|
||||
"type": "mono",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/osu.Desktop.VisualTests/bin/Debug/osu!.exe",
|
||||
@@ -15,6 +18,9 @@
|
||||
},
|
||||
{
|
||||
"name": "Launch Desktop",
|
||||
"windows": {
|
||||
"type": "clr"
|
||||
},
|
||||
"type": "mono",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/osu.Desktop/bin/Debug/osu!.exe",
|
||||
@@ -27,6 +33,11 @@
|
||||
},
|
||||
{
|
||||
"name": "Attach",
|
||||
"windows": {
|
||||
"type": "clr",
|
||||
"request": "attach",
|
||||
"processName": "osu!"
|
||||
},
|
||||
"type": "mono",
|
||||
"request": "attach",
|
||||
"address": "localhost",
|
||||
|
||||
Vendored
+32
-2
@@ -10,9 +10,39 @@
|
||||
"showOutput": "silent",
|
||||
"command": "msbuild",
|
||||
"args": [
|
||||
// Ask msbuild to generate full paths for file names.
|
||||
"/property:GenerateFullPaths=true"
|
||||
"/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",
|
||||
"isBuildCommand": true
|
||||
},
|
||||
{
|
||||
"taskName": "rebuild",
|
||||
"isShellCommand": true,
|
||||
"showOutput": "silent",
|
||||
"command": "msbuild",
|
||||
"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
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
# osu! [](https://ci.appveyor.com/project/peppy/osu)
|
||||
# osu! [](https://ci.appveyor.com/project/peppy/osu) [](https://www.codefactor.io/repository/github/ppy/osu) [](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
|
||||
|
||||
@@ -12,14 +8,14 @@ This is still heavily under development and is not intended for end-user use. Th
|
||||
|
||||
# Requirements
|
||||
|
||||
- A desktop platform which can compile .NET 4.5.
|
||||
- Visual Studio or MonoDevelop is recommended.
|
||||
- A desktop platform which can compile .NET 4.5 (tested on macOS, linux and windows). We recommend using [Visual Studio Code](https://code.visualstudio.com/) (all platforms) or [Visual Studio Community Edition](https://www.visualstudio.com/) (windows only), both of which are free.
|
||||
- Make sure you initialise and keep submodules up-to-date.
|
||||
|
||||
# Contributing
|
||||
|
||||
We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention on having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time, to ensure no effort is wasted.
|
||||
|
||||
Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://goo.gl/nFdoyI). If you're unsure of what you can help with, check out the [list](https://github.com/ppy/osu/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Abounty) of available issues with bounty.
|
||||
Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://www.bountysource.com/teams/ppy). If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu-framework/issues).
|
||||
|
||||
Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. I welcome all feedback so we can make contributing to this project as pain-free as possible.
|
||||
|
||||
|
||||
+1
-1
Submodule osu-framework updated: b4e1b9a0eb...cebdfb1bbb
@@ -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",
|
||||
|
||||
@@ -10,7 +10,6 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@@ -62,15 +61,12 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
add(new DrawableSlider(new Slider
|
||||
{
|
||||
StartTime = framedClock.CurrentTime + 600,
|
||||
CurveObject = new CurvedHitObject
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(-200, 0),
|
||||
new Vector2(400, 0),
|
||||
},
|
||||
Distance = 400
|
||||
new Vector2(-200, 0),
|
||||
new Vector2(400, 0),
|
||||
},
|
||||
Distance = 400,
|
||||
Position = new Vector2(-200, 0),
|
||||
Velocity = 1,
|
||||
TickDistance = 100,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -19,10 +19,10 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
|
||||
protected override IEnumerable<OsuHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
|
||||
{
|
||||
IHasCurve curveData = original as IHasCurve;
|
||||
IHasEndTime endTimeData = original as IHasEndTime;
|
||||
IHasPosition positionData = original as IHasPosition;
|
||||
IHasCombo comboData = original as IHasCombo;
|
||||
var curveData = original as IHasCurve;
|
||||
var endTimeData = original as IHasEndTime;
|
||||
var positionData = original as IHasPosition;
|
||||
var comboData = original as IHasCombo;
|
||||
|
||||
if (curveData != null)
|
||||
{
|
||||
@@ -30,7 +30,11 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
{
|
||||
StartTime = original.StartTime,
|
||||
Samples = original.Samples,
|
||||
CurveObject = curveData,
|
||||
ControlPoints = curveData.ControlPoints,
|
||||
CurveType = curveData.CurveType,
|
||||
Distance = curveData.Distance,
|
||||
RepeatSamples = curveData.RepeatSamples,
|
||||
RepeatCount = curveData.RepeatCount,
|
||||
Position = positionData?.Position ?? Vector2.Zero,
|
||||
NewCombo = comboData?.NewCombo ?? false
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -20,24 +20,33 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
/// </summary>
|
||||
private const float base_scoring_distance = 100;
|
||||
|
||||
public IHasCurve CurveObject { get; set; }
|
||||
|
||||
public SliderCurve Curve => CurveObject.Curve;
|
||||
public readonly SliderCurve Curve = new SliderCurve();
|
||||
|
||||
public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity;
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
public override Vector2 EndPosition => PositionAt(1);
|
||||
|
||||
public Vector2 PositionAt(double progress) => CurveObject.PositionAt(progress);
|
||||
public double ProgressAt(double progress) => CurveObject.ProgressAt(progress);
|
||||
public int RepeatAt(double progress) => CurveObject.RepeatAt(progress);
|
||||
public List<Vector2> ControlPoints
|
||||
{
|
||||
get { return Curve.ControlPoints; }
|
||||
set { Curve.ControlPoints = value; }
|
||||
}
|
||||
|
||||
public List<Vector2> ControlPoints => CurveObject.ControlPoints;
|
||||
public CurveType CurveType => CurveObject.CurveType;
|
||||
public double Distance => CurveObject.Distance;
|
||||
public CurveType CurveType
|
||||
{
|
||||
get { return Curve.CurveType; }
|
||||
set { Curve.CurveType = value; }
|
||||
}
|
||||
|
||||
public int RepeatCount => CurveObject.RepeatCount;
|
||||
public double Distance
|
||||
{
|
||||
get { return Curve.Distance; }
|
||||
set { Curve.Distance = value; }
|
||||
}
|
||||
|
||||
public List<SampleInfoList> RepeatSamples { get; set; } = new List<SampleInfoList>();
|
||||
public int RepeatCount { get; set; } = 1;
|
||||
|
||||
private int stackHeight;
|
||||
public override int StackHeight
|
||||
@@ -63,6 +72,18 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
||||
}
|
||||
|
||||
public Vector2 PositionAt(double progress) => Curve.PositionAt(ProgressAt(progress));
|
||||
|
||||
public double ProgressAt(double progress)
|
||||
{
|
||||
double p = progress * RepeatCount % 1;
|
||||
if (RepeatAt(progress) % 2 == 1)
|
||||
p = 1 - p;
|
||||
return p;
|
||||
}
|
||||
|
||||
public int RepeatAt(double progress) => (int)(progress * RepeatCount);
|
||||
|
||||
public IEnumerable<SliderTick> Ticks
|
||||
{
|
||||
get
|
||||
@@ -96,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()
|
||||
}))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using System;
|
||||
@@ -28,7 +27,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
protected override void PreprocessHitObjects()
|
||||
{
|
||||
foreach (var h in Objects)
|
||||
(h as IHasCurve)?.Curve?.Calculate();
|
||||
(h as Slider)?.Curve?.Calculate();
|
||||
}
|
||||
|
||||
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
|
||||
@@ -190,4 +189,4 @@ namespace osu.Game.Rulesets.Osu
|
||||
Aim,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
-15
@@ -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" />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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[]
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Beatmaps.Drawables
|
||||
{
|
||||
internal class DifficultyColouredContainer : Container, IHasAccentColour
|
||||
{
|
||||
public Color4 AccentColour { get; set; }
|
||||
|
||||
private readonly BeatmapInfo beatmap;
|
||||
private OsuColour palette;
|
||||
|
||||
public DifficultyColouredContainer(BeatmapInfo beatmap)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour palette)
|
||||
{
|
||||
this.palette = palette;
|
||||
AccentColour = getColour(beatmap);
|
||||
}
|
||||
|
||||
private enum DifficultyRating
|
||||
{
|
||||
Easy,
|
||||
Normal,
|
||||
Hard,
|
||||
Insane,
|
||||
Expert
|
||||
}
|
||||
|
||||
private DifficultyRating getDifficultyRating(BeatmapInfo beatmap)
|
||||
{
|
||||
var rating = beatmap.StarDifficulty;
|
||||
|
||||
if (rating < 1.5) return DifficultyRating.Easy;
|
||||
if (rating < 2.25) return DifficultyRating.Normal;
|
||||
if (rating < 3.75) return DifficultyRating.Hard;
|
||||
if (rating < 5.25) return DifficultyRating.Insane;
|
||||
return DifficultyRating.Expert;
|
||||
}
|
||||
|
||||
private Color4 getColour(BeatmapInfo beatmap)
|
||||
{
|
||||
switch (getDifficultyRating(beatmap))
|
||||
{
|
||||
case DifficultyRating.Easy:
|
||||
return palette.Green;
|
||||
default:
|
||||
case DifficultyRating.Normal:
|
||||
return palette.Yellow;
|
||||
case DifficultyRating.Hard:
|
||||
return palette.Pink;
|
||||
case DifficultyRating.Insane:
|
||||
return palette.Purple;
|
||||
case DifficultyRating.Expert:
|
||||
return palette.Gray0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics;
|
||||
using OpenTK;
|
||||
@@ -11,23 +10,20 @@ using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Beatmaps.Drawables
|
||||
{
|
||||
internal class DifficultyIcon : Container
|
||||
|
||||
internal class DifficultyIcon : DifficultyColouredContainer
|
||||
{
|
||||
private readonly BeatmapInfo beatmap;
|
||||
private OsuColour palette;
|
||||
|
||||
public DifficultyIcon(BeatmapInfo beatmap)
|
||||
public DifficultyIcon(BeatmapInfo beatmap) : base(beatmap)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
const float size = 20;
|
||||
Size = new Vector2(size);
|
||||
Size = new Vector2(20);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour palette)
|
||||
private void load()
|
||||
{
|
||||
this.palette = palette;
|
||||
|
||||
Children = new[]
|
||||
{
|
||||
new TextAwesome
|
||||
@@ -35,7 +31,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
TextSize = Size.X,
|
||||
Colour = getColour(beatmap),
|
||||
Colour = AccentColour,
|
||||
Icon = FontAwesome.fa_circle
|
||||
},
|
||||
new TextAwesome
|
||||
@@ -48,43 +44,5 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private enum DifficultyRating
|
||||
{
|
||||
Easy,
|
||||
Normal,
|
||||
Hard,
|
||||
Insane,
|
||||
Expert
|
||||
}
|
||||
|
||||
private DifficultyRating getDifficultyRating(BeatmapInfo beatmap)
|
||||
{
|
||||
var rating = beatmap.StarDifficulty;
|
||||
|
||||
if (rating < 1.5) return DifficultyRating.Easy;
|
||||
if (rating < 2.25) return DifficultyRating.Normal;
|
||||
if (rating < 3.75) return DifficultyRating.Hard;
|
||||
if (rating < 5.25) return DifficultyRating.Insane;
|
||||
return DifficultyRating.Expert;
|
||||
}
|
||||
|
||||
private Color4 getColour(BeatmapInfo beatmap)
|
||||
{
|
||||
switch (getDifficultyRating(beatmap))
|
||||
{
|
||||
case DifficultyRating.Easy:
|
||||
return palette.Green;
|
||||
default:
|
||||
case DifficultyRating.Normal:
|
||||
return palette.Yellow;
|
||||
case DifficultyRating.Hard:
|
||||
return palette.Pink;
|
||||
case DifficultyRating.Insane:
|
||||
return palette.Purple;
|
||||
case DifficultyRating.Expert:
|
||||
return palette.Gray0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
// TODO: Not sure how far back to go, or differences between versions
|
||||
}
|
||||
|
||||
private HitObjectParser parser;
|
||||
private ConvertHitObjectParser parser;
|
||||
|
||||
private LegacySampleBank defaultSampleBank;
|
||||
private int defaultSampleVolume = 100;
|
||||
@@ -90,16 +90,16 @@ namespace osu.Game.Beatmaps.Formats
|
||||
switch (beatmap.BeatmapInfo.RulesetID)
|
||||
{
|
||||
case 0:
|
||||
parser = new Rulesets.Objects.Legacy.Osu.HitObjectParser();
|
||||
parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser();
|
||||
break;
|
||||
case 1:
|
||||
parser = new Rulesets.Objects.Legacy.Taiko.HitObjectParser();
|
||||
parser = new Rulesets.Objects.Legacy.Taiko.ConvertHitObjectParser();
|
||||
break;
|
||||
case 2:
|
||||
parser = new Rulesets.Objects.Legacy.Catch.HitObjectParser();
|
||||
parser = new Rulesets.Objects.Legacy.Catch.ConvertHitObjectParser();
|
||||
break;
|
||||
case 3:
|
||||
parser = new Rulesets.Objects.Legacy.Mania.HitObjectParser();
|
||||
parser = new Rulesets.Objects.Legacy.Mania.ConvertHitObjectParser();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public readonly BeatmapSetInfo BeatmapSetInfo;
|
||||
|
||||
public readonly Bindable<IEnumerable<Mod>> Mods = new Bindable<IEnumerable<Mod>>();
|
||||
public readonly Bindable<IEnumerable<Mod>> Mods = new Bindable<IEnumerable<Mod>>(new Mod[] { });
|
||||
|
||||
public readonly bool WithStoryboard;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// 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;
|
||||
|
||||
namespace osu.Game.Configuration
|
||||
{
|
||||
@@ -11,181 +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.MenuVoice, true);
|
||||
Set(OsuConfig.MenuMusic, true);
|
||||
|
||||
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.DimLevel, 30, 0, 100);
|
||||
|
||||
Set(OsuConfig.MouseDisableButtons, false);
|
||||
Set(OsuConfig.MouseDisableWheel, false);
|
||||
|
||||
Set(OsuConfig.SnakingInSliders, true);
|
||||
Set(OsuConfig.SnakingOutSliders, true);
|
||||
// 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.MenuMusic, true).Disabled = true;
|
||||
Set(OsuConfig.MenuVoice, 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)
|
||||
@@ -195,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,
|
||||
RawInput,
|
||||
AbsoluteToOsuWindow,
|
||||
ConfineMouse,
|
||||
[Obsolete]
|
||||
ConfineMouseToFullscreen,
|
||||
ShowMenuTips,
|
||||
HiddenShowFirstApproach,
|
||||
ComboColourSliderBall,
|
||||
AlternativeChatFont,
|
||||
BeatmapDetailTab,
|
||||
Username,
|
||||
DisplayStarsMaximum,
|
||||
DisplayStarsMinimum,
|
||||
AudioDevice,
|
||||
ReleaseStream,
|
||||
UpdateFailCount,
|
||||
SavePassword,
|
||||
SaveUsername,
|
||||
TreeSortMode,
|
||||
TreeSortMode2,
|
||||
ShowUnicode,
|
||||
PermanentSongInfo,
|
||||
Ticker,
|
||||
CompatibilityContext,
|
||||
CanForceOptimusCompatibility,
|
||||
|
||||
DisplayStarsMinimum,
|
||||
DisplayStarsMaximum,
|
||||
SnakingInSliders,
|
||||
SnakingOutSliders,
|
||||
ShowFpsDisplay
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
@@ -18,11 +19,13 @@ namespace osu.Game.Database
|
||||
/// <summary>
|
||||
/// Points of failure on a relative time scale (usually 0..100).
|
||||
/// </summary>
|
||||
[JsonProperty(@"fail")]
|
||||
public IEnumerable<int> Fails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Points of retry on a relative time scale (usually 0..100).
|
||||
/// </summary>
|
||||
[JsonProperty(@"exit")]
|
||||
public IEnumerable<int> Retries { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -23,14 +23,14 @@ namespace osu.Game.Graphics.UserInterface
|
||||
private readonly Box leftBox;
|
||||
private readonly Box rightBox;
|
||||
|
||||
public string TooltipText
|
||||
public virtual string TooltipText
|
||||
{
|
||||
get
|
||||
{
|
||||
var bindableDouble = CurrentNumber as BindableNumber<double>;
|
||||
if (bindableDouble != null)
|
||||
{
|
||||
if (bindableDouble.MaxValue == 1 && bindableDouble.MinValue == 0)
|
||||
if (bindableDouble.MaxValue == 1 && (bindableDouble.MinValue == 0 || bindableDouble.MinValue == -1))
|
||||
return bindableDouble.Value.ToString(@"P0");
|
||||
return bindableDouble.Value.ToString(@"n1");
|
||||
}
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Database;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class GetBeatmapDetailsRequest : APIRequest<GetBeatmapDetailsResponse>
|
||||
{
|
||||
private readonly BeatmapInfo beatmap;
|
||||
|
||||
private string lookupString => beatmap.OnlineBeatmapID > 0 ? beatmap.OnlineBeatmapID.ToString() : $@"lookup?checksum={beatmap.Hash}&filename={beatmap.Path}";
|
||||
|
||||
public GetBeatmapDetailsRequest(BeatmapInfo beatmap)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
}
|
||||
|
||||
protected override string Target => $@"beatmaps/{lookupString}";
|
||||
}
|
||||
|
||||
public class GetBeatmapDetailsResponse : BeatmapMetrics
|
||||
{
|
||||
//the online API returns some metrics as a nested object.
|
||||
[JsonProperty(@"failtimes")]
|
||||
private BeatmapMetrics failTimes
|
||||
{
|
||||
set
|
||||
{
|
||||
Fails = value.Fails;
|
||||
Retries = value.Retries;
|
||||
}
|
||||
}
|
||||
|
||||
//and other metrics in the beatmap set.
|
||||
[JsonProperty(@"beatmapset")]
|
||||
private BeatmapMetrics beatmapSet
|
||||
{
|
||||
set
|
||||
{
|
||||
Ratings = value.Ratings;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -149,6 +152,7 @@ namespace osu.Game
|
||||
{
|
||||
new Container
|
||||
{
|
||||
AlwaysReceiveInput = true,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Depth = float.MinValue,
|
||||
Children = new Drawable[]
|
||||
@@ -159,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)
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -2,59 +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.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);
|
||||
}
|
||||
|
||||
@@ -80,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;
|
||||
});
|
||||
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 =>
|
||||
{
|
||||
@@ -344,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();
|
||||
@@ -371,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;
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace osu.Game.Overlays.Options
|
||||
set
|
||||
{
|
||||
text.Text = value;
|
||||
text.Alpha = !string.IsNullOrEmpty(value) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,14 @@ using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Overlays.Options
|
||||
{
|
||||
public class OptionSlider<T> : FillFlowContainer 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()
|
||||
{
|
||||
private readonly SliderBar<T> slider;
|
||||
private readonly SpriteText text;
|
||||
@@ -50,7 +57,7 @@ namespace osu.Game.Overlays.Options
|
||||
{
|
||||
Alpha = 0,
|
||||
},
|
||||
slider = new OsuSliderBar<T>
|
||||
slider = new U()
|
||||
{
|
||||
Margin = new MarginPadding { Top = 5, Bottom = 5 },
|
||||
RelativeSizeAxes = Axes.X
|
||||
|
||||
+9
-11
@@ -2,33 +2,31 @@
|
||||
// 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
|
||||
{
|
||||
LabelText = "OS TabletPC support",
|
||||
Bindable = config.GetBindable<bool>(OsuConfig.Tablet)
|
||||
LabelText = "Interface voices",
|
||||
Bindable = config.GetBindable<bool>(OsuConfig.MenuVoice)
|
||||
},
|
||||
new OsuCheckbox
|
||||
{
|
||||
LabelText = "Wiimote/TaTaCon Drum Support",
|
||||
Bindable = config.GetBindable<bool>(OsuConfig.Wiimote)
|
||||
LabelText = "osu! music theme",
|
||||
Bindable = config.GetBindable<bool>(OsuConfig.MenuMusic)
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
// 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;
|
||||
@@ -18,10 +17,10 @@ namespace osu.Game.Overlays.Options.Sections.Audio
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OptionSlider<double>
|
||||
new OptionSlider<double, OffsetSlider>
|
||||
{
|
||||
LabelText = "Audio Offset",
|
||||
Bindable = (BindableDouble)config.GetBindable<double>(OsuConfig.AudioOffset)
|
||||
Bindable = config.GetBindable<double>(OsuConfig.AudioOffset)
|
||||
},
|
||||
new OsuButton
|
||||
{
|
||||
@@ -30,5 +29,10 @@ namespace osu.Game.Overlays.Options.Sections.Audio
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class OffsetSlider : OsuSliderBar<double>
|
||||
{
|
||||
public override string TooltipText => Current.Value.ToString(@"0ms");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// 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;
|
||||
@@ -18,25 +17,10 @@ namespace osu.Game.Overlays.Options.Sections.Gameplay
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OptionSlider<int>
|
||||
{
|
||||
LabelText = "Background dim",
|
||||
Bindable = (BindableInt)config.GetBindable<int>(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 = (BindableDouble)config.GetBindable<double>(OsuConfig.ScoreMeterScale)
|
||||
LabelText = "Background dim",
|
||||
Bindable = config.GetBindable<double>(OsuConfig.DimLevel)
|
||||
},
|
||||
new OsuCheckbox
|
||||
{
|
||||
@@ -48,21 +32,6 @@ namespace osu.Game.Overlays.Options.Sections.Gameplay
|
||||
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,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.Game.Configuration;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Overlays.Options.Sections.Gameplay
|
||||
{
|
||||
@@ -17,18 +17,23 @@ namespace osu.Game.Overlays.Options.Sections.Gameplay
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OptionSlider<double>
|
||||
new OptionSlider<double, StarSlider>
|
||||
{
|
||||
LabelText = "Display beatmaps from",
|
||||
Bindable = (BindableDouble)config.GetBindable<double>(OsuConfig.DisplayStarsMinimum)
|
||||
Bindable = config.GetBindable<double>(OsuConfig.DisplayStarsMinimum)
|
||||
},
|
||||
new OptionSlider<double>
|
||||
new OptionSlider<double, StarSlider>
|
||||
{
|
||||
LabelText = "up to",
|
||||
Bindable = (BindableDouble)config.GetBindable<double>(OsuConfig.DisplayStarsMaximum)
|
||||
Bindable = config.GetBindable<double>(OsuConfig.DisplayStarsMaximum)
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private class StarSlider : OsuSliderBar<double>
|
||||
{
|
||||
public override string TooltipText => Current.Value.ToString(@"0.## stars");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +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 +13,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
|
||||
{
|
||||
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)
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -27,41 +27,6 @@ namespace osu.Game.Overlays.Options.Sections.Graphics
|
||||
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)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ namespace osu.Game.Overlays.Options.Sections.Graphics
|
||||
{
|
||||
protected override string Header => "Layout";
|
||||
|
||||
private OptionSlider<int> letterboxPositionX;
|
||||
private OptionSlider<int> letterboxPositionY;
|
||||
private OptionSlider<double> letterboxPositionX;
|
||||
private OptionSlider<double> letterboxPositionY;
|
||||
|
||||
private Bindable<bool> letterboxing;
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace osu.Game.Overlays.Options.Sections.Graphics
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OptionLabel { Text = "Resolution: TODO dropdown" },
|
||||
new OptionEnumDropdown<WindowMode>
|
||||
{
|
||||
LabelText = "Screen mode",
|
||||
@@ -35,15 +34,15 @@ namespace osu.Game.Overlays.Options.Sections.Graphics
|
||||
LabelText = "Letterboxing",
|
||||
Bindable = letterboxing,
|
||||
},
|
||||
letterboxPositionX = new OptionSlider<int>
|
||||
letterboxPositionX = new OptionSlider<double>
|
||||
{
|
||||
LabelText = "Horizontal position",
|
||||
Bindable = (BindableInt)config.GetBindable<int>(FrameworkConfig.LetterboxPositionX)
|
||||
Bindable = config.GetBindable<double>(FrameworkConfig.LetterboxPositionX)
|
||||
},
|
||||
letterboxPositionY = new OptionSlider<int>
|
||||
letterboxPositionY = new OptionSlider<double>
|
||||
{
|
||||
LabelText = "Vertical position",
|
||||
Bindable = (BindableInt)config.GetBindable<int>(FrameworkConfig.LetterboxPositionY)
|
||||
Bindable = config.GetBindable<double>(FrameworkConfig.LetterboxPositionY)
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -9,38 +9,18 @@ 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
|
||||
{
|
||||
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)
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,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[]
|
||||
@@ -27,18 +27,8 @@ namespace osu.Game.Overlays.Options.Sections.Graphics
|
||||
},
|
||||
new OsuCheckbox
|
||||
{
|
||||
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"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
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;
|
||||
|
||||
@@ -14,46 +15,31 @@ 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>
|
||||
{
|
||||
LabelText = "Sensitivity",
|
||||
Bindable = (BindableDouble)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
|
||||
{
|
||||
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
|
||||
{
|
||||
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)
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private class SensitivitySlider : OsuSliderBar<double>
|
||||
{
|
||||
public override string TooltipText => Current.Value.ToString(@"0.##x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// 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;
|
||||
@@ -22,59 +21,22 @@ 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>
|
||||
new OptionSlider<double, SizeSlider>
|
||||
{
|
||||
LabelText = "Menu cursor size",
|
||||
Bindable = (BindableDouble)config.GetBindable<double>(OsuConfig.MenuCursorSize)
|
||||
Bindable = config.GetBindable<double>(OsuConfig.MenuCursorSize)
|
||||
},
|
||||
new OptionSlider<double>
|
||||
new OptionSlider<double, SizeSlider>
|
||||
{
|
||||
LabelText = "Gameplay cursor size",
|
||||
Bindable = (BindableDouble)config.GetBindable<double>(OsuConfig.GameplayCursorSize)
|
||||
},
|
||||
new OsuCheckbox
|
||||
{
|
||||
LabelText = "Automatic cursor size",
|
||||
Bindable = config.GetBindable<bool>(OsuConfig.AutomaticCursorSizing)
|
||||
Bindable = config.GetBindable<double>(OsuConfig.GameplayCursorSize)
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private class SizeSlider : OsuSliderBar<double>
|
||||
{
|
||||
public override string TooltipText => Current.Value.ToString(@"0.##x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,6 @@ namespace osu.Game.Overlays
|
||||
new AudioSection(),
|
||||
new SkinSection(),
|
||||
new InputSection(),
|
||||
new EditorSection(),
|
||||
new OnlineSection(),
|
||||
new MaintenanceSection(),
|
||||
new DebugSection(),
|
||||
|
||||
@@ -1,49 +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.Game.Rulesets.Objects.Types;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects
|
||||
{
|
||||
public class CurvedHitObject : HitObject, IHasCurve
|
||||
{
|
||||
public SliderCurve Curve { get; } = new SliderCurve();
|
||||
|
||||
public int RepeatCount { get; set; } = 1;
|
||||
|
||||
public double EndTime => 0;
|
||||
public double Duration => 0;
|
||||
|
||||
public List<Vector2> ControlPoints
|
||||
{
|
||||
get { return Curve.ControlPoints; }
|
||||
set { Curve.ControlPoints = value; }
|
||||
}
|
||||
|
||||
public CurveType CurveType
|
||||
{
|
||||
get { return Curve.CurveType; }
|
||||
set { Curve.CurveType = value; }
|
||||
}
|
||||
|
||||
public double Distance
|
||||
{
|
||||
get { return Curve.Distance; }
|
||||
set { Curve.Distance = value; }
|
||||
}
|
||||
|
||||
public Vector2 PositionAt(double progress) => Curve.PositionAt(ProgressAt(progress));
|
||||
|
||||
public double ProgressAt(double progress)
|
||||
{
|
||||
var p = progress * RepeatCount % 1;
|
||||
if (RepeatAt(progress) % 2 == 1)
|
||||
p = 1 - p;
|
||||
return p;
|
||||
}
|
||||
|
||||
public int RepeatAt(double progress) => (int)(progress * RepeatCount);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Database;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects
|
||||
{
|
||||
@@ -19,12 +19,16 @@ namespace osu.Game.Rulesets.Objects
|
||||
/// <summary>
|
||||
/// The time at which the HitObject starts.
|
||||
/// </summary>
|
||||
public double StartTime { get; set; }
|
||||
public double StartTime;
|
||||
|
||||
/// <summary>
|
||||
/// The samples to be played when this hit object is hit.
|
||||
/// <para>
|
||||
/// In the case of <see cref="IHasRepeats"/> types, this is the sample of the curve body
|
||||
/// and can be treated as the default samples for the hit object.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public List<SampleInfo> Samples = new List<SampleInfo>();
|
||||
public SampleInfoList Samples = new SampleInfoList();
|
||||
|
||||
/// <summary>
|
||||
/// Applies default values to this HitObject.
|
||||
@@ -36,17 +40,24 @@ namespace osu.Game.Rulesets.Objects
|
||||
ControlPoint overridePoint;
|
||||
ControlPoint timingPoint = timing.TimingPointAt(StartTime, out overridePoint);
|
||||
|
||||
foreach (var sample in Samples)
|
||||
{
|
||||
if (sample.Volume == 0)
|
||||
sample.Volume = (overridePoint ?? timingPoint)?.SampleVolume ?? 0;
|
||||
ControlPoint samplePoint = overridePoint ?? timingPoint;
|
||||
|
||||
// If the bank is not assigned a name, assign it from the control point
|
||||
if (!string.IsNullOrEmpty(sample.Bank))
|
||||
continue;
|
||||
// Initialize first sample
|
||||
Samples.ForEach(s => initializeSampleInfo(s, samplePoint));
|
||||
|
||||
sample.Bank = (overridePoint ?? timingPoint)?.SampleBank ?? @"normal";
|
||||
}
|
||||
// Initialize any repeat samples
|
||||
var repeatData = this as IHasRepeats;
|
||||
repeatData?.RepeatSamples?.ForEach(r => r.ForEach(s => initializeSampleInfo(s, samplePoint)));
|
||||
}
|
||||
|
||||
private void initializeSampleInfo(SampleInfo sample, ControlPoint controlPoint)
|
||||
{
|
||||
if (sample.Volume == 0)
|
||||
sample.Volume = controlPoint?.SampleVolume ?? 0;
|
||||
|
||||
// If the bank is not assigned a name, assign it from the control point
|
||||
if (string.IsNullOrEmpty(sample.Bank))
|
||||
sample.Bank = controlPoint?.SampleBank ?? @"normal";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
|
||||
/// <summary>
|
||||
/// Legacy osu!catch Hit-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Hit : HitObject, IHasCombo, IHasXPosition
|
||||
internal sealed class ConvertHit : HitObject, IHasCombo, IHasXPosition
|
||||
{
|
||||
public float X { get; set; }
|
||||
|
||||
+7
-5
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -10,33 +11,34 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
|
||||
/// <summary>
|
||||
/// A HitObjectParser to parse legacy osu!catch Beatmaps.
|
||||
/// </summary>
|
||||
internal class HitObjectParser : Legacy.HitObjectParser
|
||||
internal class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
|
||||
{
|
||||
protected override HitObject CreateHit(Vector2 position, bool newCombo)
|
||||
{
|
||||
return new Hit
|
||||
return new ConvertHit
|
||||
{
|
||||
X = position.X,
|
||||
NewCombo = newCombo,
|
||||
};
|
||||
}
|
||||
|
||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount)
|
||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<SampleInfoList> repeatSamples)
|
||||
{
|
||||
return new Slider
|
||||
return new ConvertSlider
|
||||
{
|
||||
X = position.X,
|
||||
NewCombo = newCombo,
|
||||
ControlPoints = controlPoints,
|
||||
Distance = length,
|
||||
CurveType = curveType,
|
||||
RepeatSamples = repeatSamples,
|
||||
RepeatCount = repeatCount
|
||||
};
|
||||
}
|
||||
|
||||
protected override HitObject CreateSpinner(Vector2 position, double endTime)
|
||||
{
|
||||
return new Spinner
|
||||
return new ConvertSpinner
|
||||
{
|
||||
EndTime = endTime
|
||||
};
|
||||
+1
-1
@@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
|
||||
/// <summary>
|
||||
/// Legacy osu!catch Slider-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Slider : CurvedHitObject, IHasXPosition, IHasCombo
|
||||
internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition, IHasCombo
|
||||
{
|
||||
public float X { get; set; }
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
|
||||
/// <summary>
|
||||
/// Legacy osu!catch Spinner-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Spinner : HitObject, IHasEndTime
|
||||
internal sealed class ConvertSpinner : HitObject, IHasEndTime
|
||||
{
|
||||
public double EndTime { get; set; }
|
||||
|
||||
+130
-61
@@ -14,29 +14,28 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
/// <summary>
|
||||
/// A HitObjectParser to parse legacy Beatmaps.
|
||||
/// </summary>
|
||||
internal abstract class HitObjectParser : Objects.HitObjectParser
|
||||
internal abstract class ConvertHitObjectParser : HitObjectParser
|
||||
{
|
||||
public override HitObject Parse(string text)
|
||||
{
|
||||
string[] split = text.Split(',');
|
||||
var type = (HitObjectType)int.Parse(split[3]) & ~HitObjectType.ColourHax;
|
||||
bool combo = type.HasFlag(HitObjectType.NewCombo);
|
||||
type &= ~HitObjectType.NewCombo;
|
||||
ConvertHitObjectType type = (ConvertHitObjectType)int.Parse(split[3]) & ~ConvertHitObjectType.ColourHax;
|
||||
bool combo = type.HasFlag(ConvertHitObjectType.NewCombo);
|
||||
type &= ~ConvertHitObjectType.NewCombo;
|
||||
|
||||
int sampleVolume = 0;
|
||||
string normalSampleBank = null;
|
||||
string addSampleBank = null;
|
||||
var soundType = (LegacySoundType)int.Parse(split[4]);
|
||||
var bankInfo = new SampleBankInfo();
|
||||
|
||||
HitObject result;
|
||||
|
||||
if ((type & HitObjectType.Circle) > 0)
|
||||
if ((type & ConvertHitObjectType.Circle) > 0)
|
||||
{
|
||||
result = CreateHit(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo);
|
||||
|
||||
if (split.Length > 5)
|
||||
readCustomSampleBanks(split[5], ref normalSampleBank, ref addSampleBank, ref sampleVolume);
|
||||
readCustomSampleBanks(split[5], bankInfo);
|
||||
}
|
||||
else if ((type & HitObjectType.Slider) > 0)
|
||||
else if ((type & ConvertHitObjectType.Slider) > 0)
|
||||
{
|
||||
CurveType curveType = CurveType.Catmull;
|
||||
double length = 0;
|
||||
@@ -77,26 +76,74 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
if (split.Length > 7)
|
||||
length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);
|
||||
|
||||
result = CreateSlider(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, points, length, curveType, repeatCount);
|
||||
|
||||
if (split.Length > 10)
|
||||
readCustomSampleBanks(split[10], ref normalSampleBank, ref addSampleBank, ref sampleVolume);
|
||||
readCustomSampleBanks(split[10], bankInfo);
|
||||
|
||||
// One node for each repeat + the start and end nodes
|
||||
// Note that the first length of the slider is considered a repeat, but there are no actual repeats happening
|
||||
int nodes = Math.Max(0, repeatCount - 1) + 2;
|
||||
|
||||
// Populate node sample bank infos with the default hit object sample bank
|
||||
var nodeBankInfos = new List<SampleBankInfo>();
|
||||
for (int i = 0; i < nodes; i++)
|
||||
nodeBankInfos.Add(bankInfo.Clone());
|
||||
|
||||
// Read any per-node sample banks
|
||||
if (split.Length > 9 && split[9].Length > 0)
|
||||
{
|
||||
string[] sets = split[9].Split('|');
|
||||
for (int i = 0; i < nodes; i++)
|
||||
{
|
||||
if (i >= sets.Length)
|
||||
break;
|
||||
|
||||
SampleBankInfo info = nodeBankInfos[i];
|
||||
readCustomSampleBanks(sets[i], info);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate node sound types with the default hit object sound type
|
||||
var nodeSoundTypes = new List<LegacySoundType>();
|
||||
for (int i = 0; i < nodes; i++)
|
||||
nodeSoundTypes.Add(soundType);
|
||||
|
||||
// Read any per-node sound types
|
||||
if (split.Length > 8 && split[8].Length > 0)
|
||||
{
|
||||
string[] adds = split[8].Split('|');
|
||||
for (int i = 0; i < nodes; i++)
|
||||
{
|
||||
if (i >= adds.Length)
|
||||
break;
|
||||
|
||||
int sound;
|
||||
int.TryParse(adds[i], out sound);
|
||||
nodeSoundTypes[i] = (LegacySoundType)sound;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the final per-node samples
|
||||
var nodeSamples = new List<SampleInfoList>(nodes);
|
||||
for (int i = 0; i <= repeatCount; i++)
|
||||
nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
|
||||
|
||||
result = CreateSlider(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, points, length, curveType, repeatCount, nodeSamples);
|
||||
}
|
||||
else if ((type & HitObjectType.Spinner) > 0)
|
||||
else if ((type & ConvertHitObjectType.Spinner) > 0)
|
||||
{
|
||||
result = CreateSpinner(new Vector2(512, 384) / 2, Convert.ToDouble(split[5], CultureInfo.InvariantCulture));
|
||||
|
||||
if (split.Length > 6)
|
||||
readCustomSampleBanks(split[6], ref normalSampleBank, ref addSampleBank, ref sampleVolume);
|
||||
readCustomSampleBanks(split[6], bankInfo);
|
||||
}
|
||||
else if ((type & HitObjectType.Hold) > 0)
|
||||
else if ((type & ConvertHitObjectType.Hold) > 0)
|
||||
{
|
||||
// Note: Hold is generated by BMS converts
|
||||
|
||||
// Todo: Apparently end time is determined by samples??
|
||||
// Shouldn't need implementation until mania
|
||||
|
||||
result = new Hold
|
||||
result = new ConvertHold
|
||||
{
|
||||
Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])),
|
||||
NewCombo = combo
|
||||
@@ -106,50 +153,12 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
throw new InvalidOperationException($@"Unknown hit object type {type}");
|
||||
|
||||
result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture);
|
||||
|
||||
var soundType = (LegacySoundType)int.Parse(split[4]);
|
||||
|
||||
result.Samples.Add(new SampleInfo
|
||||
{
|
||||
Bank = normalSampleBank,
|
||||
Name = SampleInfo.HIT_NORMAL,
|
||||
Volume = sampleVolume
|
||||
});
|
||||
|
||||
if ((soundType & LegacySoundType.Finish) > 0)
|
||||
{
|
||||
result.Samples.Add(new SampleInfo
|
||||
{
|
||||
Bank = addSampleBank,
|
||||
Name = SampleInfo.HIT_FINISH,
|
||||
Volume = sampleVolume
|
||||
});
|
||||
}
|
||||
|
||||
if ((soundType & LegacySoundType.Whistle) > 0)
|
||||
{
|
||||
result.Samples.Add(new SampleInfo
|
||||
{
|
||||
Bank = addSampleBank,
|
||||
Name = SampleInfo.HIT_WHISTLE,
|
||||
Volume = sampleVolume
|
||||
});
|
||||
}
|
||||
|
||||
if ((soundType & LegacySoundType.Clap) > 0)
|
||||
{
|
||||
result.Samples.Add(new SampleInfo
|
||||
{
|
||||
Bank = addSampleBank,
|
||||
Name = SampleInfo.HIT_CLAP,
|
||||
Volume = sampleVolume
|
||||
});
|
||||
}
|
||||
result.Samples = convertSoundType(soundType, bankInfo);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void readCustomSampleBanks(string str, ref string normalSampleBank, ref string addSampleBank, ref int sampleVolume)
|
||||
private void readCustomSampleBanks(string str, SampleBankInfo bankInfo)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str))
|
||||
return;
|
||||
@@ -169,9 +178,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
if (stringAddBank == @"none")
|
||||
stringAddBank = null;
|
||||
|
||||
normalSampleBank = stringBank;
|
||||
addSampleBank = stringAddBank;
|
||||
sampleVolume = split.Length > 3 ? int.Parse(split[3]) : 0;
|
||||
bankInfo.Normal = stringBank;
|
||||
bankInfo.Add = stringAddBank;
|
||||
|
||||
if (split.Length > 3)
|
||||
bankInfo.Volume = int.Parse(split[3]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -191,8 +202,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
/// <param name="length">The slider length.</param>
|
||||
/// <param name="curveType">The slider curve type.</param>
|
||||
/// <param name="repeatCount">The slider repeat count.</param>
|
||||
/// <param name="repeatSamples">The samples to be played when the repeat nodes are hit. This includes the head and tail of the slider.</param>
|
||||
/// <returns>The hit object.</returns>
|
||||
protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount);
|
||||
protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<SampleInfoList> repeatSamples);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a legacy Spinner-type hit object.
|
||||
@@ -202,6 +214,63 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
/// <returns>The hit object.</returns>
|
||||
protected abstract HitObject CreateSpinner(Vector2 position, double endTime);
|
||||
|
||||
private SampleInfoList convertSoundType(LegacySoundType type, SampleBankInfo bankInfo)
|
||||
{
|
||||
var soundTypes = new SampleInfoList
|
||||
{
|
||||
new SampleInfo
|
||||
{
|
||||
Bank = bankInfo.Normal,
|
||||
Name = SampleInfo.HIT_NORMAL,
|
||||
Volume = bankInfo.Volume
|
||||
}
|
||||
};
|
||||
|
||||
if ((type & LegacySoundType.Finish) > 0)
|
||||
{
|
||||
soundTypes.Add(new SampleInfo
|
||||
{
|
||||
Bank = bankInfo.Add,
|
||||
Name = SampleInfo.HIT_FINISH,
|
||||
Volume = bankInfo.Volume
|
||||
});
|
||||
}
|
||||
|
||||
if ((type & LegacySoundType.Whistle) > 0)
|
||||
{
|
||||
soundTypes.Add(new SampleInfo
|
||||
{
|
||||
Bank = bankInfo.Add,
|
||||
Name = SampleInfo.HIT_WHISTLE,
|
||||
Volume = bankInfo.Volume
|
||||
});
|
||||
}
|
||||
|
||||
if ((type & LegacySoundType.Clap) > 0)
|
||||
{
|
||||
soundTypes.Add(new SampleInfo
|
||||
{
|
||||
Bank = bankInfo.Add,
|
||||
Name = SampleInfo.HIT_CLAP,
|
||||
Volume = bankInfo.Volume
|
||||
});
|
||||
}
|
||||
|
||||
return soundTypes;
|
||||
}
|
||||
|
||||
private class SampleBankInfo
|
||||
{
|
||||
public string Normal;
|
||||
public string Add;
|
||||
public int Volume;
|
||||
|
||||
public SampleBankInfo Clone()
|
||||
{
|
||||
return (SampleBankInfo)MemberwiseClone();
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum LegacySoundType
|
||||
{
|
||||
+1
-1
@@ -6,7 +6,7 @@ using System;
|
||||
namespace osu.Game.Rulesets.Objects.Legacy
|
||||
{
|
||||
[Flags]
|
||||
public enum HitObjectType
|
||||
internal enum ConvertHitObjectType
|
||||
{
|
||||
Circle = 1 << 0,
|
||||
Slider = 1 << 1,
|
||||
+1
-1
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
/// <summary>
|
||||
/// Legacy Hold-type, used for parsing "specials" in beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Hold : HitObject, IHasPosition, IHasCombo, IHasHold
|
||||
internal sealed class ConvertHold : HitObject, IHasPosition, IHasCombo, IHasHold
|
||||
{
|
||||
public Vector2 Position { get; set; }
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenTK;
|
||||
using osu.Game.Audio;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Legacy
|
||||
{
|
||||
internal abstract class ConvertSlider : HitObject, IHasCurve
|
||||
{
|
||||
public List<Vector2> ControlPoints { get; set; }
|
||||
public CurveType CurveType { get; set; }
|
||||
public double Distance { get; set; }
|
||||
|
||||
public List<SampleInfoList> RepeatSamples { get; set; }
|
||||
public int RepeatCount { get; set; } = 1;
|
||||
|
||||
public double EndTime { get; set; }
|
||||
public double Duration { get; set; }
|
||||
|
||||
public Vector2 PositionAt(double progress)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public double ProgressAt(double progress)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public int RepeatAt(double progress)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
|
||||
/// <summary>
|
||||
/// Legacy osu!mania Hit-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Hit : HitObject, IHasXPosition, IHasCombo
|
||||
internal sealed class ConvertHit : HitObject, IHasXPosition, IHasCombo
|
||||
{
|
||||
public float X { get; set; }
|
||||
|
||||
+7
-5
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -10,33 +11,34 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
|
||||
/// <summary>
|
||||
/// A HitObjectParser to parse legacy osu!mania Beatmaps.
|
||||
/// </summary>
|
||||
internal class HitObjectParser : Legacy.HitObjectParser
|
||||
internal class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
|
||||
{
|
||||
protected override HitObject CreateHit(Vector2 position, bool newCombo)
|
||||
{
|
||||
return new Hit
|
||||
return new ConvertHit
|
||||
{
|
||||
X = position.X,
|
||||
NewCombo = newCombo,
|
||||
};
|
||||
}
|
||||
|
||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount)
|
||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<SampleInfoList> repeatSamples)
|
||||
{
|
||||
return new Slider
|
||||
return new ConvertSlider
|
||||
{
|
||||
X = position.X,
|
||||
NewCombo = newCombo,
|
||||
ControlPoints = controlPoints,
|
||||
Distance = length,
|
||||
CurveType = curveType,
|
||||
RepeatSamples = repeatSamples,
|
||||
RepeatCount = repeatCount
|
||||
};
|
||||
}
|
||||
|
||||
protected override HitObject CreateSpinner(Vector2 position, double endTime)
|
||||
{
|
||||
return new Spinner
|
||||
return new ConvertSpinner
|
||||
{
|
||||
X = position.X,
|
||||
EndTime = endTime
|
||||
+1
-1
@@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
|
||||
/// <summary>
|
||||
/// Legacy osu!mania Slider-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Slider : CurvedHitObject, IHasXPosition, IHasCombo
|
||||
internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition, IHasCombo
|
||||
{
|
||||
public float X { get; set; }
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
|
||||
/// <summary>
|
||||
/// Legacy osu!mania Spinner-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Spinner : HitObject, IHasEndTime, IHasXPosition
|
||||
internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasXPosition
|
||||
{
|
||||
public double EndTime { get; set; }
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
|
||||
/// <summary>
|
||||
/// Legacy osu! Hit-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Hit : HitObject, IHasPosition, IHasCombo
|
||||
internal sealed class ConvertHit : HitObject, IHasPosition, IHasCombo
|
||||
{
|
||||
public Vector2 Position { get; set; }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user