Skip to content

Commit

Permalink
Merge pull request #2310 from andy840119/implement-stage-interface-fo…
Browse files Browse the repository at this point in the history
…r-mod

Define the interface for mod to modify the stage.
  • Loading branch information
andy840119 authored Dec 7, 2024
2 parents dd5527b + a994a7c commit e6c0f71
Show file tree
Hide file tree
Showing 12 changed files with 180 additions and 43 deletions.
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

0 comments on commit e6c0f71

Please sign in to comment.