mirror of
https://github.com/ppy/osu.git
synced 2025-03-15 17:47:18 +08:00
Merge branch 'master' into fix-leaderboard-first-place-fade
This commit is contained in:
commit
c18d4afdcc
@ -51,8 +51,8 @@
|
||||
<Reference Include="Java.Interop" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.831.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.928.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1005.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1005.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
@ -137,12 +137,13 @@ namespace osu.Desktop
|
||||
{
|
||||
base.SetHost(host);
|
||||
|
||||
var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico");
|
||||
|
||||
var desktopWindow = (SDL2DesktopWindow)host.Window;
|
||||
|
||||
var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico");
|
||||
if (iconStream != null)
|
||||
desktopWindow.SetIconFromStream(iconStream);
|
||||
|
||||
desktopWindow.CursorState |= CursorState.Hidden;
|
||||
desktopWindow.SetIconFromStream(iconStream);
|
||||
desktopWindow.Title = Name;
|
||||
desktopWindow.DragDrop += f => fileDrop(new[] { f });
|
||||
}
|
||||
|
@ -1,10 +1,15 @@
|
||||
// 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 System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Mods;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
@ -12,11 +17,11 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
[TestFixture]
|
||||
public class TestSceneCatchTouchInput : OsuTestScene
|
||||
{
|
||||
private CatchTouchInputMapper catchTouchInputMapper = null!;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
[Test]
|
||||
public void TestBasic()
|
||||
{
|
||||
CatchTouchInputMapper catchTouchInputMapper = null!;
|
||||
|
||||
AddStep("create input overlay", () =>
|
||||
{
|
||||
Child = new CatchInputManager(new CatchRuleset().RulesetInfo)
|
||||
@ -32,12 +37,30 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("show overlay", () => catchTouchInputMapper.Show());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBasic()
|
||||
public void TestWithoutRelax()
|
||||
{
|
||||
AddStep("show overlay", () => catchTouchInputMapper.Show());
|
||||
AddStep("create drawable ruleset without relax mod", () =>
|
||||
{
|
||||
Child = new DrawableCatchRuleset(new CatchRuleset(), new CatchBeatmap(), new List<Mod>());
|
||||
});
|
||||
AddUntilStep("wait for load", () => Child.IsLoaded);
|
||||
AddAssert("check touch input is shown", () => this.ChildrenOfType<CatchTouchInputMapper>().Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWithRelax()
|
||||
{
|
||||
AddStep("create drawable ruleset with relax mod", () =>
|
||||
{
|
||||
Child = new DrawableCatchRuleset(new CatchRuleset(), new CatchBeatmap(), new List<Mod> { new CatchModRelax() });
|
||||
});
|
||||
AddUntilStep("wait for load", () => Child.IsLoaded);
|
||||
AddAssert("check touch input is not shown", () => !this.ChildrenOfType<CatchTouchInputMapper>().Any());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
|
||||
public override BindableBool ComboBasedSize { get; } = new BindableBool(true);
|
||||
|
||||
public override float DefaultFlashlightSize => 350;
|
||||
public override float DefaultFlashlightSize => 325;
|
||||
|
||||
protected override Flashlight CreateFlashlight() => new CatchFlashlight(this, playfield);
|
||||
|
||||
@ -44,7 +44,19 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
: base(modFlashlight)
|
||||
{
|
||||
this.playfield = playfield;
|
||||
|
||||
FlashlightSize = new Vector2(0, GetSizeFor(0));
|
||||
FlashlightSmoothness = 1.4f;
|
||||
}
|
||||
|
||||
protected override float GetComboScaleFor(int combo)
|
||||
{
|
||||
if (combo >= 200)
|
||||
return 0.770f;
|
||||
if (combo >= 100)
|
||||
return 0.885f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
|
@ -4,6 +4,7 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -36,7 +37,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
KeyBindingInputManager.Add(new CatchTouchInputMapper());
|
||||
// With relax mod, input maps directly to x position and left/right buttons are not used.
|
||||
if (!Mods.Any(m => m is ModRelax))
|
||||
KeyBindingInputManager.Add(new CatchTouchInputMapper());
|
||||
}
|
||||
|
||||
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -30,15 +31,18 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
[Cached(typeof(IScrollingInfo))]
|
||||
private IScrollingInfo scrollingInfo;
|
||||
|
||||
[Cached]
|
||||
private readonly StageDefinition stage = new StageDefinition(5);
|
||||
|
||||
protected ManiaPlacementBlueprintTestScene()
|
||||
{
|
||||
scrollingInfo = ((ScrollingTestContainer)HitObjectContainer).ScrollingInfo;
|
||||
|
||||
Add(column = new Column(0)
|
||||
Add(column = new Column(0, false)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AccentColour = Color4.OrangeRed,
|
||||
AccentColour = { Value = Color4.OrangeRed },
|
||||
Clock = new FramedClock(new StopwatchClock()), // No scroll
|
||||
});
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
|
||||
protected ManiaSelectionBlueprintTestScene(int columns)
|
||||
{
|
||||
var stageDefinitions = new List<StageDefinition> { new StageDefinition { Columns = columns } };
|
||||
var stageDefinitions = new List<StageDefinition> { new StageDefinition(columns) };
|
||||
base.Content.Child = scrollingTestContainer = new ScrollingTestContainer(ScrollingDirection.Up)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
private ScrollingTestContainer.TestScrollingInfo scrollingInfo = new ScrollingTestContainer.TestScrollingInfo();
|
||||
|
||||
[Cached(typeof(EditorBeatmap))]
|
||||
private EditorBeatmap editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition())
|
||||
private EditorBeatmap editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition(2))
|
||||
{
|
||||
BeatmapInfo =
|
||||
{
|
||||
@ -56,8 +56,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
{
|
||||
Playfield = new ManiaPlayfield(new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = 4 },
|
||||
new StageDefinition { Columns = 3 }
|
||||
new StageDefinition(4),
|
||||
new StageDefinition(3)
|
||||
})
|
||||
{
|
||||
Clock = new FramedClock(new StopwatchClock())
|
||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
{
|
||||
AddStep("setup compose screen", () =>
|
||||
{
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 4 })
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition(4))
|
||||
{
|
||||
BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo },
|
||||
};
|
||||
|
@ -205,7 +205,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
EditorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 })
|
||||
EditorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition(4))
|
||||
{
|
||||
BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo }
|
||||
}),
|
||||
|
@ -1,52 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ManiaColumnTypeTest
|
||||
{
|
||||
[TestCase(new[]
|
||||
{
|
||||
ColumnType.Special
|
||||
}, 1)]
|
||||
[TestCase(new[]
|
||||
{
|
||||
ColumnType.Odd,
|
||||
ColumnType.Even,
|
||||
ColumnType.Even,
|
||||
ColumnType.Odd
|
||||
}, 4)]
|
||||
[TestCase(new[]
|
||||
{
|
||||
ColumnType.Odd,
|
||||
ColumnType.Even,
|
||||
ColumnType.Odd,
|
||||
ColumnType.Special,
|
||||
ColumnType.Odd,
|
||||
ColumnType.Even,
|
||||
ColumnType.Odd
|
||||
}, 7)]
|
||||
public void Test(IEnumerable<ColumnType> expected, int columns)
|
||||
{
|
||||
var definition = new StageDefinition
|
||||
{
|
||||
Columns = columns
|
||||
};
|
||||
var results = getResults(definition);
|
||||
Assert.AreEqual(expected, results);
|
||||
}
|
||||
|
||||
private IEnumerable<ColumnType> getResults(StageDefinition definition)
|
||||
{
|
||||
for (int i = 0; i < definition.Columns; i++)
|
||||
yield return definition.GetTypeOfColumn(i);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
[TestCase(ManiaAction.Key8)]
|
||||
public void TestEncodeDecodeSingleStage(params ManiaAction[] actions)
|
||||
{
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 9 });
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition(9));
|
||||
|
||||
var frame = new ManiaReplayFrame(0, actions);
|
||||
var legacyFrame = frame.ToLegacy(beatmap);
|
||||
@ -38,8 +38,8 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
[TestCase(ManiaAction.Key8)]
|
||||
public void TestEncodeDecodeDualStage(params ManiaAction[] actions)
|
||||
{
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 5 });
|
||||
beatmap.Stages.Add(new StageDefinition { Columns = 5 });
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition(5));
|
||||
beatmap.Stages.Add(new StageDefinition(5));
|
||||
|
||||
var frame = new ManiaReplayFrame(0, actions);
|
||||
var legacyFrame = frame.ToLegacy(beatmap);
|
||||
|
49
osu.Game.Rulesets.Mania.Tests/ManiaSpecialColumnTest.cs
Normal file
49
osu.Game.Rulesets.Mania.Tests/ManiaSpecialColumnTest.cs
Normal file
@ -0,0 +1,49 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ManiaSpecialColumnTest
|
||||
{
|
||||
[TestCase(new[]
|
||||
{
|
||||
true
|
||||
}, 1)]
|
||||
[TestCase(new[]
|
||||
{
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
}, 4)]
|
||||
[TestCase(new[]
|
||||
{
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
}, 7)]
|
||||
public void Test(IEnumerable<bool> special, int columns)
|
||||
{
|
||||
var definition = new StageDefinition(columns);
|
||||
var results = getResults(definition);
|
||||
Assert.AreEqual(special, results);
|
||||
}
|
||||
|
||||
private IEnumerable<bool> getResults(StageDefinition definition)
|
||||
{
|
||||
for (int i = 0; i < definition.Columns; i++)
|
||||
yield return definition.IsSpecialColumn(i);
|
||||
}
|
||||
}
|
||||
}
|
@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
|
||||
|
||||
private static ManiaBeatmap createRawBeatmap()
|
||||
{
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 });
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition(1));
|
||||
beatmap.ControlPointInfo.Add(0.0, new TimingControlPoint { BeatLength = 1000 }); // Set BPM to 60
|
||||
|
||||
// Add test hit objects
|
||||
|
@ -8,7 +8,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
@ -24,15 +23,16 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[Cached]
|
||||
private readonly Column column;
|
||||
|
||||
[Cached]
|
||||
private readonly StageDefinition stageDefinition = new StageDefinition(5);
|
||||
|
||||
public ColumnTestContainer(int column, ManiaAction action, bool showColumn = false)
|
||||
{
|
||||
InternalChildren = new[]
|
||||
{
|
||||
this.column = new Column(column)
|
||||
this.column = new Column(column, false)
|
||||
{
|
||||
Action = { Value = action },
|
||||
AccentColour = Color4.Orange,
|
||||
ColumnType = column % 2 == 0 ? ColumnType.Even : ColumnType.Odd,
|
||||
Alpha = showColumn ? 1 : 0
|
||||
},
|
||||
content = new ManiaInputManager(new ManiaRuleset().RulesetInfo, 4)
|
||||
|
@ -61,7 +61,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
c.Add(CreateHitObject().With(h =>
|
||||
{
|
||||
h.HitObject.StartTime = Time.Current + 5000;
|
||||
h.AccentColour.Value = Color4.Orange;
|
||||
}));
|
||||
})
|
||||
},
|
||||
|
@ -9,6 +9,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Rulesets.UI.Scrolling.Algorithms;
|
||||
using osu.Game.Tests.Visual;
|
||||
@ -24,6 +25,9 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[Cached(Type = typeof(IScrollingInfo))]
|
||||
private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
|
||||
|
||||
[Cached]
|
||||
private readonly StageDefinition stage = new StageDefinition(4);
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new ManiaRuleset();
|
||||
|
||||
protected ManiaSkinnableTestScene()
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
var stageDefinitions = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = 4 },
|
||||
new StageDefinition(4),
|
||||
};
|
||||
|
||||
SetContents(_ => new ManiaPlayfield(stageDefinitions).With(s =>
|
||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
stageDefinitions = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = 2 }
|
||||
new StageDefinition(2)
|
||||
};
|
||||
|
||||
SetContents(_ => new ManiaPlayfield(stageDefinitions));
|
||||
@ -36,8 +36,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
stageDefinitions = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = 2 },
|
||||
new StageDefinition { Columns = 2 }
|
||||
new StageDefinition(2),
|
||||
new StageDefinition(2)
|
||||
};
|
||||
|
||||
SetContents(_ => new ManiaPlayfield(stageDefinitions));
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
|
||||
return new ManiaInputManager(new ManiaRuleset().RulesetInfo, 4)
|
||||
{
|
||||
Child = new Stage(0, new StageDefinition { Columns = 4 }, ref normalAction, ref specialAction)
|
||||
Child = new Stage(0, new StageDefinition(4), ref normalAction, ref specialAction)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.UI.Components;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
@ -16,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: new StageDefinition { Columns = 4 }),
|
||||
SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground),
|
||||
_ => new DefaultStageBackground())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
@ -15,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: new StageDefinition { Columns = 4 }), _ => null)
|
||||
SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground), _ => null)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
// | - |
|
||||
// | |
|
||||
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 });
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition(1));
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
// | * |
|
||||
// | |
|
||||
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 });
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition(1));
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
// | - | - |
|
||||
// | | |
|
||||
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition(2));
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 1000, Column = 1 });
|
||||
|
||||
@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
// | * | * |
|
||||
// | | |
|
||||
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition(2));
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000, Column = 1 });
|
||||
|
||||
@ -115,7 +115,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
// | - | |
|
||||
// | | |
|
||||
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition(2));
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 2000, Column = 1 });
|
||||
|
||||
@ -142,7 +142,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
// | * | |
|
||||
// | | |
|
||||
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition(2));
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 2000, Duration = 2000, Column = 1 });
|
||||
|
||||
@ -169,7 +169,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
// | * | |
|
||||
// | | |
|
||||
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition(2));
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 });
|
||||
|
||||
|
@ -11,6 +11,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
@ -28,6 +29,9 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
[Cached(typeof(IReadOnlyList<Mod>))]
|
||||
private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>();
|
||||
|
||||
[Cached]
|
||||
private readonly StageDefinition stage = new StageDefinition(1);
|
||||
|
||||
private readonly List<Column> columns = new List<Column>();
|
||||
|
||||
public TestSceneColumn()
|
||||
@ -84,12 +88,12 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
private Drawable createColumn(ScrollingDirection direction, ManiaAction action, int index)
|
||||
{
|
||||
var column = new Column(index)
|
||||
var column = new Column(index, false)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Height = 0.85f,
|
||||
AccentColour = Color4.OrangeRed,
|
||||
AccentColour = { Value = Color4.OrangeRed },
|
||||
Action = { Value = action },
|
||||
};
|
||||
|
||||
|
@ -4,11 +4,13 @@
|
||||
#nullable disable
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
@ -24,6 +26,9 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
private Column column;
|
||||
|
||||
[Cached]
|
||||
private readonly StageDefinition stage = new StageDefinition(1);
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
@ -35,11 +40,11 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
TimeRange = 2000,
|
||||
Clock = new FramedClock(clock),
|
||||
Child = column = new Column(0)
|
||||
Child = column = new Column(0, false)
|
||||
{
|
||||
Action = { Value = ManiaAction.Key1 },
|
||||
Height = 0.85f,
|
||||
AccentColour = Color4.Gray
|
||||
AccentColour = { Value = Color4.Gray },
|
||||
},
|
||||
};
|
||||
});
|
||||
|
@ -141,7 +141,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
AddStep("load player", () =>
|
||||
{
|
||||
Beatmap.Value = CreateWorkingBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 })
|
||||
Beatmap.Value = CreateWorkingBeatmap(new ManiaBeatmap(new StageDefinition(4))
|
||||
{
|
||||
HitObjects = hitObjects,
|
||||
BeatmapInfo =
|
||||
|
@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
var specialAction = ManiaAction.Special1;
|
||||
|
||||
var stage = new Stage(0, new StageDefinition { Columns = 2 }, ref action, ref specialAction);
|
||||
var stage = new Stage(0, new StageDefinition(2), ref action, ref specialAction);
|
||||
stages.Add(stage);
|
||||
|
||||
return new ScrollingTestContainer(direction)
|
||||
|
@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
const double beat_length = 500;
|
||||
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 })
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition(1))
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
|
@ -1,14 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
{
|
||||
public enum ColumnType
|
||||
{
|
||||
Even,
|
||||
Odd,
|
||||
Special
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -60,5 +61,18 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public StageDefinition GetStageForColumnIndex(int column)
|
||||
{
|
||||
foreach (var stage in Stages)
|
||||
{
|
||||
if (column < stage.Columns)
|
||||
return stage;
|
||||
|
||||
column -= stage.Columns;
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(column), "Provided index exceeds all available stages");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -93,10 +93,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
|
||||
protected override Beatmap<ManiaHitObject> CreateBeatmap()
|
||||
{
|
||||
beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns }, originalTargetColumns);
|
||||
beatmap = new ManiaBeatmap(new StageDefinition(TargetColumns), originalTargetColumns);
|
||||
|
||||
if (Dual)
|
||||
beatmap.Stages.Add(new StageDefinition { Columns = TargetColumns });
|
||||
beatmap.Stages.Add(new StageDefinition(TargetColumns));
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
|
@ -11,32 +11,26 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
/// <summary>
|
||||
/// Defines properties for each stage in a <see cref="ManiaPlayfield"/>.
|
||||
/// </summary>
|
||||
public struct StageDefinition
|
||||
public class StageDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of <see cref="Column"/>s which this stage contains.
|
||||
/// </summary>
|
||||
public int Columns;
|
||||
public readonly int Columns;
|
||||
|
||||
public StageDefinition(int columns)
|
||||
{
|
||||
if (columns < 1)
|
||||
throw new ArgumentException("Column count must be above zero.", nameof(columns));
|
||||
|
||||
Columns = columns;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the column index is a special column for this stage.
|
||||
/// </summary>
|
||||
/// <param name="column">The 0-based column index.</param>
|
||||
/// <returns>Whether the column is a special column.</returns>
|
||||
public readonly bool IsSpecialColumn(int column) => Columns % 2 == 1 && column == Columns / 2;
|
||||
|
||||
/// <summary>
|
||||
/// Get the type of column given a column index.
|
||||
/// </summary>
|
||||
/// <param name="column">The 0-based column index.</param>
|
||||
/// <returns>The type of the column.</returns>
|
||||
public readonly ColumnType GetTypeOfColumn(int column)
|
||||
{
|
||||
if (IsSpecialColumn(column))
|
||||
return ColumnType.Special;
|
||||
|
||||
int distanceToEdge = Math.Min(column, (Columns - 1) - column);
|
||||
return distanceToEdge % 2 == 0 ? ColumnType.Odd : ColumnType.Even;
|
||||
}
|
||||
public bool IsSpecialColumn(int column) => Columns % 2 == 1 && column == Columns / 2;
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ using osu.Game.Rulesets.Mania.Edit.Setup;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
using osu.Game.Rulesets.Mania.Replays;
|
||||
using osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Mania.Skinning.Argon;
|
||||
using osu.Game.Rulesets.Mania.Skinning.Default;
|
||||
using osu.Game.Rulesets.Mania.Skinning.Legacy;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -66,6 +68,15 @@ namespace osu.Game.Rulesets.Mania
|
||||
{
|
||||
switch (skin)
|
||||
{
|
||||
case TrianglesSkin:
|
||||
return new ManiaTrianglesSkinTransformer(skin, beatmap);
|
||||
|
||||
case ArgonSkin:
|
||||
return new ManiaArgonSkinTransformer(skin, beatmap);
|
||||
|
||||
case DefaultLegacySkin:
|
||||
return new ManiaClassicSkinTransformer(skin, beatmap);
|
||||
|
||||
case LegacySkin:
|
||||
return new ManiaLegacySkinTransformer(skin, beatmap);
|
||||
}
|
||||
|
@ -3,29 +3,19 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania
|
||||
{
|
||||
public class ManiaSkinComponent : GameplaySkinComponent<ManiaSkinComponents>
|
||||
{
|
||||
/// <summary>
|
||||
/// The intended <see cref="StageDefinition"/> for this component.
|
||||
/// May be null if the component is not a direct member of a <see cref="Stage"/>.
|
||||
/// </summary>
|
||||
public readonly StageDefinition? StageDefinition;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ManiaSkinComponent"/>.
|
||||
/// </summary>
|
||||
/// <param name="component">The component.</param>
|
||||
/// <param name="stageDefinition">The intended <see cref="StageDefinition"/> for this component. May be null if the component is not a direct member of a <see cref="Stage"/>.</param>
|
||||
public ManiaSkinComponent(ManiaSkinComponents component, StageDefinition? stageDefinition = null)
|
||||
public ManiaSkinComponent(ManiaSkinComponents component)
|
||||
: base(component)
|
||||
{
|
||||
StageDefinition = stageDefinition;
|
||||
}
|
||||
|
||||
protected override string RulesetPrefix => ManiaRuleset.SHORT_NAME;
|
||||
|
@ -54,10 +54,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateInitialTransforms()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void UpdateStartTimeStateTransforms() => this.FadeOut(150);
|
||||
}
|
||||
}
|
||||
|
@ -30,20 +30,15 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
public bool UpdateResult() => base.UpdateResult(true);
|
||||
|
||||
protected override void UpdateInitialTransforms()
|
||||
{
|
||||
base.UpdateInitialTransforms();
|
||||
|
||||
// This hitobject should never expire, so this is just a safe maximum.
|
||||
LifetimeEnd = LifetimeStart + 30000;
|
||||
}
|
||||
|
||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||
{
|
||||
// suppress the base call explicitly.
|
||||
// the hold note head should never change its visual state on its own due to the "freezing" mechanic
|
||||
// (when hit, it remains visible in place at the judgement line; when dropped, it will scroll past the line).
|
||||
// it will be hidden along with its parenting hold note when required.
|
||||
|
||||
// Set `LifetimeEnd` explicitly to a non-`double.MaxValue` because otherwise this DHO is automatically expired.
|
||||
LifetimeEnd = double.PositiveInfinity;
|
||||
}
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<ManiaAction> e) => false; // Handled by the hold note
|
||||
|
@ -23,10 +23,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
protected readonly IBindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
// Leaving the default (10s) makes hitobjects not appear, as this offset is used for the initial state transforms.
|
||||
// Calculated as DrawableManiaRuleset.MAX_TIME_RANGE + some additional allowance for velocity < 1.
|
||||
protected override double InitialLifetimeOffset => 30000;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private ManiaPlayfield playfield { get; set; }
|
||||
|
||||
@ -69,22 +65,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
Direction.BindValueChanged(OnDirectionChanged, true);
|
||||
}
|
||||
|
||||
protected override void OnApply()
|
||||
{
|
||||
base.OnApply();
|
||||
|
||||
if (ParentHitObject != null)
|
||||
AccentColour.BindTo(ParentHitObject.AccentColour);
|
||||
}
|
||||
|
||||
protected override void OnFree()
|
||||
{
|
||||
base.OnFree();
|
||||
|
||||
if (ParentHitObject != null)
|
||||
AccentColour.UnbindFrom(ParentHitObject.AccentColour);
|
||||
}
|
||||
|
||||
protected virtual void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
|
||||
{
|
||||
Anchor = Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||
|
@ -1,8 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
@ -12,26 +10,38 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
{
|
||||
public class ColumnBackground : CompositeDrawable, IKeyBindingHandler<ManiaAction>, IHasAccentColour
|
||||
public class ArgonColumnBackground : CompositeDrawable, IKeyBindingHandler<ManiaAction>
|
||||
{
|
||||
private readonly IBindable<ManiaAction> action = new Bindable<ManiaAction>();
|
||||
|
||||
private Box background;
|
||||
private Box backgroundOverlay;
|
||||
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IBindable<ManiaAction> action, IScrollingInfo scrollingInfo)
|
||||
{
|
||||
this.action.BindTo(action);
|
||||
private Color4 brightColour;
|
||||
private Color4 dimColour;
|
||||
|
||||
private Box background = null!;
|
||||
private Box backgroundOverlay = null!;
|
||||
|
||||
[Resolved]
|
||||
private Column column { get; set; } = null!;
|
||||
|
||||
private Bindable<Color4> accentColour = null!;
|
||||
|
||||
public ArgonColumnBackground()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
Masking = true;
|
||||
CornerRadius = ArgonNotePiece.CORNER_RADIUS;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IScrollingInfo scrollingInfo)
|
||||
{
|
||||
InternalChildren = new[]
|
||||
{
|
||||
background = new Box
|
||||
@ -49,61 +59,42 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
}
|
||||
};
|
||||
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(dir =>
|
||||
accentColour = column.AccentColour.GetBoundCopy();
|
||||
accentColour.BindValueChanged(colour =>
|
||||
{
|
||||
backgroundOverlay.Anchor = backgroundOverlay.Origin = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||
updateColours();
|
||||
background.Colour = colour.NewValue.Darken(5);
|
||||
brightColour = colour.NewValue.Opacity(0.6f);
|
||||
dimColour = colour.NewValue.Opacity(0);
|
||||
}, true);
|
||||
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(onDirectionChanged, true);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||
{
|
||||
base.LoadComplete();
|
||||
updateColours();
|
||||
}
|
||||
|
||||
private Color4 accentColour;
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get => accentColour;
|
||||
set
|
||||
if (direction.NewValue == ScrollingDirection.Up)
|
||||
{
|
||||
if (accentColour == value)
|
||||
return;
|
||||
|
||||
accentColour = value;
|
||||
|
||||
updateColours();
|
||||
backgroundOverlay.Anchor = backgroundOverlay.Origin = Anchor.TopLeft;
|
||||
backgroundOverlay.Colour = ColourInfo.GradientVertical(brightColour, dimColour);
|
||||
}
|
||||
else
|
||||
{
|
||||
backgroundOverlay.Anchor = backgroundOverlay.Origin = Anchor.BottomLeft;
|
||||
backgroundOverlay.Colour = ColourInfo.GradientVertical(dimColour, brightColour);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateColours()
|
||||
{
|
||||
if (!IsLoaded)
|
||||
return;
|
||||
|
||||
background.Colour = AccentColour.Darken(5);
|
||||
|
||||
var brightPoint = AccentColour.Opacity(0.6f);
|
||||
var dimPoint = AccentColour.Opacity(0);
|
||||
|
||||
backgroundOverlay.Colour = ColourInfo.GradientVertical(
|
||||
direction.Value == ScrollingDirection.Up ? brightPoint : dimPoint,
|
||||
direction.Value == ScrollingDirection.Up ? dimPoint : brightPoint);
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
|
||||
{
|
||||
if (e.Action == action.Value)
|
||||
if (e.Action == column.Action.Value)
|
||||
backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
if (e.Action == action.Value)
|
||||
if (e.Action == column.Action.Value)
|
||||
backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
|
||||
}
|
||||
}
|
99
osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs
Normal file
99
osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitExplosion.cs
Normal file
@ -0,0 +1,99 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
{
|
||||
public class ArgonHitExplosion : CompositeDrawable, IHitExplosion
|
||||
{
|
||||
private const float default_large_faint_size = 0.8f;
|
||||
|
||||
public override bool RemoveWhenNotAlive => true;
|
||||
|
||||
[Resolved]
|
||||
private Column column { get; set; } = null!;
|
||||
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
private Container largeFaint = null!;
|
||||
|
||||
private Bindable<Color4> accentColour = null!;
|
||||
|
||||
public ArgonHitExplosion()
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = ArgonNotePiece.NOTE_HEIGHT;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IScrollingInfo scrollingInfo)
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
largeFaint = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
CornerRadius = ArgonNotePiece.CORNER_RADIUS,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Child = new Box
|
||||
{
|
||||
Colour = Color4.White,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(onDirectionChanged, true);
|
||||
|
||||
accentColour = column.AccentColour.GetBoundCopy();
|
||||
accentColour.BindValueChanged(colour =>
|
||||
{
|
||||
largeFaint.Colour = Interpolation.ValueAt(0.8f, colour.NewValue, Color4.White, 0, 1);
|
||||
|
||||
largeFaint.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = colour.NewValue,
|
||||
Roundness = 40,
|
||||
Radius = 60,
|
||||
};
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||
{
|
||||
if (direction.NewValue == ScrollingDirection.Up)
|
||||
{
|
||||
Anchor = Anchor.TopCentre;
|
||||
Y = ArgonNotePiece.NOTE_HEIGHT / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
Anchor = Anchor.BottomCentre;
|
||||
Y = -ArgonNotePiece.NOTE_HEIGHT / 2;
|
||||
}
|
||||
}
|
||||
|
||||
public void Animate(JudgementResult result)
|
||||
{
|
||||
this.FadeOutFromOne(PoolableHitExplosion.DURATION, Easing.Out);
|
||||
}
|
||||
}
|
||||
}
|
47
osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs
Normal file
47
osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHitTarget.cs
Normal file
@ -0,0 +1,47 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
{
|
||||
public class ArgonHitTarget : CompositeDrawable
|
||||
{
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IScrollingInfo scrollingInfo)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = ArgonNotePiece.NOTE_HEIGHT;
|
||||
|
||||
Masking = true;
|
||||
CornerRadius = ArgonNotePiece.CORNER_RADIUS;
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.3f,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Colour = Color4.White
|
||||
},
|
||||
};
|
||||
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(onDirectionChanged, true);
|
||||
}
|
||||
|
||||
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||
{
|
||||
Anchor = Origin = direction.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||
}
|
||||
}
|
||||
}
|
90
osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs
Normal file
90
osu.Game.Rulesets.Mania/Skinning/Argon/ArgonHoldBodyPiece.cs
Normal file
@ -0,0 +1,90 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents length-wise portion of a hold note.
|
||||
/// </summary>
|
||||
public class ArgonHoldBodyPiece : CompositeDrawable
|
||||
{
|
||||
protected readonly Bindable<Color4> AccentColour = new Bindable<Color4>();
|
||||
protected readonly IBindable<bool> IsHitting = new Bindable<bool>();
|
||||
|
||||
private Drawable background = null!;
|
||||
private Box foreground = null!;
|
||||
|
||||
public ArgonHoldBodyPiece()
|
||||
{
|
||||
Blending = BlendingParameters.Additive;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
// Without this, the width of the body will be slightly larger than the head/tail.
|
||||
Masking = true;
|
||||
CornerRadius = ArgonNotePiece.CORNER_RADIUS;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(DrawableHitObject? drawableObject)
|
||||
{
|
||||
InternalChildren = new[]
|
||||
{
|
||||
background = new Box { RelativeSizeAxes = Axes.Both },
|
||||
foreground = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Alpha = 0,
|
||||
},
|
||||
};
|
||||
|
||||
if (drawableObject != null)
|
||||
{
|
||||
var holdNote = (DrawableHoldNote)drawableObject;
|
||||
|
||||
AccentColour.BindTo(drawableObject.AccentColour);
|
||||
IsHitting.BindTo(holdNote.IsHitting);
|
||||
}
|
||||
|
||||
AccentColour.BindValueChanged(colour =>
|
||||
{
|
||||
background.Colour = colour.NewValue.Opacity(0.2f);
|
||||
foreground.Colour = colour.NewValue.Opacity(0.1f);
|
||||
}, true);
|
||||
|
||||
IsHitting.BindValueChanged(hitting =>
|
||||
{
|
||||
const float animation_length = 50;
|
||||
|
||||
foreground.ClearTransforms();
|
||||
|
||||
if (hitting.NewValue)
|
||||
{
|
||||
// wait for the next sync point
|
||||
double synchronisedOffset = animation_length * 2 - Time.Current % (animation_length * 2);
|
||||
|
||||
using (foreground.BeginDelayedSequence(synchronisedOffset))
|
||||
{
|
||||
foreground.FadeTo(1, animation_length).Then()
|
||||
.FadeTo(0, animation_length)
|
||||
.Loop();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreground.FadeOut(animation_length);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
{
|
||||
internal class ArgonHoldNoteTailPiece : CompositeDrawable
|
||||
{
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||
|
||||
private readonly Box colouredBox;
|
||||
private readonly Box shadow;
|
||||
|
||||
public ArgonHoldNoteTailPiece()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = ArgonNotePiece.NOTE_HEIGHT;
|
||||
|
||||
CornerRadius = ArgonNotePiece.CORNER_RADIUS;
|
||||
Masking = true;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
shadow = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.82f,
|
||||
Masking = true,
|
||||
CornerRadius = ArgonNotePiece.CORNER_RADIUS,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
colouredBox = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
}
|
||||
},
|
||||
new Circle
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = ArgonNotePiece.CORNER_RADIUS * 2,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(IScrollingInfo scrollingInfo, DrawableHitObject? drawableObject)
|
||||
{
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(onDirectionChanged, true);
|
||||
|
||||
if (drawableObject != null)
|
||||
{
|
||||
accentColour.BindTo(drawableObject.AccentColour);
|
||||
accentColour.BindValueChanged(onAccentChanged, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||
{
|
||||
colouredBox.Anchor = colouredBox.Origin = direction.NewValue == ScrollingDirection.Up
|
||||
? Anchor.TopCentre
|
||||
: Anchor.BottomCentre;
|
||||
}
|
||||
|
||||
private void onAccentChanged(ValueChangedEvent<Color4> accent)
|
||||
{
|
||||
colouredBox.Colour = ColourInfo.GradientVertical(
|
||||
accent.NewValue,
|
||||
accent.NewValue.Darken(0.1f)
|
||||
);
|
||||
|
||||
shadow.Colour = accent.NewValue.Darken(0.5f);
|
||||
}
|
||||
}
|
||||
}
|
193
osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs
Normal file
193
osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs
Normal file
@ -0,0 +1,193 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
{
|
||||
public class ArgonJudgementPiece : CompositeDrawable, IAnimatableJudgement
|
||||
{
|
||||
protected readonly HitResult Result;
|
||||
|
||||
protected SpriteText JudgementText { get; private set; } = null!;
|
||||
|
||||
private RingExplosion? ringExplosion;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
public ArgonJudgementPiece(HitResult result)
|
||||
{
|
||||
Result = result;
|
||||
Origin = Anchor.Centre;
|
||||
Y = 160;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
JudgementText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = Result.GetDescription().ToUpperInvariant(),
|
||||
Colour = colours.ForHitResult(Result),
|
||||
Blending = BlendingParameters.Additive,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Font = OsuFont.Default.With(size: 28, weight: FontWeight.Regular),
|
||||
},
|
||||
};
|
||||
|
||||
if (Result.IsHit())
|
||||
{
|
||||
AddInternal(ringExplosion = new RingExplosion(Result)
|
||||
{
|
||||
Colour = colours.ForHitResult(Result),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays the default animation for this judgement piece.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The base implementation only handles fade (for all result types) and misses.
|
||||
/// Individual rulesets are recommended to implement their appropriate hit animations.
|
||||
/// </remarks>
|
||||
public virtual void PlayAnimation()
|
||||
{
|
||||
switch (Result)
|
||||
{
|
||||
default:
|
||||
JudgementText
|
||||
.ScaleTo(Vector2.One)
|
||||
.ScaleTo(new Vector2(1.4f), 1800, Easing.OutQuint);
|
||||
break;
|
||||
|
||||
case HitResult.Miss:
|
||||
this.ScaleTo(1.6f);
|
||||
this.ScaleTo(1, 100, Easing.In);
|
||||
|
||||
this.MoveTo(Vector2.Zero);
|
||||
this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint);
|
||||
|
||||
this.RotateTo(0);
|
||||
this.RotateTo(40, 800, Easing.InQuint);
|
||||
break;
|
||||
}
|
||||
|
||||
this.FadeOutFromOne(800);
|
||||
|
||||
ringExplosion?.PlayAnimation();
|
||||
}
|
||||
|
||||
public Drawable? GetAboveHitObjectsProxiedContent() => null;
|
||||
|
||||
private class RingExplosion : CompositeDrawable
|
||||
{
|
||||
private readonly float travel = 52;
|
||||
|
||||
public RingExplosion(HitResult result)
|
||||
{
|
||||
const float thickness = 4;
|
||||
|
||||
const float small_size = 9;
|
||||
const float large_size = 14;
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Blending = BlendingParameters.Additive;
|
||||
|
||||
int countSmall = 0;
|
||||
int countLarge = 0;
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case HitResult.Meh:
|
||||
countSmall = 3;
|
||||
travel *= 0.3f;
|
||||
break;
|
||||
|
||||
case HitResult.Ok:
|
||||
case HitResult.Good:
|
||||
countSmall = 4;
|
||||
travel *= 0.6f;
|
||||
break;
|
||||
|
||||
case HitResult.Great:
|
||||
case HitResult.Perfect:
|
||||
countSmall = 4;
|
||||
countLarge = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < countSmall; i++)
|
||||
AddInternal(new RingPiece(thickness) { Size = new Vector2(small_size) });
|
||||
|
||||
for (int i = 0; i < countLarge; i++)
|
||||
AddInternal(new RingPiece(thickness) { Size = new Vector2(large_size) });
|
||||
}
|
||||
|
||||
public void PlayAnimation()
|
||||
{
|
||||
foreach (var c in InternalChildren)
|
||||
{
|
||||
const float start_position_ratio = 0.3f;
|
||||
|
||||
float direction = RNG.NextSingle(0, 360);
|
||||
float distance = RNG.NextSingle(travel / 2, travel);
|
||||
|
||||
c.MoveTo(new Vector2(
|
||||
MathF.Cos(direction) * distance * start_position_ratio,
|
||||
MathF.Sin(direction) * distance * start_position_ratio
|
||||
));
|
||||
|
||||
c.MoveTo(new Vector2(
|
||||
MathF.Cos(direction) * distance,
|
||||
MathF.Sin(direction) * distance
|
||||
), 600, Easing.OutQuint);
|
||||
}
|
||||
|
||||
this.FadeOutFromOne(1000, Easing.OutQuint);
|
||||
}
|
||||
|
||||
public class RingPiece : CircularContainer
|
||||
{
|
||||
public RingPiece(float thickness = 9)
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Masking = true;
|
||||
BorderThickness = thickness;
|
||||
BorderColour = Color4.White;
|
||||
|
||||
Child = new Box
|
||||
{
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
253
osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs
Normal file
253
osu.Game.Rulesets.Mania/Skinning/Argon/ArgonKeyArea.cs
Normal file
@ -0,0 +1,253 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
{
|
||||
public class ArgonKeyArea : CompositeDrawable, IKeyBindingHandler<ManiaAction>
|
||||
{
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
private Container directionContainer = null!;
|
||||
private Drawable background = null!;
|
||||
|
||||
private Circle hitTargetLine = null!;
|
||||
|
||||
private Container<Circle> bottomIcon = null!;
|
||||
private CircularContainer topIcon = null!;
|
||||
|
||||
private Bindable<Color4> accentColour = null!;
|
||||
|
||||
[Resolved]
|
||||
private Column column { get; set; } = null!;
|
||||
|
||||
public ArgonKeyArea()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IScrollingInfo scrollingInfo)
|
||||
{
|
||||
const float icon_circle_size = 8;
|
||||
const float icon_spacing = 7;
|
||||
const float icon_vertical_offset = -30;
|
||||
|
||||
InternalChild = directionContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = Stage.HIT_TARGET_POSITION,
|
||||
Children = new[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Masking = true,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CornerRadius = ArgonNotePiece.CORNER_RADIUS,
|
||||
Child = background = new Box
|
||||
{
|
||||
Name = "Key gradient",
|
||||
Alpha = 0,
|
||||
Blending = BlendingParameters.Additive,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
},
|
||||
hitTargetLine = new Circle
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Colour = OsuColour.Gray(196 / 255f),
|
||||
Height = ArgonNotePiece.CORNER_RADIUS * 2,
|
||||
Masking = true,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Name = "Icons",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
bottomIcon = new Container<Circle>
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.Centre,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Y = icon_vertical_offset,
|
||||
Children = new[]
|
||||
{
|
||||
new Circle
|
||||
{
|
||||
Size = new Vector2(icon_circle_size),
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
new Circle
|
||||
{
|
||||
X = -icon_spacing,
|
||||
Y = icon_spacing * 1.2f,
|
||||
Size = new Vector2(icon_circle_size),
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
new Circle
|
||||
{
|
||||
X = icon_spacing,
|
||||
Y = icon_spacing * 1.2f,
|
||||
Size = new Vector2(icon_circle_size),
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
}
|
||||
},
|
||||
topIcon = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.Centre,
|
||||
Y = -icon_vertical_offset,
|
||||
Size = new Vector2(22, 14),
|
||||
Masking = true,
|
||||
BorderThickness = 4,
|
||||
BorderColour = Color4.White,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(onDirectionChanged, true);
|
||||
|
||||
accentColour = column.AccentColour.GetBoundCopy();
|
||||
accentColour.BindValueChanged(colour =>
|
||||
{
|
||||
background.Colour = colour.NewValue.Darken(1f);
|
||||
bottomIcon.Colour = colour.NewValue;
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||
{
|
||||
switch (direction.NewValue)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
directionContainer.Scale = new Vector2(1, -1);
|
||||
directionContainer.Anchor = Anchor.TopLeft;
|
||||
directionContainer.Origin = Anchor.BottomLeft;
|
||||
break;
|
||||
|
||||
case ScrollingDirection.Down:
|
||||
directionContainer.Scale = new Vector2(1, 1);
|
||||
directionContainer.Anchor = Anchor.BottomLeft;
|
||||
directionContainer.Origin = Anchor.BottomLeft;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
|
||||
{
|
||||
if (e.Action != column.Action.Value) return false;
|
||||
|
||||
const double lighting_fade_in_duration = 50;
|
||||
Color4 lightingColour = accentColour.Value.Lighten(0.9f);
|
||||
|
||||
background
|
||||
.FadeTo(1, 40).Then()
|
||||
.FadeTo(0.8f, 150, Easing.OutQuint);
|
||||
|
||||
hitTargetLine.FadeColour(Color4.White, lighting_fade_in_duration, Easing.OutQuint);
|
||||
hitTargetLine.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = lightingColour.Opacity(0.7f),
|
||||
Radius = 20,
|
||||
}, lighting_fade_in_duration, Easing.OutQuint);
|
||||
|
||||
topIcon.ScaleTo(0.9f, lighting_fade_in_duration, Easing.OutQuint);
|
||||
topIcon.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = lightingColour.Opacity(0.1f),
|
||||
Radius = 20,
|
||||
}, lighting_fade_in_duration, Easing.OutQuint);
|
||||
|
||||
bottomIcon.FadeColour(Color4.White, lighting_fade_in_duration, Easing.OutQuint);
|
||||
|
||||
foreach (var circle in bottomIcon)
|
||||
{
|
||||
circle.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = lightingColour.Opacity(0.3f),
|
||||
Radius = 60,
|
||||
}, lighting_fade_in_duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
if (e.Action != column.Action.Value) return;
|
||||
|
||||
const double lighting_fade_out_duration = 300;
|
||||
Color4 lightingColour = accentColour.Value.Lighten(0.9f).Opacity(0);
|
||||
|
||||
background.FadeTo(0, lighting_fade_out_duration, Easing.OutQuint);
|
||||
|
||||
topIcon.ScaleTo(1f, 200, Easing.OutQuint);
|
||||
topIcon.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = lightingColour,
|
||||
Radius = 20,
|
||||
}, lighting_fade_out_duration, Easing.OutQuint);
|
||||
|
||||
hitTargetLine.FadeColour(OsuColour.Gray(196 / 255f), lighting_fade_out_duration, Easing.OutQuint);
|
||||
hitTargetLine.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = lightingColour,
|
||||
Radius = 25,
|
||||
}, lighting_fade_out_duration, Easing.OutQuint);
|
||||
|
||||
bottomIcon.FadeColour(accentColour.Value, lighting_fade_out_duration, Easing.OutQuint);
|
||||
|
||||
foreach (var circle in bottomIcon)
|
||||
{
|
||||
circle.TransformTo(nameof(EdgeEffect), new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = lightingColour,
|
||||
Radius = 30,
|
||||
}, lighting_fade_out_duration, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
110
osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs
Normal file
110
osu.Game.Rulesets.Mania/Skinning/Argon/ArgonNotePiece.cs
Normal file
@ -0,0 +1,110 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
{
|
||||
internal class ArgonNotePiece : CompositeDrawable
|
||||
{
|
||||
public const float NOTE_HEIGHT = 42;
|
||||
|
||||
public const float CORNER_RADIUS = 3;
|
||||
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||
|
||||
private readonly Box colouredBox;
|
||||
private readonly Box shadow;
|
||||
|
||||
public ArgonNotePiece()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = NOTE_HEIGHT;
|
||||
|
||||
CornerRadius = CORNER_RADIUS;
|
||||
Masking = true;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
shadow = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.82f,
|
||||
Masking = true,
|
||||
CornerRadius = CORNER_RADIUS,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
colouredBox = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
}
|
||||
},
|
||||
new Circle
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = CORNER_RADIUS * 2,
|
||||
},
|
||||
new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Y = 4,
|
||||
Icon = FontAwesome.Solid.AngleDown,
|
||||
Size = new Vector2(20),
|
||||
Scale = new Vector2(1, 0.7f)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(IScrollingInfo scrollingInfo, DrawableHitObject? drawableObject)
|
||||
{
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(onDirectionChanged, true);
|
||||
|
||||
if (drawableObject != null)
|
||||
{
|
||||
accentColour.BindTo(drawableObject.AccentColour);
|
||||
accentColour.BindValueChanged(onAccentChanged, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||
{
|
||||
colouredBox.Anchor = colouredBox.Origin = direction.NewValue == ScrollingDirection.Up
|
||||
? Anchor.TopCentre
|
||||
: Anchor.BottomCentre;
|
||||
}
|
||||
|
||||
private void onAccentChanged(ValueChangedEvent<Color4> accent)
|
||||
{
|
||||
colouredBox.Colour = ColourInfo.GradientVertical(
|
||||
accent.NewValue.Lighten(0.1f),
|
||||
accent.NewValue
|
||||
);
|
||||
|
||||
shadow.Colour = accent.NewValue.Darken(0.5f);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
{
|
||||
public class ArgonStageBackground : CompositeDrawable
|
||||
{
|
||||
public ArgonStageBackground()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
{
|
||||
public class ManiaArgonSkinTransformer : SkinTransformer
|
||||
{
|
||||
private readonly ManiaBeatmap beatmap;
|
||||
|
||||
public ManiaArgonSkinTransformer(ISkin skin, IBeatmap beatmap)
|
||||
: base(skin)
|
||||
{
|
||||
this.beatmap = (ManiaBeatmap)beatmap;
|
||||
}
|
||||
|
||||
public override Drawable? GetDrawableComponent(ISkinComponent component)
|
||||
{
|
||||
switch (component)
|
||||
{
|
||||
case GameplaySkinComponent<HitResult> resultComponent:
|
||||
return new ArgonJudgementPiece(resultComponent.Component);
|
||||
|
||||
case ManiaSkinComponent maniaComponent:
|
||||
// TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries.
|
||||
switch (maniaComponent.Component)
|
||||
{
|
||||
case ManiaSkinComponents.StageBackground:
|
||||
return new ArgonStageBackground();
|
||||
|
||||
case ManiaSkinComponents.ColumnBackground:
|
||||
return new ArgonColumnBackground();
|
||||
|
||||
case ManiaSkinComponents.HoldNoteBody:
|
||||
return new ArgonHoldBodyPiece();
|
||||
|
||||
case ManiaSkinComponents.HoldNoteTail:
|
||||
return new ArgonHoldNoteTailPiece();
|
||||
|
||||
case ManiaSkinComponents.HoldNoteHead:
|
||||
case ManiaSkinComponents.Note:
|
||||
return new ArgonNotePiece();
|
||||
|
||||
case ManiaSkinComponents.HitTarget:
|
||||
return new ArgonHitTarget();
|
||||
|
||||
case ManiaSkinComponents.KeyArea:
|
||||
return new ArgonKeyArea();
|
||||
|
||||
case ManiaSkinComponents.HitExplosion:
|
||||
return new ArgonHitExplosion();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return base.GetDrawableComponent(component);
|
||||
}
|
||||
|
||||
public override IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
if (lookup is ManiaSkinConfigurationLookup maniaLookup)
|
||||
{
|
||||
int column = maniaLookup.ColumnIndex ?? 0;
|
||||
var stage = beatmap.GetStageForColumnIndex(column);
|
||||
|
||||
switch (maniaLookup.Lookup)
|
||||
{
|
||||
case LegacyManiaSkinConfigurationLookups.StagePaddingBottom:
|
||||
case LegacyManiaSkinConfigurationLookups.StagePaddingTop:
|
||||
return SkinUtils.As<TValue>(new Bindable<float>(30));
|
||||
|
||||
case LegacyManiaSkinConfigurationLookups.ColumnWidth:
|
||||
return SkinUtils.As<TValue>(new Bindable<float>(
|
||||
stage.IsSpecialColumn(column) ? 120 : 60
|
||||
));
|
||||
|
||||
case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour:
|
||||
|
||||
Color4 colour;
|
||||
|
||||
if (stage.IsSpecialColumn(column))
|
||||
colour = new Color4(159, 101, 255, 255);
|
||||
else
|
||||
{
|
||||
switch (column % 8)
|
||||
{
|
||||
default:
|
||||
colour = new Color4(240, 216, 0, 255);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
colour = new Color4(240, 101, 0, 255);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
colour = new Color4(240, 0, 130, 255);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
colour = new Color4(192, 0, 240, 255);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
colour = new Color4(178, 0, 240, 255);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
colour = new Color4(0, 96, 240, 255);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
colour = new Color4(0, 226, 240, 255);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
colour = new Color4(0, 240, 96, 255);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SkinUtils.As<TValue>(new Bindable<Color4>(colour));
|
||||
}
|
||||
}
|
||||
|
||||
return base.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
// 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.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Default
|
||||
{
|
||||
public class ManiaTrianglesSkinTransformer : SkinTransformer
|
||||
{
|
||||
private readonly ManiaBeatmap beatmap;
|
||||
|
||||
public ManiaTrianglesSkinTransformer(ISkin skin, IBeatmap beatmap)
|
||||
: base(skin)
|
||||
{
|
||||
this.beatmap = (ManiaBeatmap)beatmap;
|
||||
}
|
||||
|
||||
private readonly Color4 colourEven = new Color4(6, 84, 0, 255);
|
||||
private readonly Color4 colourOdd = new Color4(94, 0, 57, 255);
|
||||
private readonly Color4 colourSpecial = new Color4(0, 48, 63, 255);
|
||||
|
||||
public override IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
if (lookup is ManiaSkinConfigurationLookup maniaLookup)
|
||||
{
|
||||
switch (maniaLookup.Lookup)
|
||||
{
|
||||
case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour:
|
||||
int column = maniaLookup.ColumnIndex ?? 0;
|
||||
|
||||
var stage = beatmap.GetStageForColumnIndex(column);
|
||||
|
||||
if (stage.IsSpecialColumn(column))
|
||||
return SkinUtils.As<TValue>(new Bindable<Color4>(colourSpecial));
|
||||
|
||||
int distanceToEdge = Math.Min(column, (stage.Columns - 1) - column);
|
||||
return SkinUtils.As<TValue>(new Bindable<Color4>(distanceToEdge % 2 == 0 ? colourOdd : colourEven));
|
||||
}
|
||||
}
|
||||
|
||||
return base.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -20,6 +21,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
[Resolved]
|
||||
protected Column Column { get; private set; }
|
||||
|
||||
[Resolved]
|
||||
private StageDefinition stage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The column type identifier to use for texture lookups, in the case of no user-provided configuration.
|
||||
/// </summary>
|
||||
@ -28,19 +32,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
switch (Column.ColumnType)
|
||||
if (Column.IsSpecial)
|
||||
FallbackColumnIndex = "S";
|
||||
else
|
||||
{
|
||||
case ColumnType.Special:
|
||||
FallbackColumnIndex = "S";
|
||||
break;
|
||||
|
||||
case ColumnType.Odd:
|
||||
FallbackColumnIndex = "1";
|
||||
break;
|
||||
|
||||
case ColumnType.Even:
|
||||
FallbackColumnIndex = "2";
|
||||
break;
|
||||
int distanceToEdge = Math.Min(Column.Index, (stage.Columns - 1) - Column.Index);
|
||||
FallbackColumnIndex = distanceToEdge % 2 == 0 ? "1" : "2";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,20 +18,17 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
{
|
||||
public class LegacyStageBackground : CompositeDrawable
|
||||
{
|
||||
private readonly StageDefinition stageDefinition;
|
||||
|
||||
private Drawable leftSprite;
|
||||
private Drawable rightSprite;
|
||||
private ColumnFlow<Drawable> columnBackgrounds;
|
||||
|
||||
public LegacyStageBackground(StageDefinition stageDefinition)
|
||||
public LegacyStageBackground()
|
||||
{
|
||||
this.stageDefinition = stageDefinition;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
private void load(ISkinSource skin, StageDefinition stageDefinition)
|
||||
{
|
||||
string leftImage = skin.GetManiaSkinConfig<string>(LegacyManiaSkinConfigurationLookups.LeftStageImage)?.Value
|
||||
?? "mania-stage-left";
|
||||
|
@ -0,0 +1,38 @@
|
||||
// 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.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
{
|
||||
public class ManiaClassicSkinTransformer : ManiaLegacySkinTransformer
|
||||
{
|
||||
public ManiaClassicSkinTransformer(ISkin skin, IBeatmap beatmap)
|
||||
: base(skin, beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
if (lookup is ManiaSkinConfigurationLookup maniaLookup)
|
||||
{
|
||||
var baseLookup = base.GetConfig<TLookup, TValue>(lookup);
|
||||
|
||||
if (baseLookup != null)
|
||||
return baseLookup;
|
||||
|
||||
// default provisioning.
|
||||
switch (maniaLookup.Lookup)
|
||||
{
|
||||
case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour:
|
||||
return SkinUtils.As<TValue>(new Bindable<Color4>(Color4.Black));
|
||||
}
|
||||
}
|
||||
|
||||
return base.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -20,8 +19,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
{
|
||||
public class ManiaLegacySkinTransformer : LegacySkinTransformer
|
||||
{
|
||||
private readonly ManiaBeatmap beatmap;
|
||||
|
||||
/// <summary>
|
||||
/// Mapping of <see cref="HitResult"/> to their corresponding
|
||||
/// <see cref="LegacyManiaSkinConfigurationLookups"/> value.
|
||||
@ -60,6 +57,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
/// </summary>
|
||||
private readonly Lazy<bool> hasKeyTexture;
|
||||
|
||||
private readonly ManiaBeatmap beatmap;
|
||||
|
||||
public ManiaLegacySkinTransformer(ISkin skin, IBeatmap beatmap)
|
||||
: base(skin)
|
||||
{
|
||||
@ -113,8 +112,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
return new LegacyHitExplosion();
|
||||
|
||||
case ManiaSkinComponents.StageBackground:
|
||||
Debug.Assert(maniaComponent.StageDefinition != null);
|
||||
return new LegacyStageBackground(maniaComponent.StageDefinition.Value);
|
||||
return new LegacyStageBackground();
|
||||
|
||||
case ManiaSkinComponents.StageForeground:
|
||||
return new LegacyStageForeground();
|
||||
@ -151,7 +149,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
if (lookup is ManiaSkinConfigurationLookup maniaLookup)
|
||||
return base.GetConfig<LegacyManiaSkinConfigurationLookup, TValue>(new LegacyManiaSkinConfigurationLookup(beatmap.TotalColumns, maniaLookup.Lookup, maniaLookup.TargetColumn));
|
||||
{
|
||||
return base.GetConfig<LegacyManiaSkinConfigurationLookup, TValue>(new LegacyManiaSkinConfigurationLookup(beatmap.TotalColumns, maniaLookup.Lookup, maniaLookup.ColumnIndex));
|
||||
}
|
||||
|
||||
return base.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
|
@ -15,9 +15,9 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
/// </summary>
|
||||
/// <param name="skin">The skin from which configuration is retrieved.</param>
|
||||
/// <param name="lookup">The value to retrieve.</param>
|
||||
/// <param name="index">If not null, denotes the index of the column to which the entry applies.</param>
|
||||
public static IBindable<T> GetManiaSkinConfig<T>(this ISkin skin, LegacyManiaSkinConfigurationLookups lookup, int? index = null)
|
||||
/// <param name="columnIndex">If not null, denotes the index of the column to which the entry applies.</param>
|
||||
public static IBindable<T> GetManiaSkinConfig<T>(this ISkin skin, LegacyManiaSkinConfigurationLookups lookup, int? columnIndex = null)
|
||||
=> skin.GetConfig<ManiaSkinConfigurationLookup, T>(
|
||||
new ManiaSkinConfigurationLookup(lookup, index));
|
||||
new ManiaSkinConfigurationLookup(lookup, columnIndex));
|
||||
}
|
||||
}
|
||||
|
@ -16,20 +16,21 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
public readonly LegacyManiaSkinConfigurationLookups Lookup;
|
||||
|
||||
/// <summary>
|
||||
/// The intended <see cref="Column"/> index for the configuration.
|
||||
/// The column which is being looked up.
|
||||
/// May be null if the configuration does not apply to a <see cref="Column"/>.
|
||||
/// Note that this is the absolute index across all stages.
|
||||
/// </summary>
|
||||
public readonly int? TargetColumn;
|
||||
public readonly int? ColumnIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ManiaSkinConfigurationLookup"/>.
|
||||
/// </summary>
|
||||
/// <param name="lookup">The lookup value.</param>
|
||||
/// <param name="targetColumn">The intended <see cref="Column"/> index for the configuration. May be null if the configuration does not apply to a <see cref="Column"/>.</param>
|
||||
public ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups lookup, int? targetColumn = null)
|
||||
/// <param name="columnIndex">The intended <see cref="Column"/> index for the configuration. May be null if the configuration does not apply to a <see cref="Column"/>.</param>
|
||||
public ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups lookup, int? columnIndex = null)
|
||||
{
|
||||
Lookup = lookup;
|
||||
TargetColumn = targetColumn;
|
||||
ColumnIndex = columnIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,30 +3,29 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Skinning;
|
||||
using osu.Game.Rulesets.Mania.UI.Components;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
[Cached]
|
||||
public class Column : ScrollingPlayfield, IKeyBindingHandler<ManiaAction>, IHasAccentColour
|
||||
public class Column : ScrollingPlayfield, IKeyBindingHandler<ManiaAction>
|
||||
{
|
||||
public const float COLUMN_WIDTH = 80;
|
||||
public const float SPECIAL_COLUMN_WIDTH = 70;
|
||||
@ -39,20 +38,41 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
public readonly Bindable<ManiaAction> Action = new Bindable<ManiaAction>();
|
||||
|
||||
public readonly ColumnHitObjectArea HitObjectArea;
|
||||
internal readonly Container TopLevelContainer;
|
||||
private readonly DrawablePool<PoolableHitExplosion> hitExplosionPool;
|
||||
internal readonly Container TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both };
|
||||
private DrawablePool<PoolableHitExplosion> hitExplosionPool;
|
||||
private readonly OrderedHitPolicy hitPolicy;
|
||||
public Container UnderlayElements => HitObjectArea.UnderlayElements;
|
||||
|
||||
private readonly GameplaySampleTriggerSource sampleTriggerSource;
|
||||
private GameplaySampleTriggerSource sampleTriggerSource;
|
||||
|
||||
public Column(int index)
|
||||
/// <summary>
|
||||
/// Whether this is a special (ie. scratch) column.
|
||||
/// </summary>
|
||||
public readonly bool IsSpecial;
|
||||
|
||||
public readonly Bindable<Color4> AccentColour = new Bindable<Color4>(Color4.Black);
|
||||
|
||||
public Column(int index, bool isSpecial)
|
||||
{
|
||||
Index = index;
|
||||
IsSpecial = isSpecial;
|
||||
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
Width = COLUMN_WIDTH;
|
||||
|
||||
hitPolicy = new OrderedHitPolicy(HitObjectContainer);
|
||||
HitObjectArea = new ColumnHitObjectArea(HitObjectContainer) { RelativeSizeAxes = Axes.Both };
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private ISkinSource skin { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
skin.SourceChanged += onSourceChanged;
|
||||
onSourceChanged();
|
||||
|
||||
Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
@ -64,18 +84,16 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
sampleTriggerSource = new GameplaySampleTriggerSource(HitObjectContainer),
|
||||
// For input purposes, the background is added at the highest depth, but is then proxied back below all other elements
|
||||
background.CreateProxy(),
|
||||
HitObjectArea = new ColumnHitObjectArea(HitObjectContainer) { RelativeSizeAxes = Axes.Both },
|
||||
HitObjectArea,
|
||||
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
background,
|
||||
TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both },
|
||||
TopLevelContainer,
|
||||
new ColumnTouchInputArea(this)
|
||||
};
|
||||
|
||||
hitPolicy = new OrderedHitPolicy(HitObjectContainer);
|
||||
|
||||
TopLevelContainer.Add(HitObjectArea.Explosions.CreateProxy());
|
||||
|
||||
RegisterPool<Note, DrawableNote>(10, 50);
|
||||
@ -85,18 +103,24 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
RegisterPool<HoldNoteTick, DrawableHoldNoteTick>(50, 250);
|
||||
}
|
||||
|
||||
private void onSourceChanged()
|
||||
{
|
||||
AccentColour.Value = skin.GetManiaSkinConfig<Color4>(LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour, Index)?.Value ?? Color4.Black;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
NewResult += OnNewResult;
|
||||
}
|
||||
|
||||
public ColumnType ColumnType { get; set; }
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
public bool IsSpecial => ColumnType == ColumnType.Special;
|
||||
|
||||
public Color4 AccentColour { get; set; }
|
||||
if (skin != null)
|
||||
skin.SourceChanged -= onSourceChanged;
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
@ -111,7 +135,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
DrawableManiaHitObject maniaObject = (DrawableManiaHitObject)drawableHitObject;
|
||||
|
||||
maniaObject.AccentColour.Value = AccentColour;
|
||||
maniaObject.AccentColour.BindTo(AccentColour);
|
||||
maniaObject.CheckHittable = hitPolicy.IsHittable;
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
AutoSizeAxes = Axes.X;
|
||||
|
||||
Masking = true;
|
||||
|
||||
InternalChild = columns = new FillFlowContainer<Container>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
|
@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
[Resolved]
|
||||
private Column column { get; set; }
|
||||
|
||||
private Bindable<Color4> accentColour;
|
||||
|
||||
public DefaultColumnBackground()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
@ -55,9 +57,13 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
}
|
||||
};
|
||||
|
||||
background.Colour = column.AccentColour.Darken(5);
|
||||
brightColour = column.AccentColour.Opacity(0.6f);
|
||||
dimColour = column.AccentColour.Opacity(0);
|
||||
accentColour = column.AccentColour.GetBoundCopy();
|
||||
accentColour.BindValueChanged(colour =>
|
||||
{
|
||||
background.Colour = colour.NewValue.Darken(5);
|
||||
brightColour = colour.NewValue.Opacity(0.6f);
|
||||
dimColour = colour.NewValue.Opacity(0);
|
||||
}, true);
|
||||
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(onDirectionChanged, true);
|
||||
|
@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
private Container hitTargetLine;
|
||||
private Drawable hitTargetBar;
|
||||
|
||||
private Bindable<Color4> accentColour;
|
||||
|
||||
[Resolved]
|
||||
private Column column { get; set; }
|
||||
|
||||
@ -54,12 +56,16 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
},
|
||||
};
|
||||
|
||||
hitTargetLine.EdgeEffect = new EdgeEffectParameters
|
||||
accentColour = column.AccentColour.GetBoundCopy();
|
||||
accentColour.BindValueChanged(colour =>
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Radius = 5,
|
||||
Colour = column.AccentColour.Opacity(0.5f),
|
||||
};
|
||||
hitTargetLine.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Radius = 5,
|
||||
Colour = colour.NewValue.Opacity(0.5f),
|
||||
};
|
||||
}, true);
|
||||
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(onDirectionChanged, true);
|
||||
|
@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
private Container keyIcon;
|
||||
private Drawable gradient;
|
||||
|
||||
private Bindable<Color4> accentColour;
|
||||
|
||||
[Resolved]
|
||||
private Column column { get; set; }
|
||||
|
||||
@ -75,15 +77,19 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
}
|
||||
};
|
||||
|
||||
keyIcon.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Radius = 5,
|
||||
Colour = column.AccentColour.Opacity(0.5f),
|
||||
};
|
||||
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(onDirectionChanged, true);
|
||||
|
||||
accentColour = column.AccentColour.GetBoundCopy();
|
||||
accentColour.BindValueChanged(colour =>
|
||||
{
|
||||
keyIcon.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Radius = 5,
|
||||
Colour = colour.NewValue.Opacity(0.5f),
|
||||
};
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||
|
@ -32,6 +32,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
private CircularContainer largeFaint;
|
||||
private CircularContainer mainGlow1;
|
||||
private CircularContainer mainGlow2;
|
||||
private CircularContainer mainGlow3;
|
||||
|
||||
private Bindable<Color4> accentColour;
|
||||
|
||||
public DefaultHitExplosion()
|
||||
{
|
||||
@ -48,8 +52,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
const float roundness = 80;
|
||||
const float initial_height = 10;
|
||||
|
||||
var colour = Interpolation.ValueAt(0.4f, column.AccentColour, Color4.White, 0, 1);
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
largeFaint = new CircularContainer
|
||||
@ -61,13 +63,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
// we want our size to be very small so the glow dominates it.
|
||||
Size = new Vector2(default_large_faint_size),
|
||||
Blending = BlendingParameters.Additive,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.1f, column.AccentColour, Color4.White, 0, 1).Opacity(0.3f),
|
||||
Roundness = 160,
|
||||
Radius = 200,
|
||||
},
|
||||
},
|
||||
mainGlow1 = new CircularContainer
|
||||
{
|
||||
@ -76,15 +71,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Blending = BlendingParameters.Additive,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.6f, column.AccentColour, Color4.White, 0, 1),
|
||||
Roundness = 20,
|
||||
Radius = 50,
|
||||
},
|
||||
},
|
||||
new CircularContainer
|
||||
mainGlow2 = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -93,15 +81,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
Size = new Vector2(0.01f, initial_height),
|
||||
Blending = BlendingParameters.Additive,
|
||||
Rotation = RNG.NextSingle(-angle_variance, angle_variance),
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = colour,
|
||||
Roundness = roundness,
|
||||
Radius = 40,
|
||||
},
|
||||
},
|
||||
new CircularContainer
|
||||
mainGlow3 = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -110,18 +91,44 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
Size = new Vector2(0.01f, initial_height),
|
||||
Blending = BlendingParameters.Additive,
|
||||
Rotation = RNG.NextSingle(-angle_variance, angle_variance),
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = colour,
|
||||
Roundness = roundness,
|
||||
Radius = 40,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(onDirectionChanged, true);
|
||||
|
||||
accentColour = column.AccentColour.GetBoundCopy();
|
||||
accentColour.BindValueChanged(colour =>
|
||||
{
|
||||
largeFaint.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.1f, colour.NewValue, Color4.White, 0, 1).Opacity(0.3f),
|
||||
Roundness = 160,
|
||||
Radius = 200,
|
||||
};
|
||||
mainGlow1.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.6f, colour.NewValue, Color4.White, 0, 1),
|
||||
Roundness = 20,
|
||||
Radius = 50,
|
||||
};
|
||||
mainGlow2.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.4f, colour.NewValue, Color4.White, 0, 1),
|
||||
Roundness = roundness,
|
||||
Radius = 40,
|
||||
};
|
||||
mainGlow3.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.4f, colour.NewValue, Color4.White, 0, 1),
|
||||
Roundness = roundness,
|
||||
Radius = 40,
|
||||
};
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
@ -12,6 +13,7 @@ using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Skinning;
|
||||
using osu.Game.Rulesets.Mania.UI.Components;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@ -19,7 +21,6 @@ using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
@ -28,6 +29,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
/// </summary>
|
||||
public class Stage : ScrollingPlayfield
|
||||
{
|
||||
[Cached]
|
||||
public readonly StageDefinition Definition;
|
||||
|
||||
public const float COLUMN_SPACING = 1;
|
||||
|
||||
public const float HIT_TARGET_POSITION = 110;
|
||||
@ -40,13 +44,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
private readonly Drawable barLineContainer;
|
||||
|
||||
private readonly Dictionary<ColumnType, Color4> columnColours = new Dictionary<ColumnType, Color4>
|
||||
{
|
||||
{ ColumnType.Even, new Color4(6, 84, 0, 255) },
|
||||
{ ColumnType.Odd, new Color4(94, 0, 57, 255) },
|
||||
{ ColumnType.Special, new Color4(0, 48, 63, 255) }
|
||||
};
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Columns.Any(c => c.ReceivePositionalInputAt(screenSpacePos));
|
||||
|
||||
private readonly int firstColumnIndex;
|
||||
@ -54,6 +51,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
public Stage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
|
||||
{
|
||||
this.firstColumnIndex = firstColumnIndex;
|
||||
Definition = definition;
|
||||
|
||||
Name = "Stage";
|
||||
|
||||
@ -75,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
AutoSizeAxes = Axes.X,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: definition), _ => new DefaultStageBackground())
|
||||
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground), _ => new DefaultStageBackground())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
@ -100,7 +98,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
}
|
||||
},
|
||||
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: definition), _ => null)
|
||||
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground), _ => null)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
@ -118,15 +116,13 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
for (int i = 0; i < definition.Columns; i++)
|
||||
{
|
||||
var columnType = definition.GetTypeOfColumn(i);
|
||||
bool isSpecial = definition.IsSpecialColumn(i);
|
||||
|
||||
var column = new Column(firstColumnIndex + i)
|
||||
var column = new Column(firstColumnIndex + i, isSpecial)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 1,
|
||||
ColumnType = columnType,
|
||||
AccentColour = columnColours[columnType],
|
||||
Action = { Value = columnType == ColumnType.Special ? specialColumnStartAction++ : normalColumnStartAction++ }
|
||||
Action = { Value = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++ }
|
||||
};
|
||||
|
||||
topLevelContainer.Add(column.TopLevelContainer.CreateProxy());
|
||||
@ -135,6 +131,37 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
}
|
||||
}
|
||||
|
||||
private ISkinSource currentSkin;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
{
|
||||
currentSkin = skin;
|
||||
|
||||
skin.SourceChanged += onSkinChanged;
|
||||
onSkinChanged();
|
||||
}
|
||||
|
||||
private void onSkinChanged()
|
||||
{
|
||||
float paddingTop = currentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.StagePaddingTop))?.Value ?? 0;
|
||||
float paddingBottom = currentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.StagePaddingBottom))?.Value ?? 0;
|
||||
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Top = paddingTop,
|
||||
Bottom = paddingBottom,
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (currentSkin != null)
|
||||
currentSkin.SourceChanged -= onSkinChanged;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 251 B |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/cursor-smoke.png
Normal file
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/cursor-smoke.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
136
osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs
Normal file
136
osu.Game.Rulesets.Osu.Tests/TestSceneSmoke.cs
Normal file
@ -0,0 +1,136 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Testing.Input;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public class TestSceneSmoke : OsuSkinnableTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestSmoking()
|
||||
{
|
||||
addStep("Create short smoke", 2_000);
|
||||
addStep("Create medium smoke", 5_000);
|
||||
addStep("Create long smoke", 10_000);
|
||||
}
|
||||
|
||||
private void addStep(string stepName, double duration)
|
||||
{
|
||||
var smokeContainers = new List<SmokeContainer>();
|
||||
|
||||
AddStep(stepName, () =>
|
||||
{
|
||||
smokeContainers.Clear();
|
||||
SetContents(_ =>
|
||||
{
|
||||
smokeContainers.Add(new TestSmokeContainer
|
||||
{
|
||||
Duration = duration,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
});
|
||||
|
||||
return new SmokingInputManager
|
||||
{
|
||||
Duration = duration,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Size = new Vector2(0.95f),
|
||||
Child = smokeContainers[^1],
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
AddUntilStep("Until skinnable expires", () =>
|
||||
{
|
||||
if (smokeContainers.Count == 0)
|
||||
return false;
|
||||
|
||||
Logger.Log("How many: " + smokeContainers.Count);
|
||||
|
||||
foreach (var smokeContainer in smokeContainers)
|
||||
{
|
||||
if (smokeContainer.Children.Count != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private class SmokingInputManager : ManualInputManager
|
||||
{
|
||||
public double Duration { get; init; }
|
||||
|
||||
private double? startTime;
|
||||
|
||||
public SmokingInputManager()
|
||||
{
|
||||
UseParentInput = false;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
MoveMouseTo(ToScreenSpace(DrawSize / 2));
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
const float spin_angle = 4 * MathF.PI;
|
||||
|
||||
startTime ??= Time.Current;
|
||||
|
||||
float fraction = (float)((Time.Current - startTime) / Duration);
|
||||
|
||||
float angle = fraction * spin_angle;
|
||||
float radius = fraction * Math.Min(DrawSize.X, DrawSize.Y) / 2;
|
||||
|
||||
Vector2 pos = radius * new Vector2(MathF.Cos(angle), MathF.Sin(angle)) + DrawSize / 2;
|
||||
MoveMouseTo(ToScreenSpace(pos));
|
||||
}
|
||||
}
|
||||
|
||||
private class TestSmokeContainer : SmokeContainer
|
||||
{
|
||||
public double Duration { get; init; }
|
||||
|
||||
private bool isPressing;
|
||||
private bool isFinished;
|
||||
|
||||
private double? startTime;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
startTime ??= Time.Current + 0.1;
|
||||
|
||||
if (!isPressing && !isFinished && Time.Current > startTime)
|
||||
{
|
||||
OnPressed(new KeyBindingPressEvent<OsuAction>(new InputState(), OsuAction.Smoke));
|
||||
isPressing = true;
|
||||
isFinished = false;
|
||||
}
|
||||
|
||||
if (isPressing && Time.Current > startTime + Duration)
|
||||
{
|
||||
OnReleased(new KeyBindingReleaseEvent<OsuAction>(new InputState(), OsuAction.Smoke));
|
||||
isPressing = false;
|
||||
isFinished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public override BindableBool ComboBasedSize { get; } = new BindableBool(true);
|
||||
|
||||
public override float DefaultFlashlightSize => 180;
|
||||
public override float DefaultFlashlightSize => 200;
|
||||
|
||||
private OsuFlashlight flashlight = null!;
|
||||
|
||||
@ -63,6 +63,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
followDelay = modFlashlight.FollowDelay.Value;
|
||||
|
||||
FlashlightSize = new Vector2(0, GetSizeFor(0));
|
||||
FlashlightSmoothness = 1.4f;
|
||||
}
|
||||
|
||||
public void OnSliderTrackingChange(ValueChangedEvent<bool> e)
|
||||
|
@ -204,12 +204,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
// todo: temporary / arbitrary, used for lifetime optimisation.
|
||||
this.Delay(800).FadeOut();
|
||||
|
||||
// in the case of an early state change, the fade should be expedited to the current point in time.
|
||||
if (HitStateUpdateTime < HitObject.StartTime)
|
||||
ApproachCircle.FadeOut(50);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
default:
|
||||
ApproachCircle.FadeOut();
|
||||
break;
|
||||
|
||||
case ArmedState.Idle:
|
||||
HitArea.HitAction = null;
|
||||
break;
|
||||
|
@ -11,7 +11,9 @@ using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Scoring;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
@ -64,6 +66,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
ScaleBindable.UnbindFrom(HitObject.ScaleBindable);
|
||||
}
|
||||
|
||||
protected override void UpdateInitialTransforms()
|
||||
{
|
||||
base.UpdateInitialTransforms();
|
||||
|
||||
// Dim should only be applied at a top level, as it will be implicitly applied to nested objects.
|
||||
if (ParentHitObject == null)
|
||||
{
|
||||
// Of note, no one noticed this was missing for years, but it definitely feels like it should still exist.
|
||||
// For now this is applied across all skins, and matches stable.
|
||||
// For simplicity, dim colour is applied to the DrawableHitObject itself.
|
||||
// We may need to make a nested container setup if this even causes a usage conflict (ie. with a mod).
|
||||
this.FadeColour(new Color4(195, 195, 195, 255));
|
||||
using (BeginDelayedSequence(InitialLifetimeOffset - OsuHitWindows.MISS_WINDOW))
|
||||
this.FadeColour(Color4.White, 100);
|
||||
}
|
||||
}
|
||||
|
||||
protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt;
|
||||
|
||||
private OsuInputManager osuActionInputManager;
|
||||
|
@ -80,6 +80,9 @@ namespace osu.Game.Rulesets.Osu
|
||||
LeftButton,
|
||||
|
||||
[Description("Right button")]
|
||||
RightButton
|
||||
RightButton,
|
||||
|
||||
[Description("Smoke")]
|
||||
Smoke,
|
||||
}
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
{
|
||||
new KeyBinding(InputKey.Z, OsuAction.LeftButton),
|
||||
new KeyBinding(InputKey.X, OsuAction.RightButton),
|
||||
new KeyBinding(InputKey.C, OsuAction.Smoke),
|
||||
new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton),
|
||||
new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
|
||||
};
|
||||
|
@ -21,6 +21,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
SliderBall,
|
||||
SliderBody,
|
||||
SpinnerBody,
|
||||
CursorSmoke,
|
||||
ApproachCircle,
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
Position = currentFrame.Position;
|
||||
if (currentFrame.MouseLeft) Actions.Add(OsuAction.LeftButton);
|
||||
if (currentFrame.MouseRight) Actions.Add(OsuAction.RightButton);
|
||||
if (currentFrame.Smoke) Actions.Add(OsuAction.Smoke);
|
||||
}
|
||||
|
||||
public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
|
||||
@ -41,6 +42,8 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
state |= ReplayButtonState.Left1;
|
||||
if (Actions.Contains(OsuAction.RightButton))
|
||||
state |= ReplayButtonState.Right1;
|
||||
if (Actions.Contains(OsuAction.Smoke))
|
||||
state |= ReplayButtonState.Smoke;
|
||||
|
||||
return new LegacyReplayFrame(Time, Position.X, Position.Y, state);
|
||||
}
|
||||
|
@ -1,20 +1,23 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Scoring
|
||||
{
|
||||
public class OsuHitWindows : HitWindows
|
||||
{
|
||||
/// <summary>
|
||||
/// osu! ruleset has a fixed miss window regardless of difficulty settings.
|
||||
/// </summary>
|
||||
public const double MISS_WINDOW = 400;
|
||||
|
||||
private static readonly DifficultyRange[] osu_ranges =
|
||||
{
|
||||
new DifficultyRange(HitResult.Great, 80, 50, 20),
|
||||
new DifficultyRange(HitResult.Ok, 140, 100, 60),
|
||||
new DifficultyRange(HitResult.Meh, 200, 150, 100),
|
||||
new DifficultyRange(HitResult.Miss, 400, 400, 400),
|
||||
new DifficultyRange(HitResult.Miss, MISS_WINDOW, MISS_WINDOW, MISS_WINDOW),
|
||||
};
|
||||
|
||||
public override bool IsHitResultAllowed(HitResult result)
|
||||
|
@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
return new ArgonJudgementPiece(resultComponent.Component);
|
||||
|
||||
case OsuSkinComponent osuComponent:
|
||||
// TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries.
|
||||
switch (osuComponent.Component)
|
||||
{
|
||||
case OsuSkinComponents.HitCircle:
|
||||
|
@ -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 osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
public class DefaultSmokeSegment : SmokeSegment
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
// ISkinSource doesn't currently fallback to global textures.
|
||||
// We might want to change this in the future if the intention is to allow the user to skin this as per legacy skins.
|
||||
Texture = textures.Get("Gameplay/osu/cursor-smoke");
|
||||
}
|
||||
}
|
||||
}
|
19
osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmokeSegment.cs
Normal file
19
osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySmokeSegment.cs
Normal file
@ -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 osu.Framework.Allocation;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public class LegacySmokeSegment : SmokeSegment
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Texture = skin.GetTexture("cursor-smoke");
|
||||
}
|
||||
}
|
||||
}
|
@ -106,6 +106,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorSmoke:
|
||||
if (GetTexture("cursor-smoke") != null)
|
||||
return new LegacySmokeSegment();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.HitCircleText:
|
||||
if (!this.HasFont(LegacyFont.HitCircle))
|
||||
return null;
|
||||
|
366
osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs
Normal file
366
osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs
Normal file
@ -0,0 +1,366 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Rendering;
|
||||
using osu.Framework.Graphics.Rendering.Vertices;
|
||||
using osu.Framework.Graphics.Shaders;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Utils;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning
|
||||
{
|
||||
public abstract class SmokeSegment : Drawable, ITexturedShaderDrawable
|
||||
{
|
||||
private const int max_point_count = 18_000;
|
||||
|
||||
// fade anim values
|
||||
private const double initial_fade_out_duration = 4000;
|
||||
|
||||
private const double re_fade_in_speed = 3;
|
||||
private const double re_fade_in_duration = 50;
|
||||
|
||||
private const double final_fade_out_speed = 2;
|
||||
private const double final_fade_out_duration = 8000;
|
||||
|
||||
private const float initial_alpha = 0.6f;
|
||||
private const float re_fade_in_alpha = 1f;
|
||||
|
||||
private readonly int rotationSeed = RNG.Next();
|
||||
|
||||
// scale anim values
|
||||
private const double scale_duration = 1200;
|
||||
|
||||
private const float initial_scale = 0.65f;
|
||||
private const float final_scale = 1f;
|
||||
|
||||
// rotation anim values
|
||||
private const double rotation_duration = 500;
|
||||
|
||||
private const float max_rotation = 0.25f;
|
||||
|
||||
public IShader? TextureShader { get; private set; }
|
||||
public IShader? RoundedTextureShader { get; private set; }
|
||||
|
||||
protected Texture? Texture { get; set; }
|
||||
|
||||
private float radius => Texture?.DisplayWidth * 0.165f ?? 3;
|
||||
|
||||
protected readonly List<SmokePoint> SmokePoints = new List<SmokePoint>();
|
||||
|
||||
private float pointInterval => radius * 7f / 8;
|
||||
|
||||
private double smokeStartTime { get; set; } = double.MinValue;
|
||||
|
||||
private double smokeEndTime { get; set; } = double.MaxValue;
|
||||
|
||||
private float totalDistance;
|
||||
private Vector2? lastPosition;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ShaderManager shaders)
|
||||
{
|
||||
RoundedTextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED);
|
||||
TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
LifetimeStart = smokeStartTime = Time.Current;
|
||||
|
||||
totalDistance = pointInterval;
|
||||
}
|
||||
|
||||
private Vector2 nextPointDirection()
|
||||
{
|
||||
float angle = RNG.NextSingle(0, 2 * MathF.PI);
|
||||
return new Vector2(MathF.Sin(angle), -MathF.Cos(angle));
|
||||
}
|
||||
|
||||
public void AddPosition(Vector2 position, double time)
|
||||
{
|
||||
lastPosition ??= position;
|
||||
|
||||
float delta = (position - (Vector2)lastPosition).LengthFast;
|
||||
totalDistance += delta;
|
||||
int count = (int)(totalDistance / pointInterval);
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
Vector2 increment = position - (Vector2)lastPosition;
|
||||
increment.NormalizeFast();
|
||||
|
||||
Vector2 pointPos = (pointInterval - (totalDistance - delta)) * increment + (Vector2)lastPosition;
|
||||
increment *= pointInterval;
|
||||
|
||||
if (SmokePoints.Count > 0 && SmokePoints[^1].Time > time)
|
||||
{
|
||||
int index = ~SmokePoints.BinarySearch(new SmokePoint { Time = time }, new SmokePoint.UpperBoundComparer());
|
||||
SmokePoints.RemoveRange(index, SmokePoints.Count - index);
|
||||
}
|
||||
|
||||
totalDistance %= pointInterval;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
SmokePoints.Add(new SmokePoint
|
||||
{
|
||||
Position = pointPos,
|
||||
Time = time,
|
||||
Direction = nextPointDirection(),
|
||||
});
|
||||
|
||||
pointPos += increment;
|
||||
}
|
||||
|
||||
Invalidate(Invalidation.DrawNode);
|
||||
}
|
||||
|
||||
lastPosition = position;
|
||||
|
||||
if (SmokePoints.Count >= max_point_count)
|
||||
FinishDrawing(time);
|
||||
}
|
||||
|
||||
public void FinishDrawing(double time)
|
||||
{
|
||||
smokeEndTime = time;
|
||||
|
||||
double initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, smokeEndTime - smokeStartTime);
|
||||
LifetimeEnd = smokeEndTime + final_fade_out_duration + initialFadeOutDurationTrunc / re_fade_in_speed + initialFadeOutDurationTrunc / final_fade_out_speed;
|
||||
}
|
||||
|
||||
protected override DrawNode CreateDrawNode() => new SmokeDrawNode(this);
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Invalidate(Invalidation.DrawNode);
|
||||
}
|
||||
|
||||
protected struct SmokePoint
|
||||
{
|
||||
public Vector2 Position;
|
||||
public double Time;
|
||||
public Vector2 Direction;
|
||||
|
||||
public struct UpperBoundComparer : IComparer<SmokePoint>
|
||||
{
|
||||
public int Compare(SmokePoint x, SmokePoint target)
|
||||
{
|
||||
// By returning -1 when the target value is equal to x, guarantees that the
|
||||
// element at BinarySearch's returned index will always be the first element
|
||||
// larger. Since 0 is never returned, the target is never "found", so the return
|
||||
// value will be the index's complement.
|
||||
|
||||
return x.Time > target.Time ? 1 : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class SmokeDrawNode : TexturedShaderDrawNode
|
||||
{
|
||||
protected new SmokeSegment Source => (SmokeSegment)base.Source;
|
||||
|
||||
protected double SmokeStartTime { get; private set; }
|
||||
protected double SmokeEndTime { get; private set; }
|
||||
protected double CurrentTime { get; private set; }
|
||||
|
||||
private readonly List<SmokePoint> points = new List<SmokePoint>();
|
||||
private IVertexBatch<TexturedVertex2D>? quadBatch;
|
||||
private float radius;
|
||||
private Vector2 drawSize;
|
||||
private Texture? texture;
|
||||
|
||||
// anim calculation vars (color, scale, direction)
|
||||
private double initialFadeOutDurationTrunc;
|
||||
private double firstVisiblePointTime;
|
||||
|
||||
private double initialFadeOutTime;
|
||||
private double reFadeInTime;
|
||||
private double finalFadeOutTime;
|
||||
|
||||
private Random rotationRNG = new Random();
|
||||
|
||||
public SmokeDrawNode(ITexturedShaderDrawable source)
|
||||
: base(source)
|
||||
{
|
||||
}
|
||||
|
||||
public override void ApplyState()
|
||||
{
|
||||
base.ApplyState();
|
||||
|
||||
points.Clear();
|
||||
points.AddRange(Source.SmokePoints);
|
||||
|
||||
radius = Source.radius;
|
||||
drawSize = Source.DrawSize;
|
||||
texture = Source.Texture;
|
||||
|
||||
SmokeStartTime = Source.smokeStartTime;
|
||||
SmokeEndTime = Source.smokeEndTime;
|
||||
CurrentTime = Source.Clock.CurrentTime;
|
||||
|
||||
rotationRNG = new Random(Source.rotationSeed);
|
||||
|
||||
initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime);
|
||||
firstVisiblePointTime = SmokeEndTime - initialFadeOutDurationTrunc;
|
||||
|
||||
initialFadeOutTime = CurrentTime;
|
||||
reFadeInTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTime * (1 - 1 / re_fade_in_speed);
|
||||
finalFadeOutTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTime * (1 - 1 / final_fade_out_speed);
|
||||
}
|
||||
|
||||
public sealed override void Draw(IRenderer renderer)
|
||||
{
|
||||
base.Draw(renderer);
|
||||
|
||||
if (points.Count == 0)
|
||||
return;
|
||||
|
||||
quadBatch ??= renderer.CreateQuadBatch<TexturedVertex2D>(max_point_count / 10, 10);
|
||||
texture ??= renderer.WhitePixel;
|
||||
RectangleF textureRect = texture.GetTextureRect();
|
||||
|
||||
var shader = GetAppropriateShader(renderer);
|
||||
|
||||
renderer.SetBlend(BlendingParameters.Additive);
|
||||
renderer.PushLocalMatrix(DrawInfo.Matrix);
|
||||
|
||||
shader.Bind();
|
||||
texture.Bind();
|
||||
|
||||
foreach (var point in points)
|
||||
drawPointQuad(point, textureRect);
|
||||
|
||||
shader.Unbind();
|
||||
renderer.PopLocalMatrix();
|
||||
}
|
||||
|
||||
protected Color4 ColourAtPosition(Vector2 localPos) => DrawColourInfo.Colour.HasSingleColour
|
||||
? ((SRGBColour)DrawColourInfo.Colour).Linear
|
||||
: DrawColourInfo.Colour.Interpolate(Vector2.Divide(localPos, drawSize)).Linear;
|
||||
|
||||
protected virtual Color4 PointColour(SmokePoint point)
|
||||
{
|
||||
var color = Color4.White;
|
||||
|
||||
double timeDoingInitialFadeOut = Math.Min(initialFadeOutTime, SmokeEndTime) - point.Time;
|
||||
|
||||
if (timeDoingInitialFadeOut > 0)
|
||||
{
|
||||
float fraction = Math.Clamp((float)(timeDoingInitialFadeOut / initial_fade_out_duration), 0, 1);
|
||||
color.A = (1 - fraction) * initial_alpha;
|
||||
}
|
||||
|
||||
if (color.A > 0)
|
||||
{
|
||||
double timeDoingReFadeIn = reFadeInTime - point.Time / re_fade_in_speed;
|
||||
double timeDoingFinalFadeOut = finalFadeOutTime - point.Time / final_fade_out_speed;
|
||||
|
||||
if (timeDoingFinalFadeOut > 0)
|
||||
{
|
||||
float fraction = Math.Clamp((float)(timeDoingFinalFadeOut / final_fade_out_duration), 0, 1);
|
||||
fraction = MathF.Pow(fraction, 5);
|
||||
color.A = (1 - fraction) * re_fade_in_alpha;
|
||||
}
|
||||
else if (timeDoingReFadeIn > 0)
|
||||
{
|
||||
float fraction = Math.Clamp((float)(timeDoingReFadeIn / re_fade_in_duration), 0, 1);
|
||||
fraction = 1 - MathF.Pow(1 - fraction, 5);
|
||||
color.A = fraction * (re_fade_in_alpha - color.A) + color.A;
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
protected virtual float PointScale(SmokePoint point)
|
||||
{
|
||||
double timeDoingScale = CurrentTime - point.Time;
|
||||
float fraction = Math.Clamp((float)(timeDoingScale / scale_duration), 0, 1);
|
||||
fraction = 1 - MathF.Pow(1 - fraction, 5);
|
||||
return fraction * (final_scale - initial_scale) + initial_scale;
|
||||
}
|
||||
|
||||
protected virtual Vector2 PointDirection(SmokePoint point)
|
||||
{
|
||||
float initialAngle = MathF.Atan2(point.Direction.Y, point.Direction.X);
|
||||
float finalAngle = initialAngle + nextRotation();
|
||||
|
||||
double timeDoingRotation = CurrentTime - point.Time;
|
||||
float fraction = Math.Clamp((float)(timeDoingRotation / rotation_duration), 0, 1);
|
||||
fraction = 1 - MathF.Pow(1 - fraction, 5);
|
||||
float angle = fraction * (finalAngle - initialAngle) + initialAngle;
|
||||
|
||||
return new Vector2(MathF.Sin(angle), -MathF.Cos(angle));
|
||||
}
|
||||
|
||||
private float nextRotation() => max_rotation * ((float)rotationRNG.NextDouble() * 2 - 1);
|
||||
|
||||
private void drawPointQuad(SmokePoint point, RectangleF textureRect)
|
||||
{
|
||||
Debug.Assert(quadBatch != null);
|
||||
|
||||
var colour = PointColour(point);
|
||||
float scale = PointScale(point);
|
||||
var dir = PointDirection(point);
|
||||
var ortho = dir.PerpendicularLeft;
|
||||
|
||||
if (colour.A == 0 || scale == 0)
|
||||
return;
|
||||
|
||||
var localTopLeft = point.Position + (radius * scale * (-ortho - dir));
|
||||
var localTopRight = point.Position + (radius * scale * (-ortho + dir));
|
||||
var localBotLeft = point.Position + (radius * scale * (ortho - dir));
|
||||
var localBotRight = point.Position + (radius * scale * (ortho + dir));
|
||||
|
||||
quadBatch.Add(new TexturedVertex2D
|
||||
{
|
||||
Position = localTopLeft,
|
||||
TexturePosition = textureRect.TopLeft,
|
||||
Colour = Color4Extensions.Multiply(ColourAtPosition(localTopLeft), colour),
|
||||
});
|
||||
quadBatch.Add(new TexturedVertex2D
|
||||
{
|
||||
Position = localTopRight,
|
||||
TexturePosition = textureRect.TopRight,
|
||||
Colour = Color4Extensions.Multiply(ColourAtPosition(localTopRight), colour),
|
||||
});
|
||||
quadBatch.Add(new TexturedVertex2D
|
||||
{
|
||||
Position = localBotRight,
|
||||
TexturePosition = textureRect.BottomRight,
|
||||
Colour = Color4Extensions.Multiply(ColourAtPosition(localBotRight), colour),
|
||||
});
|
||||
quadBatch.Add(new TexturedVertex2D
|
||||
{
|
||||
Position = localBotLeft,
|
||||
TexturePosition = textureRect.BottomLeft,
|
||||
Colour = Color4Extensions.Multiply(ColourAtPosition(localBotLeft), colour),
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
quadBatch?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -54,6 +54,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
playfieldBorder = new PlayfieldBorder { RelativeSizeAxes = Axes.Both },
|
||||
new SmokeContainer { RelativeSizeAxes = Axes.Both },
|
||||
spinnerProxies = new ProxyContainer { RelativeSizeAxes = Axes.Both },
|
||||
FollowPoints = new FollowPointRenderer { RelativeSizeAxes = Axes.Both },
|
||||
judgementLayer = new JudgementContainer<DrawableOsuJudgement> { RelativeSizeAxes = Axes.Both },
|
||||
|
77
osu.Game.Rulesets.Osu/UI/SmokeContainer.cs
Normal file
77
osu.Game.Rulesets.Osu/UI/SmokeContainer.cs
Normal file
@ -0,0 +1,77 @@
|
||||
// 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;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Osu.Skinning;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages smoke trails generated from user input.
|
||||
/// </summary>
|
||||
public class SmokeContainer : Container, IRequireHighFrequencyMousePosition, IKeyBindingHandler<OsuAction>
|
||||
{
|
||||
private SmokeSkinnableDrawable? currentSegmentSkinnable;
|
||||
|
||||
private Vector2 lastMousePosition;
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 _) => true;
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
||||
{
|
||||
if (e.Action == OsuAction.Smoke)
|
||||
{
|
||||
AddInternal(currentSegmentSkinnable = new SmokeSkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorSmoke), _ => new DefaultSmokeSegment()));
|
||||
|
||||
// Add initial position immediately.
|
||||
addPosition();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
||||
{
|
||||
if (e.Action == OsuAction.Smoke)
|
||||
{
|
||||
if (currentSegmentSkinnable?.Drawable is SmokeSegment segment)
|
||||
{
|
||||
segment.FinishDrawing(Time.Current);
|
||||
currentSegmentSkinnable = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
lastMousePosition = e.MousePosition;
|
||||
addPosition();
|
||||
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
private void addPosition() => (currentSegmentSkinnable?.Drawable as SmokeSegment)?.AddPosition(lastMousePosition, Time.Current);
|
||||
|
||||
private class SmokeSkinnableDrawable : SkinnableDrawable
|
||||
{
|
||||
public override bool RemoveWhenNotAlive => true;
|
||||
|
||||
public override double LifetimeStart => Drawable.LifetimeStart;
|
||||
public override double LifetimeEnd => Drawable.LifetimeEnd;
|
||||
|
||||
public SmokeSkinnableDrawable(ISkinComponent component, Func<ISkinComponent, Drawable>? defaultImplementation = null, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||
: base(component, defaultImplementation, confineMode)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
private int countOk;
|
||||
private int countMeh;
|
||||
private int countMiss;
|
||||
private double accuracy;
|
||||
|
||||
private double effectiveMissCount;
|
||||
|
||||
@ -36,6 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
|
||||
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
|
||||
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
|
||||
accuracy = customAccuracy;
|
||||
|
||||
// The effectiveMissCount is calculated by gaining a ratio for totalSuccessfulHits and increasing the miss penalty for shorter object counts lower than 1000.
|
||||
if (totalSuccessfulHits > 0)
|
||||
@ -87,7 +89,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
if (score.Mods.Any(m => m is ModFlashlight<TaikoHitObject>))
|
||||
difficultyValue *= 1.050 * lengthBonus;
|
||||
|
||||
return difficultyValue * Math.Pow(score.Accuracy, 2.0);
|
||||
return difficultyValue * Math.Pow(accuracy, 2.0);
|
||||
}
|
||||
|
||||
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
|
||||
@ -95,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
if (attributes.GreatHitWindow <= 0)
|
||||
return 0;
|
||||
|
||||
double accuracyValue = Math.Pow(60.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(score.Accuracy, 8.0) * Math.Pow(attributes.StarRating, 0.4) * 27.0;
|
||||
double accuracyValue = Math.Pow(60.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(accuracy, 8.0) * Math.Pow(attributes.StarRating, 0.4) * 27.0;
|
||||
|
||||
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
|
||||
accuracyValue *= lengthBonus;
|
||||
@ -110,5 +112,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
private int totalHits => countGreat + countOk + countMeh + countMiss;
|
||||
|
||||
private int totalSuccessfulHits => countGreat + countOk + countMeh;
|
||||
|
||||
private double customAccuracy => totalHits > 0 ? (countGreat * 300 + countOk * 150) / (totalHits * 300.0) : 0;
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
|
||||
public override BindableBool ComboBasedSize { get; } = new BindableBool(true);
|
||||
|
||||
public override float DefaultFlashlightSize => 250;
|
||||
public override float DefaultFlashlightSize => 200;
|
||||
|
||||
protected override Flashlight CreateFlashlight() => new TaikoFlashlight(this, playfield);
|
||||
|
||||
@ -46,7 +46,9 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
: base(modFlashlight)
|
||||
{
|
||||
this.taikoPlayfield = taikoPlayfield;
|
||||
|
||||
FlashlightSize = getSizeFor(0);
|
||||
FlashlightSmoothness = 1.4f;
|
||||
|
||||
AddLayout(flashlightProperties);
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
@ -78,7 +77,7 @@ namespace osu.Game.Tests.Online
|
||||
}
|
||||
};
|
||||
|
||||
beatmaps.AllowImport = new TaskCompletionSource<bool>();
|
||||
beatmaps.AllowImport.Reset();
|
||||
|
||||
testBeatmapFile = TestResources.GetQuickTestBeatmapForImport();
|
||||
|
||||
@ -132,7 +131,7 @@ namespace osu.Game.Tests.Online
|
||||
AddStep("finish download", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet))!.TriggerSuccess(testBeatmapFile));
|
||||
addAvailabilityCheckStep("state importing", BeatmapAvailability.Importing);
|
||||
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true));
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.Set());
|
||||
AddUntilStep("wait for import", () => beatmaps.CurrentImport != null);
|
||||
AddUntilStep("ensure beatmap available", () => beatmaps.IsAvailableLocally(testBeatmapSet));
|
||||
addAvailabilityCheckStep("state is locally available", BeatmapAvailability.LocallyAvailable);
|
||||
@ -141,7 +140,7 @@ namespace osu.Game.Tests.Online
|
||||
[Test]
|
||||
public void TestTrackerRespectsSoftDeleting()
|
||||
{
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true));
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.Set());
|
||||
AddStep("import beatmap", () => beatmaps.Import(testBeatmapFile).WaitSafely());
|
||||
addAvailabilityCheckStep("state locally available", BeatmapAvailability.LocallyAvailable);
|
||||
|
||||
@ -155,7 +154,7 @@ namespace osu.Game.Tests.Online
|
||||
[Test]
|
||||
public void TestTrackerRespectsChecksum()
|
||||
{
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true));
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.Set());
|
||||
AddStep("import beatmap", () => beatmaps.Import(testBeatmapFile).WaitSafely());
|
||||
addAvailabilityCheckStep("initially locally available", BeatmapAvailability.LocallyAvailable);
|
||||
|
||||
@ -202,7 +201,7 @@ namespace osu.Game.Tests.Online
|
||||
|
||||
private class TestBeatmapManager : BeatmapManager
|
||||
{
|
||||
public TaskCompletionSource<bool> AllowImport = new TaskCompletionSource<bool>();
|
||||
public readonly ManualResetEventSlim AllowImport = new ManualResetEventSlim();
|
||||
|
||||
public Live<BeatmapSetInfo> CurrentImport { get; private set; }
|
||||
|
||||
@ -229,7 +228,9 @@ namespace osu.Game.Tests.Online
|
||||
|
||||
public override Live<BeatmapSetInfo> ImportModel(BeatmapSetInfo item, ArchiveReader archive = null, bool batchImport = false, CancellationToken cancellationToken = default)
|
||||
{
|
||||
testBeatmapManager.AllowImport.Task.WaitSafely();
|
||||
if (!testBeatmapManager.AllowImport.Wait(TimeSpan.FromSeconds(10), cancellationToken))
|
||||
throw new TimeoutException("Timeout waiting for import to be allowed.");
|
||||
|
||||
return (testBeatmapManager.CurrentImport = base.ImportModel(item, archive, batchImport, cancellationToken));
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
||||
addSeekStep(3000);
|
||||
AddAssert("all judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged));
|
||||
AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses >= 7));
|
||||
AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Select(kc => kc.CountPresses).Sum() == 15);
|
||||
AddStep("clear results", () => Player.Results.Clear());
|
||||
addSeekStep(0);
|
||||
AddAssert("none judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged));
|
||||
|
@ -11,8 +11,10 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -167,14 +169,39 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("add control points", () => addControlPoints(testControlPoints, Time.Current));
|
||||
}
|
||||
|
||||
private void addHitObject(double time)
|
||||
[Test]
|
||||
public void TestVeryFlowScroll()
|
||||
{
|
||||
const double long_time_range = 100000;
|
||||
var manualClock = new ManualClock();
|
||||
|
||||
AddStep("set manual clock", () =>
|
||||
{
|
||||
manualClock.CurrentTime = 0;
|
||||
scrollContainers.ForEach(c => c.Clock = new FramedClock(manualClock));
|
||||
|
||||
setScrollAlgorithm(ScrollVisualisationMethod.Constant);
|
||||
scrollContainers.ForEach(c => c.TimeRange = long_time_range);
|
||||
});
|
||||
|
||||
AddStep("add hit objects", () =>
|
||||
{
|
||||
addHitObject(long_time_range);
|
||||
addHitObject(long_time_range + 100, 250);
|
||||
});
|
||||
|
||||
AddAssert("hit objects are alive", () => playfields.All(p => p.HitObjectContainer.AliveObjects.Count() == 2));
|
||||
}
|
||||
|
||||
private void addHitObject(double time, float size = 75)
|
||||
{
|
||||
playfields.ForEach(p =>
|
||||
{
|
||||
var hitObject = new TestDrawableHitObject(time);
|
||||
setAnchor(hitObject, p);
|
||||
var hitObject = new TestHitObject(size) { StartTime = time };
|
||||
var drawable = new TestDrawableHitObject(hitObject);
|
||||
|
||||
p.Add(hitObject);
|
||||
setAnchor(drawable, p);
|
||||
p.Add(drawable);
|
||||
});
|
||||
}
|
||||
|
||||
@ -248,6 +275,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override ScrollingHitObjectContainer CreateScrollingHitObjectContainer() => new TestScrollingHitObjectContainer();
|
||||
}
|
||||
|
||||
private class TestDrawableControlPoint : DrawableHitObject<HitObject>
|
||||
@ -281,22 +310,41 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
}
|
||||
|
||||
private class TestDrawableHitObject : DrawableHitObject<HitObject>
|
||||
private class TestHitObject : HitObject
|
||||
{
|
||||
public TestDrawableHitObject(double time)
|
||||
: base(new HitObject { StartTime = time, HitWindows = HitWindows.Empty })
|
||||
{
|
||||
Origin = Anchor.Custom;
|
||||
OriginPosition = new Vector2(75 / 4.0f);
|
||||
public readonly float Size;
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
public TestHitObject(float size)
|
||||
{
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestDrawableHitObject : DrawableHitObject<TestHitObject>
|
||||
{
|
||||
public TestDrawableHitObject(TestHitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
Size = new Vector2(hitObject.Size);
|
||||
|
||||
AddInternal(new Box
|
||||
{
|
||||
Size = new Vector2(75),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = new Color4(RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), 1)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class TestScrollingHitObjectContainer : ScrollingHitObjectContainer
|
||||
{
|
||||
protected override RectangleF GetConservativeBoundingBox(HitObjectLifetimeEntry entry)
|
||||
{
|
||||
if (entry.HitObject is TestHitObject testObject)
|
||||
return new RectangleF().Inflate(testObject.Size / 2);
|
||||
|
||||
return base.GetConservativeBoundingBox(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
AddSliderStep("score", 0, 1000000, 500000, v => scoreProcessor.TotalScore.Value = v);
|
||||
AddSliderStep("accuracy", 0f, 1f, 0.5f, v => scoreProcessor.Accuracy.Value = v);
|
||||
AddSliderStep("combo", 0, 1000, 0, v => scoreProcessor.Combo.Value = v);
|
||||
AddSliderStep("combo", 0, 10000, 0, v => scoreProcessor.HighestCombo.Value = v);
|
||||
AddStep("toggle expanded", () => leaderboard.Expanded.Value = !leaderboard.Expanded.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -32,6 +32,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
protected new OutroPlayer Player => (OutroPlayer)base.Player;
|
||||
|
||||
private double currentBeatmapDuration;
|
||||
private double currentStoryboardDuration;
|
||||
|
||||
private bool showResults = true;
|
||||
@ -45,7 +46,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("enable storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, true));
|
||||
AddStep("set dim level to 0", () => LocalConfig.SetValue<double>(OsuSetting.DimLevel, 0));
|
||||
AddStep("reset fail conditions", () => currentFailConditions = (_, _) => false);
|
||||
AddStep("set storyboard duration to 2s", () => currentStoryboardDuration = 2000);
|
||||
AddStep("set beatmap duration to 0s", () => currentBeatmapDuration = 0);
|
||||
AddStep("set storyboard duration to 8s", () => currentStoryboardDuration = 8000);
|
||||
AddStep("set ShowResults = true", () => showResults = true);
|
||||
}
|
||||
|
||||
@ -151,6 +153,24 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("player exited", () => Stack.CurrentScreen == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPerformExitAfterOutro()
|
||||
{
|
||||
CreateTest(() =>
|
||||
{
|
||||
AddStep("set beatmap duration to 4s", () => currentBeatmapDuration = 4000);
|
||||
AddStep("set storyboard duration to 1s", () => currentStoryboardDuration = 1000);
|
||||
});
|
||||
|
||||
AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.CurrentTime >= currentStoryboardDuration);
|
||||
AddStep("exit via pause", () => Player.ExitViaPause());
|
||||
AddAssert("player paused", () => !Player.IsResuming);
|
||||
|
||||
AddStep("resume player", () => Player.Resume());
|
||||
AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value);
|
||||
AddUntilStep("wait for score shown", () => Player.IsScoreShown);
|
||||
}
|
||||
|
||||
protected override bool AllowFail => true;
|
||||
|
||||
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||
@ -160,7 +180,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||
{
|
||||
var beatmap = new Beatmap();
|
||||
beatmap.HitObjects.Add(new HitCircle());
|
||||
beatmap.HitObjects.Add(new HitCircle { StartTime = currentBeatmapDuration });
|
||||
return beatmap;
|
||||
}
|
||||
|
||||
@ -189,7 +209,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
private event Func<HealthProcessor, JudgementResult, bool> failConditions;
|
||||
|
||||
public OutroPlayer(Func<HealthProcessor, JudgementResult, bool> failConditions, bool showResults = true)
|
||||
: base(false, showResults)
|
||||
: base(showResults: showResults)
|
||||
{
|
||||
this.failConditions = failConditions;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
[Test]
|
||||
public void TestEditDefaultSkin()
|
||||
{
|
||||
AddAssert("is default skin", () => skinManager.CurrentSkinInfo.Value.ID == SkinInfo.TRIANGLES_SKIN);
|
||||
AddAssert("is default skin", () => skinManager.CurrentSkinInfo.Value.ID == SkinInfo.ARGON_SKIN);
|
||||
|
||||
AddStep("open settings", () => { Game.Settings.Show(); });
|
||||
|
||||
@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddStep("open skin editor", () => skinEditor.Show());
|
||||
|
||||
// Until step required as the skin editor may take time to load (and an extra scheduled frame for the mutable part).
|
||||
AddUntilStep("is modified default skin", () => skinManager.CurrentSkinInfo.Value.ID != SkinInfo.TRIANGLES_SKIN);
|
||||
AddUntilStep("is modified default skin", () => skinManager.CurrentSkinInfo.Value.ID != SkinInfo.ARGON_SKIN);
|
||||
AddAssert("is not protected", () => skinManager.CurrentSkinInfo.Value.PerformRead(s => !s.Protected));
|
||||
|
||||
AddUntilStep("export button enabled", () => Game.Settings.ChildrenOfType<SkinSection.ExportSkinButton>().SingleOrDefault()?.Enabled.Value == true);
|
||||
|
@ -9,8 +9,10 @@ using NUnit.Framework;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Scoring;
|
||||
@ -92,6 +94,31 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
returnToMenu();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFromSongSelectWithFilter([Values] ScorePresentType type)
|
||||
{
|
||||
AddStep("enter song select", () => Game.ChildrenOfType<ButtonSystem>().Single().OnSolo.Invoke());
|
||||
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded);
|
||||
|
||||
AddStep("filter to nothing", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).FilterControl.CurrentTextSearch.Value = "fdsajkl;fgewq");
|
||||
AddUntilStep("wait for no results", () => Beatmap.IsDefault);
|
||||
|
||||
var firstImport = importScore(1, new CatchRuleset().RulesetInfo);
|
||||
presentAndConfirm(firstImport, type);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFromSongSelectWithConvertRulesetChange([Values] ScorePresentType type)
|
||||
{
|
||||
AddStep("enter song select", () => Game.ChildrenOfType<ButtonSystem>().Single().OnSolo.Invoke());
|
||||
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded);
|
||||
|
||||
AddStep("set convert to false", () => Game.LocalConfig.SetValue(OsuSetting.ShowConvertedBeatmaps, false));
|
||||
|
||||
var firstImport = importScore(1, new CatchRuleset().RulesetInfo);
|
||||
presentAndConfirm(firstImport, type);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFromSongSelect([Values] ScorePresentType type)
|
||||
{
|
||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Configuration
|
||||
{
|
||||
// UI/selection defaults
|
||||
SetDefault(OsuSetting.Ruleset, string.Empty);
|
||||
SetDefault(OsuSetting.Skin, SkinInfo.TRIANGLES_SKIN.ToString());
|
||||
SetDefault(OsuSetting.Skin, SkinInfo.ARGON_SKIN.ToString());
|
||||
|
||||
SetDefault(OsuSetting.BeatmapDetailTab, PlayBeatmapDetailArea.TabType.Details);
|
||||
SetDefault(OsuSetting.BeatmapDetailModsFilter, false);
|
||||
@ -120,7 +120,7 @@ namespace osu.Game.Configuration
|
||||
// Gameplay
|
||||
SetDefault(OsuSetting.PositionalHitsounds, true); // replaced by level setting below, can be removed 20220703.
|
||||
SetDefault(OsuSetting.PositionalHitsoundsLevel, 0.2f, 0, 1);
|
||||
SetDefault(OsuSetting.DimLevel, 0.8, 0, 1, 0.01);
|
||||
SetDefault(OsuSetting.DimLevel, 0.7, 0, 1, 0.01);
|
||||
SetDefault(OsuSetting.BlurLevel, 0, 0, 1, 0.01);
|
||||
SetDefault(OsuSetting.LightenDuringBreaks, true);
|
||||
|
||||
|
@ -89,6 +89,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString Collections => new TranslatableString(getKey(@"collections"), @"Collections");
|
||||
|
||||
/// <summary>
|
||||
/// "Mod presets"
|
||||
/// </summary>
|
||||
public static LocalisableString ModPresets => new TranslatableString(getKey(@"mod_presets"), @"Mod presets");
|
||||
|
||||
/// <summary>
|
||||
/// "Name"
|
||||
/// </summary>
|
||||
|
@ -44,11 +44,6 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString ClearAllCaches => new TranslatableString(getKey(@"clear_all_caches"), @"Clear all caches");
|
||||
|
||||
/// <summary>
|
||||
/// "Compact realm"
|
||||
/// </summary>
|
||||
public static LocalisableString CompactRealm => new TranslatableString(getKey(@"compact_realm"), @"Compact realm");
|
||||
|
||||
private static string getKey(string key) => $"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString RunSetupWizard => new TranslatableString(getKey(@"run_setup_wizard"), @"Run setup wizard");
|
||||
|
||||
/// <summary>
|
||||
/// "You are running the latest release ({0})"
|
||||
/// </summary>
|
||||
public static LocalisableString RunningLatestRelease(string version) => new TranslatableString(getKey(@"running_latest_release"), @"You are running the latest release ({0})", version);
|
||||
|
||||
private static string getKey(string key) => $"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,41 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString SelectDirectory => new TranslatableString(getKey(@"select_directory"), @"Select directory");
|
||||
|
||||
/// <summary>
|
||||
/// "Migration in progress"
|
||||
/// </summary>
|
||||
public static LocalisableString MigrationInProgress => new TranslatableString(getKey(@"migration_in_progress"), @"Migration in progress");
|
||||
|
||||
/// <summary>
|
||||
/// "This could take a few minutes depending on the speed of your disk(s)."
|
||||
/// </summary>
|
||||
public static LocalisableString MigrationDescription => new TranslatableString(getKey(@"migration_description"), @"This could take a few minutes depending on the speed of your disk(s).");
|
||||
|
||||
/// <summary>
|
||||
/// "Please avoid interacting with the game!"
|
||||
/// </summary>
|
||||
public static LocalisableString ProhibitedInteractDuringMigration => new TranslatableString(getKey(@"prohibited_interact_during_migration"), @"Please avoid interacting with the game!");
|
||||
|
||||
/// <summary>
|
||||
/// "Some files couldn't be cleaned up during migration. Clicking this notification will open the folder so you can manually clean things up."
|
||||
/// </summary>
|
||||
public static LocalisableString FailedCleanupNotification => new TranslatableString(getKey(@"failed_cleanup_notification"), @"Some files couldn't be cleaned up during migration. Clicking this notification will open the folder so you can manually clean things up.");
|
||||
|
||||
/// <summary>
|
||||
/// "Please select a new location"
|
||||
/// </summary>
|
||||
public static LocalisableString SelectNewLocation => new TranslatableString(getKey(@"select_new_location"), @"Please select a new location");
|
||||
|
||||
/// <summary>
|
||||
/// "The target directory already seems to have an osu! install. Use that data instead?"
|
||||
/// </summary>
|
||||
public static LocalisableString TargetDirectoryAlreadyInstalledOsu => new TranslatableString(getKey(@"target_directory_already_installed_osu"), @"The target directory already seems to have an osu! install. Use that data instead?");
|
||||
|
||||
/// <summary>
|
||||
/// "To complete this operation, osu! will close. Please open it again to use the new data location."
|
||||
/// </summary>
|
||||
public static LocalisableString RestartAndReOpenRequiredForCompletion => new TranslatableString(getKey(@"restart_and_re_open_required_for_completion"), @"To complete this operation, osu! will close. Please open it again to use the new data location.");
|
||||
|
||||
/// <summary>
|
||||
/// "Import beatmaps from stable"
|
||||
/// </summary>
|
||||
@ -84,6 +119,26 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString RestoreAllRecentlyDeletedModPresets => new TranslatableString(getKey(@"restore_all_recently_deleted_mod_presets"), @"Restore all recently deleted mod presets");
|
||||
|
||||
/// <summary>
|
||||
/// "Deleted all collections!"
|
||||
/// </summary>
|
||||
public static LocalisableString DeletedAllCollections => new TranslatableString(getKey(@"deleted_all_collections"), @"Deleted all collections!");
|
||||
|
||||
/// <summary>
|
||||
/// "Deleted all mod presets!"
|
||||
/// </summary>
|
||||
public static LocalisableString DeletedAllModPresets => new TranslatableString(getKey(@"deleted_all_mod_presets"), @"Deleted all mod presets!");
|
||||
|
||||
/// <summary>
|
||||
/// "Restored all deleted mod presets!"
|
||||
/// </summary>
|
||||
public static LocalisableString RestoredAllDeletedModPresets => new TranslatableString(getKey(@"restored_all_deleted_mod_presets"), @"Restored all deleted mod presets!");
|
||||
|
||||
/// <summary>
|
||||
/// "Please select your osu!stable install location"
|
||||
/// </summary>
|
||||
public static LocalisableString StableDirectorySelectHeader => new TranslatableString(getKey(@"stable_directory_select_header"), @"Please select your osu!stable install location");
|
||||
|
||||
private static string getKey(string key) => $"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString NoTabletDetected => new TranslatableString(getKey(@"no_tablet_detected"), @"No tablet detected!");
|
||||
|
||||
/// <summary>
|
||||
/// "If your tablet is not detected, please read [this FAQ]({0}) for troubleshooting steps."
|
||||
/// </summary>
|
||||
public static LocalisableString NoTabletDetectedDescription(string url) => new TranslatableString(getKey(@"no_tablet_detected_description"), @"If your tablet is not detected, please read [this FAQ]({0}) for troubleshooting steps.", url);
|
||||
|
||||
/// <summary>
|
||||
/// "Reset to full area"
|
||||
/// </summary>
|
||||
|
@ -1,9 +1,9 @@
|
||||
// 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.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -58,7 +58,7 @@ namespace osu.Game.Online.Spectator
|
||||
{
|
||||
await connection.InvokeAsync(nameof(ISpectatorServer.BeginPlaySession), state);
|
||||
}
|
||||
catch (HubException exception)
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception.GetHubExceptionMessage() == HubClientConnector.SERVER_SHUTDOWN_MESSAGE)
|
||||
{
|
||||
|
@ -561,9 +561,11 @@ namespace osu.Game
|
||||
return;
|
||||
}
|
||||
|
||||
// This should be able to be performed from song select, but that is disabled for now
|
||||
// due to the weird decoupled ruleset logic (which can cause a crash in certain filter scenarios).
|
||||
PerformFromScreen(screen =>
|
||||
{
|
||||
Logger.Log($"{nameof(PresentScore)} updating beatmap ({databasedBeatmap}) and ruleset ({databasedScore.ScoreInfo.Ruleset} to match score");
|
||||
Logger.Log($"{nameof(PresentScore)} updating beatmap ({databasedBeatmap}) and ruleset ({databasedScore.ScoreInfo.Ruleset}) to match score");
|
||||
|
||||
Ruleset.Value = databasedScore.ScoreInfo.Ruleset;
|
||||
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
|
||||
@ -578,7 +580,7 @@ namespace osu.Game
|
||||
screen.Push(new SoloResultsScreen(databasedScore.ScoreInfo, false));
|
||||
break;
|
||||
}
|
||||
}, validScreens: new[] { typeof(PlaySongSelect) });
|
||||
});
|
||||
}
|
||||
|
||||
public override Task Import(params ImportTask[] imports)
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Dialog
|
||||
@ -20,7 +21,7 @@ namespace osu.Game.Overlays.Dialog
|
||||
/// <param name="message">The description of the action to be displayed to the user.</param>
|
||||
/// <param name="onConfirm">An action to perform on confirmation.</param>
|
||||
/// <param name="onCancel">An optional action to perform on cancel.</param>
|
||||
public ConfirmDialog(string message, Action onConfirm, Action onCancel = null)
|
||||
public ConfirmDialog(LocalisableString message, Action onConfirm, Action onCancel = null)
|
||||
{
|
||||
HeaderText = message;
|
||||
BodyText = "Last chance to turn back";
|
||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings
|
||||
},
|
||||
new SettingsButton
|
||||
{
|
||||
Text = DebugSettingsStrings.CompactRealm,
|
||||
Text = "Compact realm",
|
||||
Action = () =>
|
||||
{
|
||||
// Blocking operations implicitly causes a Compact().
|
||||
|
@ -44,9 +44,12 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
},
|
||||
};
|
||||
|
||||
if (!LanguageExtensions.TryParseCultureCode(frameworkLocale.Value, out var locale))
|
||||
locale = Language.en;
|
||||
languageSelection.Current.Value = locale;
|
||||
frameworkLocale.BindValueChanged(locale =>
|
||||
{
|
||||
if (!LanguageExtensions.TryParseCultureCode(locale.NewValue, out var language))
|
||||
language = Language.en;
|
||||
languageSelection.Current.Value = language;
|
||||
}, true);
|
||||
|
||||
languageSelection.Current.BindValueChanged(val => frameworkLocale.Value = val.NewValue.ToCultureCode());
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
{
|
||||
notifications?.Post(new SimpleNotification
|
||||
{
|
||||
Text = $"You are running the latest release ({game.Version})",
|
||||
Text = GeneralSettingsStrings.RunningLatestRelease(game.Version),
|
||||
Icon = FontAwesome.Solid.CheckCircle,
|
||||
});
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user