1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 10:07:52 +08:00

Make mod selfcontained

This commit is contained in:
sw1tchbl4d3 2022-06-19 15:11:12 +02:00
parent a5bf16e873
commit 98527fec26
10 changed files with 213 additions and 102 deletions

View File

@ -9,10 +9,6 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{
public class TaikoDrumRollJudgement : TaikoJudgement
{
public bool IsBonus = false;
public override HitResult MaxResult => IsBonus ? HitResult.LargeBonus : HitResult.Great;
protected override double HealthIncreaseFor(HitResult result)
{
// Drum rolls can be ignored with no health penalty

View File

@ -9,9 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{
public class TaikoDrumRollTickJudgement : TaikoJudgement
{
public bool IsBonus = false;
public override HitResult MaxResult => IsBonus ? HitResult.LargeBonus : HitResult.SmallTickHit;
public override HitResult MaxResult => HitResult.SmallTickHit;
protected override double HealthIncreaseFor(HitResult result)
{

View File

@ -9,10 +9,6 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{
public class TaikoSwellJudgement : TaikoJudgement
{
public bool IsBonus = false;
public override HitResult MaxResult => IsBonus ? HitResult.LargeBonus : HitResult.Great;
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)

View File

@ -3,15 +3,22 @@
#nullable disable
using System.Linq;
using System.Threading;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Taiko.Mods
{
public class TaikoModClassic : ModClassic, IApplicableToDrawableRuleset<TaikoHitObject>, IUpdatableByPlayfield, IApplicableToHitObject
public class TaikoModClassic : ModClassic, IApplicableToDrawableRuleset<TaikoHitObject>, IUpdatableByPlayfield, IApplicableAfterBeatmapConversion
{
private DrawableTaikoRuleset drawableTaikoRuleset;
@ -19,14 +26,177 @@ namespace osu.Game.Rulesets.Taiko.Mods
{
drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset;
drawableTaikoRuleset.LockPlayfieldAspect.Value = false;
drawableTaikoRuleset.Playfield.RegisterPool<ClassicDrumRoll, ClassicDrawableDrumRoll>(5);
drawableTaikoRuleset.Playfield.RegisterPool<ClassicDrumRollTick, DrawableDrumRollTick>(100);
drawableTaikoRuleset.Playfield.RegisterPool<ClassicSwell, ClassicDrawableSwell>(5);
}
public void ApplyToHitObject(HitObject hitObject)
public void ApplyToBeatmap(IBeatmap beatmap)
{
if (hitObject is DrumRoll drumRoll)
drumRoll.SetBonus(true);
else if (hitObject is Swell swell)
swell.SetBonus(true);
var taikoBeatmap = (TaikoBeatmap)beatmap;
if (taikoBeatmap.HitObjects.Count == 0) return;
var hitObjects = taikoBeatmap.HitObjects.Select(ho =>
{
if (ho is DrumRoll drumRoll)
{
var newDrumRoll = new ClassicDrumRoll(drumRoll);
return newDrumRoll;
}
if (ho is Swell swell)
{
var newSwell = new ClassicSwell(swell);
return newSwell;
}
return ho;
}).ToList();
taikoBeatmap.HitObjects = hitObjects;
}
private class ClassicDrumRoll : DrumRoll
{
public ClassicDrumRoll(DrumRoll original)
{
StartTime = original.StartTime;
Samples = original.Samples;
EndTime = original.EndTime;
Duration = original.Duration;
TickRate = original.TickRate;
RequiredGoodHits = original.RequiredGoodHits;
RequiredGreatHits = original.RequiredGreatHits;
}
public override Judgement CreateJudgement() => new TaikoClassicDrumRollJudgement();
protected override void CreateTicks(CancellationToken cancellationToken)
{
if (TickSpacing == 0)
return;
bool first = true;
for (double t = StartTime; t < EndTime + TickSpacing / 2; t += TickSpacing)
{
cancellationToken.ThrowIfCancellationRequested();
AddNested(new ClassicDrumRollTick
{
FirstTick = first,
TickSpacing = TickSpacing,
StartTime = t,
IsStrong = IsStrong
});
first = false;
}
}
}
private class ClassicDrumRollTick : DrumRollTick
{
public override Judgement CreateJudgement() => new TaikoClassicDrumRollTickJudgement();
}
private class ClassicSwell : Swell
{
public ClassicSwell(Swell original)
{
StartTime = original.StartTime;
Samples = original.Samples;
EndTime = original.EndTime;
Duration = original.Duration;
RequiredHits = original.RequiredHits;
}
public override Judgement CreateJudgement() => new TaikoClassicSwellJudgement();
}
private class TaikoClassicDrumRollJudgement : TaikoDrumRollJudgement
{
public override HitResult MaxResult => HitResult.LargeBonus;
}
private class TaikoClassicDrumRollTickJudgement : TaikoDrumRollTickJudgement
{
public override HitResult MaxResult => HitResult.LargeBonus;
}
private class TaikoClassicSwellJudgement : TaikoSwellJudgement
{
public override HitResult MaxResult => HitResult.LargeBonus;
}
private class ClassicDrawableDrumRoll : DrawableDrumRoll
{
public override bool DisplayResult => false;
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (userTriggered)
return;
if (timeOffset < 0)
return;
ApplyResult(r => r.Type = HitResult.IgnoreMiss);
}
}
private class ClassicDrawableSwell : DrawableSwell
{
public override bool DisplayResult => false;
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (userTriggered)
{
DrawableSwellTick nextTick = null;
foreach (var t in Ticks)
{
if (!t.Result.HasResult)
{
nextTick = t;
break;
}
}
nextTick?.TriggerResult(true);
int numHits = Ticks.Count(r => r.IsHit);
AnimateCompletion(numHits);
if (numHits == HitObject.RequiredHits)
ApplyResult(r => r.Type = HitResult.LargeBonus);
}
else
{
if (timeOffset < 0)
return;
int numHits = 0;
foreach (var tick in Ticks)
{
if (tick.IsHit)
{
numHits++;
continue;
}
if (!tick.Result.HasResult)
tick.TriggerResult(false);
}
ApplyResult(r => r.Type = numHits > HitObject.RequiredHits / 2 ? HitResult.SmallBonus : HitResult.IgnoreMiss);
}
}
}
public void Update(Playfield playfield)

View File

@ -144,14 +144,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (countHit >= HitObject.RequiredGoodHits)
{
ApplyResult(r =>
{
// With the Classic mod, don't award points for a finished drum roll, only for ticks.
if (r.Judgement.MaxResult == HitResult.LargeBonus)
r.Type = HitResult.IgnoreMiss;
else
r.Type = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Ok;
});
ApplyResult(r => r.Type = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Ok);
}
else
ApplyResult(r => r.Type = r.Judgement.MinResult);

View File

@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
/// </summary>
private const double ring_appear_offset = 100;
private readonly Container<DrawableSwellTick> ticks;
protected readonly Container<DrawableSwellTick> Ticks;
private readonly Container bodyContainer;
private readonly CircularContainer targetRing;
private readonly CircularContainer expandingRing;
@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
}
});
AddInternal(ticks = new Container<DrawableSwellTick> { RelativeSizeAxes = Axes.Both });
AddInternal(Ticks = new Container<DrawableSwellTick> { RelativeSizeAxes = Axes.Both });
}
[BackgroundDependencyLoader]
@ -132,6 +132,20 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Origin = Anchor.Centre,
});
protected void AnimateCompletion(int numHits)
{
float completion = (float)numHits / HitObject.RequiredHits;
expandingRing
.FadeTo(expandingRing.Alpha + Math.Clamp(completion / 16, 0.1f, 0.6f), 50)
.Then()
.FadeTo(completion / 8, 2000, Easing.OutQuint);
MainPiece.Drawable.RotateTo((float)(completion * HitObject.Duration / 8), 4000, Easing.OutQuint);
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint);
}
protected override void OnFree()
{
base.OnFree();
@ -148,7 +162,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
switch (hitObject)
{
case DrawableSwellTick tick:
ticks.Add(tick);
Ticks.Add(tick);
break;
}
}
@ -156,7 +170,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override void ClearNestedHitObjects()
{
base.ClearNestedHitObjects();
ticks.Clear(false);
Ticks.Clear(false);
}
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)
@ -176,7 +190,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
DrawableSwellTick nextTick = null;
foreach (var t in ticks)
foreach (var t in Ticks)
{
if (!t.Result.HasResult)
{
@ -187,21 +201,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
nextTick?.TriggerResult(true);
int numHits = ticks.Count(r => r.IsHit);
int numHits = Ticks.Count(r => r.IsHit);
float completion = (float)numHits / HitObject.RequiredHits;
expandingRing
.FadeTo(expandingRing.Alpha + Math.Clamp(completion / 16, 0.1f, 0.6f), 50)
.Then()
.FadeTo(completion / 8, 2000, Easing.OutQuint);
MainPiece.Drawable.RotateTo((float)(completion * HitObject.Duration / 8), 4000, Easing.OutQuint);
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint);
AnimateCompletion(numHits);
if (numHits == HitObject.RequiredHits)
ApplyResult(r => r.Type = r.Judgement.MaxResult);
ApplyResult(r => r.Type = HitResult.Great);
}
else
{
@ -210,7 +215,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
int numHits = 0;
foreach (var tick in ticks)
foreach (var tick in Ticks)
{
if (tick.IsHit)
{
@ -222,14 +227,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
tick.TriggerResult(false);
}
ApplyResult(r =>
{
// With the Classic mod, don't award combo or accuracy for a finished swell.
if (r.Judgement.MaxResult == HitResult.LargeBonus)
r.Type = numHits > HitObject.RequiredHits / 2 ? HitResult.LargeBonus : r.Judgement.MinResult;
else
r.Type = numHits > HitObject.RequiredHits / 2 ? HitResult.Ok : r.Judgement.MinResult;
});
ApplyResult(r => r.Type = numHits > HitObject.RequiredHits / 2 ? HitResult.Ok : r.Judgement.MinResult);
}
}

