Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define the interface for mod to modify the stage. #2310

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions osu.Game.Rulesets.Karaoke/Mods/IApplicableToStage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) andy840119 <[email protected]>. Licensed under the GPL Licence.
// See the LICENCE file in the repository root for full licence text.

using osu.Game.Rulesets.Karaoke.Stages.Infos;
using osu.Game.Rulesets.Mods;

namespace osu.Game.Rulesets.Karaoke.Mods;

public interface IApplicableToStage : IApplicableMod
{
bool CanApply(StageInfo stageInfo);
}
12 changes: 12 additions & 0 deletions osu.Game.Rulesets.Karaoke/Mods/IApplicableToStageElement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) andy840119 <[email protected]>. Licensed under the GPL Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Collections.Generic;
using osu.Game.Rulesets.Karaoke.Stages;

namespace osu.Game.Rulesets.Karaoke.Mods;

public interface IApplicableToStageElement : IApplicableToStage
{
IEnumerable<IStageElement> PostProcess(IEnumerable<IStageElement> stageElements);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) andy840119 <[email protected]>. Licensed under the GPL Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Collections.Generic;
using osu.Game.Rulesets.Karaoke.Stages.Commands;
using osu.Game.Rulesets.Objects;

namespace osu.Game.Rulesets.Karaoke.Mods;

public interface IApplicableToStageHitObjectCommand : IApplicableToStage
{
IEnumerable<IStageCommand> PostProcessInitialCommands(HitObject hitObject, IEnumerable<IStageCommand> commands);

IEnumerable<IStageCommand> PostProcessStartTimeStateCommands(HitObject hitObject, IEnumerable<IStageCommand> commands);

IEnumerable<IStageCommand> PostProcessHitStateCommands(HitObject hitObject, IEnumerable<IStageCommand> commands);
}
18 changes: 18 additions & 0 deletions osu.Game.Rulesets.Karaoke/Mods/IApplicableToStageInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) andy840119 <[email protected]>. Licensed under the GPL Licence.
// See the LICENCE file in the repository root for full licence text.

using osu.Game.Rulesets.Karaoke.Beatmaps;
using osu.Game.Rulesets.Karaoke.Stages.Infos;

namespace osu.Game.Rulesets.Karaoke.Mods;

/// <summary>
/// An interface for mods that prefer to use the type of <see cref="StageInfo"/>.
/// Also, it can override the parameter of <see cref="StageInfo"/>.
/// </summary>
public interface IApplicableToStageInfo : IApplicableToStage
{
StageInfo? CreateDefaultStageInfo(KaraokeBeatmap beatmap);

void ApplyToStageInfo(StageInfo stageInfo);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) andy840119 <[email protected]>. Licensed under the GPL Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Collections.Generic;
using osu.Game.Rulesets.Karaoke.Stages.Commands;
using osu.Game.Rulesets.UI;

namespace osu.Game.Rulesets.Karaoke.Mods;

public interface IApplicableToStagePlayfieldCommand : IApplicableToStage
{
IEnumerable<IStageCommand> PostProcessCommands(Playfield playfield, IEnumerable<IStageCommand> commands);
}
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Karaoke/Mods/KaraokeModClassicStage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ protected override ClassicStageInfo CreateStageInfo(KaraokeBeatmap beatmap)
return (ClassicStageInfo)generator.Generate(beatmap);
}

protected override void ApplyToCurrentStageInfo(ClassicStageInfo stageInfo)
protected override void ApplyToStageInfo(ClassicStageInfo stageInfo)
{
// todo: adjust stage by config.
}
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Karaoke/Mods/KaraokeModPreviewStage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ protected override PreviewStageInfo CreateStageInfo(KaraokeBeatmap beatmap)
return (PreviewStageInfo)generator.Generate(beatmap);
}

protected override void ApplyToCurrentStageInfo(PreviewStageInfo stageInfo)
protected override void ApplyToStageInfo(PreviewStageInfo stageInfo)
{
// todo: adjust stage by config.
}
Expand Down
49 changes: 18 additions & 31 deletions osu.Game.Rulesets.Karaoke/Mods/ModStage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,43 @@

