1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 07:27:25 +08:00

Merge branch 'master' into kudosu-section

This commit is contained in:
Dean Herbert 2017-11-23 17:19:42 +09:00 committed by GitHub
commit 9d481eba96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
103 changed files with 1233 additions and 449 deletions

View File

@ -15,7 +15,9 @@ This is still heavily under development and is not intended for end-user use. Th
We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention on having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time, to ensure no effort is wasted.
Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://www.bountysource.com/teams/ppy). If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu-framework/issues).
Please make sure you are familiar with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. New component development, and where possible, bug fixing and debugging existing components **should always be done under VisualTests**.
Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://www.bountysource.com/teams/ppy). If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues).
Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. I welcome all feedback so we can make contributing to this project as pain-free as possible.

@ -1 +1 @@
Subproject commit 887db793c705b45071aea5e0c1cc931a7887165f
Subproject commit c6fd2914926f2a6df23eda536c0310f072581b1b

View File

@ -3,7 +3,6 @@
using System;
using System.Diagnostics;
using System.Net.Http;
using osu.Framework.Allocation;
using osu.Framework.Development;
using osu.Framework.Graphics;
@ -198,10 +197,9 @@ namespace osu.Desktop.Overlays
}
}
}
catch (HttpRequestException)
catch (Exception)
{
//likely have no internet connection.
//we'll ignore this and retry later.
// we'll ignore this and retry later. can be triggered by no internet connection or thread abortion.
}
finally
{

View File

@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu
};
public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new[]
{
{
new BeatmapStatistic
{
Name = @"Circle count",

View File

@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
/// <summary>
/// Taiko fails at the end of the map if the player has not half-filled their HP bar.
/// </summary>
protected override bool FailCondition => Hits == MaxHits && Health.Value <= 0.5;
protected override bool DefaultFailCondition => Hits == MaxHits && Health.Value <= 0.5;
private double hpIncreaseTick;
private double hpIncreaseGreat;

View File

@ -0,0 +1,27 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Screens.Edit.Components;
using osu.Game.Tests.Beatmaps;
using OpenTK;
namespace osu.Game.Tests.Visual
{
public class TestCasePlaybackControl : OsuTestCase
{
public TestCasePlaybackControl()
{
var playback = new PlaybackControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(200,100)
};
playback.Beatmap.Value = new TestWorkingBeatmap(new Beatmap());
Add(playback);
}
}
}

View File

@ -36,7 +36,8 @@ namespace osu.Game.Tests.Visual
Username = @"peppy",
Id = 2,
Country = new Country { FlagName = @"AU" },
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg"
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
IsSupporter = true,
}) { Width = 300 },
},
});

View File

@ -124,6 +124,7 @@
<Compile Include="Visual\TestCaseOnScreenDisplay.cs" />
<Compile Include="Visual\TestCaseAllPlayers.cs" />
<Compile Include="Visual\TestCaseOsuGame.cs" />
<Compile Include="Visual\TestCasePlaybackControl.cs" />
<Compile Include="Visual\TestCasePlaySongSelect.cs" />
<Compile Include="Visual\TestCaseReplay.cs" />
<Compile Include="Visual\TestCaseReplaySettingsOverlay.cs" />

View File

@ -18,6 +18,7 @@ using osu.Framework.Platform;
using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.IO;
using osu.Game.IPC;
using osu.Game.Online.API;
@ -52,6 +53,11 @@ namespace osu.Game.Beatmaps
/// </summary>
public event Action<BeatmapInfo> BeatmapRestored;
/// <summary>
/// Fired when a beatmap download begins.
/// </summary>
public event Action<DownloadBeatmapSetRequest> BeatmapDownloadBegan;
/// <summary>
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
/// </summary>
@ -165,7 +171,7 @@ namespace osu.Game.Beatmaps
catch (Exception e)
{
e = e.InnerException ?? e;
Logger.Error(e, @"Could not import beatmap set");
Logger.Error(e, $@"Could not import beatmap set ({Path.GetFileName(path)})");
}
}
@ -221,21 +227,29 @@ namespace osu.Game.Beatmaps
/// Downloads a beatmap.
/// </summary>
/// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
/// <returns>A new <see cref="DownloadBeatmapSetRequest"/>, or an existing one if a download is already in progress.</returns>
public DownloadBeatmapSetRequest Download(BeatmapSetInfo beatmapSetInfo)
/// <param name="noVideo">Whether the beatmap should be downloaded without video. Defaults to false.</param>
public void Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false)
{
var existing = GetExistingDownload(beatmapSetInfo);
if (existing != null) return existing;
if (existing != null || api == null) return;
if (api == null) return null;
if (!api.LocalUser.Value.IsSupporter)
{
PostNotification?.Invoke(new SimpleNotification
{
Icon = FontAwesome.fa_superpowers,
Text = "You gotta be a supporter to download for now 'yo"
});
return;
}
ProgressNotification downloadNotification = new ProgressNotification
{
Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
};
var request = new DownloadBeatmapSetRequest(beatmapSetInfo);
var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo);
request.DownloadProgressed += progress =>
{
@ -280,8 +294,7 @@ namespace osu.Game.Beatmaps
// don't run in the main api queue as this is a long-running task.
Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
return request;
BeatmapDownloadBegan?.Invoke(request);
}
/// <summary>
@ -483,7 +496,8 @@ namespace osu.Game.Beatmaps
using (var stream = new StreamReader(reader.GetStream(mapName)))
metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
beatmapSet = new BeatmapSetInfo
// check if a set already exists with the same online id.
beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID) ?? new BeatmapSetInfo
{
OnlineBeatmapSetID = metadata.OnlineBeatmapSetID,
Beatmaps = new List<BeatmapInfo>(),
@ -510,16 +524,21 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary
beatmap.BeatmapInfo.Metadata = null;
var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID);
RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
if (existing == null)
{
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary
beatmap.BeatmapInfo.Metadata = null;
// TODO: this should be done in a better place once we actually need to dynamically update it.
beatmap.BeatmapInfo.Ruleset = ruleset;
beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0;
RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
// TODO: this should be done in a better place once we actually need to dynamically update it.
beatmap.BeatmapInfo.Ruleset = ruleset;
beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0;
beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
}
}
}

View File

@ -7,7 +7,7 @@ using osu.Framework.Graphics.Sprites;
namespace osu.Game.Beatmaps.Drawables
{
internal class BeatmapBackgroundSprite : Sprite
public class BeatmapBackgroundSprite : Sprite
{
private readonly WorkingBeatmap working;

View File

@ -11,7 +11,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
@ -28,10 +27,8 @@ namespace osu.Game.Beatmaps.Drawables
public Action<BeatmapSetInfo> RestoreHiddenRequested;
private readonly SpriteText title;
private readonly SpriteText artist;
private readonly WorkingBeatmap beatmap;
private readonly FillFlowContainer difficultyIcons;
public BeatmapSetHeader(WorkingBeatmap beatmap)
@ -41,6 +38,25 @@ namespace osu.Game.Beatmaps.Drawables
this.beatmap = beatmap;
difficultyIcons = new FillFlowContainer
{
Margin = new MarginPadding { Top = 5 },
AutoSizeAxes = Axes.Both,
};
}
protected override void Selected()
{
base.Selected();
GainedSelection?.Invoke(this);
}
[BackgroundDependencyLoader]
private void load(LocalisationEngine localisation)
{
if (localisation == null)
throw new ArgumentNullException(nameof(localisation));
Children = new Drawable[]
{
new DelayedLoadWrapper(
@ -60,44 +76,26 @@ namespace osu.Game.Beatmaps.Drawables
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
title = new OsuSpriteText
new OsuSpriteText
{
Font = @"Exo2.0-BoldItalic",
Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title),
TextSize = 22,
Shadow = true,
},
artist = new OsuSpriteText
new OsuSpriteText
{
Font = @"Exo2.0-SemiBoldItalic",
Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist),
TextSize = 17,
Shadow = true,
},
difficultyIcons = new FillFlowContainer
{
Margin = new MarginPadding { Top = 5 },
AutoSizeAxes = Axes.Both,
}
difficultyIcons
}
}
};
}
protected override void Selected()
{
base.Selected();
GainedSelection?.Invoke(this);
}
[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);
}
private class PanelBackground : BufferedContainer
{
public PanelBackground(WorkingBeatmap working)
@ -185,4 +183,4 @@ namespace osu.Game.Beatmaps.Drawables
}
}
}
}
}

