mirror of
https://github.com/ppy/osu
synced 2024-12-27 17:32:56 +00:00
Add flow for copying existing difficulty content
This commit is contained in:
parent
154460845b
commit
a2c2b2bbb3
@ -20,6 +20,7 @@ using osu.Game.Online.API;
|
|||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Stores;
|
using osu.Game.Stores;
|
||||||
|
|
||||||
@ -112,29 +113,36 @@ namespace osu.Game.Beatmaps
|
|||||||
/// The new difficulty will be backed by a <see cref="BeatmapInfo"/> model
|
/// The new difficulty will be backed by a <see cref="BeatmapInfo"/> model
|
||||||
/// and represented by the returned <see cref="WorkingBeatmap"/>.
|
/// and represented by the returned <see cref="WorkingBeatmap"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual WorkingBeatmap CreateNewBlankDifficulty(BeatmapSetInfo beatmapSetInfo, RulesetInfo rulesetInfo)
|
public virtual WorkingBeatmap CreateNewBlankDifficulty(NewDifficultyCreationParameters creationParameters)
|
||||||
{
|
{
|
||||||
// fetch one of the existing difficulties to copy timing points and metadata from,
|
var referenceBeatmap = creationParameters.ReferenceBeatmap;
|
||||||
// so that the user doesn't have to fill all of that out again.
|
var targetBeatmapSet = creationParameters.BeatmapSet;
|
||||||
// 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 newBeatmapInfo = new BeatmapInfo(rulesetInfo, new BeatmapDifficulty(), referenceBeatmap.Metadata.DeepClone());
|
var newBeatmapInfo = new BeatmapInfo(creationParameters.Ruleset, new BeatmapDifficulty(), referenceBeatmap.Metadata.DeepClone());
|
||||||
|
|
||||||
// populate circular beatmap set info <-> beatmap info references manually.
|
// populate circular beatmap set info <-> beatmap info references manually.
|
||||||
// several places like `BeatmapModelManager.Save()` or `GetWorkingBeatmap()`
|
// several places like `BeatmapModelManager.Save()` or `GetWorkingBeatmap()`
|
||||||
// rely on them being freely traversable in both directions for correct operation.
|
// rely on them being freely traversable in both directions for correct operation.
|
||||||
beatmapSetInfo.Beatmaps.Add(newBeatmapInfo);
|
targetBeatmapSet.Beatmaps.Add(newBeatmapInfo);
|
||||||
newBeatmapInfo.BeatmapSet = beatmapSetInfo;
|
newBeatmapInfo.BeatmapSet = targetBeatmapSet;
|
||||||
|
|
||||||
var newBeatmap = new Beatmap { BeatmapInfo = newBeatmapInfo };
|
IBeatmap newBeatmap;
|
||||||
foreach (var timingPoint in referenceBeatmap.Beatmap.ControlPointInfo.TimingPoints)
|
|
||||||
|
if (creationParameters.ClearAllObjects)
|
||||||
|
{
|
||||||
|
newBeatmap = new Beatmap { BeatmapInfo = newBeatmapInfo };
|
||||||
|
foreach (var timingPoint in referenceBeatmap.ControlPointInfo.TimingPoints)
|
||||||
newBeatmap.ControlPointInfo.Add(timingPoint.Time, timingPoint.DeepClone());
|
newBeatmap.ControlPointInfo.Add(timingPoint.Time, timingPoint.DeepClone());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newBeatmap = referenceBeatmap.Clone();
|
||||||
|
newBeatmap.BeatmapInfo = newBeatmapInfo;
|
||||||
|
}
|
||||||
|
|
||||||
beatmapModelManager.Save(newBeatmapInfo, newBeatmap);
|
beatmapModelManager.Save(newBeatmapInfo, newBeatmap);
|
||||||
|
|
||||||
workingBeatmapCache.Invalidate(beatmapSetInfo);
|
workingBeatmapCache.Invalidate(targetBeatmapSet);
|
||||||
return GetWorkingBeatmap(newBeatmap.BeatmapInfo);
|
return GetWorkingBeatmap(newBeatmap.BeatmapInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
45
osu.Game/Screens/Edit/CreateNewDifficultyDialog.cs
Normal file
45
osu.Game/Screens/Edit/CreateNewDifficultyDialog.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Overlays.Dialog;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit
|
||||||
|
{
|
||||||
|
public class CreateNewDifficultyDialog : PopupDialog
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Delegate used to create new difficulties.
|
||||||
|
/// A value of <see langword="true"/> in the <c>clearAllObjects</c> parameter
|
||||||
|
/// indicates that the new difficulty should have its hitobjects cleared;
|
||||||
|
/// otherwise, the new difficulty should be an exact copy of an existing one.
|
||||||
|
/// </summary>
|
||||||
|
public delegate void CreateNewDifficulty(bool clearAllObjects);
|
||||||
|
|
||||||
|
public CreateNewDifficultyDialog(CreateNewDifficulty createNewDifficulty)
|
||||||
|
{
|
||||||
|
HeaderText = "Would you like to clear all objects?";
|
||||||
|
|
||||||
|
Icon = FontAwesome.Regular.Clone;
|
||||||
|
|
||||||
|
Buttons = new PopupDialogButton[]
|
||||||
|
{
|
||||||
|
new PopupDialogOkButton
|
||||||
|
{
|
||||||
|
Text = "Yeah, let's start from scratch!",
|
||||||
|
Action = () => createNewDifficulty.Invoke(true)
|
||||||
|
},
|
||||||
|
new PopupDialogCancelButton
|
||||||
|
{
|
||||||
|
Text = "No, create an exact copy of this difficulty",
|
||||||
|
Action = () => createNewDifficulty.Invoke(false)
|
||||||
|
},
|
||||||
|
new PopupDialogCancelButton
|
||||||
|
{
|
||||||
|
Text = "I changed my mind, I want to keep editing this difficulty",
|
||||||
|
Action = () => { }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -841,7 +841,25 @@ namespace osu.Game.Screens.Edit
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void CreateNewDifficulty(RulesetInfo rulesetInfo)
|
protected void CreateNewDifficulty(RulesetInfo rulesetInfo)
|
||||||
=> loader?.ScheduleSwitchToNewDifficulty(editorBeatmap.BeatmapInfo.BeatmapSet, rulesetInfo, GetState());
|
{
|
||||||
|
if (!rulesetInfo.Equals(editorBeatmap.BeatmapInfo.Ruleset))
|
||||||
|
{
|
||||||
|
switchToNewDifficulty(rulesetInfo, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialogOverlay.Push(new CreateNewDifficultyDialog(clearAllObjects => switchToNewDifficulty(rulesetInfo, clearAllObjects)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchToNewDifficulty(RulesetInfo rulesetInfo, bool clearAllObjects)
|
||||||
|
=> loader?.ScheduleSwitchToNewDifficulty(new NewDifficultyCreationParameters
|
||||||
|
{
|
||||||
|
BeatmapSet = editorBeatmap.BeatmapInfo.BeatmapSet,
|
||||||
|
Ruleset = rulesetInfo,
|
||||||
|
ReferenceBeatmap = playableBeatmap,
|
||||||
|
ClearAllObjects = clearAllObjects,
|
||||||
|
EditorState = GetState()
|
||||||
|
});
|
||||||
|
|
||||||
private EditorMenuItem createDifficultySwitchMenu()
|
private EditorMenuItem createDifficultySwitchMenu()
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,6 @@ 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;
|
||||||
@ -80,12 +79,12 @@ namespace osu.Game.Screens.Edit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ScheduleSwitchToNewDifficulty(BeatmapSetInfo beatmapSetInfo, RulesetInfo rulesetInfo, EditorState editorState)
|
public void ScheduleSwitchToNewDifficulty(NewDifficultyCreationParameters creationParameters)
|
||||||
=> scheduleDifficultySwitch(() =>
|
=> scheduleDifficultySwitch(() =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return beatmapManager.CreateNewBlankDifficulty(beatmapSetInfo, rulesetInfo);
|
return beatmapManager.CreateNewBlankDifficulty(creationParameters);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -94,7 +93,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
Logger.Error(ex, ex.Message);
|
Logger.Error(ex, ex.Message);
|
||||||
return Beatmap.Value;
|
return Beatmap.Value;
|
||||||
}
|
}
|
||||||
}, editorState);
|
}, creationParameters.EditorState);
|
||||||
|
|
||||||
public void ScheduleSwitchToExistingDifficulty(BeatmapInfo beatmapInfo, EditorState editorState)
|
public void ScheduleSwitchToExistingDifficulty(BeatmapInfo beatmapInfo, EditorState editorState)
|
||||||
=> scheduleDifficultySwitch(() => beatmapManager.GetWorkingBeatmap(beatmapInfo), editorState);
|
=> scheduleDifficultySwitch(() => beatmapManager.GetWorkingBeatmap(beatmapInfo), editorState);
|
||||||
|
36
osu.Game/Screens/Edit/NewDifficultyCreationParameters.cs
Normal file
36
osu.Game/Screens/Edit/NewDifficultyCreationParameters.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit
|
||||||
|
{
|
||||||
|
public class NewDifficultyCreationParameters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="BeatmapSetInfo"/> that should contain the newly-created difficulty.
|
||||||
|
/// </summary>
|
||||||
|
public BeatmapSetInfo BeatmapSet { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="RulesetInfo"/> that the new difficulty should be playable for.
|
||||||
|
/// </summary>
|
||||||
|
public RulesetInfo Ruleset { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A reference <see cref="IBeatmap"/> upon which the new difficulty should be based.
|
||||||
|
/// </summary>
|
||||||
|
public IBeatmap ReferenceBeatmap { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether all objects should be cleared from the new difficulty.
|
||||||
|
/// </summary>
|
||||||
|
public bool ClearAllObjects { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The saved state of the previous <see cref="Editor"/> which should be restored upon opening the newly-created difficulty.
|
||||||
|
/// </summary>
|
||||||
|
public EditorState EditorState { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -136,7 +136,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
return new TestWorkingBeatmapCache(this, audioManager, resources, storage, defaultBeatmap, host);
|
return new TestWorkingBeatmapCache(this, audioManager, resources, storage, defaultBeatmap, host);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override WorkingBeatmap CreateNewBlankDifficulty(BeatmapSetInfo beatmapSetInfo, RulesetInfo rulesetInfo)
|
public override WorkingBeatmap CreateNewBlankDifficulty(NewDifficultyCreationParameters creationParameters)
|
||||||
{
|
{
|
||||||
// don't actually care about properly creating a difficulty for this context.
|
// don't actually care about properly creating a difficulty for this context.
|
||||||
return TestBeatmap;
|
return TestBeatmap;
|
||||||
|
Loading…
Reference in New Issue
Block a user