1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-15 13:23:18 +08:00

Compare commits

...

274 Commits

111 changed files with 2183 additions and 814 deletions
+1 -1
View File
@@ -390,7 +390,7 @@ namespace osu.Desktop.Deploy
public static void AuthenticatedBlockingPerform(this WebRequest r)
{
r.Headers.Add("Authorization", $"token {GitHubAccessToken}");
r.AddHeader("Authorization", $"token {GitHubAccessToken}");
r.Perform();
}
}
+1 -1
View File
@@ -106,7 +106,7 @@ namespace osu.Desktop
var filePaths = new [] { e.FileName };
if (filePaths.All(f => Path.GetExtension(f) == @".osz"))
Task.Run(() => BeatmapManager.Import(filePaths));
Task.Factory.StartNew(() => BeatmapManager.Import(filePaths), TaskCreationOptions.LongRunning);
else if (filePaths.All(f => Path.GetExtension(f) == @".osr"))
Task.Run(() =>
{
@@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected override void UpdateState(ArmedState state)
{
switch (State)
switch (State.Value)
{
case ArmedState.Hit:
AccentColour = Color4.Green;
+3 -1
View File
@@ -57,8 +57,10 @@ namespace osu.Game.Rulesets.Mania.UI
{
base.LoadComplete();
this.ScaleTo(2f, 600, Easing.OutQuint).FadeOut(500).Expire();
this.ScaleTo(2f, 600, Easing.OutQuint).FadeOut(500);
inner.FadeOut(250);
Expire(true);
}
}
}
@@ -14,6 +14,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
{
private const float width = 8;
public override bool RemoveWhenNotAlive => false;
public FollowPoint()
{
Origin = Anchor.Centre;
@@ -52,9 +52,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
}
}
public override bool RemoveCompletedTransforms => false;
private void update()
{
Clear();
if (hitObjects == null)
return;
@@ -58,6 +58,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
},
ApproachCircle = new ApproachCircle
{
Alpha = 0,
Scale = new Vector2(4),
Colour = AccentColour,
}
};
@@ -82,21 +84,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
});
}
protected override void UpdateInitialState()
{
base.UpdateInitialState();
// sane defaults
ring.Show();
circle.Show();
number.Show();
glow.Show();
ApproachCircle.Hide();
ApproachCircle.ScaleTo(new Vector2(4));
explode.Hide();
}
protected override void UpdatePreemptState()
{
base.UpdatePreemptState();
@@ -23,12 +23,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected sealed override void UpdateState(ArmedState state)
{
FinishTransforms();
double transformTime = HitObject.StartTime - TIME_PREEMPT;
using (BeginAbsoluteSequence(HitObject.StartTime - TIME_PREEMPT, true))
base.ApplyTransformsAt(transformTime, true);
base.ClearTransformsAfter(transformTime, true);
using (BeginAbsoluteSequence(transformTime, true))
{
UpdateInitialState();
UpdatePreemptState();
using (BeginDelayedSequence(TIME_PREEMPT + (Judgements.FirstOrDefault()?.TimeOffset ?? 0), true))
@@ -36,11 +37,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
protected virtual void UpdateInitialState()
{
Hide();
}
protected virtual void UpdatePreemptState()
{
this.FadeIn(TIME_FADEIN);
@@ -50,6 +46,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
}
// Todo: At some point we need to move these to DrawableHitObject after ensuring that all other Rulesets apply
// transforms in the same way and don't rely on them not being cleared
public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { }
public override void ApplyTransformsAt(double time, bool propagateChildren = false) { }
private OsuInputManager osuActionInputManager;
internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager);
}
@@ -18,9 +18,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public double FadeInTime;
public double FadeOutTime;
public override bool RemoveWhenNotAlive => false;
public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider) : base(repeatPoint)
public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider)
: base(repeatPoint)
{
this.repeatPoint = repeatPoint;
this.drawableSlider = drawableSlider;
@@ -28,6 +27,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
AutoSizeAxes = Axes.Both;
Blending = BlendingMode.Additive;
Origin = Anchor.Centre;
Scale = new Vector2(0.5f);
Children = new Drawable[]
{
@@ -51,12 +51,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
var animIn = Math.Min(150, repeatPoint.StartTime - FadeInTime);
this.Animate(
d => d.FadeIn(animIn),
d => d.ScaleTo(0.5f).ScaleTo(1.2f, animIn)
).Then(
d => d.ScaleTo(1, 150, Easing.Out)
);
this.FadeIn(animIn).ScaleTo(1.2f, animIn)
.Then()
.ScaleTo(1, 150, Easing.Out);
}
protected override void UpdateCurrentState(ArmedState state)
@@ -43,7 +43,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ball = new SliderBall(s)
{
Scale = new Vector2(s.Scale),
AccentColour = AccentColour
AccentColour = AccentColour,
AlwaysPresent = true,
Alpha = 0
},
initialCircle = new DrawableHitCircle(new HitCircle
{
@@ -148,16 +150,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
protected override void UpdateInitialState()
{
base.UpdateInitialState();
body.Alpha = 1;
//we need to be present to handle input events. note that we still don't get enough events (we don't get a position if the mouse hasn't moved since the slider appeared).
ball.AlwaysPresent = true;
ball.Alpha = 0;
}
protected override void UpdateCurrentState(ArmedState state)
{
ball.FadeIn();
@@ -20,8 +20,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public bool Tracking;
public override bool RemoveWhenNotAlive => false;
public override bool DisplayJudgement => false;
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
@@ -101,14 +101,21 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
// If the current time is between the start and end of the slider, we should track mouse input regardless of the cursor position.
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => canCurrentlyTrack || base.ReceiveMouseInputAt(screenSpacePos);
public override void ClearTransforms(bool propagateChildren = false, string targetMember = null)
{
// Consider the case of rewinding - children's transforms are handled internally, so propagating down
// any further will cause weirdness with the Tracking bool below. Let's not propagate further at this point.
base.ClearTransforms(false, targetMember);
}
private bool tracking;
public bool Tracking
{
get { return tracking; }
private set
{
if (value == tracking) return;
if (value == tracking)
return;
tracking = value;
follow.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint);
@@ -123,8 +130,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
base.Update();
// Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position.
if (Time.Current < slider.EndTime)
Tracking = canCurrentlyTrack && lastState != null && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
Tracking = canCurrentlyTrack
&& lastState != null
&& base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position)
&& ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
}
public void UpdateProgress(double progress, int repeat)
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
@@ -61,6 +62,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public void SetRotation(float currentRotation)
{
// If we've gone back in time, it's fine to work with a fresh set of records for now
if (records.Count > 0 && Time.Current < records.Last().Time)
records.Clear();
if (records.Count > 0)
{
var record = records.Peek();
@@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Tests
h.Depth = depth++;
if (auto)
h.State = ArmedState.Hit;
h.State.Value = ArmedState.Hit;
playfieldContainer.Add(h);
var proxyable = h as IDrawableHitObjectWithProxiedApproach;
@@ -1,8 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.Judgements
{
public class TaikoStrongHitJudgement : TaikoJudgement
@@ -11,9 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Judgements
public TaikoStrongHitJudgement()
{
base.Result = HitResult.Perfect;
Final = true;
}
public new HitResult Result => base.Result;
}
}
@@ -17,6 +17,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
/// </summary>
protected abstract TaikoAction[] HitActions { get; }
/// <summary>
/// Whether a second hit is allowed to be processed. This occurs once this hit object has been hit successfully.
/// </summary>
protected bool SecondHitAllowed { get; private set; }
/// <summary>
/// Whether the last key pressed is a valid hit key.
/// </summary>
@@ -45,7 +50,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (!validKeyPressed)
AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
else if (hitOffset < HitObject.HitWindowGood)
AddJudgement(new TaikoJudgement { Result = hitOffset < HitObject.HitWindowGreat ? HitResult.Great : HitResult.Good });
{
AddJudgement(new TaikoJudgement
{
Result = hitOffset < HitObject.HitWindowGreat ? HitResult.Great : HitResult.Good,
Final = !HitObject.IsStrong
});
SecondHitAllowed = true;
}
else
AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
}
@@ -72,7 +85,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
var offset = !AllJudged ? 0 : Time.Current - HitObject.StartTime;
using (BeginDelayedSequence(HitObject.StartTime - Time.Current + offset, true))
{
switch (State)
switch (State.Value)
{
case ArmedState.Idle:
this.Delay(HitObject.HitWindowMiss).Expire();
@@ -3,6 +3,7 @@
using System;
using System.Linq;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
@@ -24,27 +25,25 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
}
private bool processedSecondHit;
public override bool AllJudged => processedSecondHit && base.AllJudged;
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!base.AllJudged)
if (!SecondHitAllowed)
{
base.CheckForJudgements(userTriggered, timeOffset);
return;
}
if (!userTriggered)
{
if (timeOffset > second_hit_window)
AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Miss });
return;
}
// If we get here, we're assured that the key pressed is the correct secondary key
if (Math.Abs(firstHitTime - Time.Current) < second_hit_window)
{
AddJudgement(new TaikoStrongHitJudgement());
processedSecondHit = true;
}
AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Great });
}
public override bool OnReleased(TaikoAction action)
@@ -56,8 +55,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public override bool OnPressed(TaikoAction action)
{
if (AllJudged)
return false;
// Check if we've handled the first key
if (!base.AllJudged)
if (!SecondHitAllowed)
{
// First key hasn't been handled yet, attempt to handle it
bool handled = base.OnPressed(action);
@@ -72,10 +74,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
return handled;
}
// If we've already hit the second key, don't handle this object any further
if (processedSecondHit)
return false;
// Don't handle represses of the first key
if (firstHitAction == action)
return false;
+6 -1
View File
@@ -244,7 +244,12 @@ namespace osu.Game.Rulesets.Taiko.UI
if (judgedObject.X >= -0.05f && judgedObject is DrawableHit)
{
// If we're far enough away from the left stage, we should bring outselves in front of it
topLevelHitContainer.Add(judgedObject.CreateProxy());
// Todo: The following try-catch is temporary for replay rewinding support
try
{
topLevelHitContainer.Add(judgedObject.CreateProxy());
}
catch { }
}
hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
@@ -0,0 +1,52 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Timing;
using osu.Game.Screens.Menu;
namespace osu.Game.Tests.Visual
{
public class TestCaseIntroSequence : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(OsuLogo),
};
public TestCaseIntroSequence()
{
OsuLogo logo;
var rateAdjustClock = new StopwatchClock(true);
var framedClock = new FramedClock(rateAdjustClock);
framedClock.ProcessFrame();
Add(new Container
{
RelativeSizeAxes = Axes.Both,
Clock = framedClock,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
logo = new OsuLogo
{
Anchor = Anchor.Centre,
}
}
});
AddStep(@"Restart", logo.PlayIntro);
AddSliderStep("Playback speed", 0.0, 2.0, 1, v => rateAdjustClock.Rate = v);
}
}
}
+39
View File
@@ -0,0 +1,39 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Timing;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
{
public class TestCaseOsuGame : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(OsuLogo),
};
public TestCaseOsuGame()
{
var rateAdjustClock = new StopwatchClock(true);
var framedClock = new FramedClock(rateAdjustClock);
framedClock.ProcessFrame();
Add(new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
});
Add(new Loader());
AddSliderStep("Playback speed", 0.0, 2.0, 1, v => rateAdjustClock.Rate = v);
}
}
}
@@ -3,8 +3,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Database;
@@ -61,7 +64,7 @@ namespace osu.Game.Tests.Visual
return new BeatmapSetInfo
{
OnlineBeatmapSetID = 1234 + i,
Hash = "d8e8fca2dc0f896fd7cb4cb0031ba249",
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
OnlineBeatmapSetID = 1234 + i,
@@ -3,7 +3,7 @@
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Play.ReplaySettings;
namespace osu.Game.Tests.Visual
+1 -1
View File
@@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual
}
});
AddStep("Show cookiezi", () => ranks.User = new User { Id = 124493 });
AddStep("Show cookiezi", () => ranks.User.Value = new User { Id = 124493 });
}
}
}
+2
View File
@@ -110,6 +110,7 @@
<Compile Include="Visual\TestCaseGamefield.cs" />
<Compile Include="Visual\TestCaseGraph.cs" />
<Compile Include="Visual\TestCaseIconButton.cs" />
<Compile Include="Visual\TestCaseIntroSequence.cs" />
<Compile Include="Visual\TestCaseKeyConfiguration.cs" />
<Compile Include="Visual\TestCaseKeyCounter.cs" />
<Compile Include="Visual\TestCaseLeaderboard.cs" />
@@ -121,6 +122,7 @@
<Compile Include="Visual\TestCaseNotificationOverlay.cs" />
<Compile Include="Visual\TestCaseOnScreenDisplay.cs" />
<Compile Include="Visual\TestCaseAllPlayers.cs" />
<Compile Include="Visual\TestCaseOsuGame.cs" />
<Compile Include="Visual\TestCasePlaySongSelect.cs" />
<Compile Include="Visual\TestCaseReplay.cs" />
<Compile Include="Visual\TestCaseReplaySettingsOverlay.cs" />
+15 -3
View File
@@ -6,12 +6,13 @@ using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Newtonsoft.Json;
using osu.Game.Database;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets;
namespace osu.Game.Beatmaps
{
public class BeatmapInfo : IEquatable<BeatmapInfo>, IJsonSerializable
public class BeatmapInfo : IEquatable<BeatmapInfo>, IJsonSerializable, IHasPrimaryKey
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
@@ -19,12 +20,23 @@ namespace osu.Game.Beatmaps
//TODO: should be in database
public int BeatmapVersion;
private int? onlineBeatmapID;
private int? onlineBeatmapSetID;
[JsonProperty("id")]
public int? OnlineBeatmapID { get; set; }
public int? OnlineBeatmapID
{
get { return onlineBeatmapID; }
set { onlineBeatmapID = value > 0 ? value : null; }
}
[JsonProperty("beatmapset_id")]
[NotMapped]
public int? OnlineBeatmapSetID { get; set; }
public int? OnlineBeatmapSetID
{
get { return onlineBeatmapSetID; }
set { onlineBeatmapSetID = value > 0 ? value : null; }
}
public int BeatmapSetInfoID { get; set; }
+16 -9
View File
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Ionic.Zip;
using Microsoft.EntityFrameworkCore;
@@ -244,11 +245,17 @@ namespace osu.Game.Beatmaps
request.Success += data =>
{
downloadNotification.State = ProgressNotificationState.Completed;
downloadNotification.Text = $"Importing {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}";
using (var stream = new MemoryStream(data))
using (var archive = new OszArchiveReader(stream))
Import(archive);
Task.Factory.StartNew(() =>
{
// This gets scheduled back to the update thread, but we want the import to run in the background.
using (var stream = new MemoryStream(data))
using (var archive = new OszArchiveReader(stream))
Import(archive);
downloadNotification.State = ProgressNotificationState.Completed;
}, TaskCreationOptions.LongRunning);
currentDownloads.Remove(request);
};
@@ -272,7 +279,7 @@ namespace osu.Game.Beatmaps
PostNotification?.Invoke(downloadNotification);
// don't run in the main api queue as this is a long-running task.
Task.Run(() => request.Perform(api));
Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
return request;
}
@@ -372,7 +379,7 @@ namespace osu.Game.Beatmaps
/// </summary>
/// <param name="query">The query.</param>
/// <returns>The first result for the provided query, or null if no results were found.</returns>
public BeatmapSetInfo QueryBeatmapSet(Func<BeatmapSetInfo, bool> query) => beatmaps.BeatmapSets.FirstOrDefault(query);
public BeatmapSetInfo QueryBeatmapSet(Expression<Func<BeatmapSetInfo, bool>> query) => beatmaps.BeatmapSets.AsNoTracking().FirstOrDefault(query);
/// <summary>
/// Refresh an existing instance of a <see cref="BeatmapSetInfo"/> from the store.
@@ -386,21 +393,21 @@ namespace osu.Game.Beatmaps
/// </summary>
/// <param name="query">The query.</param>
/// <returns>Results from the provided query.</returns>
public List<BeatmapSetInfo> QueryBeatmapSets(Func<BeatmapSetInfo, bool> query) => beatmaps.BeatmapSets.Where(query).ToList();
public IEnumerable<BeatmapSetInfo> QueryBeatmapSets(Expression<Func<BeatmapSetInfo, bool>> query) => beatmaps.BeatmapSets.AsNoTracking().Where(query);
/// <summary>
/// Perform a lookup query on available <see cref="BeatmapInfo"/>s.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>The first result for the provided query, or null if no results were found.</returns>
public BeatmapInfo QueryBeatmap(Func<BeatmapInfo, bool> query) => beatmaps.Beatmaps.FirstOrDefault(query);
public BeatmapInfo QueryBeatmap(Expression<Func<BeatmapInfo, bool>> query) => beatmaps.Beatmaps.AsNoTracking().FirstOrDefault(query);
/// <summary>
/// Perform a lookup query on available <see cref="BeatmapInfo"/>s.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>Results from the provided query.</returns>
public List<BeatmapInfo> QueryBeatmaps(Func<BeatmapInfo, bool> query) => beatmaps.Beatmaps.Where(query).ToList();
public IEnumerable<BeatmapInfo> QueryBeatmaps(Expression<Func<BeatmapInfo, bool>> query) => beatmaps.Beatmaps.AsNoTracking().Where(query);
/// <summary>
/// Creates an <see cref="ArchiveReader"/> from a valid storage path.
+8 -1
View File
@@ -14,8 +14,15 @@ namespace osu.Game.Beatmaps
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
private int? onlineBeatmapSetID;
[NotMapped]
public int? OnlineBeatmapSetID { get; set; }
[JsonProperty(@"id")]
public int? OnlineBeatmapSetID
{
get { return onlineBeatmapSetID; }
set { onlineBeatmapSetID = value > 0 ? value : null; }
}
public string Title { get; set; }
public string TitleUnicode { get; set; }
+2 -1
View File
@@ -4,10 +4,11 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using osu.Game.Database;
namespace osu.Game.Beatmaps
{
public class BeatmapSetInfo
public class BeatmapSetInfo : IHasPrimaryKey
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
+19 -35
View File
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using osu.Game.Database;
@@ -25,21 +24,6 @@ namespace osu.Game.Beatmaps
{
}
protected override void Prepare(bool reset = false)
{
if (reset)
{
var context = GetContext();
// https://stackoverflow.com/a/10450893
context.Database.ExecuteSqlCommand("DELETE FROM BeatmapMetadata");
context.Database.ExecuteSqlCommand("DELETE FROM BeatmapDifficulty");
context.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetInfo");
context.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetFileInfo");
context.Database.ExecuteSqlCommand("DELETE FROM BeatmapInfo");
}
}
/// <summary>
/// Add a <see cref="BeatmapSetInfo"/> to the database.
/// </summary>
@@ -63,10 +47,10 @@ namespace osu.Game.Beatmaps
{
var context = GetContext();
if (beatmapSet.DeletePending) return false;
Refresh(ref beatmapSet, BeatmapSets);
if (beatmapSet.DeletePending) return false;
beatmapSet.DeletePending = true;
context.Update(beatmapSet);
context.SaveChanges();
BeatmapSetRemoved?.Invoke(beatmapSet);
@@ -82,10 +66,10 @@ namespace osu.Game.Beatmaps
{
var context = GetContext();
if (!beatmapSet.DeletePending) return false;
Refresh(ref beatmapSet, BeatmapSets);
if (!beatmapSet.DeletePending) return false;
beatmapSet.DeletePending = false;
context.Update(beatmapSet);
context.SaveChanges();
BeatmapSetAdded?.Invoke(beatmapSet);
@@ -101,10 +85,10 @@ namespace osu.Game.Beatmaps
{
var context = GetContext();
if (beatmap.Hidden) return false;
Refresh(ref beatmap, Beatmaps);
if (beatmap.Hidden) return false;
beatmap.Hidden = true;
context.Update(beatmap);
context.SaveChanges();
BeatmapHidden?.Invoke(beatmap);
@@ -120,10 +104,10 @@ namespace osu.Game.Beatmaps
{
var context = GetContext();
if (!beatmap.Hidden) return false;
Refresh(ref beatmap, Beatmaps);
if (!beatmap.Hidden) return false;
beatmap.Hidden = false;
context.Update(beatmap);
context.SaveChanges();
BeatmapRestored?.Invoke(beatmap);
@@ -151,17 +135,17 @@ namespace osu.Game.Beatmaps
context.SaveChanges();
}
public IEnumerable<BeatmapSetInfo> BeatmapSets => GetContext().BeatmapSetInfo
.Include(s => s.Metadata)
.Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset)
.Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
.Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
.Include(s => s.Files).ThenInclude(f => f.FileInfo);
public IQueryable<BeatmapSetInfo> BeatmapSets => GetContext().BeatmapSetInfo
.Include(s => s.Metadata)
.Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset)
.Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
.Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
.Include(s => s.Files).ThenInclude(f => f.FileInfo);
public IEnumerable<BeatmapInfo> Beatmaps => GetContext().BeatmapInfo
.Include(b => b.BeatmapSet).ThenInclude(s => s.Metadata)
.Include(b => b.Metadata)
.Include(b => b.Ruleset)
.Include(b => b.BaseDifficulty);
public IQueryable<BeatmapInfo> Beatmaps => GetContext().BeatmapInfo
.Include(b => b.BeatmapSet).ThenInclude(s => s.Metadata)
.Include(b => b.Metadata)
.Include(b => b.Ruleset)
.Include(b => b.BaseDifficulty);
}
}
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Lists;
@@ -85,6 +86,9 @@ namespace osu.Game.Beatmaps.ControlPoints
private T binarySearch<T>(SortedList<T> list, double time, T prePoint = null)
where T : ControlPoint, new()
{
if (list == null)
throw new ArgumentNullException(nameof(list));
if (list.Count == 0)
return new T();
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites;
@@ -12,6 +13,9 @@ namespace osu.Game.Beatmaps.Drawables
public BeatmapBackgroundSprite(WorkingBeatmap working)
{
if (working == null)
throw new ArgumentNullException(nameof(working));
this.working = working;
}
@@ -72,6 +72,11 @@ namespace osu.Game.Beatmaps.Drawables
public BeatmapGroup(BeatmapSetInfo beatmapSet, BeatmapManager manager)
{
if (beatmapSet == null)
throw new ArgumentNullException(nameof(beatmapSet));
if (manager == null)
throw new ArgumentNullException(nameof(manager));
BeatmapSet = beatmapSet;
WorkingBeatmap beatmap = manager.GetWorkingBeatmap(BeatmapSet.Beatmaps.FirstOrDefault());
@@ -73,6 +73,9 @@ namespace osu.Game.Beatmaps.Drawables
public BeatmapPanel(BeatmapInfo beatmap)
{
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
Beatmap = beatmap;
Height *= 0.60f;
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
@@ -12,6 +13,9 @@ namespace osu.Game.Beatmaps.Drawables
private readonly BeatmapSetInfo set;
public BeatmapSetCover(BeatmapSetInfo set)
{
if (set == null)
throw new ArgumentNullException(nameof(set));
this.set = set;
}
@@ -36,6 +36,9 @@ namespace osu.Game.Beatmaps.Drawables
public BeatmapSetHeader(WorkingBeatmap beatmap)
{
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
this.beatmap = beatmap;
Children = new Drawable[]
@@ -88,6 +91,9 @@ namespace osu.Game.Beatmaps.Drawables
[BackgroundDependencyLoader]
private void load(LocalisationEngine localisation)
{
if (localisation == null)
throw new ArgumentNullException(nameof(localisation));
title.Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title);
artist.Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist);
}
@@ -154,6 +160,9 @@ namespace osu.Game.Beatmaps.Drawables
public void AddDifficultyIcons(IEnumerable<BeatmapPanel> panels)
{
if (panels == null)
throw new ArgumentNullException(nameof(panels));
foreach (var p in panels)
difficultyIcons.Add(new DifficultyIcon(p.Beatmap));
}
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
@@ -23,6 +24,9 @@ namespace osu.Game.Beatmaps.Drawables
[BackgroundDependencyLoader]
private void load(OsuColour palette)
{
if (palette == null)
throw new ArgumentNullException(nameof(palette));
this.palette = palette;
AccentColour = getColour(beatmap);
}
@@ -39,6 +43,9 @@ namespace osu.Game.Beatmaps.Drawables
private DifficultyRating getDifficultyRating(BeatmapInfo beatmap)
{
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
var rating = beatmap.StarDifficulty;
if (rating < 1.5) return DifficultyRating.Easy;
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
@@ -15,6 +16,9 @@ namespace osu.Game.Beatmaps.Drawables
public DifficultyIcon(BeatmapInfo beatmap) : base(beatmap)
{
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
this.beatmap = beatmap;
Size = new Vector2(20);
}
@@ -18,6 +18,9 @@ namespace osu.Game.Beatmaps.Formats
public static BeatmapDecoder GetDecoder(StreamReader stream)
{
if (stream == null)
throw new ArgumentNullException(nameof(stream));
string line;
do { line = stream.ReadLine()?.Trim(); }
while (line != null && line.Length == 0);
@@ -70,6 +70,11 @@ namespace osu.Game.Beatmaps.Formats
private void handleGeneral(Beatmap beatmap, string line)
{
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
if (line == null)
throw new ArgumentNullException(nameof(line));
var pair = splitKeyVal(line, ':');
var metadata = beatmap.BeatmapInfo.Metadata;
@@ -129,6 +134,11 @@ namespace osu.Game.Beatmaps.Formats
private void handleEditor(Beatmap beatmap, string line)
{
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
if (line == null)
throw new ArgumentNullException(nameof(line));
var pair = splitKeyVal(line, ':');
switch (pair.Key)
@@ -153,6 +163,11 @@ namespace osu.Game.Beatmaps.Formats
private void handleMetadata(Beatmap beatmap, string line)
{
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
if (line == null)
throw new ArgumentNullException(nameof(line));
var pair = splitKeyVal(line, ':');
var metadata = beatmap.BeatmapInfo.Metadata;
@@ -194,6 +209,11 @@ namespace osu.Game.Beatmaps.Formats
private void handleDifficulty(Beatmap beatmap, string line)
{
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
if (line == null)
throw new ArgumentNullException(nameof(line));
var pair = splitKeyVal(line, ':');
var difficulty = beatmap.BeatmapInfo.BaseDifficulty;
@@ -226,6 +246,9 @@ namespace osu.Game.Beatmaps.Formats
/// <param name="line">The line which may contains variables.</param>
private void decodeVariables(ref string line)
{
if (line == null)
throw new ArgumentNullException(nameof(line));
while (line.IndexOf('$') >= 0)
{
string origLine = line;
@@ -244,6 +267,11 @@ namespace osu.Game.Beatmaps.Formats
private void handleEvents(Beatmap beatmap, string line, ref StoryboardSprite storyboardSprite, ref CommandTimelineGroup timelineGroup)
{
if (line == null)
throw new ArgumentNullException(nameof(line));
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
var depth = 0;
while (line.StartsWith(" ") || line.StartsWith("_"))
{
@@ -469,6 +497,11 @@ namespace osu.Game.Beatmaps.Formats
private void handleTimingPoints(Beatmap beatmap, string line)
{
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
if (line == null)
throw new ArgumentNullException(nameof(line));
string[] split = line.Split(',');
double time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo);
@@ -555,6 +588,11 @@ namespace osu.Game.Beatmaps.Formats
private void handleColours(Beatmap beatmap, string line, ref bool hasCustomColours)
{
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
if (line == null)
throw new ArgumentNullException(nameof(line));
var pair = splitKeyVal(line, ':');
string[] split = pair.Value.Split(',');
@@ -587,6 +625,9 @@ namespace osu.Game.Beatmaps.Formats
private void handleVariables(string line)
{
if (line == null)
throw new ArgumentNullException(nameof(line));
var pair = splitKeyVal(line, '=');
variables[pair.Key] = pair.Value;
}
@@ -603,6 +644,11 @@ namespace osu.Game.Beatmaps.Formats
protected override void ParseFile(StreamReader stream, Beatmap beatmap)
{
if (beatmap == null)
throw new ArgumentNullException(nameof(beatmap));
if (stream == null)
throw new ArgumentNullException(nameof(stream));
beatmap.BeatmapInfo.BeatmapVersion = beatmapVersion;
Section section = Section.None;
@@ -679,6 +725,9 @@ namespace osu.Game.Beatmaps.Formats
private KeyValuePair<string, string> splitKeyVal(string line, char separator)
{
if (line == null)
throw new ArgumentNullException(nameof(line));
var split = line.Trim().Split(new[] { separator }, 2);
return new KeyValuePair<string, string>
+7 -9
View File
@@ -16,12 +16,12 @@ namespace osu.Game.Configuration
Set(OsuSetting.Ruleset, 0, 0, int.MaxValue);
Set(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Details);
Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10);
Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10);
Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1);
Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10, 0.1);
Set(OsuSetting.SelectionRandomType, SelectionRandomType.RandomPermutation);
Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1);
Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1, 0.01);
// Online settings
Set(OsuSetting.Username, string.Empty);
@@ -41,11 +41,11 @@ namespace osu.Game.Configuration
Set(OsuSetting.MenuVoice, true);
Set(OsuSetting.MenuMusic, true);
Set(OsuSetting.AudioOffset, 0, -500.0, 500.0);
Set(OsuSetting.AudioOffset, 0, -500.0, 500.0, 1);
// Input
Set(OsuSetting.MenuCursorSize, 1.0, 0.5f, 2);
Set(OsuSetting.GameplayCursorSize, 1.0, 0.5f, 2);
Set(OsuSetting.MenuCursorSize, 1.0, 0.5f, 2, 0.01);
Set(OsuSetting.GameplayCursorSize, 1.0, 0.5f, 2, 0.01);
Set(OsuSetting.AutoCursorSize, false);
Set(OsuSetting.MouseDisableButtons, false);
@@ -63,13 +63,12 @@ namespace osu.Game.Configuration
Set(OsuSetting.SnakingOutSliders, true);
// Gameplay
Set(OsuSetting.DimLevel, 0.3, 0, 1);
Set(OsuSetting.DimLevel, 0.3, 0, 1, 0.01);
Set(OsuSetting.ShowInterface, true);
Set(OsuSetting.KeyOverlay, false);
Set(OsuSetting.FloatingComments, false);
Set(OsuSetting.PlaybackSpeed, 1.0, 0.5f, 2);
// Update
Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
@@ -93,7 +92,6 @@ namespace osu.Game.Configuration
ShowStoryboard,
KeyOverlay,
FloatingComments,
PlaybackSpeed,
ShowInterface,
MouseDisableButtons,
MouseDisableWheel,
+19 -21
View File
@@ -2,8 +2,10 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using osu.Framework.Logging;
using Microsoft.EntityFrameworkCore;
using osu.Framework.Platform;
namespace osu.Game.Database
@@ -19,6 +21,22 @@ namespace osu.Game.Database
private readonly ThreadLocal<OsuDbContext> queryContext;
/// <summary>
/// Refresh an instance potentially from a different thread with a local context-tracked instance.
/// </summary>
/// <param name="obj">The object to use as a reference when negotiating a local instance.</param>
/// <param name="lookupSource">An optional lookup source which will be used to query and populate a freshly retrieved replacement. If not provided, the refreshed object will still be returned but will not have any includes.</param>
/// <typeparam name="T">A valid EF-stored type.</typeparam>
protected virtual void Refresh<T>(ref T obj, IEnumerable<T> lookupSource = null) where T : class, IHasPrimaryKey
{
var context = GetContext();
if (context.Entry(obj).State != EntityState.Detached) return;
var id = obj.ID;
obj = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find<T>(id);
}
/// <summary>
/// Retrieve a shared context for performing lookups (or write operations on the update thread, for now).
/// </summary>
@@ -32,16 +50,6 @@ namespace osu.Game.Database
queryContext = new ThreadLocal<OsuDbContext>(CreateContext);
Storage = storage;
try
{
Prepare();
}
catch (Exception e)
{
Logger.Error(e, $@"Failed to initialise the {GetType()}! Trying again with a clean database...");
Prepare(true);
}
}
/// <summary>
@@ -50,15 +58,5 @@ namespace osu.Game.Database
public virtual void Cleanup()
{
}
/// <summary>
/// Prepare this database for use. Tables should be created here.
/// </summary>
protected abstract void Prepare(bool reset = false);
/// <summary>
/// Reset this database to a default state. Undo all changes to database and storage backings.
/// </summary>
public void Reset() => Prepare(true);
}
}
+10
View File
@@ -0,0 +1,10 @@
// 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.Database
{
public interface IHasPrimaryKey
{
int ID { get; set; }
}
}
+5 -4
View File
@@ -78,11 +78,12 @@ namespace osu.Game.Database
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.MD5Hash);
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.Hash);
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.MD5Hash).IsUnique();
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.Hash).IsUnique();
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.OnlineBeatmapSetID).IsUnique();
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.DeletePending);
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.Hash);
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.Hash).IsUnique();
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.Variant);
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.IntAction);
@@ -251,7 +252,7 @@ namespace osu.Game.Database
Database.ExecuteSqlCommand("DROP TABLE RulesetInfo_Old");
Database.ExecuteSqlCommand(
"INSERT INTO BeatmapInfo SELECT ID, AudioLeadIn, BaseDifficultyID, BeatDivisor, BeatmapSetInfoID, Countdown, DistanceSpacing, GridSize, Hash, IFNULL(Hidden, 0), LetterboxInBreaks, MD5Hash, NULLIF(BeatmapMetadataID, 0), OnlineBeatmapID, Path, RulesetID, SpecialStyle, StackLeniency, StarDifficulty, StoredBookmarks, TimelineZoom, Version, WidescreenStoryboard FROM BeatmapInfo_Old");
"INSERT INTO BeatmapInfo SELECT ID, AudioLeadIn, BaseDifficultyID, BeatDivisor, BeatmapSetInfoID, Countdown, DistanceSpacing, GridSize, Hash, IFNULL(Hidden, 0), LetterboxInBreaks, MD5Hash, NULLIF(BeatmapMetadataID, 0), NULLIF(OnlineBeatmapID, 0), Path, RulesetID, SpecialStyle, StackLeniency, StarDifficulty, StoredBookmarks, TimelineZoom, Version, WidescreenStoryboard FROM BeatmapInfo_Old");
Database.ExecuteSqlCommand("DROP TABLE BeatmapInfo_Old");
Logger.Log("Migration complete!", LoggingTarget.Database, Framework.Logging.LogLevel.Important);
@@ -11,7 +11,7 @@ using osu.Framework.Configuration;
namespace osu.Game.Graphics.Containers
{
internal class ParallaxContainer : Container, IRequireHighFrequencyMousePosition
public class ParallaxContainer : Container, IRequireHighFrequencyMousePosition
{
public float ParallaxAmount = 0.02f;
@@ -6,6 +6,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Transforms;
namespace osu.Game.Graphics.Sprites
{
@@ -40,4 +41,23 @@ namespace osu.Game.Graphics.Sprites
return base.CreateFallbackCharacterDrawable();
}
}
public static class OsuSpriteTextTransformExtensions
{
/// <summary>
/// Sets <see cref="OsuSpriteText.Text"/> to a new value after a duration.
/// </summary>
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
public static TransformSequence<T> TransformTextTo<T>(this T spriteText, string newText, double duration = 0, Easing easing = Easing.None)
where T : OsuSpriteText
=> spriteText.TransformTo(nameof(OsuSpriteText.Text), newText, duration, easing);
/// <summary>
/// Sets <see cref="OsuSpriteText.Text"/> to a new value after a duration.
/// </summary>
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
public static TransformSequence<T> TransformTextTo<T>(this TransformSequence<T> t, string newText, double duration = 0, Easing easing = Easing.None)
where T : OsuSpriteText
=> t.Append(o => o.TransformTextTo(newText, duration, easing));
}
}
-12
View File
@@ -4,7 +4,6 @@
using System;
using System.IO;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using osu.Framework.Extensions;
using osu.Framework.IO.Stores;
using osu.Framework.Logging;
@@ -27,17 +26,6 @@ namespace osu.Game.IO
Store = new StorageBackedResourceStore(Storage);
}
protected override void Prepare(bool reset = false)
{
if (reset)
{
if (Storage.ExistsDirectory(string.Empty))
Storage.DeleteDirectory(string.Empty);
GetContext().Database.ExecuteSqlCommand("DELETE FROM FileInfo");
}
}
public FileInfo Add(Stream data, bool reference = true)
{
var context = GetContext();
@@ -3,11 +3,12 @@
using System.ComponentModel.DataAnnotations.Schema;
using osu.Framework.Input.Bindings;
using osu.Game.Database;
namespace osu.Game.Input.Bindings
{
[Table("KeyBinding")]
public class DatabasedKeyBinding : KeyBinding
public class DatabasedKeyBinding : KeyBinding, IHasPrimaryKey
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
+10 -11
View File
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using osu.Framework.Input.Bindings;
using osu.Framework.Platform;
using osu.Game.Database;
@@ -30,12 +29,6 @@ namespace osu.Game.Input
public void Register(KeyBindingInputManager manager) => insertDefaults(manager.DefaultKeyBindings);
protected override void Prepare(bool reset = false)
{
if (reset)
GetContext().Database.ExecuteSqlCommand("DELETE FROM KeyBinding");
}
private void insertDefaults(IEnumerable<KeyBinding> defaults, int? rulesetId = null, int? variant = null)
{
var context = GetContext();
@@ -67,18 +60,24 @@ namespace osu.Game.Input
}
/// <summary>
/// Retrieve <see cref="KeyBinding"/>s for a specified ruleset/variant content.
/// Retrieve <see cref="DatabasedKeyBinding"/>s for a specified ruleset/variant content.
/// </summary>
/// <param name="rulesetId">The ruleset's internal ID.</param>
/// <param name="variant">An optional variant.</param>
/// <returns></returns>
public IEnumerable<KeyBinding> Query(int? rulesetId = null, int? variant = null) =>
GetContext().DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant);
public List<DatabasedKeyBinding> Query(int? rulesetId = null, int? variant = null) =>
GetContext().DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList();
public void Update(KeyBinding keyBinding)
{
var dbKeyBinding = (DatabasedKeyBinding)keyBinding;
var context = GetContext();
context.Update(keyBinding);
Refresh(ref dbKeyBinding);
dbKeyBinding.KeyCombination = keyBinding.KeyCombination;
context.SaveChanges();
KeyBindingChanged?.Invoke();
@@ -0,0 +1,299 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using osu.Game.Database;
using System;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20171025071459_AddMissingIndexRules")]
partial class AddMissingIndexRules
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<float>("SliderMultiplier");
b.Property<float>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("AudioLeadIn");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MD5Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.HasKey("ID");
b.HasIndex("Available");
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,82 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace osu.Game.Migrations
{
public partial class AddMissingIndexRules : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_BeatmapSetInfo_Hash",
table: "BeatmapSetInfo");
migrationBuilder.DropIndex(
name: "IX_BeatmapInfo_Hash",
table: "BeatmapInfo");
migrationBuilder.DropIndex(
name: "IX_BeatmapInfo_MD5Hash",
table: "BeatmapInfo");
migrationBuilder.CreateIndex(
name: "IX_BeatmapSetInfo_Hash",
table: "BeatmapSetInfo",
column: "Hash",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_BeatmapSetInfo_OnlineBeatmapSetID",
table: "BeatmapSetInfo",
column: "OnlineBeatmapSetID",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_BeatmapInfo_Hash",
table: "BeatmapInfo",
column: "Hash",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_BeatmapInfo_MD5Hash",
table: "BeatmapInfo",
column: "MD5Hash",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_BeatmapSetInfo_Hash",
table: "BeatmapSetInfo");
migrationBuilder.DropIndex(
name: "IX_BeatmapSetInfo_OnlineBeatmapSetID",
table: "BeatmapSetInfo");
migrationBuilder.DropIndex(
name: "IX_BeatmapInfo_Hash",
table: "BeatmapInfo");
migrationBuilder.DropIndex(
name: "IX_BeatmapInfo_MD5Hash",
table: "BeatmapInfo");
migrationBuilder.CreateIndex(
name: "IX_BeatmapSetInfo_Hash",
table: "BeatmapSetInfo",
column: "Hash");
migrationBuilder.CreateIndex(
name: "IX_BeatmapInfo_Hash",
table: "BeatmapInfo",
column: "Hash");
migrationBuilder.CreateIndex(
name: "IX_BeatmapInfo_MD5Hash",
table: "BeatmapInfo",
column: "MD5Hash");
}
}
}
@@ -95,9 +95,11 @@ namespace osu.Game.Migrations
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MD5Hash");
b.HasIndex("MD5Hash")
.IsUnique();
b.HasIndex("MetadataID");
@@ -177,10 +179,14 @@ namespace osu.Game.Migrations
b.HasIndex("DeletePending");
b.HasIndex("Hash");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
+1
View File
@@ -118,6 +118,7 @@ namespace osu.Game.Online.API
//NotificationOverlay.ShowMessage("Login failed!");
log.Add(@"Login failed!");
Password = null;
authentication.Clear();
continue;
}
+3 -3
View File
@@ -36,7 +36,7 @@ namespace osu.Game.Online.API
return request;
}
private void request_Progress(WebRequest request, long current, long total) => API.Scheduler.Add(delegate { Progress?.Invoke(current, total); });
private void request_Progress(long current, long total) => API.Scheduler.Add(delegate { Progress?.Invoke(current, total); });
protected APIDownloadRequest()
{
@@ -99,8 +99,8 @@ namespace osu.Game.Online.API
throw new TimeoutException(@"API request timeout hit");
WebRequest = CreateWebRequest();
WebRequest.RetryCount = 0;
WebRequest.Headers[@"Authorization"] = $@"Bearer {api.AccessToken}";
WebRequest.AllowRetryOnTimeout = false;
WebRequest.AddHeader("Authorization", $"Bearer {api.AccessToken}");
if (checkAndProcessFailure())
return;
+12 -6
View File
@@ -27,6 +27,9 @@ namespace osu.Game.Online.API
internal bool AuthenticateWithLogin(string username, string password)
{
if (string.IsNullOrEmpty(username)) return false;
if (string.IsNullOrEmpty(password)) return false;
using (var req = new AccessTokenRequestPassword(username, password)
{
Url = $@"{endpoint}/oauth/token",
@@ -127,7 +130,8 @@ namespace osu.Game.Online.API
protected override void PrePerform()
{
Parameters[@"refresh_token"] = RefreshToken;
AddParameter("refresh_token", RefreshToken);
base.PrePerform();
}
}
@@ -146,8 +150,9 @@ namespace osu.Game.Online.API
protected override void PrePerform()
{
Parameters[@"username"] = Username;
Parameters[@"password"] = Password;
AddParameter("username", Username);
AddParameter("password", Password);
base.PrePerform();
}
}
@@ -161,9 +166,10 @@ namespace osu.Game.Online.API
protected override void PrePerform()
{
Parameters[@"grant_type"] = GrantType;
Parameters[@"client_id"] = ClientId;
Parameters[@"client_secret"] = ClientSecret;
AddParameter("grant_type", GrantType);
AddParameter("client_id", ClientId);
AddParameter("client_secret", ClientSecret);
base.PrePerform();
}
}
@@ -9,7 +9,7 @@ using osu.Game.Rulesets;
namespace osu.Game.Online.API.Requests
{
public class GetBeatmapSetsResponse : BeatmapMetadata
public class GetBeatmapSetsResponse : BeatmapMetadata // todo: this is a bit wrong...
{
[JsonProperty(@"covers")]
private BeatmapSetOnlineCovers covers { get; set; }
@@ -23,9 +23,6 @@ namespace osu.Game.Online.API.Requests
[JsonProperty(@"favourite_count")]
private int favouriteCount { get; set; }
[JsonProperty(@"id")]
private int onlineId { get; set; }
[JsonProperty(@"user_id")]
private long creatorId {
set { Author.Id = value; }
@@ -38,7 +35,7 @@ namespace osu.Game.Online.API.Requests
{
return new BeatmapSetInfo
{
OnlineBeatmapSetID = onlineId,
OnlineBeatmapSetID = OnlineBeatmapSetID,
Metadata = this,
OnlineInfo = new BeatmapSetOnlineInfo
{
+2
View File
@@ -88,6 +88,8 @@ namespace osu.Game.Overlays.BeatmapSet
};
}
public void StopPreview() => preview.Playing.Value = false;
private class DetailBox : Container
{
private readonly Container content;
+4 -4
View File
@@ -27,7 +27,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly Container coverContainer;
private readonly OsuSpriteText title, artist;
private readonly AuthorInfo author;
private readonly Details details;
public Details Details;
private DelayedLoadWrapper cover;
@@ -42,7 +42,7 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return;
beatmapSet = value;
Picker.BeatmapSet = author.BeatmapSet = details.BeatmapSet = BeatmapSet;
Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet;
title.Text = BeatmapSet.Metadata.Title;
artist.Text = BeatmapSet.Metadata.Artist;
@@ -192,7 +192,7 @@ namespace osu.Game.Overlays.BeatmapSet
},
},
},
details = new Details
Details = new Details
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
@@ -204,7 +204,7 @@ namespace osu.Game.Overlays.BeatmapSet
Picker.Beatmap.ValueChanged += b =>
{
details.Beatmap = b;
Details.Beatmap = b;
if (b.OnlineInfo.HasVideo)
{
@@ -26,7 +26,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly PlayButton playButton;
private Track preview => playButton.Preview;
private Bindable<bool> playing => playButton.Playing;
public Bindable<bool> Playing => playButton.Playing;
public BeatmapSetInfo BeatmapSet
{
@@ -66,8 +66,8 @@ namespace osu.Game.Overlays.BeatmapSet
},
};
Action = () => playing.Value = !playing.Value;
playing.ValueChanged += newValue => progress.FadeTo(newValue ? 1 : 0, 100);
Action = () => Playing.Value = !Playing.Value;
Playing.ValueChanged += newValue => progress.FadeTo(newValue ? 1 : 0, 100);
}
[BackgroundDependencyLoader]
@@ -80,7 +80,7 @@ namespace osu.Game.Overlays.BeatmapSet
{
base.Update();
if (playing.Value && preview != null)
if (Playing.Value && preview != null)
{
progress.Width = (float)(preview.CurrentTime / preview.Length);
}
@@ -88,7 +88,7 @@ namespace osu.Game.Overlays.BeatmapSet
protected override void Dispose(bool isDisposing)
{
playing.Value = false;
Playing.Value = false;
base.Dispose(isDisposing);
}
+1
View File
@@ -98,6 +98,7 @@ namespace osu.Game.Overlays
protected override void PopOut()
{
base.PopOut();
header.Details.StopPreview();
FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out);
}
+11 -8
View File
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
@@ -65,9 +66,7 @@ namespace osu.Game.Overlays
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
if (beatmapSets.Any() && panels == null)
// real use case? currently only seems to be for test case
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
}
}
@@ -274,13 +273,17 @@ namespace osu.Game.Overlays
Filter.DisplayStyleControl.Dropdown.Current.Value,
Filter.Tabs.Current.Value); //todo: sort direction (?)
getSetsRequest.Success += r =>
getSetsRequest.Success += response =>
{
BeatmapSets = r?.
Select(response => response.ToBeatmapSet(rulesets)).
Where(b => beatmaps.QueryBeatmapSet(q => q.OnlineBeatmapSetID == b.OnlineBeatmapSetID) == null);
Task.Run(() =>
{
var onlineIds = response.Select(r => r.OnlineBeatmapSetID).ToList();
var presentOnlineIds = beatmaps.QueryBeatmapSets(s => onlineIds.Contains(s.OnlineBeatmapSetID)).Select(r => r.OnlineBeatmapSetID).ToList();
var sets = response.Select(r => r.ToBeatmapSet(rulesets)).Where(b => !presentOnlineIds.Contains(b.OnlineBeatmapSetID)).ToList();
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
// may not need scheduling; loads async internally.
Schedule(() => BeatmapSets = sets);
});
};
api.Queue(getSetsRequest);
+2 -7
View File
@@ -9,6 +9,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
using OpenTK.Graphics;
using osu.Framework.Configuration;
namespace osu.Game.Overlays.Profile
{
@@ -22,13 +23,7 @@ namespace osu.Game.Overlays.Profile
protected override Container<Drawable> Content => content;
public virtual User User
{
get { return user; }
set { user = value; }
}
private User user;
public readonly Bindable<User> User = new Bindable<User>();
protected ProfileSection()
{
+72 -55
View File
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using osu.Framework.Allocation;
@@ -33,6 +32,9 @@ namespace osu.Game.Overlays.Profile
{
this.user = user;
int[] userRanks = user.RankHistory?.Data ?? new[] { user.Statistics.Rank };
ranks = userRanks.SkipWhile(x => x == 0).ToArray();
Padding = new MarginPadding { Vertical = padding };
Children = new Drawable[]
{
@@ -58,19 +60,21 @@ namespace osu.Game.Overlays.Profile
Font = @"Exo2.0-RegularItalic",
TextSize = secondary_textsize
},
graph = new RankChartLineGraph
};
if (ranks.Length > 0)
{
Add(graph = new RankChartLineGraph
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Y = -secondary_textsize,
DefaultValueCount = 90,
BallRelease = updateRankTexts,
BallMove = showHistoryRankTexts
}
};
DefaultValueCount = ranks.Length,
});
ranks = user.RankHistory?.Data ?? new[] { user.Statistics.Rank };
graph.OnBallMove += showHistoryRankTexts;
}
}
private void updateRankTexts()
@@ -82,7 +86,8 @@ namespace osu.Game.Overlays.Profile
private void showHistoryRankTexts(int dayIndex)
{
rankText.Text = ranks[dayIndex] > 0 ? $"#{ranks[dayIndex]:#,0}" : "no rank";
rankText.Text = $"#{ranks[dayIndex]:#,0}";
dayIndex++;
relativeText.Text = dayIndex == ranks.Length ? "Now" : $"{ranks.Length - dayIndex} days ago";
//plural should be handled in a general way
}
@@ -90,14 +95,15 @@ namespace osu.Game.Overlays.Profile
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
graph.Colour = colours.Yellow;
if (user.Statistics.Rank > 0)
if (graph != null)
{
graph.Colour = colours.Yellow;
// use logarithmic coordinates
graph.Values = ranks.Select(x => -(float)Math.Log(x));
graph.ResetBall();
graph.SetStaticBallPosition();
}
updateRankTexts();
}
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
@@ -110,67 +116,78 @@ namespace osu.Game.Overlays.Profile
return base.Invalidate(invalidation, source, shallPropagate);
}
protected override bool OnHover(InputState state)
{
graph?.UpdateBallPosition(state.Mouse.Position.X);
graph?.ShowBall();
return base.OnHover(state);
}
protected override bool OnMouseMove(InputState state)
{
graph?.UpdateBallPosition(state.Mouse.Position.X);
return base.OnMouseMove(state);
}
protected override void OnHoverLost(InputState state)
{
if (graph != null)
{
graph.HideBall();
updateRankTexts();
}
base.OnHoverLost(state);
}
private class RankChartLineGraph : LineGraph
{
private readonly CircularContainer ball;
private bool ballShown;
private const double fade_duration = 200;
private const double transform_duration = 100;
private readonly CircularContainer staticBall;
private readonly CircularContainer movingBall;
public Action<int> BallMove;
public Action BallRelease;
public Action<int> OnBallMove;
public RankChartLineGraph()
{
Add(ball = new CircularContainer
Add(staticBall = new CircularContainer
{
Origin = Anchor.Centre,
Size = new Vector2(8),
Masking = true,
Origin = Anchor.Centre,
Alpha = 0,
RelativePositionAxes = Axes.Both,
Children = new Drawable[]
{
new Box { RelativeSizeAxes = Axes.Both }
}
Child = new Box { RelativeSizeAxes = Axes.Both }
});
Add(movingBall = new CircularContainer
{
Origin = Anchor.Centre,
Size = new Vector2(8),
Alpha = 0,
Masking = true,
RelativePositionAxes = Axes.Both,
Child = new Box { RelativeSizeAxes = Axes.Both }
});
}
public void ResetBall()
public void SetStaticBallPosition() => staticBall.Position = new Vector2(1, GetYPosition(Values.Last()));
public void UpdateBallPosition(float mouseXPosition)
{
ball.MoveTo(new Vector2(1, GetYPosition(Values.Last())), ballShown ? transform_duration : 0, Easing.OutQuint);
ball.Show();
BallRelease();
ballShown = true;
int index = calculateIndex(mouseXPosition);
movingBall.Position = calculateBallPosition(index);
OnBallMove.Invoke(index);
}
protected override bool OnMouseMove(InputState state)
{
if (ballShown)
{
var values = (IList<float>)Values;
var position = ToLocalSpace(state.Mouse.NativeState.Position);
int count = Math.Max(values.Count, DefaultValueCount);
int index = (int)Math.Round(position.X / DrawWidth * (count - 1));
if (index >= count - values.Count)
{
int i = index + values.Count - count;
float y = GetYPosition(values[i]);
if (Math.Abs(y * DrawHeight - position.Y) <= 8f)
{
ball.MoveTo(new Vector2(index / (float)(count - 1), y), transform_duration, Easing.OutQuint);
BallMove(i);
}
}
}
return base.OnMouseMove(state);
}
public void ShowBall() => movingBall.FadeIn(fade_duration);
protected override void OnHoverLost(InputState state)
public void HideBall() => movingBall.FadeOut(fade_duration);
private int calculateIndex(float mouseXPosition) => (int)Math.Round(mouseXPosition / DrawWidth * (DefaultValueCount - 1));
private Vector2 calculateBallPosition(int index)
{
if (ballShown)
ResetBall();
base.OnHoverLost(state);
float y = GetYPosition(Values.ElementAt(index));
return new Vector2(index / (float)(DefaultValueCount - 1), y);
}
}
}
@@ -1,6 +1,9 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile.Sections.Ranks;
namespace osu.Game.Overlays.Profile.Sections
{
public class HistoricalSection : ProfileSection
@@ -8,5 +11,10 @@ namespace osu.Game.Overlays.Profile.Sections
public override string Title => "Historical";
public override string Identifier => "historical";
public HistoricalSection()
{
Child = new PaginatedScoreContainer(ScoreType.Recent, User, "Recent Plays (24h)");
}
}
}
@@ -0,0 +1,49 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class DrawablePerformanceScore : DrawableScore
{
private readonly double? weight;
public DrawablePerformanceScore(Score score, double? weight = null)
: base(score)
{
this.weight = weight;
}
[BackgroundDependencyLoader]
private new void load(OsuColour colour)
{
double pp = Score.PP ?? 0;
Stats.Add(new OsuSpriteText
{
Text = $"{pp:0}pp",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
TextSize = 18,
Font = "Exo2.0-BoldItalic",
});
if (weight.HasValue)
{
Stats.Add(new OsuSpriteText
{
Text = $"weighted: {pp * weight:0}pp ({weight:P0})",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = colour.GrayA,
TextSize = 11,
Font = "Exo2.0-RegularItalic",
});
}
}
}
}
@@ -18,18 +18,16 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class DrawableScore : Container
public abstract class DrawableScore : Container
{
private readonly FillFlowContainer<OsuSpriteText> stats;
protected readonly FillFlowContainer<OsuSpriteText> Stats;
private readonly FillFlowContainer metadata;
private readonly ModContainer modContainer;
private readonly Score score;
private readonly double? weight;
protected readonly Score Score;
public DrawableScore(Score score, double? weight = null)
protected DrawableScore(Score score)
{
this.score = score;
this.weight = weight;
Score = score;
Children = new Drawable[]
{
@@ -39,7 +37,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
Width = 60,
FillMode = FillMode.Fit,
},
stats = new FillFlowContainer<OsuSpriteText>
Stats = new FillFlowContainer<OsuSpriteText>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
@@ -74,40 +72,18 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
};
}
[BackgroundDependencyLoader]
[BackgroundDependencyLoader(true)]
private void load(OsuColour colour, LocalisationEngine locale, BeatmapSetOverlay beatmapSetOverlay)
{
double pp = score.PP ?? 0;
stats.Add(new OsuSpriteText
Stats.Add(new OsuSpriteText
{
Text = $"{pp:0}pp",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
TextSize = 18,
Font = "Exo2.0-BoldItalic",
});
if (weight.HasValue)
{
stats.Add(new OsuSpriteText
{
Text = $"weighted: {pp * weight:0}pp ({weight:P0})",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = colour.GrayA,
TextSize = 11,
Font = "Exo2.0-RegularItalic",
});
}
stats.Add(new OsuSpriteText
{
Text = $"accuracy: {score.Accuracy:P2}",
Text = $"accuracy: {Score.Accuracy:P2}",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = colour.GrayA,
TextSize = 11,
Font = "Exo2.0-RegularItalic",
Depth = -1,
});
metadata.Add(new OsuHoverContainer
@@ -115,7 +91,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
AutoSizeAxes = Axes.Both,
Action = () =>
{
if (score.Beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay.ShowBeatmapSet(score.Beatmap.OnlineBeatmapSetID.Value);
if (Score.Beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.ShowBeatmapSet(Score.Beatmap.OnlineBeatmapSetID.Value);
},
Child = new FillFlowContainer
{
@@ -125,15 +101,15 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
new OsuSpriteText
{
Current = locale.GetUnicodePreference(
$"{score.Beatmap.Metadata.TitleUnicode ?? score.Beatmap.Metadata.Title} [{score.Beatmap.Version}] ",
$"{score.Beatmap.Metadata.Title ?? score.Beatmap.Metadata.TitleUnicode} [{score.Beatmap.Version}] "
$"{Score.Beatmap.Metadata.TitleUnicode ?? Score.Beatmap.Metadata.Title} [{Score.Beatmap.Version}] ",
$"{Score.Beatmap.Metadata.Title ?? Score.Beatmap.Metadata.TitleUnicode} [{Score.Beatmap.Version}] "
),
TextSize = 15,
Font = "Exo2.0-SemiBoldItalic",
},
new OsuSpriteText
{
Current = locale.GetUnicodePreference(score.Beatmap.Metadata.ArtistUnicode, score.Beatmap.Metadata.Artist),
Current = locale.GetUnicodePreference(Score.Beatmap.Metadata.ArtistUnicode, Score.Beatmap.Metadata.Artist),
TextSize = 12,
Padding = new MarginPadding { Top = 3 },
Font = "Exo2.0-RegularItalic",
@@ -142,7 +118,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
},
});
foreach (Mod mod in score.Mods)
foreach (Mod mod in Score.Mods)
modContainer.Add(new ModIcon(mod)
{
AutoSizeAxes = Axes.Both,
@@ -0,0 +1,31 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class DrawableTotalScore : DrawableScore
{
public DrawableTotalScore(Score score)
: base(score)
{
}
[BackgroundDependencyLoader]
private new void load()
{
Stats.Add(new OsuSpriteText
{
Text = Score.TotalScore.ToString("#,###"),
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
TextSize = 18,
Font = "Exo2.0-BoldItalic",
});
}
}
}
@@ -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 OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Rulesets;
using osu.Game.Users;
using System;
using System.Linq;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class PaginatedScoreContainer : FillFlowContainer
{
private readonly FillFlowContainer<DrawableScore> scoreContainer;
private readonly OsuSpriteText missing;
private readonly OsuHoverContainer showMoreButton;
private readonly LoadingAnimation showMoreLoading;
private readonly bool includeWeight;
private readonly ScoreType type;
private int visiblePages;
private readonly Bindable<User> user = new Bindable<User>();
private RulesetStore rulesets;
private APIAccess api;
public PaginatedScoreContainer(ScoreType type, Bindable<User> user, string header, bool includeWeight = false)
{
this.type = type;
this.includeWeight = includeWeight;
this.user.BindTo(user);
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Direction = FillDirection.Vertical;
Children = new Drawable[]
{
new OsuSpriteText
{
TextSize = 15,
Text = header,
Font = "Exo2.0-RegularItalic",
Margin = new MarginPadding { Top = 10, Bottom = 10 },
},
scoreContainer = new FillFlowContainer<DrawableScore>
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
},
showMoreButton = new OsuHoverContainer
{
Alpha = 0,
Action = showMore,
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Child = new OsuSpriteText
{
TextSize = 14,
Text = "show more",
}
},
showMoreLoading = new LoadingAnimation
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Size = new Vector2(14),
},
missing = new OsuSpriteText
{
TextSize = 14,
Text = type == ScoreType.Recent ? "No performance records. :(" : "No awesome performance records yet. :(",
},
};
}
[BackgroundDependencyLoader]
private void load(APIAccess api, RulesetStore rulesets)
{
this.api = api;
this.rulesets = rulesets;
user.ValueChanged += user_ValueChanged;
user.TriggerChange();
}
private void user_ValueChanged(User newUser)
{
visiblePages = 0;
scoreContainer.Clear();
showMoreButton.Hide();
missing.Show();
if (newUser != null)
showMore();
}
private void showMore()
{
var req = new GetUserScoresRequest(user.Value.Id, type, visiblePages++ * 5);
showMoreLoading.Show();
showMoreButton.Hide();
req.Success += scores =>
{
foreach (var s in scores)
s.ApplyRuleset(rulesets.GetRuleset(s.OnlineRulesetID));
showMoreButton.FadeTo(scores.Count == 5 ? 1 : 0);
showMoreLoading.Hide();
if (!scores.Any()) return;
missing.Hide();
foreach (OnlineScore score in scores)
{
DrawableScore drawableScore;
switch (type)
{
default:
drawableScore = new DrawablePerformanceScore(score, includeWeight ? Math.Pow(0.95, scoreContainer.Count) : (double?)null);
break;
case ScoreType.Recent:
drawableScore = new DrawableTotalScore(score);
break;
}
drawableScore.RelativeSizeAxes = Axes.X;
drawableScore.Height = 60;
scoreContainer.Add(drawableScore);
}
};
api.Queue(req);
}
}
}
@@ -1,20 +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 osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Profile.Sections.Ranks;
using System;
using System.Linq;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Rulesets;
using osu.Game.Users;
using osu.Game.Graphics.UserInterface;
using OpenTK;
using osu.Framework.Allocation;
namespace osu.Game.Overlays.Profile.Sections
{
@@ -24,147 +12,13 @@ namespace osu.Game.Overlays.Profile.Sections
public override string Identifier => "top_ranks";
private readonly ScoreContainer best, first;
public RanksSection()
{
Children = new Drawable[]
Children = new[]
{
best = new ScoreContainer(ScoreType.Best, "Best Performance", true),
first = new ScoreContainer(ScoreType.Firsts, "First Place Ranks"),
new PaginatedScoreContainer(ScoreType.Best, User, "Best Performance", true),
new PaginatedScoreContainer(ScoreType.Firsts, User, "First Place Ranks"),
};
}
public override User User
{
get
{
return base.User;
}
set
{
base.User = value;
best.User = value;
first.User = value;
}
}
private class ScoreContainer : FillFlowContainer
{
private readonly FillFlowContainer<DrawableScore> scoreContainer;
private readonly OsuSpriteText missing;
private readonly OsuHoverContainer showMoreButton;
private readonly LoadingAnimation showMoreLoading;
private readonly ScoreType type;
private int visiblePages;
private User user;
private readonly bool includeWeigth;
private RulesetStore rulesets;
private APIAccess api;
public User User
{
set
{
user = value;
visiblePages = 0;
scoreContainer.Clear();
showMoreButton.Hide();
missing.Show();
showMore();
}
}
public ScoreContainer(ScoreType type, string header, bool includeWeigth = false)
{
this.type = type;
this.includeWeigth = includeWeigth;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Direction = FillDirection.Vertical;
Children = new Drawable[]
{
new OsuSpriteText
{
TextSize = 15,
Text = header,
Font = "Exo2.0-RegularItalic",
Margin = new MarginPadding { Top = 10, Bottom = 10 },
},
scoreContainer = new FillFlowContainer<DrawableScore>
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
},
showMoreButton = new OsuHoverContainer
{
Alpha = 0,
Action = showMore,
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Child = new OsuSpriteText
{
TextSize = 14,
Text = "show more",
}
},
showMoreLoading = new LoadingAnimation
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Size = new Vector2(14),
},
missing = new OsuSpriteText
{
TextSize = 14,
Text = "No awesome performance records yet. :(",
},
};
}
[BackgroundDependencyLoader]
private void load(APIAccess api, RulesetStore rulesets)
{
this.api = api;
this.rulesets = rulesets;
}
private void showMore()
{
var req = new GetUserScoresRequest(user.Id, type, visiblePages++ * 5);
showMoreLoading.Show();
showMoreButton.Hide();
req.Success += scores =>
{
foreach (var s in scores)
s.ApplyRuleset(rulesets.GetRuleset(s.OnlineRulesetID));
showMoreButton.FadeTo(scores.Count == 5 ? 1 : 0);
showMoreLoading.Hide();
if (scores.Any())
{
missing.Hide();
foreach (OnlineScore score in scores)
scoreContainer.Add(new DrawableScore(score, includeWeigth ? Math.Pow(0.95, scoreContainer.Count) : (double?)null)
{
RelativeSizeAxes = Axes.X,
Height = 60,
});
}
};
Schedule(() => { api.Queue(req); });
}
}
}
}
@@ -29,7 +29,8 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
Action = () =>
{
importButton.Enabled.Value = false;
Task.Run(() => beatmaps.ImportFromStable()).ContinueWith(t => Schedule(() => importButton.Enabled.Value = true));
Task.Factory.StartNew(beatmaps.ImportFromStable)
.ContinueWith(t => Schedule(() => importButton.Enabled.Value = true), TaskContinuationOptions.LongRunning);
}
},
deleteButton = new SettingsButton
+18 -9
View File
@@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Settings
controlWithCurrent?.Current.BindTo(bindable);
if (ShowsDefaultIndicator)
{
restoreDefaultValueButton.Bindable.BindTo(bindable);
restoreDefaultValueButton.Bindable = bindable.GetBoundCopy();
restoreDefaultValueButton.Bindable.TriggerChange();
}
}
@@ -134,7 +134,17 @@ namespace osu.Game.Overlays.Settings
private class RestoreDefaultValueButton<T> : Box, IHasTooltip
{
internal readonly Bindable<T> Bindable = new Bindable<T>();
private Bindable<T> bindable;
internal Bindable<T> Bindable
{
get { return bindable; }
set
{
bindable = value;
bindable.ValueChanged += newValue => UpdateState();
bindable.DisabledChanged += disabled => UpdateState();
}
}
private Color4 buttonColour;
@@ -142,9 +152,6 @@ namespace osu.Game.Overlays.Settings
public RestoreDefaultValueButton()
{
Bindable.ValueChanged += value => UpdateState();
Bindable.DisabledChanged += disabled => UpdateState();
RelativeSizeAxes = Axes.Y;
Width = SettingsOverlay.CONTENT_MARGINS;
Alpha = 0f;
@@ -160,8 +167,8 @@ namespace osu.Game.Overlays.Settings
protected override bool OnClick(InputState state)
{
if (!Bindable.Disabled)
Bindable.SetDefault();
if (bindable != null && !bindable.Disabled)
bindable.SetDefault();
return true;
}
@@ -186,8 +193,10 @@ namespace osu.Game.Overlays.Settings
internal void UpdateState()
{
var colour = Bindable.Disabled ? Color4.Gray : buttonColour;
this.FadeTo(Bindable.IsDefault ? 0f : hovering && !Bindable.Disabled ? 1f : 0.5f, 200, Easing.OutQuint);
if (bindable == null)
return;
var colour = bindable.Disabled ? Color4.Gray : buttonColour;
this.FadeTo(bindable.IsDefault ? 0f : hovering && !bindable.Disabled ? 1f : 0.5f, 200, Easing.OutQuint);
this.FadeColour(ColourInfo.GradientHorizontal(colour.Opacity(0.8f), colour.Opacity(0)), 200, Easing.OutQuint);
}
}
+2 -2
View File
@@ -95,7 +95,7 @@ namespace osu.Game.Overlays
//new RecentSection(),
new RanksSection(),
//new MedalsSection(),
//new HistoricalSection(),
new HistoricalSection(),
//new BeatmapsSection(),
//new KudosuSection()
};
@@ -175,7 +175,7 @@ namespace osu.Game.Overlays
var sec = sections.FirstOrDefault(s => s.Identifier == id);
if (sec != null)
{
sec.User = user;
sec.User.Value = user;
sectionsContainer.Add(sec);
tabs.AddItem(sec);
@@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Judgements
break;
}
Expire();
Expire(true);
}
}
}
+18
View File
@@ -17,8 +17,26 @@ namespace osu.Game.Rulesets.Judgements
/// </summary>
public virtual HitResult MaxResult => HitResult.Perfect;
/// <summary>
/// The combo prior to this judgement occurring.
/// </summary>
internal int ComboAtJudgement;
/// <summary>
/// The highest combo achieved prior to this judgement occurring.
/// </summary>
internal int HighestComboAtJudgement;
/// <summary>
/// Whether a successful hit occurred.
/// </summary>
public bool IsHit => Result > HitResult.Miss;
/// <summary>
/// Whether this judgement is the final judgement for the hit object.
/// </summary>
public bool Final = true;
/// <summary>
/// The offset from a perfect hit at which this judgement occurred.
/// Populated when added via <see cref="DrawableHitObject{TObject}.AddJudgement"/>.
@@ -13,6 +13,7 @@ using OpenTK.Graphics;
using osu.Game.Audio;
using System.Linq;
using osu.Game.Graphics;
using osu.Framework.Configuration;
namespace osu.Game.Rulesets.Objects.Drawables
{
@@ -30,6 +31,9 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// </summary>
public virtual bool DisplayJudgement => true;
public override bool RemoveCompletedTransforms => false;
public override bool RemoveWhenNotAlive => false;
protected DrawableHitObject(HitObject hitObject)
{
HitObject = hitObject;
@@ -40,6 +44,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
where TObject : HitObject
{
public event Action<DrawableHitObject, Judgement> OnJudgement;
public event Action<DrawableHitObject, Judgement> OnJudgementRemoved;
public new readonly TObject HitObject;
@@ -56,31 +61,42 @@ namespace osu.Game.Rulesets.Objects.Drawables
protected List<SampleChannel> Samples = new List<SampleChannel>();
public readonly Bindable<ArmedState> State = new Bindable<ArmedState>();
protected DrawableHitObject(TObject hitObject)
: base(hitObject)
{
HitObject = hitObject;
}
private ArmedState state;
public ArmedState State
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
get { return state; }
set
foreach (SampleInfo sample in HitObject.Samples)
{
if (state == value)
return;
state = value;
SampleChannel channel = audio.Sample.Get($@"Gameplay/{sample.Bank}-{sample.Name}");
if (!IsLoaded)
return;
if (channel == null)
continue;
channel.Volume.Value = sample.Volume;
Samples.Add(channel);
}
}
protected override void LoadComplete()
{
base.LoadComplete();
State.ValueChanged += state =>
{
UpdateState(state);
if (State == ArmedState.Hit)
PlaySamples();
}
};
State.TriggerChange();
}
protected void PlaySamples()
@@ -88,21 +104,13 @@ namespace osu.Game.Rulesets.Objects.Drawables
Samples.ForEach(s => s?.Play());
}
protected override void LoadComplete()
{
base.LoadComplete();
//force application of the state that was set before we loaded.
UpdateState(State);
}
private bool hasJudgementResult;
private bool judgementOccurred;
private bool judgementFinalized => judgements.LastOrDefault()?.Final == true;
/// <summary>
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been judged.
/// </summary>
public virtual bool AllJudged => (!ProvidesJudgement || hasJudgementResult) && (NestedHitObjects?.All(h => h.AllJudged) ?? true);
public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (NestedHitObjects?.All(h => h.AllJudged) ?? true);
/// <summary>
/// Notifies that a new judgement has occurred for this <see cref="DrawableHitObject"/>.
@@ -110,7 +118,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// <param name="judgement">The <see cref="Judgement"/>.</param>
protected void AddJudgement(Judgement judgement)
{
hasJudgementResult = judgement.Result >= HitResult.Miss;
judgementOccurred = true;
// Ensure that the judgement is given a valid time offset, because this may not get set by the caller
@@ -124,10 +131,10 @@ namespace osu.Game.Rulesets.Objects.Drawables
case HitResult.None:
break;
case HitResult.Miss:
State = ArmedState.Miss;
State.Value = ArmedState.Miss;
break;
default:
State = ArmedState.Hit;
State.Value = ArmedState.Hit;
break;
}
@@ -152,7 +159,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
judgementOccurred |= d.UpdateJudgement(userTriggered);
}
if (!ProvidesJudgement || hasJudgementResult || judgementOccurred)
if (!ProvidesJudgement || judgementFinalized || judgementOccurred)
return judgementOccurred;
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
@@ -170,6 +177,25 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// implies that this check occurred after the end time of <see cref="HitObject"/>. </param>
protected virtual void CheckForJudgements(bool userTriggered, double timeOffset) { }
protected override void Update()
{
base.Update();
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
while (judgements.Count > 0)
{
var lastJudgement = judgements[judgements.Count - 1];
if (lastJudgement.TimeOffset + endTime <= Time.Current)
break;
judgements.RemoveAt(judgements.Count - 1);
State.Value = ArmedState.Idle;
OnJudgementRemoved?.Invoke(this, lastJudgement);
}
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
@@ -177,21 +203,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
UpdateJudgement(false);
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
foreach (SampleInfo sample in HitObject.Samples)
{
SampleChannel channel = audio.Sample.Get($@"Gameplay/{sample.Bank}-{sample.Name}");
if (channel == null)
continue;
channel.Volume.Value = sample.Volume;
Samples.Add(channel);
}
}
private List<DrawableHitObject<TObject>> nestedHitObjects;
protected IEnumerable<DrawableHitObject<TObject>> NestedHitObjects => nestedHitObjects;
@@ -104,7 +104,13 @@ namespace osu.Game.Rulesets.Replays
{
//if we changed frames, we want to execute once *exactly* on the frame's time.
if (currentDirection == time.CompareTo(NextFrame.Time) && advanceFrame())
{
// If going backwards, we need to execute once _before_ the frame time to reverse any judgements
// that would occur as a result of this frame in forward playback
if (currentDirection == -1)
return currentTime = CurrentFrame.Time - 1;
return currentTime = CurrentFrame.Time;
}
//if we didn't change frames, we need to ensure we are allowed to run frames in between, else return null.
if (inImportantSection)
+5 -8
View File
@@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using osu.Game.Database;
namespace osu.Game.Rulesets
@@ -29,6 +28,7 @@ namespace osu.Game.Rulesets
public RulesetStore(Func<OsuDbContext> factory)
: base(factory)
{
AddMissingRulesets();
}
/// <summary>
@@ -41,21 +41,16 @@ namespace osu.Game.Rulesets
/// <summary>
/// All available rulesets.
/// </summary>
public IEnumerable<RulesetInfo> AvailableRulesets => GetContext().RulesetInfo.Where(r => r.Available);
public IEnumerable<RulesetInfo> AvailableRulesets;
private static Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args) => loaded_assemblies.Keys.FirstOrDefault(a => a.FullName == args.Name);
private const string ruleset_library_prefix = "osu.Game.Rulesets";
protected override void Prepare(bool reset = false)
protected void AddMissingRulesets()
{
var context = GetContext();
if (reset)
{
context.Database.ExecuteSqlCommand("DELETE FROM RulesetInfo");
}
var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, new RulesetInfo())).ToList();
//add all legacy modes in correct order
@@ -98,6 +93,8 @@ namespace osu.Game.Rulesets
}
context.SaveChanges();
AvailableRulesets = context.RulesetInfo.Where(r => r.Available).ToList();
}
private static void loadRulesetFromFile(string file)
+40 -2
View File
@@ -174,6 +174,7 @@ namespace osu.Game.Rulesets.Scoring
private double maxBaseScore;
private double rollingMaxBaseScore;
private double baseScore;
private double bonusScore;
protected ScoreProcessor()
{
@@ -184,6 +185,7 @@ namespace osu.Game.Rulesets.Scoring
Debug.Assert(base_portion + combo_portion == 1.0);
rulesetContainer.OnJudgement += AddJudgement;
rulesetContainer.OnJudgementRemoved += RemoveJudgement;
SimulateAutoplay(rulesetContainer.Beatmap);
Reset(true);
@@ -212,14 +214,26 @@ namespace osu.Game.Rulesets.Scoring
protected void AddJudgement(Judgement judgement)
{
OnNewJudgement(judgement);
NotifyNewJudgement(judgement);
updateScore();
NotifyNewJudgement(judgement);
UpdateFailed();
}
protected void RemoveJudgement(Judgement judgement)
{
OnJudgementRemoved(judgement);
updateScore();
}
/// <summary>
/// Applies a judgement.
/// </summary>
/// <param name="judgement">The judgement to apply/</param>
protected virtual void OnNewJudgement(Judgement judgement)
{
double bonusScore = 0;
judgement.ComboAtJudgement = Combo;
judgement.HighestComboAtJudgement = HighestCombo;
if (judgement.AffectsCombo)
{
@@ -242,7 +256,30 @@ namespace osu.Game.Rulesets.Scoring
}
else if (judgement.IsHit)
bonusScore += judgement.NumericResult;
}
/// <summary>
/// Removes a judgement. This should reverse everything in <see cref="OnNewJudgement(Judgement)"/>.
/// </summary>
/// <param name="judgement">The judgement to remove.</param>
protected virtual void OnJudgementRemoved(Judgement judgement)
{
Combo.Value = judgement.ComboAtJudgement;
HighestCombo.Value = judgement.HighestComboAtJudgement;
if (judgement.AffectsCombo)
{
baseScore -= judgement.NumericResult;
rollingMaxBaseScore -= judgement.MaxNumericResult;
Hits--;
}
else if (judgement.IsHit)
bonusScore -= judgement.NumericResult;
}
private void updateScore()
{
if (rollingMaxBaseScore != 0)
Accuracy.Value = baseScore / rollingMaxBaseScore;
@@ -271,6 +308,7 @@ namespace osu.Game.Rulesets.Scoring
Hits = 0;
baseScore = 0;
rollingMaxBaseScore = 0;
bonusScore = 0;
}
}
-4
View File
@@ -143,9 +143,5 @@ namespace osu.Game.Rulesets.Scoring
return new Replay { Frames = frames };
}
protected override void Prepare(bool reset = false)
{
}
}
}
+3
View File
@@ -104,6 +104,7 @@ namespace osu.Game.Rulesets.UI
where TObject : HitObject
{
public event Action<Judgement> OnJudgement;
public event Action<Judgement> OnJudgementRemoved;
/// <summary>
/// The Beatmap
@@ -241,6 +242,8 @@ namespace osu.Game.Rulesets.UI
OnJudgement?.Invoke(j);
};
drawableObject.OnJudgementRemoved += (d, j) => { OnJudgementRemoved?.Invoke(j); };
Playfield.Add(drawableObject);
}
+1 -1
View File
@@ -24,7 +24,7 @@ namespace osu.Game.Screens.Edit
{
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
internal override bool ShowOverlays => false;
public override bool ShowOverlays => false;
private readonly Box bottomBackground;
private readonly Container screenContainer;
+41 -5
View File
@@ -2,26 +2,62 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Screens.Menu;
using OpenTK;
using osu.Framework.Screens;
namespace osu.Game.Screens
{
internal class Loader : OsuScreen
public class Loader : OsuScreen
{
internal override bool ShowOverlays => false;
private bool showDisclaimer;
public override bool ShowOverlays => false;
public Loader()
{
ValidForResume = false;
}
[BackgroundDependencyLoader]
private void load(OsuGame game)
protected override void LogoArriving(OsuLogo logo, bool resuming)
{
if (game.IsDeployedBuild)
base.LogoArriving(logo, resuming);
logo.RelativePositionAxes = Axes.None;
logo.Triangles = false;
logo.Origin = Anchor.BottomRight;
logo.Anchor = Anchor.BottomRight;
logo.Position = new Vector2(-40);
logo.Scale = new Vector2(0.2f);
logo.FadeInFromZero(5000, Easing.OutQuint);
}
protected override void OnEntering(Screen last)
{
base.OnEntering(last);
if (showDisclaimer)
LoadComponentAsync(new Disclaimer(), d => Push(d));
else
LoadComponentAsync(new Intro(), d => Push(d));
}
protected override void LogoSuspending(OsuLogo logo)
{
base.LogoSuspending(logo);
logo.FadeOut(100).OnComplete(l =>
{
l.Anchor = Anchor.TopLeft;
l.Origin = Anchor.Centre;
});
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
showDisclaimer = game.IsDeployedBuild;
}
}
}
+70 -42
View File
@@ -39,12 +39,25 @@ namespace osu.Game.Screens.Menu
//todo: make these non-internal somehow.
internal const float BUTTON_AREA_HEIGHT = 100;
internal const float BUTTON_WIDTH = 140f;
internal const float WEDGE_WIDTH = 20;
public const int EXIT_DELAY = 3000;
private OsuLogo logo;
public void SetOsuLogo(OsuLogo logo)
{
this.logo = logo;
if (this.logo != null)
{
this.logo.Action = onOsuLogo;
// osuLogo.SizeForFlow relies on loading to be complete.
buttonFlow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0);
}
}
private readonly OsuLogo osuLogo;
private readonly Drawable iconFacade;
private readonly Container buttonArea;
private readonly Box buttonAreaBackground;
@@ -99,12 +112,6 @@ namespace osu.Game.Screens.Menu
}
}
},
osuLogo = new OsuLogo
{
Action = onOsuLogo,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
}
};
buttonsPlay.Add(new Button(@"solo", @"select-6", FontAwesome.fa_user, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P));
@@ -127,14 +134,6 @@ namespace osu.Game.Screens.Menu
sampleBack = audio.Sample.Get(@"Menu/select-4");
}
protected override void LoadComplete()
{
base.LoadComplete();
// osuLogo.SizeForFlow relies on loading to be complete.
buttonFlow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + osuLogo.SizeForFlow / 4), 0);
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Repeat) return false;
@@ -142,7 +141,7 @@ namespace osu.Game.Screens.Menu
switch (args.Key)
{
case Key.Space:
osuLogo.TriggerOnClick(state);
logo?.TriggerOnClick(state);
return true;
case Key.Escape:
switch (State)
@@ -215,24 +214,31 @@ namespace osu.Game.Screens.Menu
backButton.ContractStyle = 0;
settingsButton.ContractStyle = 0;
bool fromInitial = lastState == MenuState.Initial;
if (state == MenuState.TopLevel)
buttonArea.FinishTransforms(true);
using (buttonArea.BeginDelayedSequence(fromInitial ? 150 : 0, true))
using (buttonArea.BeginDelayedSequence(lastState == MenuState.Initial ? 150 : 0, true))
{
switch (state)
{
case MenuState.Exit:
case MenuState.Initial:
trackingPosition = false;
buttonAreaBackground.ScaleTo(Vector2.One, 500, Easing.Out);
buttonArea.FadeOut(300);
osuLogo.Delay(150)
.Schedule(() => toolbar?.Hide())
.ScaleTo(1, 800, Easing.OutExpo)
.MoveTo(Vector2.Zero, 800, Easing.OutExpo);
logo?.Delay(150)
.Schedule(() =>
{
toolbar?.Hide();
logo.ClearTransforms(targetMember: nameof(Position));
logo.RelativePositionAxes = Axes.Both;
logo.MoveTo(new Vector2(0.5f), 800, Easing.OutExpo);
logo.ScaleTo(1, 800, Easing.OutExpo);
});
foreach (Button b in buttonsTopLevel)
b.State = ButtonState.Contracted;
@@ -240,27 +246,38 @@ namespace osu.Game.Screens.Menu
foreach (Button b in buttonsPlay)
b.State = ButtonState.Contracted;
if (state == MenuState.Exit)
{
osuLogo.RotateTo(20, EXIT_DELAY * 1.5f);
osuLogo.FadeOut(EXIT_DELAY);
}
else if (lastState == MenuState.TopLevel)
if (state != MenuState.Exit && lastState == MenuState.TopLevel)
sampleBack?.Play();
break;
case MenuState.TopLevel:
buttonAreaBackground.ScaleTo(Vector2.One, 200, Easing.Out);
var sequence = osuLogo
.ScaleTo(0.5f, 200, Easing.In)
.MoveTo(buttonFlow.DrawPosition, 200, Easing.In);
logo.ClearTransforms(targetMember: nameof(Position));
logo.RelativePositionAxes = Axes.None;
if (fromInitial && osuLogo.Scale.X > 0.5f)
sequence.OnComplete(o =>
{
o.Impact();
toolbar?.Show();
});
trackingPosition = true;
switch (lastState)
{
case MenuState.Initial:
logo.ScaleTo(0.5f, 200, Easing.In);
trackingPosition = false;
logo
.MoveTo(iconTrackingPosition, lastState == MenuState.EnteringMode ? 0 : 200, Easing.In)
.OnComplete(o =>
{
trackingPosition = true;
o.Impact();
toolbar?.Show();
});
break;
default:
logo.ScaleTo(0.5f, 200, Easing.OutQuint);
break;
}
buttonArea.FadeIn(300);
@@ -280,6 +297,8 @@ namespace osu.Game.Screens.Menu
case MenuState.EnteringMode:
buttonAreaBackground.ScaleTo(new Vector2(2, 0), 300, Easing.InSine);
trackingPosition = true;
buttonsTopLevel.ForEach(b => b.ContractStyle = 1);
buttonsPlay.ForEach(b => b.ContractStyle = 1);
backButton.ContractStyle = 1;
@@ -301,15 +320,24 @@ namespace osu.Game.Screens.Menu
}
}
private Vector2 iconTrackingPosition => logo.Parent.ToLocalSpace(iconFacade.ScreenSpaceDrawQuad.Centre);
private bool trackingPosition;
protected override void Update()
{
//if (OsuGame.IdleTime > 6000 && State != MenuState.Exit)
// State = MenuState.Initial;
osuLogo.Interactive = Alpha > 0.2f;
iconFacade.Width = osuLogo.SizeForFlow * 0.5f;
base.Update();
if (logo != null)
{
if (trackingPosition)
logo.Position = iconTrackingPosition;
iconFacade.Width = logo.SizeForFlow * 0.5f;
}
}
}
+3 -3
View File
@@ -12,15 +12,15 @@ using OpenTK.Graphics;
namespace osu.Game.Screens.Menu
{
internal class Disclaimer : OsuScreen
public class Disclaimer : OsuScreen
{
private Intro intro;
private readonly SpriteIcon icon;
private Color4 iconColour;
internal override bool ShowOverlays => false;
public override bool ShowOverlays => false;
internal override bool HasLocalCursorDisplayed => true;
public override bool HasLocalCursorDisplayed => true;
public Disclaimer()
{
+49 -34
View File
@@ -14,13 +14,14 @@ using osu.Game.Beatmaps.IO;
using osu.Game.Configuration;
using osu.Game.Graphics.Containers;
using osu.Game.Screens.Backgrounds;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Screens.Menu
{
public class Intro : OsuScreen
{
private readonly OsuLogo logo;
private readonly IntroSequence introSequence;
private const string menu_music_beatmap_hash = "3c8b1fcc9434dbb29e2fb613d3b9eada9d7bb6c125ceb32396c3b53437280c83";
@@ -33,38 +34,16 @@ namespace osu.Game.Screens.Menu
private SampleChannel welcome;
private SampleChannel seeya;
internal override bool HasLocalCursorDisplayed => true;
public override bool HasLocalCursorDisplayed => true;
internal override bool ShowOverlays => false;
public override bool ShowOverlays => false;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenEmpty();
public Intro()
{
Children = new Drawable[]
{
new ParallaxContainer
{
ParallaxAmount = 0.01f,
Children = new Drawable[]
{
logo = new OsuLogo
{
Alpha = 0,
Triangles = false,
Blending = BlendingMode.Additive,
Interactive = false,
Colour = Color4.DarkGray,
Ripple = false
}
}
}
};
}
private Bindable<bool> menuVoice;
private Bindable<bool> menuMusic;
private Track track;
private readonly ParallaxContainer parallax;
[BackgroundDependencyLoader]
private void load(AudioManager audio, OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game)
@@ -99,7 +78,9 @@ namespace osu.Game.Screens.Menu
welcome = audio.Sample.Get(@"welcome");
seeya = audio.Sample.Get(@"seeya");
beatmaps.Delete(setInfo);
if (setInfo.Protected)
beatmaps.Delete(setInfo);
}
protected override void OnEntering(Screen last)
@@ -121,14 +102,48 @@ namespace osu.Game.Screens.Menu
{
DidLoadMenu = true;
Push(mainMenu);
}, 2300);
}, 600);
}, delay_step_one);
}, delay_step_two);
}
logo.ScaleTo(0.4f);
logo.FadeOut();
private const double delay_step_one = 2300;
private const double delay_step_two = 600;
logo.ScaleTo(1, 4400, Easing.OutQuint);
logo.FadeIn(20000, Easing.OutQuint);
public const int EXIT_DELAY = 3000;
protected override void LogoArriving(OsuLogo logo, bool resuming)
{
base.LogoArriving(logo, resuming);
logo.RelativePositionAxes = Axes.Both;
logo.Colour = Color4.White;
logo.Ripple = false;
const int quick_appear = 350;
int initialMovementTime = logo.Alpha > 0.2f ? quick_appear : 0;
logo.MoveTo(new Vector2(0.5f), initialMovementTime, Easing.OutQuint);
if (!resuming)
{
logo.Triangles = true;
logo.ScaleTo(1);
logo.FadeIn();
logo.PlayIntro();
}
else
{
logo.Triangles = false;
logo
.ScaleTo(1, initialMovementTime, Easing.OutQuint)
.FadeIn(quick_appear, Easing.OutQuint)
.Then()
.RotateTo(20, EXIT_DELAY * 1.5f)
.FadeOut(EXIT_DELAY);
}
}
protected override void OnSuspending(Screen next)
@@ -148,7 +163,7 @@ namespace osu.Game.Screens.Menu
if (!(last is MainMenu))
Content.FadeIn(300);
double fadeOutTime = 2000;
double fadeOutTime = EXIT_DELAY;
//we also handle the exit transition.
if (menuVoice)
seeya.Play();
+297
View File
@@ -0,0 +1,297 @@
// 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 OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Screens.Menu
{
public class IntroSequence : Container
{
private const float logo_size = 460; //todo: this should probably be 480
private OsuSpriteText welcomeText;
private Container<Box> lines;
private Box lineTopLeft;
private Box lineBottomLeft;
private Box lineTopRight;
private Box lineBottomRight;
private Ring smallRing;
private Ring mediumRing;
private Ring bigRing;
private Box backgroundFill;
private Box foregroundFill;
private CircularContainer pinkCircle;
private CircularContainer blueCircle;
private CircularContainer yellowCircle;
private CircularContainer purpleCircle;
public IntroSequence()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
const int line_offset = 80;
const int circle_offset = 250;
Children = new Drawable[]
{
lines = new Container<Box>
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Children = new []
{
lineTopLeft = new Box
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.Centre,
Position = new Vector2(-line_offset, -line_offset),
Rotation = 45,
Colour = Color4.White.Opacity(180),
},
lineTopRight = new Box
{
Origin = Anchor.CentreRight,
Anchor = Anchor.Centre,
Position = new Vector2(line_offset, -line_offset),
Rotation = -45,
Colour = Color4.White.Opacity(80),
},
lineBottomLeft = new Box
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.Centre,
Position = new Vector2(-line_offset, line_offset),
Rotation = -45,
Colour = Color4.White.Opacity(230),
},
lineBottomRight = new Box
{
Origin = Anchor.CentreRight,
Anchor = Anchor.Centre,
Position = new Vector2(line_offset, line_offset),
Rotation = 45,
Colour = Color4.White.Opacity(130),
},
}
},
bigRing = new Ring(OsuColour.FromHex(@"B6C5E9"), 0.85f),
mediumRing = new Ring(Color4.White.Opacity(130), 0.7f),
smallRing = new Ring(Color4.White, 0.6f),
welcomeText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = "welcome",
Padding = new MarginPadding { Bottom = 10 },
Font = @"Exo2.0-Light",
Alpha = 0,
TextSize = 42,
Spacing = new Vector2(5),
},
new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(logo_size),
Masking = true,
Children = new Drawable[]
{
backgroundFill = new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Height = 0,
Colour = OsuColour.FromHex(@"C6D8FF").Opacity(160),
},
foregroundFill = new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = Vector2.Zero,
RelativeSizeAxes = Axes.Both,
Width = 0,
Colour = Color4.White,
},
}
},
purpleCircle = new Circle
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
Position = new Vector2(0, circle_offset),
Colour = OsuColour.FromHex(@"AA92FF"),
},
blueCircle = new Circle
{
Anchor = Anchor.Centre,
Origin = Anchor.CentreRight,
Position = new Vector2(-circle_offset, 0),
Colour = OsuColour.FromHex(@"8FE5FE"),
},
yellowCircle = new Circle
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
Position = new Vector2(0, -circle_offset),
Colour = OsuColour.FromHex(@"FFD64C"),
},
pinkCircle = new Circle
{
Anchor = Anchor.Centre,
Origin = Anchor.CentreLeft,
Position = new Vector2(circle_offset, 0),
Colour = OsuColour.FromHex(@"e967a1"),
},
};
foreach (var line in lines)
{
line.Size = new Vector2(105, 1.5f);
line.Alpha = 0;
}
Scale = new Vector2(0.5f);
}
public void Start(double length)
{
if (Children.Any())
{
// restart if we were already run previously.
FinishTransforms(true);
load();
}
smallRing.ResizeTo(logo_size * 0.086f, 400, Easing.InOutQuint);
mediumRing.ResizeTo(130, 340, Easing.OutQuad);
mediumRing.Foreground.ResizeTo(1, 880, Easing.Out);
Func<double> remainingTime = () => length - TransformDelay;
using (BeginDelayedSequence(250, true))
{
welcomeText.FadeIn(700);
welcomeText.TransformSpacingTo(new Vector2(20, 0), remainingTime(), Easing.Out);
const int line_duration = 700;
const int line_resize = 150;
foreach (var line in lines)
{
line.FadeIn(40).ResizeWidthTo(0, line_duration - line_resize, Easing.OutQuint);
}
const int line_end_offset = 120;
smallRing.Foreground.ResizeTo(1, line_duration, Easing.OutQuint);
lineTopLeft.MoveTo(new Vector2(-line_end_offset, -line_end_offset), line_duration, Easing.OutQuint);
lineTopRight.MoveTo(new Vector2(line_end_offset, -line_end_offset), line_duration, Easing.OutQuint);
lineBottomLeft.MoveTo(new Vector2(-line_end_offset, line_end_offset), line_duration, Easing.OutQuint);
lineBottomRight.MoveTo(new Vector2(line_end_offset, line_end_offset), line_duration, Easing.OutQuint);
using (BeginDelayedSequence(length * 0.56, true))
{
bigRing.ResizeTo(logo_size, 500, Easing.InOutQuint);
bigRing.Foreground.Delay(250).ResizeTo(1, 850, Easing.OutQuint);
using (BeginDelayedSequence(250, true))
{
backgroundFill.ResizeHeightTo(1, remainingTime(), Easing.InOutQuart);
backgroundFill.RotateTo(-90, remainingTime(), Easing.InOutQuart);
using (BeginDelayedSequence(50, true))
{
foregroundFill.ResizeWidthTo(1, remainingTime(), Easing.InOutQuart);
foregroundFill.RotateTo(-90, remainingTime(), Easing.InOutQuart);
}
this.ScaleTo(1, remainingTime(), Easing.InOutCubic);
const float circle_size = logo_size * 0.9f;
const int rotation_delay = 110;
const int appear_delay = 80;
purpleCircle.MoveToY(circle_size / 2, remainingTime(), Easing.InOutQuart);
purpleCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart);
purpleCircle.ResizeTo(circle_size, remainingTime(), Easing.InOutQuart);
using (BeginDelayedSequence(appear_delay, true))
{
yellowCircle.MoveToY(-circle_size / 2, remainingTime(), Easing.InOutQuart);
yellowCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart);
yellowCircle.ResizeTo(circle_size, remainingTime(), Easing.InOutQuart);
using (BeginDelayedSequence(appear_delay, true))
{
blueCircle.MoveToX(-circle_size / 2, remainingTime(), Easing.InOutQuart);
blueCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart);
blueCircle.ResizeTo(circle_size, remainingTime(), Easing.InOutQuart);
using (BeginDelayedSequence(appear_delay, true))
{
pinkCircle.MoveToX(circle_size / 2, remainingTime(), Easing.InOutQuart);
pinkCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart);
pinkCircle.ResizeTo(circle_size, remainingTime(), Easing.InOutQuart);
}
}
}
}
}
}
}
private class Ring : Container<Circle>
{
public readonly Circle Foreground;
public Ring(Color4 ringColour, float foregroundSize)
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Children = new[]
{
new Circle
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.98f),
Colour = ringColour,
},
Foreground = new Circle
{
Size = new Vector2(foregroundSize),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
}
};
}
}
}
}
+1 -1
View File
@@ -19,7 +19,7 @@ using osu.Framework.Allocation;
namespace osu.Game.Screens.Menu
{
internal class LogoVisualisation : Drawable, IHasAccentColour
public class LogoVisualisation : Drawable, IHasAccentColour
{
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
+42 -15
View File
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@@ -23,7 +24,7 @@ namespace osu.Game.Screens.Menu
{
private readonly ButtonSystem buttons;
internal override bool ShowOverlays => buttons.State != MenuState.Initial;
public override bool ShowOverlays => buttons.State != MenuState.Initial;
private readonly BackgroundScreenDefault background;
private Screen songSelect;
@@ -58,13 +59,16 @@ namespace osu.Game.Screens.Menu
};
}
[BackgroundDependencyLoader]
private void load(OsuGame game)
[BackgroundDependencyLoader(true)]
private void load(OsuGame game = null)
{
LoadComponentAsync(background);
buttons.OnSettings = game.ToggleSettings;
buttons.OnDirect = game.ToggleDirect;
if (game != null)
{
buttons.OnSettings = game.ToggleSettings;
buttons.OnDirect = game.ToggleDirect;
}
preloadSongSelect();
}
@@ -102,6 +106,38 @@ namespace osu.Game.Screens.Menu
Beatmap.ValueChanged += beatmap_ValueChanged;
}
protected override void LogoArriving(OsuLogo logo, bool resuming)
{
base.LogoArriving(logo, resuming);
buttons.SetOsuLogo(logo);
logo.Triangles = true;
logo.Ripple = false;
logo.FadeColour(Color4.White, 100, Easing.OutQuint);
logo.FadeIn(100, Easing.OutQuint);
if (resuming)
{
buttons.State = MenuState.TopLevel;
const float length = 300;
Content.FadeIn(length, Easing.OutQuint);
Content.MoveTo(new Vector2(0, 0), length, Easing.OutQuint);
sideFlashes.Delay(length).FadeIn(64, Easing.InQuint);
}
}
protected override void LogoSuspending(OsuLogo logo)
{
logo.FadeOut(300, Easing.InSine)
.ScaleTo(0.2f, 300, Easing.InSine)
.OnComplete(l => buttons.SetOsuLogo(null));
}
private void beatmap_ValueChanged(WorkingBeatmap newValue)
{
if (!IsCurrentScreen)
@@ -121,7 +157,7 @@ namespace osu.Game.Screens.Menu
Content.FadeOut(length, Easing.InSine);
Content.MoveTo(new Vector2(-800, 0), length, Easing.InSine);
sideFlashes.FadeOut(length / 4, Easing.OutQuint);
sideFlashes.FadeOut(64, Easing.OutQuint);
}
protected override void OnResuming(Screen last)
@@ -132,15 +168,6 @@ namespace osu.Game.Screens.Menu
//we may have consumed our preloaded instance, so let's make another.
preloadSongSelect();
const float length = 300;
buttons.State = MenuState.TopLevel;
Content.FadeIn(length, Easing.OutQuint);
Content.MoveTo(new Vector2(0, 0), length, Easing.OutQuint);
sideFlashes.FadeIn(length / 4, Easing.InQuint);
}
protected override bool OnExiting(Screen next)
+29 -7
View File
@@ -29,6 +29,8 @@ namespace osu.Game.Screens.Menu
{
public readonly Color4 OsuPink = OsuColour.FromHex(@"e967a1");
private const double transition_length = 300;
private readonly Sprite logo;
private readonly CircularContainer logoContainer;
private readonly Container logoBounceContainer;
@@ -37,6 +39,8 @@ namespace osu.Game.Screens.Menu
private readonly Container logoHoverContainer;
private readonly LogoVisualisation visualizer;
private readonly IntroSequence intro;
private SampleChannel sampleClick;
private SampleChannel sampleBeat;
@@ -54,7 +58,7 @@ namespace osu.Game.Screens.Menu
public bool Triangles
{
set { colourAndTriangles.Alpha = value ? 1 : 0; }
set { colourAndTriangles.FadeTo(value ? 1 : 0, transition_length, Easing.OutQuint); }
}
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => logoContainer.ReceiveMouseInputAt(screenSpacePos);
@@ -62,10 +66,9 @@ namespace osu.Game.Screens.Menu
public bool Ripple
{
get { return rippleContainer.Alpha > 0; }
set { rippleContainer.Alpha = value ? 1 : 0; }
set { rippleContainer.FadeTo(value ? 1 : 0, transition_length, Easing.OutQuint); }
}
public bool Interactive = true;
private readonly Box flashLayer;
private readonly Container impactContainer;
@@ -76,17 +79,23 @@ namespace osu.Game.Screens.Menu
public OsuLogo()
{
// Required to make Schedule calls run in OsuScreen even when we are not visible.
AlwaysPresent = true;
EarlyActivationMilliseconds = early_activation;
Size = new Vector2(default_size);
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
intro = new IntroSequence
{
RelativeSizeAxes = Axes.Both,
},
logoHoverContainer = new Container
{
AutoSizeAxes = Axes.Both,
@@ -266,6 +275,17 @@ namespace osu.Game.Screens.Menu
}
}
public void PlayIntro()
{
const double length = 3150;
const double fade = 200;
logoHoverContainer.FadeOut().Delay(length).FadeIn(fade);
intro.Show();
intro.Start(length);
intro.Delay(length + fade).FadeOut();
}
protected override void Update()
{
base.Update();
@@ -290,9 +310,11 @@ namespace osu.Game.Screens.Menu
}
}
private bool interactive => Action != null && Alpha > 0.2f;
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
if (!Interactive) return false;
if (!interactive) return false;
logoBounceContainer.ScaleTo(0.9f, 1000, Easing.Out);
return true;
@@ -306,7 +328,7 @@ namespace osu.Game.Screens.Menu
protected override bool OnClick(InputState state)
{
if (!Interactive) return false;
if (!interactive) return false;
sampleClick.Play();
@@ -320,7 +342,7 @@ namespace osu.Game.Screens.Menu
protected override bool OnHover(InputState state)
{
if (!Interactive) return false;
if (!interactive) return false;
logoHoverContainer.ScaleTo(1.1f, 500, Easing.OutElastic);
return true;
+58 -4
View File
@@ -10,13 +10,15 @@ using osu.Game.Graphics.Containers;
using OpenTK;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio;
using osu.Framework.Graphics;
using osu.Game.Rulesets;
using osu.Game.Screens.Menu;
namespace osu.Game.Screens
{
public abstract class OsuScreen : Screen
{
internal BackgroundScreen Background { get; private set; }
public BackgroundScreen Background { get; private set; }
/// <summary>
/// Override to create a BackgroundMode for the current screen.
@@ -24,17 +26,19 @@ namespace osu.Game.Screens
/// </summary>
protected virtual BackgroundScreen CreateBackground() => null;
internal virtual bool ShowOverlays => true;
public virtual bool ShowOverlays => true;
protected new OsuGameBase Game => base.Game as OsuGameBase;
internal virtual bool HasLocalCursorDisplayed => false;
public virtual bool HasLocalCursorDisplayed => false;
private OsuLogo logo;
/// <summary>
/// Whether the beatmap or ruleset should be allowed to be changed by the user or game.
/// Used to mark exclusive areas where this is strongly prohibited, like gameplay.
/// </summary>
internal virtual bool AllowBeatmapRulesetChange => true;
public virtual bool AllowBeatmapRulesetChange => true;
protected readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
@@ -72,9 +76,16 @@ namespace osu.Game.Screens
protected override void OnResuming(Screen last)
{
base.OnResuming(last);
logo.DelayUntilTransformsFinished().Schedule(() => LogoArriving(logo, true));
sampleExit?.Play();
}
protected override void OnSuspending(Screen next)
{
base.OnSuspending(next);
onSuspendingLogo();
}
protected override void OnEntering(Screen last)
{
OsuScreen lastOsu = last as OsuScreen;
@@ -106,11 +117,19 @@ namespace osu.Game.Screens
});
}
if ((logo = lastOsu?.logo) == null)
AddInternal(logo = new OsuLogo());
base.OnEntering(last);
logo.DelayUntilTransformsFinished().Schedule(() => LogoArriving(logo, false));
}
protected override bool OnExiting(Screen next)
{
if (ValidForResume && logo != null)
onExitingLogo();
OsuScreen nextOsu = next as OsuScreen;
if (Background != null && !Background.Equals(nextOsu?.Background))
@@ -128,5 +147,40 @@ namespace osu.Game.Screens
Beatmap.UnbindAll();
return false;
}
/// <summary>
/// Fired when this screen was entered or resumed and the logo state is required to be adjusted.
/// </summary>
protected virtual void LogoArriving(OsuLogo logo, bool resuming)
{
logo.Action = null;
logo.FadeOut(300, Easing.OutQuint);
}
private void onExitingLogo()
{
logo.ClearTransforms();
LogoExiting(logo);
}
/// <summary>
/// Fired when this screen was exited to add any outwards transition to the logo.
/// </summary>
protected virtual void LogoExiting(OsuLogo logo)
{
}
private void onSuspendingLogo()
{
logo.ClearTransforms();
LogoSuspending(logo);
}
/// <summary>
/// Fired when this screen was suspended to add any outwards transition to the logo.
/// </summary>
protected virtual void LogoSuspending(OsuLogo logo)
{
}
}
}
@@ -7,6 +7,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Scoring;
using System.Collections.Generic;
using osu.Framework.Graphics.UserInterface;
namespace osu.Game.Screens.Play.BreaksOverlay
{
@@ -30,6 +31,8 @@ namespace osu.Game.Screens.Play.BreaksOverlay
}
}
public override bool RemoveCompletedTransforms => false;
private readonly bool letterboxing;
private readonly LetterboxOverlay letterboxOverlay;
private readonly Container remainingTimeAdjustmentBox;
@@ -101,38 +104,41 @@ namespace osu.Game.Screens.Play.BreaksOverlay
if (!b.HasEffect)
continue;
using (BeginAbsoluteSequence(b.StartTime, true))
{
remainingTimeAdjustmentBox
.ResizeWidthTo(remaining_time_container_max_size, fade_duration, Easing.OutQuint)
.Delay(b.Duration - fade_duration)
.ResizeWidthTo(0);
remainingTimeBox
.ResizeWidthTo(0, b.Duration - fade_duration)
.Then()
.ResizeWidthTo(1);
remainingTimeCounter.CountTo(b.Duration).CountTo(0, b.Duration);
}
using (BeginAbsoluteSequence(b.StartTime))
{
Schedule(() => onBreakIn(b));
Schedule(showBreak);
using (BeginDelayedSequence(b.Duration - fade_duration))
Schedule(onBreakOut);
Schedule(hideBreak);
}
}
}
private void onBreakIn(BreakPeriod b)
private void showBreak()
{
if (letterboxing)
letterboxOverlay.Show();
remainingTimeAdjustmentBox
.ResizeWidthTo(remaining_time_container_max_size, fade_duration, Easing.OutQuint)
.Delay(b.Duration - fade_duration)
.ResizeWidthTo(0);
remainingTimeBox
.ResizeWidthTo(0, b.Duration - fade_duration)
.Then()
.ResizeWidthTo(1);
remainingTimeCounter.StartCounting(b.EndTime);
remainingTimeCounter.Show();
info.Show();
arrowsOverlay.Show();
}
private void onBreakOut()
private void hideBreak()
{
if (letterboxing)
letterboxOverlay.Hide();
@@ -1,65 +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.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics;
using System;
using osu.Game.Beatmaps.Timing;
using osu.Framework.Graphics.UserInterface;
namespace osu.Game.Screens.Play.BreaksOverlay
{
public class RemainingTimeCounter : VisibilityContainer
public class RemainingTimeCounter : Counter
{
private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
private readonly OsuSpriteText counter;
private int? previousSecond;
private double endTime;
private bool isCounting;
public RemainingTimeCounter()
{
AutoSizeAxes = Axes.Both;
Child = counter = new OsuSpriteText
InternalChild = counter = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 33,
Font = "Venera",
};
Alpha = 0;
}
public void StartCounting(double endTime)
{
this.endTime = endTime;
isCounting = true;
}
protected override void OnCountChanged(double count) => counter.Text = ((int)Math.Ceiling(count / 1000)).ToString();
protected override void Update()
{
base.Update();
if (isCounting)
{
var currentTime = Clock.CurrentTime;
if (currentTime < endTime)
{
int currentSecond = (int)Math.Ceiling((endTime - Clock.CurrentTime) / 1000.0);
if (currentSecond != previousSecond)
{
counter.Text = currentSecond.ToString();
previousSecond = currentSecond;
}
}
else isCounting = false;
}
}
protected override void PopIn() => this.FadeIn(fade_duration);
protected override void PopOut() => this.FadeOut(fade_duration);
public override void Show() => this.FadeIn(fade_duration);
public override void Hide() => this.FadeOut(fade_duration);
}
}
@@ -0,0 +1,68 @@
// 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.Game.Screens.Play.ReplaySettings;
using OpenTK;
using osu.Framework.Input;
using OpenTK.Input;
namespace osu.Game.Screens.Play.HUD
{
public class ReplaySettingsOverlay : VisibilityContainer
{
private const int fade_duration = 200;
public bool ReplayLoaded;
public override bool HandleInput => true;
public readonly PlaybackSettings PlaybackSettings;
//public readonly CollectionSettings CollectionSettings;
//public readonly DiscussionSettings DiscussionSettings;
public ReplaySettingsOverlay()
{
AlwaysPresent = true;
RelativeSizeAxes = Axes.Both;
Child = new FillFlowContainer<ReplayGroup>
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 20),
Margin = new MarginPadding { Top = 100, Right = 10 },
Children = new []
{
//CollectionSettings = new CollectionSettings(),
//DiscussionSettings = new DiscussionSettings(),
PlaybackSettings = new PlaybackSettings(),
}
};
State = Visibility.Visible;
}
protected override void PopIn() => this.FadeIn(fade_duration);
protected override void PopOut() => this.FadeOut(fade_duration);
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Repeat) return false;
if (state.Keyboard.ControlPressed)
{
if (args.Key == Key.H && ReplayLoaded)
{
ToggleVisibility();
return true;
}
}
return base.OnKeyDown(state, args);
}
}
}
+8 -7
View File
@@ -32,6 +32,7 @@ namespace osu.Game.Screens.Play
public readonly HealthDisplay HealthDisplay;
public readonly SongProgress Progress;
public readonly ModDisplay ModDisplay;
public readonly ReplaySettingsOverlay ReplaySettingsOverlay;
private Bindable<bool> showHud;
private bool replayLoaded;
@@ -55,7 +56,7 @@ namespace osu.Game.Screens.Play
HealthDisplay = CreateHealthDisplay(),
Progress = CreateProgress(),
ModDisplay = CreateModsContainer(),
//ReplaySettingsOverlay = CreateReplaySettingsOverlay(),
ReplaySettingsOverlay = CreateReplaySettingsOverlay(),
}
});
}
@@ -96,9 +97,14 @@ namespace osu.Game.Screens.Play
replayLoaded = rulesetContainer.HasReplayLoaded;
ReplaySettingsOverlay.ReplayLoaded = replayLoaded;
// in the case a replay isn't loaded, we want some elements to only appear briefly.
if (!replayLoaded)
{
ReplaySettingsOverlay.Hide();
ModDisplay.Delay(2000).FadeOut(200);
}
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
@@ -176,12 +182,7 @@ namespace osu.Game.Screens.Play
Margin = new MarginPadding { Top = 20, Right = 10 },
};
//protected virtual ReplaySettingsOverlay CreateReplaySettingsOverlay() => new ReplaySettingsOverlay
//{
// Anchor = Anchor.TopRight,
// Origin = Anchor.TopRight,
// Margin = new MarginPadding { Top = 100, Right = 10 },
//};
protected virtual ReplaySettingsOverlay CreateReplaySettingsOverlay() => new ReplaySettingsOverlay();
public virtual void BindProcessor(ScoreProcessor processor)
{
+27 -7
View File
@@ -17,6 +17,8 @@ using osu.Game.Rulesets.UI;
using osu.Game.Screens.Backgrounds;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Threading;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
@@ -34,13 +36,13 @@ namespace osu.Game.Screens.Play
{
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
internal override bool ShowOverlays => false;
public override bool ShowOverlays => false;
internal override bool HasLocalCursorDisplayed => !pauseContainer.IsPaused && !HasFailed && RulesetContainer.ProvidingUserCursor;
public override bool HasLocalCursorDisplayed => !pauseContainer.IsPaused && !HasFailed && RulesetContainer.ProvidingUserCursor;
public Action RestartRequested;
internal override bool AllowBeatmapRulesetChange => false;
public override bool AllowBeatmapRulesetChange => false;
public bool HasFailed { get; private set; }
@@ -142,14 +144,20 @@ namespace osu.Game.Screens.Play
userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
userAudioOffset.TriggerChange();
Schedule(() =>
Task.Run(() =>
{
adjustableSourceClock.Reset();
foreach (var mod in working.Mods.Value.OfType<IApplicableToClock>())
mod.ApplyToClock(adjustableSourceClock);
// this is temporary until we have blocking (async.Wait()) audio component methods.
// then we can call ResetAsync().Wait() or the blocking version above.
while (adjustableSourceClock.IsRunning)
Thread.Sleep(1);
decoupledClock.ChangeSource(adjustableSourceClock);
Schedule(() =>
{
decoupledClock.ChangeSource(adjustableSourceClock);
applyRateFromMods();
});
});
Children = new Drawable[]
@@ -230,11 +238,20 @@ namespace osu.Game.Screens.Play
breakOverlay.BindProcessor(scoreProcessor);
hudOverlay.ReplaySettingsOverlay.PlaybackSettings.AdjustableClock = adjustableSourceClock;
// Bind ScoreProcessor to ourselves
scoreProcessor.AllJudged += onCompletion;
scoreProcessor.Failed += onFail;
}
private void applyRateFromMods()
{
adjustableSourceClock.Rate = 1;
foreach (var mod in Beatmap.Value.Mods.Value.OfType<IApplicableToClock>())
mod.ApplyToClock(adjustableSourceClock);
}
private void initializeStoryboard(bool asyncLoad)
{
var beatmap = Beatmap.Value.Beatmap;
@@ -332,6 +349,9 @@ namespace osu.Game.Screens.Play
{
if (HasFailed || !ValidForResume || pauseContainer?.AllowExit != false || RulesetContainer?.HasReplayLoaded != false)
{
// In the case of replays, we may have changed the playback rate.
applyRateFromMods();
fadeOut();
return base.OnExiting(next);
}
+17 -14
View File
@@ -10,9 +10,9 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Menu;
using OpenTK;
using osu.Framework.Localisation;
using osu.Game.Screens.Menu;
namespace osu.Game.Screens.Play
{
@@ -20,13 +20,12 @@ namespace osu.Game.Screens.Play
{
private Player player;
private readonly OsuLogo logo;
private BeatmapMetadataDisplay info;
private bool showOverlays = true;
internal override bool ShowOverlays => showOverlays;
public override bool ShowOverlays => showOverlays;
internal override bool AllowBeatmapRulesetChange => false;
public override bool AllowBeatmapRulesetChange => false;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
@@ -39,15 +38,6 @@ namespace osu.Game.Screens.Play
showOverlays = false;
ValidForResume = true;
};
Children = new Drawable[]
{
logo = new OsuLogo
{
Scale = new Vector2(0.15f),
Interactive = false,
},
};
}
[BackgroundDependencyLoader]
@@ -101,11 +91,24 @@ namespace osu.Game.Screens.Play
contentIn();
logo.Delay(500).MoveToOffset(new Vector2(0, -180), 500, Easing.InOutExpo);
info.Delay(750).FadeIn(500);
this.Delay(2150).Schedule(pushWhenLoaded);
}
protected override void LogoArriving(OsuLogo logo, bool resuming)
{
base.LogoArriving(logo, resuming);
logo.ClearTransforms(targetMember: nameof(Position));
logo.RelativePositionAxes = Axes.Both;
logo.ScaleTo(new Vector2(0.15f), 300, Easing.In);
logo.MoveTo(new Vector2(0.5f), 300, Easing.In);
logo.FadeIn(350);
logo.Delay(resuming ? 0 : 500).MoveToOffset(new Vector2(0, -0.24f), 500, Easing.InOutExpo);
}
private void pushWhenLoaded()
{
if (player.LoadState != LoadState.Ready)
@@ -1,9 +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.Allocation;
using osu.Game.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Timing;
using osu.Framework.Configuration;
namespace osu.Game.Screens.Play.ReplaySettings
{
@@ -11,18 +10,34 @@ namespace osu.Game.Screens.Play.ReplaySettings
{
protected override string Title => @"playback";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
public IAdjustableClock AdjustableClock { set; get; }
private readonly ReplaySliderBar<double> sliderbar;
public PlaybackSettings()
{
Children = new Drawable[]
Child = sliderbar = new ReplaySliderBar<double>
{
new ReplaySliderBar<double>
LabelText = "Playback speed",
Bindable = new BindableDouble(1)
{
LabelText = "Playback speed",
Bindable = config.GetBindable<double>(OsuSetting.PlaybackSpeed),
KeyboardStep = 0.5f
}
Default = 1,
MinValue = 0.5,
MaxValue = 2,
Precision = 0.01,
},
};
}
protected override void LoadComplete()
{
base.LoadComplete();
if (AdjustableClock == null)
return;
var clockRate = AdjustableClock.Rate;
sliderbar.Bindable.ValueChanged += rateMultiplier => AdjustableClock.Rate = clockRate * rateMultiplier;
}
}
}
@@ -1,24 +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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Screens.Play.ReplaySettings;
using OpenTK;
namespace osu.Game.Screens.Play
{
public class ReplaySettingsOverlay : FillFlowContainer
{
public ReplaySettingsOverlay()
{
Direction = FillDirection.Vertical;
AutoSizeAxes = Axes.Both;
Spacing = new Vector2(0, 20);
Add(new CollectionSettings());
Add(new DiscussionSettings());
Add(new PlaybackSettings());
}
}
}
+1 -1
View File
@@ -31,7 +31,7 @@ namespace osu.Game.Screens.Ranking
private ResultModeTabControl modeChangeButtons;
internal override bool AllowBeatmapRulesetChange => false;
public override bool AllowBeatmapRulesetChange => false;
private Container currentPage;

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