mirror of
https://github.com/osukey/osukey.git
synced 2025-06-05 12:57:39 +09:00
Merge branch 'master' into scoring-standardisation
This commit is contained in:
commit
bad48d6d44
@ -31,6 +31,9 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
|
|
||||||
public override Replay Generate()
|
public override Replay Generate()
|
||||||
{
|
{
|
||||||
|
if (Beatmap.HitObjects.Count == 0)
|
||||||
|
return Replay;
|
||||||
|
|
||||||
// todo: add support for HT DT
|
// todo: add support for HT DT
|
||||||
const double dash_speed = Catcher.BASE_SPEED;
|
const double dash_speed = Catcher.BASE_SPEED;
|
||||||
const double movement_speed = dash_speed / 2;
|
const double movement_speed = dash_speed / 2;
|
||||||
|
@ -16,7 +16,7 @@ using osu.Game.Rulesets.UI.Scrolling;
|
|||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||||
{
|
{
|
||||||
public abstract class ManiaPlacementBlueprintTestScene : PlacementBlueprintTestScene
|
public abstract class ManiaPlacementBlueprintTestScene : PlacementBlueprintTestScene
|
||||||
{
|
{
|
@ -8,7 +8,7 @@ using osu.Game.Rulesets.Mania.UI;
|
|||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||||
{
|
{
|
||||||
public abstract class ManiaSelectionBlueprintTestScene : SelectionBlueprintTestScene
|
public abstract class ManiaSelectionBlueprintTestScene : SelectionBlueprintTestScene
|
||||||
{
|
{
|
@ -8,7 +8,7 @@ using osu.Game.Rulesets.Mania.Configuration;
|
|||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneEditor : EditorTestScene
|
public class TestSceneEditor : EditorTestScene
|
@ -8,7 +8,7 @@ using osu.Game.Rulesets.Mania.Objects.Drawables;
|
|||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||||
{
|
{
|
||||||
public class TestSceneHoldNotePlacementBlueprint : ManiaPlacementBlueprintTestScene
|
public class TestSceneHoldNotePlacementBlueprint : ManiaPlacementBlueprintTestScene
|
||||||
{
|
{
|
@ -12,7 +12,7 @@ using osu.Game.Rulesets.Mania.Objects.Drawables;
|
|||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||||
{
|
{
|
||||||
public class TestSceneHoldNoteSelectionBlueprint : ManiaSelectionBlueprintTestScene
|
public class TestSceneHoldNoteSelectionBlueprint : ManiaSelectionBlueprintTestScene
|
||||||
{
|
{
|
@ -20,7 +20,7 @@ using osu.Game.Screens.Edit;
|
|||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||||
{
|
{
|
||||||
public class TestSceneManiaBeatSnapGrid : EditorClockTestScene
|
public class TestSceneManiaBeatSnapGrid : EditorClockTestScene
|
||||||
{
|
{
|
@ -23,7 +23,7 @@ using osu.Game.Tests.Visual;
|
|||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||||
{
|
{
|
||||||
public class TestSceneManiaHitObjectComposer : EditorClockTestScene
|
public class TestSceneManiaHitObjectComposer : EditorClockTestScene
|
||||||
{
|
{
|
@ -18,7 +18,7 @@ using osu.Game.Tests.Visual;
|
|||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||||
{
|
{
|
||||||
public class TestSceneNotePlacementBlueprint : ManiaPlacementBlueprintTestScene
|
public class TestSceneNotePlacementBlueprint : ManiaPlacementBlueprintTestScene
|
||||||
{
|
{
|
@ -12,7 +12,7 @@ using osu.Game.Rulesets.UI.Scrolling;
|
|||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||||
{
|
{
|
||||||
public class TestSceneNoteSelectionBlueprint : ManiaSelectionBlueprintTestScene
|
public class TestSceneNoteSelectionBlueprint : ManiaSelectionBlueprintTestScene
|
||||||
{
|
{
|
@ -46,6 +46,9 @@ namespace osu.Game.Rulesets.Mania.Replays
|
|||||||
|
|
||||||
public override Replay Generate()
|
public override Replay Generate()
|
||||||
{
|
{
|
||||||
|
if (Beatmap.HitObjects.Count == 0)
|
||||||
|
return Replay;
|
||||||
|
|
||||||
var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time);
|
var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time);
|
||||||
|
|
||||||
var actions = new List<ManiaAction>();
|
var actions = new List<ManiaAction>();
|
||||||
|
@ -9,7 +9,7 @@ using osu.Game.Rulesets.Osu.Objects;
|
|||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||||
{
|
{
|
||||||
public class TestSceneHitCirclePlacementBlueprint : PlacementBlueprintTestScene
|
public class TestSceneHitCirclePlacementBlueprint : PlacementBlueprintTestScene
|
||||||
{
|
{
|
@ -11,7 +11,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
|
|||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||||
{
|
{
|
||||||
public class TestSceneHitCircleSelectionBlueprint : SelectionBlueprintTestScene
|
public class TestSceneHitCircleSelectionBlueprint : SelectionBlueprintTestScene
|
||||||
{
|
{
|
@ -19,7 +19,7 @@ using osu.Game.Tests.Visual;
|
|||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||||
{
|
{
|
||||||
public class TestSceneOsuDistanceSnapGrid : OsuManualInputManagerTestScene
|
public class TestSceneOsuDistanceSnapGrid : OsuManualInputManagerTestScene
|
||||||
{
|
{
|
@ -12,7 +12,7 @@ using osu.Game.Rulesets.Osu.Objects;
|
|||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||||
{
|
{
|
||||||
public class TestScenePathControlPointVisualiser : OsuTestScene
|
public class TestScenePathControlPointVisualiser : OsuTestScene
|
||||||
{
|
{
|
@ -14,7 +14,7 @@ using osu.Game.Tests.Visual;
|
|||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||||
{
|
{
|
||||||
public class TestSceneSliderPlacementBlueprint : PlacementBlueprintTestScene
|
public class TestSceneSliderPlacementBlueprint : PlacementBlueprintTestScene
|
||||||
{
|
{
|
@ -16,7 +16,7 @@ using osu.Game.Tests.Visual;
|
|||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||||
{
|
{
|
||||||
public class TestSceneSliderSelectionBlueprint : SelectionBlueprintTestScene
|
public class TestSceneSliderSelectionBlueprint : SelectionBlueprintTestScene
|
||||||
{
|
{
|
@ -9,7 +9,7 @@ using osu.Game.Rulesets.Osu.Objects;
|
|||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||||
{
|
{
|
||||||
public class TestSceneSpinnerPlacementBlueprint : PlacementBlueprintTestScene
|
public class TestSceneSpinnerPlacementBlueprint : PlacementBlueprintTestScene
|
||||||
{
|
{
|
@ -11,7 +11,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
|
|||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||||
{
|
{
|
||||||
public class TestSceneSpinnerSelectionBlueprint : SelectionBlueprintTestScene
|
public class TestSceneSpinnerSelectionBlueprint : SelectionBlueprintTestScene
|
||||||
{
|
{
|
@ -8,6 +8,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -20,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
/// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay.
|
/// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay.
|
||||||
/// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points.
|
/// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const double editor_hit_object_fade_out_extension = 500;
|
private const double editor_hit_object_fade_out_extension = 700;
|
||||||
|
|
||||||
public DrawableOsuEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableOsuEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
@ -32,20 +33,37 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
private void updateState(DrawableHitObject hitObject, ArmedState state)
|
private void updateState(DrawableHitObject hitObject, ArmedState state)
|
||||||
{
|
{
|
||||||
switch (state)
|
if (state == ArmedState.Idle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// adjust the visuals of certain object types to make them stay on screen for longer than usual.
|
||||||
|
switch (hitObject)
|
||||||
{
|
{
|
||||||
case ArmedState.Miss:
|
default:
|
||||||
// Get the existing fade out transform
|
// there are quite a few drawable hit types we don't want to extent (spinners, ticks etc.)
|
||||||
var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha));
|
return;
|
||||||
if (existing == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
hitObject.RemoveTransform(existing);
|
case DrawableSlider _:
|
||||||
|
// no specifics to sliders but let them fade slower below.
|
||||||
|
break;
|
||||||
|
|
||||||
using (hitObject.BeginAbsoluteSequence(existing.StartTime))
|
case DrawableHitCircle circle: // also handles slider heads
|
||||||
hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire();
|
circle.ApproachCircle
|
||||||
|
.FadeOutFromOne(editor_hit_object_fade_out_extension)
|
||||||
|
.Expire();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the existing fade out transform
|
||||||
|
var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha));
|
||||||
|
|
||||||
|
if (existing == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hitObject.RemoveTransform(existing);
|
||||||
|
|
||||||
|
using (hitObject.BeginAbsoluteSequence(existing.StartTime))
|
||||||
|
hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Playfield CreatePlayfield() => new OsuPlayfieldNoCursor();
|
protected override Playfield CreatePlayfield() => new OsuPlayfieldNoCursor();
|
||||||
|
@ -9,7 +9,9 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Caching;
|
using osu.Framework.Caching;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
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.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -17,6 +19,7 @@ using osu.Game.Rulesets.Objects;
|
|||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Edit.Components.TernaryButtons;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -39,11 +42,11 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
new SpinnerCompositionTool()
|
new SpinnerCompositionTool()
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly BindableBool distanceSnapToggle = new BindableBool(true) { Description = "Distance Snap" };
|
private readonly Bindable<TernaryState> distanceSnapToggle = new Bindable<TernaryState>();
|
||||||
|
|
||||||
protected override IEnumerable<Bindable<bool>> Toggles => base.Toggles.Concat(new[]
|
protected override IEnumerable<TernaryButton> CreateTernaryButtons() => base.CreateTernaryButtons().Concat(new[]
|
||||||
{
|
{
|
||||||
distanceSnapToggle
|
new TernaryButton(distanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler })
|
||||||
});
|
});
|
||||||
|
|
||||||
private BindableList<HitObject> selectedHitObjects;
|
private BindableList<HitObject> selectedHitObjects;
|
||||||
@ -156,7 +159,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
distanceSnapGridCache.Invalidate();
|
distanceSnapGridCache.Invalidate();
|
||||||
distanceSnapGrid = null;
|
distanceSnapGrid = null;
|
||||||
|
|
||||||
if (!distanceSnapToggle.Value)
|
if (distanceSnapToggle.Value != TernaryState.True)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (BlueprintContainer.CurrentTool)
|
switch (BlueprintContainer.CurrentTool)
|
||||||
|
@ -72,6 +72,9 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
|
|
||||||
public override Replay Generate()
|
public override Replay Generate()
|
||||||
{
|
{
|
||||||
|
if (Beatmap.HitObjects.Count == 0)
|
||||||
|
return Replay;
|
||||||
|
|
||||||
buttonIndex = 0;
|
buttonIndex = 0;
|
||||||
|
|
||||||
AddFrameToReplay(new OsuReplayFrame(-100000, new Vector2(256, 500)));
|
AddFrameToReplay(new OsuReplayFrame(-100000, new Vector2(256, 500)));
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
using osu.Game.Rulesets.Taiko.UI;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Tests
|
||||||
|
{
|
||||||
|
public abstract class DrawableTaikoRulesetTestScene : OsuTestScene
|
||||||
|
{
|
||||||
|
protected DrawableTaikoRuleset DrawableRuleset { get; private set; }
|
||||||
|
protected Container PlayfieldContainer { get; private set; }
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
var controlPointInfo = new ControlPointInfo();
|
||||||
|
controlPointInfo.Add(0, new TimingControlPoint());
|
||||||
|
|
||||||
|
WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = new List<HitObject> { new Hit { Type = HitType.Centre } },
|
||||||
|
BeatmapInfo = new BeatmapInfo
|
||||||
|
{
|
||||||
|
BaseDifficulty = new BeatmapDifficulty(),
|
||||||
|
Metadata = new BeatmapMetadata
|
||||||
|
{
|
||||||
|
Artist = @"Unknown",
|
||||||
|
Title = @"Sample Beatmap",
|
||||||
|
AuthorString = @"peppy",
|
||||||
|
},
|
||||||
|
Ruleset = new TaikoRuleset().RulesetInfo
|
||||||
|
},
|
||||||
|
ControlPointInfo = controlPointInfo
|
||||||
|
});
|
||||||
|
|
||||||
|
Add(PlayfieldContainer = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = 768,
|
||||||
|
Children = new[] { DrawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap.GetPlayableBeatmap(new TaikoRuleset().RulesetInfo)) }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,26 +2,36 @@
|
|||||||
// 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 osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Tests
|
namespace osu.Game.Rulesets.Taiko.Tests
|
||||||
{
|
{
|
||||||
internal class DrawableTestHit : DrawableTaikoHitObject
|
public class DrawableTestHit : DrawableHit
|
||||||
{
|
{
|
||||||
private readonly HitResult type;
|
public readonly HitResult Type;
|
||||||
|
|
||||||
public DrawableTestHit(Hit hit, HitResult type = HitResult.Great)
|
public DrawableTestHit(Hit hit, HitResult type = HitResult.Great)
|
||||||
: base(hit)
|
: base(hit)
|
||||||
{
|
{
|
||||||
this.type = type;
|
Type = type;
|
||||||
|
|
||||||
|
HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateInitialTransforms()
|
||||||
|
{
|
||||||
|
// base implementation in DrawableHitObject forces alpha to 1.
|
||||||
|
// suppress locally to allow hiding the visuals wherever necessary.
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Result.Type = type;
|
Result.Type = Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnPressed(TaikoAction action) => false;
|
public override bool OnPressed(TaikoAction action) => false;
|
||||||
|
@ -2,17 +2,14 @@
|
|||||||
// 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.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Tests
|
namespace osu.Game.Rulesets.Taiko.Tests
|
||||||
{
|
{
|
||||||
public class DrawableTestStrongHit : DrawableHit
|
public class DrawableTestStrongHit : DrawableTestHit
|
||||||
{
|
{
|
||||||
private readonly HitResult type;
|
|
||||||
private readonly bool hitBoth;
|
private readonly bool hitBoth;
|
||||||
|
|
||||||
public DrawableTestStrongHit(double startTime, HitResult type = HitResult.Great, bool hitBoth = true)
|
public DrawableTestStrongHit(double startTime, HitResult type = HitResult.Great, bool hitBoth = true)
|
||||||
@ -20,12 +17,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
{
|
{
|
||||||
IsStrong = true,
|
IsStrong = true,
|
||||||
StartTime = startTime,
|
StartTime = startTime,
|
||||||
})
|
}, type)
|
||||||
{
|
{
|
||||||
// in order to create nested strong hit
|
|
||||||
HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
||||||
|
|
||||||
this.type = type;
|
|
||||||
this.hitBoth = hitBoth;
|
this.hitBoth = hitBoth;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,10 +26,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
{
|
{
|
||||||
base.LoadAsyncComplete();
|
base.LoadAsyncComplete();
|
||||||
|
|
||||||
Result.Type = type;
|
|
||||||
|
|
||||||
var nestedStrongHit = (DrawableStrongNestedHit)NestedHitObjects.Single();
|
var nestedStrongHit = (DrawableStrongNestedHit)NestedHitObjects.Single();
|
||||||
nestedStrongHit.Result.Type = hitBoth ? type : HitResult.Miss;
|
nestedStrongHit.Result.Type = hitBoth ? Type : HitResult.Miss;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnPressed(TaikoAction action) => false;
|
public override bool OnPressed(TaikoAction action) => false;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Tests
|
namespace osu.Game.Rulesets.Taiko.Tests.Editor
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneEditor : EditorTestScene
|
public class TestSceneEditor : EditorTestScene
|
@ -12,7 +12,7 @@ using osu.Game.Rulesets.Taiko.Objects;
|
|||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Tests
|
namespace osu.Game.Rulesets.Taiko.Tests.Editor
|
||||||
{
|
{
|
||||||
public class TestSceneTaikoHitObjectComposer : EditorClockTestScene
|
public class TestSceneTaikoHitObjectComposer : EditorClockTestScene
|
||||||
{
|
{
|
@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
foreach (var playfield in playfields)
|
foreach (var playfield in playfields)
|
||||||
{
|
{
|
||||||
var hit = new DrawableTestHit(new Hit(), judgementResult.Type);
|
var hit = new DrawableTestHit(new Hit(), judgementResult.Type);
|
||||||
Add(hit);
|
playfield.Add(hit);
|
||||||
|
|
||||||
playfield.OnNewResult(hit, judgementResult);
|
playfield.OnNewResult(hit, judgementResult);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Taiko.UI;
|
using osu.Game.Rulesets.Taiko.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||||
@ -29,15 +28,17 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
AddStep("Good", () => SetContents(() => getContentFor(createStrongHit(HitResult.Good, hitBoth))));
|
AddStep("Good", () => SetContents(() => getContentFor(createStrongHit(HitResult.Good, hitBoth))));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable getContentFor(DrawableTaikoHitObject hit)
|
private Drawable getContentFor(DrawableTestHit hit)
|
||||||
{
|
{
|
||||||
return new Container
|
return new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
hit,
|
// the hit needs to be added to hierarchy in order for nested objects to be created correctly.
|
||||||
new HitExplosion(hit)
|
// setting zero alpha is supposed to prevent the test from looking broken.
|
||||||
|
hit.With(h => h.Alpha = 0),
|
||||||
|
new HitExplosion(hit, hit.Type)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -46,9 +47,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private DrawableTaikoHitObject createHit(HitResult type) => new DrawableTestHit(new Hit { StartTime = Time.Current }, type);
|
private DrawableTestHit createHit(HitResult type) => new DrawableTestHit(new Hit { StartTime = Time.Current }, type);
|
||||||
|
|
||||||
private DrawableTaikoHitObject createStrongHit(HitResult type, bool hitBoth)
|
private DrawableTestHit createStrongHit(HitResult type, bool hitBoth) => new DrawableTestStrongHit(Time.Current, type, hitBoth);
|
||||||
=> new DrawableTestStrongHit(Time.Current, type, hitBoth);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
48
osu.Game.Rulesets.Taiko.Tests/TestSceneFlyingHits.cs
Normal file
48
osu.Game.Rulesets.Taiko.Tests/TestSceneFlyingHits.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
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;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneFlyingHits : DrawableTaikoRulesetTestScene
|
||||||
|
{
|
||||||
|
[TestCase(HitType.Centre)]
|
||||||
|
[TestCase(HitType.Rim)]
|
||||||
|
public void TestFlyingHits(HitType hitType)
|
||||||
|
{
|
||||||
|
DrawableFlyingHit flyingHit = null;
|
||||||
|
|
||||||
|
AddStep("add flying hit", () =>
|
||||||
|
{
|
||||||
|
addFlyingHit(hitType);
|
||||||
|
|
||||||
|
// flying hits all land in one common scrolling container (and stay there for rewind purposes),
|
||||||
|
// so we need to manually get the latest one.
|
||||||
|
flyingHit = this.ChildrenOfType<DrawableFlyingHit>()
|
||||||
|
.OrderByDescending(h => h.HitObject.StartTime)
|
||||||
|
.FirstOrDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("hit type is correct", () => flyingHit.HitObject.Type == hitType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addFlyingHit(HitType hitType)
|
||||||
|
{
|
||||||
|
var tick = new DrumRollTick { HitWindows = HitWindows.Empty, StartTime = DrawableRuleset.Playfield.Time.Current };
|
||||||
|
|
||||||
|
DrawableDrumRollTick h;
|
||||||
|
DrawableRuleset.Playfield.Add(h = new DrawableDrumRollTick(tick) { JudgementType = hitType });
|
||||||
|
((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(tick, new TaikoDrumRollTickJudgement()) { Type = HitResult.Perfect });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,11 +2,9 @@
|
|||||||
// 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 System.Collections.Generic;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -18,13 +16,12 @@ using osu.Game.Rulesets.Taiko.Judgements;
|
|||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Taiko.UI;
|
using osu.Game.Rulesets.Taiko.UI;
|
||||||
using osu.Game.Tests.Visual;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Tests
|
namespace osu.Game.Rulesets.Taiko.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneHits : OsuTestScene
|
public class TestSceneHits : DrawableTaikoRulesetTestScene
|
||||||
{
|
{
|
||||||
private const double default_duration = 3000;
|
private const double default_duration = 3000;
|
||||||
private const float scroll_time = 1000;
|
private const float scroll_time = 1000;
|
||||||
@ -32,8 +29,6 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
protected override double TimePerAction => default_duration * 2;
|
protected override double TimePerAction => default_duration * 2;
|
||||||
|
|
||||||
private readonly Random rng = new Random(1337);
|
private readonly Random rng = new Random(1337);
|
||||||
private DrawableTaikoRuleset drawableRuleset;
|
|
||||||
private Container playfieldContainer;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
@ -64,35 +59,6 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
AddStep("Height test 4", () => changePlayfieldSize(4));
|
AddStep("Height test 4", () => changePlayfieldSize(4));
|
||||||
AddStep("Height test 5", () => changePlayfieldSize(5));
|
AddStep("Height test 5", () => changePlayfieldSize(5));
|
||||||
AddStep("Reset height", () => changePlayfieldSize(6));
|
AddStep("Reset height", () => changePlayfieldSize(6));
|
||||||
|
|
||||||
var controlPointInfo = new ControlPointInfo();
|
|
||||||
controlPointInfo.Add(0, new TimingControlPoint());
|
|
||||||
|
|
||||||
WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap
|
|
||||||
{
|
|
||||||
HitObjects = new List<HitObject> { new Hit { Type = HitType.Centre } },
|
|
||||||
BeatmapInfo = new BeatmapInfo
|
|
||||||
{
|
|
||||||
BaseDifficulty = new BeatmapDifficulty(),
|
|
||||||
Metadata = new BeatmapMetadata
|
|
||||||
{
|
|
||||||
Artist = @"Unknown",
|
|
||||||
Title = @"Sample Beatmap",
|
|
||||||
AuthorString = @"peppy",
|
|
||||||
},
|
|
||||||
Ruleset = new TaikoRuleset().RulesetInfo
|
|
||||||
},
|
|
||||||
ControlPointInfo = controlPointInfo
|
|
||||||
});
|
|
||||||
|
|
||||||
Add(playfieldContainer = new Container
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = 768,
|
|
||||||
Children = new[] { drawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap.GetPlayableBeatmap(new TaikoRuleset().RulesetInfo)) }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void changePlayfieldSize(int step)
|
private void changePlayfieldSize(int step)
|
||||||
@ -128,11 +94,11 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
switch (step)
|
switch (step)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, rng.Next(25, 400)), 500);
|
PlayfieldContainer.Delay(delay).ResizeTo(new Vector2(1, rng.Next(25, 400)), 500);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, TaikoPlayfield.DEFAULT_HEIGHT), 500);
|
PlayfieldContainer.Delay(delay).ResizeTo(new Vector2(1, TaikoPlayfield.DEFAULT_HEIGHT), 500);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,9 +115,9 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
|
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
|
||||||
|
|
||||||
Add(h);
|
DrawableRuleset.Playfield.Add(h);
|
||||||
|
|
||||||
((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult });
|
((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult });
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addStrongHitJudgement(bool kiai)
|
private void addStrongHitJudgement(bool kiai)
|
||||||
@ -166,37 +132,37 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
|
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
|
||||||
|
|
||||||
Add(h);
|
DrawableRuleset.Playfield.Add(h);
|
||||||
|
|
||||||
((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult });
|
((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult });
|
||||||
((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new HitObject(), new TaikoStrongJudgement()) { Type = HitResult.Great });
|
((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new HitObject(), new TaikoStrongJudgement()) { Type = HitResult.Great });
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addMissJudgement()
|
private void addMissJudgement()
|
||||||
{
|
{
|
||||||
DrawableTestHit h;
|
DrawableTestHit h;
|
||||||
Add(h = new DrawableTestHit(new Hit(), HitResult.Miss));
|
DrawableRuleset.Playfield.Add(h = new DrawableTestHit(new Hit(), HitResult.Miss));
|
||||||
((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = HitResult.Miss });
|
((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = HitResult.Miss });
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addBarLine(bool major, double delay = scroll_time)
|
private void addBarLine(bool major, double delay = scroll_time)
|
||||||
{
|
{
|
||||||
BarLine bl = new BarLine { StartTime = drawableRuleset.Playfield.Time.Current + delay };
|
BarLine bl = new BarLine { StartTime = DrawableRuleset.Playfield.Time.Current + delay };
|
||||||
|
|
||||||
drawableRuleset.Playfield.Add(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl));
|
DrawableRuleset.Playfield.Add(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSwell(double duration = default_duration)
|
private void addSwell(double duration = default_duration)
|
||||||
{
|
{
|
||||||
var swell = new Swell
|
var swell = new Swell
|
||||||
{
|
{
|
||||||
StartTime = drawableRuleset.Playfield.Time.Current + scroll_time,
|
StartTime = DrawableRuleset.Playfield.Time.Current + scroll_time,
|
||||||
Duration = duration,
|
Duration = duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
swell.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
swell.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
drawableRuleset.Playfield.Add(new DrawableSwell(swell));
|
DrawableRuleset.Playfield.Add(new DrawableSwell(swell));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addDrumRoll(bool strong, double duration = default_duration, bool kiai = false)
|
private void addDrumRoll(bool strong, double duration = default_duration, bool kiai = false)
|
||||||
@ -206,7 +172,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
var d = new DrumRoll
|
var d = new DrumRoll
|
||||||
{
|
{
|
||||||
StartTime = drawableRuleset.Playfield.Time.Current + scroll_time,
|
StartTime = DrawableRuleset.Playfield.Time.Current + scroll_time,
|
||||||
IsStrong = strong,
|
IsStrong = strong,
|
||||||
Duration = duration,
|
Duration = duration,
|
||||||
TickRate = 8,
|
TickRate = 8,
|
||||||
@ -217,33 +183,33 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
d.ApplyDefaults(cpi, new BeatmapDifficulty());
|
d.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
drawableRuleset.Playfield.Add(new DrawableDrumRoll(d));
|
DrawableRuleset.Playfield.Add(new DrawableDrumRoll(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addCentreHit(bool strong)
|
private void addCentreHit(bool strong)
|
||||||
{
|
{
|
||||||
Hit h = new Hit
|
Hit h = new Hit
|
||||||
{
|
{
|
||||||
StartTime = drawableRuleset.Playfield.Time.Current + scroll_time,
|
StartTime = DrawableRuleset.Playfield.Time.Current + scroll_time,
|
||||||
IsStrong = strong
|
IsStrong = strong
|
||||||
};
|
};
|
||||||
|
|
||||||
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
drawableRuleset.Playfield.Add(new DrawableHit(h));
|
DrawableRuleset.Playfield.Add(new DrawableHit(h));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addRimHit(bool strong)
|
private void addRimHit(bool strong)
|
||||||
{
|
{
|
||||||
Hit h = new Hit
|
Hit h = new Hit
|
||||||
{
|
{
|
||||||
StartTime = drawableRuleset.Playfield.Time.Current + scroll_time,
|
StartTime = DrawableRuleset.Playfield.Time.Current + scroll_time,
|
||||||
IsStrong = strong
|
IsStrong = strong
|
||||||
};
|
};
|
||||||
|
|
||||||
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
drawableRuleset.Playfield.Add(new DrawableHit(h));
|
DrawableRuleset.Playfield.Add(new DrawableHit(h));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestStrongNestedHit : DrawableStrongNestedHit
|
private class TestStrongNestedHit : DrawableStrongNestedHit
|
||||||
|
@ -57,7 +57,13 @@ namespace osu.Game.Rulesets.Taiko.Edit
|
|||||||
ChangeHandler.BeginChange();
|
ChangeHandler.BeginChange();
|
||||||
|
|
||||||
foreach (var h in hits)
|
foreach (var h in hits)
|
||||||
h.IsStrong = state;
|
{
|
||||||
|
if (h.IsStrong != state)
|
||||||
|
{
|
||||||
|
h.IsStrong = state;
|
||||||
|
EditorBeatmap.UpdateHitObject(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ChangeHandler.EndChange();
|
ChangeHandler.EndChange();
|
||||||
}
|
}
|
||||||
|
@ -27,5 +27,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadSamples()
|
||||||
|
{
|
||||||
|
// block base call - flying hits are not supposed to play samples
|
||||||
|
// the base call could overwrite the type of this hit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,9 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
|||||||
|
|
||||||
public override Replay Generate()
|
public override Replay Generate()
|
||||||
{
|
{
|
||||||
|
if (Beatmap.HitObjects.Count == 0)
|
||||||
|
return Replay;
|
||||||
|
|
||||||
bool hitButton = true;
|
bool hitButton = true;
|
||||||
|
|
||||||
Frames.Add(new TaikoReplayFrame(-100000));
|
Frames.Add(new TaikoReplayFrame(-100000));
|
||||||
|
@ -15,8 +15,14 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
{
|
{
|
||||||
internal class DefaultHitExplosion : CircularContainer
|
internal class DefaultHitExplosion : CircularContainer
|
||||||
{
|
{
|
||||||
[Resolved]
|
private readonly DrawableHitObject judgedObject;
|
||||||
private DrawableHitObject judgedObject { get; set; }
|
private readonly HitResult result;
|
||||||
|
|
||||||
|
public DefaultHitExplosion(DrawableHitObject judgedObject, HitResult result)
|
||||||
|
{
|
||||||
|
this.judgedObject = judgedObject;
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
@ -31,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
Alpha = 0.15f;
|
Alpha = 0.15f;
|
||||||
Masking = true;
|
Masking = true;
|
||||||
|
|
||||||
if (judgedObject.Result.Type == HitResult.Miss)
|
if (result == HitResult.Miss)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim;
|
bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim;
|
||||||
|
@ -25,15 +25,18 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
[Cached(typeof(DrawableHitObject))]
|
[Cached(typeof(DrawableHitObject))]
|
||||||
public readonly DrawableHitObject JudgedObject;
|
public readonly DrawableHitObject JudgedObject;
|
||||||
|
|
||||||
|
private readonly HitResult result;
|
||||||
|
|
||||||
private SkinnableDrawable skinnable;
|
private SkinnableDrawable skinnable;
|
||||||
|
|
||||||
public override double LifetimeStart => skinnable.Drawable.LifetimeStart;
|
public override double LifetimeStart => skinnable.Drawable.LifetimeStart;
|
||||||
|
|
||||||
public override double LifetimeEnd => skinnable.Drawable.LifetimeEnd;
|
public override double LifetimeEnd => skinnable.Drawable.LifetimeEnd;
|
||||||
|
|
||||||
public HitExplosion(DrawableHitObject judgedObject)
|
public HitExplosion(DrawableHitObject judgedObject, HitResult result)
|
||||||
{
|
{
|
||||||
JudgedObject = judgedObject;
|
JudgedObject = judgedObject;
|
||||||
|
this.result = result;
|
||||||
|
|
||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
@ -47,14 +50,12 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(JudgedObject)), _ => new DefaultHitExplosion());
|
Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(JudgedObject)), _ => new DefaultHitExplosion(JudgedObject, result));
|
||||||
}
|
}
|
||||||
|
|
||||||
private TaikoSkinComponents getComponentName(DrawableHitObject judgedObject)
|
private TaikoSkinComponents getComponentName(DrawableHitObject judgedObject)
|
||||||
{
|
{
|
||||||
var resultType = judgedObject.Result?.Type ?? HitResult.Great;
|
switch (result)
|
||||||
|
|
||||||
switch (resultType)
|
|
||||||
{
|
{
|
||||||
case HitResult.Miss:
|
case HitResult.Miss:
|
||||||
return TaikoSkinComponents.TaikoExplosionMiss;
|
return TaikoSkinComponents.TaikoExplosionMiss;
|
||||||
|
@ -9,6 +9,7 @@ using osu.Game.Beatmaps.ControlPoints;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
@ -206,8 +207,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
});
|
});
|
||||||
|
|
||||||
var type = (judgedObject.HitObject as Hit)?.Type ?? HitType.Centre;
|
var type = (judgedObject.HitObject as Hit)?.Type ?? HitType.Centre;
|
||||||
|
addExplosion(judgedObject, result.Type, type);
|
||||||
addExplosion(judgedObject, type);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,9 +219,9 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
/// As legacy skins have different explosions for singular and double strong hits,
|
/// As legacy skins have different explosions for singular and double strong hits,
|
||||||
/// explosion addition is scheduled to ensure that both hits are processed if they occur on the same frame.
|
/// explosion addition is scheduled to ensure that both hits are processed if they occur on the same frame.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private void addExplosion(DrawableHitObject drawableObject, HitType type) => Schedule(() =>
|
private void addExplosion(DrawableHitObject drawableObject, HitResult result, HitType type) => Schedule(() =>
|
||||||
{
|
{
|
||||||
hitExplosionContainer.Add(new HitExplosion(drawableObject));
|
hitExplosionContainer.Add(new HitExplosion(drawableObject, result));
|
||||||
if (drawableObject.HitObject.Kiai)
|
if (drawableObject.HitObject.Kiai)
|
||||||
kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type));
|
kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type));
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
// 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 NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Editing
|
namespace osu.Game.Tests.Editing
|
||||||
@ -13,11 +15,12 @@ namespace osu.Game.Tests.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSaveRestoreState()
|
public void TestSaveRestoreState()
|
||||||
{
|
{
|
||||||
var handler = new EditorChangeHandler(new EditorBeatmap(new Beatmap()));
|
var (handler, beatmap) = createChangeHandler();
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
|
addArbitraryChange(beatmap);
|
||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.True);
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
@ -29,15 +32,48 @@ namespace osu.Game.Tests.Editing
|
|||||||
Assert.That(handler.CanRedo.Value, Is.True);
|
Assert.That(handler.CanRedo.Value, Is.True);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSaveSameStateDoesNotSave()
|
||||||
|
{
|
||||||
|
var (handler, beatmap) = createChangeHandler();
|
||||||
|
|
||||||
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
|
addArbitraryChange(beatmap);
|
||||||
|
handler.SaveState();
|
||||||
|
|
||||||
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
|
string hash = handler.CurrentStateHash;
|
||||||
|
|
||||||
|
// save a save without making any changes
|
||||||
|
handler.SaveState();
|
||||||
|
|
||||||
|
Assert.That(hash, Is.EqualTo(handler.CurrentStateHash));
|
||||||
|
|
||||||
|
handler.RestoreState(-1);
|
||||||
|
|
||||||
|
Assert.That(hash, Is.Not.EqualTo(handler.CurrentStateHash));
|
||||||
|
|
||||||
|
// we should only be able to restore once even though we saved twice.
|
||||||
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
|
Assert.That(handler.CanRedo.Value, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestMaxStatesSaved()
|
public void TestMaxStatesSaved()
|
||||||
{
|
{
|
||||||
var handler = new EditorChangeHandler(new EditorBeatmap(new Beatmap()));
|
var (handler, beatmap) = createChangeHandler();
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
|
|
||||||
for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++)
|
for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++)
|
||||||
|
{
|
||||||
|
addArbitraryChange(beatmap);
|
||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
|
}
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.True);
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
|
|
||||||
@ -53,12 +89,15 @@ namespace osu.Game.Tests.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestMaxStatesExceeded()
|
public void TestMaxStatesExceeded()
|
||||||
{
|
{
|
||||||
var handler = new EditorChangeHandler(new EditorBeatmap(new Beatmap()));
|
var (handler, beatmap) = createChangeHandler();
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
|
|
||||||
for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES * 2; i++)
|
for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES * 2; i++)
|
||||||
|
{
|
||||||
|
addArbitraryChange(beatmap);
|
||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
|
}
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.True);
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
|
|
||||||
@ -70,5 +109,17 @@ namespace osu.Game.Tests.Editing
|
|||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private (EditorChangeHandler, EditorBeatmap) createChangeHandler()
|
||||||
|
{
|
||||||
|
var beatmap = new EditorBeatmap(new Beatmap());
|
||||||
|
|
||||||
|
return (new EditorChangeHandler(beatmap), beatmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addArbitraryChange(EditorBeatmap beatmap)
|
||||||
|
{
|
||||||
|
beatmap.Add(new HitCircle { StartTime = RNG.Next(0, 100000) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
// 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 System.Threading;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
@ -175,6 +178,24 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
assertHealthNotEqualTo(0);
|
assertHealthNotEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSingleLongObjectDoesNotDrain()
|
||||||
|
{
|
||||||
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = { new JudgeableLongHitObject() }
|
||||||
|
};
|
||||||
|
|
||||||
|
beatmap.HitObjects[0].ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
|
createProcessor(beatmap);
|
||||||
|
setTime(0);
|
||||||
|
assertHealthEqualTo(1);
|
||||||
|
|
||||||
|
setTime(5000);
|
||||||
|
assertHealthEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
private Beatmap createBeatmap(double startTime, double endTime, params BreakPeriod[] breaks)
|
private Beatmap createBeatmap(double startTime, double endTime, params BreakPeriod[] breaks)
|
||||||
{
|
{
|
||||||
var beatmap = new Beatmap
|
var beatmap = new Beatmap
|
||||||
@ -235,5 +256,23 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class JudgeableLongHitObject : JudgeableHitObject, IHasDuration
|
||||||
|
{
|
||||||
|
public double EndTime => StartTime + Duration;
|
||||||
|
public double Duration { get; set; } = 5000;
|
||||||
|
|
||||||
|
public JudgeableLongHitObject()
|
||||||
|
: base(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
base.CreateNestedHitObjects(cancellationToken);
|
||||||
|
|
||||||
|
AddNested(new JudgeableHitObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
// 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.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Screens.Edit.Setup;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
using SharpCompress.Archives;
|
||||||
|
using SharpCompress.Archives.Zip;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
|
{
|
||||||
|
public class TestSceneEditorBeatmapCreation : EditorTestScene
|
||||||
|
{
|
||||||
|
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
|
||||||
|
|
||||||
|
protected override bool EditorComponentsReady => Editor.ChildrenOfType<SetupScreen>().SingleOrDefault()?.IsLoaded == true;
|
||||||
|
|
||||||
|
public override void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("set dummy", () => Beatmap.Value = new DummyWorkingBeatmap(Audio, null));
|
||||||
|
|
||||||
|
base.SetUpSteps();
|
||||||
|
|
||||||
|
// if we save a beatmap with a hash collision, things fall over.
|
||||||
|
// probably needs a more solid resolution in the future but this will do for now.
|
||||||
|
AddStep("make new beatmap unique", () => EditorBeatmap.Metadata.Title = Guid.NewGuid().ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCreateNewBeatmap()
|
||||||
|
{
|
||||||
|
AddStep("save beatmap", () => Editor.Save());
|
||||||
|
AddAssert("new beatmap persisted", () => EditorBeatmap.BeatmapInfo.ID > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddAudioTrack()
|
||||||
|
{
|
||||||
|
AddAssert("switch track to real track", () =>
|
||||||
|
{
|
||||||
|
var setup = Editor.ChildrenOfType<SetupScreen>().First();
|
||||||
|
|
||||||
|
var temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
|
string extractedFolder = $"{temp}_extracted";
|
||||||
|
Directory.CreateDirectory(extractedFolder);
|
||||||
|
|
||||||
|
using (var zip = ZipArchive.Open(temp))
|
||||||
|
zip.WriteToDirectory(extractedFolder);
|
||||||
|
|
||||||
|
bool success = setup.ChangeAudioTrack(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3"));
|
||||||
|
|
||||||
|
File.Delete(temp);
|
||||||
|
Directory.Delete(extractedFolder, true);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("track length changed", () => Beatmap.Value.Track.Length > 60000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
protected new OverlayTestPlayer Player => base.Player as OverlayTestPlayer;
|
protected new OverlayTestPlayer Player => base.Player as OverlayTestPlayer;
|
||||||
|
|
||||||
|
public override void SetUpSteps()
|
||||||
|
{
|
||||||
|
base.SetUpSteps();
|
||||||
|
|
||||||
|
AddUntilStep("gameplay has started",
|
||||||
|
() => Player.GameplayClockContainer.GameplayClock.CurrentTime > Player.DrawableRuleset.GameplayStartTime);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestGameplayOverlayActivation()
|
public void TestGameplayOverlayActivation()
|
||||||
{
|
{
|
||||||
@ -21,7 +29,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestGameplayOverlayActivationPaused()
|
public void TestGameplayOverlayActivationPaused()
|
||||||
{
|
{
|
||||||
AddUntilStep("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
|
AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
|
||||||
AddStep("pause gameplay", () => Player.Pause());
|
AddStep("pause gameplay", () => Player.Pause());
|
||||||
AddUntilStep("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered);
|
AddUntilStep("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered);
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,11 @@ namespace osu.Game.Audio
|
|||||||
public const string HIT_NORMAL = @"hitnormal";
|
public const string HIT_NORMAL = @"hitnormal";
|
||||||
public const string HIT_CLAP = @"hitclap";
|
public const string HIT_CLAP = @"hitclap";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All valid sample addition constants.
|
||||||
|
/// </summary>
|
||||||
|
public static IEnumerable<string> AllAdditions => new[] { HIT_WHISTLE, HIT_CLAP, HIT_FINISH };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The bank to load the sample from.
|
/// The bank to load the sample from.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -260,7 +260,7 @@ namespace osu.Game.Beatmaps
|
|||||||
fileInfo.Filename = beatmapInfo.Path;
|
fileInfo.Filename = beatmapInfo.Path;
|
||||||
|
|
||||||
stream.Seek(0, SeekOrigin.Begin);
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
UpdateFile(setInfo, fileInfo, stream);
|
ReplaceFile(setInfo, fileInfo, stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,12 +401,27 @@ namespace osu.Game.Database
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update an existing file, or create a new entry if not already part of the <paramref name="model"/>'s files.
|
/// Replace an existing file with a new version.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="model">The item to operate on.</param>
|
/// <param name="model">The item to operate on.</param>
|
||||||
/// <param name="file">The file model to be updated or added.</param>
|
/// <param name="file">The existing file to be replaced.</param>
|
||||||
/// <param name="contents">The new file contents.</param>
|
/// <param name="contents">The new file contents.</param>
|
||||||
public void UpdateFile(TModel model, TFileModel file, Stream contents)
|
/// <param name="filename">An optional filename for the new file. Will use the previous filename if not specified.</param>
|
||||||
|
public void ReplaceFile(TModel model, TFileModel file, Stream contents, string filename = null)
|
||||||
|
{
|
||||||
|
using (ContextFactory.GetForWrite())
|
||||||
|
{
|
||||||
|
DeleteFile(model, file);
|
||||||
|
AddFile(model, contents, filename ?? file.Filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete new file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The item to operate on.</param>
|
||||||
|
/// <param name="file">The existing file to be deleted.</param>
|
||||||
|
public void DeleteFile(TModel model, TFileModel file)
|
||||||
{
|
{
|
||||||
using (var usage = ContextFactory.GetForWrite())
|
using (var usage = ContextFactory.GetForWrite())
|
||||||
{
|
{
|
||||||
@ -415,15 +430,28 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
Files.Dereference(file.FileInfo);
|
Files.Dereference(file.FileInfo);
|
||||||
|
|
||||||
// Remove the file model.
|
// This shouldn't be required, but here for safety in case the provided TModel is not being change tracked
|
||||||
|
// Definitely can be removed once we rework the database backend.
|
||||||
usage.Context.Set<TFileModel>().Remove(file);
|
usage.Context.Set<TFileModel>().Remove(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the new file info and containing file model.
|
|
||||||
model.Files.Remove(file);
|
model.Files.Remove(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a new file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The item to operate on.</param>
|
||||||
|
/// <param name="contents">The new file contents.</param>
|
||||||
|
/// <param name="filename">The filename for the new file.</param>
|
||||||
|
public void AddFile(TModel model, Stream contents, string filename)
|
||||||
|
{
|
||||||
|
using (ContextFactory.GetForWrite())
|
||||||
|
{
|
||||||
model.Files.Add(new TFileModel
|
model.Files.Add(new TFileModel
|
||||||
{
|
{
|
||||||
Filename = file.Filename,
|
Filename = filename,
|
||||||
FileInfo = Files.Add(contents)
|
FileInfo = Files.Add(contents)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -112,6 +112,9 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
CornerRadius = 5;
|
CornerRadius = 5;
|
||||||
|
|
||||||
|
// needs to be set initially for the ResizeTo to respect minimum size
|
||||||
|
Size = new Vector2(SCROLL_BAR_HEIGHT);
|
||||||
|
|
||||||
const float margin = 3;
|
const float margin = 3;
|
||||||
|
|
||||||
Margin = new MarginPadding
|
Margin = new MarginPadding
|
||||||
|
@ -44,13 +44,18 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
|||||||
Component.BorderColour = colours.Blue;
|
Component.BorderColour = colours.Blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override OsuTextBox CreateComponent() => new OsuTextBox
|
protected virtual OsuTextBox CreateTextBox() => new OsuTextBox
|
||||||
{
|
{
|
||||||
CommitOnFocusLost = true,
|
CommitOnFocusLost = true,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
CornerRadius = CORNER_RADIUS,
|
CornerRadius = CORNER_RADIUS,
|
||||||
}.With(t => t.OnCommit += (sender, newText) => OnCommit?.Invoke(sender, newText));
|
};
|
||||||
|
|
||||||
|
protected override OsuTextBox CreateComponent() => CreateTextBox().With(t =>
|
||||||
|
{
|
||||||
|
t.OnCommit += (sender, newText) => OnCommit?.Invoke(sender, newText);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,11 @@ namespace osu.Game.Overlays
|
|||||||
mods.BindValueChanged(_ => ResetTrackAdjustments(), true);
|
mods.BindValueChanged(_ => ResetTrackAdjustments(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Forcefully reload the current <see cref="WorkingBeatmap"/>'s track from disk.
|
||||||
|
/// </summary>
|
||||||
|
public void ReloadCurrentTrack() => changeTrack();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Change the position of a <see cref="BeatmapSetInfo"/> in the current playlist.
|
/// Change the position of a <see cref="BeatmapSetInfo"/> in the current playlist.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -45,15 +45,21 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
beatmap.HitObjectAdded += addHitObject;
|
beatmap.HitObjectAdded += addHitObject;
|
||||||
|
beatmap.HitObjectUpdated += updateReplay;
|
||||||
beatmap.HitObjectRemoved += removeHitObject;
|
beatmap.HitObjectRemoved += removeHitObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateReplay(HitObject obj = null) =>
|
||||||
|
drawableRuleset.RegenerateAutoplay();
|
||||||
|
|
||||||
private void addHitObject(HitObject hitObject)
|
private void addHitObject(HitObject hitObject)
|
||||||
{
|
{
|
||||||
var drawableObject = drawableRuleset.CreateDrawableRepresentation((TObject)hitObject);
|
var drawableObject = drawableRuleset.CreateDrawableRepresentation((TObject)hitObject);
|
||||||
|
|
||||||
drawableRuleset.Playfield.Add(drawableObject);
|
drawableRuleset.Playfield.Add(drawableObject);
|
||||||
drawableRuleset.Playfield.PostProcess();
|
drawableRuleset.Playfield.PostProcess();
|
||||||
|
|
||||||
|
updateReplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeHitObject(HitObject hitObject)
|
private void removeHitObject(HitObject hitObject)
|
||||||
@ -62,6 +68,8 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
drawableRuleset.Playfield.Remove(drawableObject);
|
drawableRuleset.Playfield.Remove(drawableObject);
|
||||||
drawableRuleset.Playfield.PostProcess();
|
drawableRuleset.Playfield.PostProcess();
|
||||||
|
|
||||||
|
drawableRuleset.RegenerateAutoplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool PropagatePositionalInputSubTree => false;
|
public override bool PropagatePositionalInputSubTree => false;
|
||||||
|
@ -6,7 +6,6 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
@ -14,7 +13,6 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Overlays.Settings;
|
|
||||||
using osu.Game.Rulesets.Configuration;
|
using osu.Game.Rulesets.Configuration;
|
||||||
using osu.Game.Rulesets.Edit.Tools;
|
using osu.Game.Rulesets.Edit.Tools;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -24,6 +22,7 @@ using osu.Game.Rulesets.UI;
|
|||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Components.RadioButtons;
|
using osu.Game.Screens.Edit.Components.RadioButtons;
|
||||||
|
using osu.Game.Screens.Edit.Components.TernaryButtons;
|
||||||
using osu.Game.Screens.Edit.Compose;
|
using osu.Game.Screens.Edit.Compose;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -63,7 +62,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
private RadioButtonCollection toolboxCollection;
|
private RadioButtonCollection toolboxCollection;
|
||||||
|
|
||||||
private ToolboxGroup togglesCollection;
|
private FillFlowContainer togglesCollection;
|
||||||
|
|
||||||
protected HitObjectComposer(Ruleset ruleset)
|
protected HitObjectComposer(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
@ -77,7 +76,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
drawableRulesetWrapper = new DrawableEditRulesetWrapper<TObject>(CreateDrawableRuleset(Ruleset, EditorBeatmap.PlayableBeatmap))
|
drawableRulesetWrapper = new DrawableEditRulesetWrapper<TObject>(CreateDrawableRuleset(Ruleset, EditorBeatmap.PlayableBeatmap, new[] { Ruleset.GetAutoplayMod() }))
|
||||||
{
|
{
|
||||||
Clock = EditorClock,
|
Clock = EditorClock,
|
||||||
ProcessCustomClock = false
|
ProcessCustomClock = false
|
||||||
@ -121,14 +120,19 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
Spacing = new Vector2(10),
|
Spacing = new Vector2(10),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new ToolboxGroup("toolbox") { Child = toolboxCollection = new RadioButtonCollection { RelativeSizeAxes = Axes.X } },
|
new ToolboxGroup("toolbox (1-9)")
|
||||||
togglesCollection = new ToolboxGroup("toggles")
|
|
||||||
{
|
{
|
||||||
ChildrenEnumerable = Toggles.Select(b => new SettingsCheckbox
|
Child = toolboxCollection = new RadioButtonCollection { RelativeSizeAxes = Axes.X }
|
||||||
|
},
|
||||||
|
new ToolboxGroup("toggles (Q~P)")
|
||||||
|
{
|
||||||
|
Child = togglesCollection = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Bindable = b,
|
RelativeSizeAxes = Axes.X,
|
||||||
LabelText = b?.Description ?? "unknown"
|
AutoSizeAxes = Axes.Y,
|
||||||
})
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(0, 5),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -139,6 +143,9 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
.Select(t => new RadioButton(t.Name, () => toolSelected(t), t.CreateIcon))
|
.Select(t => new RadioButton(t.Name, () => toolSelected(t), t.CreateIcon))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
TernaryStates = CreateTernaryButtons().ToArray();
|
||||||
|
togglesCollection.AddRange(TernaryStates.Select(b => new DrawableTernaryButton(b)));
|
||||||
|
|
||||||
setSelectTool();
|
setSelectTool();
|
||||||
|
|
||||||
EditorBeatmap.SelectedHitObjects.CollectionChanged += selectionChanged;
|
EditorBeatmap.SelectedHitObjects.CollectionChanged += selectionChanged;
|
||||||
@ -167,10 +174,14 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
protected abstract IReadOnlyList<HitObjectCompositionTool> CompositionTools { get; }
|
protected abstract IReadOnlyList<HitObjectCompositionTool> CompositionTools { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A collection of toggles which will be displayed to the user.
|
/// A collection of states which will be displayed to the user in the toolbox.
|
||||||
/// The display name will be decided by <see cref="Bindable{T}.Description"/>.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual IEnumerable<Bindable<bool>> Toggles => BlueprintContainer.Toggles;
|
public TernaryButton[] TernaryStates { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create all ternary states required to be displayed to the user.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual IEnumerable<TernaryButton> CreateTernaryButtons() => BlueprintContainer.TernaryStates;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Construct a relevant blueprint container. This will manage hitobject selection/placement input handling and display logic.
|
/// Construct a relevant blueprint container. This will manage hitobject selection/placement input handling and display logic.
|
||||||
@ -215,9 +226,9 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
{
|
{
|
||||||
var item = togglesCollection.ElementAtOrDefault(rightIndex);
|
var item = togglesCollection.ElementAtOrDefault(rightIndex);
|
||||||
|
|
||||||
if (item is SettingsCheckbox checkbox)
|
if (item is DrawableTernaryButton button)
|
||||||
{
|
{
|
||||||
checkbox.Bindable.Value = !checkbox.Bindable.Value;
|
button.Button.Toggle();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
|
|
||||||
private double computeDrainRate()
|
private double computeDrainRate()
|
||||||
{
|
{
|
||||||
if (healthIncreases.Count == 0)
|
if (healthIncreases.Count <= 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
int adjustment = 1;
|
int adjustment = 1;
|
||||||
|
@ -151,8 +151,11 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
public virtual PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new PlayfieldAdjustmentContainer();
|
public virtual PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new PlayfieldAdjustmentContainer();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuConfigManager config { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuConfigManager config, CancellationToken? cancellationToken)
|
private void load(CancellationToken? cancellationToken)
|
||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -178,11 +181,18 @@ namespace osu.Game.Rulesets.UI
|
|||||||
.WithChild(ResumeOverlay)));
|
.WithChild(ResumeOverlay)));
|
||||||
}
|
}
|
||||||
|
|
||||||
applyRulesetMods(Mods, config);
|
RegenerateAutoplay();
|
||||||
|
|
||||||
loadObjects(cancellationToken);
|
loadObjects(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RegenerateAutoplay()
|
||||||
|
{
|
||||||
|
// for now this is applying mods which aren't just autoplay.
|
||||||
|
// we'll need to reconsider this flow in the future.
|
||||||
|
applyRulesetMods(Mods, config);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates and adds drawable representations of hit objects to the play field.
|
/// Creates and adds drawable representations of hit objects to the play field.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -123,39 +123,63 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!FrameStablePlayback)
|
if (FrameStablePlayback)
|
||||||
return;
|
|
||||||
|
|
||||||
if (firstConsumption)
|
|
||||||
{
|
{
|
||||||
// On the first update, frame-stability seeking would result in unexpected/unwanted behaviour.
|
if (firstConsumption)
|
||||||
// Instead we perform an initial seek to the proposed time.
|
{
|
||||||
|
// On the first update, frame-stability seeking would result in unexpected/unwanted behaviour.
|
||||||
|
// Instead we perform an initial seek to the proposed time.
|
||||||
|
|
||||||
// process frame (in addition to finally clause) to clear out ElapsedTime
|
// process frame (in addition to finally clause) to clear out ElapsedTime
|
||||||
manualClock.CurrentTime = newProposedTime;
|
manualClock.CurrentTime = newProposedTime;
|
||||||
framedClock.ProcessFrame();
|
framedClock.ProcessFrame();
|
||||||
|
|
||||||
firstConsumption = false;
|
firstConsumption = false;
|
||||||
}
|
}
|
||||||
else if (manualClock.CurrentTime < gameplayStartTime)
|
else if (manualClock.CurrentTime < gameplayStartTime)
|
||||||
manualClock.CurrentTime = newProposedTime = Math.Min(gameplayStartTime, newProposedTime);
|
manualClock.CurrentTime = newProposedTime = Math.Min(gameplayStartTime, newProposedTime);
|
||||||
else if (Math.Abs(manualClock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f)
|
else if (Math.Abs(manualClock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f)
|
||||||
{
|
{
|
||||||
newProposedTime = newProposedTime > manualClock.CurrentTime
|
newProposedTime = newProposedTime > manualClock.CurrentTime
|
||||||
? Math.Min(newProposedTime, manualClock.CurrentTime + sixty_frame_time)
|
? Math.Min(newProposedTime, manualClock.CurrentTime + sixty_frame_time)
|
||||||
: Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time);
|
: Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAttached)
|
if (isAttached)
|
||||||
{
|
{
|
||||||
double? newTime = ReplayInputHandler.SetFrameFromTime(newProposedTime);
|
double? newTime;
|
||||||
|
|
||||||
if (newTime == null)
|
if (FrameStablePlayback)
|
||||||
{
|
{
|
||||||
// we shouldn't execute for this time value. probably waiting on more replay data.
|
// when stability is turned on, we shouldn't execute for time values the replay is unable to satisfy.
|
||||||
validState = false;
|
if ((newTime = ReplayInputHandler.SetFrameFromTime(newProposedTime)) == null)
|
||||||
requireMoreUpdateLoops = true;
|
{
|
||||||
return;
|
// setting invalid state here ensures that gameplay will not continue (ie. our child
|
||||||
|
// hierarchy won't be updated).
|
||||||
|
validState = false;
|
||||||
|
|
||||||
|
// potentially loop to catch-up playback.
|
||||||
|
requireMoreUpdateLoops = true;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// when stability is disabled, we don't really care about accuracy.
|
||||||
|
// looping over the replay will allow it to catch up and feed out the required values
|
||||||
|
// for the current time.
|
||||||
|
while ((newTime = ReplayInputHandler.SetFrameFromTime(newProposedTime)) != newProposedTime)
|
||||||
|
{
|
||||||
|
if (newTime == null)
|
||||||
|
{
|
||||||
|
// special case for when the replay actually can't arrive at the required time.
|
||||||
|
// protects from potential endless loop.
|
||||||
|
validState = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newProposedTime = newTime.Value;
|
newProposedTime = newTime.Value;
|
||||||
|
@ -18,7 +18,8 @@ namespace osu.Game.Screens.Edit.Components
|
|||||||
private const float contents_padding = 15;
|
private const float contents_padding = 15;
|
||||||
|
|
||||||
protected readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
protected readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
||||||
protected Track Track => Beatmap.Value.Track;
|
|
||||||
|
protected readonly IBindable<Track> Track = new Bindable<Track>();
|
||||||
|
|
||||||
private readonly Drawable background;
|
private readonly Drawable background;
|
||||||
private readonly Container content;
|
private readonly Container content;
|
||||||
@ -42,9 +43,11 @@ namespace osu.Game.Screens.Edit.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(IBindable<WorkingBeatmap> beatmap, OsuColour colours)
|
private void load(IBindable<WorkingBeatmap> beatmap, OsuColour colours, EditorClock clock)
|
||||||
{
|
{
|
||||||
Beatmap.BindTo(beatmap);
|
Beatmap.BindTo(beatmap);
|
||||||
|
Track.BindTo(clock.Track);
|
||||||
|
|
||||||
background.Colour = colours.Gray1;
|
background.Colour = colours.Gray1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,12 +62,12 @@ namespace osu.Game.Screens.Edit.Components
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Track?.AddAdjustment(AdjustableProperty.Tempo, tempo);
|
Track.BindValueChanged(tr => tr.NewValue?.AddAdjustment(AdjustableProperty.Tempo, tempo), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
Track?.RemoveAdjustment(AdjustableProperty.Tempo, tempo);
|
Track.Value?.RemoveAdjustment(AdjustableProperty.Tempo, tempo);
|
||||||
|
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,112 @@
|
|||||||
|
// 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 osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Effects;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Components.TernaryButtons
|
||||||
|
{
|
||||||
|
internal class DrawableTernaryButton : TriangleButton
|
||||||
|
{
|
||||||
|
private Color4 defaultBackgroundColour;
|
||||||
|
private Color4 defaultBubbleColour;
|
||||||
|
private Color4 selectedBackgroundColour;
|
||||||
|
private Color4 selectedBubbleColour;
|
||||||
|
|
||||||
|
private Drawable icon;
|
||||||
|
|
||||||
|
public readonly TernaryButton Button;
|
||||||
|
|
||||||
|
public DrawableTernaryButton(TernaryButton button)
|
||||||
|
{
|
||||||
|
Button = button;
|
||||||
|
|
||||||
|
Text = button.Description;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
defaultBackgroundColour = colours.Gray3;
|
||||||
|
defaultBubbleColour = defaultBackgroundColour.Darken(0.5f);
|
||||||
|
selectedBackgroundColour = colours.BlueDark;
|
||||||
|
selectedBubbleColour = selectedBackgroundColour.Lighten(0.5f);
|
||||||
|
|
||||||
|
Triangles.Alpha = 0;
|
||||||
|
|
||||||
|
Content.EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Radius = 2,
|
||||||
|
Offset = new Vector2(0, 1),
|
||||||
|
Colour = Color4.Black.Opacity(0.5f)
|
||||||
|
};
|
||||||
|
|
||||||
|
Add(icon = (Button.CreateIcon?.Invoke() ?? new Circle()).With(b =>
|
||||||
|
{
|
||||||
|
b.Blending = BlendingParameters.Additive;
|
||||||
|
b.Anchor = Anchor.CentreLeft;
|
||||||
|
b.Origin = Anchor.CentreLeft;
|
||||||
|
b.Size = new Vector2(20);
|
||||||
|
b.X = 10;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Button.Bindable.BindValueChanged(selected => updateSelectionState(), true);
|
||||||
|
|
||||||
|
Action = onAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onAction()
|
||||||
|
{
|
||||||
|
Button.Toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSelectionState()
|
||||||
|
{
|
||||||
|
if (!IsLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (Button.Bindable.Value)
|
||||||
|
{
|
||||||
|
case TernaryState.Indeterminate:
|
||||||
|
icon.Colour = selectedBubbleColour.Darken(0.5f);
|
||||||
|
BackgroundColour = selectedBackgroundColour.Darken(0.5f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TernaryState.False:
|
||||||
|
icon.Colour = defaultBubbleColour;
|
||||||
|
BackgroundColour = defaultBackgroundColour;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TernaryState.True:
|
||||||
|
icon.Colour = selectedBubbleColour;
|
||||||
|
BackgroundColour = selectedBackgroundColour;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override SpriteText CreateText() => new OsuSpriteText
|
||||||
|
{
|
||||||
|
Depth = -1,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
X = 40f
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
// 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.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Components.TernaryButtons
|
||||||
|
{
|
||||||
|
public class TernaryButton
|
||||||
|
{
|
||||||
|
public readonly Bindable<TernaryState> Bindable;
|
||||||
|
|
||||||
|
public readonly string Description;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A function which creates a drawable icon to represent this item. If null, a sane default should be used.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Func<Drawable> CreateIcon;
|
||||||
|
|
||||||
|
public TernaryButton(Bindable<TernaryState> bindable, string description, Func<Drawable> createIcon = null)
|
||||||
|
{
|
||||||
|
Bindable = bindable;
|
||||||
|
Description = description;
|
||||||
|
CreateIcon = createIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Toggle()
|
||||||
|
{
|
||||||
|
switch (Bindable.Value)
|
||||||
|
{
|
||||||
|
case TernaryState.False:
|
||||||
|
case TernaryState.Indeterminate:
|
||||||
|
Bindable.Value = TernaryState.True;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TernaryState.True:
|
||||||
|
Bindable.Value = TernaryState.False;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -22,6 +23,8 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
|||||||
{
|
{
|
||||||
protected readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
protected readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
||||||
|
|
||||||
|
protected readonly IBindable<Track> Track = new Bindable<Track>();
|
||||||
|
|
||||||
private readonly Container<T> content;
|
private readonly Container<T> content;
|
||||||
|
|
||||||
protected override Container<T> Content => content;
|
protected override Container<T> Content => content;
|
||||||
@ -35,12 +38,15 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
|||||||
updateRelativeChildSize();
|
updateRelativeChildSize();
|
||||||
LoadBeatmap(b.NewValue);
|
LoadBeatmap(b.NewValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Track.ValueChanged += _ => updateRelativeChildSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(IBindable<WorkingBeatmap> beatmap)
|
private void load(IBindable<WorkingBeatmap> beatmap, EditorClock clock)
|
||||||
{
|
{
|
||||||
Beatmap.BindTo(beatmap);
|
Beatmap.BindTo(beatmap);
|
||||||
|
Track.BindTo(clock.Track);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateRelativeChildSize()
|
private void updateRelativeChildSize()
|
||||||
|
@ -201,6 +201,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
if (isDraggingBlueprint)
|
if (isDraggingBlueprint)
|
||||||
{
|
{
|
||||||
|
// handle positional change etc.
|
||||||
|
foreach (var obj in selectedHitObjects)
|
||||||
|
Beatmap.UpdateHitObject(obj);
|
||||||
|
|
||||||
changeHandler?.EndChange();
|
changeHandler?.EndChange();
|
||||||
isDraggingBlueprint = false;
|
isDraggingBlueprint = false;
|
||||||
}
|
}
|
||||||
|
@ -3,16 +3,21 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Humanizer;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
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;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osu.Game.Screens.Edit.Components.TernaryButtons;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components
|
namespace osu.Game.Screens.Edit.Compose.Components
|
||||||
@ -48,6 +53,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
TernaryStates = CreateTernaryButtons().ToArray();
|
||||||
|
|
||||||
AddInternal(placementBlueprintContainer);
|
AddInternal(placementBlueprintContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,36 +64,92 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
inputManager = GetContainingInputManager();
|
inputManager = GetContainingInputManager();
|
||||||
|
|
||||||
Beatmap.SelectedHitObjects.CollectionChanged += (_, __) => updateTogglesFromSelection();
|
// updates to selected are handled for us by SelectionHandler.
|
||||||
|
NewCombo.BindTo(SelectionHandler.SelectionNewComboState);
|
||||||
|
|
||||||
// the updated object may be in the selection
|
// we are responsible for current placement blueprint updated based on state changes.
|
||||||
Beatmap.HitObjectUpdated += _ => updateTogglesFromSelection();
|
NewCombo.ValueChanged += _ => updatePlacementNewCombo();
|
||||||
|
|
||||||
NewCombo.ValueChanged += combo =>
|
// we own SelectionHandler so don't need to worry about making bindable copies (for simplicity)
|
||||||
|
foreach (var kvp in SelectionHandler.SelectionSampleStates)
|
||||||
{
|
{
|
||||||
if (Beatmap.SelectedHitObjects.Count > 0)
|
kvp.Value.BindValueChanged(_ => updatePlacementSamples());
|
||||||
{
|
}
|
||||||
SelectionHandler.SetNewCombo(combo.NewValue);
|
|
||||||
}
|
|
||||||
else if (currentPlacement != null)
|
|
||||||
{
|
|
||||||
// update placement object from toggle
|
|
||||||
if (currentPlacement.HitObject is IHasComboInformation c)
|
|
||||||
c.NewCombo = combo.NewValue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTogglesFromSelection() =>
|
private void updatePlacementNewCombo()
|
||||||
NewCombo.Value = Beatmap.SelectedHitObjects.OfType<IHasComboInformation>().All(c => c.NewCombo);
|
{
|
||||||
|
if (currentPlacement == null) return;
|
||||||
|
|
||||||
public readonly Bindable<bool> NewCombo = new Bindable<bool> { Description = "New Combo" };
|
if (currentPlacement.HitObject is IHasComboInformation c)
|
||||||
|
c.NewCombo = NewCombo.Value == TernaryState.True;
|
||||||
|
}
|
||||||
|
|
||||||
public virtual IEnumerable<Bindable<bool>> Toggles => new[]
|
private void updatePlacementSamples()
|
||||||
|
{
|
||||||
|
if (currentPlacement == null) return;
|
||||||
|
|
||||||
|
foreach (var kvp in SelectionHandler.SelectionSampleStates)
|
||||||
|
sampleChanged(kvp.Key, kvp.Value.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sampleChanged(string sampleName, TernaryState state)
|
||||||
|
{
|
||||||
|
if (currentPlacement == null) return;
|
||||||
|
|
||||||
|
var samples = currentPlacement.HitObject.Samples;
|
||||||
|
|
||||||
|
var existingSample = samples.FirstOrDefault(s => s.Name == sampleName);
|
||||||
|
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case TernaryState.False:
|
||||||
|
if (existingSample != null)
|
||||||
|
samples.Remove(existingSample);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TernaryState.True:
|
||||||
|
if (existingSample == null)
|
||||||
|
samples.Add(new HitSampleInfo { Name = sampleName });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly Bindable<TernaryState> NewCombo = new Bindable<TernaryState> { Description = "New Combo" };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A collection of states which will be displayed to the user in the toolbox.
|
||||||
|
/// </summary>
|
||||||
|
public TernaryButton[] TernaryStates { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create all ternary states required to be displayed to the user.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual IEnumerable<TernaryButton> CreateTernaryButtons()
|
||||||
{
|
{
|
||||||
//TODO: this should only be enabled (visible?) for rulesets that provide combo-supporting HitObjects.
|
//TODO: this should only be enabled (visible?) for rulesets that provide combo-supporting HitObjects.
|
||||||
NewCombo
|
yield return new TernaryButton(NewCombo, "New combo", () => new SpriteIcon { Icon = FontAwesome.Regular.DotCircle });
|
||||||
};
|
|
||||||
|
foreach (var kvp in SelectionHandler.SelectionSampleStates)
|
||||||
|
yield return new TernaryButton(kvp.Value, kvp.Key.Replace("hit", string.Empty).Titleize(), () => getIconForSample(kvp.Key));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable getIconForSample(string sampleName)
|
||||||
|
{
|
||||||
|
switch (sampleName)
|
||||||
|
{
|
||||||
|
case HitSampleInfo.HIT_CLAP:
|
||||||
|
return new SpriteIcon { Icon = FontAwesome.Solid.Hands };
|
||||||
|
|
||||||
|
case HitSampleInfo.HIT_WHISTLE:
|
||||||
|
return new SpriteIcon { Icon = FontAwesome.Solid.Bullhorn };
|
||||||
|
|
||||||
|
case HitSampleInfo.HIT_FINISH:
|
||||||
|
return new SpriteIcon { Icon = FontAwesome.Solid.DrumSteelpan };
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
#region Placement
|
#region Placement
|
||||||
|
|
||||||
@ -149,10 +212,17 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
if (blueprint != null)
|
if (blueprint != null)
|
||||||
{
|
{
|
||||||
|
// doing this post-creations as adding the default hit sample should be the case regardless of the ruleset.
|
||||||
|
blueprint.HitObject.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_NORMAL });
|
||||||
|
|
||||||
placementBlueprintContainer.Child = currentPlacement = blueprint;
|
placementBlueprintContainer.Child = currentPlacement = blueprint;
|
||||||
|
|
||||||
// Fixes a 1-frame position discrepancy due to the first mouse move event happening in the next frame
|
// Fixes a 1-frame position discrepancy due to the first mouse move event happening in the next frame
|
||||||
updatePlacementPosition();
|
updatePlacementPosition();
|
||||||
|
|
||||||
|
updatePlacementSamples();
|
||||||
|
|
||||||
|
updatePlacementNewCombo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,8 +288,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
{
|
{
|
||||||
var comboInfo = h as IHasComboInformation;
|
var comboInfo = h as IHasComboInformation;
|
||||||
|
|
||||||
if (comboInfo == null)
|
if (comboInfo == null || comboInfo.NewCombo == state) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
comboInfo.NewCombo = state;
|
comboInfo.NewCombo = state;
|
||||||
EditorBeatmap?.UpdateHitObject(h);
|
EditorBeatmap?.UpdateHitObject(h);
|
||||||
@ -316,19 +315,22 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
#region Selection State
|
#region Selection State
|
||||||
|
|
||||||
private readonly Bindable<TernaryState> selectionNewComboState = new Bindable<TernaryState>();
|
/// <summary>
|
||||||
|
/// The state of "new combo" for all selected hitobjects.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Bindable<TernaryState> SelectionNewComboState = new Bindable<TernaryState>();
|
||||||
|
|
||||||
private readonly Dictionary<string, Bindable<TernaryState>> selectionSampleStates = new Dictionary<string, Bindable<TernaryState>>();
|
/// <summary>
|
||||||
|
/// The state of each sample type for all selected hitobjects. Keys match with <see cref="HitSampleInfo"/> constant specifications.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Dictionary<string, Bindable<TernaryState>> SelectionSampleStates = new Dictionary<string, Bindable<TernaryState>>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set up ternary state bindables and bind them to selection/hitobject changes (in both directions)
|
/// Set up ternary state bindables and bind them to selection/hitobject changes (in both directions)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void createStateBindables()
|
private void createStateBindables()
|
||||||
{
|
{
|
||||||
// hit samples
|
foreach (var sampleName in HitSampleInfo.AllAdditions)
|
||||||
var sampleTypes = new[] { HitSampleInfo.HIT_WHISTLE, HitSampleInfo.HIT_CLAP, HitSampleInfo.HIT_FINISH };
|
|
||||||
|
|
||||||
foreach (var sampleName in sampleTypes)
|
|
||||||
{
|
{
|
||||||
var bindable = new Bindable<TernaryState>
|
var bindable = new Bindable<TernaryState>
|
||||||
{
|
{
|
||||||
@ -349,11 +351,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
selectionSampleStates[sampleName] = bindable;
|
SelectionSampleStates[sampleName] = bindable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// new combo
|
// new combo
|
||||||
selectionNewComboState.ValueChanged += state =>
|
SelectionNewComboState.ValueChanged += state =>
|
||||||
{
|
{
|
||||||
switch (state.NewValue)
|
switch (state.NewValue)
|
||||||
{
|
{
|
||||||
@ -377,9 +379,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void UpdateTernaryStates()
|
protected virtual void UpdateTernaryStates()
|
||||||
{
|
{
|
||||||
selectionNewComboState.Value = GetStateFromSelection(SelectedHitObjects.OfType<IHasComboInformation>(), h => h.NewCombo);
|
SelectionNewComboState.Value = GetStateFromSelection(SelectedHitObjects.OfType<IHasComboInformation>(), h => h.NewCombo);
|
||||||
|
|
||||||
foreach (var (sampleName, bindable) in selectionSampleStates)
|
foreach (var (sampleName, bindable) in SelectionSampleStates)
|
||||||
{
|
{
|
||||||
bindable.Value = GetStateFromSelection(SelectedHitObjects, h => h.Samples.Any(s => s.Name == sampleName));
|
bindable.Value = GetStateFromSelection(SelectedHitObjects, h => h.Samples.Any(s => s.Name == sampleName));
|
||||||
}
|
}
|
||||||
@ -413,7 +415,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
if (selectedBlueprints.All(b => b.HitObject is IHasComboInformation))
|
if (selectedBlueprints.All(b => b.HitObject is IHasComboInformation))
|
||||||
{
|
{
|
||||||
items.Add(new TernaryStateMenuItem("New combo") { State = { BindTarget = selectionNewComboState } });
|
items.Add(new TernaryStateMenuItem("New combo") { State = { BindTarget = SelectionNewComboState } });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedBlueprints.Count == 1)
|
if (selectedBlueprints.Count == 1)
|
||||||
@ -423,7 +425,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
{
|
{
|
||||||
new OsuMenuItem("Sound")
|
new OsuMenuItem("Sound")
|
||||||
{
|
{
|
||||||
Items = selectionSampleStates.Select(kvp =>
|
Items = SelectionSampleStates.Select(kvp =>
|
||||||
new TernaryStateMenuItem(kvp.Value.Description) { State = { BindTarget = kvp.Value } }).ToArray()
|
new TernaryStateMenuItem(kvp.Value.Description) { State = { BindTarget = kvp.Value } }).ToArray()
|
||||||
},
|
},
|
||||||
new OsuMenuItem("Delete", MenuItemType.Destructive, deleteSelected),
|
new OsuMenuItem("Delete", MenuItemType.Destructive, deleteSelected),
|
||||||
|
@ -43,6 +43,7 @@ using osuTK.Input;
|
|||||||
namespace osu.Game.Screens.Edit
|
namespace osu.Game.Screens.Edit
|
||||||
{
|
{
|
||||||
[Cached(typeof(IBeatSnapProvider))]
|
[Cached(typeof(IBeatSnapProvider))]
|
||||||
|
[Cached]
|
||||||
public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler<GlobalAction>, IKeyBindingHandler<PlatformAction>, IBeatSnapProvider
|
public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler<GlobalAction>, IKeyBindingHandler<PlatformAction>, IBeatSnapProvider
|
||||||
{
|
{
|
||||||
public override float BackgroundParallaxAmount => 0.1f;
|
public override float BackgroundParallaxAmount => 0.1f;
|
||||||
@ -91,6 +92,9 @@ namespace osu.Game.Screens.Edit
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private IAPIProvider api { get; set; }
|
private IAPIProvider api { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private MusicController music { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours, GameHost host)
|
private void load(OsuColour colours, GameHost host)
|
||||||
{
|
{
|
||||||
@ -98,9 +102,9 @@ namespace osu.Game.Screens.Edit
|
|||||||
beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue);
|
beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue);
|
||||||
|
|
||||||
// Todo: should probably be done at a DrawableRuleset level to share logic with Player.
|
// Todo: should probably be done at a DrawableRuleset level to share logic with Player.
|
||||||
var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock();
|
|
||||||
clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false };
|
clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false };
|
||||||
clock.ChangeSource(sourceClock);
|
|
||||||
|
UpdateClockSource();
|
||||||
|
|
||||||
dependencies.CacheAs(clock);
|
dependencies.CacheAs(clock);
|
||||||
AddInternal(clock);
|
AddInternal(clock);
|
||||||
@ -271,6 +275,15 @@ namespace osu.Game.Screens.Edit
|
|||||||
bottomBackground.Colour = colours.Gray2;
|
bottomBackground.Colour = colours.Gray2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the beatmap's track has changed, this method must be called to keep the editor in a valid state.
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateClockSource()
|
||||||
|
{
|
||||||
|
var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock();
|
||||||
|
clock.ChangeSource(sourceClock);
|
||||||
|
}
|
||||||
|
|
||||||
protected void Save()
|
protected void Save()
|
||||||
{
|
{
|
||||||
// apply any set-level metadata changes.
|
// apply any set-level metadata changes.
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
@ -89,23 +90,27 @@ namespace osu.Game.Screens.Edit
|
|||||||
if (isRestoring)
|
if (isRestoring)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (currentState < savedStates.Count - 1)
|
|
||||||
savedStates.RemoveRange(currentState + 1, savedStates.Count - currentState - 1);
|
|
||||||
|
|
||||||
if (savedStates.Count > MAX_SAVED_STATES)
|
|
||||||
savedStates.RemoveAt(0);
|
|
||||||
|
|
||||||
using (var stream = new MemoryStream())
|
using (var stream = new MemoryStream())
|
||||||
{
|
{
|
||||||
using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
|
using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
|
||||||
new LegacyBeatmapEncoder(editorBeatmap, editorBeatmap.BeatmapSkin).Encode(sw);
|
new LegacyBeatmapEncoder(editorBeatmap, editorBeatmap.BeatmapSkin).Encode(sw);
|
||||||
|
|
||||||
savedStates.Add(stream.ToArray());
|
var newState = stream.ToArray();
|
||||||
|
|
||||||
|
// if the previous state is binary equal we don't need to push a new one, unless this is the initial state.
|
||||||
|
if (savedStates.Count > 0 && newState.SequenceEqual(savedStates.Last())) return;
|
||||||
|
|
||||||
|
if (currentState < savedStates.Count - 1)
|
||||||
|
savedStates.RemoveRange(currentState + 1, savedStates.Count - currentState - 1);
|
||||||
|
|
||||||
|
if (savedStates.Count > MAX_SAVED_STATES)
|
||||||
|
savedStates.RemoveAt(0);
|
||||||
|
|
||||||
|
savedStates.Add(newState);
|
||||||
|
|
||||||
|
currentState = savedStates.Count - 1;
|
||||||
|
updateBindables();
|
||||||
}
|
}
|
||||||
|
|
||||||
currentState = savedStates.Count - 1;
|
|
||||||
|
|
||||||
updateBindables();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Transforms;
|
using osu.Framework.Graphics.Transforms;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
@ -17,7 +19,11 @@ namespace osu.Game.Screens.Edit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourceChangeableClock
|
public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourceChangeableClock
|
||||||
{
|
{
|
||||||
public readonly double TrackLength;
|
public IBindable<Track> Track => track;
|
||||||
|
|
||||||
|
private readonly Bindable<Track> track = new Bindable<Track>();
|
||||||
|
|
||||||
|
public double TrackLength => track.Value?.Length ?? 60000;
|
||||||
|
|
||||||
public ControlPointInfo ControlPointInfo;
|
public ControlPointInfo ControlPointInfo;
|
||||||
|
|
||||||
@ -35,7 +41,6 @@ namespace osu.Game.Screens.Edit
|
|||||||
this.beatDivisor = beatDivisor;
|
this.beatDivisor = beatDivisor;
|
||||||
|
|
||||||
ControlPointInfo = controlPointInfo;
|
ControlPointInfo = controlPointInfo;
|
||||||
TrackLength = trackLength;
|
|
||||||
|
|
||||||
underlyingClock = new DecoupleableInterpolatingFramedClock();
|
underlyingClock = new DecoupleableInterpolatingFramedClock();
|
||||||
}
|
}
|
||||||
@ -190,7 +195,11 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo;
|
public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo;
|
||||||
|
|
||||||
public void ChangeSource(IClock source) => underlyingClock.ChangeSource(source);
|
public void ChangeSource(IClock source)
|
||||||
|
{
|
||||||
|
track.Value = source as Track;
|
||||||
|
underlyingClock.ChangeSource(source);
|
||||||
|
}
|
||||||
|
|
||||||
public IClock Source => underlyingClock.Source;
|
public IClock Source => underlyingClock.Source;
|
||||||
|
|
||||||
|
@ -1,17 +1,24 @@
|
|||||||
// 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 System;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.Drawables;
|
using osu.Game.Beatmaps.Drawables;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Setup
|
namespace osu.Game.Screens.Edit.Setup
|
||||||
@ -23,6 +30,16 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
private LabelledTextBox titleTextBox;
|
private LabelledTextBox titleTextBox;
|
||||||
private LabelledTextBox creatorTextBox;
|
private LabelledTextBox creatorTextBox;
|
||||||
private LabelledTextBox difficultyTextBox;
|
private LabelledTextBox difficultyTextBox;
|
||||||
|
private LabelledTextBox audioTrackTextBox;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private MusicController music { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private BeatmapManager beatmaps { get; set; }
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
private Editor editor { get; set; }
|
||||||
|
|
||||||
public SetupScreen()
|
public SetupScreen()
|
||||||
: base(EditorScreenMode.SongSetup)
|
: base(EditorScreenMode.SongSetup)
|
||||||
@ -32,6 +49,12 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
|
Container audioTrackFileChooserContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
};
|
||||||
|
|
||||||
Child = new Container
|
Child = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -75,6 +98,18 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = "Resources"
|
||||||
|
},
|
||||||
|
audioTrackTextBox = new FileChooserLabelledTextBox
|
||||||
|
{
|
||||||
|
Label = "Audio Track",
|
||||||
|
Current = { Value = Beatmap.Value.Metadata.AudioFile ?? "Click to select a track" },
|
||||||
|
Target = audioTrackFileChooserContainer,
|
||||||
|
TabbableContentContainer = this
|
||||||
|
},
|
||||||
|
audioTrackFileChooserContainer,
|
||||||
|
new OsuSpriteText
|
||||||
{
|
{
|
||||||
Text = "Beatmap metadata"
|
Text = "Beatmap metadata"
|
||||||
},
|
},
|
||||||
@ -109,10 +144,47 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
audioTrackTextBox.Current.BindValueChanged(audioTrackChanged);
|
||||||
|
|
||||||
foreach (var item in flow.OfType<LabelledTextBox>())
|
foreach (var item in flow.OfType<LabelledTextBox>())
|
||||||
item.OnCommit += onCommit;
|
item.OnCommit += onCommit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ChangeAudioTrack(string path)
|
||||||
|
{
|
||||||
|
var info = new FileInfo(path);
|
||||||
|
|
||||||
|
if (!info.Exists)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var set = Beatmap.Value.BeatmapSetInfo;
|
||||||
|
|
||||||
|
// remove the previous audio track for now.
|
||||||
|
// in the future we probably want to check if this is being used elsewhere (other difficulties?)
|
||||||
|
var oldFile = set.Files.FirstOrDefault(f => f.Filename == Beatmap.Value.Metadata.AudioFile);
|
||||||
|
|
||||||
|
using (var stream = info.OpenRead())
|
||||||
|
{
|
||||||
|
if (oldFile != null)
|
||||||
|
beatmaps.ReplaceFile(set, oldFile, stream, info.Name);
|
||||||
|
else
|
||||||
|
beatmaps.AddFile(set, stream, info.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Beatmap.Value.Metadata.AudioFile = info.Name;
|
||||||
|
|
||||||
|
music.ReloadCurrentTrack();
|
||||||
|
|
||||||
|
editor?.UpdateClockSource();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void audioTrackChanged(ValueChangedEvent<string> filePath)
|
||||||
|
{
|
||||||
|
if (!ChangeAudioTrack(filePath.NewValue))
|
||||||
|
audioTrackTextBox.Current.Value = filePath.OldValue;
|
||||||
|
}
|
||||||
|
|
||||||
private void onCommit(TextBox sender, bool newText)
|
private void onCommit(TextBox sender, bool newText)
|
||||||
{
|
{
|
||||||
if (!newText) return;
|
if (!newText) return;
|
||||||
@ -125,4 +197,60 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
Beatmap.Value.BeatmapInfo.Version = difficultyTextBox.Current.Value;
|
Beatmap.Value.BeatmapInfo.Version = difficultyTextBox.Current.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class FileChooserLabelledTextBox : LabelledTextBox
|
||||||
|
{
|
||||||
|
public Container Target;
|
||||||
|
|
||||||
|
private readonly IBindable<FileInfo> currentFile = new Bindable<FileInfo>();
|
||||||
|
|
||||||
|
public FileChooserLabelledTextBox()
|
||||||
|
{
|
||||||
|
currentFile.BindValueChanged(onFileSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onFileSelected(ValueChangedEvent<FileInfo> file)
|
||||||
|
{
|
||||||
|
if (file.NewValue == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Target.Clear();
|
||||||
|
Current.Value = file.NewValue.FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override OsuTextBox CreateTextBox() =>
|
||||||
|
new FileChooserOsuTextBox
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
CornerRadius = CORNER_RADIUS,
|
||||||
|
OnFocused = DisplayFileChooser
|
||||||
|
};
|
||||||
|
|
||||||
|
public void DisplayFileChooser()
|
||||||
|
{
|
||||||
|
Target.Child = new FileSelector(validFileExtensions: new[] { ".mp3", ".ogg" })
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = 400,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
CurrentFile = { BindTarget = currentFile }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class FileChooserOsuTextBox : OsuTextBox
|
||||||
|
{
|
||||||
|
public Action OnFocused;
|
||||||
|
|
||||||
|
protected override void OnFocus(FocusEvent e)
|
||||||
|
{
|
||||||
|
OnFocused?.Invoke();
|
||||||
|
base.OnFocus(e);
|
||||||
|
|
||||||
|
GetContainingInputManager().TriggerFocusContention(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,13 +26,15 @@ namespace osu.Game.Tests.Visual
|
|||||||
Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value);
|
Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual bool EditorComponentsReady => Editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true
|
||||||
|
&& Editor.ChildrenOfType<TimelineArea>().FirstOrDefault()?.IsLoaded == true;
|
||||||
|
|
||||||
public override void SetUpSteps()
|
public override void SetUpSteps()
|
||||||
{
|
{
|
||||||
base.SetUpSteps();
|
base.SetUpSteps();
|
||||||
|
|
||||||
AddStep("load editor", () => LoadScreen(Editor = CreateEditor()));
|
AddStep("load editor", () => LoadScreen(Editor = CreateEditor()));
|
||||||
AddUntilStep("wait for editor to load", () => Editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true
|
AddUntilStep("wait for editor to load", () => EditorComponentsReady);
|
||||||
&& Editor.ChildrenOfType<TimelineArea>().FirstOrDefault()?.IsLoaded == true);
|
|
||||||
AddStep("get beatmap", () => EditorBeatmap = Editor.ChildrenOfType<EditorBeatmap>().Single());
|
AddStep("get beatmap", () => EditorBeatmap = Editor.ChildrenOfType<EditorBeatmap>().Single());
|
||||||
AddStep("get clock", () => EditorClock = Editor.ChildrenOfType<EditorClock>().Single());
|
AddStep("get clock", () => EditorClock = Editor.ChildrenOfType<EditorClock>().Single());
|
||||||
}
|
}
|
||||||
|
@ -909,6 +909,7 @@ private void load()
|
|||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=beatmaps/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=beatmaps/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=beatmap_0027s/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=beatmap_0027s/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=bindable/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=bindable/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=bindables/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Catmull/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Catmull/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Drawables/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Drawables/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=gameplay/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=gameplay/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user