Merge branch 'master'

Conflicts:
	osu-framework
	osu.Game/Screens/Play/Player.cs
This commit is contained in:
Dean Herbert
2018-01-30 18:30:03 +09:00
100 changed files with 1916 additions and 667 deletions

View File

@ -287,15 +287,16 @@ namespace osu.Game.Beatmaps
Import(archive);
downloadNotification.State = ProgressNotificationState.Completed;
currentDownloads.Remove(request);
}, TaskCreationOptions.LongRunning);
currentDownloads.Remove(request);
};
request.Failure += data =>
request.Failure += error =>
{
if (error is OperationCanceledException) return;
downloadNotification.State = ProgressNotificationState.Completed;
Logger.Error(data, "Failed to get beatmap download information");
Logger.Error(error, "Beatmap download failed!");
currentDownloads.Remove(request);
};

View File

@ -301,6 +301,6 @@ namespace osu.Game.Beatmaps.Formats
}
}
private string cleanFilename(string path) => FileSafety.PathSanitise(path.Trim('\"'));
private string cleanFilename(string path) => FileSafety.PathStandardise(FileSafety.PathSanitise(path.Trim('\"')));
}
}

View File

@ -0,0 +1,71 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Configuration;
using osu.Game.Rulesets;
namespace osu.Game.Configuration
{
public abstract class DatabasedConfigManager<T> : ConfigManager<T>
where T : struct
{
private readonly SettingsStore settings;
private readonly int variant;
private readonly List<DatabasedSetting> databasedSettings;
private readonly RulesetInfo ruleset;
protected DatabasedConfigManager(SettingsStore settings, RulesetInfo ruleset = null, int variant = 0)
{
this.settings = settings;
this.ruleset = ruleset;
this.variant = variant;
databasedSettings = settings.Query(ruleset?.ID, variant);
InitialiseDefaults();
}
protected override void PerformLoad()
{
}
protected override bool PerformSave()
{
return true;
}
protected override void AddBindable<TBindable>(T lookup, Bindable<TBindable> bindable)
{
base.AddBindable(lookup, bindable);
var setting = databasedSettings.FirstOrDefault(s => (int)s.Key == (int)(object)lookup);
if (setting != null)
{
bindable.Parse(setting.Value);
}
else
{
settings.Update(setting = new DatabasedSetting
{
Key = lookup,
Value = bindable.Value,
RulesetID = ruleset?.ID,
Variant = variant,
});
databasedSettings.Add(setting);
}
bindable.ValueChanged += v =>
{
setting.Value = v;
settings.Update(setting);
};
}
}
}

View File

@ -0,0 +1,51 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel.DataAnnotations.Schema;
using osu.Game.Database;
namespace osu.Game.Configuration
{
[Table("Settings")]
public class DatabasedSetting : IHasPrimaryKey
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public int? RulesetID { get; set; }
public int? Variant { get; set; }
[Column("Key")]
public int IntKey
{
get => (int)Key;
private set => Key = value;
}
[Column("Value")]
public string StringValue
{
get => Value.ToString();
set => Value = value;
}
public object Key;
public object Value;
public DatabasedSetting(object key, object value)
{
Key = key;
Value = value;
}
/// <summary>
/// Constructor for derived classes that may require serialisation.
/// </summary>
public DatabasedSetting()
{
}
public override string ToString() => $"{Key}=>{Value}";
}
}

View File

@ -8,7 +8,7 @@ using osu.Game.Screens.Select;
namespace osu.Game.Configuration
{
public class OsuConfigManager : ConfigManager<OsuSetting>
public class OsuConfigManager : IniConfigManager<OsuSetting>
{
protected override void InitialiseDefaults()
{

View File

@ -0,0 +1,44 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Database;
namespace osu.Game.Configuration
{
public class SettingsStore : DatabaseBackedStore
{
public event Action SettingChanged;
public SettingsStore(Func<OsuDbContext> createContext)
: base(createContext)
{
}
/// <summary>
/// Retrieve <see cref="DatabasedSetting"/>s for a specified ruleset/variant content.
/// </summary>
/// <param name="rulesetId">The ruleset's internal ID.</param>
/// <param name="variant">An optional variant.</param>
/// <returns></returns>
public List<DatabasedSetting> Query(int? rulesetId = null, int? variant = null) =>
GetContext().DatabasedSetting.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList();
public void Update(DatabasedSetting setting)
{
var context = GetContext();
var newValue = setting.Value;
Refresh(ref setting);
setting.Value = newValue;
context.SaveChanges();
SettingChanged?.Invoke();
}
}
}

View File

@ -34,8 +34,14 @@ namespace osu.Game.Database
if (context.Entry(obj).State != EntityState.Detached) return;
var id = obj.ID;
obj = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find<T>(id);
context.Entry(obj).Reload();
var foundObject = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find<T>(id);
if (foundObject != null)
{
obj = foundObject;
context.Entry(obj).Reload();
}
else
context.Add(obj);
}
/// <summary>

View File

@ -8,9 +8,10 @@ using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Input.Bindings;
using osu.Game.Configuration;
using osu.Game.IO;
using osu.Game.Rulesets;
using DatabasedKeyBinding = osu.Game.Input.Bindings.DatabasedKeyBinding;
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
namespace osu.Game.Database
@ -22,6 +23,7 @@ namespace osu.Game.Database
public DbSet<BeatmapMetadata> BeatmapMetadata { get; set; }
public DbSet<BeatmapSetInfo> BeatmapSetInfo { get; set; }
public DbSet<DatabasedKeyBinding> DatabasedKeyBinding { get; set; }
public DbSet<DatabasedSetting> DatabasedSetting { get; set; }
public DbSet<FileInfo> FileInfo { get; set; }
public DbSet<RulesetInfo> RulesetInfo { get; set; }
@ -86,9 +88,11 @@ namespace osu.Game.Database
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.DeletePending);
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.Hash).IsUnique();
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.Variant);
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => new { b.RulesetID, b.Variant });
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.IntAction);
modelBuilder.Entity<DatabasedSetting>().HasIndex(b => new { b.RulesetID, b.Variant });
modelBuilder.Entity<FileInfo>().HasIndex(b => b.Hash).IsUnique();
modelBuilder.Entity<FileInfo>().HasIndex(b => b.ReferenceCount);

