mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 13:37:25 +08:00
Merge branch 'master'
Conflicts: osu-framework osu.Game/Screens/Play/Player.cs
This commit is contained in:
commit
df65443f07
2
LICENCE
2
LICENCE
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 8f36ddab946ff538620081ede7719461d4732b79
|
||||
Subproject commit 2610a3133721b0bc4af852342aa2a179d0e66497
|
@ -1 +1 @@
|
||||
Subproject commit 7724abdf1d7c9705ba2e3989a9c604e17ccdc871
|
||||
Subproject commit 266965f0d795b94a126e2da302bd2c10eadd642a
|
@ -12,12 +12,12 @@
|
||||
<description>click the circles. to the beat.</description>
|
||||
<summary>click the circles.</summary>
|
||||
<releaseNotes>testing</releaseNotes>
|
||||
<copyright>Copyright ppy Pty Ltd 2007-2017</copyright>
|
||||
<copyright>Copyright ppy Pty Ltd 2007-2018</copyright>
|
||||
<language>en-AU</language>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="*.exe" target="lib\net45\" exclude="**vshost**"/>
|
||||
<file src="*.dll" target="lib\net45\"/>
|
||||
<file src="*.dll" target="lib\net45\"/>
|
||||
<file src="*.config" target="lib\net45\"/>
|
||||
<file src="x86\*.dll" target="lib\net45\x86\"/>
|
||||
<file src="x64\*.dll" target="lib\net45\x64\"/>
|
||||
|
@ -23,6 +23,5 @@ namespace osu.Game.Rulesets.Catch
|
||||
MoveRight,
|
||||
[Description("Engage dash")]
|
||||
Dash,
|
||||
PositionUpdate
|
||||
}
|
||||
}
|
||||
|
@ -21,9 +21,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
/// </summary>
|
||||
private const float base_scoring_distance = 100;
|
||||
|
||||
public readonly SliderCurve Curve = new SliderCurve();
|
||||
|
||||
public int RepeatCount { get; set; } = 1;
|
||||
public int RepeatCount { get; set; }
|
||||
|
||||
public double Velocity;
|
||||
public double TickDistance;
|
||||
@ -55,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
var length = Curve.Distance;
|
||||
var tickDistance = Math.Min(TickDistance, length);
|
||||
var repeatDuration = length / Velocity;
|
||||
var spanDuration = length / Velocity;
|
||||
|
||||
var minDistanceFromEnd = Velocity * 0.01;
|
||||
|
||||
@ -67,10 +65,10 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
X = X
|
||||
});
|
||||
|
||||
for (var repeat = 0; repeat < RepeatCount; repeat++)
|
||||
for (var span = 0; span < this.SpanCount(); span++)
|
||||
{
|
||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||
var reversed = repeat % 2 == 1;
|
||||
var spanStartTime = StartTime + span * spanDuration;
|
||||
var reversed = span % 2 == 1;
|
||||
|
||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||
{
|
||||
@ -80,7 +78,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
var timeProgress = d / length;
|
||||
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
|
||||
|
||||
var lastTickTime = repeatStartTime + timeProgress * repeatDuration;
|
||||
var lastTickTime = spanStartTime + timeProgress * spanDuration;
|
||||
AddNested(new Droplet
|
||||
{
|
||||
StartTime = lastTickTime,
|
||||
@ -95,17 +93,17 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
});
|
||||
}
|
||||
|
||||
double tinyTickInterval = tickDistance / length * repeatDuration;
|
||||
double tinyTickInterval = tickDistance / length * spanDuration;
|
||||
while (tinyTickInterval > 100)
|
||||
tinyTickInterval /= 2;
|
||||
|
||||
for (double t = 0; t < repeatDuration; t += tinyTickInterval)
|
||||
for (double t = 0; t < spanDuration; t += tinyTickInterval)
|
||||
{
|
||||
double progress = reversed ? 1 - t / repeatDuration : t / repeatDuration;
|
||||
double progress = reversed ? 1 - t / spanDuration : t / spanDuration;
|
||||
|
||||
AddNested(new TinyDroplet
|
||||
{
|
||||
StartTime = repeatStartTime + t,
|
||||
StartTime = spanStartTime + t,
|
||||
ComboColour = ComboColour,
|
||||
X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
@ -121,15 +119,15 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
Samples = Samples,
|
||||
ComboColour = ComboColour,
|
||||
StartTime = repeatStartTime + repeatDuration,
|
||||
StartTime = spanStartTime + spanDuration,
|
||||
X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity;
|
||||
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
||||
|
||||
public float EndX => Curve.PositionAt(ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH;
|
||||
public float EndX => Curve.PositionAt(this.ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH;
|
||||
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
@ -139,6 +137,8 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
set { Curve.Distance = value; }
|
||||
}
|
||||
|
||||
public SliderCurve Curve { get; } = new SliderCurve();
|
||||
|
||||
public List<Vector2> ControlPoints
|
||||
{
|
||||
get { return Curve.ControlPoints; }
|
||||
@ -152,17 +152,5 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
get { return Curve.CurveType; }
|
||||
set { Curve.CurveType = value; }
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
// 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.Linq;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Users;
|
||||
|
||||
@ -23,15 +26,78 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
|
||||
public override Replay Generate()
|
||||
{
|
||||
// todo: add support for HT DT
|
||||
const double dash_speed = CatcherArea.Catcher.BASE_SPEED;
|
||||
const double movement_speed = dash_speed / 2;
|
||||
float lastPosition = 0.5f;
|
||||
double lastTime = 0;
|
||||
|
||||
// Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
|
||||
Replay.Frames.Add(new CatchReplayFrame(-100000, 0));
|
||||
Replay.Frames.Add(new CatchReplayFrame(-100000, lastPosition));
|
||||
|
||||
void moveToNext(CatchHitObject h)
|
||||
{
|
||||
float positionChange = Math.Abs(lastPosition - h.X);
|
||||
double timeAvailable = h.StartTime - lastTime;
|
||||
|
||||
//So we can either make it there without a dash or not.
|
||||
double speedRequired = positionChange / timeAvailable;
|
||||
|
||||
bool dashRequired = speedRequired > movement_speed && h.StartTime != 0;
|
||||
|
||||
// todo: get correct catcher size, based on difficulty CS.
|
||||
const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f;
|
||||
|
||||
if (lastPosition - catcher_width_half < h.X && lastPosition + catcher_width_half > h.X)
|
||||
{
|
||||
//we are already in the correct range.
|
||||
lastTime = h.StartTime;
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, lastPosition));
|
||||
return;
|
||||
}
|
||||
|
||||
if (h is BananaShower.Banana)
|
||||
{
|
||||
// auto bananas unrealistically warp to catch 100% combo.
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
}
|
||||
else if (h.HyperDash)
|
||||
{
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition, ReplayButtonState.Right1));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
}
|
||||
else if (dashRequired)
|
||||
{
|
||||
//we do a movement in two parts - the dash part then the normal part...
|
||||
double timeAtNormalSpeed = positionChange / movement_speed;
|
||||
double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable;
|
||||
double timeAtDashSpeed = timeWeNeedToSave / 2;
|
||||
|
||||
float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable);
|
||||
|
||||
//dash movement
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, ReplayButtonState.Left1));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
}
|
||||
else
|
||||
{
|
||||
double timeBefore = positionChange / movement_speed;
|
||||
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition, ReplayButtonState.Right1));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
}
|
||||
|
||||
lastTime = h.StartTime;
|
||||
lastPosition = h.X;
|
||||
}
|
||||
|
||||
foreach (var obj in Beatmap.HitObjects)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case Fruit _:
|
||||
Replay.Frames.Add(new CatchReplayFrame(obj.StartTime, obj.X));
|
||||
moveToNext(obj);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -42,7 +108,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
case BananaShower.Banana _:
|
||||
case TinyDroplet _:
|
||||
case Droplet _:
|
||||
Replay.Frames.Add(new CatchReplayFrame(nestedObj.StartTime, nestedObj.X));
|
||||
moveToNext(nestedObj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -14,15 +14,29 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
{
|
||||
}
|
||||
|
||||
public override List<InputState> GetPendingStates() => new List<InputState>
|
||||
public override List<InputState> GetPendingStates()
|
||||
{
|
||||
new CatchReplayState
|
||||
if (!Position.HasValue) return new List<InputState>();
|
||||
|
||||
var action = new List<CatchAction>();
|
||||
|
||||
if (CurrentFrame.ButtonState == ReplayButtonState.Left1)
|
||||
action.Add(CatchAction.Dash);
|
||||
|
||||
if (Position.Value.X > CurrentFrame.Position.X)
|
||||
action.Add(CatchAction.MoveRight);
|
||||
else if (Position.Value.X < CurrentFrame.Position.X)
|
||||
action.Add(CatchAction.MoveLeft);
|
||||
|
||||
return new List<InputState>
|
||||
{
|
||||
PressedActions = new List<CatchAction> { CatchAction.PositionUpdate },
|
||||
CatcherX = ((CatchReplayFrame)CurrentFrame).MouseX
|
||||
},
|
||||
new CatchReplayState { PressedActions = new List<CatchAction>() },
|
||||
};
|
||||
new CatchReplayState
|
||||
{
|
||||
PressedActions = action,
|
||||
CatcherX = Position.Value.X
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public class CatchReplayState : ReplayState<CatchAction>
|
||||
{
|
||||
|
@ -9,8 +9,8 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
{
|
||||
public override bool IsImportant => MouseX > 0;
|
||||
|
||||
public CatchReplayFrame(double time, float? x = null)
|
||||
: base(time, x ?? -1, null, ReplayButtonState.None)
|
||||
public CatchReplayFrame(double time, float? x = null, ReplayButtonState button = ReplayButtonState.None)
|
||||
: base(time, x ?? -1, null, button)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class CatcherArea : Container, IKeyBindingHandler<CatchAction>
|
||||
public class CatcherArea : Container
|
||||
{
|
||||
public const float CATCHER_SIZE = 172;
|
||||
|
||||
@ -84,16 +84,14 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(CatchAction action)
|
||||
protected override void Update()
|
||||
{
|
||||
if (action != CatchAction.PositionUpdate) return false;
|
||||
base.Update();
|
||||
|
||||
CatchFramedReplayInputHandler.CatchReplayState state = (CatchFramedReplayInputHandler.CatchReplayState)GetContainingInputManager().CurrentState;
|
||||
var state = GetContainingInputManager().CurrentState as CatchFramedReplayInputHandler.CatchReplayState;
|
||||
|
||||
if (state.CatcherX.HasValue)
|
||||
if (state?.CatcherX != null)
|
||||
MovableCatcher.X = state.CatcherX.Value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OnReleased(CatchAction action) => false;
|
||||
|
@ -32,6 +32,10 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="JetBrains.Annotations, Version=11.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
</packages>
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
/// <summary>
|
||||
/// The definitions for each stage in a <see cref="ManiaPlayfield"/>.
|
||||
/// </summary>
|
||||
public readonly List<StageDefinition> Stages = new List<StageDefinition>();
|
||||
public List<StageDefinition> Stages = new List<StageDefinition>();
|
||||
|
||||
/// <summary>
|
||||
/// Total number of columns represented by all stages in this <see cref="ManiaBeatmap"/>.
|
||||
@ -24,10 +24,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ManiaBeatmap"/>.
|
||||
/// </summary>
|
||||
/// <param name="initialStage">The initial stage.</param>
|
||||
public ManiaBeatmap(StageDefinition initialStage)
|
||||
/// <param name="defaultStage">The initial stages.</param>
|
||||
public ManiaBeatmap(StageDefinition defaultStage)
|
||||
{
|
||||
Stages.Add(initialStage);
|
||||
Stages.Add(defaultStage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
if (curveData == null)
|
||||
return HitObject.Samples;
|
||||
|
||||
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.RepeatCount;
|
||||
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount();
|
||||
|
||||
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
||||
return curveData.RepeatSamples[index];
|
||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
private readonly double endTime;
|
||||
private readonly double segmentDuration;
|
||||
private readonly int repeatCount;
|
||||
private readonly int spanCount;
|
||||
|
||||
private PatternType convertType;
|
||||
|
||||
@ -39,25 +39,25 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
var distanceData = hitObject as IHasDistance;
|
||||
var repeatsData = hitObject as IHasRepeats;
|
||||
|
||||
repeatCount = repeatsData?.RepeatCount ?? 1;
|
||||
spanCount = repeatsData?.SpanCount() ?? 1;
|
||||
|
||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
||||
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(hitObject.StartTime);
|
||||
|
||||
// The true distance, accounting for any repeats
|
||||
double distance = (distanceData?.Distance ?? 0) * repeatCount;
|
||||
double distance = (distanceData?.Distance ?? 0) * spanCount;
|
||||
// The velocity of the osu! hit object - calculated as the velocity of a slider
|
||||
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength;
|
||||
// The duration of the osu! hit object
|
||||
double osuDuration = distance / osuVelocity;
|
||||
|
||||
endTime = hitObject.StartTime + osuDuration;
|
||||
segmentDuration = (endTime - HitObject.StartTime) / repeatCount;
|
||||
segmentDuration = (endTime - HitObject.StartTime) / spanCount;
|
||||
}
|
||||
|
||||
public override Pattern Generate()
|
||||
{
|
||||
if (repeatCount > 1)
|
||||
if (spanCount > 1)
|
||||
{
|
||||
if (segmentDuration <= 90)
|
||||
return generateRandomHoldNotes(HitObject.StartTime, 1);
|
||||
@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
if (segmentDuration <= 120)
|
||||
{
|
||||
convertType |= PatternType.ForceNotStack;
|
||||
return generateRandomNotes(HitObject.StartTime, repeatCount + 1);
|
||||
return generateRandomNotes(HitObject.StartTime, spanCount + 1);
|
||||
}
|
||||
|
||||
if (segmentDuration <= 160)
|
||||
@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
if (duration >= 4000)
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0);
|
||||
|
||||
if (segmentDuration > 400 && repeatCount < TotalColumns - 1 - RandomStart)
|
||||
if (segmentDuration > 400 && spanCount < TotalColumns - 1 - RandomStart)
|
||||
return generateTiledHoldNotes(HitObject.StartTime);
|
||||
|
||||
return generateHoldAndNormalNotes(HitObject.StartTime);
|
||||
@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
int column = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||
bool increasing = Random.NextDouble() > 0.5;
|
||||
|
||||
for (int i = 0; i <= repeatCount; i++)
|
||||
for (int i = 0; i <= spanCount; i++)
|
||||
{
|
||||
addToPattern(pattern, column, startTime, startTime);
|
||||
startTime += segmentDuration;
|
||||
@ -262,7 +262,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
int interval = Random.Next(1, TotalColumns - (legacy ? 1 : 0));
|
||||
|
||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||
for (int i = 0; i <= repeatCount; i++)
|
||||
for (int i = 0; i <= spanCount; i++)
|
||||
{
|
||||
addToPattern(pattern, nextColumn, startTime, startTime);
|
||||
|
||||
@ -350,7 +350,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
var pattern = new Pattern();
|
||||
|
||||
int columnRepeat = Math.Min(repeatCount, TotalColumns);
|
||||
int columnRepeat = Math.Min(spanCount, TotalColumns);
|
||||
|
||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||
@ -409,7 +409,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
int nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
|
||||
var rowPattern = new Pattern();
|
||||
for (int i = 0; i <= repeatCount; i++)
|
||||
for (int i = 0; i <= spanCount; i++)
|
||||
{
|
||||
if (!(ignoreHead && startTime == HitObject.StartTime))
|
||||
{
|
||||
@ -442,7 +442,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
if (curveData == null)
|
||||
return HitObject.Samples;
|
||||
|
||||
double segmentTime = (endTime - HitObject.StartTime) / repeatCount;
|
||||
double segmentTime = (endTime - HitObject.StartTime) / spanCount;
|
||||
|
||||
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
||||
return curveData.RepeatSamples[index];
|
||||
|
34
osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs
Normal file
34
osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs
Normal file
@ -0,0 +1,34 @@
|
||||
// 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.Configuration.Tracking;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Configuration;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Configuration
|
||||
{
|
||||
public class ManiaConfigManager : RulesetConfigManager<ManiaSetting>
|
||||
{
|
||||
public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant)
|
||||
: base(settings, ruleset, variant)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void InitialiseDefaults()
|
||||
{
|
||||
base.InitialiseDefaults();
|
||||
|
||||
Set(ManiaSetting.ScrollTime, 1500.0, 50.0, 10000.0, 50.0);
|
||||
}
|
||||
|
||||
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
||||
{
|
||||
new TrackedSetting<double>(ManiaSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
|
||||
};
|
||||
}
|
||||
|
||||
public enum ManiaSetting
|
||||
{
|
||||
ScrollTime
|
||||
}
|
||||
}
|
@ -17,8 +17,13 @@ namespace osu.Game.Rulesets.Mania
|
||||
|
||||
public enum ManiaAction
|
||||
{
|
||||
[Description("Special")]
|
||||
Special,
|
||||
[Description("Special 1")]
|
||||
Special1 = 1,
|
||||
[Description("Special 2")]
|
||||
Special2,
|
||||
|
||||
// This offsets the start value of normal keys in-case we add more special keys
|
||||
// above at a later time, without breaking replays/configs.
|
||||
[Description("Key 1")]
|
||||
Key1 = 10,
|
||||
[Description("Key 2")]
|
||||
@ -36,6 +41,24 @@ namespace osu.Game.Rulesets.Mania
|
||||
[Description("Key 8")]
|
||||
Key8,
|
||||
[Description("Key 9")]
|
||||
Key9
|
||||
Key9,
|
||||
[Description("Key 10")]
|
||||
Key10,
|
||||
[Description("Key 11")]
|
||||
Key11,
|
||||
[Description("Key 12")]
|
||||
Key12,
|
||||
[Description("Key 13")]
|
||||
Key13,
|
||||
[Description("Key 14")]
|
||||
Key14,
|
||||
[Description("Key 15")]
|
||||
Key15,
|
||||
[Description("Key 16")]
|
||||
Key16,
|
||||
[Description("Key 17")]
|
||||
Key17,
|
||||
[Description("Key 18")]
|
||||
Key18,
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
// 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.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Graphics;
|
||||
@ -86,7 +88,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
},
|
||||
},
|
||||
new ManiaModRandom(),
|
||||
new ManiaModKeyCoop(),
|
||||
new ManiaModDualStages(),
|
||||
new MultiMod
|
||||
{
|
||||
Mods = new Mod[]
|
||||
@ -117,42 +119,189 @@ namespace osu.Game.Rulesets.Mania
|
||||
{
|
||||
}
|
||||
|
||||
public override IEnumerable<int> AvailableVariants => new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
public override IEnumerable<int> AvailableVariants
|
||||
{
|
||||
get
|
||||
{
|
||||
for (int i = 1; i <= 9; i++)
|
||||
yield return (int)PlayfieldType.Single + i;
|
||||
for (int i = 2; i <= 18; i += 2)
|
||||
yield return (int)PlayfieldType.Dual + i;
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0)
|
||||
{
|
||||
var leftKeys = new[]
|
||||
switch (getPlayfieldType(variant))
|
||||
{
|
||||
InputKey.A,
|
||||
InputKey.S,
|
||||
InputKey.D,
|
||||
InputKey.F
|
||||
};
|
||||
case PlayfieldType.Single:
|
||||
return new VariantMappingGenerator
|
||||
{
|
||||
LeftKeys = new[]
|
||||
{
|
||||
InputKey.A,
|
||||
InputKey.S,
|
||||
InputKey.D,
|
||||
InputKey.F
|
||||
},
|
||||
RightKeys = new[]
|
||||
{
|
||||
InputKey.J,
|
||||
InputKey.K,
|
||||
InputKey.L,
|
||||
InputKey.Semicolon
|
||||
},
|
||||
SpecialKey = InputKey.Space,
|
||||
SpecialAction = ManiaAction.Special1,
|
||||
NormalActionStart = ManiaAction.Key1,
|
||||
}.GenerateKeyBindingsFor(variant, out _);
|
||||
case PlayfieldType.Dual:
|
||||
int keys = getDualStageKeyCount(variant);
|
||||
|
||||
var rightKeys = new[]
|
||||
{
|
||||
InputKey.J,
|
||||
InputKey.K,
|
||||
InputKey.L,
|
||||
InputKey.Semicolon
|
||||
};
|
||||
var stage1Bindings = new VariantMappingGenerator
|
||||
{
|
||||
LeftKeys = new[]
|
||||
{
|
||||
InputKey.Number1,
|
||||
InputKey.Number2,
|
||||
InputKey.Number3,
|
||||
InputKey.Number4,
|
||||
},
|
||||
RightKeys = new[]
|
||||
{
|
||||
InputKey.Z,
|
||||
InputKey.X,
|
||||
InputKey.C,
|
||||
InputKey.V
|
||||
},
|
||||
SpecialKey = InputKey.Tilde,
|
||||
SpecialAction = ManiaAction.Special1,
|
||||
NormalActionStart = ManiaAction.Key1
|
||||
}.GenerateKeyBindingsFor(keys, out var nextNormal);
|
||||
|
||||
ManiaAction currentKey = ManiaAction.Key1;
|
||||
var stage2Bindings = new VariantMappingGenerator
|
||||
{
|
||||
LeftKeys = new[]
|
||||
{
|
||||
InputKey.Number7,
|
||||
InputKey.Number8,
|
||||
InputKey.Number9,
|
||||
InputKey.Number0
|
||||
},
|
||||
RightKeys = new[]
|
||||
{
|
||||
InputKey.O,
|
||||
InputKey.P,
|
||||
InputKey.BracketLeft,
|
||||
InputKey.BracketRight
|
||||
},
|
||||
SpecialKey = InputKey.BackSlash,
|
||||
SpecialAction = ManiaAction.Special2,
|
||||
NormalActionStart = nextNormal
|
||||
}.GenerateKeyBindingsFor(keys, out _);
|
||||
|
||||
var bindings = new List<KeyBinding>();
|
||||
return stage1Bindings.Concat(stage2Bindings);
|
||||
}
|
||||
|
||||
for (int i = leftKeys.Length - variant / 2; i < leftKeys.Length; i++)
|
||||
bindings.Add(new KeyBinding(leftKeys[i], currentKey++));
|
||||
|
||||
for (int i = 0; i < variant / 2; i++)
|
||||
bindings.Add(new KeyBinding(rightKeys[i], currentKey++));
|
||||
|
||||
if (variant % 2 == 1)
|
||||
bindings.Add(new KeyBinding(InputKey.Space, ManiaAction.Special));
|
||||
|
||||
return bindings;
|
||||
return new KeyBinding[0];
|
||||
}
|
||||
|
||||
public override string GetVariantName(int variant) => $"{variant}K";
|
||||
public override string GetVariantName(int variant)
|
||||
{
|
||||
switch (getPlayfieldType(variant))
|
||||
{
|
||||
default:
|
||||
return $"{variant}K";
|
||||
case PlayfieldType.Dual:
|
||||
{
|
||||
var keys = getDualStageKeyCount(variant);
|
||||
return $"{keys}K + {keys}K";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the number of keys for each stage in a <see cref="PlayfieldType.Dual"/> variant.
|
||||
/// </summary>
|
||||
/// <param name="variant">The variant.</param>
|
||||
private int getDualStageKeyCount(int variant) => (variant - (int)PlayfieldType.Dual) / 2;
|
||||
|
||||
/// <summary>
|
||||
/// Finds the <see cref="PlayfieldType"/> that corresponds to a variant value.
|
||||
/// </summary>
|
||||
/// <param name="variant">The variant value.</param>
|
||||
/// <returns>The <see cref="PlayfieldType"/> that corresponds to <paramref name="variant"/>.</returns>
|
||||
private PlayfieldType getPlayfieldType(int variant)
|
||||
{
|
||||
return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast<int>().OrderByDescending(i => i).First(v => variant >= v);
|
||||
}
|
||||
|
||||
private class VariantMappingGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// All the <see cref="InputKey"/>s available to the left hand.
|
||||
/// </summary>
|
||||
public InputKey[] LeftKeys;
|
||||
|
||||
/// <summary>
|
||||
/// All the <see cref="InputKey"/>s available to the right hand.
|
||||
/// </summary>
|
||||
public InputKey[] RightKeys;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="InputKey"/> for the special key.
|
||||
/// </summary>
|
||||
public InputKey SpecialKey;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ManiaAction"/> at which the normal columns should begin.
|
||||
/// </summary>
|
||||
public ManiaAction NormalActionStart;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ManiaAction"/> for the special column.
|
||||
/// </summary>
|
||||
public ManiaAction SpecialAction;
|
||||
|
||||
/// <summary>
|
||||
/// Generates a list of <see cref="KeyBinding"/>s for a specific number of columns.
|
||||
/// </summary>
|
||||
/// <param name="columns">The number of columns that need to be bound.</param>
|
||||
/// <param name="nextNormalAction">The next <see cref="ManiaAction"/> to use for normal columns.</param>
|
||||
/// <returns>The keybindings.</returns>
|
||||
public IEnumerable<KeyBinding> GenerateKeyBindingsFor(int columns, out ManiaAction nextNormalAction)
|
||||
{
|
||||
ManiaAction currentNormalAction = NormalActionStart;
|
||||
|
||||
var bindings = new List<KeyBinding>();
|
||||
|
||||
for (int i = LeftKeys.Length - columns / 2; i < LeftKeys.Length; i++)
|
||||
bindings.Add(new KeyBinding(LeftKeys[i], currentNormalAction++));
|
||||
|
||||
for (int i = 0; i < columns / 2; i++)
|
||||
bindings.Add(new KeyBinding(RightKeys[i], currentNormalAction++));
|
||||
|
||||
if (columns % 2 == 1)
|
||||
bindings.Add(new KeyBinding(SpecialKey, SpecialAction));
|
||||
|
||||
nextNormalAction = currentNormalAction;
|
||||
return bindings;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum PlayfieldType
|
||||
{
|
||||
/// <summary>
|
||||
/// Columns are grouped into a single stage.
|
||||
/// Number of columns in this stage lies at (item - Single).
|
||||
/// </summary>
|
||||
Single = 0,
|
||||
/// <summary>
|
||||
/// Columns are grouped into two stages.
|
||||
/// Overall number of columns lies at (item - Dual), further computation is required for
|
||||
/// number of columns in each individual stage.
|
||||
/// </summary>
|
||||
Dual = 1000,
|
||||
}
|
||||
}
|
||||
|
15
osu.Game.Rulesets.Mania/Mods/IPlayfieldTypeMod.cs
Normal file
15
osu.Game.Rulesets.Mania/Mods/IPlayfieldTypeMod.cs
Normal file
@ -0,0 +1,15 @@
|
||||
// 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.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public interface IPlayfieldTypeMod : IApplicableMod
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="PlayfieldType"/> which this <see cref="IPlayfieldTypeMod"/> requires.
|
||||
/// </summary>
|
||||
PlayfieldType PlayfieldType { get; }
|
||||
}
|
||||
}
|
53
osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
Normal file
53
osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
Normal file
@ -0,0 +1,53 @@
|
||||
// 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.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter<ManiaHitObject>, IApplicableToRulesetContainer<ManiaHitObject>
|
||||
{
|
||||
public override string Name => "Dual Stages";
|
||||
public override string ShortenedName => "DS";
|
||||
public override string Description => @"Double the stages, double the fun!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool Ranked => false;
|
||||
|
||||
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter)
|
||||
{
|
||||
var mbc = (ManiaBeatmapConverter)beatmapConverter;
|
||||
|
||||
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
|
||||
if (mbc.IsForCurrentRuleset)
|
||||
return;
|
||||
|
||||
mbc.TargetColumns *= 2;
|
||||
}
|
||||
|
||||
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
|
||||
{
|
||||
var mrc = (ManiaRulesetContainer)rulesetContainer;
|
||||
|
||||
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
|
||||
if (mrc.IsForCurrentRuleset)
|
||||
return;
|
||||
|
||||
var newDefinitions = new List<StageDefinition>();
|
||||
foreach (var existing in mrc.Beatmap.Stages)
|
||||
{
|
||||
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
|
||||
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
|
||||
}
|
||||
|
||||
mrc.Beatmap.Stages = newDefinitions;
|
||||
}
|
||||
|
||||
public PlayfieldType PlayfieldType => PlayfieldType.Dual;
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
// 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.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModKeyCoop : Mod
|
||||
{
|
||||
public override string Name => "KeyCoop";
|
||||
public override string ShortenedName => "2P";
|
||||
public override string Description => @"Double the key amount, double the fun!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool Ranked => true;
|
||||
}
|
||||
}
|
@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
RelativeChildSize = new Vector2(1, 10000),
|
||||
Children = new[]
|
||||
{
|
||||
new DrawableHoldNote(new HoldNote { Duration = 1 }, ManiaAction.Key1)
|
||||
new DrawableHoldNote(new HoldNote { Duration = 1000 } , ManiaAction.Key1)
|
||||
{
|
||||
Y = 5000,
|
||||
Height = 1000,
|
||||
|
@ -2,11 +2,13 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
@ -31,15 +33,43 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
var rng = new Random(1337);
|
||||
|
||||
AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal));
|
||||
AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal));
|
||||
AddStep("Left special style", () => createPlayfield(4, SpecialColumnPosition.Left));
|
||||
AddStep("Right special style", () => createPlayfield(4, SpecialColumnPosition.Right));
|
||||
AddStep("5 columns", () => createPlayfield(5, SpecialColumnPosition.Normal));
|
||||
AddStep("8 columns", () => createPlayfield(8, SpecialColumnPosition.Normal));
|
||||
AddStep("Left special style", () => createPlayfield(8, SpecialColumnPosition.Left));
|
||||
AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right));
|
||||
AddStep("Reversed", () => createPlayfield(4, SpecialColumnPosition.Normal, true));
|
||||
AddStep("1 column", () => createPlayfield(1));
|
||||
AddStep("4 columns", () => createPlayfield(4));
|
||||
AddStep("5 columns", () => createPlayfield(5));
|
||||
AddStep("8 columns", () => createPlayfield(8));
|
||||
AddStep("4 + 4 columns", () =>
|
||||
{
|
||||
var stages = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = 4 },
|
||||
new StageDefinition { Columns = 4 },
|
||||
};
|
||||
createPlayfield(stages);
|
||||
});
|
||||
|
||||
AddStep("2 + 4 + 2 columns", () =>
|
||||
{
|
||||
var stages = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = 2 },
|
||||
new StageDefinition { Columns = 4 },
|
||||
new StageDefinition { Columns = 2 },
|
||||
};
|
||||
createPlayfield(stages);
|
||||
});
|
||||
|
||||
AddStep("1 + 8 + 1 columns", () =>
|
||||
{
|
||||
var stages = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = 1 },
|
||||
new StageDefinition { Columns = 8 },
|
||||
new StageDefinition { Columns = 1 },
|
||||
};
|
||||
createPlayfield(stages);
|
||||
});
|
||||
|
||||
AddStep("Reversed", () => createPlayfield(4, true));
|
||||
|
||||
AddStep("Notes with input", () => createPlayfieldWithNotes());
|
||||
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(true));
|
||||
@ -48,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
AddStep("Hit explosion", () =>
|
||||
{
|
||||
var playfield = createPlayfield(4, SpecialColumnPosition.Normal);
|
||||
var playfield = createPlayfield(4);
|
||||
|
||||
int col = rng.Next(0, 4);
|
||||
|
||||
@ -58,6 +88,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
};
|
||||
|
||||
playfield.OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
|
||||
playfield.Columns[col].OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
|
||||
});
|
||||
}
|
||||
|
||||
@ -67,19 +98,29 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
maniaRuleset = rulesets.GetRuleset(3);
|
||||
}
|
||||
|
||||
private ManiaPlayfield createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false)
|
||||
private ManiaPlayfield createPlayfield(int cols, bool inverted = false)
|
||||
{
|
||||
var stages = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = cols },
|
||||
};
|
||||
|
||||
return createPlayfield(stages, inverted);
|
||||
}
|
||||
|
||||
private ManiaPlayfield createPlayfield(List<StageDefinition> stages, bool inverted = false)
|
||||
{
|
||||
Clear();
|
||||
|
||||
var inputManager = new ManiaInputManager(maniaRuleset, cols) { RelativeSizeAxes = Axes.Both };
|
||||
var inputManager = new ManiaInputManager(maniaRuleset, stages.Sum(g => g.Columns)) { RelativeSizeAxes = Axes.Both };
|
||||
Add(inputManager);
|
||||
|
||||
ManiaPlayfield playfield;
|
||||
inputManager.Add(playfield = new ManiaPlayfield(cols)
|
||||
|
||||
inputManager.Add(playfield = new ManiaPlayfield(stages)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
SpecialColumnPosition = specialPos
|
||||
});
|
||||
|
||||
playfield.Inverted.Value = inverted;
|
||||
@ -97,7 +138,12 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
Add(inputManager);
|
||||
|
||||
ManiaPlayfield playfield;
|
||||
inputManager.Add(playfield = new ManiaPlayfield(4)
|
||||
var stages = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = 4 },
|
||||
};
|
||||
|
||||
inputManager.Add(playfield = new ManiaPlayfield(stages)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -11,13 +11,14 @@ using osu.Framework.Graphics.Colour;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
public class Column : ScrollingPlayfield, IHasAccentColour
|
||||
public class Column : ScrollingPlayfield, IKeyBindingHandler<ManiaAction>, IHasAccentColour
|
||||
{
|
||||
private const float key_icon_size = 10;
|
||||
private const float key_icon_corner_radius = 3;
|
||||
@ -47,6 +48,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
public Column()
|
||||
: base(ScrollingDirection.Up)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
Width = column_width;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
@ -61,7 +63,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
Name = "Hit target + hit objects",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = ManiaPlayfield.HIT_TARGET_POSITION },
|
||||
Padding = new MarginPadding { Top = ManiaStage.HIT_TARGET_POSITION },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
@ -115,7 +117,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
Name = "Key",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = ManiaPlayfield.HIT_TARGET_POSITION,
|
||||
Height = ManiaStage.HIT_TARGET_POSITION,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
@ -205,12 +207,12 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
hitObject.Depth = (float)hitObject.HitObject.StartTime;
|
||||
hitObject.AccentColour = AccentColour;
|
||||
hitObject.OnJudgement += onJudgement;
|
||||
hitObject.OnJudgement += OnJudgement;
|
||||
|
||||
HitObjects.Add(hitObject);
|
||||
}
|
||||
|
||||
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||
{
|
||||
if (!judgement.IsHit)
|
||||
return;
|
||||
@ -258,5 +260,26 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
public bool OnPressed(ManiaAction action) => Pressed?.Invoke(action) ?? false;
|
||||
public bool OnReleased(ManiaAction action) => Released?.Invoke(action) ?? false;
|
||||
}
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
{
|
||||
// Play the sounds of the next hitobject
|
||||
if (HitObjects.AliveObjects.Any())
|
||||
{
|
||||
// If there are alive hitobjects, we can abuse the fact that AliveObjects are sorted by time (see: Add())
|
||||
HitObjects.AliveObjects.First().PlaySamples();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not, we do a slow search - we might want to do a BinarySearch here if this becomes problematic
|
||||
// We fallback to LastOrDefault() if we're beyond the last note in the map
|
||||
var hitObject = HitObjects.Objects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current) ?? HitObjects.Objects.LastOrDefault();
|
||||
hitObject?.PlaySamples();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(ManiaAction action) => false;
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
internal class DrawableManiaJudgement : DrawableJudgement
|
||||
{
|
||||
public DrawableManiaJudgement(Judgement judgement)
|
||||
: base(judgement)
|
||||
: base(judgement)
|
||||
{
|
||||
JudgementText.TextSize = 25;
|
||||
}
|
||||
|
@ -3,237 +3,92 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using System;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
public class ManiaPlayfield : ScrollingPlayfield
|
||||
{
|
||||
public const float HIT_TARGET_POSITION = 50;
|
||||
|
||||
private SpecialColumnPosition specialColumnPosition;
|
||||
/// <summary>
|
||||
/// The style to use for the special column.
|
||||
/// </summary>
|
||||
public SpecialColumnPosition SpecialColumnPosition
|
||||
{
|
||||
get { return specialColumnPosition; }
|
||||
set
|
||||
{
|
||||
if (IsLoaded)
|
||||
throw new InvalidOperationException($"Setting {nameof(SpecialColumnPosition)} after the playfield is loaded requires re-creating the playfield.");
|
||||
specialColumnPosition = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this playfield should be inverted. This flips everything inside the playfield.
|
||||
/// </summary>
|
||||
public readonly Bindable<bool> Inverted = new Bindable<bool>(true);
|
||||
|
||||
private readonly FlowContainer<Column> columns;
|
||||
public IEnumerable<Column> Columns => columns.Children;
|
||||
public List<Column> Columns => stages.SelectMany(x => x.Columns).ToList();
|
||||
private readonly List<ManiaStage> stages = new List<ManiaStage>();
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container<Drawable> content;
|
||||
|
||||
private List<Color4> normalColumnColours = new List<Color4>();
|
||||
private Color4 specialColumnColour;
|
||||
|
||||
private readonly Container<DrawableManiaJudgement> judgements;
|
||||
|
||||
private readonly int columnCount;
|
||||
|
||||
public ManiaPlayfield(int columnCount)
|
||||
public ManiaPlayfield(List<StageDefinition> stageDefinitions)
|
||||
: base(ScrollingDirection.Up)
|
||||
{
|
||||
this.columnCount = columnCount;
|
||||
if (stageDefinitions == null)
|
||||
throw new ArgumentNullException(nameof(stageDefinitions));
|
||||
|
||||
if (columnCount <= 0)
|
||||
throw new ArgumentException("Can't have zero or fewer columns.");
|
||||
if (stageDefinitions.Count <= 0)
|
||||
throw new ArgumentException("Can't have zero or fewer stages.");
|
||||
|
||||
Inverted.Value = true;
|
||||
|
||||
Container topLevelContainer;
|
||||
InternalChildren = new Drawable[]
|
||||
GridContainer playfieldGrid;
|
||||
InternalChild = playfieldGrid = new GridContainer
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Name = "Playfield elements",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Name = "Columns mask",
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Name = "Background",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black
|
||||
},
|
||||
columns = new FillFlowContainer<Column>
|
||||
{
|
||||
Name = "Columns",
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Padding = new MarginPadding { Left = 1, Right = 1 },
|
||||
Spacing = new Vector2(1, 0)
|
||||
},
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Name = "Barlines mask",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 1366, // Bar lines should only be masked on the vertical axis
|
||||
BypassAutoSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Child = content = new Container
|
||||
{
|
||||
Name = "Bar lines",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
|
||||
}
|
||||
},
|
||||
judgements = new Container<DrawableManiaJudgement>
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Y = HIT_TARGET_POSITION + 150,
|
||||
BypassAutoSizeAxes = Axes.Both
|
||||
},
|
||||
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
|
||||
}
|
||||
}
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[] { new Drawable[stageDefinitions.Count] }
|
||||
};
|
||||
|
||||
var currentAction = ManiaAction.Key1;
|
||||
for (int i = 0; i < columnCount; i++)
|
||||
var normalColumnAction = ManiaAction.Key1;
|
||||
var specialColumnAction = ManiaAction.Special1;
|
||||
int firstColumnIndex = 0;
|
||||
for (int i = 0; i < stageDefinitions.Count; i++)
|
||||
{
|
||||
var c = new Column();
|
||||
c.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||
var newStage = new ManiaStage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
|
||||
newStage.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||
newStage.Inverted.BindTo(Inverted);
|
||||
|
||||
c.IsSpecial = isSpecialColumn(i);
|
||||
c.Action = c.IsSpecial ? ManiaAction.Special : currentAction++;
|
||||
playfieldGrid.Content[0][i] = newStage;
|
||||
|
||||
topLevelContainer.Add(c.TopLevelContainer.CreateProxy());
|
||||
stages.Add(newStage);
|
||||
AddNested(newStage);
|
||||
|
||||
columns.Add(c);
|
||||
AddNested(c);
|
||||
firstColumnIndex += newStage.Columns.Count;
|
||||
}
|
||||
|
||||
Inverted.ValueChanged += invertedChanged;
|
||||
Inverted.TriggerChange();
|
||||
}
|
||||
|
||||
private void invertedChanged(bool newValue)
|
||||
public override void Add(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Add(h);
|
||||
|
||||
public void Add(BarLine barline) => stages.ForEach(s => s.Add(barline));
|
||||
|
||||
private ManiaStage getStageByColumn(int column)
|
||||
{
|
||||
Scale = new Vector2(1, newValue ? -1 : 1);
|
||||
judgements.Scale = Scale;
|
||||
int sum = 0;
|
||||
foreach (var stage in stages)
|
||||
{
|
||||
sum = sum + stage.Columns.Count;
|
||||
if (sum > column)
|
||||
return stage;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private void load(ManiaConfigManager maniaConfig)
|
||||
{
|
||||
normalColumnColours = new List<Color4>
|
||||
{
|
||||
colours.RedDark,
|
||||
colours.GreenDark
|
||||
};
|
||||
|
||||
specialColumnColour = colours.BlueDark;
|
||||
|
||||
// Set the special column + colour + key
|
||||
foreach (var column in Columns)
|
||||
{
|
||||
if (!column.IsSpecial)
|
||||
continue;
|
||||
|
||||
column.AccentColour = specialColumnColour;
|
||||
}
|
||||
|
||||
var nonSpecialColumns = Columns.Where(c => !c.IsSpecial).ToList();
|
||||
|
||||
// We'll set the colours of the non-special columns in a separate loop, because the non-special
|
||||
// column colours are mirrored across their centre and special styles mess with this
|
||||
for (int i = 0; i < Math.Ceiling(nonSpecialColumns.Count / 2f); i++)
|
||||
{
|
||||
Color4 colour = normalColumnColours[i % normalColumnColours.Count];
|
||||
nonSpecialColumns[i].AccentColour = colour;
|
||||
nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
|
||||
}
|
||||
maniaConfig.BindWith(ManiaSetting.ScrollTime, VisibleTimeRange);
|
||||
}
|
||||
|
||||
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||
{
|
||||
judgements.Clear();
|
||||
judgements.Add(new DrawableManiaJudgement(judgement)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the column index is a special column for this playfield.
|
||||
/// </summary>
|
||||
/// <param name="column">The 0-based column index.</param>
|
||||
/// <returns>Whether the column is a special column.</returns>
|
||||
private bool isSpecialColumn(int column)
|
||||
{
|
||||
switch (SpecialColumnPosition)
|
||||
{
|
||||
default:
|
||||
case SpecialColumnPosition.Normal:
|
||||
return columnCount % 2 == 1 && column == columnCount / 2;
|
||||
case SpecialColumnPosition.Left:
|
||||
return column == 0;
|
||||
case SpecialColumnPosition.Right:
|
||||
return column == columnCount - 1;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Add(DrawableHitObject h)
|
||||
{
|
||||
h.OnJudgement += OnJudgement;
|
||||
Columns.ElementAt(((ManiaHitObject)h.HitObject).Column).Add(h);
|
||||
}
|
||||
|
||||
public void Add(DrawableBarLine barline) => HitObjects.Add(barline);
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
// Due to masking differences, it is not possible to get the width of the columns container automatically
|
||||
// While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually
|
||||
content.Width = columns.Width;
|
||||
getStageByColumn(((ManiaHitObject)judgedObject.HitObject).Column).OnJudgement(judgedObject, judgement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
@ -11,7 +10,11 @@ using osu.Framework.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Replays;
|
||||
@ -22,6 +25,7 @@ using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
@ -29,7 +33,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap;
|
||||
|
||||
public IEnumerable<DrawableBarLine> BarLines;
|
||||
public IEnumerable<BarLine> BarLines;
|
||||
|
||||
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||
: base(ruleset, beatmap, isForCurrentRuleset)
|
||||
@ -38,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
|
||||
|
||||
var timingPoints = Beatmap.ControlPointInfo.TimingPoints;
|
||||
var barLines = new List<DrawableBarLine>();
|
||||
var barLines = new List<BarLine>();
|
||||
|
||||
for (int i = 0; i < timingPoints.Count; i++)
|
||||
{
|
||||
@ -50,12 +54,12 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
int index = 0;
|
||||
for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
|
||||
{
|
||||
barLines.Add(new DrawableBarLine(new BarLine
|
||||
barLines.Add(new BarLine
|
||||
{
|
||||
StartTime = t,
|
||||
ControlPoint = point,
|
||||
BeatIndex = index
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +72,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
BarLines.ForEach(Playfield.Add);
|
||||
}
|
||||
|
||||
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.TotalColumns)
|
||||
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -76,7 +80,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
||||
|
||||
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Beatmap.TotalColumns);
|
||||
public override int Variant => (int)(Mods.OfType<IPlayfieldTypeMod>().FirstOrDefault()?.PlayfieldType ?? PlayfieldType.Single) + Beatmap.TotalColumns;
|
||||
|
||||
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
|
||||
|
||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(IsForCurrentRuleset, WorkingBeatmap.Beatmap);
|
||||
|
||||
@ -98,5 +104,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f);
|
||||
|
||||
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this);
|
||||
|
||||
protected override IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => new ManiaConfigManager(settings, Ruleset.RulesetInfo, Variant);
|
||||
}
|
||||
}
|
||||
|
229
osu.Game.Rulesets.Mania/UI/ManiaStage.cs
Normal file
229
osu.Game.Rulesets.Mania/UI/ManiaStage.cs
Normal file
@ -0,0 +1,229 @@
|
||||
// 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.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of <see cref="Column"/>s.
|
||||
/// </summary>
|
||||
internal class ManiaStage : ScrollingPlayfield
|
||||
{
|
||||
public const float HIT_TARGET_POSITION = 50;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this playfield should be inverted. This flips everything inside the playfield.
|
||||
/// </summary>
|
||||
public readonly Bindable<bool> Inverted = new Bindable<bool>(true);
|
||||
|
||||
public IReadOnlyList<Column> Columns => columnFlow.Children;
|
||||
private readonly FillFlowContainer<Column> columnFlow;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container<Drawable> content;
|
||||
|
||||
public Container<DrawableManiaJudgement> Judgements => judgements;
|
||||
private readonly Container<DrawableManiaJudgement> judgements;
|
||||
|
||||
private readonly Container topLevelContainer;
|
||||
|
||||
private List<Color4> normalColumnColours = new List<Color4>();
|
||||
private Color4 specialColumnColour;
|
||||
|
||||
private readonly int firstColumnIndex;
|
||||
private readonly StageDefinition definition;
|
||||
|
||||
public ManiaStage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
|
||||
: base(ScrollingDirection.Up)
|
||||
{
|
||||
this.firstColumnIndex = firstColumnIndex;
|
||||
this.definition = definition;
|
||||
|
||||
Name = "Stage";
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
AutoSizeAxes = Axes.X;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Name = "Columns mask",
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Name = "Background",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black
|
||||
},
|
||||
columnFlow = new FillFlowContainer<Column>
|
||||
{
|
||||
Name = "Columns",
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Padding = new MarginPadding { Left = 1, Right = 1 },
|
||||
Spacing = new Vector2(1, 0)
|
||||
},
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Name = "Barlines mask",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 1366, // Bar lines should only be masked on the vertical axis
|
||||
BypassAutoSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Child = content = new Container
|
||||
{
|
||||
Name = "Bar lines",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
|
||||
}
|
||||
},
|
||||
judgements = new Container<DrawableManiaJudgement>
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Y = HIT_TARGET_POSITION + 150,
|
||||
BypassAutoSizeAxes = Axes.Both
|
||||
},
|
||||
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < definition.Columns; i++)
|
||||
{
|
||||
var isSpecial = isSpecialColumn(i);
|
||||
var column = new Column
|
||||
{
|
||||
IsSpecial = isSpecial,
|
||||
Action = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++
|
||||
};
|
||||
|
||||
AddColumn(column);
|
||||
}
|
||||
|
||||
Inverted.ValueChanged += invertedChanged;
|
||||
Inverted.TriggerChange();
|
||||
}
|
||||
|
||||
private void invertedChanged(bool newValue)
|
||||
{
|
||||
Scale = new Vector2(1, newValue ? -1 : 1);
|
||||
Judgements.Scale = Scale;
|
||||
}
|
||||
|
||||
public void AddColumn(Column c)
|
||||
{
|
||||
c.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||
|
||||
topLevelContainer.Add(c.TopLevelContainer.CreateProxy());
|
||||
columnFlow.Add(c);
|
||||
AddNested(c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the column index is a special column for this playfield.
|
||||
/// </summary>
|
||||
/// <param name="column">The 0-based column index.</param>
|
||||
/// <returns>Whether the column is a special column.</returns>
|
||||
private bool isSpecialColumn(int column) => definition.Columns % 2 == 1 && column == definition.Columns / 2;
|
||||
|
||||
public override void Add(DrawableHitObject h)
|
||||
{
|
||||
var maniaObject = (ManiaHitObject)h.HitObject;
|
||||
int columnIndex = maniaObject.Column - firstColumnIndex;
|
||||
Columns.ElementAt(columnIndex).Add(h);
|
||||
h.OnJudgement += OnJudgement;
|
||||
}
|
||||
|
||||
public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline));
|
||||
|
||||
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||
{
|
||||
judgements.Clear();
|
||||
judgements.Add(new DrawableManiaJudgement(judgement)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
normalColumnColours = new List<Color4>
|
||||
{
|
||||
colours.RedDark,
|
||||
colours.GreenDark
|
||||
};
|
||||
|
||||
specialColumnColour = colours.BlueDark;
|
||||
|
||||
// Set the special column + colour + key
|
||||
foreach (var column in Columns)
|
||||
{
|
||||
if (!column.IsSpecial)
|
||||
continue;
|
||||
|
||||
column.AccentColour = specialColumnColour;
|
||||
}
|
||||
|
||||
var nonSpecialColumns = Columns.Where(c => !c.IsSpecial).ToList();
|
||||
|
||||
// We'll set the colours of the non-special columns in a separate loop, because the non-special
|
||||
// column colours are mirrored across their centre and special styles mess with this
|
||||
for (int i = 0; i < Math.Ceiling(nonSpecialColumns.Count / 2f); i++)
|
||||
{
|
||||
Color4 colour = normalColumnColours[i % normalColumnColours.Count];
|
||||
nonSpecialColumns[i].AccentColour = colour;
|
||||
nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
// Due to masking differences, it is not possible to get the width of the columns container automatically
|
||||
// While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually
|
||||
content.Width = columnFlow.Width;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
// 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.Rulesets.Mania.UI
|
||||
{
|
||||
public enum SpecialColumnPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// The special column will lie in the center of the columns.
|
||||
/// </summary>
|
||||
Normal,
|
||||
/// <summary>
|
||||
/// The special column will lie to the left of the columns.
|
||||
/// </summary>
|
||||
Left,
|
||||
/// <summary>
|
||||
/// The special column will lie to the right of the columns.
|
||||
/// </summary>
|
||||
Right
|
||||
}
|
||||
}
|
@ -32,6 +32,10 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="JetBrains.Annotations, Version=11.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
@ -58,12 +62,14 @@
|
||||
<Compile Include="Beatmaps\Patterns\Legacy\PatternType.cs" />
|
||||
<Compile Include="Beatmaps\ManiaBeatmapConverter.cs" />
|
||||
<Compile Include="Beatmaps\Patterns\Pattern.cs" />
|
||||
<Compile Include="Configuration\ManiaConfigManager.cs" />
|
||||
<Compile Include="MathUtils\FastRandom.cs" />
|
||||
<Compile Include="Judgements\HitWindows.cs" />
|
||||
<Compile Include="Judgements\HoldNoteTailJudgement.cs" />
|
||||
<Compile Include="Judgements\HoldNoteTickJudgement.cs" />
|
||||
<Compile Include="Judgements\ManiaJudgement.cs" />
|
||||
<Compile Include="ManiaDifficultyCalculator.cs" />
|
||||
<Compile Include="Mods\IPlayfieldTypeMod.cs" />
|
||||
<Compile Include="Mods\ManiaKeyMod.cs" />
|
||||
<Compile Include="Mods\ManiaModAutoplay.cs" />
|
||||
<Compile Include="Mods\ManiaModDaycore.cs" />
|
||||
@ -83,7 +89,7 @@
|
||||
<Compile Include="Mods\ManiaModKey7.cs" />
|
||||
<Compile Include="Mods\ManiaModKey8.cs" />
|
||||
<Compile Include="Mods\ManiaModKey9.cs" />
|
||||
<Compile Include="Mods\ManiaModKeyCoop.cs" />
|
||||
<Compile Include="Mods\ManiaModDualStages.cs" />
|
||||
<Compile Include="Mods\ManiaModNightcore.cs" />
|
||||
<Compile Include="Mods\ManiaModPerfect.cs" />
|
||||
<Compile Include="Mods\ManiaModRandom.cs" />
|
||||
@ -115,12 +121,12 @@
|
||||
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||
<Compile Include="UI\Column.cs" />
|
||||
<Compile Include="UI\DrawableManiaJudgement.cs" />
|
||||
<Compile Include="UI\ManiaStage.cs" />
|
||||
<Compile Include="UI\HitExplosion.cs" />
|
||||
<Compile Include="UI\ManiaRulesetContainer.cs" />
|
||||
<Compile Include="UI\ManiaPlayfield.cs" />
|
||||
<Compile Include="ManiaRuleset.cs" />
|
||||
<Compile Include="Mods\ManiaModNoFail.cs" />
|
||||
<Compile Include="UI\SpecialColumnPosition.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
</packages>
|
@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
continue;
|
||||
|
||||
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
|
||||
float stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
|
||||
double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
|
||||
|
||||
if (objectN.StartTime - endTime > stackThreshold)
|
||||
//We are no longer within stacking range of the next object.
|
||||
@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
OsuHitObject objectI = beatmap.HitObjects[i];
|
||||
if (objectI.StackHeight != 0 || objectI is Spinner) continue;
|
||||
|
||||
float stackThreshold = objectI.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
|
||||
double stackThreshold = objectI.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
|
||||
|
||||
/* If this object is a hitcircle, then we enter this "special" case.
|
||||
* It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider.
|
||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override string Description => @"Play with no approach circles and fading notes for a slight score advantage.";
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
|
||||
private const float fade_in_duration_multiplier = 0.4f;
|
||||
private const double fade_in_duration_multiplier = 0.4;
|
||||
private const double fade_out_duration_multiplier = 0.3;
|
||||
|
||||
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||
|
@ -16,8 +16,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
private readonly RepeatPoint repeatPoint;
|
||||
private readonly DrawableSlider drawableSlider;
|
||||
|
||||
public double FadeInTime;
|
||||
public double FadeOutTime;
|
||||
private double animDuration;
|
||||
|
||||
public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider)
|
||||
: base(repeatPoint)
|
||||
@ -48,11 +47,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
protected override void UpdatePreemptState()
|
||||
{
|
||||
var animIn = Math.Min(150, repeatPoint.StartTime - FadeInTime);
|
||||
animDuration = Math.Min(150, repeatPoint.SpanDuration / 2);
|
||||
|
||||
this.FadeIn(animIn).ScaleTo(1.2f, animIn)
|
||||
this.FadeIn(animDuration).ScaleTo(1.2f, animDuration / 2)
|
||||
.Then()
|
||||
.ScaleTo(1, 150, Easing.Out);
|
||||
.ScaleTo(1, animDuration / 2, Easing.Out);
|
||||
}
|
||||
|
||||
protected override void UpdateCurrentState(ArmedState state)
|
||||
@ -60,18 +59,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
this.Delay(FadeOutTime - repeatPoint.StartTime).FadeOut();
|
||||
this.Delay(HitObject.TimePreempt).FadeOut();
|
||||
break;
|
||||
case ArmedState.Miss:
|
||||
this.FadeOut(160);
|
||||
this.FadeOut(animDuration);
|
||||
break;
|
||||
case ArmedState.Hit:
|
||||
this.FadeOut(120, Easing.OutQuint)
|
||||
.ScaleTo(Scale * 1.5f, 120, Easing.OutQuint);
|
||||
this.FadeOut(animDuration, Easing.OutQuint)
|
||||
.ScaleTo(Scale * 1.5f, animDuration, Easing.OutQuint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSnakingPosition(Vector2 start, Vector2 end) => Position = repeatPoint.RepeatIndex % 2 == 1 ? end : start;
|
||||
public void UpdateSnakingPosition(Vector2 start, Vector2 end) => Position = repeatPoint.RepeatIndex % 2 == 0 ? end : start;
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ using System.Linq;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
@ -60,7 +61,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Samples = s.Samples,
|
||||
SampleControlPoint = s.SampleControlPoint,
|
||||
TimePreempt = s.TimePreempt,
|
||||
TimeFadein = s.TimeFadein
|
||||
TimeFadein = s.TimeFadein,
|
||||
HitWindow300 = s.HitWindow300,
|
||||
HitWindow100 = s.HitWindow100,
|
||||
HitWindow50 = s.HitWindow50
|
||||
})
|
||||
};
|
||||
|
||||
@ -69,12 +73,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
AddNested(InitialCircle);
|
||||
|
||||
var repeatDuration = s.Curve.Distance / s.Velocity;
|
||||
foreach (var tick in s.NestedHitObjects.OfType<SliderTick>())
|
||||
{
|
||||
var repeatStartTime = s.StartTime + tick.RepeatIndex * repeatDuration;
|
||||
var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? HitObject.TimeFadein : HitObject.TimeFadein / 2);
|
||||
var fadeOutTime = repeatStartTime + repeatDuration;
|
||||
var spanStartTime = s.StartTime + tick.SpanIndex * s.SpanDuration;
|
||||
var fadeInTime = spanStartTime + (tick.StartTime - spanStartTime) / 2 - (tick.SpanIndex == 0 ? HitObject.TimeFadein : HitObject.TimeFadein / 2);
|
||||
var fadeOutTime = spanStartTime + s.SpanDuration;
|
||||
|
||||
var drawableTick = new DrawableSliderTick(tick)
|
||||
{
|
||||
@ -89,15 +92,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
foreach (var repeatPoint in s.NestedHitObjects.OfType<RepeatPoint>())
|
||||
{
|
||||
var repeatStartTime = s.StartTime + repeatPoint.RepeatIndex * repeatDuration;
|
||||
var fadeInTime = repeatStartTime + (repeatPoint.StartTime - repeatStartTime) / 2 - (repeatPoint.RepeatIndex == 0 ? HitObject.TimeFadein : HitObject.TimeFadein / 2);
|
||||
var fadeOutTime = repeatStartTime + repeatDuration;
|
||||
|
||||
var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this)
|
||||
{
|
||||
FadeInTime = fadeInTime,
|
||||
FadeOutTime = fadeOutTime,
|
||||
Position = repeatPoint.Position,
|
||||
Position = repeatPoint.Position
|
||||
};
|
||||
|
||||
repeatPoints.Add(drawableRepeatPoint);
|
||||
@ -106,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
}
|
||||
}
|
||||
|
||||
private int currentRepeat;
|
||||
private int currentSpan;
|
||||
public bool Tracking;
|
||||
|
||||
protected override void Update()
|
||||
@ -117,17 +114,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
double progress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
|
||||
|
||||
int repeat = slider.RepeatAt(progress);
|
||||
int span = slider.SpanAt(progress);
|
||||
progress = slider.ProgressAt(progress);
|
||||
|
||||
if (repeat > currentRepeat)
|
||||
currentRepeat = repeat;
|
||||
if (span > currentSpan)
|
||||
currentSpan = span;
|
||||
|
||||
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
|
||||
if (!InitialCircle.Judgements.Any(j => j.IsHit))
|
||||
InitialCircle.Position = slider.Curve.PositionAt(progress);
|
||||
|
||||
foreach (var c in components.OfType<ISliderProgress>()) c.UpdateProgress(progress, repeat);
|
||||
foreach (var c in components.OfType<ISliderProgress>()) c.UpdateProgress(progress, span);
|
||||
foreach (var c in components.OfType<ITrackSnaking>()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0));
|
||||
foreach (var t in ticks.Children) t.Tracking = Ball.Tracking;
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateProgress(double progress, int repeat)
|
||||
public void UpdateProgress(double progress, int span)
|
||||
{
|
||||
Position = slider.Curve.PositionAt(progress);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ using OpenTK;
|
||||
using OpenTK.Graphics.ES30;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
@ -164,14 +165,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
return true;
|
||||
}
|
||||
|
||||
public void UpdateProgress(double progress, int repeat)
|
||||
public void UpdateProgress(double progress, int span)
|
||||
{
|
||||
double start = 0;
|
||||
double end = snakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1;
|
||||
|
||||
if (repeat >= slider.RepeatCount - 1)
|
||||
if (span >= slider.SpanCount() - 1)
|
||||
{
|
||||
if (Math.Min(repeat, slider.RepeatCount - 1) % 2 == 1)
|
||||
if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1)
|
||||
{
|
||||
start = 0;
|
||||
end = snakingOut ? progress : 1;
|
||||
|
@ -5,6 +5,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
public interface ISliderProgress
|
||||
{
|
||||
void UpdateProgress(double progress, int repeat);
|
||||
void UpdateProgress(double progress, int span);
|
||||
}
|
||||
}
|
||||
|
@ -16,12 +16,12 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
public const double OBJECT_RADIUS = 64;
|
||||
|
||||
private const double hittable_range = 300;
|
||||
private const double hit_window_50 = 150;
|
||||
private const double hit_window_100 = 80;
|
||||
private const double hit_window_300 = 30;
|
||||
public double HitWindow50 = 150;
|
||||
public double HitWindow100 = 80;
|
||||
public double HitWindow300 = 30;
|
||||
|
||||
public float TimePreempt = 600;
|
||||
public float TimeFadein = 400;
|
||||
public double TimePreempt = 600;
|
||||
public double TimeFadein = 400;
|
||||
|
||||
public Vector2 Position { get; set; }
|
||||
public float X => Position.X;
|
||||
@ -50,13 +50,13 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return 300;
|
||||
return hittable_range;
|
||||
case HitResult.Meh:
|
||||
return 150;
|
||||
return HitWindow50;
|
||||
case HitResult.Good:
|
||||
return 80;
|
||||
return HitWindow100;
|
||||
case HitResult.Great:
|
||||
return 30;
|
||||
return HitWindow300;
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,6 +78,10 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
|
||||
TimeFadein = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300);
|
||||
|
||||
HitWindow50 = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 200, 150, 100);
|
||||
HitWindow100 = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 140, 100, 60);
|
||||
HitWindow300 = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 80, 50, 20);
|
||||
|
||||
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,25 @@
|
||||
// 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.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
public class RepeatPoint : OsuHitObject
|
||||
{
|
||||
public int RepeatIndex { get; set; }
|
||||
public double SpanDuration { get; set; }
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
// We want to show the first RepeatPoint as the TimePreempt dictates but on short (and possibly fast) sliders
|
||||
// we may need to cut down this time on following RepeatPoints to only show up to two RepeatPoints at any given time.
|
||||
if (RepeatIndex > 0)
|
||||
TimePreempt = Math.Min(SpanDuration * 2, TimePreempt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,12 +20,12 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
/// </summary>
|
||||
private const float base_scoring_distance = 100;
|
||||
|
||||
public readonly SliderCurve Curve = new SliderCurve();
|
||||
|
||||
public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity;
|
||||
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
public override Vector2 EndPosition => PositionAt(1);
|
||||
public override Vector2 EndPosition => this.PositionAt(1);
|
||||
|
||||
public SliderCurve Curve { get; } = new SliderCurve();
|
||||
|
||||
public List<Vector2> ControlPoints
|
||||
{
|
||||
@ -58,7 +58,12 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
internal float LazyTravelDistance;
|
||||
|
||||
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>();
|
||||
public int RepeatCount { get; set; } = 1;
|
||||
public int RepeatCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The length of one span of this <see cref="Slider"/>.
|
||||
/// </summary>
|
||||
public double SpanDuration => Duration / this.SpanCount();
|
||||
|
||||
private int stackHeight;
|
||||
|
||||
@ -88,18 +93,6 @@ 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);
|
||||
|
||||
protected override void CreateNestedHitObjects()
|
||||
{
|
||||
base.CreateNestedHitObjects();
|
||||
@ -114,14 +107,13 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
var length = Curve.Distance;
|
||||
var tickDistance = Math.Min(TickDistance, length);
|
||||
var repeatDuration = length / Velocity;
|
||||
|
||||
var minDistanceFromEnd = Velocity * 0.01;
|
||||
|
||||
for (var repeat = 0; repeat < RepeatCount; repeat++)
|
||||
for (var span = 0; span < this.SpanCount(); span++)
|
||||
{
|
||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||
var reversed = repeat % 2 == 1;
|
||||
var spanStartTime = StartTime + span * SpanDuration;
|
||||
var reversed = span % 2 == 1;
|
||||
|
||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||
{
|
||||
@ -144,8 +136,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
AddNested(new SliderTick
|
||||
{
|
||||
RepeatIndex = repeat,
|
||||
StartTime = repeatStartTime + timeProgress * repeatDuration,
|
||||
SpanIndex = span,
|
||||
StartTime = spanStartTime + timeProgress * SpanDuration,
|
||||
Position = Curve.PositionAt(distanceProgress),
|
||||
StackHeight = StackHeight,
|
||||
Scale = Scale,
|
||||
@ -158,21 +150,18 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
private void createRepeatPoints()
|
||||
{
|
||||
var repeatDuration = Distance / Velocity;
|
||||
|
||||
for (var repeat = 1; repeat < RepeatCount; repeat++)
|
||||
for (int repeatIndex = 0, repeat = 1; repeatIndex < RepeatCount; repeatIndex++, repeat++)
|
||||
{
|
||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||
|
||||
AddNested(new RepeatPoint
|
||||
{
|
||||
RepeatIndex = repeat,
|
||||
StartTime = repeatStartTime,
|
||||
RepeatIndex = repeatIndex,
|
||||
SpanDuration = SpanDuration,
|
||||
StartTime = StartTime + repeat * SpanDuration,
|
||||
Position = Curve.PositionAt(repeat % 2),
|
||||
StackHeight = StackHeight,
|
||||
Scale = Scale,
|
||||
ComboColour = ComboColour,
|
||||
Samples = new List<SampleInfo>(RepeatSamples[repeat])
|
||||
Samples = new List<SampleInfo>(RepeatSamples[repeatIndex])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
public class SliderTick : OsuHitObject
|
||||
{
|
||||
public int RepeatIndex { get; set; }
|
||||
public int SpanIndex { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
|
||||
|
@ -63,6 +63,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
AddStep("Fast Short Slider", () => testShortHighSpeed());
|
||||
AddStep("Fast Short Slider 1 Repeat", () => testShortHighSpeed(1));
|
||||
AddStep("Fast Short Slider 2 Repeats", () => testShortHighSpeed(2));
|
||||
AddStep("Fast Short Slider 6 Repeats", () => testShortHighSpeed(6));
|
||||
|
||||
AddStep("Perfect Curve", testCurve);
|
||||
// TODO more curve types?
|
||||
@ -84,14 +85,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2)
|
||||
{
|
||||
repeats++; // The first run through the slider is considered a repeat
|
||||
|
||||
var repeatSamples = new List<List<SampleInfo>>();
|
||||
if (repeats > 1)
|
||||
{
|
||||
for (int i = 0; i < repeats; i++)
|
||||
repeatSamples.Add(new List<SampleInfo>());
|
||||
}
|
||||
for (int i = 0; i < repeats; i++)
|
||||
repeatSamples.Add(new List<SampleInfo>());
|
||||
|
||||
var slider = new Slider
|
||||
{
|
||||
|
@ -33,6 +33,10 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="JetBrains.Annotations, Version=11.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
</packages>
|
@ -84,7 +84,8 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
|
||||
if (distanceData != null)
|
||||
{
|
||||
int repeats = repeatsData?.RepeatCount ?? 1;
|
||||
// Number of spans of the object - one for the initial length and for each repeat
|
||||
int spans = repeatsData?.SpanCount() ?? 1;
|
||||
|
||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime);
|
||||
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);
|
||||
@ -93,7 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment;
|
||||
|
||||
// The true distance, accounting for any repeats. This ends up being the drum roll distance later
|
||||
double distance = distanceData.Distance * repeats * legacy_velocity_multiplier;
|
||||
double distance = distanceData.Distance * spans * legacy_velocity_multiplier;
|
||||
|
||||
// The velocity of the taiko hit object - calculated as the velocity of a drum roll
|
||||
double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
|
||||
@ -111,7 +112,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
double osuDuration = distance / osuVelocity;
|
||||
|
||||
// If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
|
||||
double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / repeats);
|
||||
double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans);
|
||||
|
||||
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
|
||||
{
|
||||
|
@ -32,6 +32,10 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="JetBrains.Annotations, Version=11.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
</packages>
|
@ -70,7 +70,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition);
|
||||
Assert.IsTrue(sprite.IsDrawable);
|
||||
Assert.AreEqual(Anchor.Centre, sprite.Origin);
|
||||
Assert.AreEqual(Path.Combine("SB", "lyric", "ja-21.png"), sprite.Path);
|
||||
Assert.AreEqual("SB/lyric/ja-21.png", sprite.Path);
|
||||
|
||||
var animation = background.Elements.ElementAt(12) as StoryboardAnimation;
|
||||
Assert.NotNull(animation);
|
||||
@ -82,7 +82,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.IsTrue(animation.IsDrawable);
|
||||
Assert.AreEqual(AnimationLoopType.LoopForever, animation.LoopType);
|
||||
Assert.AreEqual(Anchor.Centre, animation.Origin);
|
||||
Assert.AreEqual(Path.Combine("SB", "red jitter", "red_0000.jpg"), animation.Path);
|
||||
Assert.AreEqual("SB/red jitter/red_0000.jpg", animation.Path);
|
||||
Assert.AreEqual(78993, animation.StartTime);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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 OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
@ -15,6 +16,7 @@ using osu.Game.Screens.Edit.Screens.Compose.Timeline;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[Ignore("CI regularly hangs on this TestCase...")]
|
||||
public class TestCaseWaveform : OsuTestCase
|
||||
{
|
||||
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
|
||||
|
@ -33,6 +33,10 @@
|
||||
<Reference Include="DeepEqual, Version=1.6.0.0, Culture=neutral, PublicKeyToken=null">
|
||||
<HintPath>$(SolutionDir)\packages\DeepEqual.1.6.0.0\lib\net40\DeepEqual.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="JetBrains.Annotations, Version=11.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
|
@ -1,11 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
-->
|
||||
<packages>
|
||||
<package id="DeepEqual" version="1.6.0.0" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
<package id="System.ValueTuple" version="4.4.0" targetFramework="net461" />
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
-->
|
||||
<packages>
|
||||
<package id="DeepEqual" version="1.6.0.0" targetFramework="net461" />
|
||||
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
<package id="System.ValueTuple" version="4.4.0" targetFramework="net461" />
|
||||
</packages>
|
@ -287,15 +287,16 @@ namespace osu.Game.Beatmaps
|
||||
Import(archive);
|
||||
|
||||
downloadNotification.State = ProgressNotificationState.Completed;
|
||||
currentDownloads.Remove(request);
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
|
||||
currentDownloads.Remove(request);
|
||||
};
|
||||
|
||||
request.Failure += data =>
|
||||
request.Failure += error =>
|
||||
{
|
||||
if (error is OperationCanceledException) return;
|
||||
|
||||
downloadNotification.State = ProgressNotificationState.Completed;
|
||||
Logger.Error(data, "Failed to get beatmap download information");
|
||||
Logger.Error(error, "Beatmap download failed!");
|
||||
currentDownloads.Remove(request);
|
||||
};
|
||||
|
||||
|
@ -301,6 +301,6 @@ namespace osu.Game.Beatmaps.Formats
|
||||
}
|
||||
}
|
||||
|
||||
private string cleanFilename(string path) => FileSafety.PathSanitise(path.Trim('\"'));
|
||||
private string cleanFilename(string path) => FileSafety.PathStandardise(FileSafety.PathSanitise(path.Trim('\"')));
|
||||
}
|
||||
}
|
||||
|
71
osu.Game/Configuration/DatabasedConfigManager.cs
Normal file
71
osu.Game/Configuration/DatabasedConfigManager.cs
Normal file
@ -0,0 +1,71 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Rulesets;
|
||||
|
||||
namespace osu.Game.Configuration
|
||||
{
|
||||
public abstract class DatabasedConfigManager<T> : ConfigManager<T>
|
||||
where T : struct
|
||||
{
|
||||
private readonly SettingsStore settings;
|
||||
|
||||
private readonly int variant;
|
||||
|
||||
private readonly List<DatabasedSetting> databasedSettings;
|
||||
|
||||
private readonly RulesetInfo ruleset;
|
||||
|
||||
protected DatabasedConfigManager(SettingsStore settings, RulesetInfo ruleset = null, int variant = 0)
|
||||
{
|
||||
this.settings = settings;
|
||||
this.ruleset = ruleset;
|
||||
this.variant = variant;
|
||||
|
||||
databasedSettings = settings.Query(ruleset?.ID, variant);
|
||||
|
||||
InitialiseDefaults();
|
||||
}
|
||||
|
||||
protected override void PerformLoad()
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool PerformSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void AddBindable<TBindable>(T lookup, Bindable<TBindable> bindable)
|
||||
{
|
||||
base.AddBindable(lookup, bindable);
|
||||
|
||||
var setting = databasedSettings.FirstOrDefault(s => (int)s.Key == (int)(object)lookup);
|
||||
if (setting != null)
|
||||
{
|
||||
bindable.Parse(setting.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.Update(setting = new DatabasedSetting
|
||||
{
|
||||
Key = lookup,
|
||||
Value = bindable.Value,
|
||||
RulesetID = ruleset?.ID,
|
||||
Variant = variant,
|
||||
});
|
||||
|
||||
databasedSettings.Add(setting);
|
||||
}
|
||||
|
||||
bindable.ValueChanged += v =>
|
||||
{
|
||||
setting.Value = v;
|
||||
settings.Update(setting);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
51
osu.Game/Configuration/DatabasedSetting.cs
Normal file
51
osu.Game/Configuration/DatabasedSetting.cs
Normal file
@ -0,0 +1,51 @@
|
||||
// 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.ComponentModel.DataAnnotations.Schema;
|
||||
using osu.Game.Database;
|
||||
|
||||
namespace osu.Game.Configuration
|
||||
{
|
||||
[Table("Settings")]
|
||||
public class DatabasedSetting : IHasPrimaryKey
|
||||
{
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int ID { get; set; }
|
||||
|
||||
public int? RulesetID { get; set; }
|
||||
|
||||
public int? Variant { get; set; }
|
||||
|
||||
[Column("Key")]
|
||||
public int IntKey
|
||||
{
|
||||
get => (int)Key;
|
||||
private set => Key = value;
|
||||
}
|
||||
|
||||
[Column("Value")]
|
||||
public string StringValue
|
||||
{
|
||||
get => Value.ToString();
|
||||
set => Value = value;
|
||||
}
|
||||
|
||||
public object Key;
|
||||
public object Value;
|
||||
|
||||
public DatabasedSetting(object key, object value)
|
||||
{
|
||||
Key = key;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for derived classes that may require serialisation.
|
||||
/// </summary>
|
||||
public DatabasedSetting()
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString() => $"{Key}=>{Value}";
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ using osu.Game.Screens.Select;
|
||||
|
||||
namespace osu.Game.Configuration
|
||||
{
|
||||
public class OsuConfigManager : ConfigManager<OsuSetting>
|
||||
public class OsuConfigManager : IniConfigManager<OsuSetting>
|
||||
{
|
||||
protected override void InitialiseDefaults()
|
||||
{
|
||||
|
44
osu.Game/Configuration/SettingsStore.cs
Normal file
44
osu.Game/Configuration/SettingsStore.cs
Normal file
@ -0,0 +1,44 @@
|
||||
// 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.Database;
|
||||
|
||||
namespace osu.Game.Configuration
|
||||
{
|
||||
public class SettingsStore : DatabaseBackedStore
|
||||
{
|
||||
public event Action SettingChanged;
|
||||
|
||||
public SettingsStore(Func<OsuDbContext> createContext)
|
||||
: base(createContext)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve <see cref="DatabasedSetting"/>s for a specified ruleset/variant content.
|
||||
/// </summary>
|
||||
/// <param name="rulesetId">The ruleset's internal ID.</param>
|
||||
/// <param name="variant">An optional variant.</param>
|
||||
/// <returns></returns>
|
||||
public List<DatabasedSetting> Query(int? rulesetId = null, int? variant = null) =>
|
||||
GetContext().DatabasedSetting.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList();
|
||||
|
||||
public void Update(DatabasedSetting setting)
|
||||
{
|
||||
var context = GetContext();
|
||||
|
||||
var newValue = setting.Value;
|
||||
|
||||
Refresh(ref setting);
|
||||
|
||||
setting.Value = newValue;
|
||||
|
||||
context.SaveChanges();
|
||||
|
||||
SettingChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
@ -34,8 +34,14 @@ namespace osu.Game.Database
|
||||
if (context.Entry(obj).State != EntityState.Detached) return;
|
||||
|
||||
var id = obj.ID;
|
||||
obj = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find<T>(id);
|
||||
context.Entry(obj).Reload();
|
||||
var foundObject = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find<T>(id);
|
||||
if (foundObject != null)
|
||||
{
|
||||
obj = foundObject;
|
||||
context.Entry(obj).Reload();
|
||||
}
|
||||
else
|
||||
context.Add(obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -8,9 +8,10 @@ using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets;
|
||||
using DatabasedKeyBinding = osu.Game.Input.Bindings.DatabasedKeyBinding;
|
||||
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||
|
||||
namespace osu.Game.Database
|
||||
@ -22,6 +23,7 @@ namespace osu.Game.Database
|
||||
public DbSet<BeatmapMetadata> BeatmapMetadata { get; set; }
|
||||
public DbSet<BeatmapSetInfo> BeatmapSetInfo { get; set; }
|
||||
public DbSet<DatabasedKeyBinding> DatabasedKeyBinding { get; set; }
|
||||
public DbSet<DatabasedSetting> DatabasedSetting { get; set; }
|
||||
public DbSet<FileInfo> FileInfo { get; set; }
|
||||
public DbSet<RulesetInfo> RulesetInfo { get; set; }
|
||||
|
||||
@ -86,9 +88,11 @@ namespace osu.Game.Database
|
||||
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.DeletePending);
|
||||
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.Hash).IsUnique();
|
||||
|
||||
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.Variant);
|
||||
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => new { b.RulesetID, b.Variant });
|
||||
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.IntAction);
|
||||
|
||||
modelBuilder.Entity<DatabasedSetting>().HasIndex(b => new { b.RulesetID, b.Variant });
|
||||
|
||||
modelBuilder.Entity<FileInfo>().HasIndex(b => b.Hash).IsUnique();
|
||||
modelBuilder.Entity<FileInfo>().HasIndex(b => b.ReferenceCount);
|
||||
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
public Color4 FillColour
|
||||
{
|
||||
set { fill.Colour = value; }
|
||||
set { fill.FadeColour(value, 150, Easing.OutQuint); }
|
||||
}
|
||||
|
||||
public Color4 BackgroundColour
|
||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Input.Bindings
|
||||
/// A KeyBindingInputManager with a database backing for custom overrides.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the custom action.</typeparam>
|
||||
public class DatabasedKeyBindingInputManager<T> : KeyBindingContainer<T>
|
||||
public class DatabasedKeyBindingContainer<T> : KeyBindingContainer<T>
|
||||
where T : struct
|
||||
{
|
||||
private readonly RulesetInfo ruleset;
|
||||
@ -23,7 +23,7 @@ namespace osu.Game.Input.Bindings
|
||||
|
||||
private KeyBindingStore store;
|
||||
|
||||
public override IEnumerable<KeyBinding> DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings();
|
||||
public override IEnumerable<KeyBinding> DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings(variant ?? 0);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance.
|
||||
@ -31,7 +31,7 @@ namespace osu.Game.Input.Bindings
|
||||
/// <param name="ruleset">A reference to identify the current <see cref="Ruleset"/>. Used to lookup mappings. Null for global mappings.</param>
|
||||
/// <param name="variant">An optional variant for the specified <see cref="Ruleset"/>. Used when a ruleset has more than one possible keyboard layouts.</param>
|
||||
/// <param name="simultaneousMode">Specify how to deal with multiple matches of <see cref="KeyCombination"/>s and <see cref="T"/>s.</param>
|
||||
public DatabasedKeyBindingInputManager(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None)
|
||||
public DatabasedKeyBindingContainer(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None)
|
||||
: base(simultaneousMode)
|
||||
{
|
||||
this.ruleset = ruleset;
|
@ -10,11 +10,11 @@ using osu.Framework.Input.Bindings;
|
||||
|
||||
namespace osu.Game.Input.Bindings
|
||||
{
|
||||
public class GlobalKeyBindingInputManager : DatabasedKeyBindingInputManager<GlobalAction>, IHandleGlobalInput
|
||||
public class GlobalActionContainer : DatabasedKeyBindingContainer<GlobalAction>, IHandleGlobalInput
|
||||
{
|
||||
private readonly Drawable handler;
|
||||
|
||||
public GlobalKeyBindingInputManager(OsuGameBase game)
|
||||
public GlobalActionContainer(OsuGameBase game)
|
||||
{
|
||||
if (game is IKeyBindingHandler<GlobalAction>)
|
||||
handler = game;
|
||||
@ -37,7 +37,8 @@ namespace osu.Game.Input.Bindings
|
||||
|
||||
public IEnumerable<KeyBinding> InGameKeyBindings => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.Space, GlobalAction.SkipCutscene)
|
||||
new KeyBinding(InputKey.Space, GlobalAction.SkipCutscene),
|
||||
new KeyBinding(InputKey.Tilde, GlobalAction.QuickRetry)
|
||||
};
|
||||
|
||||
protected override IEnumerable<Drawable> KeyBindingInputQueue =>
|
||||
@ -65,6 +66,8 @@ namespace osu.Game.Input.Bindings
|
||||
|
||||
// In-Game Keybindings
|
||||
[Description("Skip Cutscene")]
|
||||
SkipCutscene
|
||||
SkipCutscene,
|
||||
[Description("Quick Retry (Hold)")]
|
||||
QuickRetry,
|
||||
}
|
||||
}
|
329
osu.Game/Migrations/20180125143340_Settings.Designer.cs
generated
Normal file
329
osu.Game/Migrations/20180125143340_Settings.Designer.cs
generated
Normal file
@ -0,0 +1,329 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using osu.Game.Database;
|
||||
using System;
|
||||
|
||||
namespace osu.Game.Migrations
|
||||
{
|
||||
[DbContext(typeof(OsuDbContext))]
|
||||
[Migration("20180125143340_Settings")]
|
||||
partial class Settings
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<float>("ApproachRate");
|
||||
|
||||
b.Property<float>("CircleSize");
|
||||
|
||||
b.Property<float>("DrainRate");
|
||||
|
||||
b.Property<float>("OverallDifficulty");
|
||||
|
||||
b.Property<float>("SliderMultiplier");
|
||||
|
||||
b.Property<float>("SliderTickRate");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.ToTable("BeatmapDifficulty");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AudioLeadIn");
|
||||
|
||||
b.Property<int>("BaseDifficultyID");
|
||||
|
||||
b.Property<int>("BeatDivisor");
|
||||
|
||||
b.Property<int>("BeatmapSetInfoID");
|
||||
|
||||
b.Property<bool>("Countdown");
|
||||
|
||||
b.Property<double>("DistanceSpacing");
|
||||
|
||||
b.Property<int>("GridSize");
|
||||
|
||||
b.Property<string>("Hash");
|
||||
|
||||
b.Property<bool>("Hidden");
|
||||
|
||||
b.Property<bool>("LetterboxInBreaks");
|
||||
|
||||
b.Property<string>("MD5Hash");
|
||||
|
||||
b.Property<int?>("MetadataID");
|
||||
|
||||
b.Property<int?>("OnlineBeatmapID");
|
||||
|
||||
b.Property<string>("Path");
|
||||
|
||||
b.Property<int>("RulesetID");
|
||||
|
||||
b.Property<bool>("SpecialStyle");
|
||||
|
||||
b.Property<float>("StackLeniency");
|
||||
|
||||
b.Property<double>("StarDifficulty");
|
||||
|
||||
b.Property<string>("StoredBookmarks");
|
||||
|
||||
b.Property<double>("TimelineZoom");
|
||||
|
||||
b.Property<string>("Version");
|
||||
|
||||
b.Property<bool>("WidescreenStoryboard");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("BaseDifficultyID");
|
||||
|
||||
b.HasIndex("BeatmapSetInfoID");
|
||||
|
||||
b.HasIndex("Hash")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("MD5Hash")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("MetadataID");
|
||||
|
||||
b.HasIndex("OnlineBeatmapID")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("RulesetID");
|
||||
|
||||
b.ToTable("BeatmapInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Artist");
|
||||
|
||||
b.Property<string>("ArtistUnicode");
|
||||
|
||||
b.Property<string>("AudioFile");
|
||||
|
||||
b.Property<string>("AuthorString")
|
||||
.HasColumnName("Author");
|
||||
|
||||
b.Property<string>("BackgroundFile");
|
||||
|
||||
b.Property<int>("PreviewTime");
|
||||
|
||||
b.Property<string>("Source");
|
||||
|
||||
b.Property<string>("Tags");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<string>("TitleUnicode");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.ToTable("BeatmapMetadata");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("BeatmapSetInfoID");
|
||||
|
||||
b.Property<int>("FileInfoID");
|
||||
|
||||
b.Property<string>("Filename")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("BeatmapSetInfoID");
|
||||
|
||||
b.HasIndex("FileInfoID");
|
||||
|
||||
b.ToTable("BeatmapSetFileInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("DeletePending");
|
||||
|
||||
b.Property<string>("Hash");
|
||||
|
||||
b.Property<int?>("MetadataID");
|
||||
|
||||
b.Property<int?>("OnlineBeatmapSetID");
|
||||
|
||||
b.Property<bool>("Protected");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("DeletePending");
|
||||
|
||||
b.HasIndex("Hash")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("MetadataID");
|
||||
|
||||
b.HasIndex("OnlineBeatmapSetID")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("BeatmapSetInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.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.Beatmaps.BeatmapInfo", b =>
|
||||
{
|
||||
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
|
||||
.WithMany()
|
||||
.HasForeignKey("BaseDifficultyID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
|
||||
.WithMany("Beatmaps")
|
||||
.HasForeignKey("BeatmapSetInfoID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
|
||||
.WithMany("Beatmaps")
|
||||
.HasForeignKey("MetadataID");
|
||||
|
||||
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
|
||||
.WithMany()
|
||||
.HasForeignKey("RulesetID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
|
||||
{
|
||||
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
|
||||
.WithMany("Files")
|
||||
.HasForeignKey("BeatmapSetInfoID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
|
||||
.WithMany()
|
||||
.HasForeignKey("FileInfoID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
|
||||
{
|
||||
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
|
||||
.WithMany("BeatmapSets")
|
||||
.HasForeignKey("MetadataID");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
57
osu.Game/Migrations/20180125143340_Settings.cs
Normal file
57
osu.Game/Migrations/20180125143340_Settings.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Migrations
|
||||
{
|
||||
public partial class Settings : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_KeyBinding_Variant",
|
||||
table: "KeyBinding");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Settings",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Key = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
RulesetID = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
Value = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Variant = table.Column<int>(type: "INTEGER", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Settings", x => x.ID);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_KeyBinding_RulesetID_Variant",
|
||||
table: "KeyBinding",
|
||||
columns: new[] { "RulesetID", "Variant" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Settings_RulesetID_Variant",
|
||||
table: "Settings",
|
||||
columns: new[] { "RulesetID", "Variant" });
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Settings");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_KeyBinding_RulesetID_Variant",
|
||||
table: "KeyBinding");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_KeyBinding_Variant",
|
||||
table: "KeyBinding",
|
||||
column: "Variant");
|
||||
}
|
||||
}
|
||||
}
|
@ -193,6 +193,28 @@ namespace osu.Game.Migrations
|
||||
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")
|
||||
@ -212,7 +234,7 @@ namespace osu.Game.Migrations
|
||||
|
||||
b.HasIndex("IntAction");
|
||||
|
||||
b.HasIndex("Variant");
|
||||
b.HasIndex("RulesetID", "Variant");
|
||||
|
||||
b.ToTable("KeyBinding");
|
||||
});
|
||||
|
@ -126,6 +126,7 @@ namespace osu.Game.Online.API
|
||||
userReq.Success += u =>
|
||||
{
|
||||
LocalUser.Value = u;
|
||||
Username = LocalUser.Value.Username;
|
||||
failureCount = 0;
|
||||
|
||||
//we're connected!
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Configuration;
|
||||
@ -27,6 +28,7 @@ using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game
|
||||
@ -71,6 +73,7 @@ namespace osu.Game
|
||||
private OsuScreen screenStack;
|
||||
|
||||
private VolumeControl volume;
|
||||
private OnScreenDisplay onscreenDisplay;
|
||||
|
||||
private Bindable<int> configRuleset;
|
||||
public Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
|
||||
@ -79,6 +82,9 @@ namespace osu.Game
|
||||
|
||||
private SettingsOverlay settings;
|
||||
|
||||
// todo: move this to SongSelect once Screen has the ability to unsuspend.
|
||||
public readonly Bindable<IEnumerable<Mod>> SelectedMods = new Bindable<IEnumerable<Mod>>(new List<Mod>());
|
||||
|
||||
public OsuGame(string[] args = null)
|
||||
{
|
||||
this.args = args;
|
||||
@ -110,7 +116,7 @@ namespace osu.Game
|
||||
Task.Run(() => BeatmapManager.Import(paths.ToArray()));
|
||||
}
|
||||
|
||||
dependencies.Cache(this);
|
||||
dependencies.CacheAs(this);
|
||||
|
||||
configRuleset = LocalConfig.GetBindable<int>(OsuSetting.Ruleset);
|
||||
Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First();
|
||||
@ -195,7 +201,7 @@ namespace osu.Game
|
||||
}, overlayContent.Add);
|
||||
|
||||
loadComponentSingleFile(volume = new VolumeControl(), Add);
|
||||
loadComponentSingleFile(new OnScreenDisplay(), Add);
|
||||
loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add);
|
||||
|
||||
//overlay elements
|
||||
loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add);
|
||||
@ -232,6 +238,7 @@ namespace osu.Game
|
||||
forwardLoggedErrorsToNotifications();
|
||||
|
||||
dependencies.Cache(settings);
|
||||
dependencies.Cache(onscreenDisplay);
|
||||
dependencies.Cache(social);
|
||||
dependencies.Cache(direct);
|
||||
dependencies.Cache(chat);
|
||||
|
@ -44,6 +44,8 @@ namespace osu.Game
|
||||
|
||||
protected KeyBindingStore KeyBindingStore;
|
||||
|
||||
protected SettingsStore SettingsStore;
|
||||
|
||||
protected CursorOverrideContainer CursorOverrideContainer;
|
||||
|
||||
protected override string MainResourceFile => @"osu.Game.Resources.dll";
|
||||
@ -93,7 +95,7 @@ namespace osu.Game
|
||||
|
||||
dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore<byte[]>(Resources, @"Textures"))));
|
||||
|
||||
dependencies.Cache(this);
|
||||
dependencies.CacheAs(this);
|
||||
dependencies.Cache(LocalConfig);
|
||||
|
||||
runMigrations();
|
||||
@ -109,10 +111,11 @@ namespace osu.Game
|
||||
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory.GetContext, RulesetStore, API, Host));
|
||||
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory.GetContext, Host, BeatmapManager, RulesetStore));
|
||||
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory.GetContext, RulesetStore));
|
||||
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory.GetContext));
|
||||
dependencies.Cache(new OsuColour());
|
||||
|
||||
//this completely overrides the framework default. will need to change once we make a proper FontStore.
|
||||
dependencies.Cache(Fonts = new FontStore { ScaleAdjust = 100 }, true);
|
||||
dependencies.Cache(Fonts = new FontStore { ScaleAdjust = 100 });
|
||||
|
||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/FontAwesome"));
|
||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/osuFont"));
|
||||
@ -209,10 +212,10 @@ namespace osu.Game
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
GlobalKeyBindingInputManager globalBinding;
|
||||
GlobalActionContainer globalBinding;
|
||||
|
||||
CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both };
|
||||
CursorOverrideContainer.Child = globalBinding = new GlobalKeyBindingInputManager(this)
|
||||
CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both }
|
||||
|
@ -16,7 +16,6 @@ using osu.Game.Graphics.Sprites;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Audio.Track;
|
||||
@ -65,11 +64,14 @@ namespace osu.Game.Overlays.Direct
|
||||
Colour = Color4.Black.Opacity(0.3f),
|
||||
};
|
||||
|
||||
private OsuColour colours;
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay)
|
||||
{
|
||||
this.beatmaps = beatmaps;
|
||||
this.beatmapSetOverlay = beatmapSetOverlay;
|
||||
this.colours = colours;
|
||||
|
||||
AddInternal(content = new Container
|
||||
{
|
||||
@ -182,7 +184,6 @@ namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
progressBar.Current.Value = 0;
|
||||
progressBar.FadeOut(500);
|
||||
Logger.Error(e, "Failed to get beatmap download information");
|
||||
};
|
||||
|
||||
request.DownloadProgressed += progress => progressBar.Current.Value = progress;
|
||||
@ -190,7 +191,7 @@ namespace osu.Game.Overlays.Direct
|
||||
request.Success += data =>
|
||||
{
|
||||
progressBar.Current.Value = 1;
|
||||
progressBar.FadeOut(500);
|
||||
progressBar.FillColour = colours.Yellow;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -298,7 +298,7 @@ namespace osu.Game.Overlays
|
||||
Task.Run(() =>
|
||||
{
|
||||
var onlineIds = response.Select(r => r.OnlineBeatmapSetID).ToList();
|
||||
var presentOnlineIds = beatmaps.QueryBeatmapSets(s => onlineIds.Contains(s.OnlineBeatmapSetID)).Select(r => r.OnlineBeatmapSetID).ToList();
|
||||
var presentOnlineIds = beatmaps.QueryBeatmapSets(s => onlineIds.Contains(s.OnlineBeatmapSetID) && !s.DeletePending).Select(r => r.OnlineBeatmapSetID).ToList();
|
||||
var sets = response.Select(r => r.ToBeatmapSet(rulesets)).Where(b => !presentOnlineIds.Contains(b.OnlineBeatmapSetID)).ToList();
|
||||
|
||||
// may not need scheduling; loads async internally.
|
||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_hot;
|
||||
public override string Header => "Global";
|
||||
|
||||
public GlobalKeyBindingsSection(GlobalKeyBindingInputManager manager)
|
||||
public GlobalKeyBindingsSection(GlobalActionContainer manager)
|
||||
{
|
||||
Add(new DefaultBindingsSubsection(manager));
|
||||
Add(new InGameKeyBindingsSubsection(manager));
|
||||
@ -23,7 +23,7 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
{
|
||||
protected override string Header => string.Empty;
|
||||
|
||||
public DefaultBindingsSubsection(GlobalKeyBindingInputManager manager)
|
||||
public DefaultBindingsSubsection(GlobalActionContainer manager)
|
||||
: base(null)
|
||||
{
|
||||
Defaults = manager.GlobalKeyBindings;
|
||||
@ -34,7 +34,7 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
{
|
||||
protected override string Header => "In Game";
|
||||
|
||||
public InGameKeyBindingsSubsection(GlobalKeyBindingInputManager manager) : base(null)
|
||||
public InGameKeyBindingsSubsection(GlobalActionContainer manager) : base(null)
|
||||
{
|
||||
Defaults = manager.InGameKeyBindings;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Overlays
|
||||
protected override Drawable CreateHeader() => new SettingsHeader("key configuration", "Customise your keys!");
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(RulesetStore rulesets, GlobalKeyBindingInputManager global)
|
||||
private void load(RulesetStore rulesets, GlobalActionContainer global)
|
||||
{
|
||||
AddSection(new GlobalKeyBindingsSection(global));
|
||||
|
||||
|
@ -188,17 +188,19 @@ namespace osu.Game.Overlays.Mods
|
||||
start = Mods.Length - 1;
|
||||
|
||||
for (int i = start; i < Mods.Length && i >= 0; i += direction)
|
||||
{
|
||||
if (Mods[i].HasImplementation)
|
||||
{
|
||||
changeSelectedIndex(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (SelectAt(i)) return;
|
||||
|
||||
Deselect();
|
||||
}
|
||||
|
||||
public bool SelectAt(int index)
|
||||
{
|
||||
if (!Mods[index].HasImplementation) return false;
|
||||
|
||||
changeSelectedIndex(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Deselect() => changeSelectedIndex(-1);
|
||||
|
||||
private void displayMod(Mod mod)
|
||||
|
@ -113,6 +113,23 @@ namespace osu.Game.Overlays.Mods
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Select one or more mods in this section.
|
||||
/// </summary>
|
||||
/// <param name="mods">The types of <see cref="Mod"/>s which should be deselected.</param>
|
||||
public void SelectTypes(IEnumerable<Mod> mods)
|
||||
{
|
||||
foreach (var button in buttons)
|
||||
{
|
||||
for (int i = 0; i < button.Mods.Length; i++)
|
||||
{
|
||||
foreach (var mod in mods)
|
||||
if (mod.GetType().IsInstanceOfType(button.Mods[i]))
|
||||
button.SelectAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected ModSection()
|
||||
{
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
@ -51,6 +51,8 @@ namespace osu.Game.Overlays.Mods
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets)
|
||||
{
|
||||
SelectedMods.ValueChanged += selectedModsChanged;
|
||||
|
||||
LowMultiplierColour = colours.Red;
|
||||
HighMultiplierColour = colours.Green;
|
||||
|
||||
@ -63,6 +65,37 @@ namespace osu.Game.Overlays.Mods
|
||||
Ruleset.TriggerChange();
|
||||
}
|
||||
|
||||
private void selectedModsChanged(IEnumerable<Mod> obj)
|
||||
{
|
||||
foreach (ModSection section in ModSectionsContainer.Children)
|
||||
section.SelectTypes(obj);
|
||||
|
||||
updateMods();
|
||||
}
|
||||
|
||||
private void updateMods()
|
||||
{
|
||||
double multiplier = 1.0;
|
||||
bool ranked = true;
|
||||
|
||||
foreach (Mod mod in SelectedMods.Value)
|
||||
{
|
||||
multiplier *= mod.ScoreMultiplier;
|
||||
ranked &= mod.Ranked;
|
||||
}
|
||||
|
||||
MultiplierLabel.Text = $"{multiplier:N2}x";
|
||||
if (!ranked)
|
||||
MultiplierLabel.Text += " (Unranked)";
|
||||
|
||||
if (multiplier > 1.0)
|
||||
MultiplierLabel.FadeColour(HighMultiplierColour, 200);
|
||||
else if (multiplier < 1.0)
|
||||
MultiplierLabel.FadeColour(LowMultiplierColour, 200);
|
||||
else
|
||||
MultiplierLabel.FadeColour(Color4.White, 200);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
base.PopOut();
|
||||
@ -97,6 +130,7 @@ namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
foreach (ModSection section in ModSectionsContainer.Children)
|
||||
section.DeselectAll();
|
||||
|
||||
refreshSelectedMods();
|
||||
}
|
||||
|
||||
@ -119,30 +153,7 @@ namespace osu.Game.Overlays.Mods
|
||||
refreshSelectedMods();
|
||||
}
|
||||
|
||||
private void refreshSelectedMods()
|
||||
{
|
||||
SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
|
||||
|
||||
double multiplier = 1.0;
|
||||
bool ranked = true;
|
||||
|
||||
foreach (Mod mod in SelectedMods.Value)
|
||||
{
|
||||
multiplier *= mod.ScoreMultiplier;
|
||||
ranked &= mod.Ranked;
|
||||
}
|
||||
|
||||
MultiplierLabel.Text = $"{multiplier:N2}x";
|
||||
if (!ranked)
|
||||
MultiplierLabel.Text += " (Unranked)";
|
||||
|
||||
if (multiplier > 1.0)
|
||||
MultiplierLabel.FadeColour(HighMultiplierColour, 200);
|
||||
else if (multiplier < 1.0)
|
||||
MultiplierLabel.FadeColour(LowMultiplierColour, 200);
|
||||
else
|
||||
MultiplierLabel.FadeColour(Color4.White, 200);
|
||||
}
|
||||
private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
|
||||
|
||||
public ModSelectOverlay()
|
||||
{
|
||||
|
@ -5,8 +5,6 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
@ -20,10 +18,12 @@ using osu.Framework.Localisation;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays.Music;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Music;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
@ -65,6 +65,12 @@ namespace osu.Game.Overlays
|
||||
AlwaysPresent = true;
|
||||
}
|
||||
|
||||
protected override bool OnDragStart(InputState state)
|
||||
{
|
||||
base.OnDragStart(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnDrag(InputState state)
|
||||
{
|
||||
if (base.OnDrag(state)) return true;
|
||||
|
@ -5,7 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Configuration.Tracking;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@ -118,43 +118,62 @@ namespace osu.Game.Overlays
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(FrameworkConfigManager frameworkConfig)
|
||||
{
|
||||
trackSetting(frameworkConfig.GetBindable<FrameSync>(FrameworkSetting.FrameSync), v => display(v, "Frame Limiter", v.GetDescription(), "Ctrl+F7"));
|
||||
trackSetting(frameworkConfig.GetBindable<string>(FrameworkSetting.AudioDevice), v => display(v, "Audio Device", string.IsNullOrEmpty(v) ? "Default" : v, v));
|
||||
trackSetting(frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowLogOverlay), v => display(v, "Debug Logs", v ? "visible" : "hidden", "Ctrl+F10"));
|
||||
|
||||
void displayResolution() => display(null, "Screen Resolution", frameworkConfig.Get<int>(FrameworkSetting.Width) + "x" + frameworkConfig.Get<int>(FrameworkSetting.Height));
|
||||
|
||||
trackSetting(frameworkConfig.GetBindable<int>(FrameworkSetting.Width), v => displayResolution());
|
||||
trackSetting(frameworkConfig.GetBindable<int>(FrameworkSetting.Height), v => displayResolution());
|
||||
|
||||
trackSetting(frameworkConfig.GetBindable<double>(FrameworkSetting.CursorSensitivity), v => display(v, "Cursor Sensitivity", v.ToString(@"0.##x"), "Ctrl+Alt+R to reset"));
|
||||
trackSetting(frameworkConfig.GetBindable<string>(FrameworkSetting.ActiveInputHandlers),
|
||||
delegate (string v)
|
||||
{
|
||||
bool raw = v.Contains("Raw");
|
||||
display(raw, "Raw Input", raw ? "enabled" : "disabled", "Ctrl+Alt+R to reset");
|
||||
});
|
||||
|
||||
trackSetting(frameworkConfig.GetBindable<WindowMode>(FrameworkSetting.WindowMode), v => display(v, "Screen Mode", v.ToString(), "Alt+Enter"));
|
||||
BeginTracking(this, frameworkConfig);
|
||||
}
|
||||
|
||||
private readonly List<IBindable> references = new List<IBindable>();
|
||||
private readonly Dictionary<(object, IConfigManager), TrackedSettings> trackedConfigManagers = new Dictionary<(object, IConfigManager), TrackedSettings>();
|
||||
|
||||
private void trackSetting<T>(Bindable<T> bindable, Action<T> action)
|
||||
/// <summary>
|
||||
/// Registers a <see cref="ConfigManager{T}"/> to have its settings tracked by this <see cref="OnScreenDisplay"/>.
|
||||
/// </summary>
|
||||
/// <param name="source">The object that is registering the <see cref="ConfigManager{T}"/> to be tracked.</param>
|
||||
/// <param name="configManager">The <see cref="ConfigManager{T}"/> to be tracked.</param>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="configManager"/> is null.</exception>
|
||||
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is already being tracked from the same <paramref name="source"/>.</exception>
|
||||
public void BeginTracking(object source, ITrackableConfigManager configManager)
|
||||
{
|
||||
// we need to keep references as we bind
|
||||
references.Add(bindable);
|
||||
if (configManager == null) throw new ArgumentNullException(nameof(configManager));
|
||||
|
||||
bindable.ValueChanged += action;
|
||||
if (trackedConfigManagers.ContainsKey((source, configManager)))
|
||||
throw new InvalidOperationException($"{nameof(configManager)} is already registered.");
|
||||
|
||||
var trackedSettings = configManager.CreateTrackedSettings();
|
||||
if (trackedSettings == null)
|
||||
return;
|
||||
|
||||
configManager.LoadInto(trackedSettings);
|
||||
trackedSettings.SettingChanged += display;
|
||||
|
||||
trackedConfigManagers.Add((source, configManager), trackedSettings);
|
||||
}
|
||||
|
||||
private void display(object rawValue, string settingName, string settingValue, string shortcut = @"")
|
||||
/// <summary>
|
||||
/// Unregisters a <see cref="ConfigManager{T}"/> from having its settings tracked by this <see cref="OnScreenDisplay"/>.
|
||||
/// </summary>
|
||||
/// <param name="source">The object that registered the <see cref="ConfigManager{T}"/> to be tracked.</param>
|
||||
/// <param name="configManager">The <see cref="ConfigManager{T}"/> that is being tracked.</param>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="configManager"/> is null.</exception>
|
||||
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is not being tracked from the same <see cref="source"/>.</exception>
|
||||
public void StopTracking(object source, ITrackableConfigManager configManager)
|
||||
{
|
||||
if (configManager == null) throw new ArgumentNullException(nameof(configManager));
|
||||
|
||||
if (!trackedConfigManagers.TryGetValue((source, configManager), out var existing))
|
||||
throw new InvalidOperationException($"{nameof(configManager)} is not registered.");
|
||||
|
||||
existing.Unload();
|
||||
existing.SettingChanged -= display;
|
||||
|
||||
trackedConfigManagers.Remove((source, configManager));
|
||||
}
|
||||
|
||||
private void display(SettingDescription description)
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
textLine1.Text = settingName.ToUpper();
|
||||
textLine2.Text = settingValue;
|
||||
textLine3.Text = shortcut.ToUpper();
|
||||
textLine1.Text = description.Name.ToUpper();
|
||||
textLine2.Text = description.Value;
|
||||
textLine3.Text = description.Shortcut.ToUpper();
|
||||
|
||||
box.Animate(
|
||||
b => b.FadeIn(500, Easing.OutQuint),
|
||||
@ -167,16 +186,16 @@ namespace osu.Game.Overlays
|
||||
int optionCount = 0;
|
||||
int selectedOption = -1;
|
||||
|
||||
if (rawValue is bool)
|
||||
if (description.RawValue is bool)
|
||||
{
|
||||
optionCount = 1;
|
||||
if ((bool)rawValue) selectedOption = 0;
|
||||
if ((bool)description.RawValue) selectedOption = 0;
|
||||
}
|
||||
else if (rawValue is Enum)
|
||||
else if (description.RawValue is Enum)
|
||||
{
|
||||
var values = Enum.GetValues(rawValue.GetType());
|
||||
var values = Enum.GetValues(description.RawValue.GetType());
|
||||
optionCount = values.Length;
|
||||
selectedOption = Convert.ToInt32(rawValue);
|
||||
selectedOption = Convert.ToInt32(description.RawValue);
|
||||
}
|
||||
|
||||
textLine2.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre;
|
||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Toolbar
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(SettingsOverlay settings)
|
||||
private void load(MainSettings settings)
|
||||
{
|
||||
StateContainer = settings;
|
||||
}
|
||||
|
11
osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs
Normal file
11
osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs
Normal file
@ -0,0 +1,11 @@
|
||||
// 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.Configuration.Tracking;
|
||||
|
||||
namespace osu.Game.Rulesets.Configuration
|
||||
{
|
||||
public interface IRulesetConfigManager : ITrackableConfigManager
|
||||
{
|
||||
}
|
||||
}
|
15
osu.Game/Rulesets/Configuration/RulesetConfigManager.cs
Normal file
15
osu.Game/Rulesets/Configuration/RulesetConfigManager.cs
Normal file
@ -0,0 +1,15 @@
|
||||
// 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.Configuration;
|
||||
|
||||
namespace osu.Game.Rulesets.Configuration
|
||||
{
|
||||
public abstract class RulesetConfigManager<T> : DatabasedConfigManager<T>, IRulesetConfigManager
|
||||
where T : struct
|
||||
{
|
||||
protected RulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant) : base(settings, ruleset, variant)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -136,7 +136,10 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// </summary>
|
||||
public event Action<DrawableHitObject, ArmedState> ApplyCustomUpdateState;
|
||||
|
||||
protected void PlaySamples() => Samples.ForEach(s => s?.Play());
|
||||
/// <summary>
|
||||
/// Plays all the hitsounds for this <see cref="DrawableHitObject"/>.
|
||||
/// </summary>
|
||||
public void PlaySamples() => Samples.ForEach(s => s?.Play());
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
|
@ -77,6 +77,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
if (repeatCount > 9000)
|
||||
throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high");
|
||||
|
||||
// osu-stable treated the first span of the slider as a repeat, but no repeats are happening
|
||||
repeatCount = Math.Max(0, repeatCount - 1);
|
||||
|
||||
|
||||
if (split.Length > 7)
|
||||
length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);
|
||||
|
||||
@ -84,8 +88,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
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;
|
||||
int nodes = repeatCount + 2;
|
||||
|
||||
// Populate node sample bank infos with the default hit object sample bank
|
||||
var nodeBankInfos = new List<SampleBankInfo>();
|
||||
@ -128,7 +131,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
|
||||
// Generate the final per-node samples
|
||||
var nodeSamples = new List<List<SampleInfo>>(nodes);
|
||||
for (int i = 0; i <= repeatCount; i++)
|
||||
for (int i = 0; i < nodes; 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);
|
||||
|
@ -2,7 +2,6 @@
|
||||
// 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;
|
||||
@ -18,34 +17,23 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
/// </summary>
|
||||
private const float base_scoring_distance = 100;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ConvertSlider"/>s don't need a curve since they're converted to ruleset-specific hitobjects.
|
||||
/// </summary>
|
||||
public SliderCurve Curve { get; } = null;
|
||||
public List<Vector2> ControlPoints { get; set; }
|
||||
public CurveType CurveType { get; set; }
|
||||
|
||||
public double Distance { get; set; }
|
||||
|
||||
public List<List<SampleInfo>> RepeatSamples { get; set; }
|
||||
public int RepeatCount { get; set; } = 1;
|
||||
public int RepeatCount { get; set; }
|
||||
|
||||
public double EndTime => StartTime + RepeatCount * Distance / Velocity;
|
||||
public double EndTime => StartTime + this.SpanCount() * Distance / Velocity;
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
public double Velocity = 1;
|
||||
|
||||
public Vector2 PositionAt(double progress)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public double ProgressAt(double progress)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public int RepeatAt(double progress)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
@ -11,6 +11,11 @@ namespace osu.Game.Rulesets.Objects.Types
|
||||
/// </summary>
|
||||
public interface IHasCurve : IHasDistance, IHasRepeats
|
||||
{
|
||||
/// <summary>
|
||||
/// The curve.
|
||||
/// </summary>
|
||||
SliderCurve Curve { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The control points that shape the curve.
|
||||
/// </summary>
|
||||
@ -20,7 +25,10 @@ namespace osu.Game.Rulesets.Objects.Types
|
||||
/// The type of curve.
|
||||
/// </summary>
|
||||
CurveType CurveType { get; }
|
||||
}
|
||||
|
||||
public static class HasCurveExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the position on the curve at a given progress, accounting for repeat logic.
|
||||
/// <para>
|
||||
@ -28,20 +36,28 @@ namespace osu.Game.Rulesets.Objects.Types
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
|
||||
Vector2 PositionAt(double progress);
|
||||
public static Vector2 PositionAt(this IHasCurve obj, double progress)
|
||||
=> obj.Curve.PositionAt(obj.ProgressAt(progress));
|
||||
|
||||
/// <summary>
|
||||
/// Finds the progress along the curve, accounting for repeat logic.
|
||||
/// </summary>
|
||||
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
|
||||
/// <returns>[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</returns>
|
||||
double ProgressAt(double progress);
|
||||
public static double ProgressAt(this IHasCurve obj, double progress)
|
||||
{
|
||||
double p = progress * obj.SpanCount() % 1;
|
||||
if (obj.SpanAt(progress) % 2 == 1)
|
||||
p = 1 - p;
|
||||
return p;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines which repeat of the curve the progress point is on.
|
||||
/// Determines which span of the curve the progress point is on.
|
||||
/// </summary>
|
||||
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
|
||||
/// <returns>[0, RepeatCount] where 0 is the first run.</returns>
|
||||
int RepeatAt(double progress);
|
||||
/// <returns>[0, SpanCount) where 0 is the first run.</returns>
|
||||
public static int SpanAt(this IHasCurve obj, double progress)
|
||||
=> (int)(progress * obj.SpanCount());
|
||||
}
|
||||
}
|
||||
|
@ -21,4 +21,13 @@ namespace osu.Game.Rulesets.Objects.Types
|
||||
/// </summary>
|
||||
List<List<SampleInfo>> RepeatSamples { get; }
|
||||
}
|
||||
|
||||
public static class HasRepeatsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount of times the length of this <see cref="IHasRepeats"/> spans.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object that has repeats.</param>
|
||||
public static int SpanCount(this IHasRepeats obj) => obj.RepeatCount + 1;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -36,8 +35,7 @@ namespace osu.Game.Rulesets.UI
|
||||
/// <param name="customWidth">Whether we want our internal coordinate system to be scaled to a specified width.</param>
|
||||
protected Playfield(float? customWidth = null)
|
||||
{
|
||||
// Default height since we force relative size axes
|
||||
Size = Vector2.One;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
AddInternal(ScaledContent = new ScaledContainer
|
||||
{
|
||||
@ -62,12 +60,6 @@ namespace osu.Game.Rulesets.UI
|
||||
Add(HitObjects);
|
||||
}
|
||||
|
||||
public override Axes RelativeSizeAxes
|
||||
{
|
||||
get { return Axes.Both; }
|
||||
set { throw new InvalidOperationException($@"{nameof(Playfield)}'s {nameof(RelativeSizeAxes)} should never be changed from {Axes.Both}"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield.
|
||||
/// </summary>
|
||||
|
@ -16,6 +16,9 @@ using System.Linq;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets.Configuration;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using OpenTK;
|
||||
@ -35,6 +38,11 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
public bool AspectAdjust = true;
|
||||
|
||||
/// <summary>
|
||||
/// The selected variant.
|
||||
/// </summary>
|
||||
public virtual int Variant => 0;
|
||||
|
||||
/// <summary>
|
||||
/// The input manager for this RulesetContainer.
|
||||
/// </summary>
|
||||
@ -65,6 +73,14 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
protected readonly Ruleset Ruleset;
|
||||
|
||||
private IRulesetConfigManager rulesetConfig;
|
||||
private OnScreenDisplay onScreenDisplay;
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
|
||||
/// <summary>
|
||||
/// A visual representation of a <see cref="Rulesets.Ruleset"/>.
|
||||
/// </summary>
|
||||
@ -77,6 +93,20 @@ namespace osu.Game.Rulesets.UI
|
||||
Cursor = CreateCursor();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OnScreenDisplay onScreenDisplay, SettingsStore settings)
|
||||
{
|
||||
this.onScreenDisplay = onScreenDisplay;
|
||||
|
||||
rulesetConfig = CreateConfig(Ruleset, settings);
|
||||
|
||||
if (rulesetConfig != null)
|
||||
{
|
||||
dependencies.Cache(rulesetConfig);
|
||||
onScreenDisplay?.BeginTracking(this, rulesetConfig);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract ScoreProcessor CreateScoreProcessor();
|
||||
|
||||
/// <summary>
|
||||
@ -110,11 +140,24 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
protected virtual CursorContainer CreateCursor() => null;
|
||||
|
||||
protected virtual IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => null;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Playfield.
|
||||
/// </summary>
|
||||
/// <returns>The Playfield.</returns>
|
||||
protected abstract Playfield CreatePlayfield();
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (rulesetConfig != null)
|
||||
{
|
||||
onScreenDisplay?.StopTracking(this, rulesetConfig);
|
||||
rulesetConfig = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -154,7 +197,7 @@ namespace osu.Game.Rulesets.UI
|
||||
/// <summary>
|
||||
/// Whether the specified beatmap is assumed to be specific to the current ruleset.
|
||||
/// </summary>
|
||||
protected readonly bool IsForCurrentRuleset;
|
||||
public readonly bool IsForCurrentRuleset;
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor<TObject>(this);
|
||||
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.UI
|
||||
public abstract class RulesetInputManager<T> : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler
|
||||
where T : struct
|
||||
{
|
||||
public class RulesetKeyBindingContainer : DatabasedKeyBindingInputManager<T>
|
||||
public class RulesetKeyBindingContainer : DatabasedKeyBindingContainer<T>
|
||||
{
|
||||
public RulesetKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
: base(ruleset, variant, unique)
|
||||
|
@ -41,6 +41,12 @@ namespace osu.Game.Screens.Play.BreaksOverlay
|
||||
private readonly InfoContainer info;
|
||||
private readonly ArrowsOverlay arrowsOverlay;
|
||||
|
||||
public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor)
|
||||
: this(letterboxing)
|
||||
{
|
||||
bindProcessor(scoreProcessor);
|
||||
}
|
||||
|
||||
public BreakOverlay(bool letterboxing)
|
||||
{
|
||||
this.letterboxing = letterboxing;
|
||||
@ -148,7 +154,7 @@ namespace osu.Game.Screens.Play.BreaksOverlay
|
||||
arrowsOverlay.Hide();
|
||||
}
|
||||
|
||||
public void BindProcessor(ScoreProcessor processor)
|
||||
private void bindProcessor(ScoreProcessor processor)
|
||||
{
|
||||
info.AccuracyDisplay.Current.BindTo(processor.Accuracy);
|
||||
info.GradeDisplay.Current.BindTo(processor.Rank);
|
||||
|
@ -6,6 +6,8 @@ using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
@ -39,7 +41,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private static bool hasShownNotificationOnce;
|
||||
|
||||
public HUDOverlay()
|
||||
public HUDOverlay(ScoreProcessor scoreProcessor, RulesetContainer rulesetContainer, DecoupleableInterpolatingFramedClock decoupledClock, WorkingBeatmap working, IAdjustableClock adjustableSourceClock)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
@ -59,6 +61,18 @@ namespace osu.Game.Screens.Play
|
||||
ReplaySettingsOverlay = CreateReplaySettingsOverlay(),
|
||||
}
|
||||
});
|
||||
|
||||
BindProcessor(scoreProcessor);
|
||||
BindRulesetContainer(rulesetContainer);
|
||||
|
||||
Progress.Objects = rulesetContainer.Objects;
|
||||
Progress.AudioClock = decoupledClock;
|
||||
Progress.AllowSeeking = rulesetContainer.HasReplayLoaded;
|
||||
Progress.OnSeek = pos => decoupledClock.Seek(pos);
|
||||
|
||||
ModDisplay.Current.BindTo(working.Mods);
|
||||
|
||||
ReplaySettingsOverlay.PlaybackSettings.AdjustableClock = adjustableSourceClock;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
@ -115,7 +129,7 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void BindRulesetContainer(RulesetContainer rulesetContainer)
|
||||
protected virtual void BindRulesetContainer(RulesetContainer rulesetContainer)
|
||||
{
|
||||
(rulesetContainer.KeyBindingInputManager as ICanAttachKeyCounter)?.Attach(KeyCounter);
|
||||
|
||||
@ -201,7 +215,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
protected virtual ReplaySettingsOverlay CreateReplaySettingsOverlay() => new ReplaySettingsOverlay();
|
||||
|
||||
public virtual void BindProcessor(ScoreProcessor processor)
|
||||
protected virtual void BindProcessor(ScoreProcessor processor)
|
||||
{
|
||||
ScoreCounter?.Current.BindTo(processor.TotalScore);
|
||||
AccuracyCounter?.Current.BindTo(processor.Accuracy);
|
||||
|
@ -1,18 +1,18 @@
|
||||
// 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.Input;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Allocation;
|
||||
using System;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Input.Bindings;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
public class HotkeyRetryOverlay : Container
|
||||
public class HotkeyRetryOverlay : Container, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
public Action Action;
|
||||
|
||||
@ -40,28 +40,20 @@ namespace osu.Game.Screens.Play
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
public bool OnPressed(GlobalAction action)
|
||||
{
|
||||
if (args.Repeat) return false;
|
||||
if (action != GlobalAction.QuickRetry) return false;
|
||||
|
||||
if (args.Key == Key.Tilde)
|
||||
{
|
||||
overlay.FadeIn(activate_delay, Easing.Out);
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnKeyDown(state, args);
|
||||
overlay.FadeIn(activate_delay, Easing.Out);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
|
||||
public bool OnReleased(GlobalAction action)
|
||||
{
|
||||
if (args.Key == Key.Tilde && !fired)
|
||||
{
|
||||
overlay.FadeOut(fadeout_delay, Easing.Out);
|
||||
return true;
|
||||
}
|
||||
if (action != GlobalAction.QuickRetry) return false;
|
||||
|
||||
return base.OnKeyUp(state, args);
|
||||
overlay.FadeOut(fadeout_delay, Easing.Out);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
|
@ -1,35 +1,35 @@
|
||||
// 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;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Screens.Play.BreaksOverlay;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Storyboards.Drawables;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
@ -79,7 +79,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
#endregion
|
||||
|
||||
private BreakOverlay breakOverlay;
|
||||
private Container storyboardContainer;
|
||||
private DrawableStoryboard storyboard;
|
||||
|
||||
@ -155,6 +154,8 @@ namespace osu.Game.Screens.Play
|
||||
userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
|
||||
userAudioOffset.TriggerChange();
|
||||
|
||||
scoreProcessor = RulesetContainer.CreateScoreProcessor();
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
storyboardContainer = new Container
|
||||
@ -170,13 +171,12 @@ namespace osu.Game.Screens.Play
|
||||
OnRetry = Restart,
|
||||
OnQuit = Exit,
|
||||
CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded,
|
||||
OnPause = () => {
|
||||
OnPause = () =>
|
||||
{
|
||||
pauseContainer.Retries = RestartCount;
|
||||
hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused;
|
||||
},
|
||||
OnResume = () => {
|
||||
hudOverlay.KeyCounter.IsCounting = true;
|
||||
},
|
||||
OnResume = () => hudOverlay.KeyCounter.IsCounting = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
@ -186,12 +186,12 @@ namespace osu.Game.Screens.Play
|
||||
Child = RulesetContainer,
|
||||
},
|
||||
new SkipButton(firstObjectTime) { AudioClock = decoupledClock },
|
||||
hudOverlay = new HUDOverlay
|
||||
hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, decoupledClock, working, adjustableSourceClock)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
},
|
||||
breakOverlay = new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks)
|
||||
new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, scoreProcessor)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -207,7 +207,8 @@ namespace osu.Game.Screens.Play
|
||||
},
|
||||
new HotkeyRetryOverlay
|
||||
{
|
||||
Action = () => {
|
||||
Action = () =>
|
||||
{
|
||||
if (!IsCurrentScreen) return;
|
||||
|
||||
//we want to hide the hitrenderer immediately (looks better).
|
||||
@ -218,24 +219,9 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
};
|
||||
|
||||
scoreProcessor = RulesetContainer.CreateScoreProcessor();
|
||||
|
||||
if (showStoryboard)
|
||||
initializeStoryboard(false);
|
||||
|
||||
hudOverlay.BindProcessor(scoreProcessor);
|
||||
hudOverlay.BindRulesetContainer(RulesetContainer);
|
||||
|
||||
hudOverlay.Progress.Objects = RulesetContainer.Objects;
|
||||
hudOverlay.Progress.AudioClock = decoupledClock;
|
||||
hudOverlay.Progress.OnSeek = pos => decoupledClock.Seek(pos);
|
||||
|
||||
hudOverlay.ModDisplay.Current.BindTo(working.Mods);
|
||||
|
||||
breakOverlay.BindProcessor(scoreProcessor);
|
||||
|
||||
hudOverlay.ReplaySettingsOverlay.PlaybackSettings.AdjustableClock = adjustableSourceClock;
|
||||
|
||||
// Bind ScoreProcessor to ourselves
|
||||
scoreProcessor.AllJudged += onCompletion;
|
||||
scoreProcessor.Failed += onFail;
|
||||
|
@ -13,6 +13,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Ranking;
|
||||
@ -47,10 +48,13 @@ namespace osu.Game.Screens.Select
|
||||
private SampleChannel sampleConfirm;
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay)
|
||||
private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay, OsuGame game)
|
||||
{
|
||||
sampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection");
|
||||
|
||||
if (game != null)
|
||||
modSelect.SelectedMods.BindTo(game.SelectedMods);
|
||||
|
||||
Footer.AddButton(@"mods", colours.Yellow, modSelect, Key.F1, float.MaxValue);
|
||||
|
||||
BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1);
|
||||
@ -121,6 +125,9 @@ namespace osu.Game.Screens.Select
|
||||
if (Beatmap.Value.Track != null)
|
||||
Beatmap.Value.Track.Looping = false;
|
||||
|
||||
Beatmap.Value.Mods.UnbindBindings();
|
||||
Beatmap.Value.Mods.Value = new Mod[] { };
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ namespace osu.Game.Screens.Select
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuGame osu, OsuColour colours)
|
||||
{
|
||||
dependencies.Cache(this);
|
||||
dependencies.CacheAs(this);
|
||||
|
||||
if (Footer != null)
|
||||
{
|
||||
|
@ -6,7 +6,7 @@ using osu.Framework.Platform;
|
||||
|
||||
namespace osu.Game.Screens.Tournament.Components
|
||||
{
|
||||
public class DrawingsConfigManager : ConfigManager<DrawingsConfig>
|
||||
public class DrawingsConfigManager : IniConfigManager<DrawingsConfig>
|
||||
{
|
||||
protected override string Filename => @"drawings.ini";
|
||||
|
||||
|
@ -91,6 +91,10 @@
|
||||
<Reference Include="Humanizer, Version=2.2.0.0, Culture=neutral, PublicKeyToken=979442b78dfc278e, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Humanizer.Core.2.2.0\lib\netstandard1.0\Humanizer.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="JetBrains.Annotations, Version=11.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Data.Sqlite, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Microsoft.Data.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.Data.Sqlite.dll</HintPath>
|
||||
</Reference>
|
||||
@ -265,10 +269,17 @@
|
||||
<Compile Include="Beatmaps\Formats\JsonBeatmapDecoder.cs" />
|
||||
<Compile Include="Beatmaps\Formats\LegacyDecoder.cs" />
|
||||
<Compile Include="Beatmaps\Formats\LegacyStoryboardDecoder.cs" />
|
||||
<Compile Include="Configuration\DatabasedSetting.cs" />
|
||||
<Compile Include="Configuration\SettingsStore.cs" />
|
||||
<Compile Include="Configuration\DatabasedConfigManager.cs" />
|
||||
<Compile Include="Configuration\SpeedChangeVisualisationMethod.cs" />
|
||||
<Compile Include="Database\DatabaseContextFactory.cs" />
|
||||
<Compile Include="Database\IHasPrimaryKey.cs" />
|
||||
<Compile Include="Graphics\Textures\LargeTextureStore.cs" />
|
||||
<Compile Include="Migrations\20180125143340_Settings.cs" />
|
||||
<Compile Include="Migrations\20180125143340_Settings.Designer.cs">
|
||||
<DependentUpon>20180125143340_Settings.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Overlays\Profile\SupporterIcon.cs" />
|
||||
<Compile Include="Online\API\Requests\GetFriendsRequest.cs" />
|
||||
<Compile Include="Overlays\Settings\DangerousSettingsButton.cs" />
|
||||
@ -314,6 +325,8 @@
|
||||
<Compile Include="Overlays\Profile\Sections\Ranks\ScoreModsContainer.cs" />
|
||||
<Compile Include="Overlays\Settings\Sections\Gameplay\ScrollingSettings.cs" />
|
||||
<Compile Include="Overlays\Settings\Sections\Maintenance\DeleteAllBeatmapsDialog.cs" />
|
||||
<Compile Include="Rulesets\Configuration\IRulesetConfigManager.cs" />
|
||||
<Compile Include="Rulesets\Configuration\RulesetConfigManager.cs" />
|
||||
<Compile Include="Rulesets\Mods\IApplicableFailOverride.cs" />
|
||||
<Compile Include="Rulesets\Mods\IApplicableMod.cs" />
|
||||
<Compile Include="Rulesets\Mods\IApplicableToBeatmapConverter.cs" />
|
||||
@ -433,8 +446,8 @@
|
||||
<Compile Include="Graphics\UserInterface\Volume\VolumeControlReceptor.cs" />
|
||||
<Compile Include="Graphics\UserInterface\Volume\VolumeMeter.cs" />
|
||||
<Compile Include="Input\Bindings\DatabasedKeyBinding.cs" />
|
||||
<Compile Include="Input\Bindings\DatabasedKeyBindingInputManager.cs" />
|
||||
<Compile Include="Input\Bindings\GlobalKeyBindingInputManager.cs" />
|
||||
<Compile Include="Input\Bindings\DatabasedKeyBindingContainer.cs" />
|
||||
<Compile Include="Input\Bindings\GlobalActionContainer.cs" />
|
||||
<Compile Include="Input\Handlers\ReplayInputHandler.cs" />
|
||||
<Compile Include="Input\KeyBindingStore.cs" />
|
||||
<Compile Include="IO\FileInfo.cs" />
|
||||
|
@ -12,12 +12,12 @@
|
||||
<description>click the circles. to the beat.</description>
|
||||
<summary>click the circles.</summary>
|
||||
<releaseNotes>testing</releaseNotes>
|
||||
<copyright>Copyright ppy Pty Ltd 2007-2017</copyright>
|
||||
<copyright>Copyright ppy Pty Ltd 2007-2018</copyright>
|
||||
<language>en-AU</language>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="*.exe" target="lib\net45\" exclude="**vshost**"/>
|
||||
<file src="*.dll" target="lib\net45\"/>
|
||||
<file src="*.dll" target="lib\net45\"/>
|
||||
<file src="*.config" target="lib\net45\"/>
|
||||
<file src="x86\*.dll" target="lib\net45\x86\"/>
|
||||
<file src="x64\*.dll" target="lib\net45\x64\"/>
|
||||
|
@ -47,6 +47,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
|
||||
<package id="Humanizer.Core.zh-CN" version="2.2.0" targetFramework="net461" />
|
||||
<package id="Humanizer.Core.zh-Hans" version="2.2.0" targetFramework="net461" />
|
||||
<package id="Humanizer.Core.zh-Hant" version="2.2.0" targetFramework="net461" />
|
||||
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
|
||||
<package id="Microsoft.CSharp" version="4.4.0" targetFramework="net461" />
|
||||
<package id="Microsoft.Data.Sqlite.Core" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.EntityFrameworkCore" version="2.0.0" targetFramework="net461" />
|
||||
|
Loading…
Reference in New Issue
Block a user