mirror of
https://github.com/ppy/osu.git
synced 2025-01-13 12:53:11 +08:00
Merge remote-tracking branch 'upstream/master' into add-ruleset-legacy-skin
This commit is contained in:
commit
dc1046bf0c
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
{
|
{
|
||||||
public class CatchRuleset : Ruleset
|
public class CatchRuleset : Ruleset
|
||||||
{
|
{
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableCatchRuleset(this, beatmap, mods);
|
public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableCatchRuleset(this, beatmap, mods);
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
|
||||||
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap);
|
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap);
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
|
|
||||||
protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt;
|
protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt;
|
||||||
|
|
||||||
protected override void UpdateInitialTransforms() => this.FadeIn(200);
|
protected override void UpdateInitialTransforms() => this.FadeInFromZero(200);
|
||||||
|
|
||||||
protected override void UpdateStateTransforms(ArmedState state)
|
protected override void UpdateStateTransforms(ArmedState state)
|
||||||
{
|
{
|
||||||
|
@ -25,11 +25,11 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
protected override bool UserScrollSpeedAdjustment => false;
|
protected override bool UserScrollSpeedAdjustment => false;
|
||||||
|
|
||||||
public DrawableCatchRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableCatchRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
Direction.Value = ScrollingDirection.Down;
|
Direction.Value = ScrollingDirection.Down;
|
||||||
TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
|
TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
|
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
{
|
{
|
||||||
public new IScrollingInfo ScrollingInfo => base.ScrollingInfo;
|
public new IScrollingInfo ScrollingInfo => base.ScrollingInfo;
|
||||||
|
|
||||||
public DrawableManiaEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableManiaEditRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
[Cached(Type = typeof(IManiaHitObjectComposer))]
|
[Cached(Type = typeof(IManiaHitObjectComposer))]
|
||||||
public class ManiaHitObjectComposer : HitObjectComposer<ManiaHitObject>, IManiaHitObjectComposer
|
public class ManiaHitObjectComposer : HitObjectComposer<ManiaHitObject>, IManiaHitObjectComposer
|
||||||
{
|
{
|
||||||
protected new DrawableManiaEditRuleset DrawableRuleset { get; private set; }
|
private DrawableManiaEditRuleset drawableRuleset;
|
||||||
|
|
||||||
public ManiaHitObjectComposer(Ruleset ruleset)
|
public ManiaHitObjectComposer(Ruleset ruleset)
|
||||||
: base(ruleset)
|
: base(ruleset)
|
||||||
@ -33,23 +33,23 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="screenSpacePosition">The screen-space position.</param>
|
/// <param name="screenSpacePosition">The screen-space position.</param>
|
||||||
/// <returns>The column which intersects with <paramref name="screenSpacePosition"/>.</returns>
|
/// <returns>The column which intersects with <paramref name="screenSpacePosition"/>.</returns>
|
||||||
public Column ColumnAt(Vector2 screenSpacePosition) => DrawableRuleset.GetColumnByPosition(screenSpacePosition);
|
public Column ColumnAt(Vector2 screenSpacePosition) => drawableRuleset.GetColumnByPosition(screenSpacePosition);
|
||||||
|
|
||||||
private DependencyContainer dependencies;
|
private DependencyContainer dependencies;
|
||||||
|
|
||||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
=> dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
=> dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||||
|
|
||||||
public int TotalColumns => ((ManiaPlayfield)DrawableRuleset.Playfield).TotalColumns;
|
public int TotalColumns => ((ManiaPlayfield)drawableRuleset.Playfield).TotalColumns;
|
||||||
|
|
||||||
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
{
|
{
|
||||||
DrawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods);
|
drawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods);
|
||||||
|
|
||||||
// This is the earliest we can cache the scrolling info to ourselves, before masks are added to the hierarchy and inject it
|
// This is the earliest we can cache the scrolling info to ourselves, before masks are added to the hierarchy and inject it
|
||||||
dependencies.CacheAs(DrawableRuleset.ScrollingInfo);
|
dependencies.CacheAs(drawableRuleset.ScrollingInfo);
|
||||||
|
|
||||||
return DrawableRuleset;
|
return drawableRuleset;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
|
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
|
||||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
{
|
{
|
||||||
public class ManiaRuleset : Ruleset
|
public class ManiaRuleset : Ruleset
|
||||||
{
|
{
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableManiaRuleset(this, beatmap, mods);
|
public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableManiaRuleset(this, beatmap, mods);
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
|
||||||
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score);
|
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score);
|
||||||
|
|
||||||
|
@ -46,8 +46,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
Anchor = Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
Anchor = Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateInitialTransforms() => this.FadeIn();
|
|
||||||
|
|
||||||
protected override void UpdateStateTransforms(ArmedState state)
|
protected override void UpdateStateTransforms(ArmedState state)
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state)
|
||||||
|
@ -36,11 +36,13 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
public IEnumerable<BarLine> BarLines;
|
public IEnumerable<BarLine> BarLines;
|
||||||
|
|
||||||
|
protected override bool RelativeScaleBeatLengths => true;
|
||||||
|
|
||||||
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
||||||
|
|
||||||
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
||||||
|
|
||||||
public DrawableManiaRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableManiaRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
// Generate the bar lines
|
// Generate the bar lines
|
||||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
public class DrawableOsuEditRuleset : DrawableOsuRuleset
|
public class DrawableOsuEditRuleset : DrawableOsuRuleset
|
||||||
{
|
{
|
||||||
public DrawableOsuEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableOsuEditRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override DrawableRuleset<OsuHitObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
protected override DrawableRuleset<OsuHitObject> CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
=> new DrawableOsuEditRuleset(ruleset, beatmap, mods);
|
=> new DrawableOsuEditRuleset(ruleset, beatmap, mods);
|
||||||
|
|
||||||
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
|
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
|
||||||
|
@ -29,6 +29,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
private readonly HitArea hitArea;
|
private readonly HitArea hitArea;
|
||||||
|
|
||||||
|
private readonly SkinnableDrawable mainContent;
|
||||||
|
|
||||||
public DrawableHitCircle(HitCircle h)
|
public DrawableHitCircle(HitCircle h)
|
||||||
: base(h)
|
: base(h)
|
||||||
{
|
{
|
||||||
@ -56,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new SkinnableDrawable("Play/osu/hitcircle", _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)),
|
mainContent = new SkinnableDrawable("Play/osu/hitcircle", _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)),
|
||||||
ApproachCircle = new ApproachCircle
|
ApproachCircle = new ApproachCircle
|
||||||
{
|
{
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
@ -108,6 +110,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.UpdateInitialTransforms();
|
base.UpdateInitialTransforms();
|
||||||
|
|
||||||
|
mainContent.FadeInFromZero(HitObject.TimeFadeIn);
|
||||||
|
|
||||||
ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt));
|
ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt));
|
||||||
ApproachCircle.ScaleTo(1f, HitObject.TimePreempt);
|
ApproachCircle.ScaleTo(1f, HitObject.TimePreempt);
|
||||||
ApproachCircle.Expire(true);
|
ApproachCircle.Expire(true);
|
||||||
|
@ -36,8 +36,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt;
|
protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt;
|
||||||
|
|
||||||
protected override void UpdateInitialTransforms() => this.FadeIn(HitObject.TimeFadeIn);
|
|
||||||
|
|
||||||
private OsuInputManager osuActionInputManager;
|
private OsuInputManager osuActionInputManager;
|
||||||
internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager);
|
internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager);
|
||||||
|
|
||||||
|
@ -93,6 +93,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void UpdateInitialTransforms()
|
||||||
|
{
|
||||||
|
base.UpdateInitialTransforms();
|
||||||
|
|
||||||
|
Body.FadeInFromZero(HitObject.TimeFadeIn);
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
{
|
{
|
||||||
public class OsuRuleset : Ruleset
|
public class OsuRuleset : Ruleset
|
||||||
{
|
{
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableOsuRuleset(this, beatmap, mods);
|
public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableOsuRuleset(this, beatmap, mods);
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap);
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap);
|
||||||
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap);
|
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap);
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
{
|
{
|
||||||
protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config;
|
protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config;
|
||||||
|
|
||||||
public DrawableOsuRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableOsuRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -78,8 +78,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
public abstract bool OnPressed(TaikoAction action);
|
public abstract bool OnPressed(TaikoAction action);
|
||||||
public virtual bool OnReleased(TaikoAction action) => false;
|
public virtual bool OnReleased(TaikoAction action) => false;
|
||||||
|
|
||||||
protected override void UpdateInitialTransforms() => this.FadeIn();
|
|
||||||
|
|
||||||
public override double LifetimeStart
|
public override double LifetimeStart
|
||||||
{
|
{
|
||||||
get => base.LifetimeStart;
|
get => base.LifetimeStart;
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
{
|
{
|
||||||
public class TaikoRuleset : Ruleset
|
public class TaikoRuleset : Ruleset
|
||||||
{
|
{
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableTaikoRuleset(this, beatmap, mods);
|
public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableTaikoRuleset(this, beatmap, mods);
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap);
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap);
|
||||||
|
|
||||||
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
|
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
|
||||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
|
|
||||||
protected override bool UserScrollSpeedAdjustment => false;
|
protected override bool UserScrollSpeedAdjustment => false;
|
||||||
|
|
||||||
public DrawableTaikoRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableTaikoRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
Direction.Value = ScrollingDirection.Left;
|
Direction.Value = ScrollingDirection.Left;
|
||||||
|
@ -16,15 +16,13 @@ using osu.Game.Rulesets.Osu.Edit;
|
|||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit.Compose;
|
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editor
|
namespace osu.Game.Tests.Visual.Editor
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
[Cached(Type = typeof(IPlacementHandler))]
|
public class TestSceneHitObjectComposer : OsuTestScene
|
||||||
public class TestSceneHitObjectComposer : OsuTestScene, IPlacementHandler
|
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
{
|
{
|
||||||
@ -39,8 +37,6 @@ namespace osu.Game.Tests.Visual.Editor
|
|||||||
typeof(HitCirclePlacementBlueprint),
|
typeof(HitCirclePlacementBlueprint),
|
||||||
};
|
};
|
||||||
|
|
||||||
private HitObjectComposer composer;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
@ -67,15 +63,7 @@ namespace osu.Game.Tests.Visual.Editor
|
|||||||
Dependencies.CacheAs<IAdjustableClock>(clock);
|
Dependencies.CacheAs<IAdjustableClock>(clock);
|
||||||
Dependencies.CacheAs<IFrameBasedClock>(clock);
|
Dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||||
|
|
||||||
Child = composer = new OsuHitObjectComposer(new OsuRuleset());
|
Child = new OsuHitObjectComposer(new OsuRuleset());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeginPlacement(HitObject hitObject)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndPlacement(HitObject hitObject) => composer.Add(hitObject);
|
|
||||||
|
|
||||||
public void Delete(HitObject hitObject) => composer.Remove(hitObject);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,305 @@
|
|||||||
|
// 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 System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
public class TestSceneDrawableScrollingRuleset : OsuTestScene
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of time visible by the "view window" of the playfield.
|
||||||
|
/// All hitobjects added through <see cref="createBeatmap"/> are spaced apart by this value, such that for a beat length of 1000,
|
||||||
|
/// there will be at most 2 hitobjects visible in the "view window".
|
||||||
|
/// </summary>
|
||||||
|
private const double time_range = 1000;
|
||||||
|
|
||||||
|
private readonly ManualClock testClock = new ManualClock();
|
||||||
|
private TestDrawableScrollingRuleset drawableRuleset;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() => Schedule(() => testClock.CurrentTime = 0);
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRelativeBeatLengthScaleSingleTimingPoint()
|
||||||
|
{
|
||||||
|
var beatmap = createBeatmap(new TimingControlPoint { BeatLength = time_range / 2 });
|
||||||
|
|
||||||
|
createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true);
|
||||||
|
|
||||||
|
assertPosition(0, 0f);
|
||||||
|
|
||||||
|
// The single timing point is 1x speed relative to itself, such that the hitobject occurring time_range milliseconds later should appear
|
||||||
|
// at the bottom of the view window regardless of the timing point's beat length
|
||||||
|
assertPosition(1, 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRelativeBeatLengthScaleTimingPointBeyondEndDoesNotBecomeDominant()
|
||||||
|
{
|
||||||
|
var beatmap = createBeatmap(
|
||||||
|
new TimingControlPoint { BeatLength = time_range / 2 },
|
||||||
|
new TimingControlPoint { Time = 12000, BeatLength = time_range },
|
||||||
|
new TimingControlPoint { Time = 100000, BeatLength = time_range });
|
||||||
|
|
||||||
|
createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true);
|
||||||
|
|
||||||
|
assertPosition(0, 0f);
|
||||||
|
assertPosition(1, 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRelativeBeatLengthScaleFromSecondTimingPoint()
|
||||||
|
{
|
||||||
|
var beatmap = createBeatmap(
|
||||||
|
new TimingControlPoint { BeatLength = time_range },
|
||||||
|
new TimingControlPoint { Time = 3 * time_range, BeatLength = time_range / 2 });
|
||||||
|
|
||||||
|
createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true);
|
||||||
|
|
||||||
|
// The first timing point should have a relative velocity of 2
|
||||||
|
assertPosition(0, 0f);
|
||||||
|
assertPosition(1, 0.5f);
|
||||||
|
assertPosition(2, 1f);
|
||||||
|
|
||||||
|
// Move to the second timing point
|
||||||
|
setTime(3 * time_range);
|
||||||
|
assertPosition(3, 0f);
|
||||||
|
|
||||||
|
// As above, this is the timing point that is 1x speed relative to itself, so the hitobject occurring time_range milliseconds later should be at the bottom of the view window
|
||||||
|
assertPosition(4, 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNonRelativeScale()
|
||||||
|
{
|
||||||
|
var beatmap = createBeatmap(
|
||||||
|
new TimingControlPoint { BeatLength = time_range },
|
||||||
|
new TimingControlPoint { Time = 3 * time_range, BeatLength = time_range / 2 });
|
||||||
|
|
||||||
|
createTest(beatmap);
|
||||||
|
|
||||||
|
assertPosition(0, 0f);
|
||||||
|
assertPosition(1, 1);
|
||||||
|
|
||||||
|
// Move to the second timing point
|
||||||
|
setTime(3 * time_range);
|
||||||
|
assertPosition(3, 0f);
|
||||||
|
|
||||||
|
// For a beat length of 500, the view window of this timing point is elongated 2x (1000 / 500), such that the second hitobject is two TimeRanges away (offscreen)
|
||||||
|
// To bring it on-screen, half TimeRange is added to the current time, bringing the second half of the view window into view, and the hitobject should appear at the bottom
|
||||||
|
setTime(3 * time_range + time_range / 2);
|
||||||
|
assertPosition(4, 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertPosition(int index, float relativeY) => AddAssert($"hitobject {index} at {relativeY}",
|
||||||
|
() => Precision.AlmostEquals(drawableRuleset.Playfield.AllHitObjects.ElementAt(index).DrawPosition.Y, drawableRuleset.Playfield.HitObjectContainer.DrawHeight * relativeY));
|
||||||
|
|
||||||
|
private void setTime(double time)
|
||||||
|
{
|
||||||
|
AddStep($"set time = {time}", () => testClock.CurrentTime = time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an <see cref="IBeatmap"/>, containing 10 hitobjects and user-provided timing points.
|
||||||
|
/// The hitobjects are spaced <see cref="time_range"/> milliseconds apart.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timingControlPoints">The timing points to add to the beatmap.</param>
|
||||||
|
/// <returns>The <see cref="IBeatmap"/>.</returns>
|
||||||
|
private IBeatmap createBeatmap(params TimingControlPoint[] timingControlPoints)
|
||||||
|
{
|
||||||
|
var beatmap = new Beatmap<HitObject> { BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo } };
|
||||||
|
|
||||||
|
beatmap.ControlPointInfo.TimingPoints.AddRange(timingControlPoints);
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
beatmap.HitObjects.Add(new HitObject { StartTime = i * time_range });
|
||||||
|
|
||||||
|
return beatmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTest(IBeatmap beatmap, Action<TestDrawableScrollingRuleset> overrideAction = null) => AddStep("create test", () =>
|
||||||
|
{
|
||||||
|
var ruleset = new TestScrollingRuleset();
|
||||||
|
|
||||||
|
drawableRuleset = (TestDrawableScrollingRuleset)ruleset.CreateDrawableRulesetWith(CreateWorkingBeatmap(beatmap), Array.Empty<Mod>());
|
||||||
|
drawableRuleset.FrameStablePlayback = false;
|
||||||
|
|
||||||
|
overrideAction?.Invoke(drawableRuleset);
|
||||||
|
|
||||||
|
Child = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Height = 0.75f,
|
||||||
|
Width = 400,
|
||||||
|
Masking = true,
|
||||||
|
Clock = new FramedClock(testClock),
|
||||||
|
Child = drawableRuleset
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
#region Ruleset
|
||||||
|
|
||||||
|
private class TestScrollingRuleset : Ruleset
|
||||||
|
{
|
||||||
|
public TestScrollingRuleset(RulesetInfo rulesetInfo = null)
|
||||||
|
: base(rulesetInfo)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<Mod> GetModsFor(ModType type) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new TestDrawableScrollingRuleset(this, beatmap, mods);
|
||||||
|
|
||||||
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TestBeatmapConverter(beatmap);
|
||||||
|
|
||||||
|
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public override string Description { get; } = string.Empty;
|
||||||
|
|
||||||
|
public override string ShortName { get; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestDrawableScrollingRuleset : DrawableScrollingRuleset<TestHitObject>
|
||||||
|
{
|
||||||
|
public bool RelativeScaleBeatLengthsOverride { get; set; }
|
||||||
|
|
||||||
|
protected override bool RelativeScaleBeatLengths => RelativeScaleBeatLengthsOverride;
|
||||||
|
|
||||||
|
protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping;
|
||||||
|
|
||||||
|
public TestDrawableScrollingRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
|
: base(ruleset, beatmap, mods)
|
||||||
|
{
|
||||||
|
TimeRange.Value = time_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DrawableHitObject<TestHitObject> CreateDrawableRepresentation(TestHitObject h) => new DrawableTestHitObject(h);
|
||||||
|
|
||||||
|
protected override PassThroughInputManager CreateInputManager() => new PassThroughInputManager();
|
||||||
|
|
||||||
|
protected override Playfield CreatePlayfield() => new TestPlayfield();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestPlayfield : ScrollingPlayfield
|
||||||
|
{
|
||||||
|
public TestPlayfield()
|
||||||
|
{
|
||||||
|
AddInternal(new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0.2f,
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Top = 150 },
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = 2,
|
||||||
|
Colour = Color4.Green
|
||||||
|
},
|
||||||
|
HitObjectContainer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestBeatmapConverter : BeatmapConverter<TestHitObject>
|
||||||
|
{
|
||||||
|
public TestBeatmapConverter(IBeatmap beatmap)
|
||||||
|
: base(beatmap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IEnumerable<Type> ValidConversionTypes => new[] { typeof(HitObject) };
|
||||||
|
|
||||||
|
protected override IEnumerable<TestHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
yield return new TestHitObject
|
||||||
|
{
|
||||||
|
StartTime = original.StartTime,
|
||||||
|
EndTime = (original as IHasEndTime)?.EndTime ?? (original.StartTime + 100)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region HitObject
|
||||||
|
|
||||||
|
private class TestHitObject : HitObject, IHasEndTime
|
||||||
|
{
|
||||||
|
public double EndTime { get; set; }
|
||||||
|
|
||||||
|
public double Duration => EndTime - StartTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DrawableTestHitObject : DrawableHitObject<TestHitObject>
|
||||||
|
{
|
||||||
|
public DrawableTestHitObject(TestHitObject hitObject)
|
||||||
|
: base(hitObject)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre;
|
||||||
|
Origin = Anchor.TopCentre;
|
||||||
|
|
||||||
|
Size = new Vector2(100, 25);
|
||||||
|
|
||||||
|
AddRangeInternal(new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.LightPink
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = 2,
|
||||||
|
Colour = Color4.Red
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A Beatmap containing converted HitObjects.
|
/// A Beatmap containing converted HitObjects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Beatmap<T> : IBeatmap
|
public class Beatmap<T> : IBeatmap<T>
|
||||||
where T : HitObject
|
where T : HitObject
|
||||||
{
|
{
|
||||||
public BeatmapInfo BeatmapInfo { get; set; } = new BeatmapInfo
|
public BeatmapInfo BeatmapInfo { get; set; } = new BeatmapInfo
|
||||||
@ -36,17 +36,13 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public List<BreakPeriod> Breaks { get; set; } = new List<BreakPeriod>();
|
public List<BreakPeriod> Breaks { get; set; } = new List<BreakPeriod>();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Total amount of break time in the beatmap.
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public double TotalBreakTime => Breaks.Sum(b => b.Duration);
|
public double TotalBreakTime => Breaks.Sum(b => b.Duration);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The HitObjects this Beatmap contains.
|
|
||||||
/// </summary>
|
|
||||||
[JsonConverter(typeof(TypedListConverter<HitObject>))]
|
[JsonConverter(typeof(TypedListConverter<HitObject>))]
|
||||||
public List<T> HitObjects = new List<T>();
|
public List<T> HitObjects { get; set; } = new List<T>();
|
||||||
|
|
||||||
|
IReadOnlyList<T> IBeatmap<T>.HitObjects => HitObjects;
|
||||||
|
|
||||||
IReadOnlyList<HitObject> IBeatmap.HitObjects => HitObjects;
|
IReadOnlyList<HitObject> IBeatmap.HitObjects => HitObjects;
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSignatures TimeSignature = TimeSignatures.SimpleQuadruple;
|
public TimeSignatures TimeSignature = TimeSignatures.SimpleQuadruple;
|
||||||
|
|
||||||
|
public const double DEFAULT_BEAT_LENGTH = 1000;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The beat length at this control point.
|
/// The beat length at this control point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -23,7 +25,7 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
set => beatLength = MathHelper.Clamp(value, 6, 60000);
|
set => beatLength = MathHelper.Clamp(value, 6, 60000);
|
||||||
}
|
}
|
||||||
|
|
||||||
private double beatLength = 1000;
|
private double beatLength = DEFAULT_BEAT_LENGTH;
|
||||||
|
|
||||||
public bool Equals(TimingControlPoint other)
|
public bool Equals(TimingControlPoint other)
|
||||||
=> base.Equals(other)
|
=> base.Equals(other)
|
||||||
|
@ -54,7 +54,7 @@ namespace osu.Game.Beatmaps
|
|||||||
{
|
{
|
||||||
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[] { };
|
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[] { };
|
||||||
|
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
private class LegacyDifficultyCalculatorControlPoint : TimingControlPoint
|
private class LegacyDifficultyCalculatorControlPoint : TimingControlPoint
|
||||||
{
|
{
|
||||||
public override double BeatLength { get; set; } = 1000;
|
public override double BeatLength { get; set; } = DEFAULT_BEAT_LENGTH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,4 +53,13 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <returns>The shallow-cloned beatmap.</returns>
|
/// <returns>The shallow-cloned beatmap.</returns>
|
||||||
IBeatmap Clone();
|
IBeatmap Clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IBeatmap<out T> : IBeatmap
|
||||||
|
where T : HitObject
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The hitobjects contained by this beatmap.
|
||||||
|
/// </summary>
|
||||||
|
new IReadOnlyList<T> HitObjects { get; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
61
osu.Game/Beatmaps/IWorkingBeatmap.cs
Normal file
61
osu.Game/Beatmaps/IWorkingBeatmap.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// 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 System.Collections.Generic;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Storyboards;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps
|
||||||
|
{
|
||||||
|
public interface IWorkingBeatmap
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the <see cref="IBeatmap"/> which this <see cref="WorkingBeatmap"/> represents.
|
||||||
|
/// </summary>
|
||||||
|
IBeatmap Beatmap { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the background for this <see cref="WorkingBeatmap"/>.
|
||||||
|
/// </summary>
|
||||||
|
Texture Background { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the audio track for this <see cref="WorkingBeatmap"/>.
|
||||||
|
/// </summary>
|
||||||
|
Track Track { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the <see cref="Waveform"/> for the <see cref="Track"/> of this <see cref="WorkingBeatmap"/>.
|
||||||
|
/// </summary>
|
||||||
|
Waveform Waveform { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the <see cref="Storyboard"/> which this <see cref="WorkingBeatmap"/> provides.
|
||||||
|
/// </summary>
|
||||||
|
Storyboard Storyboard { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the <see cref="Skin"/> which this <see cref="WorkingBeatmap"/> provides.
|
||||||
|
/// </summary>
|
||||||
|
Skin Skin { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a playable <see cref="IBeatmap"/> from <see cref="Beatmap"/> using the applicable converters for a specific <see cref="RulesetInfo"/>.
|
||||||
|
/// <para>
|
||||||
|
/// The returned <see cref="IBeatmap"/> is in a playable state - all <see cref="HitObject"/> and <see cref="BeatmapDifficulty"/> <see cref="Mod"/>s
|
||||||
|
/// have been applied, and <see cref="HitObject"/>s have been fully constructed.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ruleset">The <see cref="RulesetInfo"/> to create a playable <see cref="IBeatmap"/> for.</param>
|
||||||
|
/// <param name="mods">The <see cref="Mod"/>s to apply to the <see cref="IBeatmap"/>.</param>
|
||||||
|
/// <returns>The converted <see cref="IBeatmap"/>.</returns>
|
||||||
|
/// <exception cref="BeatmapInvalidForRulesetException">If <see cref="Beatmap"/> could not be converted to <paramref name="ruleset"/>.</exception>
|
||||||
|
IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods);
|
||||||
|
}
|
||||||
|
}
|
@ -16,14 +16,13 @@ using osu.Framework.Audio;
|
|||||||
using osu.Framework.Statistics;
|
using osu.Framework.Statistics;
|
||||||
using osu.Game.IO.Serialization;
|
using osu.Game.IO.Serialization;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
{
|
{
|
||||||
public abstract class WorkingBeatmap : IDisposable
|
public abstract class WorkingBeatmap : IWorkingBeatmap, IDisposable
|
||||||
{
|
{
|
||||||
public readonly BeatmapInfo BeatmapInfo;
|
public readonly BeatmapInfo BeatmapInfo;
|
||||||
|
|
||||||
@ -97,17 +96,6 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <returns>The applicable <see cref="IBeatmapConverter"/>.</returns>
|
/// <returns>The applicable <see cref="IBeatmapConverter"/>.</returns>
|
||||||
protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap);
|
protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructs a playable <see cref="IBeatmap"/> from <see cref="Beatmap"/> using the applicable converters for a specific <see cref="RulesetInfo"/>.
|
|
||||||
/// <para>
|
|
||||||
/// The returned <see cref="IBeatmap"/> is in a playable state - all <see cref="HitObject"/> and <see cref="BeatmapDifficulty"/> <see cref="Mod"/>s
|
|
||||||
/// have been applied, and <see cref="HitObject"/>s have been fully constructed.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ruleset">The <see cref="RulesetInfo"/> to create a playable <see cref="IBeatmap"/> for.</param>
|
|
||||||
/// <param name="mods">The <see cref="Mod"/>s to apply to the <see cref="IBeatmap"/>.</param>
|
|
||||||
/// <returns>The converted <see cref="IBeatmap"/>.</returns>
|
|
||||||
/// <exception cref="BeatmapInvalidForRulesetException">If <see cref="Beatmap"/> could not be converted to <paramref name="ruleset"/>.</exception>
|
|
||||||
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods)
|
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods)
|
||||||
{
|
{
|
||||||
var rulesetInstance = ruleset.CreateInstance();
|
var rulesetInstance = ruleset.CreateInstance();
|
||||||
|
@ -62,15 +62,23 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition))
|
closeIfOutside(e);
|
||||||
{
|
|
||||||
Hide();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.OnClick(e);
|
return base.OnClick(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnDragEnd(DragEndEvent e)
|
||||||
|
{
|
||||||
|
closeIfOutside(e);
|
||||||
|
return base.OnDragEnd(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeIfOutside(MouseEvent e)
|
||||||
|
{
|
||||||
|
if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition))
|
||||||
|
Hide();
|
||||||
|
}
|
||||||
|
|
||||||
public virtual bool OnPressed(GlobalAction action)
|
public virtual bool OnPressed(GlobalAction action)
|
||||||
{
|
{
|
||||||
switch (action)
|
switch (action)
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Humanizer;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Graphics
|
namespace osu.Game.Graphics
|
||||||
{
|
{
|
||||||
@ -71,7 +71,7 @@ namespace osu.Game.Graphics
|
|||||||
Scheduler.AddDelayed(updateTimeWithReschedule, timeUntilNextUpdate);
|
Scheduler.AddDelayed(updateTimeWithReschedule, timeUntilNextUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual string Format() => Date.Humanize();
|
protected virtual string Format() => HumanizerUtils.Humanize(Date);
|
||||||
|
|
||||||
private void updateTime() => Text = Format();
|
private void updateTime() => Text = Format();
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using Humanizer;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -14,6 +13,7 @@ using osu.Game.Graphics.Sprites;
|
|||||||
using osu.Game.Online.Leaderboards;
|
using osu.Game.Online.Leaderboards;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Users.Drawables;
|
using osu.Game.Users.Drawables;
|
||||||
|
using osu.Game.Utils;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
|||||||
{
|
{
|
||||||
avatar.User = value.User;
|
avatar.User = value.User;
|
||||||
flag.Country = value.User.Country;
|
flag.Country = value.User.Country;
|
||||||
date.Text = $@"achieved {value.Date.Humanize()}";
|
date.Text = $@"achieved {HumanizerUtils.Humanize(value.Date)}";
|
||||||
|
|
||||||
usernameText.Clear();
|
usernameText.Clear();
|
||||||
usernameText.AddUserLink(value.User);
|
usernameText.AddUserLink(value.User);
|
||||||
|
@ -1,116 +0,0 @@
|
|||||||
// 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 System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.UI;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit
|
|
||||||
{
|
|
||||||
public abstract class DrawableEditRuleset : CompositeDrawable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="Playfield"/> contained by this <see cref="DrawableEditRuleset"/>.
|
|
||||||
/// </summary>
|
|
||||||
public abstract Playfield Playfield { get; }
|
|
||||||
|
|
||||||
public abstract PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer();
|
|
||||||
|
|
||||||
internal DrawableEditRuleset()
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a <see cref="HitObject"/> to the <see cref="Beatmap"/> and displays a visual representation of it.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hitObject">The <see cref="HitObject"/> to add.</param>
|
|
||||||
/// <returns>The visual representation of <paramref name="hitObject"/>.</returns>
|
|
||||||
internal abstract DrawableHitObject Add(HitObject hitObject);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes a <see cref="HitObject"/> from the <see cref="Beatmap"/> and the display.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hitObject">The <see cref="HitObject"/> to remove.</param>
|
|
||||||
/// <returns>The visual representation of the removed <paramref name="hitObject"/>.</returns>
|
|
||||||
internal abstract DrawableHitObject Remove(HitObject hitObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DrawableEditRuleset<TObject> : DrawableEditRuleset
|
|
||||||
where TObject : HitObject
|
|
||||||
{
|
|
||||||
public override Playfield Playfield => drawableRuleset.Playfield;
|
|
||||||
|
|
||||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => drawableRuleset.CreatePlayfieldAdjustmentContainer();
|
|
||||||
|
|
||||||
private Ruleset ruleset => drawableRuleset.Ruleset;
|
|
||||||
private Beatmap<TObject> beatmap => drawableRuleset.Beatmap;
|
|
||||||
|
|
||||||
private readonly DrawableRuleset<TObject> drawableRuleset;
|
|
||||||
|
|
||||||
public DrawableEditRuleset(DrawableRuleset<TObject> drawableRuleset)
|
|
||||||
{
|
|
||||||
this.drawableRuleset = drawableRuleset;
|
|
||||||
|
|
||||||
InternalChild = drawableRuleset;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
drawableRuleset.FrameStablePlayback = false;
|
|
||||||
Playfield.DisplayJudgements.Value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override DrawableHitObject Add(HitObject hitObject)
|
|
||||||
{
|
|
||||||
var tObject = (TObject)hitObject;
|
|
||||||
|
|
||||||
// Add to beatmap, preserving sorting order
|
|
||||||
var insertionIndex = beatmap.HitObjects.FindLastIndex(h => h.StartTime <= hitObject.StartTime);
|
|
||||||
beatmap.HitObjects.Insert(insertionIndex + 1, tObject);
|
|
||||||
|
|
||||||
// Process object
|
|
||||||
var processor = ruleset.CreateBeatmapProcessor(beatmap);
|
|
||||||
|
|
||||||
processor?.PreProcess();
|
|
||||||
tObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty);
|
|
||||||
processor?.PostProcess();
|
|
||||||
|
|
||||||
// Add visual representation
|
|
||||||
var drawableObject = drawableRuleset.CreateDrawableRepresentation(tObject);
|
|
||||||
|
|
||||||
drawableRuleset.Playfield.Add(drawableObject);
|
|
||||||
drawableRuleset.Playfield.PostProcess();
|
|
||||||
|
|
||||||
return drawableObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override DrawableHitObject Remove(HitObject hitObject)
|
|
||||||
{
|
|
||||||
var tObject = (TObject)hitObject;
|
|
||||||
|
|
||||||
// Remove from beatmap
|
|
||||||
beatmap.HitObjects.Remove(tObject);
|
|
||||||
|
|
||||||
// Process the beatmap
|
|
||||||
var processor = ruleset.CreateBeatmapProcessor(beatmap);
|
|
||||||
|
|
||||||
processor?.PreProcess();
|
|
||||||
processor?.PostProcess();
|
|
||||||
|
|
||||||
// Remove visual representation
|
|
||||||
var drawableObject = Playfield.AllHitObjects.Single(d => d.HitObject == hitObject);
|
|
||||||
|
|
||||||
drawableRuleset.Playfield.Remove(drawableObject);
|
|
||||||
drawableRuleset.Playfield.PostProcess();
|
|
||||||
|
|
||||||
return drawableObject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
80
osu.Game/Rulesets/Edit/DrawableEditRulesetWrapper.cs
Normal file
80
osu.Game/Rulesets/Edit/DrawableEditRulesetWrapper.cs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// 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 System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Edit
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A wrapper for a <see cref="DrawableRuleset{TObject}"/>. Handles adding visual representations of <see cref="HitObject"/>s to the underlying <see cref="DrawableRuleset{TObject}"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal class DrawableEditRulesetWrapper<TObject> : CompositeDrawable
|
||||||
|
where TObject : HitObject
|
||||||
|
{
|
||||||
|
public Playfield Playfield => drawableRuleset.Playfield;
|
||||||
|
|
||||||
|
private readonly DrawableRuleset<TObject> drawableRuleset;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IEditorBeatmap<TObject> beatmap { get; set; }
|
||||||
|
|
||||||
|
public DrawableEditRulesetWrapper(DrawableRuleset<TObject> drawableRuleset)
|
||||||
|
{
|
||||||
|
this.drawableRuleset = drawableRuleset;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = drawableRuleset;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
drawableRuleset.FrameStablePlayback = false;
|
||||||
|
Playfield.DisplayJudgements.Value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
beatmap.HitObjectAdded += addHitObject;
|
||||||
|
beatmap.HitObjectRemoved += removeHitObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addHitObject(HitObject hitObject)
|
||||||
|
{
|
||||||
|
var drawableObject = drawableRuleset.CreateDrawableRepresentation((TObject)hitObject);
|
||||||
|
|
||||||
|
drawableRuleset.Playfield.Add(drawableObject);
|
||||||
|
drawableRuleset.Playfield.PostProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeHitObject(HitObject hitObject)
|
||||||
|
{
|
||||||
|
var drawableObject = Playfield.AllHitObjects.Single(d => d.HitObject == hitObject);
|
||||||
|
|
||||||
|
drawableRuleset.Playfield.Remove(drawableObject);
|
||||||
|
drawableRuleset.Playfield.PostProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => drawableRuleset.CreatePlayfieldAdjustmentContainer();
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (beatmap != null)
|
||||||
|
{
|
||||||
|
beatmap.HitObjectAdded -= addHitObject;
|
||||||
|
beatmap.HitObjectRemoved -= removeHitObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,45 +18,47 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Components.RadioButtons;
|
using osu.Game.Screens.Edit.Components.RadioButtons;
|
||||||
|
using osu.Game.Screens.Edit.Compose;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit
|
namespace osu.Game.Rulesets.Edit
|
||||||
{
|
{
|
||||||
public abstract class HitObjectComposer : CompositeDrawable
|
[Cached(Type = typeof(IPlacementHandler))]
|
||||||
|
public abstract class HitObjectComposer<TObject> : HitObjectComposer, IPlacementHandler
|
||||||
|
where TObject : HitObject
|
||||||
{
|
{
|
||||||
public IEnumerable<DrawableHitObject> HitObjects => DrawableRuleset.Playfield.AllHitObjects;
|
protected IRulesetConfigManager Config { get; private set; }
|
||||||
|
|
||||||
protected readonly Ruleset Ruleset;
|
protected readonly Ruleset Ruleset;
|
||||||
|
|
||||||
protected readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
private IWorkingBeatmap workingBeatmap;
|
||||||
|
private Beatmap<TObject> playableBeatmap;
|
||||||
protected IRulesetConfigManager Config { get; private set; }
|
private EditorBeatmap<TObject> editorBeatmap;
|
||||||
|
private IBeatmapProcessor beatmapProcessor;
|
||||||
private readonly List<Container> layerContainers = new List<Container>();
|
|
||||||
|
|
||||||
protected DrawableEditRuleset DrawableRuleset { get; private set; }
|
|
||||||
|
|
||||||
|
private DrawableEditRulesetWrapper<TObject> drawableRulesetWrapper;
|
||||||
private BlueprintContainer blueprintContainer;
|
private BlueprintContainer blueprintContainer;
|
||||||
|
private readonly List<Container> layerContainers = new List<Container>();
|
||||||
|
|
||||||
private InputManager inputManager;
|
private InputManager inputManager;
|
||||||
|
|
||||||
internal HitObjectComposer(Ruleset ruleset)
|
protected HitObjectComposer(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
Ruleset = ruleset;
|
Ruleset = ruleset;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(IBindable<WorkingBeatmap> beatmap, IFrameBasedClock framedClock)
|
private void load(IFrameBasedClock framedClock)
|
||||||
{
|
{
|
||||||
Beatmap.BindTo(beatmap);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DrawableRuleset = CreateDrawableRuleset();
|
drawableRulesetWrapper = new DrawableEditRulesetWrapper<TObject>(CreateDrawableRuleset(Ruleset, workingBeatmap, Array.Empty<Mod>()))
|
||||||
DrawableRuleset.Clock = framedClock;
|
{
|
||||||
|
Clock = framedClock
|
||||||
|
};
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -64,10 +66,10 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var layerBelowRuleset = DrawableRuleset.CreatePlayfieldAdjustmentContainer();
|
var layerBelowRuleset = drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer();
|
||||||
layerBelowRuleset.Child = new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both };
|
layerBelowRuleset.Child = new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
var layerAboveRuleset = DrawableRuleset.CreatePlayfieldAdjustmentContainer();
|
var layerAboveRuleset = drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer();
|
||||||
layerAboveRuleset.Child = blueprintContainer = new BlueprintContainer();
|
layerAboveRuleset.Child = blueprintContainer = new BlueprintContainer();
|
||||||
|
|
||||||
layerContainers.Add(layerBelowRuleset);
|
layerContainers.Add(layerBelowRuleset);
|
||||||
@ -98,7 +100,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
layerBelowRuleset,
|
layerBelowRuleset,
|
||||||
DrawableRuleset,
|
drawableRulesetWrapper,
|
||||||
layerAboveRuleset
|
layerAboveRuleset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,6 +120,28 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
toolboxCollection.Items[0].Select();
|
toolboxCollection.Items[0].Select();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
|
{
|
||||||
|
var parentWorkingBeatmap = parent.Get<IBindable<WorkingBeatmap>>().Value;
|
||||||
|
|
||||||
|
playableBeatmap = (Beatmap<TObject>)parentWorkingBeatmap.GetPlayableBeatmap(Ruleset.RulesetInfo, Array.Empty<Mod>());
|
||||||
|
workingBeatmap = new EditorWorkingBeatmap<TObject>(playableBeatmap, parentWorkingBeatmap);
|
||||||
|
|
||||||
|
beatmapProcessor = Ruleset.CreateBeatmapProcessor(playableBeatmap);
|
||||||
|
|
||||||
|
editorBeatmap = new EditorBeatmap<TObject>(playableBeatmap);
|
||||||
|
editorBeatmap.HitObjectAdded += addHitObject;
|
||||||
|
editorBeatmap.HitObjectRemoved += removeHitObject;
|
||||||
|
|
||||||
|
var dependencies = new DependencyContainer(parent);
|
||||||
|
dependencies.CacheAs<IEditorBeatmap>(editorBeatmap);
|
||||||
|
dependencies.CacheAs<IEditorBeatmap<TObject>>(editorBeatmap);
|
||||||
|
|
||||||
|
Config = dependencies.Get<RulesetConfigCache>().GetConfigFor(Ruleset);
|
||||||
|
|
||||||
|
return base.CreateChildDependencies(dependencies);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
@ -125,45 +149,76 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
inputManager = GetContainingInputManager();
|
inputManager = GetContainingInputManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
|
||||||
{
|
|
||||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
|
||||||
|
|
||||||
dependencies.CacheAs(this);
|
|
||||||
Config = dependencies.Get<RulesetConfigCache>().GetConfigFor(Ruleset);
|
|
||||||
|
|
||||||
return dependencies;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void UpdateAfterChildren()
|
protected override void UpdateAfterChildren()
|
||||||
{
|
{
|
||||||
base.UpdateAfterChildren();
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
layerContainers.ForEach(l =>
|
layerContainers.ForEach(l =>
|
||||||
{
|
{
|
||||||
l.Anchor = DrawableRuleset.Playfield.Anchor;
|
l.Anchor = drawableRulesetWrapper.Playfield.Anchor;
|
||||||
l.Origin = DrawableRuleset.Playfield.Origin;
|
l.Origin = drawableRulesetWrapper.Playfield.Origin;
|
||||||
l.Position = DrawableRuleset.Playfield.Position;
|
l.Position = drawableRulesetWrapper.Playfield.Position;
|
||||||
l.Size = DrawableRuleset.Playfield.Size;
|
l.Size = drawableRulesetWrapper.Playfield.Size;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addHitObject(HitObject hitObject)
|
||||||
|
{
|
||||||
|
beatmapProcessor?.PreProcess();
|
||||||
|
hitObject.ApplyDefaults(playableBeatmap.ControlPointInfo, playableBeatmap.BeatmapInfo.BaseDifficulty);
|
||||||
|
beatmapProcessor?.PostProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeHitObject(HitObject hitObject)
|
||||||
|
{
|
||||||
|
beatmapProcessor?.PreProcess();
|
||||||
|
beatmapProcessor?.PostProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<DrawableHitObject> HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects;
|
||||||
|
public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position);
|
||||||
|
|
||||||
|
protected abstract IReadOnlyList<HitObjectCompositionTool> CompositionTools { get; }
|
||||||
|
|
||||||
|
protected abstract DrawableRuleset<TObject> CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods);
|
||||||
|
|
||||||
|
public void BeginPlacement(HitObject hitObject)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndPlacement(HitObject hitObject) => editorBeatmap.Add(hitObject);
|
||||||
|
|
||||||
|
public void Delete(HitObject hitObject) => editorBeatmap.Remove(hitObject);
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (editorBeatmap != null)
|
||||||
|
{
|
||||||
|
editorBeatmap.HitObjectAdded -= addHitObject;
|
||||||
|
editorBeatmap.HitObjectRemoved -= removeHitObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cached(typeof(HitObjectComposer))]
|
||||||
|
public abstract class HitObjectComposer : CompositeDrawable
|
||||||
|
{
|
||||||
|
internal HitObjectComposer()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All the <see cref="DrawableHitObject"/>s.
|
||||||
|
/// </summary>
|
||||||
|
public abstract IEnumerable<DrawableHitObject> HitObjects { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the user's cursor is currently in an area of the <see cref="HitObjectComposer"/> that is valid for placement.
|
/// Whether the user's cursor is currently in an area of the <see cref="HitObjectComposer"/> that is valid for placement.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool CursorInPlacementArea => DrawableRuleset.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position);
|
public abstract bool CursorInPlacementArea { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a <see cref="HitObject"/> to the <see cref="Beatmaps.Beatmap"/> and visualises it.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hitObject">The <see cref="HitObject"/> to add.</param>
|
|
||||||
public void Add(HitObject hitObject) => blueprintContainer.AddBlueprintFor(DrawableRuleset.Add(hitObject));
|
|
||||||
|
|
||||||
public void Remove(HitObject hitObject) => blueprintContainer.RemoveBlueprintFor(DrawableRuleset.Remove(hitObject));
|
|
||||||
|
|
||||||
internal abstract DrawableEditRuleset CreateDrawableRuleset();
|
|
||||||
|
|
||||||
protected abstract IReadOnlyList<HitObjectCompositionTool> CompositionTools { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="SelectionBlueprint"/> for a specific <see cref="DrawableHitObject"/>.
|
/// Creates a <see cref="SelectionBlueprint"/> for a specific <see cref="DrawableHitObject"/>.
|
||||||
@ -176,18 +231,4 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual SelectionHandler CreateSelectionHandler() => new SelectionHandler();
|
public virtual SelectionHandler CreateSelectionHandler() => new SelectionHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class HitObjectComposer<TObject> : HitObjectComposer
|
|
||||||
where TObject : HitObject
|
|
||||||
{
|
|
||||||
protected HitObjectComposer(Ruleset ruleset)
|
|
||||||
: base(ruleset)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override DrawableEditRuleset CreateDrawableRuleset()
|
|
||||||
=> new DrawableEditRuleset<TObject>(CreateDrawableRuleset(Ruleset, Beatmap.Value, Array.Empty<Mod>()));
|
|
||||||
|
|
||||||
protected abstract DrawableRuleset<TObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.TypeExtensions;
|
using osu.Framework.Extensions.TypeExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
@ -186,6 +187,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Apply (generally fade-in) transforms leading into the <see cref="HitObject"/> start time.
|
/// Apply (generally fade-in) transforms leading into the <see cref="HitObject"/> start time.
|
||||||
/// The local drawable hierarchy is recursively delayed to <see cref="LifetimeStart"/> for convenience.
|
/// The local drawable hierarchy is recursively delayed to <see cref="LifetimeStart"/> for convenience.
|
||||||
|
///
|
||||||
|
/// By default this will fade in the object from zero with no duration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This is called once before every <see cref="UpdateStateTransforms"/>. This is to ensure a good state in the case
|
/// This is called once before every <see cref="UpdateStateTransforms"/>. This is to ensure a good state in the case
|
||||||
@ -193,6 +196,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
protected virtual void UpdateInitialTransforms()
|
protected virtual void UpdateInitialTransforms()
|
||||||
{
|
{
|
||||||
|
this.FadeInFromZero();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -59,7 +59,7 @@ namespace osu.Game.Rulesets
|
|||||||
/// <param name="mods">The <see cref="Mod"/>s to apply.</param>
|
/// <param name="mods">The <see cref="Mod"/>s to apply.</param>
|
||||||
/// <exception cref="BeatmapInvalidForRulesetException">Unable to successfully load the beatmap to be usable with this ruleset.</exception>
|
/// <exception cref="BeatmapInvalidForRulesetException">Unable to successfully load the beatmap to be usable with this ruleset.</exception>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public abstract DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods);
|
public abstract DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="IBeatmapConverter"/> to convert a <see cref="IBeatmap"/> to one that is applicable for this <see cref="Ruleset"/>.
|
/// Creates a <see cref="IBeatmapConverter"/> to convert a <see cref="IBeatmap"/> to one that is applicable for this <see cref="Ruleset"/>.
|
||||||
|
@ -20,7 +20,13 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The aggregate multiplier which this <see cref="MultiplierControlPoint"/> provides.
|
/// The aggregate multiplier which this <see cref="MultiplierControlPoint"/> provides.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double Multiplier => Velocity * DifficultyPoint.SpeedMultiplier * 1000 / TimingPoint.BeatLength;
|
public double Multiplier => Velocity * DifficultyPoint.SpeedMultiplier * BaseBeatLength / TimingPoint.BeatLength;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The base beat length to scale the <see cref="TimingPoint"/> provided multiplier relative to.
|
||||||
|
/// </summary>
|
||||||
|
/// <example>For a <see cref="BaseBeatLength"/> of 1000, a <see cref="TimingPoint"/> with a beat length of 500 will increase the multiplier by 2.</example>
|
||||||
|
public double BaseBeatLength = TimingControlPoint.DEFAULT_BEAT_LENGTH;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The velocity multiplier.
|
/// The velocity multiplier.
|
||||||
|
@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <param name="ruleset">The ruleset being represented.</param>
|
/// <param name="ruleset">The ruleset being represented.</param>
|
||||||
/// <param name="workingBeatmap">The beatmap to create the hit renderer for.</param>
|
/// <param name="workingBeatmap">The beatmap to create the hit renderer for.</param>
|
||||||
/// <param name="mods">The <see cref="Mod"/>s to apply.</param>
|
/// <param name="mods">The <see cref="Mod"/>s to apply.</param>
|
||||||
protected DrawableRuleset(Ruleset ruleset, WorkingBeatmap workingBeatmap, IReadOnlyList<Mod> mods)
|
protected DrawableRuleset(Ruleset ruleset, IWorkingBeatmap workingBeatmap, IReadOnlyList<Mod> mods)
|
||||||
: base(ruleset)
|
: base(ruleset)
|
||||||
{
|
{
|
||||||
if (workingBeatmap == null)
|
if (workingBeatmap == null)
|
||||||
|
@ -69,6 +69,11 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual bool UserScrollSpeedAdjustment => true;
|
protected virtual bool UserScrollSpeedAdjustment => true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether <see cref="TimingControlPoint"/> beat lengths should scale relative to the most common beat length in the <see cref="Beatmap"/>.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual bool RelativeScaleBeatLengths => false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides the default <see cref="MultiplierControlPoint"/>s that adjust the scrolling rate of <see cref="HitObject"/>s
|
/// Provides the default <see cref="MultiplierControlPoint"/>s that adjust the scrolling rate of <see cref="HitObject"/>s
|
||||||
/// inside this <see cref="DrawableRuleset{TObject}"/>.
|
/// inside this <see cref="DrawableRuleset{TObject}"/>.
|
||||||
@ -81,7 +86,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
[Cached(Type = typeof(IScrollingInfo))]
|
[Cached(Type = typeof(IScrollingInfo))]
|
||||||
private readonly LocalScrollingInfo scrollingInfo;
|
private readonly LocalScrollingInfo scrollingInfo;
|
||||||
|
|
||||||
protected DrawableScrollingRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
protected DrawableScrollingRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
scrollingInfo = new LocalScrollingInfo();
|
scrollingInfo = new LocalScrollingInfo();
|
||||||
@ -107,16 +112,38 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
// Calculate default multiplier control points
|
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
|
||||||
|
double baseBeatLength = TimingControlPoint.DEFAULT_BEAT_LENGTH;
|
||||||
|
|
||||||
|
if (RelativeScaleBeatLengths)
|
||||||
|
{
|
||||||
|
IReadOnlyList<TimingControlPoint> timingPoints = Beatmap.ControlPointInfo.TimingPoints;
|
||||||
|
double maxDuration = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < timingPoints.Count; i++)
|
||||||
|
{
|
||||||
|
if (timingPoints[i].Time > lastObjectTime)
|
||||||
|
break;
|
||||||
|
|
||||||
|
double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time : lastObjectTime;
|
||||||
|
double duration = endTime - timingPoints[i].Time;
|
||||||
|
|
||||||
|
if (duration > maxDuration)
|
||||||
|
{
|
||||||
|
maxDuration = duration;
|
||||||
|
baseBeatLength = timingPoints[i].BeatLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge sequences of timing and difficulty control points to create the aggregate "multiplier" control point
|
||||||
var lastTimingPoint = new TimingControlPoint();
|
var lastTimingPoint = new TimingControlPoint();
|
||||||
var lastDifficultyPoint = new DifficultyControlPoint();
|
var lastDifficultyPoint = new DifficultyControlPoint();
|
||||||
|
|
||||||
// Merge timing + difficulty points
|
|
||||||
var allPoints = new SortedList<ControlPoint>(Comparer<ControlPoint>.Default);
|
var allPoints = new SortedList<ControlPoint>(Comparer<ControlPoint>.Default);
|
||||||
allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints);
|
allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints);
|
||||||
allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints);
|
allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints);
|
||||||
|
|
||||||
// Generate the timing points, making non-timing changes use the previous timing change
|
// Generate the timing points, making non-timing changes use the previous timing change and vice-versa
|
||||||
var timingChanges = allPoints.Select(c =>
|
var timingChanges = allPoints.Select(c =>
|
||||||
{
|
{
|
||||||
var timingPoint = c as TimingControlPoint;
|
var timingPoint = c as TimingControlPoint;
|
||||||
@ -131,14 +158,13 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
return new MultiplierControlPoint(c.Time)
|
return new MultiplierControlPoint(c.Time)
|
||||||
{
|
{
|
||||||
Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier,
|
Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier,
|
||||||
|
BaseBeatLength = baseBeatLength,
|
||||||
TimingPoint = lastTimingPoint,
|
TimingPoint = lastTimingPoint,
|
||||||
DifficultyPoint = lastDifficultyPoint
|
DifficultyPoint = lastDifficultyPoint
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
|
// Trim unwanted sequences of timing changes
|
||||||
|
|
||||||
// Perform some post processing of the timing changes
|
|
||||||
timingChanges = timingChanges
|
timingChanges = timingChanges
|
||||||
// Collapse sections after the last hit object
|
// Collapse sections after the last hit object
|
||||||
.Where(s => s.StartTime <= lastObjectTime)
|
.Where(s => s.StartTime <= lastObjectTime)
|
||||||
@ -147,7 +173,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
|
|
||||||
controlPoints.AddRange(timingChanges);
|
controlPoints.AddRange(timingChanges);
|
||||||
|
|
||||||
// If we have no control points, add a default one
|
|
||||||
if (controlPoints.Count == 0)
|
if (controlPoints.Count == 0)
|
||||||
controlPoints.Add(new MultiplierControlPoint { Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier });
|
controlPoints.Add(new MultiplierControlPoint { Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier });
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Framework.Input.States;
|
using osu.Framework.Input.States;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Edit.Tools;
|
using osu.Game.Rulesets.Edit.Tools;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components
|
namespace osu.Game.Screens.Edit.Compose.Components
|
||||||
@ -29,6 +30,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private HitObjectComposer composer { get; set; }
|
private HitObjectComposer composer { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IEditorBeatmap beatmap { get; set; }
|
||||||
|
|
||||||
public BlueprintContainer()
|
public BlueprintContainer()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
@ -53,7 +57,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
};
|
};
|
||||||
|
|
||||||
foreach (var obj in composer.HitObjects)
|
foreach (var obj in composer.HitObjects)
|
||||||
AddBlueprintFor(obj);
|
addBlueprintFor(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
beatmap.HitObjectAdded += addBlueprintFor;
|
||||||
|
beatmap.HitObjectRemoved += removeBlueprintFor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HitObjectCompositionTool currentTool;
|
private HitObjectCompositionTool currentTool;
|
||||||
@ -75,11 +87,32 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private void addBlueprintFor(HitObject hitObject)
|
||||||
/// Adds a blueprint for a <see cref="DrawableHitObject"/> which adds movement support.
|
{
|
||||||
/// </summary>
|
var drawable = composer.HitObjects.FirstOrDefault(d => d.HitObject == hitObject);
|
||||||
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to create a blueprint for.</param>
|
if (drawable == null)
|
||||||
public void AddBlueprintFor(DrawableHitObject hitObject)
|
return;
|
||||||
|
|
||||||
|
addBlueprintFor(drawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeBlueprintFor(HitObject hitObject)
|
||||||
|
{
|
||||||
|
var blueprint = selectionBlueprints.Single(m => m.HitObject.HitObject == hitObject);
|
||||||
|
if (blueprint == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
blueprint.Deselect();
|
||||||
|
|
||||||
|
blueprint.Selected -= onBlueprintSelected;
|
||||||
|
blueprint.Deselected -= onBlueprintDeselected;
|
||||||
|
blueprint.SelectionRequested -= onSelectionRequested;
|
||||||
|
blueprint.DragRequested -= onDragRequested;
|
||||||
|
|
||||||
|
selectionBlueprints.Remove(blueprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addBlueprintFor(DrawableHitObject hitObject)
|
||||||
{
|
{
|
||||||
refreshTool();
|
refreshTool();
|
||||||
|
|
||||||
@ -95,25 +128,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
selectionBlueprints.Add(blueprint);
|
selectionBlueprints.Add(blueprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private void removeBlueprintFor(DrawableHitObject hitObject) => removeBlueprintFor(hitObject.HitObject);
|
||||||
/// Removes a blueprint for a <see cref="DrawableHitObject"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hitObject">The <see cref="DrawableHitObject"/> for which to remove the blueprint.</param>
|
|
||||||
public void RemoveBlueprintFor(DrawableHitObject hitObject)
|
|
||||||
{
|
|
||||||
var blueprint = selectionBlueprints.Single(m => m.HitObject == hitObject);
|
|
||||||
if (blueprint == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
blueprint.Deselect();
|
|
||||||
|
|
||||||
blueprint.Selected -= onBlueprintSelected;
|
|
||||||
blueprint.Deselected -= onBlueprintDeselected;
|
|
||||||
blueprint.SelectionRequested -= onSelectionRequested;
|
|
||||||
blueprint.DragRequested -= onDragRequested;
|
|
||||||
|
|
||||||
selectionBlueprints.Remove(blueprint);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
@ -183,6 +198,17 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
private void onDragRequested(SelectionBlueprint blueprint, DragEvent dragEvent) => selectionHandler.HandleDrag(blueprint, dragEvent);
|
private void onDragRequested(SelectionBlueprint blueprint, DragEvent dragEvent) => selectionHandler.HandleDrag(blueprint, dragEvent);
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (beatmap != null)
|
||||||
|
{
|
||||||
|
beatmap.HitObjectAdded -= addBlueprintFor;
|
||||||
|
beatmap.HitObjectRemoved -= removeBlueprintFor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class SelectionBlueprintContainer : Container<SelectionBlueprint>
|
private class SelectionBlueprintContainer : Container<SelectionBlueprint>
|
||||||
{
|
{
|
||||||
protected override int Compare(Drawable x, Drawable y)
|
protected override int Compare(Drawable x, Drawable y)
|
||||||
|
@ -9,15 +9,13 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose
|
namespace osu.Game.Screens.Edit.Compose
|
||||||
{
|
{
|
||||||
[Cached(Type = typeof(IPlacementHandler))]
|
public class ComposeScreen : EditorScreen
|
||||||
public class ComposeScreen : EditorScreen, IPlacementHandler
|
|
||||||
{
|
{
|
||||||
private const float vertical_margins = 10;
|
private const float vertical_margins = 10;
|
||||||
private const float horizontal_margins = 20;
|
private const float horizontal_margins = 20;
|
||||||
@ -119,13 +117,5 @@ namespace osu.Game.Screens.Edit.Compose
|
|||||||
|
|
||||||
composerContainer.Child = composer;
|
composerContainer.Child = composer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeginPlacement(HitObject hitObject)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndPlacement(HitObject hitObject) => composer.Add(hitObject);
|
|
||||||
|
|
||||||
public void Delete(HitObject hitObject) => composer.Remove(hitObject);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
83
osu.Game/Screens/Edit/EditorBeatmap.cs
Normal file
83
osu.Game/Screens/Edit/EditorBeatmap.cs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// 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 System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit
|
||||||
|
{
|
||||||
|
public class EditorBeatmap<T> : IEditorBeatmap<T>
|
||||||
|
where T : HitObject
|
||||||
|
{
|
||||||
|
public event Action<HitObject> HitObjectAdded;
|
||||||
|
public event Action<HitObject> HitObjectRemoved;
|
||||||
|
|
||||||
|
private readonly Beatmap<T> beatmap;
|
||||||
|
|
||||||
|
public EditorBeatmap(Beatmap<T> beatmap)
|
||||||
|
{
|
||||||
|
this.beatmap = beatmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeatmapInfo BeatmapInfo
|
||||||
|
{
|
||||||
|
get => beatmap.BeatmapInfo;
|
||||||
|
set => beatmap.BeatmapInfo = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeatmapMetadata Metadata => beatmap.Metadata;
|
||||||
|
|
||||||
|
public ControlPointInfo ControlPointInfo => beatmap.ControlPointInfo;
|
||||||
|
|
||||||
|
public List<BreakPeriod> Breaks => beatmap.Breaks;
|
||||||
|
|
||||||
|
public double TotalBreakTime => beatmap.TotalBreakTime;
|
||||||
|
|
||||||
|
IReadOnlyList<T> IBeatmap<T>.HitObjects => beatmap.HitObjects;
|
||||||
|
|
||||||
|
IReadOnlyList<HitObject> IBeatmap.HitObjects => beatmap.HitObjects;
|
||||||
|
|
||||||
|
public IEnumerable<BeatmapStatistic> GetStatistics() => beatmap.GetStatistics();
|
||||||
|
|
||||||
|
public IBeatmap Clone() => (EditorBeatmap<T>)MemberwiseClone();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a <see cref="HitObject"/> to this <see cref="EditorBeatmap{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hitObject">The <see cref="HitObject"/> to add.</param>
|
||||||
|
public void Add(T hitObject)
|
||||||
|
{
|
||||||
|
// Preserve existing sorting order in the beatmap
|
||||||
|
var insertionIndex = beatmap.HitObjects.FindLastIndex(h => h.StartTime <= hitObject.StartTime);
|
||||||
|
beatmap.HitObjects.Insert(insertionIndex + 1, hitObject);
|
||||||
|
|
||||||
|
HitObjectAdded?.Invoke(hitObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a <see cref="HitObject"/> from this <see cref="EditorBeatmap{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hitObject">The <see cref="HitObject"/> to add.</param>
|
||||||
|
public void Remove(T hitObject)
|
||||||
|
{
|
||||||
|
if (beatmap.HitObjects.Remove(hitObject))
|
||||||
|
HitObjectRemoved?.Invoke(hitObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a <see cref="HitObject"/> to this <see cref="EditorBeatmap{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hitObject">The <see cref="HitObject"/> to add.</param>
|
||||||
|
public void Add(HitObject hitObject) => Add((T)hitObject);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a <see cref="HitObject"/> from this <see cref="EditorBeatmap{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hitObject">The <see cref="HitObject"/> to add.</param>
|
||||||
|
public void Remove(HitObject hitObject) => Remove((T)hitObject);
|
||||||
|
}
|
||||||
|
}
|
46
osu.Game/Screens/Edit/EditorWorkingBeatmap.cs
Normal file
46
osu.Game/Screens/Edit/EditorWorkingBeatmap.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// 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 System.Collections.Generic;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Storyboards;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Encapsulates a <see cref="WorkingBeatmap"/> while providing an overridden <see cref="Beatmap{TObject}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TObject"></typeparam>
|
||||||
|
public class EditorWorkingBeatmap<TObject> : IWorkingBeatmap
|
||||||
|
where TObject : HitObject
|
||||||
|
{
|
||||||
|
private readonly Beatmap<TObject> playableBeatmap;
|
||||||
|
private readonly WorkingBeatmap workingBeatmap;
|
||||||
|
|
||||||
|
public EditorWorkingBeatmap(Beatmap<TObject> playableBeatmap, WorkingBeatmap workingBeatmap)
|
||||||
|
{
|
||||||
|
this.playableBeatmap = playableBeatmap;
|
||||||
|
this.workingBeatmap = workingBeatmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBeatmap Beatmap => workingBeatmap.Beatmap;
|
||||||
|
|
||||||
|
public Texture Background => workingBeatmap.Background;
|
||||||
|
|
||||||
|
public Track Track => workingBeatmap.Track;
|
||||||
|
|
||||||
|
public Waveform Waveform => workingBeatmap.Waveform;
|
||||||
|
|
||||||
|
public Storyboard Storyboard => workingBeatmap.Storyboard;
|
||||||
|
|
||||||
|
public Skin Skin => workingBeatmap.Skin;
|
||||||
|
|
||||||
|
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods) => playableBeatmap;
|
||||||
|
}
|
||||||
|
}
|
36
osu.Game/Screens/Edit/IEditorBeatmap.cs
Normal file
36
osu.Game/Screens/Edit/IEditorBeatmap.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 System;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for the <see cref="IBeatmap"/> contained by the see <see cref="HitObjectComposer"/>.
|
||||||
|
/// Children of <see cref="HitObjectComposer"/> may resolve the beatmap via <see cref="IEditorBeatmap"/> or <see cref="IEditorBeatmap{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
public interface IEditorBeatmap : IBeatmap
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when a <see cref="HitObject"/> is added to this <see cref="IEditorBeatmap"/>.
|
||||||
|
/// </summary>
|
||||||
|
event Action<HitObject> HitObjectAdded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when a <see cref="HitObject"/> is removed from this <see cref="IEditorBeatmap"/>.
|
||||||
|
/// </summary>
|
||||||
|
event Action<HitObject> HitObjectRemoved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for the <see cref="IBeatmap"/> contained by the see <see cref="HitObjectComposer"/>.
|
||||||
|
/// Children of <see cref="HitObjectComposer"/> may resolve the beatmap via <see cref="IEditorBeatmap"/> or <see cref="IEditorBeatmap{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
public interface IEditorBeatmap<out T> : IEditorBeatmap, IBeatmap<T>
|
||||||
|
where T : HitObject
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
30
osu.Game/Utils/HumanizerUtils.cs
Normal file
30
osu.Game/Utils/HumanizerUtils.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// 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 System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Humanizer;
|
||||||
|
|
||||||
|
namespace osu.Game.Utils
|
||||||
|
{
|
||||||
|
public static class HumanizerUtils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Turns the current or provided date into a human readable sentence
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">The date to be humanized</param>
|
||||||
|
/// <returns>distance of time in words</returns>
|
||||||
|
public static string Humanize(DateTimeOffset input)
|
||||||
|
{
|
||||||
|
// this works around https://github.com/xamarin/xamarin-android/issues/2012 and https://github.com/Humanizr/Humanizer/issues/690#issuecomment-368536282
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return input.Humanize();
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
return input.Humanize(culture: new CultureInfo("en-US"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user