View File

@ -11,7 +11,7 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Beatmaps
{
internal class DummyWorkingBeatmap : WorkingBeatmap
public class DummyWorkingBeatmap : WorkingBeatmap
{
private readonly OsuGameBase game;

View File

@ -8,6 +8,7 @@ using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace osu.Game.Beatmaps
{
@ -29,10 +30,10 @@ namespace osu.Game.Beatmaps
Mods.ValueChanged += mods => applyRateAdjustments();
beatmap = new Lazy<Beatmap>(populateBeatmap);
background = new Lazy<Texture>(populateBackground);
track = new Lazy<Track>(populateTrack);
waveform = new Lazy<Waveform>(populateWaveform);
beatmap = new AsyncLazy<Beatmap>(populateBeatmap);
background = new AsyncLazy<Texture>(populateBackground);
track = new AsyncLazy<Track>(populateTrack);
waveform = new AsyncLazy<Waveform>(populateWaveform);
}
protected abstract Beatmap GetBeatmap();
@ -41,8 +42,10 @@ namespace osu.Game.Beatmaps
protected virtual Waveform GetWaveform() => new Waveform();
public bool BeatmapLoaded => beatmap.IsValueCreated;
public Beatmap Beatmap => beatmap.Value;
private readonly Lazy<Beatmap> beatmap;
public Beatmap Beatmap => beatmap.Value.Result;
public async Task<Beatmap> GetBeatmapAsync() => await beatmap.Value;
private readonly AsyncLazy<Beatmap> beatmap;
private Beatmap populateBeatmap()
{
@ -55,14 +58,16 @@ namespace osu.Game.Beatmaps
}
public bool BackgroundLoaded => background.IsValueCreated;
public Texture Background => background.Value;
private Lazy<Texture> background;
public Texture Background => background.Value.Result;
public async Task<Texture> GetBackgroundAsync() => await background.Value;
private AsyncLazy<Texture> background;
private Texture populateBackground() => GetBackground();
public bool TrackLoaded => track.IsValueCreated;
public Track Track => track.Value;
private Lazy<Track> track;
public Track Track => track.Value.Result;
public async Task<Track> GetTrackAsync() => await track.Value;
private AsyncLazy<Track> track;
private Track populateTrack()
{
@ -73,8 +78,9 @@ namespace osu.Game.Beatmaps
}
public bool WaveformLoaded => waveform.IsValueCreated;
public Waveform Waveform => waveform.Value;
private readonly Lazy<Waveform> waveform;
public Waveform Waveform => waveform.Value.Result;
public async Task<Waveform> GetWaveformAsync() => await waveform.Value;
private readonly AsyncLazy<Waveform> waveform;
private Waveform populateWaveform() => GetWaveform();
@ -107,5 +113,13 @@ namespace osu.Game.Beatmaps
foreach (var mod in Mods.Value.OfType<IApplicableToClock>())
mod.ApplyToClock(t);
}
public class AsyncLazy<T> : Lazy<Task<T>>
{
public AsyncLazy(Func<T> valueFactory)
: base(() => Task.Run(valueFactory))
{
}
}
}
}

View File

@ -78,6 +78,7 @@ namespace osu.Game.Database
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.OnlineBeatmapID).IsUnique();
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.MD5Hash).IsUnique();
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.Hash).IsUnique();

View File

@ -45,8 +45,8 @@ namespace osu.Game.Graphics.Containers
double currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime;
TimingControlPoint timingPoint = Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(currentTrackTime);
EffectControlPoint effectPoint = Beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime);
EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
if (timingPoint.BeatLength == 0)
return;
@ -67,7 +67,7 @@ namespace osu.Game.Graphics.Containers
return;
using (BeginDelayedSequence(-TimeSinceLastBeat, true))
OnNewBeat(beatIndex, timingPoint, effectPoint, Beatmap.Value.Track.CurrentAmplitudes);
OnNewBeat(beatIndex, timingPoint, effectPoint, track.CurrentAmplitudes);
lastBeat = beatIndex;
lastTimingPoint = timingPoint;

View File

@ -66,7 +66,7 @@ namespace osu.Game.Graphics.UserInterface
}
private float textSize = 28;
internal float TextSize
public float TextSize
{
get
{

View File

@ -38,7 +38,7 @@ namespace osu.Game.Graphics.UserInterface
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Key == Key.Escape)
if (!args.Repeat && args.Key == Key.Escape)
{
if (Text.Length > 0)
Text = string.Empty;

View File

@ -11,7 +11,7 @@ using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface.Volume
{
internal class VolumeControl : OverlayContainer
public class VolumeControl : OverlayContainer
{
private readonly VolumeMeter volumeMeterMaster;
@ -119,4 +119,4 @@ namespace osu.Game.Graphics.UserInterface.Volume
this.Delay(1000).Schedule(Hide, out popOutDelegate);
}
}
}
}

View File

@ -8,7 +8,7 @@ using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface.Volume
{
internal class VolumeControlReceptor : Container, IKeyBindingHandler<GlobalAction>
public class VolumeControlReceptor : Container, IKeyBindingHandler<GlobalAction>
{
public Func<GlobalAction, bool> ActionRequested;

View File

@ -13,7 +13,7 @@ using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface.Volume
{
internal class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
{
private readonly Box meterFill;
public BindableDouble Bindable { get; } = new BindableDouble();
@ -108,4 +108,4 @@ namespace osu.Game.Graphics.UserInterface.Volume
public bool OnReleased(GlobalAction action) => false;
}
}
}

View File

@ -0,0 +1,302 @@
// <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("20171119065731_AddBeatmapOnlineIDUniqueConstraint")]
partial class AddBeatmapOnlineIDUniqueConstraint
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<float>("SliderMultiplier");
b.Property<float>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("AudioLeadIn");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MD5Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.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
}
}
}

View File

@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace osu.Game.Migrations
{
public partial class AddBeatmapOnlineIDUniqueConstraint : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateIndex(
name: "IX_BeatmapInfo_OnlineBeatmapID",
table: "BeatmapInfo",
column: "OnlineBeatmapID",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_BeatmapInfo_OnlineBeatmapID",
table: "BeatmapInfo");
}
}
}

View File

@ -103,6 +103,9 @@ namespace osu.Game.Migrations
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");

View File

@ -6,7 +6,7 @@ using osu.Framework.IO.Network;
namespace osu.Game.Online.API
{
internal class OAuth
public class OAuth
{
private readonly string clientId;
private readonly string clientSecret;

View File

@ -8,7 +8,7 @@ using Newtonsoft.Json;
namespace osu.Game.Online.API
{
[Serializable]
internal class OAuthToken
public class OAuthToken
{
/// <summary>
/// OAuth 2.0 access token.

View File

@ -12,13 +12,16 @@ namespace osu.Game.Online.API.Requests
public Action<float> DownloadProgressed;
public DownloadBeatmapSetRequest(BeatmapSetInfo set)
private readonly bool noVideo;
public DownloadBeatmapSetRequest(BeatmapSetInfo set, bool noVideo)
{
this.noVideo = noVideo;
BeatmapSet = set;
Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total);
}
protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download";
protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}";
}
}

View File

@ -100,7 +100,7 @@ namespace osu.Game.Online.Multiplayer
}
}
internal class VersusRow : FillFlowContainer
public class VersusRow : FillFlowContainer
{
public VersusRow(Color4 first, Color4 second, float size)
{

View File

@ -109,7 +109,7 @@ namespace osu.Game
dependencies.Cache(this);
configRuleset = LocalConfig.GetBindable<int>(OsuSetting.Ruleset);
Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value);
Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First();
Ruleset.ValueChanged += r => configRuleset.Value = r.ID ?? 0;
}

