1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-16 07:32:34 +08:00

Compare commits

...

256 Commits

187 changed files with 2850 additions and 887 deletions
@@ -10,7 +10,7 @@ namespace osu.Desktop.VisualTests.Beatmaps
public class TestWorkingBeatmap : WorkingBeatmap
{
public TestWorkingBeatmap(Beatmap beatmap)
: base(beatmap.BeatmapInfo, beatmap.BeatmapInfo.BeatmapSet)
: base(beatmap.BeatmapInfo)
{
this.beatmap = beatmap;
}
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Screens.Select;
@@ -18,6 +18,7 @@ using osu.Game.Rulesets.Taiko.UI;
using System.Collections.Generic;
using osu.Desktop.VisualTests.Beatmaps;
using osu.Framework.Allocation;
using osu.Game.Beatmaps.Timing;
namespace osu.Desktop.VisualTests.Tests
{
@@ -52,6 +53,12 @@ namespace osu.Desktop.VisualTests.Tests
time += RNG.Next(50, 500);
}
TimingInfo timing = new TimingInfo();
timing.ControlPoints.Add(new ControlPoint
{
BeatLength = 200
});
WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap
{
HitObjects = objects,
@@ -64,8 +71,9 @@ namespace osu.Desktop.VisualTests.Tests
Artist = @"Unknown",
Title = @"Sample Beatmap",
Author = @"peppy",
}
}
},
},
TimingInfo = timing
});
Add(new Drawable[]
@@ -0,0 +1,89 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using OpenTK.Graphics;
using OpenTK;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseManiaHitObjects : TestCase
{
public override void Reset()
{
base.Reset();
Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
// Imagine that the containers containing the drawable notes are the "columns"
Children = new Drawable[]
{
new Container
{
Name = "Normal note column",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Width = 50,
Children = new[]
{
new Container
{
Name = "Timing section",
RelativeSizeAxes = Axes.Both,
RelativeCoordinateSpace = new Vector2(1, 10000),
Children = new[]
{
new DrawableNote(new Note
{
StartTime = 5000
})
{
AccentColour = Color4.Red
}
}
}
}
},
new Container
{
Name = "Hold note column",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Width = 50,
Children = new[]
{
new Container
{
Name = "Timing section",
RelativeSizeAxes = Axes.Both,
RelativeCoordinateSpace = new Vector2(1, 10000),
Children = new[]
{
new DrawableHoldNote(new HoldNote
{
StartTime = 5000,
Duration = 1000
})
{
AccentColour = Color4.Red
}
}
}
}
}
}
});
}
}
}
@@ -1,13 +1,16 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Input;
using osu.Framework.Testing;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.UI;
using System.Linq;
using System;
using System.Collections.Generic;
using osu.Game.Beatmaps.Timing;
using OpenTK;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects;
namespace osu.Desktop.VisualTests.Tests
{
@@ -21,33 +24,58 @@ namespace osu.Desktop.VisualTests.Tests
{
base.Reset();
const int max_columns = 10;
Action<int, SpecialColumnPosition> createPlayfield = (cols, pos) =>
{
Clear();
Add(new ManiaPlayfield(cols)
Add(new ManiaPlayfield(cols, new List<ControlPoint>())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
SpecialColumnPosition = pos
SpecialColumnPosition = pos,
Scale = new Vector2(1, -1)
});
};
for (int i = 1; i <= max_columns; i++)
Action<int, SpecialColumnPosition> createPlayfieldWithNotes = (cols, pos) =>
{
int tempI = i;
Clear();
AddStep($"{i} column" + (i > 1 ? "s" : ""), () => createPlayfield(tempI, SpecialColumnPosition.Normal));
ManiaPlayfield playField;
Add(playField = new ManiaPlayfield(cols, new List<ControlPoint> { new ControlPoint { BeatLength = 200 } })
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
SpecialColumnPosition = pos,
Scale = new Vector2(1, -1)
});
AddStep("Trigger keys down", () => ((ManiaPlayfield)Children.First()).Columns.Children.ForEach(triggerKeyDown));
AddStep("Trigger keys up", () => ((ManiaPlayfield)Children.First()).Columns.Children.ForEach(triggerKeyUp));
for (int i = 0; i < cols; i++)
{
playField.Add(new DrawableNote(new Note
{
StartTime = Time.Current + 1000,
Column = i
}));
}
};
AddStep("Left special style", () => createPlayfield(tempI, SpecialColumnPosition.Left));
AddStep("Right special style", () => createPlayfield(tempI, SpecialColumnPosition.Right));
}
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("Normal special style", () => createPlayfield(max_columns, SpecialColumnPosition.Normal));
AddStep("Normal special style", () => createPlayfield(4, SpecialColumnPosition.Normal));
AddStep("Notes", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Normal));
AddWaitStep(10);
AddStep("Left special style", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Left));
AddWaitStep(10);
AddStep("Right special style", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Right));
AddWaitStep(10);
}
private void triggerKeyDown(Column column)
@@ -0,0 +1,47 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Testing;
using osu.Game.Overlays;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseOnScreenDisplay : TestCase
{
private FrameworkConfigManager config;
private Bindable<FrameSync> frameSyncMode;
public override string Description => @"Make it easier to see setting changes";
public override void Reset()
{
base.Reset();
Add(new OnScreenDisplay());
frameSyncMode = config.GetBindable<FrameSync>(FrameworkSetting.FrameSync);
FrameSync initial = frameSyncMode.Value;
AddRepeatStep(@"Change frame limiter", setNextMode, 3);
AddStep(@"Restore frame limiter", () => frameSyncMode.Value = initial);
}
private void setNextMode()
{
var nextMode = frameSyncMode.Value + 1;
if (nextMode > FrameSync.Unlimited)
nextMode = FrameSync.VSync;
frameSyncMode.Value = nextMode;
}
[BackgroundDependencyLoader]
private void load(FrameworkConfigManager config)
{
this.config = config;
}
}
}
@@ -3,7 +3,6 @@
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
using osu.Framework.Testing;
@@ -6,18 +6,18 @@ using osu.Game.Overlays;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseOptions : TestCase
internal class TestCaseSettings : TestCase
{
public override string Description => @"Tests the options overlay";
public override string Description => @"Tests the settings overlay";
private OptionsOverlay options;
private SettingsOverlay settings;
public override void Reset()
{
base.Reset();
Children = new[] { options = new OptionsOverlay() };
options.ToggleVisibility();
Children = new[] { settings = new SettingsOverlay() };
settings.ToggleVisibility();
}
}
}
@@ -18,10 +18,14 @@ namespace osu.Desktop.VisualTests.Tests
private SongProgress progress;
private SongProgressGraph graph;
private StopwatchClock clock;
public override void Reset()
{
base.Reset();
clock = new StopwatchClock(true);
Add(progress = new SongProgress
{
RelativeSizeAxes = Axes.X,
@@ -55,6 +59,9 @@ namespace osu.Desktop.VisualTests.Tests
progress.Objects = objects;
graph.Objects = objects;
progress.AudioClock = clock;
progress.OnSeek = pos => clock.Seek(pos);
}
}
}
@@ -1,8 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using OpenTK;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Testing;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
@@ -190,10 +190,12 @@
<Compile Include="Tests\TestCaseDrawings.cs" />
<Compile Include="Tests\TestCaseGamefield.cs" />
<Compile Include="Tests\TestCaseGraph.cs" />
<Compile Include="Tests\TestCaseManiaHitObjects.cs" />
<Compile Include="Tests\TestCaseManiaPlayfield.cs" />
<Compile Include="Tests\TestCaseMenuOverlays.cs" />
<Compile Include="Tests\TestCaseMusicController.cs" />
<Compile Include="Tests\TestCaseNotificationManager.cs" />
<Compile Include="Tests\TestCaseOnScreenDisplay.cs" />
<Compile Include="Tests\TestCasePlayer.cs" />
<Compile Include="Tests\TestCaseHitObjects.cs" />
<Compile Include="Tests\TestCaseKeyCounter.cs" />
@@ -210,7 +212,7 @@
<Compile Include="Tests\TestCaseTwoLayerButton.cs" />
<Compile Include="VisualTestGame.cs" />
<Compile Include="Platform\TestStorage.cs" />
<Compile Include="Tests\TestCaseOptions.cs" />
<Compile Include="Tests\TestCaseSettings.cs" />
<Compile Include="Tests\TestCaseSongProgress.cs" />
<Compile Include="Tests\TestCaseMods.cs" />
<Compile Include="Tests\TestCaseDialogOverlay.cs" />
@@ -14,7 +14,7 @@ namespace osu.Desktop.Beatmaps.IO
{
public static void Register() => AddReader<LegacyFilesystemReader>((storage, path) => Directory.Exists(path));
private string basePath { get; }
private readonly string basePath;
public LegacyFilesystemReader(string path)
{
@@ -8,16 +8,28 @@ using System;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Objects;
using OpenTK;
namespace osu.Game.Rulesets.Mania.Beatmaps
{
internal class ManiaBeatmapConverter : BeatmapConverter<ManiaBaseHit>
internal class ManiaBeatmapConverter : BeatmapConverter<ManiaHitObject>
{
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
protected override IEnumerable<ManiaBaseHit> ConvertHitObject(HitObject original, Beatmap beatmap)
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
{
yield return null;
int availableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize);
var positionData = original as IHasXPosition;
float localWDivisor = 512.0f / availableColumns;
int column = MathHelper.Clamp((int)Math.Floor((positionData?.X ?? 1) / localWDivisor), 0, availableColumns - 1);
yield return new Note
{
StartTime = original.StartTime,
Column = column,
};
}
}
}
@@ -0,0 +1,179 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Database;
namespace osu.Game.Rulesets.Mania.Judgements
{
public class HitWindows
{
#region Constants
/// <summary>
/// PERFECT hit window at OD = 10.
/// </summary>
private const double perfect_min = 27.8;
/// <summary>
/// PERFECT hit window at OD = 5.
/// </summary>
private const double perfect_mid = 38.8;
/// <summary>
/// PERFECT hit window at OD = 0.
/// </summary>
private const double perfect_max = 44.8;
/// <summary>
/// GREAT hit window at OD = 10.
/// </summary>
private const double great_min = 68;
/// <summary>
/// GREAT hit window at OD = 5.
/// </summary>
private const double great_mid = 98;
/// <summary>
/// GREAT hit window at OD = 0.
/// </summary>
private const double great_max = 128;
/// <summary>
/// GOOD hit window at OD = 10.
/// </summary>
private const double good_min = 134;
/// <summary>
/// GOOD hit window at OD = 5.
/// </summary>
private const double good_mid = 164;
/// <summary>
/// GOOD hit window at OD = 0.
/// </summary>
private const double good_max = 194;
/// <summary>
/// OK hit window at OD = 10.
/// </summary>
private const double ok_min = 194;
/// <summary>
/// OK hit window at OD = 5.
/// </summary>
private const double ok_mid = 224;
/// <summary>
/// OK hit window at OD = 0.
/// </summary>
private const double ok_max = 254;
/// <summary>
/// BAD hit window at OD = 10.
/// </summary>
private const double bad_min = 242;
/// <summary>
/// BAD hit window at OD = 5.
/// </summary>
private const double bad_mid = 272;
/// <summary>
/// BAD hit window at OD = 0.
/// </summary>
private const double bad_max = 302;
/// <summary>
/// MISS hit window at OD = 10.
/// </summary>
private const double miss_min = 316;
/// <summary>
/// MISS hit window at OD = 5.
/// </summary>
private const double miss_mid = 346;
/// <summary>
/// MISS hit window at OD = 0.
/// </summary>
private const double miss_max = 376;
#endregion
/// <summary>
/// Hit window for a PERFECT hit.
/// </summary>
public double Perfect = perfect_mid;
/// <summary>
/// Hit window for a GREAT hit.
/// </summary>
public double Great = great_mid;
/// <summary>
/// Hit window for a GOOD hit.
/// </summary>
public double Good = good_mid;
/// <summary>
/// Hit window for an OK hit.
/// </summary>
public double Ok = ok_mid;
/// <summary>
/// Hit window for a BAD hit.
/// </summary>
public double Bad = bad_mid;
/// <summary>
/// Hit window for a MISS hit.
/// </summary>
public double Miss = miss_mid;
/// <summary>
/// Constructs default hit windows.
/// </summary>
public HitWindows()
{
}
/// <summary>
/// Constructs hit windows by fitting a parameter to a 2-part piecewise linear function for each hit window.
/// </summary>
/// <param name="difficulty">The parameter.</param>
public HitWindows(double difficulty)
{
Perfect = BeatmapDifficulty.DifficultyRange(difficulty, perfect_max, perfect_mid, perfect_min);
Great = BeatmapDifficulty.DifficultyRange(difficulty, great_max, great_mid, great_min);
Good = BeatmapDifficulty.DifficultyRange(difficulty, good_max, good_mid, good_min);
Ok = BeatmapDifficulty.DifficultyRange(difficulty, ok_max, ok_mid, ok_min);
Bad = BeatmapDifficulty.DifficultyRange(difficulty, bad_max, bad_mid, bad_min);
Miss = BeatmapDifficulty.DifficultyRange(difficulty, miss_max, miss_mid, miss_min);
}
/// <summary>
/// Constructs new hit windows which have been multiplied by a value.
/// </summary>
/// <param name="windows">The original hit windows.</param>
/// <param name="value">The value to multiply each hit window by.</param>
public static HitWindows operator *(HitWindows windows, double value)
{
return new HitWindows
{
Perfect = windows.Perfect * value,
Great = windows.Great * value,
Good = windows.Good * value,
Ok = windows.Ok * value,
Bad = windows.Bad * value,
Miss = windows.Miss * value
};
}
/// <summary>
/// Constructs new hit windows which have been divided by a value.
/// </summary>
/// <param name="windows">The original hit windows.</param>
/// <param name="value">The value to divide each hit window by.</param>
public static HitWindows operator /(HitWindows windows, double value)
{
return new HitWindows
{
Perfect = windows.Perfect / value,
Great = windows.Great / value,
Good = windows.Good / value,
Ok = windows.Ok / value,
Bad = windows.Bad / value,
Miss = windows.Miss / value
};
}
}
}
@@ -9,7 +9,7 @@ using System.Collections.Generic;
namespace osu.Game.Rulesets.Mania
{
public class ManiaDifficultyCalculator : DifficultyCalculator<ManiaBaseHit>
public class ManiaDifficultyCalculator : DifficultyCalculator<ManiaHitObject>
{
public ManiaDifficultyCalculator(Beatmap beatmap)
: base(beatmap)
@@ -21,6 +21,6 @@ namespace osu.Game.Rulesets.Mania
return 0;
}
protected override BeatmapConverter<ManiaBaseHit> CreateBeatmapConverter() => new ManiaBeatmapConverter();
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter();
}
}
@@ -1,36 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Graphics;
using OpenTK;
namespace osu.Game.Rulesets.Mania.Objects.Drawable
{
public class DrawableNote : Sprite
{
private readonly ManiaBaseHit note;
public DrawableNote(ManiaBaseHit note)
{
this.note = note;
Origin = Anchor.Centre;
Scale = new Vector2(0.1f);
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Texture = textures.Get(@"Menu/logo");
const double duration = 0;
Transforms.Add(new TransformPositionY { StartTime = note.StartTime - 200, EndTime = note.StartTime, StartValue = -0.1f, EndValue = 0.9f });
Transforms.Add(new TransformAlpha { StartTime = note.StartTime + duration + 200, EndTime = note.StartTime + duration + 400, StartValue = 1, EndValue = 0 });
Expire(true);
}
}
}
@@ -0,0 +1,65 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
public class DrawableHoldNote : DrawableManiaHitObject<HoldNote>
{
private readonly NotePiece headPiece;
private readonly BodyPiece bodyPiece;
private readonly NotePiece tailPiece;
public DrawableHoldNote(HoldNote hitObject)
: base(hitObject)
{
RelativeSizeAxes = Axes.Both;
Height = (float)HitObject.Duration;
Add(new Drawable[]
{
// For now the body piece covers the entire height of the container
// whereas possibly in the future we don't want to extend under the head/tail.
// This will be fixed when new designs are given or the current design is finalized.
bodyPiece = new BodyPiece
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
headPiece = new NotePiece
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
tailPiece = new NotePiece
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre
}
});
}
public override Color4 AccentColour
{
get { return base.AccentColour; }
set
{
if (base.AccentColour == value)
return;
base.AccentColour = value;
headPiece.AccentColour = value;
bodyPiece.AccentColour = value;
tailPiece.AccentColour = value;
}
}
protected override void UpdateState(ArmedState state)
{
}
}
}
@@ -0,0 +1,64 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
public abstract class DrawableManiaHitObject<TObject> : DrawableHitObject<ManiaHitObject, ManiaJudgement>
where TObject : ManiaHitObject
{
public new TObject HitObject;
private readonly Container glowContainer;
protected DrawableManiaHitObject(TObject hitObject)
: base(hitObject)
{
HitObject = hitObject;
RelativePositionAxes = Axes.Y;
Y = (float)HitObject.StartTime;
Add(glowContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
});
}
public override Color4 AccentColour
{
get { return base.AccentColour; }
set
{
if (base.AccentColour == value)
return;
base.AccentColour = value;
glowContainer.EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Radius = 5,
Colour = value
};
}
}
protected override ManiaJudgement CreateJudgement() => new ManiaJudgement();
}
}
@@ -0,0 +1,51 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
public class DrawableNote : DrawableManiaHitObject<Note>
{
private readonly NotePiece headPiece;
public DrawableNote(Note hitObject)
: base(hitObject)
{
RelativeSizeAxes = Axes.Both;
Height = 100;
Add(headPiece = new NotePiece
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
});
}
public override Color4 AccentColour
{
get { return base.AccentColour; }
set
{
if (base.AccentColour == value)
return;
base.AccentColour = value;
headPiece.AccentColour = value;
}
}
protected override void Update()
{
if (Time.Current > HitObject.StartTime)
Colour = Color4.Green;
}
protected override void UpdateState(ArmedState state)
{
}
}
}
@@ -0,0 +1,48 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
{
/// <summary>
/// Represents length-wise portion of a hold note.
/// </summary>
internal class BodyPiece : Container, IHasAccentColour
{
private readonly Box box;
public BodyPiece()
{
RelativeSizeAxes = Axes.Both;
Masking = true;
Children = new[]
{
box = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.3f
}
};
}
private Color4 accentColour;
public Color4 AccentColour
{
get { return accentColour; }
set
{
if (accentColour == value)
return;
accentColour = value;
box.Colour = accentColour;
}
}
}
}
@@ -0,0 +1,59 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
{
/// <summary>
/// Represents the static hit markers of notes.
/// </summary>
internal class NotePiece : Container, IHasAccentColour
{
private const float head_height = 10;
private const float head_colour_height = 6;
private readonly Box colouredBox;
public NotePiece()
{
RelativeSizeAxes = Axes.X;
Height = head_height;
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both
},
colouredBox = new Box
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
Height = head_colour_height,
Alpha = 0.2f
}
};
}
private Color4 accentColour;
public Color4 AccentColour
{
get { return accentColour; }
set
{
if (accentColour == value)
return;
accentColour = value;
colouredBox.Colour = AccentColour.Lighten(0.9f);
}
}
}
}
+29 -1
View File
@@ -1,9 +1,37 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps.Timing;
using osu.Game.Database;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Mania.Objects
{
public class HoldNote : Note
/// <summary>
/// Represents a hit object which requires pressing, holding, and releasing a key.
/// </summary>
public class HoldNote : Note, IHasEndTime
{
/// <summary>
/// Lenience of release hit windows. This is to make cases where the hold note release
/// is timed alongside presses of other hit objects less awkward.
/// </summary>
private const double release_window_lenience = 1.5;
public double Duration { get; set; }
public double EndTime => StartTime + Duration;
/// <summary>
/// The key-release hit windows for this hold note.
/// </summary>
protected HitWindows ReleaseHitWindows = new HitWindows();
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{
base.ApplyDefaults(timing, difficulty);
ReleaseHitWindows = HitWindows * release_window_lenience;
}
}
}
@@ -1,12 +1,13 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Mania.Objects.Types;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Objects
{
public abstract class ManiaBaseHit : HitObject
public abstract class ManiaHitObject : HitObject, IHasColumn
{
public int Column;
public int Column { get; set; }
}
}
+19 -1
View File
@@ -1,9 +1,27 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps.Timing;
using osu.Game.Database;
using osu.Game.Rulesets.Mania.Judgements;
namespace osu.Game.Rulesets.Mania.Objects
{
public class Note : ManiaBaseHit
/// <summary>
/// Represents a hit object which has a single hit press.
/// </summary>
public class Note : ManiaHitObject
{
/// <summary>
/// The key-press hit window for this note.
/// </summary>
protected HitWindows HitWindows = new HitWindows();
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{
base.ApplyDefaults(timing, difficulty);
HitWindows = new HitWindows(difficulty.OverallDifficulty);
}
}
}
@@ -0,0 +1,16 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Rulesets.Mania.Objects.Types
{
/// <summary>
/// A type of hit object which lies in one of a number of predetermined columns.
/// </summary>
public interface IHasColumn
{
/// <summary>
/// The column which the hit object lies in.
/// </summary>
int Column { get; }
}
}
@@ -8,13 +8,13 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Scoring
{
internal class ManiaScoreProcessor : ScoreProcessor<ManiaBaseHit, ManiaJudgement>
internal class ManiaScoreProcessor : ScoreProcessor<ManiaHitObject, ManiaJudgement>
{
public ManiaScoreProcessor()
{
}
public ManiaScoreProcessor(HitRenderer<ManiaBaseHit, ManiaJudgement> hitRenderer)
public ManiaScoreProcessor(HitRenderer<ManiaHitObject, ManiaJudgement> hitRenderer)
: base(hitRenderer)
{
}
@@ -0,0 +1,153 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using OpenTK;
using osu.Game.Beatmaps.Timing;
namespace osu.Game.Rulesets.Mania.Timing
{
/// <summary>
/// A container in which added drawables are put into a relative coordinate space spanned by a length of time.
/// <para>
/// This container contains <see cref="ControlPoint"/>s which scroll inside this container.
/// Drawables added to this container are moved inside the relevant <see cref="ControlPoint"/>,
/// and as such, will scroll along with the <see cref="ControlPoint"/>s.
/// </para>
/// </summary>
public class ControlPointContainer : Container<Drawable>
{
/// <summary>
/// The amount of time which this container spans.
/// </summary>
public double TimeSpan { get; set; }
private readonly List<DrawableControlPoint> drawableControlPoints;
public ControlPointContainer(IEnumerable<ControlPoint> timingChanges)
{
drawableControlPoints = timingChanges.Select(t => new DrawableControlPoint(t)).ToList();
Children = drawableControlPoints;
}
/// <summary>
/// Adds a drawable to this container. Note that the drawable added must have its Y-position be
/// an absolute unit of time that is _not_ relative to <see cref="TimeSpan"/>.
/// </summary>
/// <param name="drawable">The drawable to add.</param>
public override void Add(Drawable drawable)
{
// Always add timing sections to ourselves
if (drawable is DrawableControlPoint)
{
base.Add(drawable);
return;
}
var controlPoint = drawableControlPoints.LastOrDefault(t => t.CanContain(drawable)) ?? drawableControlPoints.FirstOrDefault();
if (controlPoint == null)
throw new Exception("Could not find suitable timing section to add object to.");
controlPoint.Add(drawable);
}
/// <summary>
/// A container that contains drawables within the time span of a timing section.
/// <para>
/// The content of this container will scroll relative to the current time.
/// </para>
/// </summary>
private class DrawableControlPoint : Container
{
private readonly ControlPoint timingChange;
protected override Container<Drawable> Content => content;
private readonly Container content;
/// <summary>
/// Creates a drawable control point. The height of this container will be proportional
/// to the beat length of the control point it is initialized with such that, e.g. a beat length
/// of 500ms results in this container being twice as high as its parent, which further means that
/// the content container will scroll at twice the normal rate.
/// </summary>
/// <param name="timingChange">The control point to create the drawable control point for.</param>
public DrawableControlPoint(ControlPoint timingChange)
{
this.timingChange = timingChange;
RelativeSizeAxes = Axes.Both;
AddInternal(content = new AutoTimeRelativeContainer
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Y = (float)timingChange.Time
});
}
protected override void Update()
{
var parent = (ControlPointContainer)Parent;
// Adjust our height to account for the speed changes
Height = (float)(1000 / timingChange.BeatLength / timingChange.SpeedMultiplier);
RelativeCoordinateSpace = new Vector2(1, (float)parent.TimeSpan);
// Scroll the content
content.Y = (float)(timingChange.Time - Time.Current);
}
public override void Add(Drawable drawable)
{
// The previously relatively-positioned drawable will now become relative to content, but since the drawable has no knowledge of content,
// we need to offset it back by content's position position so that it becomes correctly relatively-positioned to content
// This can be removed if hit objects were stored such that either their StartTime or their "beat offset" was relative to the timing change
// they belonged to, but this requires a radical change to the beatmap format which we're not ready to do just yet
drawable.Y -= (float)timingChange.Time;
base.Add(drawable);
}
/// <summary>
/// Whether this control point can contain a drawable. This control point can contain a drawable if the drawable is positioned "after" this control point.
/// </summary>
/// <param name="drawable">The drawable to check.</param>
public bool CanContain(Drawable drawable) => content.Y <= drawable.Y;
/// <summary>
/// A container which always keeps its height and relative coordinate space "auto-sized" to its children.
/// <para>
/// This is used in the case where children are relatively positioned/sized to time values (e.g. notes/bar lines) to keep
/// such children wrapped inside a container, otherwise they would disappear due to container flattening.
/// </para>
/// </summary>
private class AutoTimeRelativeContainer : Container
{
public override void InvalidateFromChild(Invalidation invalidation)
{
// We only want to re-compute our size when a child's size or position has changed
if ((invalidation & Invalidation.Geometry) == 0)
{
base.InvalidateFromChild(invalidation);
return;
}
if (!Children.Any())
return;
float height = Children.Select(child => child.Y + child.Height).Max();
Height = height;
RelativeCoordinateSpace = new Vector2(1, height);
base.InvalidateFromChild(invalidation);
}
}
}
}
}
+64 -55
View File
@@ -12,13 +12,17 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Colour;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Timing;
using System.Collections.Generic;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Beatmaps.Timing;
namespace osu.Game.Rulesets.Mania.UI
{
public class Column : Container, IHasAccentColour
{
private const float key_size = 50;
private const float key_icon_size = 10;
private const float key_icon_corner_radius = 3;
private const float key_icon_border_radius = 2;
@@ -35,7 +39,9 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly Container hitTargetBar;
private readonly Container keyIcon;
public Column()
public readonly ControlPointContainer ControlPointContainer;
public Column(IEnumerable<ControlPoint> timingChanges)
{
RelativeSizeAxes = Axes.Y;
Width = column_width;
@@ -48,59 +54,16 @@ namespace osu.Game.Rulesets.Mania.UI
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f
},
new FillFlowContainer
new Container
{
Name = "Key + hit target",
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new[]
Name = "Hit target + hit objects",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = ManiaPlayfield.HIT_TARGET_POSITION},
Children = new Drawable[]
{
new Container
{
Name = "Key",
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Height = key_size,
Children = new Drawable[]
{
new Box
{
Name = "Key gradient",
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0)),
Alpha = 0.5f
},
keyIcon = new Container
{
Name = "Key icon",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(key_icon_size),
Masking = true,
CornerRadius = key_icon_corner_radius,
BorderThickness = 2,
BorderColour = Color4.White, // Not true
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
}
}
},
new Container
{
Name = "Hit target",
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Height = hit_target_height,
Children = new Drawable[]
@@ -114,8 +77,6 @@ namespace osu.Game.Rulesets.Mania.UI
hitTargetBar = new Container
{
Name = "Bar",
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Height = hit_target_bar_height,
Masking = true,
@@ -128,6 +89,47 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
}
},
ControlPointContainer = new ControlPointContainer(timingChanges)
{
Name = "Hit objects",
RelativeSizeAxes = Axes.Both,
},
}
},
new Container
{
Name = "Key",
RelativeSizeAxes = Axes.X,
Height = ManiaPlayfield.HIT_TARGET_POSITION,
Children = new Drawable[]
{
new Box
{
Name = "Key gradient",
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0)),
Alpha = 0.5f
},
keyIcon = new Container
{
Name = "Key icon",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(key_icon_size),
Masking = true,
CornerRadius = key_icon_corner_radius,
BorderThickness = 2,
BorderColour = Color4.White, // Not true
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
}
}
}
@@ -176,9 +178,17 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
public void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> hitObject)
{
ControlPointContainer.Add(hitObject);
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Key == Key && !args.Repeat)
if (args.Repeat)
return false;
if (args.Key == Key)
{
background.FadeTo(background.Alpha + 0.2f, 50, EasingTypes.OutQuint);
keyIcon.ScaleTo(1.4f, 50, EasingTypes.OutQuint);
@@ -198,5 +208,4 @@ namespace osu.Game.Rulesets.Mania.UI
return false;
}
}
}
+61 -7
View File
@@ -1,34 +1,88 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using OpenTK;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Beatmaps;
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;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.UI
{
public class ManiaHitRenderer : HitRenderer<ManiaBaseHit, ManiaJudgement>
public class ManiaHitRenderer : HitRenderer<ManiaHitObject, ManiaJudgement>
{
private readonly int columns;
public int? Columns;
public ManiaHitRenderer(WorkingBeatmap beatmap, int columns = 5)
public ManiaHitRenderer(WorkingBeatmap beatmap)
: base(beatmap)
{
this.columns = columns;
}
protected override Playfield<ManiaHitObject, ManiaJudgement> CreatePlayfield()
{
ControlPoint firstTimingChange = Beatmap.TimingInfo.ControlPoints.FirstOrDefault(t => t.TimingChange);
if (firstTimingChange == null)
throw new Exception("The Beatmap contains no timing points!");
// Generate the timing points, making non-timing changes use the previous timing change
var timingChanges = Beatmap.TimingInfo.ControlPoints.Select(c =>
{
ControlPoint t = c.Clone();
if (c.TimingChange)
firstTimingChange = c;
else
t.BeatLength = firstTimingChange.BeatLength;
return t;
});
double lastObjectTime = (Objects.Last() as IHasEndTime)?.EndTime ?? Objects.Last().StartTime;
// Perform some post processing of the timing changes
timingChanges = timingChanges
// Collapse sections after the last hit object
.Where(s => s.Time <= lastObjectTime)
// Collapse sections with the same start time
.GroupBy(s => s.Time).Select(g => g.Last()).OrderBy(s => s.Time)
// Collapse sections with the same beat length
.GroupBy(s => s.BeatLength * s.SpeedMultiplier).Select(g => g.First())
.ToList();
return new ManiaPlayfield(Columns ?? (int)Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize), timingChanges)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
// Invert by default for now (should be moved to config/skin later)
Scale = new Vector2(1, -1)
};
}
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
protected override BeatmapConverter<ManiaBaseHit> CreateBeatmapConverter() => new ManiaBeatmapConverter();
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter();
protected override Playfield<ManiaBaseHit, ManiaJudgement> CreatePlayfield() => new ManiaPlayfield(columns);
protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h)
{
var note = h as Note;
if (note != null)
return new DrawableNote(note);
protected override DrawableHitObject<ManiaBaseHit, ManiaJudgement> GetVisualRepresentation(ManiaBaseHit h) => null;
return null;
}
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f);
}
}
+106 -6
View File
@@ -10,17 +10,30 @@ using OpenTK.Graphics;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Graphics.Containers;
using System;
using osu.Framework.Graphics.Primitives;
using osu.Game.Graphics;
using osu.Framework.Allocation;
using OpenTK.Input;
using System.Linq;
using System.Collections.Generic;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Timing;
using osu.Framework.Input;
using osu.Game.Beatmaps.Timing;
using osu.Framework.Graphics.Transforms;
using osu.Framework.MathUtils;
namespace osu.Game.Rulesets.Mania.UI
{
public class ManiaPlayfield : Playfield<ManiaBaseHit, ManiaJudgement>
public class ManiaPlayfield : Playfield<ManiaHitObject, ManiaJudgement>
{
public const float HIT_TARGET_POSITION = 50;
private const float time_span_default = 5000;
private const float time_span_min = 10;
private const float time_span_max = 50000;
private const float time_span_step = 200;
/// <summary>
/// Default column keys, expanding outwards from the middle as more column are added.
/// E.g. 2 columns use FJ, 4 columns use DFJK, 6 use SDFJKL, etc...
@@ -44,12 +57,14 @@ namespace osu.Game.Rulesets.Mania.UI
public readonly FlowContainer<Column> Columns;
private readonly ControlPointContainer barlineContainer;
private List<Color4> normalColumnColours = new List<Color4>();
private Color4 specialColumnColour;
private readonly int columnCount;
public ManiaPlayfield(int columnCount)
public ManiaPlayfield(int columnCount, IEnumerable<ControlPoint> timingChanges)
{
this.columnCount = columnCount;
@@ -60,10 +75,11 @@ namespace osu.Game.Rulesets.Mania.UI
{
new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Masking = true,
Children = new Drawable[]
{
new Box
@@ -73,18 +89,34 @@ namespace osu.Game.Rulesets.Mania.UI
},
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
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = HIT_TARGET_POSITION },
Children = new[]
{
barlineContainer = new ControlPointContainer(timingChanges)
{
Name = "Bar lines",
RelativeSizeAxes = Axes.Both,
}
}
}
}
}
};
for (int i = 0; i < columnCount; i++)
Columns.Add(new Column());
Columns.Add(new Column(timingChanges));
TimeSpan = time_span_default;
}
[BackgroundDependencyLoader]
@@ -156,5 +188,73 @@ namespace osu.Game.Rulesets.Mania.UI
return column == columnCount - 1;
}
}
public override void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> h) => Columns.Children.ElementAt(h.HitObject.Column).Add(h);
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (state.Keyboard.ControlPressed)
{
switch (args.Key)
{
case Key.Minus:
transformTimeSpanTo(TimeSpan + time_span_step, 200, EasingTypes.OutQuint);
break;
case Key.Plus:
transformTimeSpanTo(TimeSpan - time_span_step, 200, EasingTypes.OutQuint);
break;
}
}
return false;
}
private double timeSpan;
/// <summary>
/// The amount of time which the length of the playfield spans.
/// </summary>
public double TimeSpan
{
get { return timeSpan; }
set
{
if (timeSpan == value)
return;
timeSpan = value;
timeSpan = MathHelper.Clamp(timeSpan, time_span_min, time_span_max);
barlineContainer.TimeSpan = value;
Columns.Children.ForEach(c => c.ControlPointContainer.TimeSpan = value);
}
}
private void transformTimeSpanTo(double newTimeSpan, double duration = 0, EasingTypes easing = EasingTypes.None)
{
TransformTo(() => TimeSpan, newTimeSpan, duration, easing, new TransformTimeSpan());
}
private class TransformTimeSpan : Transform<double>
{
public override double CurrentValue
{
get
{
double time = Time?.Current ?? 0;
if (time < StartTime) return StartValue;
if (time >= EndTime) return EndValue;
return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
}
}
public override void Apply(Drawable d)
{
base.Apply(d);
var p = (ManiaPlayfield)d;
p.TimeSpan = CurrentValue;
}
}
}
}
@@ -48,12 +48,18 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Beatmaps\ManiaBeatmapConverter.cs" />
<Compile Include="Judgements\HitWindows.cs" />
<Compile Include="Judgements\ManiaJudgement.cs" />
<Compile Include="ManiaDifficultyCalculator.cs" />
<Compile Include="Objects\Drawables\DrawableHoldNote.cs" />
<Compile Include="Objects\Drawables\DrawableManiaHitObject.cs" />
<Compile Include="Objects\Drawables\DrawableNote.cs" />
<Compile Include="Objects\Drawables\Pieces\BodyPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\NotePiece.cs" />
<Compile Include="Objects\Types\IHasColumn.cs" />
<Compile Include="Scoring\ManiaScoreProcessor.cs" />
<Compile Include="Objects\Drawable\DrawableNote.cs" />
<Compile Include="Objects\HoldNote.cs" />
<Compile Include="Objects\ManiaBaseHit.cs" />
<Compile Include="Objects\ManiaHitObject.cs" />
<Compile Include="Objects\Note.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UI\Column.cs" />
@@ -62,6 +68,7 @@
<Compile Include="ManiaRuleset.cs" />
<Compile Include="Mods\ManiaMod.cs" />
<Compile Include="UI\SpecialColumnPosition.cs" />
<Compile Include="Timing\ControlPointContainer.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
+1 -1
View File
@@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModAutoplay : ModAutoplay<OsuHitObject>
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray();
protected override Score CreateReplayScore(Beatmap<OsuHitObject> beatmap) => new Score
{
@@ -108,11 +108,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private Vector2 scaleToCircle => circle.Scale * circle.DrawWidth / DrawWidth * 0.95f;
private const float spins_per_minute_needed = 100 + 5 * 15; //TODO: read per-map OD and place it on the 5
private float rotationsNeeded => (float)(spins_per_minute_needed * (spinner.EndTime - spinner.StartTime) / 60000f);
public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / rotationsNeeded, 0, 1);
public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1);
protected override void UpdatePreemptState()
{
@@ -97,8 +97,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
snakingIn = config.GetBindable<bool>(OsuConfig.SnakingInSliders);
snakingOut = config.GetBindable<bool>(OsuConfig.SnakingOutSliders);
snakingIn = config.GetBindable<bool>(OsuSetting.SnakingInSliders);
snakingOut = config.GetBindable<bool>(OsuSetting.SnakingOutSliders);
reloadTexture();
}
+14
View File
@@ -2,6 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.Timing;
using osu.Game.Database;
namespace osu.Game.Rulesets.Osu.Objects
{
@@ -10,6 +12,18 @@ namespace osu.Game.Rulesets.Osu.Objects
public double EndTime { get; set; }
public double Duration => EndTime - StartTime;
/// <summary>
/// Number of spins required to finish the spinner without miss.
/// </summary>
public int SpinsRequired { get; protected set; } = 1;
public override bool NewCombo => true;
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{
base.ApplyDefaults(timing, difficulty);
SpinsRequired = (int)(Duration / 1000 * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5));
}
}
}
@@ -58,8 +58,8 @@ namespace osu.Game.Rulesets.Osu.Replays
{
public int Compare(ReplayFrame f1, ReplayFrame f2)
{
if (f1 == null) throw new NullReferenceException($@"{nameof(f1)} cannot be null");
if (f2 == null) throw new NullReferenceException($@"{nameof(f2)} cannot be null");
if (f1 == null) throw new ArgumentNullException(nameof(f1));
if (f2 == null) throw new ArgumentNullException(nameof(f2));
return f1.Time.CompareTo(f2.Time);
}
@@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
protected override Beatmap<TaikoHitObject> ConvertBeatmap(Beatmap original)
{
// Rewrite the beatmap info to add the slider velocity multiplier
BeatmapInfo info = original.BeatmapInfo.DeepClone<BeatmapInfo>();
BeatmapInfo info = original.BeatmapInfo.DeepClone();
info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier;
Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original);
@@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
{
@@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
Frames.Add(new ReplayFrame(h.StartTime, null, null, button));
}
else
throw new Exception("Unknown hit object type.");
throw new InvalidOperationException("Unknown hit object type.");
Frames.Add(new ReplayFrame(endTime + KEY_UP_DELAY, null, null, ReplayButtonState.None));
+21 -22
View File
@@ -13,7 +13,6 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics.Primitives;
using System.Linq;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using System;
@@ -54,33 +53,33 @@ namespace osu.Game.Rulesets.Taiko.UI
{
AddInternal(new Drawable[]
{
rightBackgroundContainer = new Container
{
Name = "Transparent playfield background",
RelativeSizeAxes = Axes.Both,
BorderThickness = 2,
Masking = true,
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.2f),
Radius = 5,
},
Children = new Drawable[]
{
rightBackground = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.6f
},
}
},
new ScaleFixContainer
{
RelativeSizeAxes = Axes.X,
Height = DEFAULT_PLAYFIELD_HEIGHT,
Children = new[]
{
rightBackgroundContainer = new Container
{
Name = "Transparent playfield background",
RelativeSizeAxes = Axes.Both,
BorderThickness = 2,
Masking = true,
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.2f),
Radius = 5,
},
Children = new Drawable[]
{
rightBackground = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.6f
},
}
},
new Container
{
Name = "Transparent playfield elements",
@@ -135,14 +135,13 @@ namespace osu.Game.Tests.Beatmaps.IO
waitAction = () =>
{
while ((resultBeatmaps = host.Dependencies.Get<BeatmapDatabase>()
.Query<BeatmapInfo>().Where(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0)).Count() != 12)
.GetAllWithChildren<BeatmapInfo>(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0)).Count() != 12)
Thread.Sleep(50);
};
Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout),
@"Beatmaps did not import to the database in allocated time");
//fetch children and check we can load from the post-storage path...
var set = host.Dependencies.Get<BeatmapDatabase>().GetChildren(resultSets.First());
Assert.IsTrue(set.Beatmaps.Count == resultBeatmaps.Count(),
+1 -2
View File
@@ -11,9 +11,8 @@ namespace osu.Game.Audio
{
}
public SampleInfoList(IEnumerable<SampleInfo> elements)
public SampleInfoList(IEnumerable<SampleInfo> elements) : base(elements)
{
AddRange(elements);
}
}
}
@@ -5,7 +5,6 @@ using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Game.Database;
using osu.Game.Graphics;
@@ -9,7 +9,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites;
@@ -51,14 +50,12 @@ namespace osu.Game.Beatmaps.Drawables
title = new OsuSpriteText
{
Font = @"Exo2.0-BoldItalic",
Text = beatmap.BeatmapSetInfo.Metadata.Title,
TextSize = 22,
Shadow = true,
},
artist = new OsuSpriteText
{
Font = @"Exo2.0-SemiBoldItalic",
Text = beatmap.BeatmapSetInfo.Metadata.Artist,
TextSize = 17,
Shadow = true,
},
@@ -81,8 +78,8 @@ namespace osu.Game.Beatmaps.Drawables
[BackgroundDependencyLoader]
private void load(LocalisationEngine localisation)
{
title.Current = localisation.GetUnicodePreference(beatmap.BeatmapSetInfo.Metadata.TitleUnicode, beatmap.BeatmapSetInfo.Metadata.Title);
artist.Current = localisation.GetUnicodePreference(beatmap.BeatmapSetInfo.Metadata.ArtistUnicode, beatmap.BeatmapSetInfo.Metadata.Artist);
title.Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title);
artist.Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist);
}
private class PanelBackground : BufferedContainer
+1 -1
View File
@@ -11,7 +11,7 @@ namespace osu.Game.Beatmaps.Formats
{
public abstract class BeatmapDecoder
{
private static Dictionary<string, Type> decoders { get; } = new Dictionary<string, Type>();
private static readonly Dictionary<string, Type> decoders = new Dictionary<string, Type>();
public static BeatmapDecoder GetDecoder(StreamReader stream)
{
+6 -8
View File
@@ -13,11 +13,11 @@ namespace osu.Game.Beatmaps.IO
{
private class Reader
{
public Func<Storage, string, bool> Test { get; set; }
public Type Type { get; set; }
public Func<Storage, string, bool> Test;
public Type Type;
}
private static List<Reader> readers { get; } = new List<Reader>();
private static readonly List<Reader> readers = new List<Reader>();
public static ArchiveReader GetReader(Storage storage, string path)
{
@@ -58,11 +58,9 @@ namespace osu.Game.Beatmaps.IO
if (input == null)
return null;
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
byte[] buffer = new byte[input.Length];
input.Read(buffer, 0, buffer.Length);
return buffer;
}
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ namespace osu.Game.Beatmaps.Timing
{
public string SampleBank;
public int SampleVolume;
public TimeSignatures TimeSignature;
public TimeSignatures TimeSignature = TimeSignatures.SimpleQuadruple;
public double Time;
public double BeatLength = 500;
public double SpeedMultiplier = 1;
+5 -2
View File
@@ -18,14 +18,17 @@ namespace osu.Game.Beatmaps
public readonly BeatmapSetInfo BeatmapSetInfo;
public readonly BeatmapMetadata Metadata;
public readonly Bindable<IEnumerable<Mod>> Mods = new Bindable<IEnumerable<Mod>>(new Mod[] { });
public readonly bool WithStoryboard;
protected WorkingBeatmap(BeatmapInfo beatmapInfo, BeatmapSetInfo beatmapSetInfo, bool withStoryboard = false)
protected WorkingBeatmap(BeatmapInfo beatmapInfo, bool withStoryboard = false)
{
BeatmapInfo = beatmapInfo;
BeatmapSetInfo = beatmapSetInfo;
BeatmapSetInfo = beatmapInfo.BeatmapSet;
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo.Metadata;
WithStoryboard = withStoryboard;
Mods.ValueChanged += mods => applyRateAdjustments();
+34 -31
View File
@@ -3,73 +3,75 @@
using osu.Framework.Configuration;
using osu.Framework.Platform;
using osu.Game.Overlays;
using osu.Game.Screens.Select;
namespace osu.Game.Configuration
{
public class OsuConfigManager : ConfigManager<OsuConfig>
public class OsuConfigManager : ConfigManager<OsuSetting>
{
protected override void InitialiseDefaults()
{
// UI/selection defaults
Set(OsuConfig.Ruleset, 0, 0, int.MaxValue);
Set(OsuConfig.BeatmapDetailTab, BeatmapDetailTab.Details);
Set(OsuSetting.Ruleset, 0, 0, int.MaxValue);
Set(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Details);
Set(OsuConfig.DisplayStarsMinimum, 0.0, 0, 10);
Set(OsuConfig.DisplayStarsMaximum, 10.0, 0, 10);
Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10);
Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10);
Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1);
// Online settings
Set(OsuConfig.Username, string.Empty);
Set(OsuConfig.Token, string.Empty);
Set(OsuSetting.Username, string.Empty);
Set(OsuSetting.Token, string.Empty);
Set(OsuConfig.SavePassword, false).ValueChanged += val =>
Set(OsuSetting.SavePassword, false).ValueChanged += val =>
{
if (val) Set(OsuConfig.SaveUsername, true);
if (val) Set(OsuSetting.SaveUsername, true);
};
Set(OsuConfig.SaveUsername, true).ValueChanged += val =>
Set(OsuSetting.SaveUsername, true).ValueChanged += val =>
{
if (!val) Set(OsuConfig.SavePassword, false);
if (!val) Set(OsuSetting.SavePassword, false);
};
// Audio
Set(OsuConfig.AudioDevice, string.Empty);
Set(OsuSetting.MenuVoice, true);
Set(OsuSetting.MenuMusic, true);
Set(OsuConfig.MenuVoice, true);
Set(OsuConfig.MenuMusic, true);
Set(OsuConfig.AudioOffset, 0, -500.0, 500.0);
Set(OsuSetting.AudioOffset, 0, -500.0, 500.0);
// Input
Set(OsuConfig.MenuCursorSize, 1.0, 0.5f, 2);
Set(OsuConfig.GameplayCursorSize, 1.0, 0.5f, 2);
Set(OsuSetting.MenuCursorSize, 1.0, 0.5f, 2);
Set(OsuSetting.GameplayCursorSize, 1.0, 0.5f, 2);
Set(OsuSetting.AutoCursorSize, false);
Set(OsuConfig.MouseDisableButtons, false);
Set(OsuConfig.MouseDisableWheel, false);
Set(OsuSetting.MouseDisableButtons, false);
Set(OsuSetting.MouseDisableWheel, false);
// Graphics
Set(OsuConfig.ShowFpsDisplay, false);
Set(OsuSetting.ShowFpsDisplay, false);
Set(OsuConfig.MenuParallax, true);
Set(OsuSetting.MenuParallax, true);
Set(OsuConfig.SnakingInSliders, true);
Set(OsuConfig.SnakingOutSliders, true);
Set(OsuSetting.SnakingInSliders, true);
Set(OsuSetting.SnakingOutSliders, true);
// Gameplay
Set(OsuConfig.DimLevel, 0.3, 0, 1);
Set(OsuSetting.DimLevel, 0.3, 0, 1);
Set(OsuConfig.ShowInterface, true);
Set(OsuConfig.KeyOverlay, false);
Set(OsuSetting.ShowInterface, true);
Set(OsuSetting.KeyOverlay, false);
// Update
Set(OsuConfig.ReleaseStream, ReleaseStream.Lazer);
Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
}
public OsuConfigManager(Storage storage) : base(storage)
@@ -77,12 +79,13 @@ namespace osu.Game.Configuration
}
}
public enum OsuConfig
public enum OsuSetting
{
Ruleset,
Token,
MenuCursorSize,
GameplayCursorSize,
AutoCursorSize,
DimLevel,
KeyOverlay,
ShowInterface,
@@ -94,7 +97,6 @@ namespace osu.Game.Configuration
MenuParallax,
BeatmapDetailTab,
Username,
AudioDevice,
ReleaseStream,
SavePassword,
SaveUsername,
@@ -102,6 +104,7 @@ namespace osu.Game.Configuration
DisplayStarsMaximum,
SnakingInSliders,
SnakingOutSliders,
ShowFpsDisplay
ShowFpsDisplay,
ChatDisplayHeight
}
}
+6 -12
View File
@@ -36,14 +36,12 @@ namespace osu.Game.Database
private void deletePending()
{
foreach (var b in Query<BeatmapSetInfo>().Where(b => b.DeletePending))
foreach (var b in GetAllWithChildren<BeatmapSetInfo>(b => b.DeletePending))
{
try
{
Storage.Delete(b.Path);
GetChildren(b, true);
foreach (var i in b.Beatmaps)
{
if (i.Metadata != null) Connection.Delete(i.Metadata);
@@ -269,20 +267,16 @@ namespace osu.Game.Database
public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null, bool withStoryboard = false)
{
var beatmapSetInfo = Query<BeatmapSetInfo>().FirstOrDefault(s => s.ID == beatmapInfo.BeatmapSetInfoID);
if (beatmapInfo.BeatmapSet == null || beatmapInfo.Ruleset == null)
beatmapInfo = GetChildren(beatmapInfo, true);
if (beatmapSetInfo == null)
if (beatmapInfo.BeatmapSet == null)
throw new InvalidOperationException($@"Beatmap set {beatmapInfo.BeatmapSetInfoID} is not in the local database.");
//we need metadata
GetChildren(beatmapSetInfo);
//we also need a ruleset
GetChildren(beatmapInfo);
if (beatmapInfo.Metadata == null)
beatmapInfo.Metadata = beatmapSetInfo.Metadata;
beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata;
WorkingBeatmap working = new DatabaseWorkingBeatmap(this, beatmapInfo, beatmapSetInfo, withStoryboard);
WorkingBeatmap working = new DatabaseWorkingBeatmap(this, beatmapInfo, withStoryboard);
previous?.TransferTo(working);
+9 -4
View File
@@ -7,12 +7,17 @@ namespace osu.Game.Database
{
public class BeatmapDifficulty
{
/// <summary>
/// The default value used for all difficulty settings except <see cref="SliderMultiplier"/> and <see cref="SliderTickRate"/>.
/// </summary>
public const float DEFAULT_DIFFICULTY = 5;
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public float DrainRate { get; set; } = 5;
public float CircleSize { get; set; } = 5;
public float OverallDifficulty { get; set; } = 5;
public float ApproachRate { get; set; } = 5;
public float DrainRate { get; set; } = DEFAULT_DIFFICULTY;
public float CircleSize { get; set; } = DEFAULT_DIFFICULTY;
public float OverallDifficulty { get; set; } = DEFAULT_DIFFICULTY;
public float ApproachRate { get; set; } = DEFAULT_DIFFICULTY;
public float SliderMultiplier { get; set; } = 1;
public float SliderTickRate { get; set; } = 1;
+3 -5
View File
@@ -48,11 +48,9 @@ namespace osu.Game.Database
return Connection.Table<T>();
}
public T GetWithChildren<T>(object id) where T : class
{
return Connection.GetWithChildren<T>(id);
}
/// <summary>
/// This is expensive. Use with caution.
/// </summary>
public List<T> GetAllWithChildren<T>(Expression<Func<T, bool>> filter = null, bool recursive = true)
where T : class
{
+5 -5
View File
@@ -14,8 +14,8 @@ namespace osu.Game.Database
{
private readonly BeatmapDatabase database;
public DatabaseWorkingBeatmap(BeatmapDatabase database, BeatmapInfo beatmapInfo, BeatmapSetInfo beatmapSetInfo, bool withStoryboard = false)
: base(beatmapInfo, beatmapSetInfo, withStoryboard)
public DatabaseWorkingBeatmap(BeatmapDatabase database, BeatmapInfo beatmapInfo, bool withStoryboard = false)
: base(beatmapInfo, withStoryboard)
{
this.database = database;
}
@@ -51,13 +51,13 @@ namespace osu.Game.Database
protected override Texture GetBackground()
{
if (BeatmapInfo?.Metadata?.BackgroundFile == null)
if (Metadata?.BackgroundFile == null)
return null;
try
{
using (var reader = getReader())
return new TextureStore(new RawTextureLoaderStore(reader), false).Get(BeatmapInfo.Metadata.BackgroundFile);
return new TextureStore(new RawTextureLoaderStore(reader), false).Get(Metadata.BackgroundFile);
}
catch { return null; }
}
@@ -66,7 +66,7 @@ namespace osu.Game.Database
{
try
{
var trackData = getReader()?.GetStream(BeatmapInfo.Metadata.AudioFile);
var trackData = getReader()?.GetStream(Metadata.AudioFile);
return trackData == null ? null : new TrackBass(trackData);
}
catch { return null; }
+2 -2
View File
@@ -94,13 +94,13 @@ namespace osu.Game.Database
{
byte[] properties = new byte[5];
if (replayInStream.Read(properties, 0, 5) != 5)
throw new Exception("input .lzma is too short");
throw new IOException("input .lzma is too short");
long outSize = 0;
for (int i = 0; i < 8; i++)
{
int v = replayInStream.ReadByte();
if (v < 0)
throw new Exception("Can't Read 1");
throw new IOException("Can't Read 1");
outSize |= (long)(byte)v << (8 * i);
}
@@ -39,7 +39,7 @@ namespace osu.Game.Graphics.Containers
private void load(UserInputManager input, OsuConfigManager config)
{
this.input = input;
parallaxEnabled = config.GetBindable<bool>(OsuConfig.MenuParallax);
parallaxEnabled = config.GetBindable<bool>(OsuSetting.MenuParallax);
parallaxEnabled.ValueChanged += delegate
{
if (!parallaxEnabled)
+1 -1
View File
@@ -78,7 +78,7 @@ namespace osu.Game.Graphics.Cursor
[BackgroundDependencyLoader]
private void load(ShaderManager shaders, TextureStore textures)
{
shader = shaders?.Load(@"CursorTrail", FragmentShaderDescriptor.Texture);
shader = shaders?.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE);
texture = textures.Get(@"Cursor/cursortrail");
Scale = new Vector2(1 / texture.ScaleAdjust);
}
+29 -4
View File
@@ -11,7 +11,9 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Database;
namespace osu.Game.Graphics.Cursor
{
@@ -41,7 +43,10 @@ namespace osu.Game.Graphics.Cursor
public class OsuCursor : Container
{
private Container cursorContainer;
private Bindable<double> cursorScale;
private Bindable<bool> autoCursorScale;
private Bindable<WorkingBeatmap> beatmap;
public OsuCursor()
{
@@ -50,7 +55,7 @@ namespace osu.Game.Graphics.Cursor
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
private void load(OsuConfigManager config, OsuGameBase game)
{
Children = new Drawable[]
{
@@ -114,9 +119,29 @@ namespace osu.Game.Graphics.Cursor
},
};
cursorScale = config.GetBindable<double>(OsuConfig.GameplayCursorSize);
cursorScale.ValueChanged += newScale => cursorContainer.Scale = new Vector2((float)cursorScale);
cursorScale.TriggerChange();
beatmap = game.Beatmap.GetBoundCopy();
beatmap.ValueChanged += v => calculateScale();
cursorScale = config.GetBindable<double>(OsuSetting.GameplayCursorSize);
cursorScale.ValueChanged += v => calculateScale();
autoCursorScale = config.GetBindable<bool>(OsuSetting.AutoCursorSize);
autoCursorScale.ValueChanged += v => calculateScale();
calculateScale();
}
private void calculateScale()
{
float scale = (float)cursorScale.Value;
if (autoCursorScale && beatmap.Value != null)
{
// if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier.
scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.Difficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY);
}
cursorContainer.Scale = new Vector2(scale);
}
}
}
+1 -1
View File
@@ -128,7 +128,7 @@ namespace osu.Game.Graphics.Cursor
}
};
cursorScale = config.GetBindable<double>(OsuConfig.MenuCursorSize);
cursorScale = config.GetBindable<double>(OsuSetting.MenuCursorSize);
cursorScale.ValueChanged += newScale => cursorContainer.Scale = new Vector2((float)newScale);
cursorScale.TriggerChange();
}
@@ -8,7 +8,6 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Framework.Threading;
+49 -47
View File
@@ -16,7 +16,7 @@ namespace osu.Game.Graphics
switch (hex.Length)
{
default:
throw new Exception(@"Invalid hex string length!");
throw new ArgumentException(@"Invalid hex string length!");
case 3:
return new Color4(
(byte)(Convert.ToByte(hex.Substring(0, 1), 16) * 17),
@@ -33,57 +33,59 @@ namespace osu.Game.Graphics
}
// See https://github.com/ppy/osu-web/blob/master/resources/assets/less/colors.less
public Color4 PurpleLighter = FromHex(@"eeeeff");
public Color4 PurpleLight = FromHex(@"aa88ff");
public Color4 Purple = FromHex(@"8866ee");
public Color4 PurpleDark = FromHex(@"6644cc");
public Color4 PurpleDarker = FromHex(@"441188");
public readonly Color4 PurpleLighter = FromHex(@"eeeeff");
public readonly Color4 PurpleLight = FromHex(@"aa88ff");
public readonly Color4 Purple = FromHex(@"8866ee");
public readonly Color4 PurpleDark = FromHex(@"6644cc");
public readonly Color4 PurpleDarker = FromHex(@"441188");
public Color4 PinkLighter = FromHex(@"ffddee");
public Color4 PinkLight = FromHex(@"ff99cc");
public Color4 Pink = FromHex(@"ff66aa");
public Color4 PinkDark = FromHex(@"cc5288");
public Color4 PinkDarker = FromHex(@"bb1177");
public readonly Color4 PinkLighter = FromHex(@"ffddee");
public readonly Color4 PinkLight = FromHex(@"ff99cc");
public readonly Color4 Pink = FromHex(@"ff66aa");
public readonly Color4 PinkDark = FromHex(@"cc5288");
public readonly Color4 PinkDarker = FromHex(@"bb1177");
public Color4 BlueLighter = FromHex(@"ddffff");
public Color4 BlueLight = FromHex(@"99eeff");
public Color4 Blue = FromHex(@"66ccff");
public Color4 BlueDark = FromHex(@"44aadd");
public Color4 BlueDarker = FromHex(@"2299bb");
public readonly Color4 BlueLighter = FromHex(@"ddffff");
public readonly Color4 BlueLight = FromHex(@"99eeff");
public readonly Color4 Blue = FromHex(@"66ccff");
public readonly Color4 BlueDark = FromHex(@"44aadd");
public readonly Color4 BlueDarker = FromHex(@"2299bb");
public Color4 YellowLighter = FromHex(@"ffffdd");
public Color4 YellowLight = FromHex(@"ffdd55");
public Color4 Yellow = FromHex(@"ffcc22");
public Color4 YellowDark = FromHex(@"eeaa00");
public Color4 YellowDarker = FromHex(@"cc6600");
public readonly Color4 YellowLighter = FromHex(@"ffffdd");
public readonly Color4 YellowLight = FromHex(@"ffdd55");
public readonly Color4 Yellow = FromHex(@"ffcc22");
public readonly Color4 YellowDark = FromHex(@"eeaa00");
public readonly Color4 YellowDarker = FromHex(@"cc6600");
public Color4 GreenLighter = FromHex(@"eeffcc");
public Color4 GreenLight = FromHex(@"b3d944");
public Color4 Green = FromHex(@"88b300");
public Color4 GreenDark = FromHex(@"668800");
public Color4 GreenDarker = FromHex(@"445500");
public readonly Color4 GreenLighter = FromHex(@"eeffcc");
public readonly Color4 GreenLight = FromHex(@"b3d944");
public readonly Color4 Green = FromHex(@"88b300");
public readonly Color4 GreenDark = FromHex(@"668800");
public readonly Color4 GreenDarker = FromHex(@"445500");
public Color4 Gray0 = FromHex(@"000");
public Color4 Gray1 = FromHex(@"111");
public Color4 Gray2 = FromHex(@"222");
public Color4 Gray3 = FromHex(@"333");
public Color4 Gray4 = FromHex(@"444");
public Color4 Gray5 = FromHex(@"555");
public Color4 Gray6 = FromHex(@"666");
public Color4 Gray7 = FromHex(@"777");
public Color4 Gray8 = FromHex(@"888");
public Color4 Gray9 = FromHex(@"999");
public Color4 GrayA = FromHex(@"aaa");
public Color4 GrayB = FromHex(@"bbb");
public Color4 GrayC = FromHex(@"ccc");
public Color4 GrayD = FromHex(@"ddd");
public Color4 GrayE = FromHex(@"eee");
public Color4 GrayF = FromHex(@"fff");
public readonly Color4 Gray0 = FromHex(@"000");
public readonly Color4 Gray1 = FromHex(@"111");
public readonly Color4 Gray2 = FromHex(@"222");
public readonly Color4 Gray3 = FromHex(@"333");
public readonly Color4 Gray4 = FromHex(@"444");
public readonly Color4 Gray5 = FromHex(@"555");
public readonly Color4 Gray6 = FromHex(@"666");
public readonly Color4 Gray7 = FromHex(@"777");
public readonly Color4 Gray8 = FromHex(@"888");
public readonly Color4 Gray9 = FromHex(@"999");
public readonly Color4 GrayA = FromHex(@"aaa");
public readonly Color4 GrayB = FromHex(@"bbb");
public readonly Color4 GrayC = FromHex(@"ccc");
public readonly Color4 GrayD = FromHex(@"ddd");
public readonly Color4 GrayE = FromHex(@"eee");
public readonly Color4 GrayF = FromHex(@"fff");
public Color4 RedLighter = FromHex(@"ffeded");
public Color4 RedLight = FromHex(@"ed7787");
public Color4 Red = FromHex(@"ed1121");
public Color4 RedDark = FromHex(@"ba0011");
public Color4 RedDarker = FromHex(@"870000");
public readonly Color4 RedLighter = FromHex(@"ffeded");
public readonly Color4 RedLight = FromHex(@"ed7787");
public readonly Color4 Red = FromHex(@"ed1121");
public readonly Color4 RedDark = FromHex(@"ba0011");
public readonly Color4 RedDarker = FromHex(@"870000");
public readonly Color4 ChatBlue = FromHex(@"17292e");
}
}
@@ -6,7 +6,6 @@ using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
@@ -7,7 +7,6 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
@@ -5,7 +5,6 @@ using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.UserInterface;
namespace osu.Game.Graphics.UserInterface
@@ -9,7 +9,6 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
@@ -25,15 +24,15 @@ namespace osu.Game.Graphics.UserInterface
protected override bool InternalContains(Vector2 screenSpacePos) => base.InternalContains(screenSpacePos) || Dropdown.Contains(screenSpacePos);
private bool isEnumType => typeof(T).IsEnum;
public OsuTabControl()
{
TabContainer.Spacing = new Vector2(10f, 0f);
if (!typeof(T).IsEnum)
throw new InvalidOperationException("OsuTabControl only supports enums as the generic type argument");
foreach (var val in (T[])Enum.GetValues(typeof(T)))
AddItem(val);
if (isEnumType)
foreach (var val in (T[])Enum.GetValues(typeof(T)))
AddItem(val);
}
[BackgroundDependencyLoader]
@@ -136,7 +135,7 @@ namespace osu.Game.Graphics.UserInterface
Margin = new MarginPadding { Top = 5, Bottom = 5 },
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Text = (value as Enum)?.GetDescription(),
Text = (value as Enum)?.GetDescription() ?? value.ToString(),
TextSize = 14,
Font = @"Exo2.0-Bold", // Font should only turn bold when active?
},
@@ -6,7 +6,6 @@ using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
@@ -3,7 +3,6 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
@@ -1,20 +1,19 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Input;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using OpenTK.Input;
namespace osu.Game.Screens.Select
namespace osu.Game.Graphics.UserInterface
{
/// <summary>
/// A textbox which holds focus eagerly.
/// </summary>
public class SearchTextBox : FocusedTextBox
{
protected virtual bool AllowCommit => false;
public SearchTextBox()
{
Height = 35;
@@ -45,8 +44,10 @@ namespace osu.Game.Screens.Select
case Key.Right:
case Key.Up:
case Key.Down:
case Key.Enter:
return false;
case Key.Enter:
if (!AllowCommit) return false;
break;
}
}
@@ -216,6 +216,11 @@ namespace osu.Game.Graphics.UserInterface
});
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
return true;
}
protected override bool OnClick(InputState state)
{
var flash = new Box
@@ -6,7 +6,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Threading;
using OpenTK;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Audio;
using osu.Framework.Allocation;
@@ -16,12 +16,13 @@ namespace osu.Game.IO.Serialization
return JsonConvert.SerializeObject(obj);
}
public static T Deserialize<T>(string objString)
public static T Deserialize<T>(this string objString)
{
return JsonConvert.DeserializeObject<T>(objString);
}
public static T DeepClone<T>(this IJsonSerializable obj)
public static T DeepClone<T>(this T obj)
where T : IJsonSerializable
{
return Deserialize<T>(Serialize(obj));
}
-26
View File
@@ -1,26 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
using osu.Framework.Input;
namespace osu.Game.Input
{
public class GlobalHotkeys : Drawable
{
public Func<InputState, KeyDownEventArgs, bool> Handler;
public override bool HandleInput => true;
public GlobalHotkeys()
{
RelativeSizeAxes = Axes.Both;
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
return Handler(state, args);
}
}
}
+12 -3
View File
@@ -20,7 +20,7 @@ namespace osu.Game.Online.API
{
private readonly OAuth authentication;
public string Endpoint = @"https://new.ppy.sh";
public string Endpoint = @"https://osu.ppy.sh";
private const string client_id = @"5";
private const string client_secret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk";
@@ -34,7 +34,7 @@ namespace osu.Game.Online.API
public string Password;
public Bindable<User> LocalUser = new Bindable<User>();
public Bindable<User> LocalUser = new Bindable<User>(createGuestUser());
public string Token
{
@@ -191,7 +191,7 @@ namespace osu.Game.Online.API
req.Perform(this);
//we could still be in initialisation, at which point we don't want to say we're Online yet.
if (LocalUser.Value != null)
if (IsLoggedIn)
State = APIState.Online;
failureCount = 0;
@@ -266,6 +266,8 @@ namespace osu.Game.Online.API
}
}
public bool IsLoggedIn => LocalUser.Value.Id > 1;
public void Queue(APIRequest request)
{
queue.Enqueue(request);
@@ -295,8 +297,15 @@ namespace osu.Game.Online.API
clearCredentials();
authentication.Clear();
State = APIState.Offline;
LocalUser.Value = createGuestUser();
}
private static User createGuestUser() => new User
{
Username = @"Guest",
Id = 1,
};
public void Update()
{
Scheduler.Update();
+14 -7
View File
@@ -72,21 +72,28 @@ namespace osu.Game.Online.API
}
}
private static readonly object access_token_retrieval_lock = new object();
/// <summary>
/// Should be run before any API request to make sure we have a valid key.
/// </summary>
private bool ensureAccessToken()
{
//todo: we need to mutex this to ensure only one authentication request is running at a time.
//If we already have a valid access token, let's use it.
// if we already have a valid access token, let's use it.
if (accessTokenValid) return true;
//If not, let's try using our refresh token to request a new access token.
if (!string.IsNullOrEmpty(Token?.RefreshToken))
AuthenticateWithRefresh(Token.RefreshToken);
// we want to ensure only a single authentication update is happening at once.
lock (access_token_retrieval_lock)
{
// re-check if valid, in case another request completed and revalidated our access.
if (accessTokenValid) return true;
return accessTokenValid;
// if not, let's try using our refresh token to request a new access token.
if (!string.IsNullOrEmpty(Token?.RefreshToken))
AuthenticateWithRefresh(Token.RefreshToken);
return accessTokenValid;
}
}
private bool accessTokenValid => Token?.IsValid ?? false;
+2 -2
View File
@@ -41,13 +41,13 @@ namespace osu.Game.Online.API
[JsonProperty(@"refresh_token")]
public string RefreshToken;
public override string ToString() => $@"{AccessToken}/{AccessTokenExpiry.ToString(NumberFormatInfo.InvariantInfo)}/{RefreshToken}";
public override string ToString() => $@"{AccessToken}|{AccessTokenExpiry.ToString(NumberFormatInfo.InvariantInfo)}|{RefreshToken}";
public static OAuthToken Parse(string value)
{
try
{
string[] parts = value.Split('/');
string[] parts = value.Split('|');
return new OAuthToken
{
AccessToken = parts[0],
+7 -3
View File
@@ -23,10 +23,12 @@ namespace osu.Game.Online.Chat
[JsonProperty(@"channel_id")]
public int Id;
public SortedList<Message> Messages = new SortedList<Message>((m1, m2) => m1.Id.CompareTo(m2.Id));
public readonly SortedList<Message> Messages = new SortedList<Message>((m1, m2) => m1.Id.CompareTo(m2.Id));
//internal bool Joined;
public bool ReadOnly => Name != "#lazer";
public const int MAX_HISTORY = 300;
[JsonConstructor]
@@ -36,9 +38,9 @@ namespace osu.Game.Online.Chat
public event Action<IEnumerable<Message>> NewMessagesArrived;
public void AddNewMessages(IEnumerable<Message> messages)
public void AddNewMessages(params Message[] messages)
{
messages = messages.Except(Messages).ToList();
messages = messages.Except(Messages).ToArray();
Messages.AddRange(messages);
@@ -53,5 +55,7 @@ namespace osu.Game.Online.Chat
if (messageCount > MAX_HISTORY)
Messages.RemoveRange(0, messageCount - MAX_HISTORY);
}
public override string ToString() => Name;
}
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Users;
namespace osu.Game.Online.Chat
{
public class ErrorMessage : Message
{
private static int errorId = -1;
public ErrorMessage(string message) : base(errorId--)
{
Timestamp = DateTime.Now;
Content = message;
Sender = new User
{
Username = @"system",
Colour = @"ff0000",
};
}
}
}
+5
View File
@@ -37,6 +37,11 @@ namespace osu.Game.Online.Chat
{
}
public Message(long id)
{
Id = id;
}
public override bool Equals(object obj)
{
var objMessage = obj as Message;
+14 -16
View File
@@ -9,7 +9,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
using osu.Framework.Input;
using osu.Game.Input;
using OpenTK.Input;
using osu.Framework.Logging;
using osu.Game.Graphics.UserInterface.Volume;
@@ -20,7 +19,6 @@ using osu.Game.Screens;
using osu.Game.Screens.Menu;
using OpenTK;
using System.Linq;
using osu.Framework.Graphics.Primitives;
using System.Threading.Tasks;
using osu.Framework.Threading;
using osu.Game.Database;
@@ -63,14 +61,14 @@ namespace osu.Game
private readonly string[] args;
private OptionsOverlay options;
private SettingsOverlay settings;
public OsuGame(string[] args = null)
{
this.args = args;
}
public void ToggleOptions() => options.ToggleVisibility();
public void ToggleSettings() => settings.ToggleVisibility();
[BackgroundDependencyLoader]
private void load()
@@ -89,7 +87,7 @@ namespace osu.Game
Dependencies.Cache(this);
configRuleset = LocalConfig.GetBindable<int>(OsuConfig.Ruleset);
configRuleset = LocalConfig.GetBindable<int>(OsuSetting.Ruleset);
Ruleset.Value = RulesetDatabase.GetRuleset(configRuleset.Value);
Ruleset.ValueChanged += r => configRuleset.Value = r.ID ?? 0;
}
@@ -147,6 +145,7 @@ namespace osu.Game
},
volume = new VolumeControl(),
overlayContent = new Container{ RelativeSizeAxes = Axes.Both },
new OnScreenDisplay(),
new GlobalHotkeys //exists because UserInputManager is at a level below us.
{
Handler = globalHotkeyPressed
@@ -161,8 +160,8 @@ namespace osu.Game
});
//overlay elements
LoadComponentAsync(chat = new ChatOverlay { Depth = 0 }, overlayContent.Add);
LoadComponentAsync(options = new OptionsOverlay { Depth = -1 }, overlayContent.Add);
LoadComponentAsync(chat = new ChatOverlay { Depth = -1 }, mainContent.Add);
LoadComponentAsync(settings = new SettingsOverlay { Depth = -1 }, overlayContent.Add);
LoadComponentAsync(musicController = new MusicController
{
Depth = -2,
@@ -193,7 +192,7 @@ namespace osu.Game
});
};
Dependencies.Cache(options);
Dependencies.Cache(settings);
Dependencies.Cache(chat);
Dependencies.Cache(musicController);
Dependencies.Cache(notificationManager);
@@ -205,15 +204,15 @@ namespace osu.Game
OnHome = delegate { intro?.ChildScreen?.MakeCurrent(); },
}, overlayContent.Add);
options.StateChanged += delegate
settings.StateChanged += delegate
{
switch (options.State)
switch (settings.State)
{
case Visibility.Hidden:
intro.MoveToX(0, OptionsOverlay.TRANSITION_LENGTH, EasingTypes.OutQuint);
intro.MoveToX(0, SettingsOverlay.TRANSITION_LENGTH, EasingTypes.OutQuint);
break;
case Visibility.Visible:
intro.MoveToX(OptionsOverlay.SIDEBAR_WIDTH / 2, OptionsOverlay.TRANSITION_LENGTH, EasingTypes.OutQuint);
intro.MoveToX(SettingsOverlay.SIDEBAR_WIDTH / 2, SettingsOverlay.TRANSITION_LENGTH, EasingTypes.OutQuint);
break;
}
};
@@ -248,7 +247,7 @@ namespace osu.Game
Toolbar.ToggleVisibility();
return true;
case Key.O:
options.ToggleVisibility();
settings.ToggleVisibility();
return true;
}
}
@@ -277,7 +276,7 @@ namespace osu.Game
//central game screen change logic.
if (!currentScreen.ShowOverlays)
{
options.State = Visibility.Hidden;
settings.State = Visibility.Hidden;
Toolbar.State = Visibility.Hidden;
musicController.State = Visibility.Hidden;
chat.State = Visibility.Hidden;
@@ -321,8 +320,7 @@ namespace osu.Game
{
base.UpdateAfterChildren();
if (intro?.ChildScreen != null)
intro.ChildScreen.Padding = new MarginPadding { Top = Toolbar.Position.Y + Toolbar.DrawHeight };
mainContent.Padding = new MarginPadding { Top = Toolbar.Position.Y + Toolbar.DrawHeight };
Cursor.State = currentScreen?.HasLocalCursorDisplayed == false ? Visibility.Visible : Visibility.Hidden;
}
+5 -5
View File
@@ -125,8 +125,8 @@ namespace osu.Game
Dependencies.Cache(API = new APIAccess
{
Username = LocalConfig.Get<string>(OsuConfig.Username),
Token = LocalConfig.Get<string>(OsuConfig.Token)
Username = LocalConfig.Get<string>(OsuSetting.Username),
Token = LocalConfig.Get<string>(OsuSetting.Token)
});
API.Register(this);
@@ -137,7 +137,7 @@ namespace osu.Game
switch (state)
{
case APIState.Online:
LocalConfig.Set(OsuConfig.Username, LocalConfig.Get<bool>(OsuConfig.SaveUsername) ? API.Username : string.Empty);
LocalConfig.Set(OsuSetting.Username, LocalConfig.Get<bool>(OsuSetting.SaveUsername) ? API.Username : string.Empty);
break;
}
}
@@ -166,7 +166,7 @@ namespace osu.Game
// TODO: This is temporary until we reimplement the local FPS display.
// It's just to allow end-users to access the framework FPS display without knowing the shortcut key.
fpsDisplayVisible = LocalConfig.GetBindable<bool>(OsuConfig.ShowFpsDisplay);
fpsDisplayVisible = LocalConfig.GetBindable<bool>(OsuSetting.ShowFpsDisplay);
fpsDisplayVisible.ValueChanged += val =>
{
FrameStatisticsMode = val ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None;
@@ -192,7 +192,7 @@ namespace osu.Game
//refresh token may have changed.
if (LocalConfig != null && API != null)
{
LocalConfig.Set(OsuConfig.Token, LocalConfig.Get<bool>(OsuConfig.SavePassword) ? API.Token : string.Empty);
LocalConfig.Set(OsuSetting.Token, LocalConfig.Get<bool>(OsuSetting.SavePassword) ? API.Token : string.Empty);
LocalConfig.Save();
}
@@ -3,13 +3,13 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Online.Chat.Drawables
namespace osu.Game.Overlays.Chat
{
public class ChatLine : Container
{
@@ -62,7 +62,10 @@ namespace osu.Game.Online.Chat.Drawables
return username_colours[message.UserId % username_colours.Length];
}
private const float padding = 200;
public const float LEFT_PADDING = message_padding + padding * 2;
private const float padding = 15;
private const float message_padding = 200;
private const float text_size = 20;
public ChatLine(Message message)
@@ -72,13 +75,13 @@ namespace osu.Game.Online.Chat.Drawables
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Left = 15, Right = 15 };
Padding = new MarginPadding { Left = padding, Right = padding };
Children = new Drawable[]
{
new Container
{
Size = new Vector2(padding, text_size),
Size = new Vector2(message_padding, text_size),
Children = new Drawable[]
{
new OsuSpriteText
@@ -106,7 +109,7 @@ namespace osu.Game.Online.Chat.Drawables
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Left = padding + 15 },
Padding = new MarginPadding { Left = message_padding + padding },
Children = new Drawable[]
{
new OsuSpriteText
+196
View File
@@ -0,0 +1,196 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Chat;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Chat
{
public class ChatTabControl : OsuTabControl<Channel>
{
protected override TabItem<Channel> CreateTabItem(Channel value) => new ChannelTabItem(value);
private const float shear_width = 10;
public ChatTabControl()
{
TabContainer.Margin = new MarginPadding { Left = 50 };
TabContainer.Spacing = new Vector2(-shear_width, 0);
TabContainer.Masking = false;
AddInternal(new TextAwesome
{
Icon = FontAwesome.fa_comments,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
TextSize = 20,
Padding = new MarginPadding(10),
});
}
private class ChannelTabItem : TabItem<Channel>
{
private Color4 backgroundInactive;
private Color4 backgroundHover;
private Color4 backgroundActive;
private readonly SpriteText text;
private readonly SpriteText textBold;
private readonly Box box;
private readonly Box highlightBox;
public override bool Active
{
get { return base.Active; }
set
{
if (Active == value) return;
base.Active = value;
updateState();
}
}
private void updateState()
{
if (Active)
fadeActive();
else
fadeInactive();
}
private const float transition_length = 400;
private void fadeActive()
{
ResizeTo(new Vector2(Width, 1.1f), transition_length, EasingTypes.OutQuint);
box.FadeColour(backgroundActive, transition_length, EasingTypes.OutQuint);
highlightBox.FadeIn(transition_length, EasingTypes.OutQuint);
text.FadeOut(transition_length, EasingTypes.OutQuint);
textBold.FadeIn(transition_length, EasingTypes.OutQuint);
}
private void fadeInactive()
{
ResizeTo(new Vector2(Width, 1), transition_length, EasingTypes.OutQuint);
box.FadeColour(backgroundInactive, transition_length, EasingTypes.OutQuint);
highlightBox.FadeOut(transition_length, EasingTypes.OutQuint);
text.FadeIn(transition_length, EasingTypes.OutQuint);
textBold.FadeOut(transition_length, EasingTypes.OutQuint);
}
protected override bool OnHover(InputState state)
{
if (!Active)
box.FadeColour(backgroundHover, transition_length, EasingTypes.OutQuint);
return true;
}
protected override void OnHoverLost(InputState state)
{
updateState();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
backgroundActive = colours.ChatBlue;
backgroundInactive = colours.Gray4;
backgroundHover = colours.Gray7;
highlightBox.Colour = colours.Yellow;
updateState();
}
public ChannelTabItem(Channel value) : base(value)
{
Width = 150;
RelativeSizeAxes = Axes.Y;
Anchor = Anchor.BottomLeft;
Origin = Anchor.BottomLeft;
Shear = new Vector2(shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0);
Masking = true;
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Radius = 10,
Colour = Color4.Black.Opacity(0.2f),
};
Children = new Drawable[]
{
box = new Box
{
EdgeSmoothness = new Vector2(1, 0),
RelativeSizeAxes = Axes.Both,
},
highlightBox = new Box
{
Width = 5,
Alpha = 0,
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
EdgeSmoothness = new Vector2(1, 0),
RelativeSizeAxes = Axes.Y,
},
new Container
{
Shear = new Vector2(-shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0),
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new TextAwesome
{
Icon = FontAwesome.fa_hashtag,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Colour = Color4.Black,
X = -10,
Alpha = 0.2f,
TextSize = ChatOverlay.TAB_AREA_HEIGHT,
},
text = new OsuSpriteText
{
Margin = new MarginPadding(5),
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Text = value.ToString(),
TextSize = 18,
},
textBold = new OsuSpriteText
{
Alpha = 0,
Margin = new MarginPadding(5),
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Text = value.ToString(),
Font = @"Exo2.0-Bold",
TextSize = 18,
},
}
}
};
}
}
}
}
@@ -6,33 +6,24 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat;
namespace osu.Game.Online.Chat.Drawables
namespace osu.Game.Overlays.Chat
{
public class DrawableChannel : Container
{
private readonly Channel channel;
public readonly Channel Channel;
private readonly FillFlowContainer flow;
private readonly ScrollContainer scroll;
public DrawableChannel(Channel channel)
{
this.channel = channel;
Channel = channel;
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
new OsuSpriteText
{
Text = channel.Name,
TextSize = 50,
Alpha = 0.3f,
Anchor = Anchor.Centre,
Origin = Anchor.Centre
},
scroll = new ScrollContainer
{
RelativeSizeAxes = Axes.Both,
@@ -56,14 +47,14 @@ namespace osu.Game.Online.Chat.Drawables
{
base.LoadComplete();
newMessagesArrived(channel.Messages);
newMessagesArrived(Channel.Messages);
scrollToEnd();
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
channel.NewMessagesArrived -= newMessagesArrived;
Channel.NewMessagesArrived -= newMessagesArrived;
}
private void newMessagesArrived(IEnumerable<Message> newMessages)
@@ -93,4 +84,4 @@ namespace osu.Game.Online.Chat.Drawables
private void scrollToEnd() => Scheduler.AddDelayed(() => scroll.ScrollToEnd(), 50);
}
}
}
+235 -69
View File
@@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Linq;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
@@ -15,24 +16,23 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Chat;
using osu.Game.Online.Chat.Drawables;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.UserInterface;
using OpenTK.Graphics;
using osu.Framework.Input;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Overlays.Chat;
namespace osu.Game.Overlays
{
public class ChatOverlay : FocusedOverlayContainer, IOnlineComponent
{
private const float textbox_height = 40;
private const float textbox_height = 60;
private ScheduledDelegate messageRequest;
private readonly Container content;
protected override Container<Drawable> Content => content;
private readonly Container currentChannelContainer;
private readonly FocusedTextBox inputTextBox;
@@ -40,50 +40,113 @@ namespace osu.Game.Overlays
private const int transition_length = 500;
public const float DEFAULT_HEIGHT = 0.4f;
public const float TAB_AREA_HEIGHT = 50;
private GetMessagesRequest fetchReq;
private readonly ChatTabControl channelTabs;
private readonly Box chatBackground;
private readonly Box tabBackground;
private Bindable<double> chatHeight;
public ChatOverlay()
{
RelativeSizeAxes = Axes.X;
Size = new Vector2(1, 300);
RelativeSizeAxes = Axes.Both;
RelativePositionAxes = Axes.Both;
Size = new Vector2(1, DEFAULT_HEIGHT);
Anchor = Anchor.BottomLeft;
Origin = Anchor.BottomLeft;
AddInternal(new Drawable[]
const float padding = 5;
Children = new Drawable[]
{
new Box
new Container
{
Depth = float.MaxValue,
Name = @"chat area",
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.9f,
},
content = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 5, Bottom = textbox_height + 5 },
Padding = new MarginPadding { Top = TAB_AREA_HEIGHT },
Children = new Drawable[]
{
chatBackground = new Box
{
RelativeSizeAxes = Axes.Both,
},
currentChannelContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
{
Bottom = textbox_height + padding
},
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = textbox_height,
Padding = new MarginPadding
{
Top = padding * 2,
Bottom = padding * 2,
Left = ChatLine.LEFT_PADDING + padding * 2,
Right = padding * 2,
},
Children = new Drawable[]
{
inputTextBox = new FocusedTextBox
{
RelativeSizeAxes = Axes.Both,
Height = 1,
PlaceholderText = "type your message",
Exit = () => State = Visibility.Hidden,
OnCommit = postMessage,
HoldFocus = true,
}
}
}
}
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Name = @"tabs area",
RelativeSizeAxes = Axes.X,
Height = textbox_height,
Padding = new MarginPadding(5),
Height = TAB_AREA_HEIGHT,
Children = new Drawable[]
{
inputTextBox = new FocusedTextBox
tabBackground = new Box
{
RelativeSizeAxes = Axes.Both,
Height = 1,
PlaceholderText = "type your message",
Exit = () => State = Visibility.Hidden,
OnCommit = postMessage,
HoldFocus = true,
}
Colour = Color4.Black,
},
channelTabs = new ChatTabControl
{
RelativeSizeAxes = Axes.Both,
},
}
}
});
},
};
channelTabs.Current.ValueChanged += newChannel => CurrentChannel = newChannel;
}
protected override bool OnDragStart(InputState state)
{
if (channelTabs.Hovering)
return true;
return base.OnDragStart(state);
}
protected override bool OnDrag(InputState state)
{
chatHeight.Value = Height - state.Mouse.Delta.Y / Parent.DrawSize.Y;
return base.OnDrag(state);
}
public void APIStateChanged(APIAccess api, APIState state)
@@ -110,28 +173,48 @@ namespace osu.Game.Overlays
{
MoveToY(0, transition_length, EasingTypes.OutQuint);
FadeIn(transition_length, EasingTypes.OutQuint);
inputTextBox.HoldFocus = true;
base.PopIn();
}
protected override void PopOut()
{
MoveToY(DrawSize.Y, transition_length, EasingTypes.InSine);
MoveToY(Height, transition_length, EasingTypes.InSine);
FadeOut(transition_length, EasingTypes.InSine);
inputTextBox.HoldFocus = false;
base.PopOut();
}
[BackgroundDependencyLoader]
private void load(APIAccess api)
private void load(APIAccess api, OsuConfigManager config, OsuColour colours)
{
this.api = api;
api.Register(this);
chatHeight = config.GetBindable<double>(OsuSetting.ChatDisplayHeight);
chatHeight.ValueChanged += h =>
{
Height = (float)h;
tabBackground.FadeTo(Height == 1 ? 1 : 0.8f, 200);
};
chatHeight.TriggerChange();
chatBackground.Colour = colours.ChatBlue;
}
private long? lastMessageId;
private List<Channel> careChannels;
private readonly List<DrawableChannel> loadedChannels = new List<DrawableChannel>();
private void initializeChannels()
{
Clear();
currentChannelContainer.Clear();
loadedChannels.Clear();
careChannels = new List<Channel>();
@@ -154,18 +237,88 @@ namespace osu.Game.Overlays
Scheduler.Add(delegate
{
loading.FadeOut(100);
addChannel(channels.Find(c => c.Name == @"#lazer"));
addChannel(channels.Find(c => c.Name == @"#osu"));
addChannel(channels.Find(c => c.Name == @"#lobby"));
});
messageRequest = Scheduler.AddDelayed(fetchNewMessages, 1000, true);
};
api.Queue(req);
}
private Channel currentChannel;
protected Channel CurrentChannel
{
get
{
return currentChannel;
}
set
{
if (currentChannel == value) return;
if (currentChannel != null)
currentChannelContainer.Clear(false);
currentChannel = value;
var loaded = loadedChannels.Find(d => d.Channel == value);
if (loaded == null)
loadedChannels.Add(loaded = new DrawableChannel(currentChannel));
inputTextBox.Current.Disabled = currentChannel.ReadOnly;
currentChannelContainer.Add(loaded);
channelTabs.Current.Value = value;
}
}
private void addChannel(Channel channel)
{
Add(new DrawableChannel(channel));
careChannels.Add(channel);
if (channel == null) return;
var existing = careChannels.Find(c => c.Id == channel.Id);
if (existing != null)
{
// if we already have this channel loaded, we don't want to make a second one.
channel = existing;
}
else
{
careChannels.Add(channel);
channelTabs.AddItem(channel);
}
// let's fetch a small number of messages to bring us up-to-date with the backlog.
fetchInitialMessages(channel);
if (CurrentChannel == null)
CurrentChannel = channel;
}
private void fetchInitialMessages(Channel channel)
{
var req = new GetMessagesRequest(new List<Channel> { channel }, null);
req.Success += delegate (List<Message> messages)
{
channel.AddNewMessages(messages.ToArray());
Debug.Write("success!");
};
req.Failure += delegate
{
Debug.Write("failure!");
};
api.Queue(req);
}
private void fetchNewMessages()
@@ -179,7 +332,7 @@ namespace osu.Game.Overlays
//batch messages per channel.
foreach (var id in ids)
careChannels.Find(c => c.Id == id)?.AddNewMessages(messages.Where(m => m.TargetId == id));
careChannels.Find(c => c.Id == id)?.AddNewMessages(messages.Where(m => m.TargetId == id).ToArray());
lastMessageId = messages.LastOrDefault()?.Id ?? lastMessageId;
@@ -199,40 +352,53 @@ namespace osu.Game.Overlays
{
var postText = textbox.Text;
if (!string.IsNullOrEmpty(postText) && api.LocalUser.Value != null)
if (string.IsNullOrEmpty(postText))
return;
if (!api.IsLoggedIn)
{
var currentChannel = careChannels.FirstOrDefault();
if (currentChannel == null) return;
var message = new Message
{
Sender = api.LocalUser.Value,
Timestamp = DateTimeOffset.Now,
TargetType = TargetType.Channel, //TODO: read this from currentChannel
TargetId = currentChannel.Id,
Content = postText
};
textbox.ReadOnly = true;
var req = new PostMessageRequest(message);
req.Failure += e =>
{
textbox.FlashColour(Color4.Red, 1000);
textbox.ReadOnly = false;
};
req.Success += m =>
{
currentChannel.AddNewMessages(new[] { m });
textbox.ReadOnly = false;
textbox.Text = string.Empty;
};
api.Queue(req);
currentChannel?.AddNewMessages(new ErrorMessage("Please login to participate in chat!"));
textbox.Text = string.Empty;
return;
}
if (currentChannel == null) return;
if (postText[0] == '/')
{
// TODO: handle commands
currentChannel.AddNewMessages(new ErrorMessage("Chat commands are not supported yet!"));
textbox.Text = string.Empty;
return;
}
var message = new Message
{
Sender = api.LocalUser.Value,
Timestamp = DateTimeOffset.Now,
TargetType = TargetType.Channel, //TODO: read this from currentChannel
TargetId = currentChannel.Id,
Content = postText
};
textbox.ReadOnly = true;
var req = new PostMessageRequest(message);
req.Failure += e =>
{
textbox.FlashColour(Color4.Red, 1000);
textbox.ReadOnly = false;
};
req.Success += m =>
{
currentChannel.AddNewMessages(m);
textbox.ReadOnly = false;
textbox.Text = string.Empty;
};
api.Queue(req);
}
}
}
-1
View File
@@ -6,7 +6,6 @@ using System.Linq;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Game.Graphics;
+6 -7
View File
@@ -4,17 +4,16 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Overlays.Options.Sections.General;
using osu.Game.Overlays.Settings.Sections.General;
using OpenTK.Graphics;
namespace osu.Game.Overlays
{
internal class LoginOverlay : FocusedOverlayContainer
{
private LoginOptions optionsSection;
private LoginSettings settingsSection;
private const float transition_time = 400;
@@ -42,7 +41,7 @@ namespace osu.Game.Overlays
AutoSizeEasing = EasingTypes.OutQuint,
Children = new Drawable[]
{
optionsSection = new LoginOptions
settingsSection = new LoginSettings
{
Padding = new MarginPadding(10),
},
@@ -64,17 +63,17 @@ namespace osu.Game.Overlays
{
base.PopIn();
optionsSection.Bounding = true;
settingsSection.Bounding = true;
FadeIn(transition_time, EasingTypes.OutQuint);
optionsSection.TriggerFocus();
settingsSection.TriggerFocus();
}
protected override void PopOut()
{
base.PopOut();
optionsSection.Bounding = false;
settingsSection.Bounding = false;
FadeOut(transition_time);
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Mods
/// </summary>
public class ModButton : ModButtonEmpty
{
private ModIcon foregroundIcon { get; set; }
private ModIcon foregroundIcon;
private readonly SpriteText text;
private readonly Container<ModIcon> iconsContainer;
private SampleChannel sampleOn, sampleOff;
-1
View File
@@ -6,7 +6,6 @@ using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
@@ -8,7 +8,6 @@ using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
+1 -2
View File
@@ -6,11 +6,9 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Select;
using OpenTK;
using OpenTK.Graphics;
using System;
@@ -62,6 +60,7 @@ namespace osu.Game.Overlays.Music
protected override Color4 BackgroundUnfocused => backgroundColour;
protected override Color4 BackgroundFocused => backgroundColour;
protected override bool AllowCommit => true;
public FilterTextBox()
{
+4 -1
View File
@@ -5,7 +5,6 @@ using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
@@ -132,6 +131,10 @@ namespace osu.Game.Overlays.Music
FadeTo(matching ? 1 : 0, 200);
}
get
{
return matching;
}
}
}
}
+2
View File
@@ -22,6 +22,8 @@ namespace osu.Game.Overlays.Music
}
}
public BeatmapSetInfo FirstVisibleSet => items.Children.FirstOrDefault(i => i.MatchingCurrentFilter)?.BeatmapSetInfo;
private void itemSelected(BeatmapSetInfo b)
{
OnSelect?.Invoke(b);
+5 -1
View File
@@ -10,7 +10,6 @@ using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Database;
@@ -84,6 +83,11 @@ namespace osu.Game.Overlays.Music
list.BeatmapSets = BeatmapSets = beatmaps.GetAllWithChildren<BeatmapSetInfo>().ToList();
beatmapBacking.BindTo(game.Beatmap);
filter.Search.OnCommit = (sender, newText) => {
var beatmap = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault();
if (beatmap != null) playSpecified(beatmap);
};
}
protected override void LoadComplete()
+1 -2
View File
@@ -12,7 +12,6 @@ using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
@@ -315,7 +314,7 @@ namespace osu.Game.Overlays
}
else
{
BeatmapMetadata metadata = beatmap.Beatmap.BeatmapInfo.Metadata;
BeatmapMetadata metadata = beatmap.Metadata;
title.Current = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title);
artist.Current = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist);
}
-1
View File
@@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Game.Overlays.Notifications;
using OpenTK.Graphics;
@@ -7,7 +7,6 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
@@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
+261
View File
@@ -0,0 +1,261 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
namespace osu.Game.Overlays
{
public class OnScreenDisplay : Container
{
private readonly Container box;
public override bool HandleInput => false;
private readonly SpriteText textLine1;
private readonly SpriteText textLine2;
private readonly SpriteText textLine3;
private const float height = 110;
private const float height_contracted = height * 0.9f;
private readonly FillFlowContainer<OptionLight> optionLights;
public OnScreenDisplay()
{
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
box = new Container
{
Origin = Anchor.Centre,
RelativePositionAxes = Axes.Both,
Position = new Vector2(0.5f, 0.75f),
Masking = true,
AutoSizeAxes = Axes.X,
Height = height_contracted,
Alpha = 0,
CornerRadius = 20,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.7f,
},
new Container // purely to add a minimum width
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 240,
RelativeSizeAxes = Axes.Y,
},
textLine1 = new SpriteText
{
Padding = new MarginPadding(10),
Font = @"Exo2.0-Black",
Spacing = new Vector2(1, 0),
TextSize = 14,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
textLine2 = new SpriteText
{
TextSize = 24,
Font = @"Exo2.0-Light",
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
},
new FillFlowContainer
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
optionLights = new FillFlowContainer<OptionLight>
{
Padding = new MarginPadding { Top = 20, Bottom = 5 },
Spacing = new Vector2(5, 0),
Direction = FillDirection.Horizontal,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both
},
textLine3 = new SpriteText
{
Padding = new MarginPadding { Bottom = 15 },
Font = @"Exo2.0-Bold",
TextSize = 12,
Alpha = 0.3f,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
}
}
}
},
};
}
[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", v, v));
trackSetting(frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowLogOverlay), v => display(v, "Debug Logs", v ? "visible" : "hidden", "Ctrl+F10"));
Action displayResolution = delegate { 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<WindowMode>(FrameworkSetting.WindowMode), v => display(v, "Screen Mode", v.ToString(), "Alt+Enter"));
}
private readonly List<IBindable> references = new List<IBindable>();
private void trackSetting<T>(Bindable<T> bindable, Bindable<T>.BindableValueChanged<T> action)
{
// we need to keep references as we bind
references.Add(bindable);
bindable.ValueChanged += action;
}
private void display(object rawValue, string settingName, string settingValue, string shortcut = @"")
{
Schedule(() =>
{
textLine1.Text = settingName.ToUpper();
textLine2.Text = settingValue;
textLine3.Text = shortcut.ToUpper();
box.FadeIn(500, EasingTypes.OutQuint);
box.ResizeHeightTo(height, 500, EasingTypes.OutQuint);
using (box.BeginDelayedSequence(500))
{
box.FadeOutFromOne(1500, EasingTypes.InQuint);
box.ResizeHeightTo(height_contracted, 1500, EasingTypes.InQuint);
}
int optionCount = 0;
int selectedOption = -1;
if (rawValue is bool)
{
optionCount = 1;
if ((bool)rawValue) selectedOption = 0;
}
else if (rawValue is Enum)
{
var values = Enum.GetValues(rawValue.GetType());
optionCount = values.Length;
selectedOption = Convert.ToInt32(rawValue);
}
textLine2.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre;
textLine2.Y = optionCount > 0 ? 0 : 5;
if (optionLights.Children.Count() != optionCount)
{
optionLights.Clear();
for (int i = 0; i < optionCount; i++)
optionLights.Add(new OptionLight());
}
for (int i = 0; i < optionCount; i++)
optionLights.Children.Skip(i).First().Glowing = i == selectedOption;
});
}
private class OptionLight : Container
{
private Color4 glowingColour, idleColour;
private const float transition_speed = 300;
private const float glow_strength = 0.4f;
private readonly Box fill;
public OptionLight()
{
Children = new[]
{
fill = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 1,
},
};
}
private bool glowing;
public bool Glowing
{
set
{
glowing = value;
if (!IsLoaded) return;
updateGlow();
}
}
private void updateGlow()
{
if (glowing)
{
fill.FadeColour(glowingColour, transition_speed, EasingTypes.OutQuint);
FadeEdgeEffectTo(glow_strength, transition_speed, EasingTypes.OutQuint);
}
else
{
FadeEdgeEffectTo(0, transition_speed, EasingTypes.OutQuint);
fill.FadeColour(idleColour, transition_speed, EasingTypes.OutQuint);
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
fill.Colour = idleColour = Color4.White.Opacity(0.4f);
glowingColour = Color4.White;
Size = new Vector2(25, 5);
Masking = true;
CornerRadius = 3;
EdgeEffect = new EdgeEffect
{
Colour = colours.BlueDark.Opacity(glow_strength),
Type = EdgeEffectType.Glow,
Radius = 8,
};
FadeEdgeEffectTo(0);
updateGlow();
Flush(true);
}
}
}
}
@@ -7,14 +7,14 @@ using osu.Framework.Graphics;
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Overlays.Options.Sections.Audio
namespace osu.Game.Overlays.Settings.Sections.Audio
{
public class AudioDevicesOptions : OptionsSubsection
public class AudioDevicesSettings : SettingsSubsection
{
protected override string Header => "Devices";
private AudioManager audio;
private OptionDropdown<string> dropdown;
private SettingsDropdown<string> dropdown;
[BackgroundDependencyLoader]
private void load(AudioManager audio)
@@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Options.Sections.Audio
Children = new Drawable[]
{
dropdown = new OptionDropdown<string>
dropdown = new SettingsDropdown<string>
{
Bindable = audio.AudioDevice
},

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