using System;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Karaoke.Beatmaps;
using osu.Game.Rulesets.Karaoke.Stages.Infos;
using osu.Game.Rulesets.Mods;

namespace osu.Game.Rulesets.Karaoke.Mods;

public abstract class ModStage<TStageInfo> : ModStage
public abstract class ModStage<TStageInfo> : Mod, IApplicableToStageInfo
where TStageInfo : StageInfo
{
public override bool IsStageInfoMatched(StageInfo stageInfo)
public sealed override ModType Type => ModType.Conversion;

/// <summary>
/// Change the stage type should not affect the score.
/// </summary>
public override double ScoreMultiplier => 1;

public override Type[] IncompatibleMods => new[] { typeof(ModStage<TStageInfo>) }.Except(new[] { GetType() }).ToArray();

public bool CanApply(StageInfo stageInfo)
{
return stageInfo is TStageInfo;
return stageInfo is TStageInfo;
}

public override StageInfo GenerateDefaultStageInfo(IBeatmap beatmap)
public StageInfo? CreateDefaultStageInfo(KaraokeBeatmap beatmap)
{
if (beatmap is not KaraokeBeatmap karaokeBeatmap)
throw new InvalidOperationException();

return CreateStageInfo(karaokeBeatmap) ?? throw new InvalidOperationException();
return CreateStageInfo(beatmap);
}

public override void ApplyToStageInfo(StageInfo stageInfo)
public void ApplyToStageInfo(StageInfo stageInfo)
{
if (stageInfo is not TStageInfo tStageInfo)
throw new InvalidOperationException();
throw new ArgumentException($"The stage info is not matched with {GetType().Name}");

ApplyToCurrentStageInfo(tStageInfo);
ApplyToStageInfo(tStageInfo);
}

protected abstract void ApplyToCurrentStageInfo(TStageInfo stageInfo);

protected abstract TStageInfo? CreateStageInfo(KaraokeBeatmap beatmap);
}

public abstract class ModStage : Mod, IApplicableMod
{
public sealed override ModType Type => ModType.Conversion;

/// <summary>
/// Change the stage type should not affect the score.
/// </summary>
public override double ScoreMultiplier => 1;

public override Type[] IncompatibleMods => new[] { typeof(ModStage) }.Except(new[] { GetType() }).ToArray();

public abstract bool IsStageInfoMatched(StageInfo stageInfo);

public abstract StageInfo GenerateDefaultStageInfo(IBeatmap beatmap);

public abstract void ApplyToStageInfo(StageInfo stageInfo);
protected abstract void ApplyToStageInfo(TStageInfo stageInfo);
}
32 changes: 28 additions & 4 deletions osu.Game.Rulesets.Karaoke/Stages/Drawables/DrawableStage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Karaoke.Beatmaps;
using osu.Game.Rulesets.Karaoke.Edit.Generator.Stages.Preview;
using osu.Game.Rulesets.Karaoke.Mods;
using osu.Game.Rulesets.Karaoke.Stages.Infos;
using osu.Game.Rulesets.Karaoke.Stages.Infos.Preview;
Expand Down Expand Up @@ -77,16 +78,39 @@ private static StageInfo getStageInfo(IReadOnlyList<Mod> mods, KaraokeBeatmap be
// todo: get all available stages from resource provider.
var availableStageInfos = Array.Empty<StageInfo>();

var stageMod = mods.OfType<ModStage>().SingleOrDefault();
// Get list of matched mods.
// Return the first stage info if no stage mod is found.
var stageMod = mods.OfType<IApplicableToStageInfo>().SingleOrDefault();
if (stageMod == null)
return availableStageInfos.FirstOrDefault() ?? new PreviewStageInfo();
return availableStageInfos.FirstOrDefault() ?? createDefaultStageInfo(beatmap);

var matchedStageInfo = availableStageInfos.FirstOrDefault(x => stageMod.IsStageInfoMatched(x));
// If user select a stage mod, means user want to use the specific type of stage.
// We should find the matched stage info from the available stage infos.
var matchedStageInfo = availableStageInfos.FirstOrDefault(x => stageMod.CanApply(x));

// If the matched stage info is not found, then trying to create a default one.
if (matchedStageInfo == null)
matchedStageInfo = stageMod.GenerateDefaultStageInfo(beatmap);
{
// Note that not every stage mod can create the default stage info.
// If not possible to create, then use the default one and not override the value in the stage info.
var newStageInfo = stageMod.CreateDefaultStageInfo(beatmap);
if (newStageInfo == null)
{
return createDefaultStageInfo(beatmap);
}

matchedStageInfo = newStageInfo;
}

stageMod.ApplyToStageInfo(matchedStageInfo);
return matchedStageInfo;
}

