mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 11:07:52 +08:00
Merge branch 'master' into tournament-fix-null-population
This commit is contained in:
commit
780b8f0ec8
@ -25,6 +25,35 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
{
|
{
|
||||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSelectAfterFadedOut()
|
||||||
|
{
|
||||||
|
var slider = new Slider
|
||||||
|
{
|
||||||
|
StartTime = 0,
|
||||||
|
Position = new Vector2(100, 100),
|
||||||
|
Path = new SliderPath
|
||||||
|
{
|
||||||
|
ControlPoints =
|
||||||
|
{
|
||||||
|
new PathControlPoint(),
|
||||||
|
new PathControlPoint(new Vector2(100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AddStep("add slider", () => EditorBeatmap.Add(slider));
|
||||||
|
|
||||||
|
moveMouseToObject(() => slider);
|
||||||
|
|
||||||
|
AddStep("seek after end", () => EditorClock.Seek(750));
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("slider not selected", () => EditorBeatmap.SelectedHitObjects.Count == 0);
|
||||||
|
|
||||||
|
AddStep("seek to visible", () => EditorClock.Seek(650));
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddUntilStep("slider selected", () => EditorBeatmap.SelectedHitObjects.Single() == slider);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestContextMenuShownCorrectlyForSelectedSlider()
|
public void TestContextMenuShownCorrectlyForSelectedSlider()
|
||||||
{
|
{
|
||||||
|
@ -22,7 +22,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints
|
|||||||
protected override bool AlwaysShowWhenSelected => true;
|
protected override bool AlwaysShowWhenSelected => true;
|
||||||
|
|
||||||
protected override bool ShouldBeAlive => base.ShouldBeAlive
|
protected override bool ShouldBeAlive => base.ShouldBeAlive
|
||||||
|| (DrawableObject is not DrawableSpinner && ShowHitMarkers.Value && editorClock.CurrentTime >= Item.StartTime && editorClock.CurrentTime - Item.GetEndTime() < HitCircleOverlapMarker.FADE_OUT_EXTENSION);
|
|| (DrawableObject is not DrawableSpinner && ShowHitMarkers.Value && editorClock.CurrentTime >= Item.StartTime
|
||||||
|
&& editorClock.CurrentTime - Item.GetEndTime() < HitCircleOverlapMarker.FADE_OUT_EXTENSION);
|
||||||
|
|
||||||
|
public override bool IsSelectable =>
|
||||||
|
// Bypass fade out extension from hit markers for selection purposes.
|
||||||
|
// This is to match stable, where even when the afterimage hit markers are still visible, objects are not selectable.
|
||||||
|
base.ShouldBeAlive;
|
||||||
|
|
||||||
protected OsuSelectionBlueprint(T hitObject)
|
protected OsuSelectionBlueprint(T hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
|
@ -36,6 +36,28 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements
|
|||||||
AssertResult<Hit>(0, HitResult.Great);
|
AssertResult<Hit>(0, HitResult.Great);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHitWithBothKeysOnSameFrameDoesNotFallThroughToNextObject()
|
||||||
|
{
|
||||||
|
PerformTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new TaikoReplayFrame(0),
|
||||||
|
new TaikoReplayFrame(1000, TaikoAction.LeftCentre, TaikoAction.RightCentre),
|
||||||
|
}, CreateBeatmap(new Hit
|
||||||
|
{
|
||||||
|
Type = HitType.Centre,
|
||||||
|
StartTime = 1000,
|
||||||
|
}, new Hit
|
||||||
|
{
|
||||||
|
Type = HitType.Centre,
|
||||||
|
StartTime = 1020
|
||||||
|
}));
|
||||||
|
|
||||||
|
AssertJudgementCount(2);
|
||||||
|
AssertResult<Hit>(0, HitResult.Great);
|
||||||
|
AssertResult<Hit>(1, HitResult.Miss);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestHitRimHit()
|
public void TestHitRimHit()
|
||||||
{
|
{
|
||||||
|
@ -1,24 +1,71 @@
|
|||||||
// 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.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Taiko.Mods;
|
using osu.Game.Rulesets.Taiko.Mods;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Tests.Mods
|
namespace osu.Game.Rulesets.Taiko.Tests.Mods
|
||||||
{
|
{
|
||||||
public partial class TestSceneTaikoModHidden : TaikoModTestScene
|
public partial class TestSceneTaikoModHidden : TaikoModTestScene
|
||||||
{
|
{
|
||||||
|
private Func<bool> checkAllMaxResultJudgements(int count) => ()
|
||||||
|
=> Player.ScoreProcessor.JudgedHits >= count
|
||||||
|
&& Player.Results.All(result => result.Type == result.Judgement.MaxResult);
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDefaultBeatmapTest() => CreateModTest(new ModTestData
|
public void TestDefaultBeatmapTest() => CreateModTest(new ModTestData
|
||||||
{
|
{
|
||||||
Mod = new TaikoModHidden(),
|
Mod = new TaikoModHidden(),
|
||||||
Autoplay = true,
|
Autoplay = true,
|
||||||
PassCondition = checkSomeAutoplayHits
|
PassCondition = checkAllMaxResultJudgements(4),
|
||||||
});
|
});
|
||||||
|
|
||||||
private bool checkSomeAutoplayHits()
|
[Test]
|
||||||
=> Player.ScoreProcessor.JudgedHits >= 4
|
public void TestHitTwoNotesWithinShortPeriod()
|
||||||
&& Player.Results.All(result => result.Type == result.Judgement.MaxResult);
|
{
|
||||||
|
const double hit_time = 1;
|
||||||
|
|
||||||
|
var beatmap = new Beatmap<TaikoHitObject>
|
||||||
|
{
|
||||||
|
HitObjects = new List<TaikoHitObject>
|
||||||
|
{
|
||||||
|
new Hit
|
||||||
|
{
|
||||||
|
Type = HitType.Rim,
|
||||||
|
StartTime = hit_time,
|
||||||
|
},
|
||||||
|
new Hit
|
||||||
|
{
|
||||||
|
Type = HitType.Centre,
|
||||||
|
StartTime = hit_time * 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BeatmapInfo =
|
||||||
|
{
|
||||||
|
Difficulty = new BeatmapDifficulty
|
||||||
|
{
|
||||||
|
SliderTickRate = 4,
|
||||||
|
OverallDifficulty = 0,
|
||||||
|
},
|
||||||
|
Ruleset = new TaikoRuleset().RulesetInfo
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
beatmap.ControlPointInfo.Add(0, new EffectControlPoint { ScrollSpeed = 0.1f });
|
||||||
|
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new TaikoModHidden(),
|
||||||
|
Autoplay = true,
|
||||||
|
PassCondition = checkAllMaxResultJudgements(2),
|
||||||
|
Beatmap = beatmap,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
|
|
||||||
private bool validActionPressed;
|
private bool validActionPressed;
|
||||||
|
|
||||||
private bool pressHandledThisFrame;
|
private double? lastPressHandleTime;
|
||||||
|
|
||||||
private readonly Bindable<HitType> type = new Bindable<HitType>();
|
private readonly Bindable<HitType> type = new Bindable<HitType>();
|
||||||
|
|
||||||
@ -76,7 +76,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
|
|
||||||
HitActions = null;
|
HitActions = null;
|
||||||
HitAction = null;
|
HitAction = null;
|
||||||
validActionPressed = pressHandledThisFrame = false;
|
validActionPressed = false;
|
||||||
|
lastPressHandleTime = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateActionsFromType()
|
private void updateActionsFromType()
|
||||||
@ -114,7 +115,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
|
|
||||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
|
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
|
||||||
{
|
{
|
||||||
if (pressHandledThisFrame)
|
if (lastPressHandleTime == Time.Current)
|
||||||
return true;
|
return true;
|
||||||
if (Judged)
|
if (Judged)
|
||||||
return false;
|
return false;
|
||||||
@ -128,7 +129,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
|
|
||||||
// Regardless of whether we've hit or not, any secondary key presses in the same frame should be discarded
|
// Regardless of whether we've hit or not, any secondary key presses in the same frame should be discarded
|
||||||
// E.g. hitting a non-strong centre as a strong should not fall through and perform a hit on the next note
|
// E.g. hitting a non-strong centre as a strong should not fall through and perform a hit on the next note
|
||||||
pressHandledThisFrame = true;
|
lastPressHandleTime = Time.Current;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,15 +140,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
base.OnReleased(e);
|
base.OnReleased(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
|
|
||||||
// The input manager processes all input prior to us updating, so this is the perfect time
|
|
||||||
// for us to remove the extra press blocking, before input is handled in the next frame
|
|
||||||
pressHandledThisFrame = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||||
{
|
{
|
||||||
Debug.Assert(HitObject.HitWindows != null);
|
Debug.Assert(HitObject.HitWindows != null);
|
||||||
|
@ -14,14 +14,14 @@ using osu.Game.Tests.Beatmaps;
|
|||||||
|
|
||||||
namespace osu.Game.Tests.Editing.Checks
|
namespace osu.Game.Tests.Editing.Checks
|
||||||
{
|
{
|
||||||
public class CheckDrainTimeTest
|
public class CheckDrainLengthTest
|
||||||
{
|
{
|
||||||
private CheckDrainTime check = null!;
|
private CheckDrainLength check = null!;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
check = new CheckDrainTime();
|
check = new CheckDrainLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -40,7 +40,7 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
var issues = check.Run(context).ToList();
|
var issues = check.Run(context).ToList();
|
||||||
|
|
||||||
Assert.That(issues, Has.Count.EqualTo(1));
|
Assert.That(issues, Has.Count.EqualTo(1));
|
||||||
Assert.That(issues.Single().Template is CheckDrainTime.IssueTemplateTooShort);
|
Assert.That(issues.Single().Template is CheckDrainLength.IssueTemplateTooShort);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -63,7 +63,7 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
var issues = check.Run(context).ToList();
|
var issues = check.Run(context).ToList();
|
||||||
|
|
||||||
Assert.That(issues, Has.Count.EqualTo(1));
|
Assert.That(issues, Has.Count.EqualTo(1));
|
||||||
Assert.That(issues.Single().Template is CheckDrainTime.IssueTemplateTooShort);
|
Assert.That(issues.Single().Template is CheckDrainLength.IssueTemplateTooShort);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
@ -340,14 +340,14 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
public void TestCyclicSelectionBackwards()
|
public void TestCyclicSelectionBackwards()
|
||||||
{
|
{
|
||||||
var firstObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 0 };
|
var firstObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 0 };
|
||||||
var secondObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 300 };
|
var secondObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 200 };
|
||||||
var thirdObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 };
|
var thirdObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 400 };
|
||||||
|
|
||||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject, thirdObject }));
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject, thirdObject }));
|
||||||
|
|
||||||
moveMouseToObject(() => firstObject);
|
moveMouseToObject(() => firstObject);
|
||||||
|
|
||||||
AddStep("seek to third", () => EditorClock.Seek(600));
|
AddStep("seek to third", () => EditorClock.Seek(350));
|
||||||
|
|
||||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject));
|
AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject));
|
||||||
|
@ -69,13 +69,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
spewer.Clock = new FramedClock(testClock);
|
spewer.Clock = new FramedClock(testClock);
|
||||||
});
|
});
|
||||||
AddStep("start spewer", () => spewer.Active.Value = true);
|
AddStep("start spewer", () => spewer.Active.Value = true);
|
||||||
AddAssert("spawned first particle", () => spewer.TotalCreatedParticles == 1);
|
AddAssert("spawned first particle", () => spewer.TotalCreatedParticles, () => Is.EqualTo(1));
|
||||||
|
|
||||||
AddStep("move clock forward", () => testClock.CurrentTime = TestParticleSpewer.MAX_DURATION * 3);
|
AddStep("move clock forward", () => testClock.CurrentTime = TestParticleSpewer.MAX_DURATION * 3);
|
||||||
AddAssert("spawned second particle", () => spewer.TotalCreatedParticles == 2);
|
AddAssert("spawned second particle", () => spewer.TotalCreatedParticles, () => Is.EqualTo(2));
|
||||||
|
|
||||||
AddStep("move clock backwards", () => testClock.CurrentTime = TestParticleSpewer.MAX_DURATION * -1);
|
AddStep("move clock backwards", () => testClock.CurrentTime = TestParticleSpewer.MAX_DURATION * -1);
|
||||||
AddAssert("spawned third particle", () => spewer.TotalCreatedParticles == 3);
|
AddAssert("spawned third particle", () => spewer.TotalCreatedParticles, () => Is.EqualTo(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
private TestParticleSpewer createSpewer() =>
|
private TestParticleSpewer createSpewer() =>
|
||||||
|
@ -21,9 +21,11 @@ using osu.Game.Rulesets.Osu;
|
|||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
using osu.Game.Tests.Resources;
|
using osu.Game.Tests.Resources;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
@ -147,6 +149,38 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddUntilStep("score in database", () => Realm.Run(r => r.Find<ScoreInfo>(Player.Score.ScoreInfo.ID) != null));
|
AddUntilStep("score in database", () => Realm.Run(r => r.Find<ScoreInfo>(Player.Score.ScoreInfo.ID) != null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestReplayExport()
|
||||||
|
{
|
||||||
|
CreateTest();
|
||||||
|
|
||||||
|
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
||||||
|
|
||||||
|
AddStep("seek to completion", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.Objects.Last().GetEndTime()));
|
||||||
|
|
||||||
|
AddUntilStep("results displayed", () => (Player.GetChildScreen() as ResultsScreen)?.IsLoaded == true);
|
||||||
|
AddUntilStep("score in database", () => Realm.Run(r => r.Find<ScoreInfo>(Player.Score.ScoreInfo.ID) != null));
|
||||||
|
|
||||||
|
AddUntilStep("wait for button clickable", () => ((OsuScreen)Player.GetChildScreen())
|
||||||
|
.ChildrenOfType<ReplayDownloadButton>().FirstOrDefault()?
|
||||||
|
.ChildrenOfType<OsuClickableContainer>().FirstOrDefault()?
|
||||||
|
.Enabled.Value == true);
|
||||||
|
|
||||||
|
AddAssert("no export files", () => !LocalStorage.GetFiles("exports").Any());
|
||||||
|
|
||||||
|
AddStep("Export replay", () => InputManager.PressKey(Key.F2));
|
||||||
|
|
||||||
|
string? filePath = null;
|
||||||
|
|
||||||
|
// Files starting with _ are temporary, created by CreateFileSafely call.
|
||||||
|
AddUntilStep("wait for export file", () => filePath = LocalStorage.GetFiles("exports").SingleOrDefault(f => !f.StartsWith("_", StringComparison.Ordinal)), () => Is.Not.Null);
|
||||||
|
AddAssert("filesize is non-zero", () =>
|
||||||
|
{
|
||||||
|
using (var stream = LocalStorage.GetStream(filePath))
|
||||||
|
return stream.Length;
|
||||||
|
}, () => Is.Not.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestScoreStoredLocallyCustomRuleset()
|
public void TestScoreStoredLocallyCustomRuleset()
|
||||||
{
|
{
|
||||||
|
@ -20,7 +20,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
CreateModTest(new ModTestData
|
CreateModTest(new ModTestData
|
||||||
{
|
{
|
||||||
Beatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).Beatmap,
|
|
||||||
Mod = new UnknownMod("WNG"),
|
Mod = new UnknownMod("WNG"),
|
||||||
PassCondition = () => Player.IsLoaded && !Player.LoadedBeatmapSuccessfully
|
PassCondition = () => Player.IsLoaded && !Player.LoadedBeatmapSuccessfully
|
||||||
});
|
});
|
||||||
|
52
osu.Game.Tests/Visual/Menus/TestSceneStarFountain.cs
Normal file
52
osu.Game.Tests/Visual/Menus/TestSceneStarFountain.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Screens.Menu;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Menus
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public partial class TestSceneStarFountain : OsuTestScene
|
||||||
|
{
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("make fountains", () =>
|
||||||
|
{
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new StarFountain
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
X = 200,
|
||||||
|
},
|
||||||
|
new StarFountain
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
X = -200,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPew()
|
||||||
|
{
|
||||||
|
AddRepeatStep("activate fountains sometimes", () =>
|
||||||
|
{
|
||||||
|
foreach (var fountain in Children.OfType<StarFountain>())
|
||||||
|
{
|
||||||
|
if (RNG.NextSingle() > 0.8f)
|
||||||
|
fountain.Shoot();
|
||||||
|
}
|
||||||
|
}, 150);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
@ -283,8 +282,6 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
if (ReferenceEquals(timingPoints[^1], current))
|
if (ReferenceEquals(timingPoints[^1], current))
|
||||||
{
|
{
|
||||||
Debug.Assert(BeatSyncSource.Clock != null);
|
|
||||||
|
|
||||||
return (int)Math.Ceiling((BeatSyncSource.Clock.CurrentTime - current.Time) / current.BeatLength);
|
return (int)Math.Ceiling((BeatSyncSource.Clock.CurrentTime - current.Time) / current.BeatLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,8 +292,6 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
Debug.Assert(BeatSyncSource.Clock != null);
|
|
||||||
|
|
||||||
timeUntilNextBeat.Value = TimeUntilNextBeat;
|
timeUntilNextBeat.Value = TimeUntilNextBeat;
|
||||||
timeSinceLastBeat.Value = TimeSinceLastBeat;
|
timeSinceLastBeat.Value = TimeSinceLastBeat;
|
||||||
currentTime.Value = BeatSyncSource.Clock.CurrentTime;
|
currentTime.Value = BeatSyncSource.Clock.CurrentTime;
|
||||||
|
@ -542,7 +542,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
AddStep("clear search", () => modSelectOverlay.SearchTerm = string.Empty);
|
AddStep("clear search", () => modSelectOverlay.SearchTerm = string.Empty);
|
||||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||||
AddAssert("mod select still visible", () => modSelectOverlay.State.Value == Visibility.Visible);
|
AddAssert("mod select hidden", () => modSelectOverlay.State.Value == Visibility.Hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -1,23 +1,100 @@
|
|||||||
// 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 osu.Framework.Allocation;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Graphics.Cursor;
|
using osu.Game.Graphics.Cursor;
|
||||||
using osu.Game.Tournament.Screens.Editors;
|
using osu.Game.Tournament.Screens.Editors;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Overlays.Dialog;
|
||||||
|
using osu.Game.Tournament.Screens.Editors.Components;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tournament.Tests.Screens
|
namespace osu.Game.Tournament.Tests.Screens
|
||||||
{
|
{
|
||||||
public partial class TestSceneLadderEditorScreen : TournamentScreenTestScene
|
public partial class TestSceneLadderEditorScreen : TournamentScreenTestScene
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
private LadderEditorScreen ladderEditorScreen = null!;
|
||||||
private void load()
|
private OsuContextMenuContainer? osuContextMenuContainer;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() => Schedule(() =>
|
||||||
{
|
{
|
||||||
Add(new OsuContextMenuContainer
|
ladderEditorScreen = new LadderEditorScreen();
|
||||||
|
Add(osuContextMenuContainer = new OsuContextMenuContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = new LadderEditorScreen()
|
Child = ladderEditorScreen = new LadderEditorScreen()
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestResetBracketTeamsCancelled()
|
||||||
|
{
|
||||||
|
Bindable<string> matchBeforeReset = new Bindable<string>();
|
||||||
|
AddStep("save current match state", () =>
|
||||||
|
{
|
||||||
|
matchBeforeReset.Value = JsonConvert.SerializeObject(Ladder.CurrentMatch.Value);
|
||||||
|
});
|
||||||
|
AddStep("pull up context menu", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(ladderEditorScreen);
|
||||||
|
InputManager.Click(MouseButton.Right);
|
||||||
|
});
|
||||||
|
AddStep("click Reset teams button", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(osuContextMenuContainer.ChildrenOfType<DrawableOsuMenuItem>().Last(p =>
|
||||||
|
((OsuMenuItem)p.Item).Type == MenuItemType.Destructive), new Vector2(5, 0));
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("dialog displayed", () => DialogOverlay.CurrentDialog is LadderResetTeamsDialog);
|
||||||
|
AddStep("click cancel", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(DialogOverlay.CurrentDialog.ChildrenOfType<PopupDialogButton>().Last());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("dialog dismissed", () => DialogOverlay.CurrentDialog is not LadderResetTeamsDialog);
|
||||||
|
|
||||||
|
AddAssert("assert ladder teams unchanged", () => string.Equals(matchBeforeReset.Value, JsonConvert.SerializeObject(Ladder.CurrentMatch.Value), StringComparison.Ordinal));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestResetBracketTeams()
|
||||||
|
{
|
||||||
|
AddStep("pull up context menu", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(ladderEditorScreen);
|
||||||
|
InputManager.Click(MouseButton.Right);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("click Reset teams button", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(osuContextMenuContainer.ChildrenOfType<DrawableOsuMenuItem>().Last(p =>
|
||||||
|
((OsuMenuItem)p.Item).Type == MenuItemType.Destructive), new Vector2(5, 0));
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("dialog displayed", () => DialogOverlay.CurrentDialog is LadderResetTeamsDialog);
|
||||||
|
|
||||||
|
AddStep("click confirmation", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(DialogOverlay.CurrentDialog.ChildrenOfType<PopupDialogButton>().First());
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("dialog dismissed", () => DialogOverlay.CurrentDialog is not LadderResetTeamsDialog);
|
||||||
|
|
||||||
|
AddStep("release mouse button", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
|
|
||||||
|
AddAssert("assert ladder teams reset", () => Ladder.CurrentMatch.Value.Team1.Value == null && Ladder.CurrentMatch.Value.Team2.Value == null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
// 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 osu.Framework.Allocation;
|
||||||
using osu.Game.Tournament.Screens.Editors;
|
using osu.Game.Tournament.Screens.Editors;
|
||||||
|
|
||||||
namespace osu.Game.Tournament.Tests.Screens
|
namespace osu.Game.Tournament.Tests.Screens
|
||||||
{
|
{
|
||||||
public partial class TestSceneRoundEditorScreen : TournamentScreenTestScene
|
public partial class TestSceneRoundEditorScreen : TournamentScreenTestScene
|
||||||
{
|
{
|
||||||
public TestSceneRoundEditorScreen()
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
{
|
{
|
||||||
Add(new RoundEditorScreen
|
Add(new RoundEditorScreen
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,8 @@ namespace osu.Game.Tournament.Tests.Screens
|
|||||||
[Cached]
|
[Cached]
|
||||||
private readonly LadderInfo ladder = new LadderInfo();
|
private readonly LadderInfo ladder = new LadderInfo();
|
||||||
|
|
||||||
public TestSceneSeedingEditorScreen()
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
{
|
{
|
||||||
var match = CreateSampleMatch();
|
var match = CreateSampleMatch();
|
||||||
|
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
// 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 osu.Framework.Allocation;
|
||||||
using osu.Game.Tournament.Screens.Editors;
|
using osu.Game.Tournament.Screens.Editors;
|
||||||
|
|
||||||
namespace osu.Game.Tournament.Tests.Screens
|
namespace osu.Game.Tournament.Tests.Screens
|
||||||
{
|
{
|
||||||
public partial class TestSceneTeamEditorScreen : TournamentScreenTestScene
|
public partial class TestSceneTeamEditorScreen : TournamentScreenTestScene
|
||||||
{
|
{
|
||||||
public TestSceneTeamEditorScreen()
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
{
|
{
|
||||||
Add(new TeamEditorScreen
|
Add(new TeamEditorScreen
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Platform;
|
|||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osu.Game.Tournament.IO;
|
using osu.Game.Tournament.IO;
|
||||||
@ -16,8 +17,11 @@ using osu.Game.Tournament.Models;
|
|||||||
|
|
||||||
namespace osu.Game.Tournament.Tests
|
namespace osu.Game.Tournament.Tests
|
||||||
{
|
{
|
||||||
public abstract partial class TournamentTestScene : OsuTestScene
|
public abstract partial class TournamentTestScene : OsuManualInputManagerTestScene
|
||||||
{
|
{
|
||||||
|
[Cached(typeof(IDialogOverlay))]
|
||||||
|
protected readonly DialogOverlay DialogOverlay = new DialogOverlay { Depth = float.MinValue };
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
protected LadderInfo Ladder { get; private set; } = new LadderInfo();
|
protected LadderInfo Ladder { get; private set; } = new LadderInfo();
|
||||||
|
|
||||||
@ -43,6 +47,8 @@ namespace osu.Game.Tournament.Tests
|
|||||||
|
|
||||||
Ruleset.BindTo(Ladder.Ruleset);
|
Ruleset.BindTo(Ladder.Ruleset);
|
||||||
Dependencies.CacheAs(new StableInfo(storage));
|
Dependencies.CacheAs(new StableInfo(storage));
|
||||||
|
|
||||||
|
Add(DialogOverlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUpSteps]
|
[SetUpSteps]
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
// 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.Graphics.Sprites;
|
||||||
|
using osu.Game.Overlays.Dialog;
|
||||||
|
using osu.Game.Tournament.Models;
|
||||||
|
|
||||||
|
namespace osu.Game.Tournament.Screens.Editors.Components
|
||||||
|
{
|
||||||
|
public partial class DeleteRoundDialog : DangerousActionDialog
|
||||||
|
{
|
||||||
|
public DeleteRoundDialog(TournamentRound round, Action action)
|
||||||
|
{
|
||||||
|
HeaderText = round.Name.Value.Length > 0 ? $@"Delete round ""{round.Name.Value}""?" : @"Delete unnamed round?";
|
||||||
|
Icon = FontAwesome.Solid.Trash;
|
||||||
|
DangerousAction = action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
// 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.Graphics.Sprites;
|
||||||
|
using osu.Game.Overlays.Dialog;
|
||||||
|
using osu.Game.Tournament.Models;
|
||||||
|
|
||||||
|
namespace osu.Game.Tournament.Screens.Editors.Components
|
||||||
|
{
|
||||||
|
public partial class DeleteTeamDialog : DangerousActionDialog
|
||||||
|
{
|
||||||
|
public DeleteTeamDialog(TournamentTeam team, Action action)
|
||||||
|
{
|
||||||
|
HeaderText = team.FullName.Value.Length > 0 ? $@"Delete team ""{team.FullName.Value}""?" :
|
||||||
|
team.Acronym.Value.Length > 0 ? $@"Delete team ""{team.Acronym.Value}""?" :
|
||||||
|
@"Delete unnamed team?";
|
||||||
|
Icon = FontAwesome.Solid.Trash;
|
||||||
|
DangerousAction = action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
// 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.Graphics.Sprites;
|
||||||
|
using osu.Game.Overlays.Dialog;
|
||||||
|
|
||||||
|
namespace osu.Game.Tournament.Screens.Editors.Components
|
||||||
|
{
|
||||||
|
public partial class LadderResetTeamsDialog : DangerousActionDialog
|
||||||
|
{
|
||||||
|
public LadderResetTeamsDialog(Action action)
|
||||||
|
{
|
||||||
|
HeaderText = @"Reset teams?";
|
||||||
|
Icon = FontAwesome.Solid.Undo;
|
||||||
|
DangerousAction = action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
// 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.Graphics.Sprites;
|
||||||
|
using osu.Game.Overlays.Dialog;
|
||||||
|
|
||||||
|
namespace osu.Game.Tournament.Screens.Editors.Components
|
||||||
|
{
|
||||||
|
public partial class TournamentClearAllDialog : DangerousActionDialog
|
||||||
|
{
|
||||||
|
public TournamentClearAllDialog(Action action)
|
||||||
|
{
|
||||||
|
HeaderText = @"Clear all?";
|
||||||
|
Icon = FontAwesome.Solid.Trash;
|
||||||
|
DangerousAction = action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -16,7 +17,9 @@ using osu.Framework.Input.States;
|
|||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osu.Game.Tournament.Components;
|
using osu.Game.Tournament.Components;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Tournament.Models;
|
using osu.Game.Tournament.Models;
|
||||||
|
using osu.Game.Tournament.Screens.Editors.Components;
|
||||||
using osu.Game.Tournament.Screens.Ladder;
|
using osu.Game.Tournament.Screens.Ladder;
|
||||||
using osu.Game.Tournament.Screens.Ladder.Components;
|
using osu.Game.Tournament.Screens.Ladder.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -34,6 +37,10 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
|
|
||||||
private WarningBox rightClickMessage;
|
private WarningBox rightClickMessage;
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
[CanBeNull]
|
||||||
|
private IDialogOverlay dialogOverlay { get; set; }
|
||||||
|
|
||||||
protected override bool DrawLoserPaths => true;
|
protected override bool DrawLoserPaths => true;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -87,8 +94,11 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
}),
|
}),
|
||||||
new OsuMenuItem("Reset teams", MenuItemType.Destructive, () =>
|
new OsuMenuItem("Reset teams", MenuItemType.Destructive, () =>
|
||||||
{
|
{
|
||||||
foreach (var p in MatchesContainer)
|
dialogOverlay?.Push(new LadderResetTeamsDialog(() =>
|
||||||
p.Match.Reset();
|
{
|
||||||
|
foreach (var p in MatchesContainer)
|
||||||
|
p.Match.Reset();
|
||||||
|
}));
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,11 @@ using osu.Game.Graphics;
|
|||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
using osu.Game.Tournament.Components;
|
using osu.Game.Tournament.Components;
|
||||||
using osu.Game.Tournament.Models;
|
using osu.Game.Tournament.Models;
|
||||||
|
using osu.Game.Tournament.Screens.Editors.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tournament.Screens.Editors
|
namespace osu.Game.Tournament.Screens.Editors
|
||||||
@ -29,6 +31,9 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private LadderInfo ladderInfo { get; set; } = null!;
|
private LadderInfo ladderInfo { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IDialogOverlay? dialogOverlay { get; set; }
|
||||||
|
|
||||||
public RoundRow(TournamentRound round)
|
public RoundRow(TournamentRound round)
|
||||||
{
|
{
|
||||||
Model = round;
|
Model = round;
|
||||||
@ -99,11 +104,11 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
RelativeSizeAxes = Axes.None,
|
RelativeSizeAxes = Axes.None,
|
||||||
Width = 150,
|
Width = 150,
|
||||||
Text = "Delete Round",
|
Text = "Delete Round",
|
||||||
Action = () =>
|
Action = () => dialogOverlay?.Push(new DeleteRoundDialog(Model, () =>
|
||||||
{
|
{
|
||||||
Expire();
|
Expire();
|
||||||
ladderInfo.Rounds.Remove(Model);
|
ladderInfo.Rounds.Remove(Model);
|
||||||
},
|
}))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,8 +11,10 @@ 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.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
using osu.Game.Tournament.Models;
|
using osu.Game.Tournament.Models;
|
||||||
|
using osu.Game.Tournament.Screens.Editors.Components;
|
||||||
using osu.Game.Tournament.Screens.Drawings.Components;
|
using osu.Game.Tournament.Screens.Drawings.Components;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -61,6 +63,9 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private TournamentSceneManager? sceneManager { get; set; }
|
private TournamentSceneManager? sceneManager { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IDialogOverlay? dialogOverlay { get; set; }
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private LadderInfo ladderInfo { get; set; } = null!;
|
private LadderInfo ladderInfo { get; set; } = null!;
|
||||||
|
|
||||||
@ -157,11 +162,11 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
Text = "Delete Team",
|
Text = "Delete Team",
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
Action = () =>
|
Action = () => dialogOverlay?.Push(new DeleteTeamDialog(Model, () =>
|
||||||
{
|
{
|
||||||
Expire();
|
Expire();
|
||||||
ladderInfo.Teams.Remove(Model);
|
ladderInfo.Teams.Remove(Model);
|
||||||
},
|
})),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -13,7 +13,9 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Tournament.Components;
|
using osu.Game.Tournament.Components;
|
||||||
|
using osu.Game.Tournament.Screens.Editors.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tournament.Screens.Editors
|
namespace osu.Game.Tournament.Screens.Editors
|
||||||
@ -24,6 +26,9 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
{
|
{
|
||||||
protected abstract BindableList<TModel> Storage { get; }
|
protected abstract BindableList<TModel> Storage { get; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IDialogOverlay? dialogOverlay { get; set; }
|
||||||
|
|
||||||
private FillFlowContainer<TDrawable> flow = null!;
|
private FillFlowContainer<TDrawable> flow = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
@ -79,7 +84,10 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
BackgroundColour = colours.Pink3,
|
BackgroundColour = colours.Pink3,
|
||||||
Text = "Clear all",
|
Text = "Clear all",
|
||||||
Action = Storage.Clear
|
Action = () =>
|
||||||
|
{
|
||||||
|
dialogOverlay?.Push(new TournamentClearAllDialog(() => Storage.Clear()));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
State.BindTo(ipc.State);
|
State.BindTo(ipc.State);
|
||||||
State.BindValueChanged(stateChanged, true);
|
State.BindValueChanged(_ => updateState(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch> match)
|
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch> match)
|
||||||
@ -150,20 +150,47 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
warmup.Value = match.NewValue.Team1Score.Value + match.NewValue.Team2Score.Value == 0;
|
warmup.Value = match.NewValue.Team1Score.Value + match.NewValue.Team2Score.Value == 0;
|
||||||
scheduledOperation?.Cancel();
|
scheduledScreenChange?.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScheduledDelegate scheduledOperation;
|
private ScheduledDelegate scheduledScreenChange;
|
||||||
|
private ScheduledDelegate scheduledContract;
|
||||||
|
|
||||||
private TournamentMatchScoreDisplay scoreDisplay;
|
private TournamentMatchScoreDisplay scoreDisplay;
|
||||||
|
|
||||||
private TourneyState lastState;
|
private TourneyState lastState;
|
||||||
private MatchHeader header;
|
private MatchHeader header;
|
||||||
|
|
||||||
private void stateChanged(ValueChangedEvent<TourneyState> state)
|
private void contract()
|
||||||
|
{
|
||||||
|
scheduledContract?.Cancel();
|
||||||
|
|
||||||
|
SongBar.Expanded = false;
|
||||||
|
scoreDisplay.FadeOut(100);
|
||||||
|
using (chat?.BeginDelayedSequence(500))
|
||||||
|
chat?.Expand();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expand()
|
||||||
|
{
|
||||||
|
scheduledContract?.Cancel();
|
||||||
|
|
||||||
|
chat?.Contract();
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(300))
|
||||||
|
{
|
||||||
|
scoreDisplay.FadeIn(100);
|
||||||
|
SongBar.Expanded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateState()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (state.NewValue == TourneyState.Ranking)
|
scheduledScreenChange?.Cancel();
|
||||||
|
|
||||||
|
if (State.Value == TourneyState.Ranking)
|
||||||
{
|
{
|
||||||
if (warmup.Value || CurrentMatch.Value == null) return;
|
if (warmup.Value || CurrentMatch.Value == null) return;
|
||||||
|
|
||||||
@ -173,28 +200,7 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
|||||||
CurrentMatch.Value.Team2Score.Value++;
|
CurrentMatch.Value.Team2Score.Value++;
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduledOperation?.Cancel();
|
switch (State.Value)
|
||||||
|
|
||||||
void expand()
|
|
||||||
{
|
|
||||||
chat?.Contract();
|
|
||||||
|
|
||||||
using (BeginDelayedSequence(300))
|
|
||||||
{
|
|
||||||
scoreDisplay.FadeIn(100);
|
|
||||||
SongBar.Expanded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void contract()
|
|
||||||
{
|
|
||||||
SongBar.Expanded = false;
|
|
||||||
scoreDisplay.FadeOut(100);
|
|
||||||
using (chat?.BeginDelayedSequence(500))
|
|
||||||
chat?.Expand();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (state.NewValue)
|
|
||||||
{
|
{
|
||||||
case TourneyState.Idle:
|
case TourneyState.Idle:
|
||||||
contract();
|
contract();
|
||||||
@ -208,30 +214,41 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
|||||||
if (lastState == TourneyState.Ranking && !warmup.Value)
|
if (lastState == TourneyState.Ranking && !warmup.Value)
|
||||||
{
|
{
|
||||||
if (CurrentMatch.Value?.Completed.Value == true)
|
if (CurrentMatch.Value?.Completed.Value == true)
|
||||||
scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(TeamWinScreen)); }, delay_before_progression);
|
scheduledScreenChange = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(TeamWinScreen)); }, delay_before_progression);
|
||||||
else if (CurrentMatch.Value?.Completed.Value == false)
|
else if (CurrentMatch.Value?.Completed.Value == false)
|
||||||
scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(MapPoolScreen)); }, delay_before_progression);
|
scheduledScreenChange = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(MapPoolScreen)); }, delay_before_progression);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TourneyState.Ranking:
|
case TourneyState.Ranking:
|
||||||
scheduledOperation = Scheduler.AddDelayed(contract, 10000);
|
scheduledContract = Scheduler.AddDelayed(contract, 10000);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
chat.Contract();
|
|
||||||
expand();
|
expand();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
lastState = state.NewValue;
|
lastState = State.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Hide()
|
||||||
|
{
|
||||||
|
scheduledScreenChange?.Cancel();
|
||||||
|
base.Hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Show()
|
||||||
|
{
|
||||||
|
updateState();
|
||||||
|
base.Show();
|
||||||
|
}
|
||||||
|
|
||||||
private partial class ChromaArea : CompositeDrawable
|
private partial class ChromaArea : CompositeDrawable
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved]
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
|||||||
private readonly bool editor;
|
private readonly bool editor;
|
||||||
protected readonly FillFlowContainer<DrawableMatchTeam> Flow;
|
protected readonly FillFlowContainer<DrawableMatchTeam> Flow;
|
||||||
private readonly Drawable selectionBox;
|
private readonly Drawable selectionBox;
|
||||||
protected readonly Drawable CurrentMatchSelectionBox;
|
private readonly Drawable currentMatchSelectionBox;
|
||||||
private Bindable<TournamentMatch> globalSelection;
|
private Bindable<TournamentMatch> globalSelection;
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
@ -82,7 +82,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
|||||||
Padding = new MarginPadding(-(spacing + border_thickness)),
|
Padding = new MarginPadding(-(spacing + border_thickness)),
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Child = CurrentMatchSelectionBox = new Container
|
Child = currentMatchSelectionBox = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
@ -151,9 +151,9 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
|||||||
private void updateCurrentMatch()
|
private void updateCurrentMatch()
|
||||||
{
|
{
|
||||||
if (Match.Current.Value)
|
if (Match.Current.Value)
|
||||||
CurrentMatchSelectionBox.Show();
|
currentMatchSelectionBox.Show();
|
||||||
else
|
else
|
||||||
CurrentMatchSelectionBox.Hide();
|
currentMatchSelectionBox.Hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool selected;
|
private bool selected;
|
||||||
|
@ -37,6 +37,8 @@ namespace osu.Game.Tournament.Screens.MapPool
|
|||||||
private OsuButton buttonRedPick;
|
private OsuButton buttonRedPick;
|
||||||
private OsuButton buttonBluePick;
|
private OsuButton buttonBluePick;
|
||||||
|
|
||||||
|
private ScheduledDelegate scheduledScreenChange;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(MatchIPCInfo ipc)
|
private void load(MatchIPCInfo ipc)
|
||||||
{
|
{
|
||||||
@ -188,8 +190,6 @@ namespace osu.Game.Tournament.Screens.MapPool
|
|||||||
setNextMode();
|
setNextMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScheduledDelegate scheduledChange;
|
|
||||||
|
|
||||||
private void addForBeatmap(int beatmapId)
|
private void addForBeatmap(int beatmapId)
|
||||||
{
|
{
|
||||||
if (CurrentMatch.Value == null)
|
if (CurrentMatch.Value == null)
|
||||||
@ -216,12 +216,18 @@ namespace osu.Game.Tournament.Screens.MapPool
|
|||||||
{
|
{
|
||||||
if (pickType == ChoiceType.Pick && CurrentMatch.Value.PicksBans.Any(i => i.Type == ChoiceType.Pick))
|
if (pickType == ChoiceType.Pick && CurrentMatch.Value.PicksBans.Any(i => i.Type == ChoiceType.Pick))
|
||||||
{
|
{
|
||||||
scheduledChange?.Cancel();
|
scheduledScreenChange?.Cancel();
|
||||||
scheduledChange = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(GameplayScreen)); }, 10000);
|
scheduledScreenChange = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(GameplayScreen)); }, 10000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Hide()
|
||||||
|
{
|
||||||
|
scheduledScreenChange?.Cancel();
|
||||||
|
base.Hide();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch> match)
|
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch> match)
|
||||||
{
|
{
|
||||||
base.CurrentMatchChanged(match);
|
base.CurrentMatchChanged(match);
|
||||||
|
@ -218,8 +218,6 @@ namespace osu.Game.Tournament.Screens.Schedule
|
|||||||
|
|
||||||
Scale = new Vector2(0.8f);
|
Scale = new Vector2(0.8f);
|
||||||
|
|
||||||
CurrentMatchSelectionBox.Scale = new Vector2(1.02f, 1.15f);
|
|
||||||
|
|
||||||
bool conditional = match is ConditionalTournamentMatch;
|
bool conditional = match is ConditionalTournamentMatch;
|
||||||
|
|
||||||
if (conditional)
|
if (conditional)
|
||||||
|
@ -17,6 +17,7 @@ using osu.Framework.Platform;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Cursor;
|
using osu.Game.Graphics.Cursor;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Tournament.Models;
|
using osu.Game.Tournament.Models;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -41,6 +42,9 @@ namespace osu.Game.Tournament
|
|||||||
|
|
||||||
private LoadingSpinner loadingSpinner;
|
private LoadingSpinner loadingSpinner;
|
||||||
|
|
||||||
|
[Cached(typeof(IDialogOverlay))]
|
||||||
|
private readonly DialogOverlay dialogOverlay = new DialogOverlay();
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(FrameworkConfigManager frameworkConfig, GameHost host)
|
private void load(FrameworkConfigManager frameworkConfig, GameHost host)
|
||||||
{
|
{
|
||||||
@ -95,12 +99,12 @@ namespace osu.Game.Tournament
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = new TournamentSceneManager()
|
Child = new TournamentSceneManager()
|
||||||
}
|
},
|
||||||
|
dialogOverlay
|
||||||
}, drawables =>
|
}, drawables =>
|
||||||
{
|
{
|
||||||
loadingSpinner.Hide();
|
loadingSpinner.Hide();
|
||||||
loadingSpinner.Expire();
|
loadingSpinner.Expire();
|
||||||
|
|
||||||
AddRange(drawables);
|
AddRange(drawables);
|
||||||
|
|
||||||
windowSize.BindValueChanged(size => ScheduleAfterChildren(() =>
|
windowSize.BindValueChanged(size => ScheduleAfterChildren(() =>
|
||||||
|
@ -5,14 +5,9 @@ namespace osu.Game.Beatmaps
|
|||||||
{
|
{
|
||||||
public static class BeatSyncProviderExtensions
|
public static class BeatSyncProviderExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Check whether beat sync is currently available.
|
|
||||||
/// </summary>
|
|
||||||
public static bool CheckBeatSyncAvailable(this IBeatSyncProvider provider) => provider.Clock != null;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the beat sync provider is currently in a kiai section. Should make everything more epic.
|
/// Whether the beat sync provider is currently in a kiai section. Should make everything more epic.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool CheckIsKiaiTime(this IBeatSyncProvider provider) => provider.Clock != null && provider.ControlPoints?.EffectPointAt(provider.Clock.CurrentTime).KiaiMode == true;
|
public static bool CheckIsKiaiTime(this IBeatSyncProvider provider) => provider.ControlPoints?.EffectPointAt(provider.Clock.CurrentTime).KiaiMode == true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,8 @@ namespace osu.Game.Beatmaps
|
|||||||
ControlPointInfo? ControlPoints { get; }
|
ControlPointInfo? ControlPoints { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Access a clock currently responsible for providing beat sync. If <c>null</c>, no current provider is available.
|
/// Access a clock currently responsible for providing beat sync.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IClock? Clock { get; }
|
IClock Clock { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,11 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static double CalculatePlayableLength(this IBeatmap beatmap) => CalculatePlayableLength(beatmap.HitObjects);
|
public static double CalculatePlayableLength(this IBeatmap beatmap) => CalculatePlayableLength(beatmap.HitObjects);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Find the total milliseconds between the first and last hittable objects, excluding any break time.
|
||||||
|
/// </summary>
|
||||||
|
public static double CalculateDrainLength(this IBeatmap beatmap) => CalculatePlayableLength(beatmap.HitObjects) - beatmap.TotalBreakTime;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Find the timestamps in milliseconds of the start and end of the playable region.
|
/// Find the timestamps in milliseconds of the start and end of the playable region.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// 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.Diagnostics;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -86,14 +85,12 @@ namespace osu.Game.Graphics.Containers
|
|||||||
TimingControlPoint timingPoint;
|
TimingControlPoint timingPoint;
|
||||||
EffectControlPoint effectPoint;
|
EffectControlPoint effectPoint;
|
||||||
|
|
||||||
IsBeatSyncedWithTrack = BeatSyncSource.CheckBeatSyncAvailable() && BeatSyncSource.Clock?.IsRunning == true;
|
IsBeatSyncedWithTrack = BeatSyncSource.Clock.IsRunning;
|
||||||
|
|
||||||
double currentTrackTime;
|
double currentTrackTime;
|
||||||
|
|
||||||
if (IsBeatSyncedWithTrack)
|
if (IsBeatSyncedWithTrack)
|
||||||
{
|
{
|
||||||
Debug.Assert(BeatSyncSource.Clock != null);
|
|
||||||
|
|
||||||
currentTrackTime = BeatSyncSource.Clock.CurrentTime + EarlyActivationMilliseconds;
|
currentTrackTime = BeatSyncSource.Clock.CurrentTime + EarlyActivationMilliseconds;
|
||||||
|
|
||||||
timingPoint = BeatSyncSource.ControlPoints?.TimingPointAt(currentTrackTime) ?? TimingControlPoint.DEFAULT;
|
timingPoint = BeatSyncSource.ControlPoints?.TimingPointAt(currentTrackTime) ?? TimingControlPoint.DEFAULT;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.EnumExtensions;
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -20,9 +21,9 @@ namespace osu.Game.Graphics
|
|||||||
{
|
{
|
||||||
private readonly FallingParticle[] particles;
|
private readonly FallingParticle[] particles;
|
||||||
private int currentIndex;
|
private int currentIndex;
|
||||||
private double lastParticleAdded;
|
private double? lastParticleAdded;
|
||||||
|
|
||||||
private readonly double cooldown;
|
private readonly double timeBetweenSpawns;
|
||||||
private readonly double maxDuration;
|
private readonly double maxDuration;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -44,7 +45,7 @@ namespace osu.Game.Graphics
|
|||||||
|
|
||||||
particles = new FallingParticle[perSecond * (int)Math.Ceiling(maxDuration / 1000)];
|
particles = new FallingParticle[perSecond * (int)Math.Ceiling(maxDuration / 1000)];
|
||||||
|
|
||||||
cooldown = 1000f / perSecond;
|
timeBetweenSpawns = 1000f / perSecond;
|
||||||
this.maxDuration = maxDuration;
|
this.maxDuration = maxDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,18 +53,52 @@ namespace osu.Game.Graphics
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
if (Active.Value && CanSpawnParticles && Math.Abs(Time.Current - lastParticleAdded) > cooldown)
|
Invalidate(Invalidation.DrawNode);
|
||||||
|
|
||||||
|
if (!Active.Value || !CanSpawnParticles)
|
||||||
{
|
{
|
||||||
var newParticle = CreateParticle();
|
lastParticleAdded = null;
|
||||||
newParticle.StartTime = (float)Time.Current;
|
return;
|
||||||
|
|
||||||
particles[currentIndex] = newParticle;
|
|
||||||
|
|
||||||
currentIndex = (currentIndex + 1) % particles.Length;
|
|
||||||
lastParticleAdded = Time.Current;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Invalidate(Invalidation.DrawNode);
|
// Always want to spawn the first particle in an activation immediately.
|
||||||
|
if (lastParticleAdded == null)
|
||||||
|
{
|
||||||
|
lastParticleAdded = Time.Current;
|
||||||
|
spawnParticle();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double timeElapsed = Time.Current - lastParticleAdded.Value;
|
||||||
|
|
||||||
|
// Avoid spawning too many particles if a long amount of time has passed.
|
||||||
|
if (Math.Abs(timeElapsed) > maxDuration)
|
||||||
|
{
|
||||||
|
lastParticleAdded = Time.Current;
|
||||||
|
spawnParticle();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Assert(lastParticleAdded != null);
|
||||||
|
|
||||||
|
for (int i = 0; i < timeElapsed / timeBetweenSpawns; i++)
|
||||||
|
{
|
||||||
|
lastParticleAdded += timeBetweenSpawns;
|
||||||
|
spawnParticle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void spawnParticle()
|
||||||
|
{
|
||||||
|
Debug.Assert(lastParticleAdded != null);
|
||||||
|
|
||||||
|
var newParticle = CreateParticle();
|
||||||
|
|
||||||
|
newParticle.StartTime = (float)lastParticleAdded.Value;
|
||||||
|
|
||||||
|
particles[currentIndex] = newParticle;
|
||||||
|
|
||||||
|
currentIndex = (currentIndex + 1) % particles.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -73,7 +108,7 @@ namespace osu.Game.Graphics
|
|||||||
|
|
||||||
protected override DrawNode CreateDrawNode() => new ParticleSpewerDrawNode(this);
|
protected override DrawNode CreateDrawNode() => new ParticleSpewerDrawNode(this);
|
||||||
|
|
||||||
# region DrawNode
|
#region DrawNode
|
||||||
|
|
||||||
private class ParticleSpewerDrawNode : SpriteDrawNode
|
private class ParticleSpewerDrawNode : SpriteDrawNode
|
||||||
{
|
{
|
||||||
|
@ -950,9 +950,9 @@ namespace osu.Game
|
|||||||
if (!args?.Any(a => a == @"--no-version-overlay") ?? true)
|
if (!args?.Any(a => a == @"--no-version-overlay") ?? true)
|
||||||
loadComponentSingleFile(versionManager = new VersionManager { Depth = int.MinValue }, ScreenContainer.Add);
|
loadComponentSingleFile(versionManager = new VersionManager { Depth = int.MinValue }, ScreenContainer.Add);
|
||||||
|
|
||||||
loadComponentSingleFile(osuLogo, logo =>
|
loadComponentSingleFile(osuLogo, _ =>
|
||||||
{
|
{
|
||||||
logoContainer.Add(logo);
|
osuLogo.SetupDefaultContainer(logoContainer);
|
||||||
|
|
||||||
// Loader has to be created after the logo has finished loading as Loader performs logo transformations on entering.
|
// Loader has to be created after the logo has finished loading as Loader performs logo transformations on entering.
|
||||||
ScreenStack.Push(CreateLoader().With(l => l.RelativeSizeAxes = Axes.Both));
|
ScreenStack.Push(CreateLoader().With(l => l.RelativeSizeAxes = Axes.Both));
|
||||||
|
@ -638,8 +638,12 @@ namespace osu.Game.Overlays.Mods
|
|||||||
case GlobalAction.Select:
|
case GlobalAction.Select:
|
||||||
{
|
{
|
||||||
// Pressing select should select first filtered mod if a search is in progress.
|
// Pressing select should select first filtered mod if a search is in progress.
|
||||||
|
// If there is no search in progress, it should exit the dialog (a bit weird, but this is the expectation from stable).
|
||||||
if (string.IsNullOrEmpty(SearchTerm))
|
if (string.IsNullOrEmpty(SearchTerm))
|
||||||
|
{
|
||||||
|
hideOverlay(true);
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ModState? firstMod = columnFlow.Columns.OfType<ModColumn>().FirstOrDefault(m => m.IsPresent)?.AvailableMods.FirstOrDefault(x => x.Visible);
|
ModState? firstMod = columnFlow.Columns.OfType<ModColumn>().FirstOrDefault(m => m.IsPresent)?.AvailableMods.FirstOrDefault(x => x.Visible);
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
new CheckUnsnappedObjects(),
|
new CheckUnsnappedObjects(),
|
||||||
new CheckConcurrentObjects(),
|
new CheckConcurrentObjects(),
|
||||||
new CheckZeroLengthObjects(),
|
new CheckZeroLengthObjects(),
|
||||||
new CheckDrainTime(),
|
new CheckDrainLength(),
|
||||||
|
|
||||||
// Timing
|
// Timing
|
||||||
new CheckPreviewTime(),
|
new CheckPreviewTime(),
|
||||||
|
@ -7,10 +7,11 @@ using osu.Game.Rulesets.Edit.Checks.Components;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit.Checks
|
namespace osu.Game.Rulesets.Edit.Checks
|
||||||
{
|
{
|
||||||
public class CheckDrainTime : ICheck
|
public class CheckDrainLength : ICheck
|
||||||
{
|
{
|
||||||
private const int min_drain_threshold = 30 * 1000;
|
private const int min_drain_threshold = 30 * 1000;
|
||||||
public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Compose, "Too short drain time");
|
|
||||||
|
public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Compose, "Drain length is too short");
|
||||||
|
|
||||||
public IEnumerable<IssueTemplate> PossibleTemplates => new IssueTemplate[]
|
public IEnumerable<IssueTemplate> PossibleTemplates => new IssueTemplate[]
|
||||||
{
|
{
|
||||||
@ -19,7 +20,7 @@ namespace osu.Game.Rulesets.Edit.Checks
|
|||||||
|
|
||||||
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
|
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
|
||||||
{
|
{
|
||||||
double drainTime = context.Beatmap.CalculatePlayableLength() - context.Beatmap.TotalBreakTime;
|
double drainTime = context.Beatmap.CalculateDrainLength();
|
||||||
|
|
||||||
if (drainTime < min_drain_threshold)
|
if (drainTime < min_drain_threshold)
|
||||||
yield return new IssueTemplateTooShort(this).Create((int)(drainTime / 1000));
|
yield return new IssueTemplateTooShort(this).Create((int)(drainTime / 1000));
|
@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<SelectionBlueprint<T>> Deselected;
|
public event Action<SelectionBlueprint<T>> Deselected;
|
||||||
|
|
||||||
public override bool HandlePositionalInput => ShouldBeAlive;
|
public override bool HandlePositionalInput => IsSelectable;
|
||||||
public override bool RemoveWhenNotAlive => false;
|
public override bool RemoveWhenNotAlive => false;
|
||||||
|
|
||||||
protected SelectionBlueprint(T item)
|
protected SelectionBlueprint(T item)
|
||||||
@ -125,6 +125,11 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual MenuItem[] ContextMenuItems => Array.Empty<MenuItem>();
|
public virtual MenuItem[] ContextMenuItems => Array.Empty<MenuItem>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the <see cref="SelectionBlueprint{T}"/> can be currently selected via a click or a drag box.
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool IsSelectable => ShouldBeAlive && IsPresent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The screen-space main point that causes this <see cref="HitObjectSelectionBlueprint"/> to be selected via a drag.
|
/// The screen-space main point that causes this <see cref="HitObjectSelectionBlueprint"/> to be selected via a drag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
int timeSignature = timingPoint.TimeSignature.Numerator;
|
int timeSignature = timingPoint.TimeSignature.Numerator;
|
||||||
|
|
||||||
// play metronome from one measure before the first object.
|
// play metronome from one measure before the first object.
|
||||||
if (BeatSyncSource.Clock?.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature)
|
if (BeatSyncSource.Clock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f;
|
sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f;
|
||||||
|
@ -487,7 +487,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SelectionState.NotSelected:
|
case SelectionState.NotSelected:
|
||||||
if (blueprint.IsAlive && blueprint.IsPresent && quad.Contains(blueprint.ScreenSpaceSelectionPoint))
|
if (blueprint.IsSelectable && quad.Contains(blueprint.ScreenSpaceSelectionPoint))
|
||||||
blueprint.Select();
|
blueprint.Select();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -240,7 +240,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
if (BeatSyncSource.ControlPoints == null || BeatSyncSource.Clock == null)
|
if (BeatSyncSource.ControlPoints == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
metronomeClock.Rate = IsBeatSyncedWithTrack ? BeatSyncSource.Clock.Rate : 1;
|
metronomeClock.Rate = IsBeatSyncedWithTrack ? BeatSyncSource.Clock.Rate : 1;
|
||||||
@ -259,7 +259,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
this.TransformBindableTo(interpolatedBpm, (int)Math.Round(timingPoint.BPM), 600, Easing.OutQuint);
|
this.TransformBindableTo(interpolatedBpm, (int)Math.Round(timingPoint.BPM), 600, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BeatSyncSource.Clock?.IsRunning != true && isSwinging)
|
if (!BeatSyncSource.Clock.IsRunning && isSwinging)
|
||||||
{
|
{
|
||||||
swing.ClearTransforms(true);
|
swing.ClearTransforms(true);
|
||||||
|
|
||||||
|
@ -310,7 +310,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
}
|
}
|
||||||
|
|
||||||
double averageBeatLength = (tapTimings.Last() - tapTimings.Skip(initial_taps_to_ignore).First()) / (tapTimings.Count - initial_taps_to_ignore - 1);
|
double averageBeatLength = (tapTimings.Last() - tapTimings.Skip(initial_taps_to_ignore).First()) / (tapTimings.Count - initial_taps_to_ignore - 1);
|
||||||
double clockRate = beatSyncSource?.Clock?.Rate ?? 1;
|
double clockRate = beatSyncSource?.Clock.Rate ?? 1;
|
||||||
|
|
||||||
double bpm = Math.Round(60000 / averageBeatLength / clockRate);
|
double bpm = Math.Round(60000 / averageBeatLength / clockRate);
|
||||||
|
|
||||||
|
67
osu.Game/Screens/Menu/KiaiMenuFountains.cs
Normal file
67
osu.Game/Screens/Menu/KiaiMenuFountains.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Menu
|
||||||
|
{
|
||||||
|
public partial class KiaiMenuFountains : BeatSyncedContainer
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new StarFountain
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
X = 250,
|
||||||
|
},
|
||||||
|
new StarFountain
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomRight,
|
||||||
|
Origin = Anchor.BottomRight,
|
||||||
|
X = -250,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool isTriggered;
|
||||||
|
|
||||||
|
private double? lastTrigger;
|
||||||
|
|
||||||
|
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
|
||||||
|
{
|
||||||
|
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
|
||||||
|
|
||||||
|
if (effectPoint.KiaiMode && !isTriggered)
|
||||||
|
{
|
||||||
|
bool isNearEffectPoint = Math.Abs(BeatSyncSource.Clock.CurrentTime - effectPoint.Time) < 500;
|
||||||
|
if (isNearEffectPoint)
|
||||||
|
Shoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
isTriggered = effectPoint.KiaiMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Shoot()
|
||||||
|
{
|
||||||
|
if (lastTrigger != null && Clock.CurrentTime - lastTrigger < 500)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var fountain in Children.OfType<StarFountain>())
|
||||||
|
fountain.Shoot();
|
||||||
|
|
||||||
|
lastTrigger = Clock.CurrentTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -102,8 +102,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
for (int i = 0; i < temporalAmplitudes.Length; i++)
|
for (int i = 0; i < temporalAmplitudes.Length; i++)
|
||||||
temporalAmplitudes[i] = 0;
|
temporalAmplitudes[i] = 0;
|
||||||
|
|
||||||
if (beatSyncProvider.Clock != null)
|
addAmplitudesFromSource(beatSyncProvider);
|
||||||
addAmplitudesFromSource(beatSyncProvider);
|
|
||||||
|
|
||||||
foreach (var source in amplitudeSources)
|
foreach (var source in amplitudeSources)
|
||||||
addAmplitudesFromSource(source);
|
addAmplitudesFromSource(source);
|
||||||
|
@ -8,6 +8,7 @@ using System.Diagnostics;
|
|||||||
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.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
@ -85,6 +86,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
private ParallaxContainer buttonsContainer;
|
private ParallaxContainer buttonsContainer;
|
||||||
private SongTicker songTicker;
|
private SongTicker songTicker;
|
||||||
|
private Container logoTarget;
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics)
|
private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics)
|
||||||
@ -129,6 +131,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
logoTarget = new Container { RelativeSizeAxes = Axes.Both, },
|
||||||
sideFlashes = new MenuSideFlashes(),
|
sideFlashes = new MenuSideFlashes(),
|
||||||
songTicker = new SongTicker
|
songTicker = new SongTicker
|
||||||
{
|
{
|
||||||
@ -136,6 +139,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
Margin = new MarginPadding { Right = 15, Top = 5 }
|
Margin = new MarginPadding { Right = 15, Top = 5 }
|
||||||
},
|
},
|
||||||
|
new KiaiMenuFountains(),
|
||||||
holdToExitGameOverlay?.CreateProxy() ?? Empty()
|
holdToExitGameOverlay?.CreateProxy() ?? Empty()
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -207,6 +211,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
logo.FadeColour(Color4.White, 100, Easing.OutQuint);
|
logo.FadeColour(Color4.White, 100, Easing.OutQuint);
|
||||||
logo.FadeIn(100, Easing.OutQuint);
|
logo.FadeIn(100, Easing.OutQuint);
|
||||||
|
|
||||||
|
logo.ProxyToContainer(logoTarget);
|
||||||
|
|
||||||
if (resuming)
|
if (resuming)
|
||||||
{
|
{
|
||||||
Buttons.State = ButtonSystemState.TopLevel;
|
Buttons.State = ButtonSystemState.TopLevel;
|
||||||
@ -244,6 +250,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
var seq = logo.FadeOut(300, Easing.InSine)
|
var seq = logo.FadeOut(300, Easing.InSine)
|
||||||
.ScaleTo(0.2f, 300, Easing.InSine);
|
.ScaleTo(0.2f, 300, Easing.InSine);
|
||||||
|
|
||||||
|
logo.ReturnProxy();
|
||||||
|
|
||||||
seq.OnComplete(_ => Buttons.SetOsuLogo(null));
|
seq.OnComplete(_ => Buttons.SetOsuLogo(null));
|
||||||
seq.OnAbort(_ => Buttons.SetOsuLogo(null));
|
seq.OnAbort(_ => Buttons.SetOsuLogo(null));
|
||||||
}
|
}
|
||||||
|
@ -435,5 +435,46 @@ namespace osu.Game.Screens.Menu
|
|||||||
logoBounceContainer.MoveTo(Vector2.Zero, 800, Easing.OutElastic);
|
logoBounceContainer.MoveTo(Vector2.Zero, 800, Easing.OutElastic);
|
||||||
base.OnDragEnd(e);
|
base.OnDragEnd(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Container defaultProxyTarget;
|
||||||
|
private Container currentProxyTarget;
|
||||||
|
private Drawable proxy;
|
||||||
|
|
||||||
|
public Drawable ProxyToContainer(Container c)
|
||||||
|
{
|
||||||
|
if (currentProxyTarget != null)
|
||||||
|
throw new InvalidOperationException("Previous proxy usage was not returned");
|
||||||
|
|
||||||
|
if (defaultProxyTarget == null)
|
||||||
|
throw new InvalidOperationException($"{nameof(SetupDefaultContainer)} must be called first");
|
||||||
|
|
||||||
|
currentProxyTarget = c;
|
||||||
|
|
||||||
|
defaultProxyTarget.Remove(proxy, false);
|
||||||
|
currentProxyTarget.Add(proxy);
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReturnProxy()
|
||||||
|
{
|
||||||
|
if (currentProxyTarget == null)
|
||||||
|
throw new InvalidOperationException("No usage to return");
|
||||||
|
|
||||||
|
if (defaultProxyTarget == null)
|
||||||
|
throw new InvalidOperationException($"{nameof(SetupDefaultContainer)} must be called first");
|
||||||
|
|
||||||
|
currentProxyTarget.Remove(proxy, false);
|
||||||
|
currentProxyTarget = null;
|
||||||
|
|
||||||
|
defaultProxyTarget.Add(proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetupDefaultContainer(Container container)
|
||||||
|
{
|
||||||
|
defaultProxyTarget = container;
|
||||||
|
|
||||||
|
defaultProxyTarget.Add(this);
|
||||||
|
defaultProxyTarget.Add(proxy = CreateProxy());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
93
osu.Game/Screens/Menu/StarFountain.cs
Normal file
93
osu.Game/Screens/Menu/StarFountain.cs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// 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.Graphics.Textures;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Menu
|
||||||
|
{
|
||||||
|
public partial class StarFountain : SkinReloadableDrawable
|
||||||
|
{
|
||||||
|
private StarFountainSpewer spewer = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private TextureStore textures { get; set; } = null!;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
InternalChild = spewer = new StarFountainSpewer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Shoot() => spewer.Shoot();
|
||||||
|
|
||||||
|
protected override void SkinChanged(ISkinSource skin)
|
||||||
|
{
|
||||||
|
base.SkinChanged(skin);
|
||||||
|
spewer.Texture = skin.GetTexture("Menu/fountain-star") ?? textures.Get("Menu/fountain-star");
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class StarFountainSpewer : ParticleSpewer
|
||||||
|
{
|
||||||
|
private const int particle_duration_min = 300;
|
||||||
|
private const int particle_duration_max = 1000;
|
||||||
|
|
||||||
|
private double? lastShootTime;
|
||||||
|
private int lastShootDirection;
|
||||||
|
|
||||||
|
protected override float ParticleGravity => 800;
|
||||||
|
|
||||||
|
private const double shoot_duration = 800;
|
||||||
|
|
||||||
|
protected override bool CanSpawnParticles => lastShootTime != null && Time.Current - lastShootTime < shoot_duration;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private ISkinSource skin { get; set; } = null!;
|
||||||
|
|
||||||
|
public StarFountainSpewer()
|
||||||
|
: base(null, 240, particle_duration_max)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(TextureStore textures)
|
||||||
|
{
|
||||||
|
Texture = skin.GetTexture("Menu/fountain-star") ?? textures.Get("Menu/fountain-star");
|
||||||
|
Active.Value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override FallingParticle CreateParticle()
|
||||||
|
{
|
||||||
|
return new FallingParticle
|
||||||
|
{
|
||||||
|
StartPosition = new Vector2(0, 50),
|
||||||
|
Duration = RNG.NextSingle(particle_duration_min, particle_duration_max),
|
||||||
|
StartAngle = getRandomVariance(4),
|
||||||
|
EndAngle = getRandomVariance(2),
|
||||||
|
EndScale = 2.2f + getRandomVariance(0.4f),
|
||||||
|
Velocity = new Vector2(getCurrentAngle(), -1400 + getRandomVariance(100)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getCurrentAngle()
|
||||||
|
{
|
||||||
|
const float x_velocity_from_direction = 500;
|
||||||
|
const float x_velocity_random_variance = 60;
|
||||||
|
|
||||||
|
return lastShootDirection * x_velocity_from_direction * (float)(1 - 2 * (Clock.CurrentTime - lastShootTime!.Value) / shoot_duration) + getRandomVariance(x_velocity_random_variance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Shoot()
|
||||||
|
{
|
||||||
|
lastShootTime = Clock.CurrentTime;
|
||||||
|
lastShootDirection = RNG.Next(-1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getRandomVariance(float variance) => RNG.NextSingle(-variance, variance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,7 @@ using osu.Framework.Threading;
|
|||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Database;
|
||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.IO.Archives;
|
using osu.Game.IO.Archives;
|
||||||
@ -1170,6 +1171,7 @@ namespace osu.Game.Screens.Play
|
|||||||
// because of the clone above, it's required that we copy back the post-import hash/ID to use for availability matching.
|
// because of the clone above, it's required that we copy back the post-import hash/ID to use for availability matching.
|
||||||
score.ScoreInfo.Hash = s.Hash;
|
score.ScoreInfo.Hash = s.Hash;
|
||||||
score.ScoreInfo.ID = s.ID;
|
score.ScoreInfo.ID = s.ID;
|
||||||
|
score.ScoreInfo.Files.AddRange(s.Files.Detach());
|
||||||
});
|
});
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
@ -107,6 +107,9 @@ namespace osu.Game.Screens.Ranking
|
|||||||
|
|
||||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||||
{
|
{
|
||||||
|
if (e.Repeat)
|
||||||
|
return false;
|
||||||
|
|
||||||
switch (e.Action)
|
switch (e.Action)
|
||||||
{
|
{
|
||||||
case GlobalAction.SaveReplay:
|
case GlobalAction.SaveReplay:
|
||||||
|
@ -352,29 +352,6 @@ namespace osu.Game.Screens.Select
|
|||||||
if (working.Beatmap?.HitObjects.Any() != true)
|
if (working.Beatmap?.HitObjects.Any() != true)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
infoLabelContainer.Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new InfoLabel(new BeatmapStatistic
|
|
||||||
{
|
|
||||||
Name = "Length",
|
|
||||||
CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length),
|
|
||||||
Content = working.BeatmapInfo.Length.ToFormattedDuration().ToString(),
|
|
||||||
}),
|
|
||||||
bpmLabelContainer = new Container
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
new FillFlowContainer
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Spacing = new Vector2(20, 0),
|
|
||||||
Children = getRulesetInfoLabels()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private InfoLabel[] getRulesetInfoLabels()
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IBeatmap playableBeatmap;
|
IBeatmap playableBeatmap;
|
||||||
@ -390,14 +367,30 @@ namespace osu.Game.Screens.Select
|
|||||||
playableBeatmap = working.GetPlayableBeatmap(working.BeatmapInfo.Ruleset, Array.Empty<Mod>());
|
playableBeatmap = working.GetPlayableBeatmap(working.BeatmapInfo.Ruleset, Array.Empty<Mod>());
|
||||||
}
|
}
|
||||||
|
|
||||||
return playableBeatmap.GetStatistics().Select(s => new InfoLabel(s)).ToArray();
|
infoLabelContainer.Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new InfoLabel(new BeatmapStatistic
|
||||||
|
{
|
||||||
|
Name = $"Length (Drain: {playableBeatmap.CalculateDrainLength().ToFormattedDuration().ToString()})",
|
||||||
|
CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length),
|
||||||
|
Content = working.BeatmapInfo.Length.ToFormattedDuration().ToString(),
|
||||||
|
}),
|
||||||
|
bpmLabelContainer = new Container
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Spacing = new Vector2(20, 0),
|
||||||
|
Children = playableBeatmap.GetStatistics().Select(s => new InfoLabel(s)).ToArray()
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Error(e, "Could not load beatmap successfully!");
|
Logger.Error(e, "Could not load beatmap successfully!");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.Empty<InfoLabel>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshBPMLabel()
|
private void refreshBPMLabel()
|
||||||
|
@ -468,6 +468,13 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
public override Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT)
|
public override Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT)
|
||||||
{
|
{
|
||||||
|
switch (componentName)
|
||||||
|
{
|
||||||
|
case "Menu/fountain-star":
|
||||||
|
componentName = "star2";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (string name in getFallbackNames(componentName))
|
foreach (string name in getFallbackNames(componentName))
|
||||||
{
|
{
|
||||||
// some component names (especially user-controlled ones, like `HitX` in mania)
|
// some component names (especially user-controlled ones, like `HitX` in mania)
|
||||||
|
@ -128,7 +128,7 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
//
|
//
|
||||||
// In the case of storyboard animations, we want to synchronise with game time perfectly
|
// In the case of storyboard animations, we want to synchronise with game time perfectly
|
||||||
// so let's get a correct time based on gameplay clock and earliest transform.
|
// so let's get a correct time based on gameplay clock and earliest transform.
|
||||||
PlaybackPosition = (beatSyncProvider.Clock?.CurrentTime ?? Clock.CurrentTime) - Animation.EarliestTransformTime;
|
PlaybackPosition = beatSyncProvider.Clock.CurrentTime - Animation.EarliestTransformTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void skinSourceChanged()
|
private void skinSourceChanged()
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="11.1.2" />
|
<PackageReference Include="Realm" Version="11.1.2" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2023.724.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2023.724.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.707.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.719.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.28.1" />
|
<PackageReference Include="Sentry" Version="3.28.1" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
|
Loading…
Reference in New Issue
Block a user