View File

@ -18,7 +18,7 @@ namespace osu.Game.Graphics.UserInterface
public Color4 FillColour
{
set { fill.Colour = value; }
set { fill.FadeColour(value, 150, Easing.OutQuint); }
}
public Color4 BackgroundColour

View File

@ -14,7 +14,7 @@ namespace osu.Game.Input.Bindings
/// A KeyBindingInputManager with a database backing for custom overrides.
/// </summary>
/// <typeparam name="T">The type of the custom action.</typeparam>
public class DatabasedKeyBindingInputManager<T> : KeyBindingContainer<T>
public class DatabasedKeyBindingContainer<T> : KeyBindingContainer<T>
where T : struct
{
private readonly RulesetInfo ruleset;
@ -23,7 +23,7 @@ namespace osu.Game.Input.Bindings
private KeyBindingStore store;
public override IEnumerable<KeyBinding> DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings();
public override IEnumerable<KeyBinding> DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings(variant ?? 0);
/// <summary>
/// Create a new instance.
@ -31,7 +31,7 @@ namespace osu.Game.Input.Bindings
/// <param name="ruleset">A reference to identify the current <see cref="Ruleset"/>. Used to lookup mappings. Null for global mappings.</param>
/// <param name="variant">An optional variant for the specified <see cref="Ruleset"/>. Used when a ruleset has more than one possible keyboard layouts.</param>
/// <param name="simultaneousMode">Specify how to deal with multiple matches of <see cref="KeyCombination"/>s and <see cref="T"/>s.</param>
public DatabasedKeyBindingInputManager(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None)
public DatabasedKeyBindingContainer(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None)
: base(simultaneousMode)
{
this.ruleset = ruleset;

View File

@ -10,11 +10,11 @@ using osu.Framework.Input.Bindings;
namespace osu.Game.Input.Bindings
{
public class GlobalKeyBindingInputManager : DatabasedKeyBindingInputManager<GlobalAction>, IHandleGlobalInput
public class GlobalActionContainer : DatabasedKeyBindingContainer<GlobalAction>, IHandleGlobalInput
{
private readonly Drawable handler;
public GlobalKeyBindingInputManager(OsuGameBase game)
public GlobalActionContainer(OsuGameBase game)
{
if (game is IKeyBindingHandler<GlobalAction>)
handler = game;
@ -37,7 +37,8 @@ namespace osu.Game.Input.Bindings
public IEnumerable<KeyBinding> InGameKeyBindings => new[]
{
new KeyBinding(InputKey.Space, GlobalAction.SkipCutscene)
new KeyBinding(InputKey.Space, GlobalAction.SkipCutscene),
new KeyBinding(InputKey.Tilde, GlobalAction.QuickRetry)
};
protected override IEnumerable<Drawable> KeyBindingInputQueue =>
@ -65,6 +66,8 @@ namespace osu.Game.Input.Bindings
// In-Game Keybindings
[Description("Skip Cutscene")]
SkipCutscene
SkipCutscene,
[Description("Quick Retry (Hold)")]
QuickRetry,
}
}

View File

@ -0,0 +1,329 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using osu.Game.Database;
using System;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20180125143340_Settings")]
partial class Settings
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<float>("SliderMultiplier");
b.Property<float>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("AudioLeadIn");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MD5Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntKey")
.HasColumnName("Key");
b.Property<int?>("RulesetID");
b.Property<string>("StringValue")
.HasColumnName("Value");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.Property<string>("ShortName");
b.HasKey("ID");
b.HasIndex("Available");
b.HasIndex("ShortName")
.IsUnique();
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,57 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace osu.Game.Migrations
{
public partial class Settings : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_KeyBinding_Variant",
table: "KeyBinding");
migrationBuilder.CreateTable(
name: "Settings",
columns: table => new
{
ID = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Key = table.Column<int>(type: "INTEGER", nullable: false),
RulesetID = table.Column<int>(type: "INTEGER", nullable: true),
Value = table.Column<string>(type: "TEXT", nullable: true),
Variant = table.Column<int>(type: "INTEGER", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Settings", x => x.ID);
});
migrationBuilder.CreateIndex(
name: "IX_KeyBinding_RulesetID_Variant",
table: "KeyBinding",
columns: new[] { "RulesetID", "Variant" });
migrationBuilder.CreateIndex(
name: "IX_Settings_RulesetID_Variant",
table: "Settings",
columns: new[] { "RulesetID", "Variant" });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Settings");
migrationBuilder.DropIndex(
name: "IX_KeyBinding_RulesetID_Variant",
table: "KeyBinding");
migrationBuilder.CreateIndex(
name: "IX_KeyBinding_Variant",
table: "KeyBinding",
column: "Variant");
}
}
}

View File

@ -193,6 +193,28 @@ namespace osu.Game.Migrations
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntKey")
.HasColumnName("Key");
b.Property<int?>("RulesetID");
b.Property<string>("StringValue")
.HasColumnName("Value");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
@ -212,7 +234,7 @@ namespace osu.Game.Migrations
b.HasIndex("IntAction");
b.HasIndex("Variant");
b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding");
});

View File