private static StageInfo createDefaultStageInfo(KaraokeBeatmap beatmap)
{
var config = new PreviewStageInfoGeneratorConfig();
var generator = new PreviewStageInfoGenerator(config);

return (PreviewStageInfo)generator.Generate(beatmap);
}
}
22 changes: 20 additions & 2 deletions osu.Game.Rulesets.Karaoke/Stages/Drawables/StageElementRunner.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
// Copyright (c) andy840119 <[email protected]>. Licensed under the GPL Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Karaoke.Mods;
using osu.Game.Rulesets.Karaoke.Stages.Infos;
using osu.Game.Rulesets.Mods;

Expand All @@ -11,11 +14,13 @@ namespace osu.Game.Rulesets.Karaoke.Stages.Drawables;
public class StageElementRunner : StageRunner, IStageElementRunner
{
private IStageElementProvider? elementProvider;
private IList<IApplicableToStageElement>? stageMods;
private Container? elementContainer;

public override void OnStageInfoChanged(StageInfo stageInfo, bool scorable, IReadOnlyList<Mod> mods)
{
elementProvider = stageInfo.CreateStageElementProvider(scorable);
stageMods = mods.OfType<IApplicableToStageElement>().Where(x => x.CanApply(stageInfo)).ToList();
applyTransforms();
}

Expand All @@ -32,12 +37,25 @@ public void UpdateStageElements(Container container)

private void applyTransforms()
{
if (elementProvider == null || elementContainer == null)
if (elementContainer == null)
return;

elementContainer.Clear();

foreach (var element in elementProvider.GetElements())
foreach (var element in getCommand())
elementContainer.Add(element.CreateDrawable());
}

private IEnumerable<IStageElement> getCommand()
{
if (elementProvider == null)
return Array.Empty<IStageElement>();

var commands = elementProvider.GetElements();

if (stageMods == null)
return commands;

return stageMods.Aggregate(commands, (current, mod) => mod.PostProcess(current));
}
}
18 changes: 18 additions & 0 deletions osu.Game.Rulesets.Karaoke/Stages/Drawables/StageHitObjectRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Rulesets.Karaoke.Mods;
using osu.Game.Rulesets.Karaoke.Objects;
using osu.Game.Rulesets.Karaoke.Stages.Commands;
using osu.Game.Rulesets.Karaoke.Stages.Infos;
Expand All @@ -18,10 +19,13 @@ public class StageHitObjectRunner : StageRunner, IStageHitObjectRunner
public event Action? OnCommandUpdated;

private IHitObjectCommandProvider commandProvider = null!;
private IList<IApplicableToStageHitObjectCommand> stageMods = null!;

public override void OnStageInfoChanged(StageInfo stageInfo, bool scorable, IReadOnlyList<Mod> mods)
{
commandProvider = stageInfo.CreateHitObjectCommandProvider<Lyric>()!;
stageMods = mods.OfType<IApplicableToStageHitObjectCommand>().Where(x => x.CanApply(stageInfo)).ToList();

OnCommandUpdated?.Invoke();
}

Expand All @@ -48,13 +52,17 @@ public double GetEndTimeOffset(HitObject hitObject)
public void UpdateInitialTransforms(DrawableHitObject drawableHitObject)
{
var commands = commandProvider.GetInitialCommands(drawableHitObject.HitObject);

commands = postProcessCommand(drawableHitObject.HitObject, commands, x => x.PostProcessInitialCommands);
applyTransforms(drawableHitObject, commands);
}