View File

@ -52,16 +52,11 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// </summary>
public double RequiredGreatHits { get; protected set; }
/// <summary>
/// Defines if drum rolls are affected by the Classic mod, making them bonus only.
/// </summary>
private bool isBonus;
/// <summary>
/// The length (in milliseconds) between ticks of this drumroll.
/// <para>Half of this value is the hit window of the ticks.</para>
/// </summary>
private double tickSpacing = 100;
protected double TickSpacing = 100;
private float overallDifficulty = BeatmapDifficulty.DEFAULT_DIFFICULTY;
@ -74,13 +69,13 @@ namespace osu.Game.Rulesets.Taiko.Objects
double scoringDistance = base_distance * difficulty.SliderMultiplier * DifficultyControlPoint.SliderVelocity;
Velocity = scoringDistance / timingPoint.BeatLength;
tickSpacing = timingPoint.BeatLength / TickRate;
TickSpacing = timingPoint.BeatLength / TickRate;
overallDifficulty = difficulty.OverallDifficulty;
}
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
{
createTicks(cancellationToken);
CreateTicks(cancellationToken);
RequiredGoodHits = NestedHitObjects.Count * Math.Min(0.15, 0.05 + 0.10 / 6 * overallDifficulty);
RequiredGreatHits = NestedHitObjects.Count * Math.Min(0.30, 0.10 + 0.20 / 6 * overallDifficulty);
@ -88,21 +83,21 @@ namespace osu.Game.Rulesets.Taiko.Objects
base.CreateNestedHitObjects(cancellationToken);
}
private void createTicks(CancellationToken cancellationToken)
protected virtual void CreateTicks(CancellationToken cancellationToken)
{
if (tickSpacing == 0)
if (TickSpacing == 0)
return;
bool first = true;
for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing)
for (double t = StartTime; t < EndTime + TickSpacing / 2; t += TickSpacing)
{
cancellationToken.ThrowIfCancellationRequested();
AddNested(new DrumRollTick
{
FirstTick = first,
TickSpacing = tickSpacing,
TickSpacing = TickSpacing,
StartTime = t,
IsStrong = IsStrong
});
@ -111,18 +106,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
}
}
public void SetBonus(bool bonus)
{
isBonus = bonus;
foreach (HitObject hitObject in NestedHitObjects)
{
if (hitObject is DrumRollTick drumRollTick)
drumRollTick.IsBonus = bonus;
}
}
public override Judgement CreateJudgement() => new TaikoDrumRollJudgement { IsBonus = isBonus };
public override Judgement CreateJudgement() => new TaikoDrumRollJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;

