mirror of
https://github.com/osukey/osukey.git
synced 2025-08-07 16:43:52 +09:00
Merge pull request #11793 from peppy/playlists-freemod-support
Add "free mod" user mod selection support to the playlists system
This commit is contained in:
@ -8,11 +8,14 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
|
||||||
@ -26,6 +29,14 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
|
|
||||||
public override bool DisallowExternalBeatmapRulesetChanges => true;
|
public override bool DisallowExternalBeatmapRulesetChanges => true;
|
||||||
|
|
||||||
|
private readonly ModSelectOverlay userModsSelectOverlay;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A container that provides controls for selection of user mods.
|
||||||
|
/// This will be shown/hidden automatically when applicable.
|
||||||
|
/// </summary>
|
||||||
|
protected Drawable UserModsSection;
|
||||||
|
|
||||||
private Sample sampleStart;
|
private Sample sampleStart;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -51,9 +62,26 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
|
|
||||||
protected RoomSubScreen()
|
protected RoomSubScreen()
|
||||||
{
|
{
|
||||||
AddInternal(BeatmapAvailablilityTracker = new OnlinePlayBeatmapAvailablilityTracker
|
AddRangeInternal(new Drawable[]
|
||||||
|
{
|
||||||
|
BeatmapAvailablilityTracker = new OnlinePlayBeatmapAvailablilityTracker
|
||||||
{
|
{
|
||||||
SelectedItem = { BindTarget = SelectedItem }
|
SelectedItem = { BindTarget = SelectedItem }
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Depth = float.MinValue,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Height = 0.5f,
|
||||||
|
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING },
|
||||||
|
Child = userModsSelectOverlay = new UserModSelectOverlay
|
||||||
|
{
|
||||||
|
SelectedMods = { BindTarget = UserMods },
|
||||||
|
IsValidMod = _ => false
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +106,19 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
UserMods.BindValueChanged(_ => Scheduler.AddOnce(UpdateMods));
|
UserMods.BindValueChanged(_ => Scheduler.AddOnce(UpdateMods));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool OnBackButton()
|
||||||
|
{
|
||||||
|
if (userModsSelectOverlay.State.Value == Visibility.Visible)
|
||||||
|
{
|
||||||
|
userModsSelectOverlay.Hide();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnBackButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ShowUserModSelect() => userModsSelectOverlay.Show();
|
||||||
|
|
||||||
public override void OnEntering(IScreen last)
|
public override void OnEntering(IScreen last)
|
||||||
{
|
{
|
||||||
base.OnEntering(last);
|
base.OnEntering(last);
|
||||||
@ -117,17 +158,31 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
{
|
{
|
||||||
updateWorkingBeatmap();
|
updateWorkingBeatmap();
|
||||||
|
|
||||||
if (SelectedItem.Value == null)
|
var selected = SelectedItem.Value;
|
||||||
|
|
||||||
|
if (selected == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Remove any user mods that are no longer allowed.
|
// Remove any user mods that are no longer allowed.
|
||||||
UserMods.Value = UserMods.Value
|
UserMods.Value = UserMods.Value
|
||||||
.Where(m => SelectedItem.Value.AllowedMods.Any(a => m.GetType() == a.GetType()))
|
.Where(m => selected.AllowedMods.Any(a => m.GetType() == a.GetType()))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
UpdateMods();
|
UpdateMods();
|
||||||
|
|
||||||
Ruleset.Value = SelectedItem.Value.Ruleset.Value;
|
Ruleset.Value = selected.Ruleset.Value;
|
||||||
|
|
||||||
|
if (!selected.AllowedMods.Any())
|
||||||
|
{
|
||||||
|
UserModsSection?.Hide();
|
||||||
|
userModsSelectOverlay.Hide();
|
||||||
|
userModsSelectOverlay.IsValidMod = _ => false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UserModsSection?.Show();
|
||||||
|
userModsSelectOverlay.IsValidMod = m => selected.AllowedMods.Any(a => a.GetType() == m.GetType());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet) => Schedule(updateWorkingBeatmap);
|
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet) => Schedule(updateWorkingBeatmap);
|
||||||
@ -182,5 +237,9 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
if (track != null)
|
if (track != null)
|
||||||
track.Looping = false;
|
track.Looping = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class UserModSelectOverlay : LocalPlayerModSelectOverlay
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ using osu.Framework.Threading;
|
|||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Overlays.Mods;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Screens.OnlinePlay.Components;
|
using osu.Game.Screens.OnlinePlay.Components;
|
||||||
using osu.Game.Screens.OnlinePlay.Match;
|
using osu.Game.Screens.OnlinePlay.Match;
|
||||||
@ -42,9 +41,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private OngoingOperationTracker ongoingOperationTracker { get; set; }
|
private OngoingOperationTracker ongoingOperationTracker { get; set; }
|
||||||
|
|
||||||
private ModSelectOverlay userModsSelectOverlay;
|
|
||||||
private MultiplayerMatchSettingsOverlay settingsOverlay;
|
private MultiplayerMatchSettingsOverlay settingsOverlay;
|
||||||
private Drawable userModsSection;
|
|
||||||
|
|
||||||
private readonly IBindable<bool> isConnected = new Bindable<bool>();
|
private readonly IBindable<bool> isConnected = new Bindable<bool>();
|
||||||
|
|
||||||
@ -154,7 +151,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
new BeatmapSelectionControl { RelativeSizeAxes = Axes.X }
|
new BeatmapSelectionControl { RelativeSizeAxes = Axes.X }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
userModsSection = new FillFlowContainer
|
UserModsSection = new FillFlowContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
@ -175,7 +172,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Width = 90,
|
Width = 90,
|
||||||
Text = "Select",
|
Text = "Select",
|
||||||
Action = () => userModsSelectOverlay.Show()
|
Action = ShowUserModSelect,
|
||||||
},
|
},
|
||||||
new ModDisplay
|
new ModDisplay
|
||||||
{
|
{
|
||||||
@ -230,19 +227,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
new Dimension(GridSizeMode.AutoSize),
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Height = 0.5f,
|
|
||||||
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING },
|
|
||||||
Child = userModsSelectOverlay = new UserModSelectOverlay
|
|
||||||
{
|
|
||||||
SelectedMods = { BindTarget = UserMods },
|
|
||||||
IsValidMod = _ => false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
settingsOverlay = new MultiplayerMatchSettingsOverlay
|
settingsOverlay = new MultiplayerMatchSettingsOverlay
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -269,7 +253,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
SelectedItem.BindTo(client.CurrentMatchPlayingItem);
|
SelectedItem.BindTo(client.CurrentMatchPlayingItem);
|
||||||
SelectedItem.BindValueChanged(onSelectedItemChanged, true);
|
|
||||||
|
|
||||||
BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true);
|
BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true);
|
||||||
UserMods.BindValueChanged(onUserModsChanged);
|
UserMods.BindValueChanged(onUserModsChanged);
|
||||||
@ -285,24 +268,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onSelectedItemChanged(ValueChangedEvent<PlaylistItem> item)
|
|
||||||
{
|
|
||||||
if (client?.LocalUser == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (item.NewValue?.AllowedMods.Any() != true)
|
|
||||||
{
|
|
||||||
userModsSection.Hide();
|
|
||||||
userModsSelectOverlay.Hide();
|
|
||||||
userModsSelectOverlay.IsValidMod = _ => false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
userModsSection.Show();
|
|
||||||
userModsSelectOverlay.IsValidMod = m => item.NewValue.AllowedMods.Any(a => a.GetType() == m.GetType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void UpdateMods()
|
protected override void UpdateMods()
|
||||||
{
|
{
|
||||||
if (SelectedItem.Value == null || client.LocalUser == null)
|
if (SelectedItem.Value == null || client.LocalUser == null)
|
||||||
@ -322,12 +287,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userModsSelectOverlay.State.Value == Visibility.Visible)
|
|
||||||
{
|
|
||||||
userModsSelectOverlay.Hide();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.OnBackButton();
|
return base.OnBackButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,9 +394,5 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
|
|
||||||
modSettingChangeTracker?.Dispose();
|
modSettingChangeTracker?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UserModSelectOverlay : LocalPlayerModSelectOverlay
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,11 +32,12 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
[Resolved(typeof(Room), nameof(Room.Playlist))]
|
[Resolved(typeof(Room), nameof(Room.Playlist))]
|
||||||
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
||||||
|
|
||||||
|
protected readonly Bindable<IReadOnlyList<Mod>> FreeMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||||
|
|
||||||
[CanBeNull]
|
[CanBeNull]
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private IBindable<PlaylistItem> selectedItem { get; set; }
|
private IBindable<PlaylistItem> selectedItem { get; set; }
|
||||||
|
|
||||||
private readonly Bindable<IReadOnlyList<Mod>> freeMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
|
||||||
private readonly FreeModSelectOverlay freeModSelectOverlay;
|
private readonly FreeModSelectOverlay freeModSelectOverlay;
|
||||||
|
|
||||||
private WorkingBeatmap initialBeatmap;
|
private WorkingBeatmap initialBeatmap;
|
||||||
@ -50,7 +51,7 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
|
|
||||||
freeModSelectOverlay = new FreeModSelectOverlay
|
freeModSelectOverlay = new FreeModSelectOverlay
|
||||||
{
|
{
|
||||||
SelectedMods = { BindTarget = freeMods },
|
SelectedMods = { BindTarget = FreeMods },
|
||||||
IsValidMod = IsValidFreeMod,
|
IsValidMod = IsValidFreeMod,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -72,14 +73,14 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
// At this point, Mods contains both the required and allowed mods. For selection purposes, it should only contain the required mods.
|
// At this point, Mods contains both the required and allowed mods. For selection purposes, it should only contain the required mods.
|
||||||
// Similarly, freeMods is currently empty but should only contain the allowed mods.
|
// Similarly, freeMods is currently empty but should only contain the allowed mods.
|
||||||
Mods.Value = selectedItem?.Value?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
Mods.Value = selectedItem?.Value?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
||||||
freeMods.Value = selectedItem?.Value?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
FreeMods.Value = selectedItem?.Value?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
||||||
|
|
||||||
Ruleset.BindValueChanged(onRulesetChanged);
|
Ruleset.BindValueChanged(onRulesetChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> ruleset)
|
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> ruleset)
|
||||||
{
|
{
|
||||||
freeMods.Value = Array.Empty<Mod>();
|
FreeMods.Value = Array.Empty<Mod>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected sealed override bool OnStart()
|
protected sealed override bool OnStart()
|
||||||
@ -95,7 +96,7 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy()));
|
item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy()));
|
||||||
|
|
||||||
item.AllowedMods.Clear();
|
item.AllowedMods.Clear();
|
||||||
item.AllowedMods.AddRange(freeMods.Value.Select(m => m.CreateCopy()));
|
item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.CreateCopy()));
|
||||||
|
|
||||||
SelectItem(item);
|
SelectItem(item);
|
||||||
return true;
|
return true;
|
||||||
@ -138,7 +139,7 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
protected override IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons()
|
protected override IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons()
|
||||||
{
|
{
|
||||||
var buttons = base.CreateFooterButtons().ToList();
|
var buttons = base.CreateFooterButtons().ToList();
|
||||||
buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods { Current = freeMods }, freeModSelectOverlay));
|
buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods { Current = FreeMods }, freeModSelectOverlay));
|
||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,9 @@ using osu.Game.Online.Rooms;
|
|||||||
using osu.Game.Screens.OnlinePlay.Components;
|
using osu.Game.Screens.OnlinePlay.Components;
|
||||||
using osu.Game.Screens.OnlinePlay.Match;
|
using osu.Game.Screens.OnlinePlay.Match;
|
||||||
using osu.Game.Screens.OnlinePlay.Match.Components;
|
using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
using osuTK;
|
||||||
using Footer = osu.Game.Screens.OnlinePlay.Match.Components.Footer;
|
using Footer = osu.Game.Screens.OnlinePlay.Match.Components.Footer;
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay.Playlists
|
namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||||
@ -143,13 +145,55 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Content = new[]
|
Content = new[]
|
||||||
{
|
{
|
||||||
new Drawable[] { new OverlinedHeader("Leaderboard"), },
|
new[]
|
||||||
|
{
|
||||||
|
UserModsSection = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Margin = new MarginPadding { Bottom = 10 },
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new OverlinedHeader("Extra mods"),
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(10, 0),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new PurpleTriangleButton
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Width = 90,
|
||||||
|
Text = "Select",
|
||||||
|
Action = ShowUserModSelect,
|
||||||
|
},
|
||||||
|
new ModDisplay
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
DisplayUnrankedText = false,
|
||||||
|
Current = UserMods,
|
||||||
|
Scale = new Vector2(0.8f),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new OverlinedHeader("Leaderboard")
|
||||||
|
},
|
||||||
new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, },
|
new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, },
|
||||||
new Drawable[] { new OverlinedHeader("Chat"), },
|
new Drawable[] { new OverlinedHeader("Chat"), },
|
||||||
new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } }
|
new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } }
|
||||||
},
|
},
|
||||||
RowDimensions = new[]
|
RowDimensions = new[]
|
||||||
{
|
{
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
new Dimension(GridSizeMode.AutoSize),
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
new Dimension(),
|
new Dimension(),
|
||||||
new Dimension(GridSizeMode.AutoSize),
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
@ -56,6 +56,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
|||||||
|
|
||||||
item.RequiredMods.Clear();
|
item.RequiredMods.Clear();
|
||||||
item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy()));
|
item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy()));
|
||||||
|
|
||||||
|
item.AllowedMods.Clear();
|
||||||
|
item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.CreateCopy()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user