public void UpdateStartTimeStateTransforms(DrawableHitObject drawableHitObject)
{
var commands = commandProvider.GetStartTimeStateCommands(drawableHitObject.HitObject);
double startTimeOffset = -commandProvider.GetStartTimeOffset(drawableHitObject.HitObject);

commands = postProcessCommand(drawableHitObject.HitObject, commands, x => x.PostProcessStartTimeStateCommands);
applyTransforms(drawableHitObject, commands, startTimeOffset);
}

Expand All @@ -64,9 +72,19 @@ public void UpdateHitStateTransforms(DrawableHitObject drawableHitObject, ArmedS
return;

var commands = commandProvider.GetHitStateCommands(drawableHitObject.HitObject, state);

commands = postProcessCommand(drawableHitObject.HitObject, commands, x => x.PostProcessHitStateCommands);
applyTransforms(drawableHitObject, commands);
}

private IEnumerable<IStageCommand> postProcessCommand(
HitObject hitObject,
IEnumerable<IStageCommand> commands,
Func<IApplicableToStageHitObjectCommand, Func<HitObject, IEnumerable<IStageCommand>, IEnumerable<IStageCommand>>> postProcess)
{
return stageMods.Aggregate(commands, (current, mod) => postProcess(mod).Invoke(hitObject, current));
}

private static void applyTransforms<TDrawable>(TDrawable drawable, IEnumerable<IStageCommand> commands, double offset = 0)
where TDrawable : DrawableHitObject
{
Expand Down
26 changes: 22 additions & 4 deletions osu.Game.Rulesets.Karaoke/Stages/Drawables/StagePlayfieldRunner.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Copyright (c) andy840119 <[email protected]>. Licensed under the GPL Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Rulesets.Karaoke.Mods;
using osu.Game.Rulesets.Karaoke.Stages.Commands;
using osu.Game.Rulesets.Karaoke.Stages.Infos;
using osu.Game.Rulesets.Karaoke.UI;
Expand All @@ -14,11 +16,13 @@ namespace osu.Game.Rulesets.Karaoke.Stages.Drawables;
public class StagePlayfieldRunner : StageRunner, IStagePlayfieldRunner
{
private IPlayfieldCommandProvider? commandProvider;
private IList<IApplicableToStagePlayfieldCommand>? stageMods;
private KaraokePlayfield? karaokePlayfield;

public override void OnStageInfoChanged(StageInfo stageInfo, bool scorable, IReadOnlyList<Mod> mods)
{
commandProvider = stageInfo.CreatePlayfieldCommandProvider(scorable);
stageMods = mods.OfType<IApplicableToStagePlayfieldCommand>().Where(x => x.CanApply(stageInfo)).ToList();
applyTransforms();
}

Expand All @@ -36,15 +40,29 @@ public void UpdatePlayfieldTransforms(KaraokePlayfield playfield)

private void applyTransforms()
{
if (commandProvider == null || karaokePlayfield == null)
if (karaokePlayfield == null)
return;

var lyricPlayfield = karaokePlayfield.LyricPlayfield;
var notePlayfield = karaokePlayfield.NotePlayfield;

applyTransforms(karaokePlayfield, commandProvider.GetCommands(karaokePlayfield));
applyTransforms(lyricPlayfield, commandProvider.GetCommands(lyricPlayfield));
applyTransforms(notePlayfield, commandProvider.GetCommands(notePlayfield));
applyTransforms(karaokePlayfield, getCommand(karaokePlayfield));
applyTransforms(lyricPlayfield, getCommand(lyricPlayfield));
applyTransforms(notePlayfield, getCommand(notePlayfield));
}

private IEnumerable<IStageCommand> getCommand(
Playfield playfield)
{
if (commandProvider == null)
return Array.Empty<IStageCommand>();

var commands = commandProvider.GetCommands(playfield);

if (stageMods == null)
return commands;

return stageMods.Aggregate(commands, (current, mod) => mod.PostProcessCommands(playfield, current));
}

private static void applyTransforms<TDrawable>(TDrawable drawable, IEnumerable<IStageCommand> commands)
Expand Down
Loading