@ -126,6 +126,7 @@ namespace osu.Game.Online.API
userReq.Success += u =>
{
LocalUser.Value = u;
Username = LocalUser.Value.Username;
failureCount = 0;
//we're connected!

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Configuration;
using osu.Framework.Screens;
using osu.Game.Configuration;
@ -27,6 +28,7 @@ using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
using osu.Game.Screens.Play;
using osu.Game.Input.Bindings;
using osu.Game.Rulesets.Mods;
using OpenTK.Graphics;
namespace osu.Game
@ -71,6 +73,7 @@ namespace osu.Game
private OsuScreen screenStack;
private VolumeControl volume;
private OnScreenDisplay onscreenDisplay;
private Bindable<int> configRuleset;
public Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
@ -79,6 +82,9 @@ namespace osu.Game
private SettingsOverlay settings;
// todo: move this to SongSelect once Screen has the ability to unsuspend.
public readonly Bindable<IEnumerable<Mod>> SelectedMods = new Bindable<IEnumerable<Mod>>(new List<Mod>());
public OsuGame(string[] args = null)
{
this.args = args;
@ -110,7 +116,7 @@ namespace osu.Game
Task.Run(() => BeatmapManager.Import(paths.ToArray()));
}
dependencies.Cache(this);
dependencies.CacheAs(this);
configRuleset = LocalConfig.GetBindable<int>(OsuSetting.Ruleset);
Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First();
@ -195,7 +201,7 @@ namespace osu.Game
}, overlayContent.Add);
loadComponentSingleFile(volume = new VolumeControl(), Add);
loadComponentSingleFile(new OnScreenDisplay(), Add);
loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add);
//overlay elements
loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add);
@ -232,6 +238,7 @@ namespace osu.Game
forwardLoggedErrorsToNotifications();
dependencies.Cache(settings);
dependencies.Cache(onscreenDisplay);
dependencies.Cache(social);
dependencies.Cache(direct);
dependencies.Cache(chat);

View File

@ -44,6 +44,8 @@ namespace osu.Game
protected KeyBindingStore KeyBindingStore;
protected SettingsStore SettingsStore;
protected CursorOverrideContainer CursorOverrideContainer;
protected override string MainResourceFile => @"osu.Game.Resources.dll";
@ -93,7 +95,7 @@ namespace osu.Game
dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore<byte[]>(Resources, @"Textures"))));
dependencies.Cache(this);
dependencies.CacheAs(this);
dependencies.Cache(LocalConfig);
runMigrations();
@ -109,10 +111,11 @@ namespace osu.Game
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory.GetContext, RulesetStore, API, Host));
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory.GetContext, Host, BeatmapManager, RulesetStore));
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory.GetContext, RulesetStore));
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory.GetContext));
dependencies.Cache(new OsuColour());
//this completely overrides the framework default. will need to change once we make a proper FontStore.
dependencies.Cache(Fonts = new FontStore { ScaleAdjust = 100 }, true);
dependencies.Cache(Fonts = new FontStore { ScaleAdjust = 100 });
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/FontAwesome"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/osuFont"));
@ -209,10 +212,10 @@ namespace osu.Game
{
base.LoadComplete();
GlobalKeyBindingInputManager globalBinding;
GlobalActionContainer globalBinding;
CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both };
CursorOverrideContainer.Child = globalBinding = new GlobalKeyBindingInputManager(this)
CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this)
{
RelativeSizeAxes = Axes.Both,
Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both }

View File