View File

@ -29,8 +29,11 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly Container noVideoButtons;
private readonly FillFlowContainer videoButtons;
private readonly AuthorInfo author;
private readonly Container downloadButtonsContainer;
public Details Details;
private BeatmapManager beatmaps;
private DelayedLoadWrapper cover;
public readonly BeatmapPicker Picker;
@ -48,6 +51,7 @@ namespace osu.Game.Overlays.BeatmapSet
title.Text = BeatmapSet.Metadata.Title;
artist.Text = BeatmapSet.Metadata.Artist;
downloadButtonsContainer.FadeIn();
noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration);
videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration);
@ -164,7 +168,7 @@ namespace osu.Game.Overlays.BeatmapSet
Children = new Drawable[]
{
new FavouriteButton(),
new Container
downloadButtonsContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
@ -174,7 +178,10 @@ namespace osu.Game.Overlays.BeatmapSet
{
RelativeSizeAxes = Axes.Both,
Alpha = 0f,
Child = new DownloadButton("Download", @""),
Child = new DownloadButton("Download", @"")
{
Action = () => download(false),
},
},
videoButtons = new FillFlowContainer
{
@ -183,8 +190,14 @@ namespace osu.Game.Overlays.BeatmapSet
Alpha = 0f,
Children = new[]
{
new DownloadButton("Download", "with Video"),
new DownloadButton("Download", "without Video"),
new DownloadButton("Download", "with Video")
{
Action = () => download(false),
},
new DownloadButton("Download", "without Video")
{
Action = () => download(true),
},
},
},
},
@ -208,9 +221,39 @@ namespace osu.Game.Overlays.BeatmapSet
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OsuColour colours, BeatmapManager beatmaps)
{
tabsBg.Colour = colours.Gray3;
this.beatmaps = beatmaps;
beatmaps.BeatmapSetAdded += handleBeatmapAdd;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
beatmaps.BeatmapSetAdded -= handleBeatmapAdd;
}
private void handleBeatmapAdd(BeatmapSetInfo beatmap)
{
if (beatmap.OnlineBeatmapSetID == BeatmapSet.OnlineBeatmapSetID)
downloadButtonsContainer.FadeOut(transition_duration);
}
private void download(bool noVideo)
{
if (beatmaps.GetExistingDownload(BeatmapSet) != null)
{
downloadButtonsContainer.MoveToX(-5, 50, Easing.OutSine).Then()
.MoveToX(5, 100, Easing.InOutSine).Then()
.MoveToX(-5, 100, Easing.InOutSine).Then()
.MoveToX(0, 50, Easing.InSine).Then();
return;
}
beatmaps.Download(BeatmapSet, noVideo);
}
}
}

View File

@ -58,7 +58,7 @@ namespace osu.Game.Overlays
private readonly Box chatBackground;
private readonly Box tabBackground;
public Bindable<double> ChatHeight { get; internal set; }
public Bindable<double> ChatHeight { get; set; }
private readonly Container channelSelectionContainer;
private readonly ChannelSelectionOverlay channelSelection;

View File

@ -16,9 +16,7 @@ using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
using osu.Framework.Input;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Framework.Logging;
using osu.Game.Overlays.Notifications;
using osu.Game.Online.API.Requests;
using osu.Framework.Configuration;
using osu.Framework.Audio.Track;
@ -35,10 +33,8 @@ namespace osu.Game.Overlays.Direct
private Container content;
private APIAccess api;
private ProgressBar progressBar;
private BeatmapManager beatmaps;
private NotificationOverlay notifications;
private BeatmapSetOverlay beatmapSetOverlay;
public Track Preview => PlayButton.Preview;
@ -71,11 +67,9 @@ namespace osu.Game.Overlays.Direct
[BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications, BeatmapSetOverlay beatmapSetOverlay)
private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay)
{
this.api = api;
this.beatmaps = beatmaps;
this.notifications = notifications;
this.beatmapSetOverlay = beatmapSetOverlay;
AddInternal(content = new Container
@ -109,6 +103,14 @@ namespace osu.Game.Overlays.Direct
if (downloadRequest != null)
attachDownload(downloadRequest);
beatmaps.BeatmapDownloadBegan += attachDownload;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
beatmaps.BeatmapDownloadBegan -= attachDownload;
}
protected override void Update()
@ -151,16 +153,6 @@ namespace osu.Game.Overlays.Direct
protected void StartDownload()
{
if (!api.LocalUser.Value.IsSupporter)
{
notifications.Post(new SimpleNotification
{
Icon = FontAwesome.fa_superpowers,
Text = "You gotta be a supporter to download for now 'yo"
});
return;
}
if (beatmaps.GetExistingDownload(SetInfo) != null)
{
// we already have an active download running.
@ -172,13 +164,14 @@ namespace osu.Game.Overlays.Direct
return;
}
var request = beatmaps.Download(SetInfo);
attachDownload(request);
beatmaps.Download(SetInfo);
}
private void attachDownload(DownloadBeatmapSetRequest request)
{
if (request.BeatmapSet.OnlineBeatmapSetID != SetInfo.OnlineBeatmapSetID)
return;
progressBar.FadeIn(400, Easing.OutQuint);
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);

View File

@ -19,7 +19,7 @@ using OpenTK.Input;
namespace osu.Game.Overlays.KeyBinding
{
internal class KeyBindingRow : Container, IFilterable
public class KeyBindingRow : Container, IFilterable
{
private readonly object action;
private readonly IEnumerable<Framework.Input.Bindings.KeyBinding> bindings;

View File

@ -55,7 +55,7 @@ namespace osu.Game.Overlays.KeyBinding
}
}
internal class ResetButton : OsuButton
public class ResetButton : OsuButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)

View File

