mirror of
https://github.com/ppy/osu.git
synced 2024-12-15 05:02:55 +08:00
Merge branch 'master' into Private_Messages
This commit is contained in:
commit
6049303c40
8
.github/pull_request_template.md
vendored
Normal file
8
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
Add any details pertaining to developers above the break.
|
||||
|
||||
- [ ] Depends on #PR
|
||||
- Closes #ISSUE
|
||||
|
||||
---
|
||||
|
||||
Add a sentence or two describing this change in plain english. This will be displayed on the [changelog](https://osu.ppy.sh/home/changelog). A single screenshot or short gif is also welcomed.
|
29
osu.Desktop.Deploy/.vscode/launch.json
vendored
29
osu.Desktop.Deploy/.vscode/launch.json
vendored
@ -1,29 +0,0 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [{
|
||||
"name": "Deploy (Debug)",
|
||||
"request": "launch",
|
||||
"type": "mono",
|
||||
"program": "${workspaceRoot}/bin/Debug/net471/osu.Desktop.Deploy.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build (Debug)",
|
||||
"runtimeExecutable": null,
|
||||
"env": {},
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": "Deploy (Release)",
|
||||
"request": "launch",
|
||||
"type": "clr",
|
||||
"program": "${workspaceRoot}/bin/Release/net471/osu.Desktop.Deploy.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build (Release)",
|
||||
"runtimeExecutable": null,
|
||||
"env": {},
|
||||
"console": "internalConsole"
|
||||
}
|
||||
]
|
||||
}
|
64
osu.Desktop.Deploy/.vscode/tasks.json
vendored
64
osu.Desktop.Deploy/.vscode/tasks.json
vendored
@ -1,64 +0,0 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"command": "msbuild",
|
||||
"type": "shell",
|
||||
"suppressTaskName": true,
|
||||
"args": [
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/property:DebugType=portable",
|
||||
"/verbosity:minimal",
|
||||
"/m" //parallel compiling support.
|
||||
],
|
||||
"tasks": [{
|
||||
"taskName": "Build (Debug)",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$msCompile"
|
||||
]
|
||||
},
|
||||
{
|
||||
"taskName": "Build (Release)",
|
||||
"group": "build",
|
||||
"args": [
|
||||
"/property:Configuration=Release"
|
||||
],
|
||||
"problemMatcher": [
|
||||
"$msCompile"
|
||||
]
|
||||
},
|
||||
{
|
||||
"taskName": "Clean (Debug)",
|
||||
"args": [
|
||||
"/target:Clean"
|
||||
],
|
||||
"problemMatcher": [
|
||||
"$msCompile"
|
||||
]
|
||||
},
|
||||
{
|
||||
"taskName": "Clean (Release)",
|
||||
"args": [
|
||||
"/target:Clean",
|
||||
"/property:Configuration=Release"
|
||||
],
|
||||
"problemMatcher": [
|
||||
"$msCompile"
|
||||
]
|
||||
},
|
||||
{
|
||||
"taskName": "Clean All",
|
||||
"dependsOn": [
|
||||
"Clean (Debug)",
|
||||
"Clean (Release)"
|
||||
],
|
||||
"problemMatcher": [
|
||||
"$msCompile"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -73,7 +73,7 @@ namespace osu.Desktop
|
||||
}
|
||||
|
||||
public StableStorage()
|
||||
: base(string.Empty)
|
||||
: base(string.Empty, null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.1" />
|
||||
<PackageReference Include="squirrel.windows" Version="1.8.0" Condition="'$(TargetFramework)' == 'net471'" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Resources">
|
||||
|
@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
{
|
||||
public class CatchDifficultyAttributes : DifficultyAttributes
|
||||
{
|
||||
public double ApproachRate;
|
||||
public int MaxCombo;
|
||||
|
||||
public CatchDifficultyAttributes(Mod[] mods, double starRating)
|
||||
: base(mods, starRating)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +1,146 @@
|
||||
// Copyright (c) 2007-2018 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.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
{
|
||||
public class CatchDifficultyCalculator : DifficultyCalculator
|
||||
{
|
||||
|
||||
/// <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 = 750;
|
||||
|
||||
/// <summary>
|
||||
/// The weighting of each strain value decays to this number * it's previous value
|
||||
/// </summary>
|
||||
private const double decay_weight = 0.94;
|
||||
|
||||
private const double star_scaling_factor = 0.145;
|
||||
|
||||
public CatchDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) => new DifficultyAttributes(mods, 0);
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||
{
|
||||
if (!beatmap.HitObjects.Any())
|
||||
return new CatchDifficultyAttributes(mods, 0);
|
||||
|
||||
var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty);
|
||||
float halfCatchWidth = catcher.CatchWidth * 0.5f;
|
||||
|
||||
var difficultyHitObjects = new List<CatchDifficultyHitObject>();
|
||||
|
||||
foreach (var hitObject in beatmap.HitObjects)
|
||||
{
|
||||
// We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations.
|
||||
if (hitObject is Fruit)
|
||||
{
|
||||
difficultyHitObjects.Add(new CatchDifficultyHitObject((CatchHitObject)hitObject, halfCatchWidth));
|
||||
}
|
||||
if (hitObject is JuiceStream)
|
||||
difficultyHitObjects.AddRange(hitObject.NestedHitObjects.OfType<CatchHitObject>().Where(o => !(o is TinyDroplet)).Select(o => new CatchDifficultyHitObject(o, halfCatchWidth)));
|
||||
}
|
||||
|
||||
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
|
||||
|
||||
if (!calculateStrainValues(difficultyHitObjects, timeRate))
|
||||
return new CatchDifficultyAttributes(mods, 0);
|
||||
|
||||
// this is the same as osu!, so there's potential to share the implementation... maybe
|
||||
double preEmpt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
|
||||
double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, timeRate)) * star_scaling_factor;
|
||||
|
||||
return new CatchDifficultyAttributes(mods, starRating)
|
||||
{
|
||||
ApproachRate = preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0,
|
||||
MaxCombo = difficultyHitObjects.Count
|
||||
};
|
||||
}
|
||||
|
||||
private bool calculateStrainValues(List<CatchDifficultyHitObject> objects, double timeRate)
|
||||
{
|
||||
CatchDifficultyHitObject lastObject = null;
|
||||
|
||||
if (!objects.Any()) return false;
|
||||
|
||||
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
|
||||
foreach (var currentObject in objects)
|
||||
{
|
||||
if (lastObject != null)
|
||||
currentObject.CalculateStrains(lastObject, timeRate);
|
||||
|
||||
lastObject = currentObject;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private double calculateDifficulty(List<CatchDifficultyHitObject> objects, double timeRate)
|
||||
{
|
||||
// The strain step needs to be adjusted for the algorithm to be considered equal with speed changing mods
|
||||
double actualStrainStep = strain_step * timeRate;
|
||||
|
||||
// Find the highest strain value within each strain step
|
||||
var highestStrains = new List<double>();
|
||||
double intervalEndTime = actualStrainStep;
|
||||
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
|
||||
|
||||
CatchDifficultyHitObject previousHitObject = null;
|
||||
foreach (CatchDifficultyHitObject hitObject in objects)
|
||||
{
|
||||
// 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(CatchDifficultyHitObject.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
130
osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs
Normal file
130
osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs
Normal file
@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
{
|
||||
public class CatchDifficultyHitObject
|
||||
{
|
||||
internal static readonly double DECAY_BASE = 0.20;
|
||||
private const float normalized_hitobject_radius = 41.0f;
|
||||
private const float absolute_player_positioning_error = 16f;
|
||||
private readonly float playerPositioningError;
|
||||
|
||||
internal CatchHitObject BaseHitObject;
|
||||
|
||||
/// <summary>
|
||||
/// Measures jump difficulty. CtB doesn't have something like button pressing speed or accuracy
|
||||
/// </summary>
|
||||
internal double Strain = 1;
|
||||
|
||||
/// <summary>
|
||||
/// This is required to keep track of lazy player movement (always moving only as far as necessary)
|
||||
/// Without this quick repeat sliders / weirdly shaped streams might become ridiculously overrated
|
||||
/// </summary>
|
||||
internal float PlayerPositionOffset;
|
||||
internal float LastMovement;
|
||||
|
||||
internal float NormalizedPosition;
|
||||
internal float ActualNormalizedPosition => NormalizedPosition + PlayerPositionOffset;
|
||||
|
||||
internal CatchDifficultyHitObject(CatchHitObject baseHitObject, float catcherWidthHalf)
|
||||
{
|
||||
BaseHitObject = baseHitObject;
|
||||
|
||||
// We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
|
||||
float scalingFactor = normalized_hitobject_radius / catcherWidthHalf;
|
||||
|
||||
playerPositioningError = absolute_player_positioning_error; // * scalingFactor;
|
||||
NormalizedPosition = baseHitObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
|
||||
}
|
||||
|
||||
private const double direction_change_bonus = 12.5;
|
||||
internal void CalculateStrains(CatchDifficultyHitObject 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.
|
||||
double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
|
||||
double decay = Math.Pow(DECAY_BASE, timeElapsed / 1000);
|
||||
|
||||
// Update new position with lazy movement.
|
||||
PlayerPositionOffset =
|
||||
MathHelper.Clamp(
|
||||
previousHitObject.ActualNormalizedPosition,
|
||||
NormalizedPosition - (normalized_hitobject_radius - playerPositioningError),
|
||||
NormalizedPosition + (normalized_hitobject_radius - playerPositioningError)) // Obtain new lazy position, but be stricter by allowing for an error of a certain degree of the player.
|
||||
- NormalizedPosition; // Subtract HitObject position to obtain offset
|
||||
|
||||
LastMovement = DistanceTo(previousHitObject);
|
||||
double addition = spacingWeight(LastMovement);
|
||||
|
||||
if (NormalizedPosition < previousHitObject.NormalizedPosition)
|
||||
{
|
||||
LastMovement = -LastMovement;
|
||||
}
|
||||
|
||||
CatchHitObject previousHitCircle = previousHitObject.BaseHitObject;
|
||||
|
||||
double additionBonus = 0;
|
||||
double sqrtTime = Math.Sqrt(Math.Max(timeElapsed, 25));
|
||||
|
||||
// Direction changes give an extra point!
|
||||
if (Math.Abs(LastMovement) > 0.1)
|
||||
{
|
||||
if (Math.Abs(previousHitObject.LastMovement) > 0.1 && Math.Sign(LastMovement) != Math.Sign(previousHitObject.LastMovement))
|
||||
{
|
||||
double bonus = direction_change_bonus / sqrtTime;
|
||||
|
||||
// Weight bonus by how
|
||||
double bonusFactor = Math.Min(playerPositioningError, Math.Abs(LastMovement)) / playerPositioningError;
|
||||
|
||||
// We want time to play a role twice here!
|
||||
addition += bonus * bonusFactor;
|
||||
|
||||
// Bonus for tougher direction switches and "almost" hyperdashes at this point
|
||||
if (previousHitCircle != null && previousHitCircle.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH)
|
||||
{
|
||||
additionBonus += 0.3 * bonusFactor;
|
||||
}
|
||||
}
|
||||
|
||||
// Base bonus for every movement, giving some weight to streams.
|
||||
addition += 7.5 * Math.Min(Math.Abs(LastMovement), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtTime;
|
||||
}
|
||||
|
||||
// Bonus for "almost" hyperdashes at corner points
|
||||
if (previousHitCircle != null && previousHitCircle.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH)
|
||||
{
|
||||
if (!previousHitCircle.HyperDash)
|
||||
{
|
||||
additionBonus += 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// After a hyperdash we ARE in the correct position. Always!
|
||||
PlayerPositionOffset = 0;
|
||||
}
|
||||
|
||||
addition *= 1.0 + additionBonus * ((10 - previousHitCircle.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 10);
|
||||
}
|
||||
|
||||
addition *= 850.0 / Math.Max(timeElapsed, 25);
|
||||
|
||||
Strain = previousHitObject.Strain * decay + addition;
|
||||
}
|
||||
|
||||
private static double spacingWeight(float distance)
|
||||
{
|
||||
return Math.Pow(distance, 1.3) / 500;
|
||||
}
|
||||
|
||||
internal float DistanceTo(CatchDifficultyHitObject other)
|
||||
{
|
||||
return Math.Abs(ActualNormalizedPosition - other.ActualNormalizedPosition);
|
||||
}
|
||||
}
|
||||
}
|
@ -24,6 +24,11 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
public int ComboIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The distance for a fruit to to next hyper if it's not a hyper.
|
||||
/// </summary>
|
||||
public float DistanceToHyperDash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The next fruit starts a new combo. Used for explodey.
|
||||
/// </summary>
|
||||
|
@ -28,9 +28,9 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
}
|
||||
}
|
||||
|
||||
public override List<InputState> GetPendingStates()
|
||||
public override List<IInput> GetPendingInputs()
|
||||
{
|
||||
if (!Position.HasValue) return new List<InputState>();
|
||||
if (!Position.HasValue) return new List<IInput>();
|
||||
|
||||
var actions = new List<CatchAction>();
|
||||
|
||||
@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
else if (Position.Value < CurrentFrame.Position)
|
||||
actions.Add(CatchAction.MoveLeft);
|
||||
|
||||
return new List<InputState>
|
||||
return new List<IInput>
|
||||
{
|
||||
new CatchReplayState
|
||||
{
|
||||
|
@ -16,6 +16,7 @@ using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||
using osu.Game.Rulesets.Catch.Replays;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
@ -93,7 +94,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
var state = GetContainingInputManager().CurrentState as CatchFramedReplayInputHandler.CatchReplayState;
|
||||
var state = (GetContainingInputManager().CurrentState as RulesetInputManagerInputState<CatchAction>)?.LastReplayState as CatchFramedReplayInputHandler.CatchReplayState;
|
||||
|
||||
if (state?.CatcherX != null)
|
||||
MovableCatcher.X = state.CatcherX.Value;
|
||||
@ -105,6 +106,11 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
public class Catcher : Container, IKeyBindingHandler<CatchAction>
|
||||
{
|
||||
/// <summary>
|
||||
/// Width of the area that can be used to attempt catches during gameplay.
|
||||
/// </summary>
|
||||
internal float CatchWidth => CATCHER_SIZE * Math.Abs(Scale.X);
|
||||
|
||||
private Container<DrawableHitObject> caughtFruit;
|
||||
|
||||
public Container ExplodingFruitTarget;
|
||||
@ -232,15 +238,15 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
/// <returns>Whether the catch is possible.</returns>
|
||||
public bool AttemptCatch(CatchHitObject fruit)
|
||||
{
|
||||
double halfCatcherWidth = CATCHER_SIZE * Math.Abs(Scale.X) * 0.5f;
|
||||
float halfCatchWidth = CatchWidth * 0.5f;
|
||||
|
||||
// this stuff wil disappear once we move fruit to non-relative coordinate space in the future.
|
||||
var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH;
|
||||
var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH;
|
||||
|
||||
var validCatch =
|
||||
catchObjectPosition >= catcherPosition - halfCatcherWidth &&
|
||||
catchObjectPosition <= catcherPosition + halfCatcherWidth;
|
||||
catchObjectPosition >= catcherPosition - halfCatchWidth &&
|
||||
catchObjectPosition <= catcherPosition + halfCatchWidth;
|
||||
|
||||
if (validCatch && fruit.HyperDash)
|
||||
{
|
||||
|
@ -17,6 +17,6 @@ namespace osu.Game.Rulesets.Mania.Replays
|
||||
|
||||
protected override bool IsImportant(ManiaReplayFrame frame) => frame.Actions.Any();
|
||||
|
||||
public override List<InputState> GetPendingStates() => new List<InputState> { new ReplayState<ManiaAction> { PressedActions = CurrentFrame.Actions } };
|
||||
public override List<IInput> GetPendingInputs() => new List<IInput> { new ReplayState<ManiaAction> { PressedActions = CurrentFrame.Actions } };
|
||||
}
|
||||
}
|
||||
|
@ -30,13 +30,16 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
}
|
||||
}
|
||||
|
||||
public override List<InputState> GetPendingStates()
|
||||
public override List<IInput> GetPendingInputs()
|
||||
{
|
||||
return new List<InputState>
|
||||
return new List<IInput>
|
||||
{
|
||||
new MousePositionAbsoluteInput
|
||||
{
|
||||
Position = GamefieldToScreenSpace(Position ?? Vector2.Zero)
|
||||
},
|
||||
new ReplayState<OsuAction>
|
||||
{
|
||||
Mouse = new ReplayMouseState(GamefieldToScreenSpace(Position ?? Vector2.Zero)),
|
||||
PressedActions = CurrentFrame.Actions
|
||||
}
|
||||
};
|
||||
|
@ -17,6 +17,6 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
||||
|
||||
protected override bool IsImportant(TaikoReplayFrame frame) => frame.Actions.Any();
|
||||
|
||||
public override List<InputState> GetPendingStates() => new List<InputState> { new ReplayState<TaikoAction> { PressedActions = CurrentFrame.Actions } };
|
||||
public override List<IInput> GetPendingInputs() => new List<IInput> { new ReplayState<TaikoAction> { PressedActions = CurrentFrame.Actions } };
|
||||
}
|
||||
}
|
||||
|
@ -27,14 +27,12 @@ namespace osu.Game.Rulesets.Taiko
|
||||
|
||||
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre),
|
||||
new KeyBinding(InputKey.MouseRight, TaikoAction.LeftRim),
|
||||
new KeyBinding(InputKey.D, TaikoAction.LeftRim),
|
||||
new KeyBinding(InputKey.F, TaikoAction.LeftCentre),
|
||||
new KeyBinding(InputKey.J, TaikoAction.RightCentre),
|
||||
new KeyBinding(InputKey.K, TaikoAction.RightRim),
|
||||
new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre),
|
||||
new KeyBinding(InputKey.MouseLeft, TaikoAction.RightCentre),
|
||||
new KeyBinding(InputKey.MouseRight, TaikoAction.LeftRim),
|
||||
new KeyBinding(InputKey.MouseRight, TaikoAction.RightRim),
|
||||
};
|
||||
|
||||
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
|
||||
|
@ -167,6 +167,7 @@ namespace osu.Game.Tests.Visual
|
||||
AddStep(@"set rooms", () => lounge.Rooms = rooms);
|
||||
selectAssert(1);
|
||||
AddStep(@"open room 1", () => clickRoom(1));
|
||||
AddUntilStep(() => !lounge.IsCurrentScreen, "wait until room current");
|
||||
AddStep(@"make lounge current", lounge.MakeCurrent);
|
||||
filterAssert(@"THE FINAL", LoungeTab.Public, 1);
|
||||
filterAssert(string.Empty, LoungeTab.Public, 2);
|
||||
|
26
osu.Game.Tests/Visual/TestCaseParallaxContainer.cs
Normal file
26
osu.Game.Tests/Visual/TestCaseParallaxContainer.cs
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCaseParallaxContainer : OsuTestCase
|
||||
{
|
||||
public TestCaseParallaxContainer()
|
||||
{
|
||||
ParallaxContainer parallax;
|
||||
|
||||
Add(parallax = new ParallaxContainer
|
||||
{
|
||||
Child = new BackgroundScreenDefault { Alpha = 0.8f }
|
||||
});
|
||||
|
||||
AddStep("default parallax", () => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT);
|
||||
AddStep("high parallax", () => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * 10);
|
||||
AddStep("no parallax", () => parallax.ParallaxAmount = 0);
|
||||
AddStep("negative parallax", () => parallax.ParallaxAmount = -ParallaxContainer.DEFAULT_PARALLAX_AMOUNT);
|
||||
}
|
||||
}
|
||||
}
|
127
osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs
Normal file
127
osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs
Normal file
@ -0,0 +1,127 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCasePreviewTrackManager : OsuTestCase, IPreviewTrackOwner
|
||||
{
|
||||
private readonly PreviewTrackManager trackManager = new TestPreviewTrackManager();
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
dependencies.CacheAs(trackManager);
|
||||
dependencies.CacheAs<IPreviewTrackOwner>(this);
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddInternal(trackManager);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStartStop()
|
||||
{
|
||||
PreviewTrack track = null;
|
||||
|
||||
AddStep("get track", () => track = getOwnedTrack());
|
||||
AddStep("start", () => track.Start());
|
||||
AddAssert("started", () => track.IsRunning);
|
||||
AddStep("stop", () => track.Stop());
|
||||
AddAssert("stopped", () => !track.IsRunning);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStartMultipleTracks()
|
||||
{
|
||||
PreviewTrack track1 = null;
|
||||
PreviewTrack track2 = null;
|
||||
|
||||
AddStep("get tracks", () =>
|
||||
{
|
||||
track1 = getOwnedTrack();
|
||||
track2 = getOwnedTrack();
|
||||
});
|
||||
|
||||
AddStep("start track 1", () => track1.Start());
|
||||
AddStep("start track 2", () => track2.Start());
|
||||
AddAssert("track 1 stopped", () => !track1.IsRunning);
|
||||
AddAssert("track 2 started", () => track2.IsRunning);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCancelFromOwner()
|
||||
{
|
||||
PreviewTrack track = null;
|
||||
|
||||
AddStep("get track", () => track = getOwnedTrack());
|
||||
AddStep("start", () => track.Start());
|
||||
AddStep("stop by owner", () => trackManager.StopAnyPlaying(this));
|
||||
AddAssert("stopped", () => !track.IsRunning);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCancelFromNonOwner()
|
||||
{
|
||||
TestTrackOwner owner = null;
|
||||
PreviewTrack track = null;
|
||||
|
||||
AddStep("get track", () => AddInternal(owner = new TestTrackOwner(track = getTrack())));
|
||||
AddStep("start", () => track.Start());
|
||||
AddStep("attempt stop", () => trackManager.StopAnyPlaying(this));
|
||||
AddAssert("not stopped", () => track.IsRunning);
|
||||
AddStep("stop by true owner", () => trackManager.StopAnyPlaying(owner));
|
||||
AddAssert("stopped", () => !track.IsRunning);
|
||||
}
|
||||
|
||||
private PreviewTrack getTrack() => trackManager.Get(null);
|
||||
|
||||
private PreviewTrack getOwnedTrack()
|
||||
{
|
||||
var track = getTrack();
|
||||
|
||||
AddInternal(track);
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
private class TestTrackOwner : CompositeDrawable, IPreviewTrackOwner
|
||||
{
|
||||
public TestTrackOwner(PreviewTrack track)
|
||||
{
|
||||
AddInternal(track);
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
dependencies.CacheAs<IPreviewTrackOwner>(this);
|
||||
return dependencies;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestPreviewTrackManager : PreviewTrackManager
|
||||
{
|
||||
protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) => new TestPreviewTrack(beatmapSetInfo, trackManager);
|
||||
|
||||
protected class TestPreviewTrack : TrackManagerPreviewTrack
|
||||
{
|
||||
public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager)
|
||||
: base(beatmapSetInfo, trackManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Track GetTrack() => new TrackVirtual { Length = 100000 };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
16
osu.Game/Audio/IPreviewTrackOwner.cs
Normal file
16
osu.Game/Audio/IPreviewTrackOwner.cs
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Audio
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for objects that can own <see cref="IPreviewTrack"/>s.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="IPreviewTrackOwner"/>s can cancel the currently playing <see cref="PreviewTrack"/> through the
|
||||
/// global <see cref="PreviewTrackManager"/> if they're the owner of the playing <see cref="PreviewTrack"/>.
|
||||
/// </remarks>
|
||||
public interface IPreviewTrackOwner
|
||||
{
|
||||
}
|
||||
}
|
103
osu.Game/Audio/PreviewTrack.cs
Normal file
103
osu.Game/Audio/PreviewTrack.cs
Normal file
@ -0,0 +1,103 @@
|
||||
// Copyright (c) 2007-2018 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.Audio.Track;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Threading;
|
||||
|
||||
namespace osu.Game.Audio
|
||||
{
|
||||
public abstract class PreviewTrack : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked when this <see cref="PreviewTrack"/> has stopped playing.
|
||||
/// </summary>
|
||||
public event Action Stopped;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when this <see cref="PreviewTrack"/> has started playing.
|
||||
/// </summary>
|
||||
public event Action Started;
|
||||
|
||||
private Track track;
|
||||
private bool hasStarted;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
track = GetTrack();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Length of the track.
|
||||
/// </summary>
|
||||
public double Length => track?.Length ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// The current track time.
|
||||
/// </summary>
|
||||
public double CurrentTime => track?.CurrentTime ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the track is loaded.
|
||||
/// </summary>
|
||||
public bool TrackLoaded => track?.IsLoaded ?? false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the track is playing.
|
||||
/// </summary>
|
||||
public bool IsRunning => track?.IsRunning ?? false;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
// Todo: Track currently doesn't signal its completion, so we have to handle it manually
|
||||
if (hasStarted && track.HasCompleted)
|
||||
Stop();
|
||||
}
|
||||
|
||||
private ScheduledDelegate startDelegate;
|
||||
|
||||
/// <summary>
|
||||
/// Starts playing this <see cref="PreviewTrack"/>.
|
||||
/// </summary>
|
||||
public void Start() => startDelegate = Schedule(() =>
|
||||
{
|
||||
if (track == null)
|
||||
return;
|
||||
|
||||
if (hasStarted)
|
||||
return;
|
||||
hasStarted = true;
|
||||
|
||||
track.Restart();
|
||||
Started?.Invoke();
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Stops playing this <see cref="PreviewTrack"/>.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
startDelegate?.Cancel();
|
||||
|
||||
if (track == null)
|
||||
return;
|
||||
|
||||
if (!hasStarted)
|
||||
return;
|
||||
hasStarted = false;
|
||||
|
||||
track.Stop();
|
||||
Stopped?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the audio track.
|
||||
/// </summary>
|
||||
protected abstract Track GetTrack();
|
||||
}
|
||||
}
|
107
osu.Game/Audio/PreviewTrackManager.cs
Normal file
107
osu.Game/Audio/PreviewTrackManager.cs
Normal file
@ -0,0 +1,107 @@
|
||||
// Copyright (c) 2007-2018 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.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Audio
|
||||
{
|
||||
/// <summary>
|
||||
/// A central store for the retrieval of <see cref="PreviewTrack"/>s.
|
||||
/// </summary>
|
||||
public class PreviewTrackManager : Component
|
||||
{
|
||||
private readonly BindableDouble muteBindable = new BindableDouble();
|
||||
|
||||
private AudioManager audio;
|
||||
private TrackManager trackManager;
|
||||
|
||||
private TrackManagerPreviewTrack current;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, FrameworkConfigManager config)
|
||||
{
|
||||
trackManager = new TrackManager(new OnlineStore());
|
||||
|
||||
this.audio = audio;
|
||||
audio.AddItem(trackManager);
|
||||
|
||||
config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="PreviewTrack"/> for a <see cref="BeatmapSetInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to retrieve the preview track for.</param>
|
||||
/// <returns>The playable <see cref="PreviewTrack"/>.</returns>
|
||||
public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo)
|
||||
{
|
||||
var track = CreatePreviewTrack(beatmapSetInfo, trackManager);
|
||||
|
||||
track.Started += () =>
|
||||
{
|
||||
current?.Stop();
|
||||
current = track;
|
||||
audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable);
|
||||
};
|
||||
|
||||
track.Stopped += () =>
|
||||
{
|
||||
current = null;
|
||||
audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable);
|
||||
};
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops any currently playing <see cref="PreviewTrack"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only the immediate owner (an object that implements <see cref="IPreviewTrackOwner"/>) of the playing <see cref="PreviewTrack"/>
|
||||
/// can globally stop the currently playing <see cref="PreviewTrack"/>. The object holding a reference to the <see cref="PreviewTrack"/>
|
||||
/// can always stop the <see cref="PreviewTrack"/> themselves through <see cref="PreviewTrack.Stop()"/>.
|
||||
/// </remarks>
|
||||
/// <param name="source">The <see cref="IPreviewTrackOwner"/> which may be the owner of the <see cref="PreviewTrack"/>.</param>
|
||||
public void StopAnyPlaying(IPreviewTrackOwner source)
|
||||
{
|
||||
if (current == null || current.Owner != source)
|
||||
return;
|
||||
|
||||
current.Stop();
|
||||
current = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="TrackManagerPreviewTrack"/>.
|
||||
/// </summary>
|
||||
protected virtual TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) => new TrackManagerPreviewTrack(beatmapSetInfo, trackManager);
|
||||
|
||||
protected class TrackManagerPreviewTrack : PreviewTrack
|
||||
{
|
||||
public IPreviewTrackOwner Owner { get; private set; }
|
||||
|
||||
private readonly BeatmapSetInfo beatmapSetInfo;
|
||||
private readonly TrackManager trackManager;
|
||||
|
||||
public TrackManagerPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager)
|
||||
{
|
||||
this.beatmapSetInfo = beatmapSetInfo;
|
||||
this.trackManager = trackManager;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IPreviewTrackOwner owner)
|
||||
{
|
||||
Owner = owner;
|
||||
}
|
||||
|
||||
protected override Track GetTrack() => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo?.OnlineBeatmapSetID}.mp3");
|
||||
}
|
||||
}
|
||||
}
|
@ -389,6 +389,9 @@ namespace osu.Game.Beatmaps
|
||||
if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null)
|
||||
return true;
|
||||
|
||||
if (api.State != APIState.Online)
|
||||
return false;
|
||||
|
||||
Logger.Log("Attempting online lookup for IDs...", LoggingTarget.Database);
|
||||
|
||||
try
|
||||
|
@ -8,20 +8,32 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using OpenTK;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
public class OsuFocusedOverlayContainer : FocusedOverlayContainer
|
||||
public class OsuFocusedOverlayContainer : FocusedOverlayContainer, IPreviewTrackOwner
|
||||
{
|
||||
private SampleChannel samplePopIn;
|
||||
private SampleChannel samplePopOut;
|
||||
|
||||
private PreviewTrackManager previewTrackManager;
|
||||
|
||||
protected readonly Bindable<OverlayActivation> OverlayActivationMode = new Bindable<OverlayActivation>(OverlayActivation.All);
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuGame osuGame, AudioManager audio)
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
dependencies.CacheAs<IPreviewTrackOwner>(this);
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuGame osuGame, AudioManager audio, PreviewTrackManager previewTrackManager)
|
||||
{
|
||||
this.previewTrackManager = previewTrackManager;
|
||||
|
||||
if (osuGame != null)
|
||||
OverlayActivationMode.BindTo(osuGame.OverlayActivationMode);
|
||||
|
||||
@ -66,5 +78,11 @@ namespace osu.Game.Graphics.Containers
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
base.PopOut();
|
||||
previewTrackManager.StopAnyPlaying(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,9 @@ namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
public const float DEFAULT_PARALLAX_AMOUNT = 0.02f;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of parallax movement. Negative values will reverse the direction of parallax relative to user input.
|
||||
/// </summary>
|
||||
public float ParallaxAmount = DEFAULT_PARALLAX_AMOUNT;
|
||||
|
||||
private Bindable<bool> parallaxEnabled;
|
||||
@ -45,7 +48,7 @@ namespace osu.Game.Graphics.Containers
|
||||
if (!parallaxEnabled)
|
||||
{
|
||||
content.MoveTo(Vector2.Zero, firstUpdate ? 0 : 1000, Easing.OutQuint);
|
||||
content.Scale = new Vector2(1 + ParallaxAmount);
|
||||
content.Scale = new Vector2(1 + System.Math.Abs(ParallaxAmount));
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -69,7 +72,7 @@ namespace osu.Game.Graphics.Containers
|
||||
double elapsed = MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000);
|
||||
|
||||
content.Position = Interpolation.ValueAt(elapsed, content.Position, offset, 0, 1000, Easing.OutQuint);
|
||||
content.Scale = Interpolation.ValueAt(elapsed, content.Scale, new Vector2(1 + ParallaxAmount), 0, 1000, Easing.OutQuint);
|
||||
content.Scale = Interpolation.ValueAt(elapsed, content.Scale, new Vector2(1 + System.Math.Abs(ParallaxAmount)), 0, 1000, Easing.OutQuint);
|
||||
}
|
||||
|
||||
firstUpdate = false;
|
||||
|
@ -32,16 +32,14 @@ namespace osu.Game.Input.Handlers
|
||||
|
||||
public override int Priority => 0;
|
||||
|
||||
public class ReplayState<T> : InputState
|
||||
public class ReplayState<T> : IInput
|
||||
where T : struct
|
||||
{
|
||||
public List<T> PressedActions;
|
||||
|
||||
public override InputState Clone()
|
||||
public void Apply(InputState state, IInputStateChangeHandler handler)
|
||||
{
|
||||
var clone = (ReplayState<T>)base.Clone();
|
||||
clone.PressedActions = new List<T>(PressedActions);
|
||||
return clone;
|
||||
handler.HandleCustomInput(state, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
376
osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs
generated
Normal file
376
osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs
generated
Normal file
@ -0,0 +1,376 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using osu.Game.Database;
|
||||
|
||||
namespace osu.Game.Migrations
|
||||
{
|
||||
[DbContext(typeof(OsuDbContext))]
|
||||
[Migration("20180621044111_UpdateTaikoDefaultBindings")]
|
||||
partial class UpdateTaikoDefaultBindings
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.1.1-rtm-30846");
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<float>("ApproachRate");
|
||||
|
||||
b.Property<float>("CircleSize");
|
||||
|
||||
b.Property<float>("DrainRate");
|
||||
|
||||
b.Property<float>("OverallDifficulty");
|
||||
|
||||
b.Property<double>("SliderMultiplier");
|
||||
|
||||
b.Property<double>("SliderTickRate");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.ToTable("BeatmapDifficulty");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AudioLeadIn");
|
||||
|
||||
b.Property<int>("BaseDifficultyID");
|
||||
|
||||
b.Property<int>("BeatDivisor");
|
||||
|
||||
b.Property<int>("BeatmapSetInfoID");
|
||||
|
||||
b.Property<bool>("Countdown");
|
||||
|
||||
b.Property<double>("DistanceSpacing");
|
||||
|
||||
b.Property<int>("GridSize");
|
||||
|
||||
b.Property<string>("Hash");
|
||||
|
||||
b.Property<bool>("Hidden");
|
||||
|
||||
b.Property<bool>("LetterboxInBreaks");
|
||||
|
||||
b.Property<string>("MD5Hash");
|
||||
|
||||
b.Property<int?>("MetadataID");
|
||||
|
||||
b.Property<int?>("OnlineBeatmapID");
|
||||
|
||||
b.Property<string>("Path");
|
||||
|
||||
b.Property<int>("RulesetID");
|
||||
|
||||
b.Property<bool>("SpecialStyle");
|
||||
|
||||
b.Property<float>("StackLeniency");
|
||||
|
||||
b.Property<double>("StarDifficulty");
|
||||
|
||||
b.Property<string>("StoredBookmarks");
|
||||
|
||||
b.Property<double>("TimelineZoom");
|
||||
|
||||
b.Property<string>("Version");
|
||||
|
||||
b.Property<bool>("WidescreenStoryboard");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("BaseDifficultyID");
|
||||
|
||||
b.HasIndex("BeatmapSetInfoID");
|
||||
|
||||
b.HasIndex("Hash");
|
||||
|
||||
b.HasIndex("MD5Hash");
|
||||
|
||||
b.HasIndex("MetadataID");
|
||||
|
||||
b.HasIndex("OnlineBeatmapID")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("RulesetID");
|
||||
|
||||
b.ToTable("BeatmapInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Artist");
|
||||
|
||||
b.Property<string>("ArtistUnicode");
|
||||
|
||||
b.Property<string>("AudioFile");
|
||||
|
||||
b.Property<string>("AuthorString")
|
||||
.HasColumnName("Author");
|
||||
|
||||
b.Property<string>("BackgroundFile");
|
||||
|
||||
b.Property<int>("PreviewTime");
|
||||
|
||||
b.Property<string>("Source");
|
||||
|
||||
b.Property<string>("Tags");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<string>("TitleUnicode");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.ToTable("BeatmapMetadata");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("BeatmapSetInfoID");
|
||||
|
||||
b.Property<int>("FileInfoID");
|
||||
|
||||
b.Property<string>("Filename")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("BeatmapSetInfoID");
|
||||
|
||||
b.HasIndex("FileInfoID");
|
||||
|
||||
b.ToTable("BeatmapSetFileInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("DeletePending");
|
||||
|
||||
b.Property<string>("Hash");
|
||||
|
||||
b.Property<int?>("MetadataID");
|
||||
|
||||
b.Property<int?>("OnlineBeatmapSetID");
|
||||
|
||||
b.Property<bool>("Protected");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("DeletePending");
|
||||
|
||||
b.HasIndex("Hash")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("MetadataID");
|
||||
|
||||
b.HasIndex("OnlineBeatmapSetID")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("BeatmapSetInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("IntKey")
|
||||
.HasColumnName("Key");
|
||||
|
||||
b.Property<int?>("RulesetID");
|
||||
|
||||
b.Property<string>("StringValue")
|
||||
.HasColumnName("Value");
|
||||
|
||||
b.Property<int?>("Variant");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("RulesetID", "Variant");
|
||||
|
||||
b.ToTable("Settings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("IntAction")
|
||||
.HasColumnName("Action");
|
||||
|
||||
b.Property<string>("KeysString")
|
||||
.HasColumnName("Keys");
|
||||
|
||||
b.Property<int?>("RulesetID");
|
||||
|
||||
b.Property<int?>("Variant");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("IntAction");
|
||||
|
||||
b.HasIndex("RulesetID", "Variant");
|
||||
|
||||
b.ToTable("KeyBinding");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Hash");
|
||||
|
||||
b.Property<int>("ReferenceCount");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("Hash")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("ReferenceCount");
|
||||
|
||||
b.ToTable("FileInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
|
||||
{
|
||||
b.Property<int?>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Available");
|
||||
|
||||
b.Property<string>("InstantiationInfo");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<string>("ShortName");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("Available");
|
||||
|
||||
b.HasIndex("ShortName")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("RulesetInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("FileInfoID");
|
||||
|
||||
b.Property<string>("Filename")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<int>("SkinInfoID");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("FileInfoID");
|
||||
|
||||
b.HasIndex("SkinInfoID");
|
||||
|
||||
b.ToTable("SkinFileInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Creator");
|
||||
|
||||
b.Property<bool>("DeletePending");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.ToTable("SkinInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
|
||||
{
|
||||
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
|
||||
.WithMany()
|
||||
.HasForeignKey("BaseDifficultyID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
|
||||
.WithMany("Beatmaps")
|
||||
.HasForeignKey("BeatmapSetInfoID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
|
||||
.WithMany("Beatmaps")
|
||||
.HasForeignKey("MetadataID");
|
||||
|
||||
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
|
||||
.WithMany()
|
||||
.HasForeignKey("RulesetID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
|
||||
{
|
||||
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
|
||||
.WithMany("Files")
|
||||
.HasForeignKey("BeatmapSetInfoID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
|
||||
.WithMany()
|
||||
.HasForeignKey("FileInfoID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
|
||||
{
|
||||
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
|
||||
.WithMany("BeatmapSets")
|
||||
.HasForeignKey("MetadataID");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
|
||||
{
|
||||
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
|
||||
.WithMany()
|
||||
.HasForeignKey("FileInfoID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("osu.Game.Skinning.SkinInfo")
|
||||
.WithMany("Files")
|
||||
.HasForeignKey("SkinInfoID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using osu.Framework.Logging;
|
||||
|
||||
namespace osu.Game.Migrations
|
||||
{
|
||||
public partial class UpdateTaikoDefaultBindings : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql("DELETE FROM KeyBinding WHERE RulesetID = 1");
|
||||
Logger.Log("osu!taiko bindings have been reset due to new defaults", LoggingTarget.Runtime, LogLevel.Important);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// we can't really tell if these should be restored or not, so let's just not do so.
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,9 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using osu.Game.Database;
|
||||
using System;
|
||||
|
||||
namespace osu.Game.Migrations
|
||||
{
|
||||
@ -16,7 +14,7 @@ namespace osu.Game.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.0.3-rtm-10026");
|
||||
.HasAnnotation("ProductVersion", "2.1.1-rtm-30846");
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
|
||||
{
|
||||
|
@ -101,6 +101,8 @@ namespace osu.Game
|
||||
public OsuGame(string[] args = null)
|
||||
{
|
||||
this.args = args;
|
||||
|
||||
forwardLoggedErrorsToNotifications();
|
||||
}
|
||||
|
||||
public void ToggleSettings() => settings.ToggleVisibility();
|
||||
@ -299,8 +301,6 @@ namespace osu.Game
|
||||
Depth = -6,
|
||||
}, overlayContent.Add);
|
||||
|
||||
forwardLoggedErrorsToNotifications();
|
||||
|
||||
dependencies.Cache(settings);
|
||||
dependencies.Cache(onscreenDisplay);
|
||||
dependencies.Cache(social);
|
||||
@ -388,31 +388,40 @@ namespace osu.Game
|
||||
|
||||
private void forwardLoggedErrorsToNotifications()
|
||||
{
|
||||
int recentErrorCount = 0;
|
||||
int recentLogCount = 0;
|
||||
|
||||
const double debounce = 5000;
|
||||
|
||||
Logger.NewEntry += entry =>
|
||||
{
|
||||
if (entry.Level < LogLevel.Error || entry.Target == null) return;
|
||||
if (entry.Level < LogLevel.Important || entry.Target == null) return;
|
||||
|
||||
if (recentErrorCount < 2)
|
||||
const int short_term_display_limit = 3;
|
||||
|
||||
if (recentLogCount < short_term_display_limit)
|
||||
{
|
||||
notifications.Post(new SimpleNotification
|
||||
Schedule(() => notifications.Post(new SimpleNotification
|
||||
{
|
||||
Icon = FontAwesome.fa_bomb,
|
||||
Text = (recentErrorCount == 0 ? entry.Message : "Subsequent errors occurred and have been logged.") + "\nClick to view log files.",
|
||||
Icon = entry.Level == LogLevel.Important ? FontAwesome.fa_exclamation_circle : FontAwesome.fa_bomb,
|
||||
Text = entry.Message,
|
||||
}));
|
||||
}
|
||||
else if (recentLogCount == short_term_display_limit)
|
||||
{
|
||||
Schedule(() => notifications.Post(new SimpleNotification
|
||||
{
|
||||
Icon = FontAwesome.fa_ellipsis_h,
|
||||
Text = "Subsequent messages have been logged. Click to view log files.",
|
||||
Activated = () =>
|
||||
{
|
||||
Host.Storage.GetStorageForDirectory("logs").OpenInNativeExplorer();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref recentErrorCount);
|
||||
|
||||
Scheduler.AddDelayed(() => Interlocked.Decrement(ref recentErrorCount), debounce);
|
||||
Interlocked.Increment(ref recentLogCount);
|
||||
Scheduler.AddDelayed(() => Interlocked.Decrement(ref recentLogCount), debounce);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ using osu.Game.Online.API;
|
||||
using osu.Framework.Graphics.Performance;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics.Textures;
|
||||
using osu.Game.Input;
|
||||
@ -192,6 +193,10 @@ namespace osu.Game
|
||||
|
||||
KeyBindingStore.Register(globalBinding);
|
||||
dependencies.Cache(globalBinding);
|
||||
|
||||
PreviewTrackManager previewTrackManager;
|
||||
dependencies.Cache(previewTrackManager = new PreviewTrackManager());
|
||||
Add(previewTrackManager);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
@ -2,13 +2,13 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
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.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
@ -25,7 +25,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
|
||||
private readonly Box bg, progress;
|
||||
private readonly PlayButton playButton;
|
||||
|
||||
private Track preview => playButton.Preview;
|
||||
private PreviewTrack preview => playButton.Preview;
|
||||
public Bindable<bool> Playing => playButton.Playing;
|
||||
|
||||
public BeatmapSetInfo BeatmapSet
|
||||
@ -66,7 +66,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
|
||||
},
|
||||
};
|
||||
|
||||
Action = () => Playing.Value = !Playing.Value;
|
||||
Action = () => playButton.TriggerOnClick();
|
||||
Playing.ValueChanged += newValue => progress.FadeTo(newValue ? 1 : 0, 100);
|
||||
}
|
||||
|
||||
@ -89,12 +89,6 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
|
||||
progress.Width = 0;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
Playing.Value = false;
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
bg.FadeColour(Color4.Black.Opacity(0.5f), 100);
|
||||
|
@ -102,8 +102,6 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
updateDisplay();
|
||||
}
|
||||
|
||||
public void StopPreview() => preview.Playing.Value = false;
|
||||
|
||||
private class DetailBox : Container
|
||||
{
|
||||
private readonly Container content;
|
||||
|
@ -1,9 +1,8 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -15,9 +14,10 @@ using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Overlays.BeatmapSet.Scores;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
@ -124,8 +124,6 @@ namespace osu.Game.Overlays
|
||||
protected override void PopOut()
|
||||
{
|
||||
base.PopOut();
|
||||
header.Details.StopPreview();
|
||||
|
||||
FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out).OnComplete(_ => BeatmapSet = null);
|
||||
}
|
||||
|
||||
|
@ -4,22 +4,22 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Audio.Track;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Direct
|
||||
private BeatmapManager beatmaps;
|
||||
private BeatmapSetOverlay beatmapSetOverlay;
|
||||
|
||||
public Track Preview => PlayButton.Preview;
|
||||
public PreviewTrack Preview => PlayButton.Preview;
|
||||
public Bindable<bool> PreviewPlaying => PlayButton.Playing;
|
||||
protected abstract PlayButton PlayButton { get; }
|
||||
protected abstract Box PreviewBar { get; }
|
||||
@ -113,7 +113,7 @@ namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (PreviewPlaying && Preview != null && Preview.IsLoaded)
|
||||
if (PreviewPlaying && Preview != null && Preview.TrackLoaded)
|
||||
{
|
||||
PreviewBar.Width = (float)(Preview.CurrentTime / Preview.Length);
|
||||
}
|
||||
@ -141,7 +141,6 @@ namespace osu.Game.Overlays.Direct
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
ShowInformation();
|
||||
PreviewPlaying.Value = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,25 +1,23 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
public class PlayButton : Container
|
||||
{
|
||||
public readonly Bindable<bool> Playing = new Bindable<bool>();
|
||||
public Track Preview { get; private set; }
|
||||
public readonly BindableBool Playing = new BindableBool();
|
||||
public PreviewTrack Preview { get; private set; }
|
||||
|
||||
private BeatmapSetInfo beatmapSet;
|
||||
|
||||
@ -31,9 +29,11 @@ namespace osu.Game.Overlays.Direct
|
||||
if (value == beatmapSet) return;
|
||||
beatmapSet = value;
|
||||
|
||||
Playing.Value = false;
|
||||
trackLoader = null;
|
||||
Preview?.Stop();
|
||||
Preview?.Expire();
|
||||
Preview = null;
|
||||
|
||||
Playing.Value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,8 +41,6 @@ namespace osu.Game.Overlays.Direct
|
||||
private readonly SpriteIcon icon;
|
||||
private readonly LoadingAnimation loadingAnimation;
|
||||
|
||||
private readonly BindableDouble muteBindable = new BindableDouble();
|
||||
|
||||
private const float transition_duration = 500;
|
||||
|
||||
private bool loading
|
||||
@ -50,15 +48,9 @@ namespace osu.Game.Overlays.Direct
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
loadingAnimation.Show();
|
||||
icon.FadeOut(transition_duration * 5, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadingAnimation.Hide();
|
||||
icon.FadeIn(transition_duration, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,19 +70,22 @@ namespace osu.Game.Overlays.Direct
|
||||
loadingAnimation = new LoadingAnimation(),
|
||||
});
|
||||
|
||||
Playing.ValueChanged += updatePreviewTrack;
|
||||
Playing.ValueChanged += playingStateChanged;
|
||||
}
|
||||
|
||||
private PreviewTrackManager previewTrackManager;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colour, AudioManager audio)
|
||||
private void load(OsuColour colour, PreviewTrackManager previewTrackManager)
|
||||
{
|
||||
this.previewTrackManager = previewTrackManager;
|
||||
|
||||
hoverColour = colour.Yellow;
|
||||
this.audio = audio;
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
Playing.Value = !Playing.Value;
|
||||
Playing.Toggle();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -107,44 +102,44 @@ namespace osu.Game.Overlays.Direct
|
||||
base.OnHoverLost(state);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
private void playingStateChanged(bool playing)
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (Preview?.HasCompleted ?? false)
|
||||
{
|
||||
Playing.Value = false;
|
||||
Preview = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePreviewTrack(bool playing)
|
||||
{
|
||||
if (playing && BeatmapSet == null)
|
||||
{
|
||||
Playing.Value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
icon.Icon = playing ? FontAwesome.fa_stop : FontAwesome.fa_play;
|
||||
icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);
|
||||
|
||||
if (playing)
|
||||
{
|
||||
if (Preview == null)
|
||||
if (BeatmapSet == null)
|
||||
{
|
||||
beginAudioLoad();
|
||||
Playing.Value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Preview.Restart();
|
||||
if (Preview != null)
|
||||
{
|
||||
Preview.Start();
|
||||
return;
|
||||
}
|
||||
|
||||
audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable);
|
||||
loading = true;
|
||||
|
||||
LoadComponentAsync(Preview = previewTrackManager.Get(beatmapSet), preview =>
|
||||
{
|
||||
// beatmapset may have changed.
|
||||
if (Preview != preview)
|
||||
return;
|
||||
|
||||
AddInternal(preview);
|
||||
loading = false;
|
||||
preview.Stopped += () => Playing.Value = false;
|
||||
|
||||
// user may have changed their mind.
|
||||
if (Playing)
|
||||
preview.Start();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable);
|
||||
|
||||
Preview?.Stop();
|
||||
loading = false;
|
||||
}
|
||||
@ -155,64 +150,5 @@ namespace osu.Game.Overlays.Direct
|
||||
base.Dispose(isDisposing);
|
||||
Playing.Value = false;
|
||||
}
|
||||
|
||||
private TrackLoader trackLoader;
|
||||
private AudioManager audio;
|
||||
|
||||
private void beginAudioLoad()
|
||||
{
|
||||
if (trackLoader != null)
|
||||
{
|
||||
Preview = trackLoader.Preview;
|
||||
Playing.TriggerChange();
|
||||
return;
|
||||
}
|
||||
|
||||
loading = true;
|
||||
|
||||
LoadComponentAsync(trackLoader = new TrackLoader($"https://b.ppy.sh/preview/{BeatmapSet.OnlineBeatmapSetID}.mp3"),
|
||||
d =>
|
||||
{
|
||||
// We may have been replaced by another loader
|
||||
if (trackLoader != d) return;
|
||||
|
||||
Preview = d?.Preview;
|
||||
updatePreviewTrack(Playing);
|
||||
loading = false;
|
||||
|
||||
Add(trackLoader);
|
||||
});
|
||||
}
|
||||
|
||||
private class TrackLoader : Drawable
|
||||
{
|
||||
private readonly string preview;
|
||||
|
||||
public Track Preview;
|
||||
private TrackManager trackManager;
|
||||
|
||||
public TrackLoader(string preview)
|
||||
{
|
||||
this.preview = preview;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, FrameworkConfigManager config)
|
||||
{
|
||||
// create a local trackManager to bypass the mute we are applying above.
|
||||
audio.AddItem(trackManager = new TrackManager(new OnlineStore()));
|
||||
|
||||
// add back the user's music volume setting (since we are no longer in the global TrackManager's hierarchy).
|
||||
config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume);
|
||||
|
||||
Preview = trackManager.Get(preview);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
trackManager?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using OpenTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@ -18,6 +18,7 @@ using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.Direct;
|
||||
using osu.Game.Overlays.SearchableList;
|
||||
using osu.Game.Rulesets;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
@ -32,7 +33,6 @@ namespace osu.Game.Overlays
|
||||
private readonly FillFlowContainer resultCountsContainer;
|
||||
private readonly OsuSpriteText resultCountsText;
|
||||
private FillFlowContainer<DirectPanel> panels;
|
||||
private DirectPanel playing;
|
||||
|
||||
protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74");
|
||||
protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71");
|
||||
@ -176,10 +176,11 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, APIAccess api, RulesetStore rulesets)
|
||||
private void load(OsuColour colours, APIAccess api, RulesetStore rulesets, PreviewTrackManager previewTrackManager)
|
||||
{
|
||||
this.api = api;
|
||||
this.rulesets = rulesets;
|
||||
this.previewTrackManager = previewTrackManager;
|
||||
|
||||
resultCountsContainer.Colour = colours.Yellow;
|
||||
}
|
||||
@ -206,12 +207,6 @@ namespace osu.Game.Overlays
|
||||
panels.FadeOut(200);
|
||||
panels.Expire();
|
||||
panels = null;
|
||||
|
||||
if (playing != null)
|
||||
{
|
||||
playing.PreviewPlaying.Value = false;
|
||||
playing = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (BeatmapSets == null) return;
|
||||
@ -242,17 +237,6 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
if (panels != null) ScrollFlow.Remove(panels);
|
||||
ScrollFlow.Add(panels = newPanels);
|
||||
|
||||
foreach (DirectPanel panel in p.Children)
|
||||
panel.PreviewPlaying.ValueChanged += newValue =>
|
||||
{
|
||||
if (newValue)
|
||||
{
|
||||
if (playing != null && playing != panel)
|
||||
playing.PreviewPlaying.Value = false;
|
||||
playing = panel;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@ -261,6 +245,7 @@ namespace osu.Game.Overlays
|
||||
private readonly Bindable<string> currentQuery = new Bindable<string>();
|
||||
|
||||
private ScheduledDelegate queryChangedDebounce;
|
||||
private PreviewTrackManager previewTrackManager;
|
||||
|
||||
private void updateSearch()
|
||||
{
|
||||
@ -277,6 +262,8 @@ namespace osu.Game.Overlays
|
||||
|
||||
if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty)) return;
|
||||
|
||||
previewTrackManager.StopAnyPlaying(this);
|
||||
|
||||
getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value ?? string.Empty,
|
||||
((FilterControl)Filter).Ruleset.Value,
|
||||
Filter.DisplayStyleControl.Dropdown.Current.Value,
|
||||
@ -300,14 +287,6 @@ namespace osu.Game.Overlays
|
||||
api.Queue(getSetsRequest);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
base.PopOut();
|
||||
|
||||
if (playing != null)
|
||||
playing.PreviewPlaying.Value = false;
|
||||
}
|
||||
|
||||
private int distinctCount(List<string> list) => list.Distinct().ToArray().Length;
|
||||
|
||||
public class ResultCounts
|
||||
|
@ -59,7 +59,7 @@ namespace osu.Game.Overlays.Notifications
|
||||
}
|
||||
});
|
||||
|
||||
Content.Add(textDrawable = new OsuTextFlowContainer(t => t.TextSize = 16)
|
||||
Content.Add(textDrawable = new OsuTextFlowContainer(t => t.TextSize = 14)
|
||||
{
|
||||
Colour = OsuColour.Gray(128),
|
||||
AutoSizeAxes = Axes.Y,
|
||||
|
@ -1,14 +1,13 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using OpenTK;
|
||||
using System.Linq;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.Direct;
|
||||
using osu.Game.Users;
|
||||
using System.Linq;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Sections.Beatmaps
|
||||
{
|
||||
@ -18,10 +17,6 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
|
||||
|
||||
private readonly BeatmapSetType type;
|
||||
|
||||
private DirectPanel currentlyPlaying;
|
||||
|
||||
public event Action<PaginatedBeatmapContainer> BeganPlayingPreview;
|
||||
|
||||
public PaginatedBeatmapContainer(BeatmapSetType type, Bindable<User> user, string header, string missing = "None... yet.")
|
||||
: base(user, header, missing)
|
||||
{
|
||||
@ -56,28 +51,10 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
|
||||
|
||||
var panel = new DirectGridPanel(s.ToBeatmapSet(Rulesets));
|
||||
ItemsContainer.Add(panel);
|
||||
|
||||
panel.PreviewPlaying.ValueChanged += isPlaying =>
|
||||
{
|
||||
StopPlayingPreview();
|
||||
|
||||
if (isPlaying)
|
||||
{
|
||||
BeganPlayingPreview?.Invoke(this);
|
||||
currentlyPlaying = panel;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Api.Queue(req);
|
||||
}
|
||||
|
||||
public void StopPlayingPreview()
|
||||
{
|
||||
if (currentlyPlaying == null) return;
|
||||
currentlyPlaying.PreviewPlaying.Value = false;
|
||||
currentlyPlaying = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Linq;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.Profile.Sections.Beatmaps;
|
||||
|
||||
@ -22,15 +21,6 @@ namespace osu.Game.Overlays.Profile.Sections
|
||||
new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, "Pending Beatmaps"),
|
||||
new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps"),
|
||||
};
|
||||
|
||||
foreach (var paginatedBeatmapContainer in Children.OfType<PaginatedBeatmapContainer>())
|
||||
{
|
||||
paginatedBeatmapContainer.BeganPlayingPreview += _ =>
|
||||
{
|
||||
foreach (var bc in Children.OfType<PaginatedBeatmapContainer>())
|
||||
bc.StopPlayingPreview();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,6 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Linq;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
@ -18,6 +16,8 @@ using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.Profile;
|
||||
using osu.Game.Overlays.Profile.Sections;
|
||||
using osu.Game.Users;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
|
@ -66,7 +66,7 @@ namespace osu.Game.Overlays.Volume
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
this.TransformTo<MuteButton, SRGBColour>("BorderColour", hoveredColour, 500, Easing.OutQuint);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Graphics;
|
||||
@ -232,12 +233,13 @@ namespace osu.Game.Overlays.Volume
|
||||
{
|
||||
float amount = adjust_step * direction;
|
||||
|
||||
var mouse = GetContainingInputManager().CurrentState.Mouse;
|
||||
if (mouse.HasPreciseScroll)
|
||||
// handle the case where the OnPressed action was actually a mouse wheel.
|
||||
// this allows for precise wheel handling.
|
||||
var state = GetContainingInputManager().CurrentState;
|
||||
if (state.Mouse?.ScrollDelta.Y != 0)
|
||||
{
|
||||
float scrollDelta = mouse.ScrollDelta.Y;
|
||||
if (scrollDelta != 0)
|
||||
amount *= Math.Abs(scrollDelta / 10);
|
||||
OnScroll(state);
|
||||
return;
|
||||
}
|
||||
|
||||
Volume += amount;
|
||||
@ -260,6 +262,34 @@ namespace osu.Game.Overlays.Volume
|
||||
return false;
|
||||
}
|
||||
|
||||
// because volume precision is set to 0.01, this local is required to keep track of more precise adjustments and only apply when possible.
|
||||
private double scrollAmount;
|
||||
|
||||
protected override bool OnScroll(InputState state)
|
||||
{
|
||||
scrollAmount += adjust_step * state.Mouse.ScrollDelta.Y * (state.Mouse.HasPreciseScroll ? 0.1f : 1);
|
||||
|
||||
if (Math.Abs(scrollAmount) < Bindable.Precision)
|
||||
return true;
|
||||
|
||||
Volume += scrollAmount;
|
||||
scrollAmount = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OnReleased(GlobalAction action) => false;
|
||||
|
||||
private const float transition_length = 500;
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
this.ScaleTo(1.04f, transition_length, Easing.OutExpo);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
this.ScaleTo(1f, transition_length, Easing.OutExpo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Input.Bindings;
|
||||
@ -86,16 +87,10 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
volumeMeterMaster.Bindable.ValueChanged += _ => settingChanged();
|
||||
volumeMeterEffect.Bindable.ValueChanged += _ => settingChanged();
|
||||
volumeMeterMusic.Bindable.ValueChanged += _ => settingChanged();
|
||||
muteButton.Current.ValueChanged += _ => settingChanged();
|
||||
}
|
||||
|
||||
private void settingChanged()
|
||||
{
|
||||
Show();
|
||||
schedulePopOut();
|
||||
volumeMeterMaster.Bindable.ValueChanged += _ => Show();
|
||||
volumeMeterEffect.Bindable.ValueChanged += _ => Show();
|
||||
volumeMeterMusic.Bindable.ValueChanged += _ => Show();
|
||||
muteButton.Current.ValueChanged += _ => Show();
|
||||
}
|
||||
|
||||
public bool Adjust(GlobalAction action)
|
||||
@ -127,6 +122,14 @@ namespace osu.Game.Overlays
|
||||
|
||||
private ScheduledDelegate popOutDelegate;
|
||||
|
||||
public override void Show()
|
||||
{
|
||||
if (State == Visibility.Visible)
|
||||
schedulePopOut();
|
||||
|
||||
base.Show();
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
ClearTransforms();
|
||||
@ -140,10 +143,33 @@ namespace osu.Game.Overlays
|
||||
this.FadeOut(100);
|
||||
}
|
||||
|
||||
protected override bool OnMouseMove(InputState state)
|
||||
{
|
||||
// keep the scheduled event correctly timed as long as we have movement.
|
||||
schedulePopOut();
|
||||
return base.OnMouseMove(state);
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
schedulePopOut();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
schedulePopOut();
|
||||
base.OnHoverLost(state);
|
||||
}
|
||||
|
||||
private void schedulePopOut()
|
||||
{
|
||||
popOutDelegate?.Cancel();
|
||||
this.Delay(1000).Schedule(Hide, out popOutDelegate);
|
||||
this.Delay(1000).Schedule(() =>
|
||||
{
|
||||
if (!IsHovered)
|
||||
Hide();
|
||||
}, out popOutDelegate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Replays
|
||||
return true;
|
||||
}
|
||||
|
||||
public override List<InputState> GetPendingStates() => new List<InputState>();
|
||||
public override List<IInput> GetPendingInputs() => new List<IInput>();
|
||||
|
||||
public bool AtLastFrame => currentFrameIndex == Frames.Count - 1;
|
||||
public bool AtFirstFrame => currentFrameIndex == 0;
|
||||
@ -119,7 +119,8 @@ namespace osu.Game.Rulesets.Replays
|
||||
{
|
||||
public ReplayKeyboardState(List<Key> keys)
|
||||
{
|
||||
Keys = keys;
|
||||
foreach (var key in keys)
|
||||
Keys.Add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ using osu.Game.Input.Bindings;
|
||||
using osu.Game.Input.Handlers;
|
||||
using osu.Game.Screens.Play;
|
||||
using OpenTK.Input;
|
||||
using static osu.Game.Input.Handlers.ReplayInputHandler;
|
||||
|
||||
namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
@ -29,6 +30,18 @@ namespace osu.Game.Rulesets.UI
|
||||
}
|
||||
}
|
||||
|
||||
protected override InputState CreateInitialState()
|
||||
{
|
||||
var state = base.CreateInitialState();
|
||||
return new RulesetInputManagerInputState<T>
|
||||
{
|
||||
Mouse = state.Mouse,
|
||||
Keyboard = state.Keyboard,
|
||||
Joystick = state.Joystick,
|
||||
LastReplayState = null
|
||||
};
|
||||
}
|
||||
|
||||
protected readonly KeyBindingContainer<T> KeyBindingContainer;
|
||||
|
||||
protected override Container<Drawable> Content => KeyBindingContainer;
|
||||
@ -42,13 +55,18 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
private List<T> lastPressedActions = new List<T>();
|
||||
|
||||
protected override void HandleNewState(InputState state)
|
||||
public override void HandleCustomInput(InputState state, IInput input)
|
||||
{
|
||||
base.HandleNewState(state);
|
||||
if (!(input is ReplayState<T> replayState))
|
||||
{
|
||||
base.HandleCustomInput(state, input);
|
||||
return;
|
||||
}
|
||||
|
||||
var replayState = state as ReplayInputHandler.ReplayState<T>;
|
||||
|
||||
if (replayState == null) return;
|
||||
if (state is RulesetInputManagerInputState<T> inputState)
|
||||
{
|
||||
inputState.LastReplayState = replayState;
|
||||
}
|
||||
|
||||
// Here we handle states specifically coming from a replay source.
|
||||
// These have extra action information rather than keyboard keys or mouse buttons.
|
||||
@ -80,7 +98,7 @@ namespace osu.Game.Rulesets.UI
|
||||
if (replayInputHandler != null) RemoveHandler(replayInputHandler);
|
||||
|
||||
replayInputHandler = value;
|
||||
UseParentState = replayInputHandler == null;
|
||||
UseParentInput = replayInputHandler == null;
|
||||
|
||||
if (replayInputHandler != null)
|
||||
AddHandler(replayInputHandler);
|
||||
@ -123,7 +141,7 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState;
|
||||
|
||||
private bool isAttached => replayInputHandler != null && !UseParentState;
|
||||
private bool isAttached => replayInputHandler != null && !UseParentInput;
|
||||
|
||||
private const int max_catch_up_updates_per_frame = 50;
|
||||
|
||||
@ -267,4 +285,10 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
void Attach(KeyCounterCollection keyCounter);
|
||||
}
|
||||
|
||||
public class RulesetInputManagerInputState<T> : InputState
|
||||
where T : struct
|
||||
{
|
||||
public ReplayState<T> LastReplayState;
|
||||
}
|
||||
}
|
||||
|
@ -185,9 +185,9 @@ namespace osu.Game.Screens.Edit
|
||||
protected override bool OnScroll(InputState state)
|
||||
{
|
||||
if (state.Mouse.ScrollDelta.X + state.Mouse.ScrollDelta.Y > 0)
|
||||
clock.SeekBackward(true);
|
||||
clock.SeekBackward(!clock.IsRunning);
|
||||
else
|
||||
clock.SeekForward(true);
|
||||
clock.SeekForward(!clock.IsRunning);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -182,14 +183,14 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
if (!pendingAnimation && state.Mouse.Buttons.Count == 1)
|
||||
if (!pendingAnimation && state.Mouse.Buttons.Count() == 1)
|
||||
BeginConfirm();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
if (state.Mouse.Buttons.Count == 0)
|
||||
if (!state.Mouse.Buttons.Any())
|
||||
AbortConfirm();
|
||||
return true;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
|
||||
namespace osu.Game.Tests
|
||||
@ -13,7 +14,11 @@ namespace osu.Game.Tests
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
LoadComponentAsync(new BackgroundScreenDefault { Depth = 10 }, AddInternal);
|
||||
LoadComponentAsync(new BackgroundScreenDefault
|
||||
{
|
||||
Colour = OsuColour.Gray(0.5f),
|
||||
Depth = 10
|
||||
}, AddInternal);
|
||||
|
||||
// Have to construct this here, rather than in the constructor, because
|
||||
// we depend on some dependencies to be loaded within OsuGameBase.load().
|
||||
|
@ -7,7 +7,7 @@ namespace osu.Game.Tests.Platform
|
||||
{
|
||||
public class TestStorage : DesktopStorage
|
||||
{
|
||||
public TestStorage(string baseName) : base(baseName)
|
||||
public TestStorage(string baseName) : base(baseName, null)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual
|
||||
/// </summary>
|
||||
protected void ReturnUserInput()
|
||||
{
|
||||
AddStep("Return user input", () => InputManager.UseParentState = true);
|
||||
AddStep("Return user input", () => InputManager.UseParentInput = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2018.619.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2018.622.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.18.1" />
|
||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user