mirror of
https://github.com/ppy/osu.git
synced 2025-02-21 19:23:20 +08:00
Add support for creating new blank difficulties
This commit is contained in:
parent
b613aedeb8
commit
dc96c4888b
@ -73,7 +73,9 @@ namespace osu.Game.Beatmaps
|
|||||||
new BeatmapModelManager(realm, storage, onlineLookupQueue);
|
new BeatmapModelManager(realm, storage, onlineLookupQueue);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="WorkingBeatmap"/>.
|
/// Create a new beatmap set, backed by a <see cref="BeatmapSetInfo"/> model,
|
||||||
|
/// with a single difficulty which is backed by a <see cref="BeatmapInfo"/> model
|
||||||
|
/// and represented by the returned usable <see cref="WorkingBeatmap"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public WorkingBeatmap CreateNew(RulesetInfo ruleset, APIUser user)
|
public WorkingBeatmap CreateNew(RulesetInfo ruleset, APIUser user)
|
||||||
{
|
{
|
||||||
@ -105,6 +107,34 @@ namespace osu.Game.Beatmaps
|
|||||||
return imported.PerformRead(s => GetWorkingBeatmap(s.Beatmaps.First()));
|
return imported.PerformRead(s => GetWorkingBeatmap(s.Beatmaps.First()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a new difficulty to the beatmap set represented by the provided <see cref="BeatmapSetInfo"/>.
|
||||||
|
/// The new difficulty will be backed by a <see cref="BeatmapInfo"/> model
|
||||||
|
/// and represented by the returned <see cref="WorkingBeatmap"/>.
|
||||||
|
/// </summary>
|
||||||
|
public WorkingBeatmap CreateNewBlankDifficulty(BeatmapSetInfo beatmapSetInfo, RulesetInfo rulesetInfo)
|
||||||
|
{
|
||||||
|
// fetch one of the existing difficulties to copy timing points and metadata from,
|
||||||
|
// so that the user doesn't have to fill all of that out again.
|
||||||
|
// this silently assumes that all difficulties have the same timing points and metadata,
|
||||||
|
// but cases where this isn't true seem rather rare / pathological.
|
||||||
|
var referenceBeatmap = GetWorkingBeatmap(beatmapSetInfo.Beatmaps.First());
|
||||||
|
|
||||||
|
var newBeatmap = new Beatmap
|
||||||
|
{
|
||||||
|
BeatmapInfo = new BeatmapInfo(rulesetInfo, new BeatmapDifficulty(), referenceBeatmap.Metadata.DeepClone())
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var timingPoint in referenceBeatmap.Beatmap.ControlPointInfo.TimingPoints)
|
||||||
|
newBeatmap.ControlPointInfo.Add(timingPoint.Time, timingPoint.DeepClone());
|
||||||
|
|
||||||
|
var createdBeatmapInfo = beatmapModelManager.AddDifficultyToBeatmapSet(beatmapSetInfo, newBeatmap);
|
||||||
|
return GetWorkingBeatmap(createdBeatmapInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add back support for making a copy of another difficulty
|
||||||
|
// (likely via a separate `CopyDifficulty()` method).
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delete a beatmap difficulty.
|
/// Delete a beatmap difficulty.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -7,6 +7,7 @@ using Newtonsoft.Json;
|
|||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Models;
|
using osu.Game.Models;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
using osu.Game.Utils;
|
||||||
using Realms;
|
using Realms;
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
@ -16,7 +17,7 @@ namespace osu.Game.Beatmaps
|
|||||||
[ExcludeFromDynamicCompile]
|
[ExcludeFromDynamicCompile]
|
||||||
[Serializable]
|
[Serializable]
|
||||||
[MapTo("BeatmapMetadata")]
|
[MapTo("BeatmapMetadata")]
|
||||||
public class BeatmapMetadata : RealmObject, IBeatmapMetadataInfo
|
public class BeatmapMetadata : RealmObject, IBeatmapMetadataInfo, IDeepCloneable<BeatmapMetadata>
|
||||||
{
|
{
|
||||||
public string Title { get; set; } = string.Empty;
|
public string Title { get; set; } = string.Empty;
|
||||||
|
|
||||||
@ -57,5 +58,18 @@ namespace osu.Game.Beatmaps
|
|||||||
IUser IBeatmapMetadataInfo.Author => Author;
|
IUser IBeatmapMetadataInfo.Author => Author;
|
||||||
|
|
||||||
public override string ToString() => this.GetDisplayTitle();
|
public override string ToString() => this.GetDisplayTitle();
|
||||||
|
|
||||||
|
public BeatmapMetadata DeepClone() => new BeatmapMetadata(Author.DeepClone())
|
||||||
|
{
|
||||||
|
Title = Title,
|
||||||
|
TitleUnicode = TitleUnicode,
|
||||||
|
Artist = Artist,
|
||||||
|
ArtistUnicode = ArtistUnicode,
|
||||||
|
Source = Source,
|
||||||
|
Tags = Tags,
|
||||||
|
PreviewTime = PreviewTime,
|
||||||
|
AudioFile = AudioFile,
|
||||||
|
BackgroundFile = BackgroundFile
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,6 @@ namespace osu.Game.Beatmaps
|
|||||||
public virtual void Save(BeatmapInfo beatmapInfo, IBeatmap beatmapContent, ISkin? beatmapSkin = null)
|
public virtual void Save(BeatmapInfo beatmapInfo, IBeatmap beatmapContent, ISkin? beatmapSkin = null)
|
||||||
{
|
{
|
||||||
var setInfo = beatmapInfo.BeatmapSet;
|
var setInfo = beatmapInfo.BeatmapSet;
|
||||||
|
|
||||||
Debug.Assert(setInfo != null);
|
Debug.Assert(setInfo != null);
|
||||||
|
|
||||||
// Difficulty settings must be copied first due to the clone in `Beatmap<>.BeatmapInfo_Set`.
|
// Difficulty settings must be copied first due to the clone in `Beatmap<>.BeatmapInfo_Set`.
|
||||||
@ -85,6 +84,24 @@ namespace osu.Game.Beatmaps
|
|||||||
WorkingBeatmapCache?.Invalidate(beatmapInfo);
|
WorkingBeatmapCache?.Invalidate(beatmapInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a new difficulty to the beatmap set represented by the provided <see cref="BeatmapSetInfo"/>.
|
||||||
|
/// </summary>
|
||||||
|
public BeatmapInfo AddDifficultyToBeatmapSet(BeatmapSetInfo beatmapSetInfo, Beatmap beatmap)
|
||||||
|
{
|
||||||
|
return Realm.Run(realm =>
|
||||||
|
{
|
||||||
|
var beatmapInfo = beatmap.BeatmapInfo;
|
||||||
|
|
||||||
|
beatmapSetInfo.Beatmaps.Add(beatmapInfo);
|
||||||
|
beatmapInfo.BeatmapSet = beatmapSetInfo;
|
||||||
|
|
||||||
|
Save(beatmapInfo, beatmap);
|
||||||
|
|
||||||
|
return beatmapInfo.Detach();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private static string getFilename(BeatmapInfo beatmapInfo)
|
private static string getFilename(BeatmapInfo beatmapInfo)
|
||||||
{
|
{
|
||||||
var metadata = beatmapInfo.Metadata;
|
var metadata = beatmapInfo.Metadata;
|
||||||
@ -103,9 +120,9 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public void Update(BeatmapSetInfo item)
|
public void Update(BeatmapSetInfo item)
|
||||||
{
|
{
|
||||||
Realm.Write(realm =>
|
Realm.Write(r =>
|
||||||
{
|
{
|
||||||
var existing = realm.Find<BeatmapSetInfo>(item.ID);
|
var existing = r.Find<BeatmapSetInfo>(item.ID);
|
||||||
item.CopyChangesToRealm(existing);
|
item.CopyChangesToRealm(existing);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,16 @@ namespace osu.Game.Database
|
|||||||
if (existing != null)
|
if (existing != null)
|
||||||
copyChangesToRealm(beatmap, existing);
|
copyChangesToRealm(beatmap, existing);
|
||||||
else
|
else
|
||||||
d.Beatmaps.Add(beatmap);
|
{
|
||||||
|
var newBeatmap = new BeatmapInfo
|
||||||
|
{
|
||||||
|
ID = beatmap.ID,
|
||||||
|
BeatmapSet = d,
|
||||||
|
Ruleset = d.Realm.Find<RulesetInfo>(beatmap.Ruleset.ShortName)
|
||||||
|
};
|
||||||
|
d.Beatmaps.Add(newBeatmap);
|
||||||
|
copyChangesToRealm(beatmap, newBeatmap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,12 +2,14 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using osu.Game.Database;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
using osu.Game.Utils;
|
||||||
using Realms;
|
using Realms;
|
||||||
|
|
||||||
namespace osu.Game.Models
|
namespace osu.Game.Models
|
||||||
{
|
{
|
||||||
public class RealmUser : EmbeddedObject, IUser, IEquatable<RealmUser>
|
public class RealmUser : EmbeddedObject, IUser, IEquatable<RealmUser>, IDeepCloneable<RealmUser>
|
||||||
{
|
{
|
||||||
public int OnlineID { get; set; } = 1;
|
public int OnlineID { get; set; } = 1;
|
||||||
|
|
||||||
@ -22,5 +24,7 @@ namespace osu.Game.Models
|
|||||||
|
|
||||||
return OnlineID == other.OnlineID && Username == other.Username;
|
return OnlineID == other.OnlineID && Username == other.Username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RealmUser DeepClone() => (RealmUser)this.Detach().MemberwiseClone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -822,11 +822,14 @@ namespace osu.Game.Screens.Edit
|
|||||||
var rulesetItems = new List<MenuItem>();
|
var rulesetItems = new List<MenuItem>();
|
||||||
|
|
||||||
foreach (var ruleset in rulesets.AvailableRulesets.OrderBy(ruleset => ruleset.OnlineID))
|
foreach (var ruleset in rulesets.AvailableRulesets.OrderBy(ruleset => ruleset.OnlineID))
|
||||||
rulesetItems.Add(new EditorMenuItem(ruleset.Name));
|
rulesetItems.Add(new EditorMenuItem(ruleset.Name, MenuItemType.Standard, () => createNewDifficulty(ruleset)));
|
||||||
|
|
||||||
return new EditorMenuItem("Create new difficulty") { Items = rulesetItems };
|
return new EditorMenuItem("Create new difficulty") { Items = rulesetItems };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createNewDifficulty(RulesetInfo rulesetInfo)
|
||||||
|
=> loader?.ScheduleSwitchToNewDifficulty(editorBeatmap.BeatmapInfo.BeatmapSet, rulesetInfo, GetState());
|
||||||
|
|
||||||
private EditorMenuItem createDifficultySwitchMenu()
|
private EditorMenuItem createDifficultySwitchMenu()
|
||||||
{
|
{
|
||||||
var beatmapSet = playableBeatmap.BeatmapInfo.BeatmapSet;
|
var beatmapSet = playableBeatmap.BeatmapInfo.BeatmapSet;
|
||||||
@ -850,7 +853,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
return new EditorMenuItem("Change difficulty") { Items = difficultyItems };
|
return new EditorMenuItem("Change difficulty") { Items = difficultyItems };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void SwitchToDifficulty(BeatmapInfo nextBeatmap) => loader?.ScheduleDifficultySwitch(nextBeatmap, GetState(nextBeatmap));
|
protected void SwitchToDifficulty(BeatmapInfo nextBeatmap) => loader?.ScheduleSwitchToExistingDifficulty(nextBeatmap, GetState(nextBeatmap));
|
||||||
|
|
||||||
private void cancelExit()
|
private void cancelExit()
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,7 @@ using osu.Framework.Screens;
|
|||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
@ -78,7 +79,13 @@ namespace osu.Game.Screens.Edit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ScheduleDifficultySwitch(BeatmapInfo nextBeatmap, EditorState editorState)
|
public void ScheduleSwitchToNewDifficulty(BeatmapSetInfo beatmapSetInfo, RulesetInfo rulesetInfo, EditorState editorState)
|
||||||
|
=> scheduleDifficultySwitch(() => beatmapManager.CreateNewBlankDifficulty(beatmapSetInfo, rulesetInfo), editorState);
|
||||||
|
|
||||||
|
public void ScheduleSwitchToExistingDifficulty(BeatmapInfo beatmapInfo, EditorState editorState)
|
||||||
|
=> scheduleDifficultySwitch(() => beatmapManager.GetWorkingBeatmap(beatmapInfo), editorState);
|
||||||
|
|
||||||
|
private void scheduleDifficultySwitch(Func<WorkingBeatmap> nextBeatmap, EditorState editorState)
|
||||||
{
|
{
|
||||||
scheduledDifficultySwitch?.Cancel();
|
scheduledDifficultySwitch?.Cancel();
|
||||||
ValidForResume = true;
|
ValidForResume = true;
|
||||||
@ -87,7 +94,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
scheduledDifficultySwitch = Schedule(() =>
|
scheduledDifficultySwitch = Schedule(() =>
|
||||||
{
|
{
|
||||||
Beatmap.Value = beatmapManager.GetWorkingBeatmap(nextBeatmap);
|
Beatmap.Value = nextBeatmap.Invoke();
|
||||||
state = editorState;
|
state = editorState;
|
||||||
|
|
||||||
// This screen is a weird exception to the rule that nothing after song select changes the global beatmap.
|
// This screen is a weird exception to the rule that nothing after song select changes the global beatmap.
|
||||||
|
Loading…
Reference in New Issue
Block a user