@ -16,7 +16,6 @@ using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
using osu.Framework.Input;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Logging;
using osu.Game.Online.API.Requests;
using osu.Framework.Configuration;
using osu.Framework.Audio.Track;
@ -65,11 +64,14 @@ namespace osu.Game.Overlays.Direct
Colour = Color4.Black.Opacity(0.3f),
};
private OsuColour colours;
[BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay)
{
this.beatmaps = beatmaps;
this.beatmapSetOverlay = beatmapSetOverlay;
this.colours = colours;
AddInternal(content = new Container
{
@ -182,7 +184,6 @@ namespace osu.Game.Overlays.Direct
{
progressBar.Current.Value = 0;
progressBar.FadeOut(500);
Logger.Error(e, "Failed to get beatmap download information");
};
request.DownloadProgressed += progress => progressBar.Current.Value = progress;
@ -190,7 +191,7 @@ namespace osu.Game.Overlays.Direct
request.Success += data =>
{
progressBar.Current.Value = 1;
progressBar.FadeOut(500);
progressBar.FillColour = colours.Yellow;
};
}

View File

@ -298,7 +298,7 @@ namespace osu.Game.Overlays
Task.Run(() =>
{
var onlineIds = response.Select(r => r.OnlineBeatmapSetID).ToList();
var presentOnlineIds = beatmaps.QueryBeatmapSets(s => onlineIds.Contains(s.OnlineBeatmapSetID)).Select(r => r.OnlineBeatmapSetID).ToList();
var presentOnlineIds = beatmaps.QueryBeatmapSets(s => onlineIds.Contains(s.OnlineBeatmapSetID) && !s.DeletePending).Select(r => r.OnlineBeatmapSetID).ToList();
var sets = response.Select(r => r.ToBeatmapSet(rulesets)).Where(b => !presentOnlineIds.Contains(b.OnlineBeatmapSetID)).ToList();
// may not need scheduling; loads async internally.

View File

@ -12,7 +12,7 @@ namespace osu.Game.Overlays.KeyBinding
public override FontAwesome Icon => FontAwesome.fa_osu_hot;
public override string Header => "Global";
public GlobalKeyBindingsSection(GlobalKeyBindingInputManager manager)
public GlobalKeyBindingsSection(GlobalActionContainer manager)
{
Add(new DefaultBindingsSubsection(manager));
Add(new InGameKeyBindingsSubsection(manager));
@ -23,7 +23,7 @@ namespace osu.Game.Overlays.KeyBinding
{
protected override string Header => string.Empty;
public DefaultBindingsSubsection(GlobalKeyBindingInputManager manager)
public DefaultBindingsSubsection(GlobalActionContainer manager)
: base(null)
{
Defaults = manager.GlobalKeyBindings;
@ -34,7 +34,7 @@ namespace osu.Game.Overlays.KeyBinding
{
protected override string Header => "In Game";
public InGameKeyBindingsSubsection(GlobalKeyBindingInputManager manager) : base(null)
public InGameKeyBindingsSubsection(GlobalActionContainer manager) : base(null)
{
Defaults = manager.InGameKeyBindings;
}

View File

@ -15,7 +15,7 @@ namespace osu.Game.Overlays
protected override Drawable CreateHeader() => new SettingsHeader("key configuration", "Customise your keys!");
[BackgroundDependencyLoader(permitNulls: true)]
private void load(RulesetStore rulesets, GlobalKeyBindingInputManager global)
private void load(RulesetStore rulesets, GlobalActionContainer global)
{
AddSection(new GlobalKeyBindingsSection(global));

View File

@ -188,17 +188,19 @@ namespace osu.Game.Overlays.Mods
start = Mods.Length - 1;
for (int i = start; i < Mods.Length && i >= 0; i += direction)
{
if (Mods[i].HasImplementation)
{
changeSelectedIndex(i);
return;
}
}
if (SelectAt(i)) return;
Deselect();
}
public bool SelectAt(int index)
{
if (!Mods[index].HasImplementation) return false;
changeSelectedIndex(index);
return true;
}
public void Deselect() => changeSelectedIndex(-1);
private void displayMod(Mod mod)

View File

@ -113,6 +113,23 @@ namespace osu.Game.Overlays.Mods
}
}
/// <summary>
/// Select one or more mods in this section.
/// </summary>
/// <param name="mods">The types of <see cref="Mod"/>s which should be deselected.</param>
public void SelectTypes(IEnumerable<Mod> mods)
{
foreach (var button in buttons)
{
for (int i = 0; i < button.Mods.Length; i++)
{
foreach (var mod in mods)
if (mod.GetType().IsInstanceOfType(button.Mods[i]))
button.SelectAt(i);
}
}
}
protected ModSection()
{
AutoSizeAxes = Axes.Y;

View File

@ -51,6 +51,8 @@ namespace osu.Game.Overlays.Mods
[BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets)
{
SelectedMods.ValueChanged += selectedModsChanged;
LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green;
@ -63,6 +65,37 @@ namespace osu.Game.Overlays.Mods
Ruleset.TriggerChange();
}
private void selectedModsChanged(IEnumerable<Mod> obj)
{
foreach (ModSection section in ModSectionsContainer.Children)
section.SelectTypes(obj);
updateMods();
}
private void updateMods()
{
double multiplier = 1.0;
bool ranked = true;
foreach (Mod mod in SelectedMods.Value)
{
multiplier *= mod.ScoreMultiplier;
ranked &= mod.Ranked;
}
MultiplierLabel.Text = $"{multiplier:N2}x";
if (!ranked)
MultiplierLabel.Text += " (Unranked)";
if (multiplier > 1.0)
MultiplierLabel.FadeColour(HighMultiplierColour, 200);
else if (multiplier < 1.0)
MultiplierLabel.FadeColour(LowMultiplierColour, 200);
else
MultiplierLabel.FadeColour(Color4.White, 200);
}
protected override void PopOut()
{
base.PopOut();
@ -97,6 +130,7 @@ namespace osu.Game.Overlays.Mods
{
foreach (ModSection section in ModSectionsContainer.Children)
section.DeselectAll();
refreshSelectedMods();
}
@ -119,30 +153,7 @@ namespace osu.Game.Overlays.Mods
refreshSelectedMods();
}
private void refreshSelectedMods()
{
SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
double multiplier = 1.0;
bool ranked = true;
foreach (Mod mod in SelectedMods.Value)
{
multiplier *= mod.ScoreMultiplier;
ranked &= mod.Ranked;
}
MultiplierLabel.Text = $"{multiplier:N2}x";
if (!ranked)
MultiplierLabel.Text += " (Unranked)";
if (multiplier > 1.0)
MultiplierLabel.FadeColour(HighMultiplierColour, 200);
else if (multiplier < 1.0)
MultiplierLabel.FadeColour(LowMultiplierColour, 200);
else
MultiplierLabel.FadeColour(Color4.White, 200);
}
private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
public ModSelectOverlay()
{

View File

@ -5,8 +5,6 @@ using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
@ -20,10 +18,12 @@ using osu.Framework.Localisation;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Music;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Music;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays
{
@ -65,6 +65,12 @@ namespace osu.Game.Overlays
AlwaysPresent = true;
}
protected override bool OnDragStart(InputState state)
{
base.OnDragStart(state);
return true;
}
protected override bool OnDrag(InputState state)
{
if (base.OnDrag(state)) return true;

View File

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Framework.Configuration.Tracking;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@ -118,43 +118,62 @@ namespace osu.Game.Overlays
[BackgroundDependencyLoader]
private void load(FrameworkConfigManager frameworkConfig)
{
trackSetting(frameworkConfig.GetBindable<FrameSync>(FrameworkSetting.FrameSync), v => display(v, "Frame Limiter", v.GetDescription(), "Ctrl+F7"));
trackSetting(frameworkConfig.GetBindable<string>(FrameworkSetting.AudioDevice), v => display(v, "Audio Device", string.IsNullOrEmpty(v) ? "Default" : v, v));
trackSetting(frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowLogOverlay), v => display(v, "Debug Logs", v ? "visible" : "hidden", "Ctrl+F10"));
void displayResolution() => display(null, "Screen Resolution", frameworkConfig.Get<int>(FrameworkSetting.Width) + "x" + frameworkConfig.Get<int>(FrameworkSetting.Height));
trackSetting(frameworkConfig.GetBindable<int>(FrameworkSetting.Width), v => displayResolution());
trackSetting(frameworkConfig.GetBindable<int>(FrameworkSetting.Height), v => displayResolution());
trackSetting(frameworkConfig.GetBindable<double>(FrameworkSetting.CursorSensitivity), v => display(v, "Cursor Sensitivity", v.ToString(@"0.##x"), "Ctrl+Alt+R to reset"));
trackSetting(frameworkConfig.GetBindable<string>(FrameworkSetting.ActiveInputHandlers),
delegate (string v)
{
bool raw = v.Contains("Raw");
display(raw, "Raw Input", raw ? "enabled" : "disabled", "Ctrl+Alt+R to reset");
});
trackSetting(frameworkConfig.GetBindable<WindowMode>(FrameworkSetting.WindowMode), v => display(v, "Screen Mode", v.ToString(), "Alt+Enter"));
BeginTracking(this, frameworkConfig);
}
private readonly List<IBindable> references = new List<IBindable>();
private readonly Dictionary<(object, IConfigManager), TrackedSettings> trackedConfigManagers = new Dictionary<(object, IConfigManager), TrackedSettings>();
private void trackSetting<T>(Bindable<T> bindable, Action<T> action)
/// <summary>
/// Registers a <see cref="ConfigManager{T}"/> to have its settings tracked by this <see cref="OnScreenDisplay"/>.
/// </summary>
/// <param name="source">The object that is registering the <see cref="ConfigManager{T}"/> to be tracked.</param>
/// <param name="configManager">The <see cref="ConfigManager{T}"/> to be tracked.</param>
/// <exception cref="ArgumentNullException">If <paramref name="configManager"/> is null.</exception>
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is already being tracked from the same <paramref name="source"/>.</exception>
public void BeginTracking(object source, ITrackableConfigManager configManager)
{
// we need to keep references as we bind
references.Add(bindable);
if (configManager == null) throw new ArgumentNullException(nameof(configManager));
bindable.ValueChanged += action;
if (trackedConfigManagers.ContainsKey((source, configManager)))
throw new InvalidOperationException($"{nameof(configManager)} is already registered.");
var trackedSettings = configManager.CreateTrackedSettings();
if (trackedSettings == null)
return;
configManager.LoadInto(trackedSettings);
trackedSettings.SettingChanged += display;
trackedConfigManagers.Add((source, configManager), trackedSettings);
}
private void display(object rawValue, string settingName, string settingValue, string shortcut = @"")
/// <summary>
/// Unregisters a <see cref="ConfigManager{T}"/> from having its settings tracked by this <see cref="OnScreenDisplay"/>.
/// </summary>
/// <param name="source">The object that registered the <see cref="ConfigManager{T}"/> to be tracked.</param>
/// <param name="configManager">The <see cref="ConfigManager{T}"/> that is being tracked.</param>
/// <exception cref="ArgumentNullException">If <paramref name="configManager"/> is null.</exception>
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is not being tracked from the same <see cref="source"/>.</exception>
public void StopTracking(object source, ITrackableConfigManager configManager)
{
if (configManager == null) throw new ArgumentNullException(nameof(configManager));
if (!trackedConfigManagers.TryGetValue((source, configManager), out var existing))
throw new InvalidOperationException($"{nameof(configManager)} is not registered.");
existing.Unload();
existing.SettingChanged -= display;
trackedConfigManagers.Remove((source, configManager));
}
private void display(SettingDescription description)
{
Schedule(() =>
{
textLine1.Text = settingName.ToUpper();
textLine2.Text = settingValue;
textLine3.Text = shortcut.ToUpper();
textLine1.Text = description.Name.ToUpper();
textLine2.Text = description.Value;
textLine3.Text = description.Shortcut.ToUpper();
box.Animate(
b => b.FadeIn(500, Easing.OutQuint),
@ -167,16 +186,16 @@ namespace osu.Game.Overlays
int optionCount = 0;
int selectedOption = -1;
if (rawValue is bool)
if (description.RawValue is bool)
{
optionCount = 1;
if ((bool)rawValue) selectedOption = 0;
if ((bool)description.RawValue) selectedOption = 0;
}
else if (rawValue is Enum)
else if (description.RawValue is Enum)
{
var values = Enum.GetValues(rawValue.GetType());
var values = Enum.GetValues(description.RawValue.GetType());
optionCount = values.Length;
selectedOption = Convert.ToInt32(rawValue);
selectedOption = Convert.ToInt32(description.RawValue);
}
textLine2.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre;

View File

@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Toolbar
}
[BackgroundDependencyLoader(true)]
private void load(SettingsOverlay settings)
private void load(MainSettings settings)
{
StateContainer = settings;
}

View File

@ -0,0 +1,11 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration.Tracking;
namespace osu.Game.Rulesets.Configuration
{
public interface IRulesetConfigManager : ITrackableConfigManager
{
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Configuration;
namespace osu.Game.Rulesets.Configuration
{
public abstract class RulesetConfigManager<T> : DatabasedConfigManager<T>, IRulesetConfigManager
where T : struct
{
protected RulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant) : base(settings, ruleset, variant)
{
}
}
}

View File

@ -136,7 +136,10 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// </summary>
public event Action<DrawableHitObject, ArmedState> ApplyCustomUpdateState;
protected void PlaySamples() => Samples.ForEach(s => s?.Play());
/// <summary>
/// Plays all the hitsounds for this <see cref="DrawableHitObject"/>.
/// </summary>
public void PlaySamples() => Samples.ForEach(s => s?.Play());
protected override void Update()
{

View File

@ -77,6 +77,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
if (repeatCount > 9000)
throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high");
// osu-stable treated the first span of the slider as a repeat, but no repeats are happening
repeatCount = Math.Max(0, repeatCount - 1);
if (split.Length > 7)
length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);
@ -84,8 +88,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
readCustomSampleBanks(split[10], bankInfo);
// One node for each repeat + the start and end nodes
// Note that the first length of the slider is considered a repeat, but there are no actual repeats happening
int nodes = Math.Max(0, repeatCount - 1) + 2;
int nodes = repeatCount + 2;
// Populate node sample bank infos with the default hit object sample bank
var nodeBankInfos = new List<SampleBankInfo>();
@ -128,7 +131,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
// Generate the final per-node samples
var nodeSamples = new List<List<SampleInfo>>(nodes);
for (int i = 0; i <= repeatCount; i++)
for (int i = 0; i < nodes; i++)
nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
result = CreateSlider(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, points, length, curveType, repeatCount, nodeSamples);

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects.Types;
using System;
using System.Collections.Generic;
using OpenTK;
using osu.Game.Audio;
@ -18,34 +17,23 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// </summary>
private const float base_scoring_distance = 100;
/// <summary>
/// <see cref="ConvertSlider"/>s don't need a curve since they're converted to ruleset-specific hitobjects.
/// </summary>
public SliderCurve Curve { get; } = null;
public List<Vector2> ControlPoints { get; set; }
public CurveType CurveType { get; set; }
public double Distance { get; set; }
public List<List<SampleInfo>> RepeatSamples { get; set; }
public int RepeatCount { get; set; } = 1;
public int RepeatCount { get; set; }
public double EndTime => StartTime + RepeatCount * Distance / Velocity;
public double EndTime => StartTime + this.SpanCount() * Distance / Velocity;
public double Duration => EndTime - StartTime;
public double Velocity = 1;
public Vector2 PositionAt(double progress)
{
throw new NotImplementedException();
}
public double ProgressAt(double progress)
{
throw new NotImplementedException();
}
public int RepeatAt(double progress)
{
throw new NotImplementedException();
}
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);

View File

@ -11,6 +11,11 @@ namespace osu.Game.Rulesets.Objects.Types
/// </summary>
public interface IHasCurve : IHasDistance, IHasRepeats
{
/// <summary>
/// The curve.
/// </summary>
SliderCurve Curve { get; }
/// <summary>
/// The control points that shape the curve.
/// </summary>
@ -20,7 +25,10 @@ namespace osu.Game.Rulesets.Objects.Types
/// The type of curve.
/// </summary>
CurveType CurveType { get; }
}
public static class HasCurveExtensions
{
/// <summary>
/// Computes the position on the curve at a given progress, accounting for repeat logic.
/// <para>
@ -28,20 +36,28 @@ namespace osu.Game.Rulesets.Objects.Types
/// </para>
/// </summary>
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
Vector2 PositionAt(double progress);
public static Vector2 PositionAt(this IHasCurve obj, double progress)
=> obj.Curve.PositionAt(obj.ProgressAt(progress));
/// <summary>
/// Finds the progress along the curve, accounting for repeat logic.
/// </summary>
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
/// <returns>[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</returns>
double ProgressAt(double progress);
public static double ProgressAt(this IHasCurve obj, double progress)
{
double p = progress * obj.SpanCount() % 1;
if (obj.SpanAt(progress) % 2 == 1)
p = 1 - p;
return p;
}
/// <summary>
/// Determines which repeat of the curve the progress point is on.
/// Determines which span of the curve the progress point is on.
/// </summary>
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
/// <returns>[0, RepeatCount] where 0 is the first run.</returns>
int RepeatAt(double progress);
/// <returns>[0, SpanCount) where 0 is the first run.</returns>
public static int SpanAt(this IHasCurve obj, double progress)
=> (int)(progress * obj.SpanCount());
}
}

View File

@ -21,4 +21,13 @@ namespace osu.Game.Rulesets.Objects.Types
/// </summary>
List<List<SampleInfo>> RepeatSamples { get; }
}
public static class HasRepeatsExtensions
{
/// <summary>
/// The amount of times the length of this <see cref="IHasRepeats"/> spans.
/// </summary>
/// <param name="obj">The object that has repeats.</param>
public static int SpanCount(this IHasRepeats obj) => obj.RepeatCount + 1;
}
}

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -36,8 +35,7 @@ namespace osu.Game.Rulesets.UI
/// <param name="customWidth">Whether we want our internal coordinate system to be scaled to a specified width.</param>
protected Playfield(float? customWidth = null)
{
// Default height since we force relative size axes
Size = Vector2.One;
RelativeSizeAxes = Axes.Both;
AddInternal(ScaledContent = new ScaledContainer
{
@ -62,12 +60,6 @@ namespace osu.Game.Rulesets.UI
Add(HitObjects);
}
public override Axes RelativeSizeAxes
{
get { return Axes.Both; }
set { throw new InvalidOperationException($@"{nameof(Playfield)}'s {nameof(RelativeSizeAxes)} should never be changed from {Axes.Both}"); }
}
/// <summary>
/// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield.
/// </summary>

View File

@ -16,6 +16,9 @@ using System.Linq;
using osu.Framework.Configuration;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
using osu.Game.Configuration;
using osu.Game.Overlays;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using OpenTK;
@ -35,6 +38,11 @@ namespace osu.Game.Rulesets.UI
/// </summary>
public bool AspectAdjust = true;
/// <summary>
/// The selected variant.
/// </summary>
public virtual int Variant => 0;
/// <summary>
/// The input manager for this RulesetContainer.
/// </summary>
@ -65,6 +73,14 @@ namespace osu.Game.Rulesets.UI
protected readonly Ruleset Ruleset;
private IRulesetConfigManager rulesetConfig;
private OnScreenDisplay onScreenDisplay;
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
/// <summary>
/// A visual representation of a <see cref="Rulesets.Ruleset"/>.
/// </summary>
@ -77,6 +93,20 @@ namespace osu.Game.Rulesets.UI
Cursor = CreateCursor();
}
[BackgroundDependencyLoader(true)]
private void load(OnScreenDisplay onScreenDisplay, SettingsStore settings)
{
this.onScreenDisplay = onScreenDisplay;
rulesetConfig = CreateConfig(Ruleset, settings);
if (rulesetConfig != null)
{
dependencies.Cache(rulesetConfig);
onScreenDisplay?.BeginTracking(this, rulesetConfig);
}
}
public abstract ScoreProcessor CreateScoreProcessor();
/// <summary>
@ -110,11 +140,24 @@ namespace osu.Game.Rulesets.UI
/// </summary>
protected virtual CursorContainer CreateCursor() => null;
protected virtual IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => null;
/// <summary>
/// Creates a Playfield.
/// </summary>
/// <returns>The Playfield.</returns>
protected abstract Playfield CreatePlayfield();
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (rulesetConfig != null)
{
onScreenDisplay?.StopTracking(this, rulesetConfig);
rulesetConfig = null;
}
}
}
/// <summary>
@ -154,7 +197,7 @@ namespace osu.Game.Rulesets.UI
/// <summary>
/// Whether the specified beatmap is assumed to be specific to the current ruleset.
/// </summary>
protected readonly bool IsForCurrentRuleset;
public readonly bool IsForCurrentRuleset;
public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor<TObject>(this);

View File

@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.UI
public abstract class RulesetInputManager<T> : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler
where T : struct
{
public class RulesetKeyBindingContainer : DatabasedKeyBindingInputManager<T>
public class RulesetKeyBindingContainer : DatabasedKeyBindingContainer<T>
{
public RulesetKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
: base(ruleset, variant, unique)

View File

@ -41,6 +41,12 @@ namespace osu.Game.Screens.Play.BreaksOverlay
private readonly InfoContainer info;
private readonly ArrowsOverlay arrowsOverlay;
public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor)
: this(letterboxing)
{
bindProcessor(scoreProcessor);
}
public BreakOverlay(bool letterboxing)
{
this.letterboxing = letterboxing;
@ -148,7 +154,7 @@ namespace osu.Game.Screens.Play.BreaksOverlay
arrowsOverlay.Hide();
}
public void BindProcessor(ScoreProcessor processor)
private void bindProcessor(ScoreProcessor processor)
{
info.AccuracyDisplay.Current.BindTo(processor.Accuracy);
info.GradeDisplay.Current.BindTo(processor.Rank);

View File

@ -6,6 +6,8 @@ using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
@ -39,7 +41,7 @@ namespace osu.Game.Screens.Play
private static bool hasShownNotificationOnce;
public HUDOverlay()
public HUDOverlay(ScoreProcessor scoreProcessor, RulesetContainer rulesetContainer, DecoupleableInterpolatingFramedClock decoupledClock, WorkingBeatmap working, IAdjustableClock adjustableSourceClock)
{
RelativeSizeAxes = Axes.Both;
@ -59,6 +61,18 @@ namespace osu.Game.Screens.Play
ReplaySettingsOverlay = CreateReplaySettingsOverlay(),
}
});
BindProcessor(scoreProcessor);
BindRulesetContainer(rulesetContainer);
Progress.Objects = rulesetContainer.Objects;
Progress.AudioClock = decoupledClock;
Progress.AllowSeeking = rulesetContainer.HasReplayLoaded;
Progress.OnSeek = pos => decoupledClock.Seek(pos);
ModDisplay.Current.BindTo(working.Mods);
ReplaySettingsOverlay.PlaybackSettings.AdjustableClock = adjustableSourceClock;
}
[BackgroundDependencyLoader(true)]
@ -115,7 +129,7 @@ namespace osu.Game.Screens.Play
}
}
public virtual void BindRulesetContainer(RulesetContainer rulesetContainer)
protected virtual void BindRulesetContainer(RulesetContainer rulesetContainer)
{
(rulesetContainer.KeyBindingInputManager as ICanAttachKeyCounter)?.Attach(KeyCounter);
@ -201,7 +215,7 @@ namespace osu.Game.Screens.Play
protected virtual ReplaySettingsOverlay CreateReplaySettingsOverlay() => new ReplaySettingsOverlay();
public virtual void BindProcessor(ScoreProcessor processor)
protected virtual void BindProcessor(ScoreProcessor processor)
{
ScoreCounter?.Current.BindTo(processor.TotalScore);
AccuracyCounter?.Current.BindTo(processor.Accuracy);

View File

@ -1,18 +1,18 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Input;
using OpenTK.Input;
using osu.Framework.Allocation;
using System;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings;
using osu.Game.Input.Bindings;
using OpenTK.Graphics;
namespace osu.Game.Screens.Play
{
public class HotkeyRetryOverlay : Container
public class HotkeyRetryOverlay : Container, IKeyBindingHandler<GlobalAction>
{
public Action Action;
@ -40,28 +40,20 @@ namespace osu.Game.Screens.Play
};
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
public bool OnPressed(GlobalAction action)
{
if (args.Repeat) return false;
if (action != GlobalAction.QuickRetry) return false;
if (args.Key == Key.Tilde)
{
overlay.FadeIn(activate_delay, Easing.Out);
return true;
}
return base.OnKeyDown(state, args);
overlay.FadeIn(activate_delay, Easing.Out);
return true;
}
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
public bool OnReleased(GlobalAction action)
{
if (args.Key == Key.Tilde && !fired)
{
overlay.FadeOut(fadeout_delay, Easing.Out);
return true;
}
if (action != GlobalAction.QuickRetry) return false;
return base.OnKeyUp(state, args);
overlay.FadeOut(fadeout_delay, Easing.Out);
return true;
}
protected override void Update()

View File

@ -1,35 +1,35 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Framework.Timing;
using osu.Game.Configuration;
using osu.Game.Rulesets;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Backgrounds;
using System;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Threading;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Ranking;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Framework.Threading;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Online.API;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Play.BreaksOverlay;
using osu.Game.Screens.Ranking;
using osu.Game.Storyboards.Drawables;
using OpenTK;
namespace osu.Game.Screens.Play
{
@ -79,7 +79,6 @@ namespace osu.Game.Screens.Play
#endregion
private BreakOverlay breakOverlay;
private Container storyboardContainer;
private DrawableStoryboard storyboard;
@ -155,6 +154,8 @@ namespace osu.Game.Screens.Play
userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
userAudioOffset.TriggerChange();
scoreProcessor = RulesetContainer.CreateScoreProcessor();
Children = new Drawable[]
{
storyboardContainer = new Container
@ -170,13 +171,12 @@ namespace osu.Game.Screens.Play
OnRetry = Restart,
OnQuit = Exit,
CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded,
OnPause = () => {
OnPause = () =>
{
pauseContainer.Retries = RestartCount;
hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused;
},
OnResume = () => {
hudOverlay.KeyCounter.IsCounting = true;
},
OnResume = () => hudOverlay.KeyCounter.IsCounting = true,
Children = new Drawable[]
{
new Container
@ -186,12 +186,12 @@ namespace osu.Game.Screens.Play
Child = RulesetContainer,
},
new SkipButton(firstObjectTime) { AudioClock = decoupledClock },
hudOverlay = new HUDOverlay
hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, decoupledClock, working, adjustableSourceClock)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
},
breakOverlay = new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks)
new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, scoreProcessor)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -207,7 +207,8 @@ namespace osu.Game.Screens.Play
},
new HotkeyRetryOverlay
{
Action = () => {
Action = () =>
{
if (!IsCurrentScreen) return;
//we want to hide the hitrenderer immediately (looks better).
@ -218,24 +219,9 @@ namespace osu.Game.Screens.Play
}
};
scoreProcessor = RulesetContainer.CreateScoreProcessor();
if (showStoryboard)
initializeStoryboard(false);
hudOverlay.BindProcessor(scoreProcessor);
hudOverlay.BindRulesetContainer(RulesetContainer);
hudOverlay.Progress.Objects = RulesetContainer.Objects;
hudOverlay.Progress.AudioClock = decoupledClock;
hudOverlay.Progress.OnSeek = pos => decoupledClock.Seek(pos);
hudOverlay.ModDisplay.Current.BindTo(working.Mods);
breakOverlay.BindProcessor(scoreProcessor);
hudOverlay.ReplaySettingsOverlay.PlaybackSettings.AdjustableClock = adjustableSourceClock;
// Bind ScoreProcessor to ourselves
scoreProcessor.AllJudged += onCompletion;
scoreProcessor.Failed += onFail;

View File

@ -13,6 +13,7 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
@ -47,10 +48,13 @@ namespace osu.Game.Screens.Select
private SampleChannel sampleConfirm;
[BackgroundDependencyLoader(true)]
private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay)
private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay, OsuGame game)
{
sampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection");
if (game != null)
modSelect.SelectedMods.BindTo(game.SelectedMods);
Footer.AddButton(@"mods", colours.Yellow, modSelect, Key.F1, float.MaxValue);
BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1);
@ -121,6 +125,9 @@ namespace osu.Game.Screens.Select
if (Beatmap.Value.Track != null)
Beatmap.Value.Track.Looping = false;
Beatmap.Value.Mods.UnbindBindings();
Beatmap.Value.Mods.Value = new Mod[] { };
return false;
}

View File

@ -181,7 +181,7 @@ namespace osu.Game.Screens.Select
[BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuGame osu, OsuColour colours)
{
dependencies.Cache(this);
dependencies.CacheAs(this);
if (Footer != null)
{

View File

@ -6,7 +6,7 @@ using osu.Framework.Platform;
namespace osu.Game.Screens.Tournament.Components
{
public class DrawingsConfigManager : ConfigManager<DrawingsConfig>
public class DrawingsConfigManager : IniConfigManager<DrawingsConfig>
{
protected override string Filename => @"drawings.ini";

View File

@ -91,6 +91,10 @@
<Reference Include="Humanizer, Version=2.2.0.0, Culture=neutral, PublicKeyToken=979442b78dfc278e, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Humanizer.Core.2.2.0\lib\netstandard1.0\Humanizer.dll</HintPath>
</Reference>
<Reference Include="JetBrains.Annotations, Version=11.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Data.Sqlite, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Microsoft.Data.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.Data.Sqlite.dll</HintPath>
</Reference>
@ -265,10 +269,17 @@
<Compile Include="Beatmaps\Formats\JsonBeatmapDecoder.cs" />
<Compile Include="Beatmaps\Formats\LegacyDecoder.cs" />
<Compile Include="Beatmaps\Formats\LegacyStoryboardDecoder.cs" />
<Compile Include="Configuration\DatabasedSetting.cs" />
<Compile Include="Configuration\SettingsStore.cs" />
<Compile Include="Configuration\DatabasedConfigManager.cs" />
<Compile Include="Configuration\SpeedChangeVisualisationMethod.cs" />
<Compile Include="Database\DatabaseContextFactory.cs" />
<Compile Include="Database\IHasPrimaryKey.cs" />
<Compile Include="Graphics\Textures\LargeTextureStore.cs" />
<Compile Include="Migrations\20180125143340_Settings.cs" />
<Compile Include="Migrations\20180125143340_Settings.Designer.cs">
<DependentUpon>20180125143340_Settings.cs</DependentUpon>
</Compile>
<Compile Include="Overlays\Profile\SupporterIcon.cs" />
<Compile Include="Online\API\Requests\GetFriendsRequest.cs" />
<Compile Include="Overlays\Settings\DangerousSettingsButton.cs" />
@ -314,6 +325,8 @@
<Compile Include="Overlays\Profile\Sections\Ranks\ScoreModsContainer.cs" />
<Compile Include="Overlays\Settings\Sections\Gameplay\ScrollingSettings.cs" />
<Compile Include="Overlays\Settings\Sections\Maintenance\DeleteAllBeatmapsDialog.cs" />
<Compile Include="Rulesets\Configuration\IRulesetConfigManager.cs" />
<Compile Include="Rulesets\Configuration\RulesetConfigManager.cs" />
<Compile Include="Rulesets\Mods\IApplicableFailOverride.cs" />
<Compile Include="Rulesets\Mods\IApplicableMod.cs" />
<Compile Include="Rulesets\Mods\IApplicableToBeatmapConverter.cs" />
@ -433,8 +446,8 @@
<Compile Include="Graphics\UserInterface\Volume\VolumeControlReceptor.cs" />
<Compile Include="Graphics\UserInterface\Volume\VolumeMeter.cs" />
<Compile Include="Input\Bindings\DatabasedKeyBinding.cs" />
<Compile Include="Input\Bindings\DatabasedKeyBindingInputManager.cs" />
<Compile Include="Input\Bindings\GlobalKeyBindingInputManager.cs" />
<Compile Include="Input\Bindings\DatabasedKeyBindingContainer.cs" />
<Compile Include="Input\Bindings\GlobalActionContainer.cs" />
<Compile Include="Input\Handlers\ReplayInputHandler.cs" />
<Compile Include="Input\KeyBindingStore.cs" />
<Compile Include="IO\FileInfo.cs" />

View File

@ -12,12 +12,12 @@
<description>click the circles. to the beat.</description>
<summary>click the circles.</summary>
<releaseNotes>testing</releaseNotes>
<copyright>Copyright ppy Pty Ltd 2007-2017</copyright>
<copyright>Copyright ppy Pty Ltd 2007-2018</copyright>
<language>en-AU</language>
</metadata>
<files>
<file src="*.exe" target="lib\net45\" exclude="**vshost**"/>
<file src="*.dll" target="lib\net45\"/>
<file src="*.dll" target="lib\net45\"/>
<file src="*.config" target="lib\net45\"/>
<file src="x86\*.dll" target="lib\net45\x86\"/>
<file src="x64\*.dll" target="lib\net45\x64\"/>

View File

@ -47,6 +47,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
<package id="Humanizer.Core.zh-CN" version="2.2.0" targetFramework="net461" />
<package id="Humanizer.Core.zh-Hans" version="2.2.0" targetFramework="net461" />
<package id="Humanizer.Core.zh-Hant" version="2.2.0" targetFramework="net461" />
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
<package id="Microsoft.CSharp" version="4.4.0" targetFramework="net461" />
<package id="Microsoft.Data.Sqlite.Core" version="2.0.0" targetFramework="net461" />
<package id="Microsoft.EntityFrameworkCore" version="2.0.0" targetFramework="net461" />