@ -13,7 +13,7 @@ using osu.Game.Graphics.Cursor;
namespace osu.Game.Overlays
{
internal class LoginOverlay : OsuFocusedOverlayContainer
public class LoginOverlay : OsuFocusedOverlayContainer
{
private LoginSettings settingsSection;

View File

@ -13,7 +13,7 @@ using System;
namespace osu.Game.Overlays.Music
{
internal class FilterControl : Container
public class FilterControl : Container
{
public readonly FilterTextBox Search;

View File

@ -17,7 +17,7 @@ using OpenTK;
namespace osu.Game.Overlays.Music
{
internal class PlaylistItem : Container, IFilterable, IDraggable
public class PlaylistItem : Container, IFilterable, IDraggable
{
private const float fade_duration = 100;

View File

@ -14,7 +14,7 @@ using OpenTK;
namespace osu.Game.Overlays.Music
{
internal class PlaylistList : CompositeDrawable
public class PlaylistList : CompositeDrawable
{
public Action<BeatmapSetInfo> OnSelect;

View File

@ -37,7 +37,7 @@ namespace osu.Game.Overlays
private const float bottom_black_area_height = 55;
private Drawable currentBackground;
private Drawable background;
private ProgressBar progressBar;
private IconButton prevButton;
@ -120,7 +120,7 @@ namespace osu.Game.Overlays
},
Children = new[]
{
currentBackground = new Background(),
background = new Background(),
title = new OsuSpriteText
{
Origin = Anchor.BottomCentre,
@ -334,6 +334,7 @@ namespace osu.Game.Overlays
pendingBeatmapSwitch = Schedule(delegate
{
// todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync()
Task.Run(() =>
{
if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists
@ -352,29 +353,26 @@ namespace osu.Game.Overlays
}
});
playerContainer.Add(new AsyncLoadWrapper(new Background(beatmap)
LoadComponentAsync(new Background(beatmap) { Depth = float.MaxValue }, newBackground =>
{
OnLoadComplete = newBackground =>
switch (direction)
{
switch (direction)
{
case TransformDirection.Next:
newBackground.Position = new Vector2(400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
currentBackground.MoveToX(-400, 500, Easing.OutCubic);
break;
case TransformDirection.Prev:
newBackground.Position = new Vector2(-400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
currentBackground.MoveToX(400, 500, Easing.OutCubic);
break;
}
currentBackground.Expire();
currentBackground = newBackground;
case TransformDirection.Next:
newBackground.Position = new Vector2(400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
background.MoveToX(-400, 500, Easing.OutCubic);
break;
case TransformDirection.Prev:
newBackground.Position = new Vector2(-400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
background.MoveToX(400, 500, Easing.OutCubic);
break;
}
})
{
Depth = float.MaxValue,
background.Expire();
background = newBackground;
playerContainer.Add(newBackground);
});
});
}

View File

@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
ShowMoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide();
if (!sets.Any())
if (!sets.Any() && VisiblePages == 1)
{
MissingText.Show();
return;

View File

@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
ShowMoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide();
if (!scores.Any())
if (!scores.Any() && VisiblePages == 1)
{
MissingText.Show();
return;

View File

@ -135,7 +135,7 @@ namespace osu.Game.Overlays.Settings
private class RestoreDefaultValueButton : Box, IHasTooltip
{
private Bindable<T> bindable;
internal Bindable<T> Bindable
public Bindable<T> Bindable
{
get { return bindable; }
set
@ -185,13 +185,13 @@ namespace osu.Game.Overlays.Settings
UpdateState();
}
internal void SetButtonColour(Color4 buttonColour)
public void SetButtonColour(Color4 buttonColour)
{
this.buttonColour = buttonColour;
UpdateState();
}
internal void UpdateState()
public void UpdateState()
{
if (bindable == null)
return;

View File

@ -7,7 +7,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Settings
{
internal class SettingsLabel : SettingsItem<string>
public class SettingsLabel : SettingsItem<string>
{
protected override Drawable CreateControl() => null;

View File

@ -18,8 +18,8 @@ namespace osu.Game.Overlays.Settings
public class Sidebar : Container<SidebarButton>, IStateful<ExpandedState>
{
private readonly FillFlowContainer<SidebarButton> content;
internal const float DEFAULT_WIDTH = ToolbarButton.WIDTH;
internal const int EXPANDED_WIDTH = 200;
public const float DEFAULT_WIDTH = ToolbarButton.WIDTH;
public const int EXPANDED_WIDTH = 200;
public event Action<ExpandedState> StateChanged;
@ -137,4 +137,4 @@ namespace osu.Game.Overlays.Settings
Contracted,
Expanded,
}
}
}

View File

@ -20,7 +20,7 @@ namespace osu.Game.Overlays
{
public abstract class SettingsOverlay : OsuFocusedOverlayContainer
{
internal const float CONTENT_MARGINS = 10;
public const float CONTENT_MARGINS = 10;
public const float TRANSITION_LENGTH = 600;

View File

@ -6,7 +6,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar
{
internal class ToolbarChatButton : ToolbarOverlayToggleButton
public class ToolbarChatButton : ToolbarOverlayToggleButton
{
public ToolbarChatButton()
{
@ -19,4 +19,4 @@ namespace osu.Game.Overlays.Toolbar
StateContainer = chat;
}
}
}
}

View File

@ -6,7 +6,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar
{
internal class ToolbarDirectButton : ToolbarOverlayToggleButton
public class ToolbarDirectButton : ToolbarOverlayToggleButton
{
public ToolbarDirectButton()
{
@ -19,4 +19,4 @@ namespace osu.Game.Overlays.Toolbar
StateContainer = direct;
}
}
}
}

View File

@ -5,7 +5,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar
{
internal class ToolbarHomeButton : ToolbarButton
public class ToolbarHomeButton : ToolbarButton
{
public ToolbarHomeButton()
{
@ -14,4 +14,4 @@ namespace osu.Game.Overlays.Toolbar
TooltipSub = "Return to the main menu";
}
}
}
}

View File

@ -14,7 +14,7 @@ using osu.Game.Rulesets;
namespace osu.Game.Overlays.Toolbar
{
internal class ToolbarModeSelector : Container
public class ToolbarModeSelector : Container
{
private const float padding = 10;

View File

@ -6,7 +6,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar
{
internal class ToolbarMusicButton : ToolbarOverlayToggleButton
public class ToolbarMusicButton : ToolbarOverlayToggleButton
{
public ToolbarMusicButton()
{
@ -19,4 +19,4 @@ namespace osu.Game.Overlays.Toolbar
StateContainer = music;
}
}
}
}

View File

@ -7,7 +7,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar
{
internal class ToolbarNotificationButton : ToolbarOverlayToggleButton
public class ToolbarNotificationButton : ToolbarOverlayToggleButton
{
protected override Anchor TooltipAnchor => Anchor.TopRight;
@ -24,4 +24,4 @@ namespace osu.Game.Overlays.Toolbar
StateContainer = notificationOverlay;
}
}
}
}

View File

@ -9,7 +9,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar
{
internal class ToolbarOverlayToggleButton : ToolbarButton
public class ToolbarOverlayToggleButton : ToolbarButton
{
private readonly Box stateBackground;

View File

@ -6,7 +6,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar
{
internal class ToolbarSettingsButton : ToolbarOverlayToggleButton
public class ToolbarSettingsButton : ToolbarOverlayToggleButton
{
public ToolbarSettingsButton()
{
@ -21,4 +21,4 @@ namespace osu.Game.Overlays.Toolbar
StateContainer = settings;
}
}
}
}

View File

@ -6,7 +6,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar
{
internal class ToolbarSocialButton : ToolbarOverlayToggleButton
public class ToolbarSocialButton : ToolbarOverlayToggleButton
{
public ToolbarSocialButton()
{
@ -19,4 +19,4 @@ namespace osu.Game.Overlays.Toolbar
StateContainer = chat;
}
}
}
}

View File

@ -9,7 +9,7 @@ using OpenTK;
namespace osu.Game.Overlays.Toolbar
{
internal class ToolbarUserArea : Container
public class ToolbarUserArea : Container
{
public LoginOverlay LoginOverlay;
private ToolbarUserButton button;

View File

@ -12,7 +12,7 @@ using OpenTK.Graphics;
namespace osu.Game.Overlays.Toolbar
{
internal class ToolbarUserButton : ToolbarButton, IOnlineComponent
public class ToolbarUserButton : ToolbarButton, IOnlineComponent
{
private readonly UpdateableAvatar avatar;

View File

@ -20,12 +20,12 @@ namespace osu.Game.Rulesets.Judgements
/// <summary>
/// The combo prior to this judgement occurring.
/// </summary>
internal int ComboAtJudgement;
public int ComboAtJudgement;
/// <summary>
/// The highest combo achieved prior to this judgement occurring.
/// </summary>
internal int HighestComboAtJudgement;
public int HighestComboAtJudgement;
/// <summary>
/// Whether a successful hit occurred.
@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Judgements
/// The offset from a perfect hit at which this judgement occurred.
/// Populated when added via <see cref="DrawableHitObject{TObject}.AddJudgement"/>.
/// </summary>
public double TimeOffset { get; internal set; }
public double TimeOffset { get; set; }
/// <summary>
/// Whether the <see cref="Result"/> should affect the combo portion of the score.

View File

@ -0,0 +1,15 @@
// 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.Scoring;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// An interface for mods that make general adjustments to score processor.
/// </summary>
public interface IApplicableToScoreProcessor
{
void ApplyToScoreProcessor(ScoreProcessor scoreProcessor);
}
}

View File

@ -1,6 +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.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mods
{
public abstract class ModPerfect : ModSuddenDeath
@ -8,5 +10,7 @@ namespace osu.Game.Rulesets.Mods
public override string Name => "Perfect";
public override string ShortenedName => "PF";
public override string Description => "SS or quit.";
protected override bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Accuracy.Value != 1;
}
}
}

View File

@ -3,10 +3,11 @@
using System;
using osu.Game.Graphics;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mods
{
public abstract class ModSuddenDeath : Mod
public abstract class ModSuddenDeath : Mod, IApplicableToScoreProcessor
{
public override string Name => "Sudden Death";
public override string ShortenedName => "SD";
@ -16,5 +17,12 @@ namespace osu.Game.Rulesets.Mods
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) };
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
{
scoreProcessor.FailConditions += FailCondition;
}
protected virtual bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Combo.Value == 0;
}
}
}

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
/// <summary>
/// A HitObjectParser to parse legacy osu!catch Beatmaps.
/// </summary>
internal class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
protected override HitObject CreateHit(Vector2 position, bool newCombo)
{

View File

@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// <summary>
/// A HitObjectParser to parse legacy Beatmaps.
/// </summary>
internal abstract class ConvertHitObjectParser : HitObjectParser
public abstract class ConvertHitObjectParser : HitObjectParser
{
public override HitObject Parse(string text)
{

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
/// <summary>
/// A HitObjectParser to parse legacy osu!mania Beatmaps.
/// </summary>
internal class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
protected override HitObject CreateHit(Vector2 position, bool newCombo)
{

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
/// <summary>
/// A HitObjectParser to parse legacy osu! Beatmaps.
/// </summary>
internal class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
protected override HitObject CreateHit(Vector2 position, bool newCombo)
{

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
/// <summary>
/// A HitObjectParser to parse legacy osu!taiko Beatmaps.
/// </summary>
internal class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
protected override HitObject CreateHit(Vector2 position, bool newCombo)
{

View File

@ -31,6 +31,11 @@ namespace osu.Game.Rulesets.Scoring
/// </summary>
public event Action<Judgement> NewJudgement;
/// <summary>
/// Additional conditions on top of <see cref="DefaultFailCondition"/> that cause a failing state.
/// </summary>
public event Func<ScoreProcessor, bool> FailConditions;
/// <summary>
/// The current total score.
/// </summary>
@ -72,9 +77,9 @@ namespace osu.Game.Rulesets.Scoring
public virtual bool HasFailed { get; private set; }
/// <summary>
/// The conditions for failing.
/// The default conditions for failing.
/// </summary>
protected virtual bool FailCondition => Health.Value == Health.MinValue;
protected virtual bool DefaultFailCondition => Health.Value == Health.MinValue;
protected ScoreProcessor()
{
@ -121,7 +126,10 @@ namespace osu.Game.Rulesets.Scoring
/// </summary>
protected void UpdateFailed()
{
if (HasFailed || !FailCondition)
if (HasFailed)
return;
if (!DefaultFailCondition && FailConditions?.Invoke(this) != true)
return;
if (Failed?.Invoke() != false)
@ -216,8 +224,8 @@ namespace osu.Game.Rulesets.Scoring
OnNewJudgement(judgement);
updateScore();
NotifyNewJudgement(judgement);
UpdateFailed();
NotifyNewJudgement(judgement);
}
protected void RemoveJudgement(Judgement judgement)

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Timing
/// <summary>
/// A <see cref="ScrollingContainer"/> which scrolls linearly relative to the <see cref="MultiplierControlPoint"/> start time.
/// </summary>
internal class LinearScrollingContainer : ScrollingContainer
public class LinearScrollingContainer : ScrollingContainer
{
private readonly MultiplierControlPoint controlPoint;

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.UI
/// </summary>
public HitObjectContainer HitObjects { get; protected set; }
internal Container<Drawable> ScaledContent;
public Container<Drawable> ScaledContent;
/// <summary>
/// Whether we are currently providing the local user a gameplay cursor.

View File

@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.UI
/// A visual representation of a <see cref="Rulesets.Ruleset"/>.
/// </summary>
/// <param name="ruleset">The ruleset being repesented.</param>
internal RulesetContainer(Ruleset ruleset)
protected RulesetContainer(Ruleset ruleset)
{
Ruleset = ruleset;
}

View File

@ -32,9 +32,7 @@ namespace osu.Game.Screens.Backgrounds
Schedule(() =>
{
var newBackground = new BeatmapBackground(beatmap);
LoadComponentAsync(newBackground, delegate
LoadComponentAsync(new BeatmapBackground(beatmap), b =>
{
float newDepth = 0;
if (background != null)
@ -45,8 +43,8 @@ namespace osu.Game.Screens.Backgrounds
background.Expire();
}
newBackground.Depth = newDepth;
Add(background = newBackground);
b.Depth = newDepth;
Add(background = b);
background.BlurSigma = blurTarget;
});
});

View File

@ -3,7 +3,7 @@
namespace osu.Game.Screens.Charts
{
internal class ChartInfo : ScreenWhiteBox
public class ChartInfo : ScreenWhiteBox
{
}
}

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace osu.Game.Screens.Charts
{
internal class ChartListing : ScreenWhiteBox
public class ChartListing : ScreenWhiteBox
{
protected override IEnumerable<Type> PossibleChildren => new[] {
typeof(ChartInfo)

View File

@ -3,7 +3,7 @@
namespace osu.Game.Screens.Direct
{
internal class OnlineListing : ScreenWhiteBox
public class OnlineListing : ScreenWhiteBox
{
}
}

View File

@ -0,0 +1,50 @@
// 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.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
namespace osu.Game.Screens.Edit.Components
{
public class BottomBarContainer : Container
{
private const float corner_radius = 5;
private const float contents_padding = 15;
public readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
protected Track Track => Beatmap.Value.Track;
private readonly Drawable background;
private readonly Container content;
protected override Container<Drawable> Content => content;
public BottomBarContainer()
{
Masking = true;
CornerRadius = corner_radius;
InternalChildren = new[]
{
background = new Box { RelativeSizeAxes = Axes.Both },
content = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = contents_padding },
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Gray1;
}
}
}

View File

@ -0,0 +1,154 @@
// 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 OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Edit.Components
{
public class PlaybackControl : BottomBarContainer
{
private readonly IconButton playButton;
public PlaybackControl()
{
PlaybackTabControl tabs;
Children = new Drawable[]
{
playButton = new IconButton
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
Scale = new Vector2(1.4f),
IconScale = new Vector2(1.4f),
Icon = FontAwesome.fa_play_circle_o,
Action = togglePause,
Padding = new MarginPadding { Left = 20 }
},
new OsuSpriteText
{
Origin = Anchor.BottomLeft,
Text = "Playback Speed",
RelativePositionAxes = Axes.Y,
Y = 0.5f,
Padding = new MarginPadding { Left = 45 }
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Padding = new MarginPadding { Left = 45 },
Child = tabs = new PlaybackTabControl(),
}
};
tabs.Current.ValueChanged += newValue => Track.Tempo.Value = newValue;
}
private void togglePause()
{
if (Track.IsRunning)
Track.Stop();
else
Track.Start();
}
protected override void Update()
{
base.Update();
playButton.Icon = Track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o;
}
private class PlaybackTabControl : OsuTabControl<double>
{
private static readonly double[] tempo_values = { 0.5, 0.75, 1 };
protected override TabItem<double> CreateTabItem(double value) => new PlaybackTabItem(value);
protected override Dropdown<double> CreateDropdown() => null;
public PlaybackTabControl()
{
RelativeSizeAxes = Axes.Both;
TabContainer.Spacing = Vector2.Zero;
tempo_values.ForEach(AddItem);
}
public class PlaybackTabItem : TabItem<double>
{
private const float fade_duration = 200;
private readonly OsuSpriteText text;
private readonly OsuSpriteText textBold;
public PlaybackTabItem(double value) : base(value)
{
RelativeSizeAxes = Axes.Both;
Width = 1f / tempo_values.Length;
Children = new Drawable[]
{
text = new OsuSpriteText
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Text = $"{value:0%}",
TextSize = 14,
},
textBold = new OsuSpriteText
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Text = $"{value:0%}",
TextSize = 14,
Font = @"Exo2.0-Bold",
Alpha = 0,
},
};
}
private Color4 hoveredColour;
private Color4 normalColour;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
text.Colour = normalColour = colours.YellowDarker;
textBold.Colour = hoveredColour = colours.Yellow;
}
protected override bool OnHover(InputState state)
{
updateState();
return true;
}
protected override void OnHoverLost(InputState state) => updateState();
protected override void OnActivated() => updateState();
protected override void OnDeactivated() => updateState();
private void updateState()
{
text.FadeColour(Active || IsHovered ? hoveredColour : normalColour, fade_duration, Easing.OutQuint);
text.FadeTo(Active ? 0 : 1, fade_duration, Easing.OutQuint);
textBold.FadeTo(Active ? 1 : 0, fade_duration, Easing.OutQuint);
}
}
}
}
}

View File

@ -0,0 +1,38 @@
// 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.Game.Graphics.Sprites;
using System;
namespace osu.Game.Screens.Edit.Components
{
public class TimeInfoContainer : BottomBarContainer
{
private const int count_duration = 150;
private readonly OsuSpriteText trackTimer;
public TimeInfoContainer()
{
Children = new Drawable[]
{
trackTimer = new OsuSpriteText
{
Origin = Anchor.BottomLeft,
RelativePositionAxes = Axes.Y,
TextSize = 22,
FixedWidth = true,
Y = 0.5f,
}
};
}
protected override void Update()
{
base.Update();
trackTimer.Text = TimeSpan.FromMilliseconds(Track.CurrentTime).ToString(@"mm\:ss\:fff");
}
}
}

View File

@ -11,7 +11,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
/// <summary>
/// The part of the timeline that displays bookmarks.
/// </summary>
internal class BookmarkPart : TimelinePart
public class BookmarkPart : TimelinePart
{
protected override void LoadBeatmap(WorkingBeatmap beatmap)
{

View File

@ -12,7 +12,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
/// <summary>
/// The part of the timeline that displays breaks in the song.
/// </summary>
internal class BreakPart : TimelinePart
public class BreakPart : TimelinePart
{
protected override void LoadBeatmap(WorkingBeatmap beatmap)
{

View File

@ -14,7 +14,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
/// <summary>
/// The part of the timeline that displays the control points.
/// </summary>
internal class ControlPointPart : TimelinePart
public class ControlPointPart : TimelinePart
{
protected override void LoadBeatmap(WorkingBeatmap beatmap)
{

View File

@ -15,7 +15,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
/// <summary>
/// The part of the timeline that displays the current position of the song.
/// </summary>
internal class MarkerPart : TimelinePart
public class MarkerPart : TimelinePart
{
private readonly Drawable marker;

View File

@ -13,7 +13,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
/// <summary>
/// Represents a part of the summary timeline..
/// </summary>
internal abstract class TimelinePart : CompositeDrawable
public abstract class TimelinePart : CompositeDrawable
{
public Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();

View File

@ -3,11 +3,9 @@
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
@ -16,83 +14,64 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
/// <summary>
/// The timeline that sits at the bottom of the editor.
/// </summary>
public class SummaryTimeline : CompositeDrawable
public class SummaryTimeline : BottomBarContainer
{
private const float corner_radius = 5;
private const float contents_padding = 15;
public Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
private readonly Drawable background;
private readonly Drawable timelineBar;
public SummaryTimeline()
{
Masking = true;
CornerRadius = corner_radius;
TimelinePart markerPart, controlPointPart, bookmarkPart, breakPart;
InternalChildren = new[]
Children = new[]
{
background = new Box { RelativeSizeAxes = Axes.Both },
new Container
markerPart = new MarkerPart { RelativeSizeAxes = Axes.Both },
controlPointPart = new ControlPointPart
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
},
bookmarkPart = new BookmarkPart
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
},
timelineBar = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = contents_padding, Right = contents_padding },
Children = new[]
Children = new Drawable[]
{
markerPart = new MarkerPart { RelativeSizeAxes = Axes.Both },
controlPointPart = new ControlPointPart
new Circle
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Size = new Vector2(5)
},
bookmarkPart = new BookmarkPart
new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
Height = 1,
EdgeSmoothness = new Vector2(0, 1),
},
timelineBar = new Container
new Circle
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Circle
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Size = new Vector2(5)
},
new Box
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
Height = 1,
EdgeSmoothness = new Vector2(0, 1),
},
new Circle
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreLeft,
Size = new Vector2(5)
},
}
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreLeft,
Size = new Vector2(5)
},
breakPart = new BreakPart
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Height = 0.25f
}
}
},
breakPart = new BreakPart
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Height = 0.25f
}
};
@ -105,7 +84,6 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Gray1;
timelineBar.Colour = colours.Gray5;
}
}

View File

@ -10,7 +10,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
/// <summary>
/// Represents a spanning point on a timeline part.
/// </summary>
internal class DurationVisualisation : Container
public class DurationVisualisation : Container
{
protected DurationVisualisation(double startTime, double endTime)
{

View File

@ -10,7 +10,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
/// <summary>
/// Represents a singular point on a timeline part.
/// </summary>
internal class PointVisualisation : Box
public class PointVisualisation : Box
{
protected PointVisualisation(double startTime)
{

View File

@ -10,13 +10,13 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Menus;
using osu.Game.Screens.Edit.Components.Timelines.Summary;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Edit.Screens;
using osu.Game.Screens.Edit.Screens.Compose;
using osu.Game.Screens.Edit.Screens.Design;
using osu.Game.Screens.Edit.Components;
namespace osu.Game.Screens.Edit
{
@ -34,7 +34,9 @@ namespace osu.Game.Screens.Edit
public Editor()
{
EditorMenuBar menuBar;
TimeInfoContainer timeInfo;
SummaryTimeline timeline;
PlaybackControl playback;
Children = new[]
{
@ -84,30 +86,47 @@ namespace osu.Game.Screens.Edit
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 5, Bottom = 5, Left = 10, Right = 10 },
Child = new FillFlowContainer
Padding = new MarginPadding { Vertical = 5, Horizontal = 10 },
Child = new GridContainer
{
Name = "Bottom bar",
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new[]
ColumnDimensions = new[]
{
timeline = new SummaryTimeline
new Dimension(GridSizeMode.Absolute, 220),
new Dimension(),
new Dimension(GridSizeMode.Absolute, 220)
},
Content = new[]
{
new Drawable[]
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Width = 0.65f
}
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Right = 10 },
Child = timeInfo = new TimeInfoContainer { RelativeSizeAxes = Axes.Both },
},
timeline = new SummaryTimeline
{
RelativeSizeAxes = Axes.Both,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = 10 },
Child = playback = new PlaybackControl { RelativeSizeAxes = Axes.Both },
}
},
}
}
},
}
}
},
};
timeInfo.Beatmap.BindTo(Beatmap);
timeline.Beatmap.BindTo(Beatmap);
playback.Beatmap.BindTo(Beatmap);
menuBar.Mode.ValueChanged += onModeChanged;
}
@ -154,7 +173,11 @@ namespace osu.Game.Screens.Edit
protected override bool OnExiting(Screen next)
{
Background.FadeColour(Color4.White, 500);
Beatmap.Value.Track?.Start();
if (Beatmap.Value.Track != null)
{
Beatmap.Value.Track.Tempo.Value = 1;
Beatmap.Value.Track.Start();
}
return base.OnExiting(next);
}
}

View File

@ -9,7 +9,7 @@ using OpenTK.Graphics;
namespace osu.Game.Screens.Edit.Screens.Design
{
internal class Design : EditorScreen
public class Design : EditorScreen
{
public Design()
{

View File

@ -17,6 +17,7 @@ using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio;
using osu.Framework.Threading;
namespace osu.Game.Screens.Menu
{
@ -38,10 +39,10 @@ namespace osu.Game.Screens.Menu
private readonly FlowContainerWithOrigin buttonFlow;
//todo: make these non-internal somehow.
internal const float BUTTON_AREA_HEIGHT = 100;
public const float BUTTON_AREA_HEIGHT = 100;
internal const float BUTTON_WIDTH = 140f;
internal const float WEDGE_WIDTH = 20;
public const float BUTTON_WIDTH = 140f;
public const float WEDGE_WIDTH = 20;
private OsuLogo logo;
@ -55,6 +56,8 @@ namespace osu.Game.Screens.Menu
// osuLogo.SizeForFlow relies on loading to be complete.
buttonFlow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0);
updateLogoState();
}
}
@ -217,29 +220,17 @@ namespace osu.Game.Screens.Menu
if (state == MenuState.TopLevel)
buttonArea.FinishTransforms(true);
updateLogoState(lastState);
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);
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;
@ -252,33 +243,6 @@ namespace osu.Game.Screens.Menu
case MenuState.TopLevel:
buttonAreaBackground.ScaleTo(Vector2.One, 200, Easing.Out);
logo.ClearTransforms(targetMember: nameof(Position));
logo.RelativePositionAxes = Axes.None;
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);
foreach (Button b in buttonsTopLevel)
@ -297,8 +261,6 @@ 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;
@ -320,9 +282,69 @@ namespace osu.Game.Screens.Menu
}
}
private Vector2 iconTrackingPosition => logo.Parent.ToLocalSpace(iconFacade.ScreenSpaceDrawQuad.Centre);
private ScheduledDelegate logoDelayedAction;
private bool trackingPosition;
private void updateLogoState(MenuState lastState = MenuState.Initial)
{
if (logo == null) return;
logoDelayedAction?.Cancel();
switch (state)
{
case MenuState.Exit:
case MenuState.Initial:
logoTracking = false;
logoDelayedAction = Scheduler.AddDelayed(() =>
{
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);
}, 150);
break;
case MenuState.TopLevel:
case MenuState.Play:
logo.ClearTransforms(targetMember: nameof(Position));
logo.RelativePositionAxes = Axes.None;
switch (lastState)
{
case MenuState.TopLevel: // coming from toplevel to play
case MenuState.Initial:
logoTracking = false;
logo.ScaleTo(0.5f, 200, Easing.In);
logo.MoveTo(logoTrackingPosition, lastState == MenuState.EnteringMode ? 0 : 200, Easing.In);
logoDelayedAction = Scheduler.AddDelayed(() =>
{
logoTracking = true;
logo.Impact();
toolbar?.Show();
}, 200);
break;
default:
logoTracking = true;
logo.ScaleTo(0.5f, 200, Easing.OutQuint);
break;
}
break;
case MenuState.EnteringMode:
logoTracking = true;
break;
}
}
private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(iconFacade.ScreenSpaceDrawQuad.Centre);
private bool logoTracking;
protected override void Update()
{
@ -333,8 +355,8 @@ namespace osu.Game.Screens.Menu
if (logo != null)
{
if (trackingPosition)
logo.Position = iconTrackingPosition;
if (logoTracking)
logo.Position = logoTrackingPosition;
iconFacade.Width = logo.SizeForFlow * 0.5f;
}

View File

@ -25,7 +25,7 @@ namespace osu.Game.Screens.Menu
/// <summary>
/// Whether we have loaded the menu previously.
/// </summary>
internal bool DidLoadMenu;
public bool DidLoadMenu;
private MainMenu mainMenu;
private SampleChannel welcome;

View File

@ -85,12 +85,11 @@ namespace osu.Game.Screens.Menu
private void updateAmplitudes()
{
var track = beatmap.Value.Track;
var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null;
var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null;
float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes ?? new float[256];
var effect = beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current);
for (int i = 0; i < bars_per_visualiser; i++)
{
if (track?.IsRunning ?? false)

View File

@ -226,7 +226,7 @@ namespace osu.Game.Screens.Menu
/// </summary>
/// <param name="action">The animation to be performed</param>
/// <param name="waitForPrevious">If true, the new animation is delayed until all previous transforms finish. If false, existing transformed are cleared.</param>
internal void AppendAnimatingAction(Action action, bool waitForPrevious)
public void AppendAnimatingAction(Action action, bool waitForPrevious)
{
Action runnableAction = () =>
{

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace osu.Game.Screens.Multiplayer
{
internal class Lobby : ScreenWhiteBox
public class Lobby : ScreenWhiteBox
{
protected override IEnumerable<Type> PossibleChildren => new[] {
typeof(MatchCreate),

View File

@ -12,7 +12,7 @@ using osu.Framework.Graphics;
namespace osu.Game.Screens.Multiplayer
{
internal class Match : ScreenWhiteBox
public class Match : ScreenWhiteBox
{
protected override IEnumerable<Type> PossibleChildren => new[] {
typeof(MatchSongSelect),

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace osu.Game.Screens.Multiplayer
{
internal class MatchCreate : ScreenWhiteBox
public class MatchCreate : ScreenWhiteBox
{
protected override IEnumerable<Type> PossibleChildren => new[] {
typeof(Match)

View File

@ -227,6 +227,9 @@ namespace osu.Game.Screens.Play
// Bind ScoreProcessor to ourselves
scoreProcessor.AllJudged += onCompletion;
scoreProcessor.Failed += onFail;
foreach (var mod in Beatmap.Value.Mods.Value.OfType<IApplicableToScoreProcessor>())
mod.ApplyToScoreProcessor(scoreProcessor);
}
private void applyRateFromMods()

View File

@ -14,7 +14,7 @@ using OpenTK.Graphics;
namespace osu.Game.Screens.Ranking
{
internal class ResultsPage : Container
public class ResultsPage : Container
{
protected readonly Score Score;
protected readonly WorkingBeatmap Beatmap;

View File

@ -12,7 +12,7 @@ using osu.Framework.Graphics.Shapes;
namespace osu.Game.Screens.Ranking
{
internal class ResultsPageRanking : ResultsPage
public class ResultsPageRanking : ResultsPage
{
public ResultsPageRanking(Score score, WorkingBeatmap beatmap = null) : base(score, beatmap)
{
@ -39,4 +39,4 @@ namespace osu.Game.Screens.Ranking
};
}
}
}
}

View File

@ -26,7 +26,7 @@ using osu.Framework.Graphics.Shapes;
namespace osu.Game.Screens.Ranking
{
internal class ResultsPageScore : ResultsPage
public class ResultsPageScore : ResultsPage
{
private ScoreCounter scoreCounter;
@ -324,7 +324,7 @@ namespace osu.Game.Screens.Ranking
title.Colour = artist.Colour = colours.BlueDarker;
versionMapper.Colour = colours.Gray8;
versionMapper.Text = $"{beatmap.Version} - mapped by {beatmap.Metadata.Author}";
versionMapper.Text = $"{beatmap.Version} - mapped by {beatmap.Metadata.Author.Username}";
title.Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title);
artist.Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist);
}

View File

@ -23,7 +23,7 @@ using osu.Game.Graphics.Cursor;
namespace osu.Game.Screens.Select
{
internal class BeatmapCarousel : OsuScrollContainer
public class BeatmapCarousel : OsuScrollContainer
{
public BeatmapInfo SelectedBeatmap => selectedPanel?.Beatmap;
@ -116,7 +116,7 @@ namespace osu.Game.Screens.Select
Schedule(() => removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID)));
}
internal void UpdateBeatmap(BeatmapInfo beatmap)
public void UpdateBeatmap(BeatmapInfo beatmap)
{
// todo: this method should not run more than once for the same BeatmapSetInfo.
var set = manager.QueryBeatmapSet(s => s.ID == beatmap.BeatmapSetInfoID);
@ -572,7 +572,18 @@ namespace osu.Game.Screens.Select
// Makes sure headers are always _below_ panels,
// and depth flows downward.
panel.Depth = i + (panel is BeatmapSetHeader ? panels.Count : 0);
scrollableContent.Add(panel);
switch (panel.LoadState)
{
case LoadState.NotLoaded:
LoadComponentAsync(panel);
break;
case LoadState.Loading:
break;
default:
scrollableContent.Add(panel);
break;
}
}
}

View File

@ -22,11 +22,11 @@ using osu.Framework.Graphics.Shapes;
namespace osu.Game.Screens.Select
{
internal class BeatmapInfoWedge : OverlayContainer
public class BeatmapInfoWedge : OverlayContainer
{
private static readonly Vector2 wedged_container_shear = new Vector2(0.15f, 0);
private Drawable beatmapInfoContainer;
private Drawable info;
public BeatmapInfoWedge()
{
@ -43,12 +43,6 @@ namespace osu.Game.Screens.Select
};
}
protected override void LoadComplete()
{
base.LoadComplete();
AlwaysPresent = true;
}
protected override bool BlockPassThroughMouse => false;
protected override void PopIn()
@ -65,56 +59,62 @@ namespace osu.Game.Screens.Select
public void UpdateBeatmap(WorkingBeatmap beatmap)
{
var lastContainer = beatmapInfoContainer;
float newDepth = lastContainer?.Depth + 1 ?? 0;
Add(beatmapInfoContainer = new AsyncLoadWrapper(
new BufferedWedgeInfo(beatmap)
{
Shear = -Shear,
OnLoadComplete = d =>
{
this.FadeIn(250);
lastContainer?.FadeOut(250);
lastContainer?.Expire();
}
})
LoadComponentAsync(new BufferedWedgeInfo(beatmap)
{
Depth = newDepth,
Shear = -Shear,
Depth = info?.Depth + 1 ?? 0,
}, newInfo =>
{
// ensure we ourselves are visible if not already.
if (!IsPresent)
this.FadeIn(250);
info?.FadeOut(250);
info?.Expire();
Add(info = newInfo);
});
}
public class BufferedWedgeInfo : BufferedContainer
{
public BufferedWedgeInfo(WorkingBeatmap beatmap)
private readonly WorkingBeatmap working;
public BufferedWedgeInfo(WorkingBeatmap working)
{
BeatmapInfo beatmapInfo = beatmap.BeatmapInfo;
BeatmapMetadata metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
this.working = working;
}
[BackgroundDependencyLoader]
private void load()
{
BeatmapInfo beatmapInfo = working.BeatmapInfo;
BeatmapMetadata metadata = beatmapInfo.Metadata ?? working.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
Beatmap beatmap = working.Beatmap;
List<InfoLabel> labels = new List<InfoLabel>();
if (beatmap.Beatmap != null)
if (beatmap != null)
{
HitObject lastObject = beatmap.Beatmap.HitObjects.LastOrDefault();
HitObject lastObject = beatmap.HitObjects.LastOrDefault();
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0;
labels.Add(new InfoLabel(new BeatmapStatistic
{
Name = "Length",
Icon = FontAwesome.fa_clock_o,
Content = beatmap.Beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(endTime - beatmap.Beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"),
Content = beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(endTime - beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"),
}));
labels.Add(new InfoLabel(new BeatmapStatistic
{
Name = "BPM",
Icon = FontAwesome.fa_circle,
Content = getBPMRange(beatmap.Beatmap),
Content = getBPMRange(beatmap),
}));
//get statistics from the current ruleset.
labels.AddRange(beatmapInfo.Ruleset.CreateInstance().GetBeatmapStatistics(beatmap).Select(s => new InfoLabel(s)));
labels.AddRange(beatmapInfo.Ruleset.CreateInstance().GetBeatmapStatistics(working).Select(s => new InfoLabel(s)));
}
PixelSnapping = true;
@ -140,7 +140,7 @@ namespace osu.Game.Screens.Select
Children = new[]
{
// Zoomed-in and cropped beatmap background
new BeatmapBackgroundSprite(beatmap)
new BeatmapBackgroundSprite(working)
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
@ -149,7 +149,7 @@ namespace osu.Game.Screens.Select
},
},
},
new DifficultyColourBar(beatmap.BeatmapInfo)
new DifficultyColourBar(beatmapInfo)
{
RelativeSizeAxes = Axes.Y,
Width = 20,
@ -206,13 +206,13 @@ namespace osu.Game.Screens.Select
Font = @"Exo2.0-Medium",
Text = "mapped by ",
TextSize = 15,
},
},
new OsuSpriteText
{
Font = @"Exo2.0-Bold",
Text = metadata.Author.Username,
TextSize = 15,
},
},
}
},
new FillFlowContainer
@ -244,38 +244,39 @@ namespace osu.Game.Screens.Select
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
new SpriteIcon
{
Icon = FontAwesome.fa_square,
Origin = Anchor.Centre,
Colour = new Color4(68, 17, 136, 255),
Rotation = 45,
Size = new Vector2(20),
},
new SpriteIcon
{
Icon = statistic.Icon,
Origin = Anchor.Centre,
Colour = new Color4(255, 221, 85, 255),
Scale = new Vector2(0.8f),
Size = new Vector2(20),
},
new OsuSpriteText
{
Margin = new MarginPadding { Left = 13 },
Font = @"Exo2.0-Bold",
Colour = new Color4(255, 221, 85, 255),
Text = statistic.Content,
TextSize = 17,
Origin = Anchor.CentreLeft
},
new SpriteIcon
{
Icon = FontAwesome.fa_square,
Origin = Anchor.Centre,
Colour = new Color4(68, 17, 136, 255),
Rotation = 45,
Size = new Vector2(20),
},
new SpriteIcon
{
Icon = statistic.Icon,
Origin = Anchor.Centre,
Colour = new Color4(255, 221, 85, 255),
Scale = new Vector2(0.8f),
Size = new Vector2(20),
},
new OsuSpriteText
{
Margin = new MarginPadding { Left = 13 },
Font = @"Exo2.0-Bold",
Colour = new Color4(255, 221, 85, 255),
Text = statistic.Content,
TextSize = 17,
Origin = Anchor.CentreLeft
},
};
}
}
private class DifficultyColourBar : DifficultyColouredContainer
{
public DifficultyColourBar(BeatmapInfo beatmap) : base(beatmap)
public DifficultyColourBar(BeatmapInfo beatmap)
: base(beatmap)
{
}
@ -286,21 +287,21 @@ namespace osu.Game.Screens.Select
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = AccentColour,
Width = full_opacity_ratio,
},
new Box
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Colour = AccentColour,
Alpha = 0.5f,
X = full_opacity_ratio,
Width = 1 - full_opacity_ratio,
}
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = AccentColour,
Width = full_opacity_ratio,
},
new Box
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Colour = AccentColour,
Alpha = 0.5f,
X = full_opacity_ratio,
Width = 1 - full_opacity_ratio,
}
};
}
}

View File

@ -40,20 +40,12 @@ namespace osu.Game.Screens.Select.Leaderboards
scores = value;
getScoresRequest?.Cancel();
int i = 150;
scrollFlow?.FadeOut(200);
scrollFlow?.Expire();
scrollFlow = null;
if (scores == null)
{
if (scrollFlow != null)
{
foreach (var c in scrollFlow.Children)
c.FadeOut(i += 10);
foreach (var c in scrollFlow.Children)
c.LifetimeEnd = Time.Current + i;
}
return;
}
// schedule because we may not be loaded yet (LoadComponentAsync complains).
Schedule(() =>
@ -64,13 +56,12 @@ namespace osu.Game.Screens.Select.Leaderboards
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 5f),
Padding = new MarginPadding { Top = 10, Bottom = 5 },
ChildrenEnumerable = scores.Select(s => new LeaderboardScore(s, 1 + i) { Action = () => ScoreSelected?.Invoke(s) })
ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) })
}, f =>
{
scrollFlow?.Expire();
scrollContainer.Add(scrollFlow = f);
i = 0;
int i = 0;
foreach (var s in f.Children)
{
using (s.BeginDelayedSequence(i++ * 50, true))

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
@ -24,8 +23,6 @@ namespace osu.Game.Screens.Select.Leaderboards
{
public static readonly float HEIGHT = 60;
public event Action<Visibility> StateChanged;
public readonly int RankPosition;
public readonly Score Score;

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