View File

@ -27,12 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// </summary>
public double HitWindow => TickSpacing / 2;
/// <summary>
/// Defines if ticks are affected by the Classic mod, making them bonus only.
/// </summary>
public bool IsBonus = false;
public override Judgement CreateJudgement() => new TaikoDrumRollTickJudgement { IsBonus = IsBonus };
public override Judgement CreateJudgement() => new TaikoDrumRollTickJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;

View File

@ -26,16 +26,6 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// </summary>
public int RequiredHits = 10;
/// <summary>
/// Defines if swells are affected by the Classic mod, making them bonus only.
/// </summary>
private bool isBonus;
public void SetBonus(bool bonus)
{
isBonus = bonus;
}
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
{
base.CreateNestedHitObjects(cancellationToken);
@ -47,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
}
}
public override Judgement CreateJudgement() => new TaikoSwellJudgement { IsBonus = isBonus };
public override Judgement CreateJudgement() => new TaikoSwellJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}

View File

@ -295,15 +295,6 @@ namespace osu.Game.Rulesets.Taiko.UI
break;
default:
// Don't draw judgement results for bonus sliderticks with the Classic mod.
switch (result.Type)
{
case HitResult.IgnoreHit:
case HitResult.IgnoreMiss:
case HitResult.LargeBonus:
return;
}
judgementContainer.Add(judgementPools[result.Type].Get(j =>
{
j.Apply(result, judgedObject);