mirror of
https://github.com/ppy/osu.git
synced 2024-12-18 13:12:57 +08:00
Merge branch 'master' into present-recommended
This commit is contained in:
commit
2b30a5f217
@ -5,6 +5,6 @@
|
|||||||
"version": "3.1.100"
|
"version": "3.1.100"
|
||||||
},
|
},
|
||||||
"msbuild-sdks": {
|
"msbuild-sdks": {
|
||||||
"Microsoft.Build.Traversal": "2.2.3"
|
"Microsoft.Build.Traversal": "3.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -51,7 +51,7 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.1030.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.1120.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.1201.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -59,7 +59,7 @@ namespace osu.Desktop
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
|
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
|
||||||
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString()?.Split('"')[1].Replace("osu!.exe", "");
|
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty)?.ToString()?.Split('"')[1].Replace("osu!.exe", "");
|
||||||
|
|
||||||
if (checkExists(stableInstallPath))
|
if (checkExists(stableInstallPath))
|
||||||
return stableInstallPath;
|
return stableInstallPath;
|
||||||
@ -138,7 +138,7 @@ namespace osu.Desktop
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
// SDL2 DesktopWindow
|
// SDL2 DesktopWindow
|
||||||
case DesktopWindow desktopWindow:
|
case SDL2DesktopWindow desktopWindow:
|
||||||
desktopWindow.CursorState |= CursorState.Hidden;
|
desktopWindow.CursorState |= CursorState.Hidden;
|
||||||
desktopWindow.SetIconFromStream(iconStream);
|
desktopWindow.SetIconFromStream(iconStream);
|
||||||
desktopWindow.Title = Name;
|
desktopWindow.Title = Name;
|
||||||
|
@ -22,9 +22,9 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
// Back up the cwd before DesktopGameHost changes it
|
// Back up the cwd before DesktopGameHost changes it
|
||||||
var cwd = Environment.CurrentDirectory;
|
var cwd = Environment.CurrentDirectory;
|
||||||
bool useSdl = args.Contains("--sdl");
|
bool useOsuTK = args.Contains("--tk");
|
||||||
|
|
||||||
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true, useSdl: useSdl))
|
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true, useOsuTK: useOsuTK))
|
||||||
{
|
{
|
||||||
host.ExceptionThrown += handleException;
|
host.ExceptionThrown += handleException;
|
||||||
|
|
||||||
|
@ -24,12 +24,12 @@
|
|||||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="System.IO.Packaging" Version="4.7.0" />
|
<PackageReference Include="System.IO.Packaging" Version="5.0.0" />
|
||||||
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.4" />
|
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.4" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
|
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||||
<PackageReference Include="DiscordRichPresence" Version="1.0.150" />
|
<PackageReference Include="DiscordRichPresence" Version="1.0.166" />
|
||||||
<!-- .NET 3.1 SDK seems to cause issues with a runtime specification. This will likely be resolved in .NET 5. -->
|
<!-- .NET 3.1 SDK seems to cause issues with a runtime specification. This will likely be resolved in .NET 5. -->
|
||||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||||
<PackageReference Include="System.Runtime.Handles" Version="4.3.0" />
|
<PackageReference Include="System.Runtime.Handles" Version="4.3.0" />
|
||||||
|
@ -38,17 +38,17 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods
|
|||||||
new Fruit
|
new Fruit
|
||||||
{
|
{
|
||||||
X = 0,
|
X = 0,
|
||||||
StartTime = 250
|
StartTime = 1000
|
||||||
},
|
},
|
||||||
new Fruit
|
new Fruit
|
||||||
{
|
{
|
||||||
X = CatchPlayfield.WIDTH,
|
X = CatchPlayfield.WIDTH,
|
||||||
StartTime = 500
|
StartTime = 2000
|
||||||
},
|
},
|
||||||
new JuiceStream
|
new JuiceStream
|
||||||
{
|
{
|
||||||
X = CatchPlayfield.CENTER_X,
|
X = CatchPlayfield.CENTER_X,
|
||||||
StartTime = 750,
|
StartTime = 3000,
|
||||||
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, Vector2.UnitY * 200 })
|
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, Vector2.UnitY * 200 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
NewCombo = i % 8 == 0,
|
NewCombo = i % 8 == 0,
|
||||||
Samples = new List<HitSampleInfo>(new[]
|
Samples = new List<HitSampleInfo>(new[]
|
||||||
{
|
{
|
||||||
new HitSampleInfo { Bank = "normal", Name = "hitnormal", Volume = 100 }
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "normal", volume: 100)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneCatchPlayerLegacySkin : LegacySkinPlayerTestScene
|
||||||
|
{
|
||||||
|
protected override Ruleset CreatePlayerRuleset() => new CatchRuleset();
|
||||||
|
}
|
||||||
|
}
|
@ -10,14 +10,12 @@ using osu.Framework.Testing;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
|
||||||
using osu.Game.Rulesets.Catch.Judgements;
|
using osu.Game.Rulesets.Catch.Judgements;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
@ -103,7 +101,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
CreateDrawableRepresentation = ((DrawableRuleset<CatchHitObject>)catchRuleset.CreateInstance().CreateDrawableRulesetWith(new CatchBeatmap())).CreateDrawableRepresentation
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
if (juice.NestedHitObjects.Last() is CatchHitObject tail)
|
if (juice.NestedHitObjects.Last() is CatchHitObject tail)
|
||||||
tail.LastInCombo = true; // usually the (Catch)BeatmapProcessor would do this for us when necessary
|
tail.LastInCombo = true; // usually the (Catch)BeatmapProcessor would do this for us when necessary
|
||||||
|
|
||||||
addToPlayfield(new DrawableJuiceStream(juice, drawableRuleset.CreateDrawableRepresentation));
|
addToPlayfield(new DrawableJuiceStream(juice));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void spawnBananas(bool hit = false)
|
private void spawnBananas(bool hit = false)
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -17,34 +18,49 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
foreach (FruitVisualRepresentation rep in Enum.GetValues(typeof(FruitVisualRepresentation)))
|
AddStep("show pear", () => SetContents(() => createDrawableFruit(0)));
|
||||||
AddStep($"show {rep}", () => SetContents(() => createDrawableFruit(rep)));
|
AddStep("show grape", () => SetContents(() => createDrawableFruit(1)));
|
||||||
|
AddStep("show pineapple / apple", () => SetContents(() => createDrawableFruit(2)));
|
||||||
|
AddStep("show raspberry / orange", () => SetContents(() => createDrawableFruit(3)));
|
||||||
|
|
||||||
|
AddStep("show banana", () => SetContents(createDrawableBanana));
|
||||||
|
|
||||||
AddStep("show droplet", () => SetContents(() => createDrawableDroplet()));
|
AddStep("show droplet", () => SetContents(() => createDrawableDroplet()));
|
||||||
AddStep("show tiny droplet", () => SetContents(createDrawableTinyDroplet));
|
AddStep("show tiny droplet", () => SetContents(createDrawableTinyDroplet));
|
||||||
|
|
||||||
foreach (FruitVisualRepresentation rep in Enum.GetValues(typeof(FruitVisualRepresentation)))
|
AddStep("show hyperdash pear", () => SetContents(() => createDrawableFruit(0, true)));
|
||||||
AddStep($"show hyperdash {rep}", () => SetContents(() => createDrawableFruit(rep, true)));
|
AddStep("show hyperdash grape", () => SetContents(() => createDrawableFruit(1, true)));
|
||||||
|
AddStep("show hyperdash pineapple / apple", () => SetContents(() => createDrawableFruit(2, true)));
|
||||||
|
AddStep("show hyperdash raspberry / orange", () => SetContents(() => createDrawableFruit(3, true)));
|
||||||
|
|
||||||
AddStep("show hyperdash droplet", () => SetContents(() => createDrawableDroplet(true)));
|
AddStep("show hyperdash droplet", () => SetContents(() => createDrawableDroplet(true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable createDrawableFruit(FruitVisualRepresentation rep, bool hyperdash = false) =>
|
private Drawable createDrawableFruit(int indexInBeatmap, bool hyperdash = false) =>
|
||||||
setProperties(new DrawableFruit(new TestCatchFruit(rep)), hyperdash);
|
SetProperties(new DrawableFruit(new Fruit
|
||||||
|
{
|
||||||
|
IndexInBeatmap = indexInBeatmap,
|
||||||
|
HyperDashBindable = { Value = hyperdash }
|
||||||
|
}));
|
||||||
|
|
||||||
private Drawable createDrawableDroplet(bool hyperdash = false) => setProperties(new DrawableDroplet(new Droplet()), hyperdash);
|
private Drawable createDrawableBanana() =>
|
||||||
|
SetProperties(new DrawableBanana(new Banana()));
|
||||||
|
|
||||||
private Drawable createDrawableTinyDroplet() => setProperties(new DrawableTinyDroplet(new TinyDroplet()));
|
private Drawable createDrawableDroplet(bool hyperdash = false) =>
|
||||||
|
SetProperties(new DrawableDroplet(new Droplet
|
||||||
|
{
|
||||||
|
HyperDashBindable = { Value = hyperdash }
|
||||||
|
}));
|
||||||
|
|
||||||
private DrawableCatchHitObject setProperties(DrawableCatchHitObject d, bool hyperdash = false)
|
private Drawable createDrawableTinyDroplet() => SetProperties(new DrawableTinyDroplet(new TinyDroplet()));
|
||||||
|
|
||||||
|
protected virtual DrawableCatchHitObject SetProperties(DrawableCatchHitObject d)
|
||||||
{
|
{
|
||||||
var hitObject = d.HitObject;
|
var hitObject = d.HitObject;
|
||||||
|
hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = 0 });
|
||||||
hitObject.StartTime = 1000000000000;
|
hitObject.StartTime = 1000000000000;
|
||||||
hitObject.Scale = 1.5f;
|
hitObject.Scale = 1.5f;
|
||||||
|
|
||||||
if (hyperdash)
|
|
||||||
hitObject.HyperDashTarget = new Banana();
|
|
||||||
|
|
||||||
d.Anchor = Anchor.Centre;
|
d.Anchor = Anchor.Centre;
|
||||||
d.RelativePositionAxes = Axes.None;
|
d.RelativePositionAxes = Axes.None;
|
||||||
d.Position = Vector2.Zero;
|
d.Position = Vector2.Zero;
|
||||||
@ -55,15 +71,5 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
};
|
};
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TestCatchFruit : Fruit
|
|
||||||
{
|
|
||||||
public TestCatchFruit(FruitVisualRepresentation rep)
|
|
||||||
{
|
|
||||||
VisualRepresentation = rep;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override FruitVisualRepresentation VisualRepresentation { get; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
osu.Game.Rulesets.Catch.Tests/TestSceneFruitVisualChange.cs
Normal file
32
osu.Game.Rulesets.Catch.Tests/TestSceneFruitVisualChange.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// 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.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
|
{
|
||||||
|
public class TestSceneFruitVisualChange : TestSceneFruitObjects
|
||||||
|
{
|
||||||
|
private readonly Bindable<int> indexInBeatmap = new Bindable<int>();
|
||||||
|
private readonly Bindable<bool> hyperDash = new Bindable<bool>();
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
AddStep("fruit changes visual and hyper", () => SetContents(() => SetProperties(new DrawableFruit(new Fruit
|
||||||
|
{
|
||||||
|
IndexInBeatmapBindable = { BindTarget = indexInBeatmap },
|
||||||
|
HyperDashBindable = { BindTarget = hyperDash },
|
||||||
|
}))));
|
||||||
|
|
||||||
|
AddStep("droplet changes hyper", () => SetContents(() => SetProperties(new DrawableDroplet(new Droplet
|
||||||
|
{
|
||||||
|
HyperDashBindable = { BindTarget = hyperDash },
|
||||||
|
}))));
|
||||||
|
|
||||||
|
Scheduler.AddDelayed(() => indexInBeatmap.Value++, 250, true);
|
||||||
|
Scheduler.AddDelayed(() => hyperDash.Value = !hyperDash.Value, 1000, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -5,11 +5,11 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Catch.MathUtils;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
|
||||||
using osu.Game.Rulesets.Catch.MathUtils;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Beatmaps
|
namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||||
{
|
{
|
||||||
@ -192,24 +192,24 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
|
|
||||||
private static void initialiseHyperDash(IBeatmap beatmap)
|
private static void initialiseHyperDash(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
List<CatchHitObject> objectWithDroplets = new List<CatchHitObject>();
|
List<PalpableCatchHitObject> palpableObjects = new List<PalpableCatchHitObject>();
|
||||||
|
|
||||||
foreach (var currentObject in beatmap.HitObjects)
|
foreach (var currentObject in beatmap.HitObjects)
|
||||||
{
|
{
|
||||||
if (currentObject is Fruit fruitObject)
|
if (currentObject is Fruit fruitObject)
|
||||||
objectWithDroplets.Add(fruitObject);
|
palpableObjects.Add(fruitObject);
|
||||||
|
|
||||||
if (currentObject is JuiceStream)
|
if (currentObject is JuiceStream)
|
||||||
{
|
{
|
||||||
foreach (var currentJuiceElement in currentObject.NestedHitObjects)
|
foreach (var juice in currentObject.NestedHitObjects)
|
||||||
{
|
{
|
||||||
if (!(currentJuiceElement is TinyDroplet))
|
if (juice is PalpableCatchHitObject palpableObject && !(juice is TinyDroplet))
|
||||||
objectWithDroplets.Add((CatchHitObject)currentJuiceElement);
|
palpableObjects.Add(palpableObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
objectWithDroplets.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime));
|
palpableObjects.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime));
|
||||||
|
|
||||||
double halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) / 2;
|
double halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) / 2;
|
||||||
|
|
||||||
@ -221,10 +221,10 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
int lastDirection = 0;
|
int lastDirection = 0;
|
||||||
double lastExcess = halfCatcherWidth;
|
double lastExcess = halfCatcherWidth;
|
||||||
|
|
||||||
for (int i = 0; i < objectWithDroplets.Count - 1; i++)
|
for (int i = 0; i < palpableObjects.Count - 1; i++)
|
||||||
{
|
{
|
||||||
CatchHitObject currentObject = objectWithDroplets[i];
|
var currentObject = palpableObjects[i];
|
||||||
CatchHitObject nextObject = objectWithDroplets[i + 1];
|
var nextObject = palpableObjects[i + 1];
|
||||||
|
|
||||||
// Reset variables in-case values have changed (e.g. after applying HR)
|
// Reset variables in-case values have changed (e.g. after applying HR)
|
||||||
currentObject.HyperDashTarget = null;
|
currentObject.HyperDashTarget = null;
|
||||||
|
@ -12,9 +12,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
|||||||
{
|
{
|
||||||
private const float normalized_hitobject_radius = 41.0f;
|
private const float normalized_hitobject_radius = 41.0f;
|
||||||
|
|
||||||
public new CatchHitObject BaseObject => (CatchHitObject)base.BaseObject;
|
public new PalpableCatchHitObject BaseObject => (PalpableCatchHitObject)base.BaseObject;
|
||||||
|
|
||||||
public new CatchHitObject LastObject => (CatchHitObject)base.LastObject;
|
public new PalpableCatchHitObject LastObject => (PalpableCatchHitObject)base.LastObject;
|
||||||
|
|
||||||
public readonly float NormalizedPosition;
|
public readonly float NormalizedPosition;
|
||||||
public readonly float LastNormalizedPosition;
|
public readonly float LastNormalizedPosition;
|
||||||
|
@ -5,7 +5,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModEasy : ModEasy
|
public class CatchModEasy : ModEasyWithExtraLives
|
||||||
{
|
{
|
||||||
public override string Description => @"Larger fruits, more forgiving HP drain, less accuracy required, and three lives!";
|
public override string Description => @"Larger fruits, more forgiving HP drain, less accuracy required, and three lives!";
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,27 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Rulesets.Catch.Judgements;
|
using osu.Game.Rulesets.Catch.Judgements;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public class Banana : Fruit
|
public class Banana : Fruit, IHasComboInformation
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Index of banana in current shower.
|
/// Index of banana in current shower.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int BananaIndex;
|
public int BananaIndex;
|
||||||
|
|
||||||
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
|
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new CatchBananaJudgement();
|
public override Judgement CreateJudgement() => new CatchBananaJudgement();
|
||||||
|
|
||||||
private static readonly List<HitSampleInfo> samples = new List<HitSampleInfo> { new BananaHitSampleInfo() };
|
private static readonly List<HitSampleInfo> samples = new List<HitSampleInfo> { new BananaHitSampleInfo() };
|
||||||
@ -26,11 +31,50 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
Samples = samples;
|
Samples = samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BananaHitSampleInfo : HitSampleInfo
|
private Color4? colour;
|
||||||
{
|
|
||||||
private static string[] lookupNames { get; } = { "metronomelow", "catch-banana" };
|
|
||||||
|
|
||||||
public override IEnumerable<string> LookupNames => lookupNames;
|
Color4 IHasComboInformation.GetComboColour(IReadOnlyList<Color4> comboColours)
|
||||||
|
{
|
||||||
|
// override any external colour changes with banananana
|
||||||
|
return colour ??= getBananaColour();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color4 getBananaColour()
|
||||||
|
{
|
||||||
|
switch (RNG.Next(0, 3))
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
return new Color4(255, 240, 0, 255);
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
return new Color4(255, 192, 0, 255);
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
return new Color4(214, 221, 28, 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BananaHitSampleInfo : HitSampleInfo, IEquatable<BananaHitSampleInfo>
|
||||||
|
{
|
||||||
|
private static readonly string[] lookup_names = { "Gameplay/metronomelow", "Gameplay/catch-banana" };
|
||||||
|
|
||||||
|
public override IEnumerable<string> LookupNames => lookup_names;
|
||||||
|
|
||||||
|
public BananaHitSampleInfo(int volume = 0)
|
||||||
|
: base(string.Empty, volume: volume)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed override HitSampleInfo With(Optional<string> newName = default, Optional<string?> newBank = default, Optional<string?> newSuffix = default, Optional<int> newVolume = default)
|
||||||
|
=> new BananaHitSampleInfo(newVolume.GetOr(Volume));
|
||||||
|
|
||||||
|
public bool Equals(BananaHitSampleInfo? other)
|
||||||
|
=> other != null;
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
=> obj is BananaHitSampleInfo other && Equals(other);
|
||||||
|
|
||||||
|
public override int GetHashCode() => lookup_names.GetHashCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,6 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
{
|
{
|
||||||
public class BananaShower : CatchHitObject, IHasDuration
|
public class BananaShower : CatchHitObject, IHasDuration
|
||||||
{
|
{
|
||||||
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
|
|
||||||
|
|
||||||
public override bool LastInCombo => true;
|
public override bool LastInCombo => true;
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
||||||
|
@ -16,32 +16,47 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
{
|
{
|
||||||
public const float OBJECT_RADIUS = 64;
|
public const float OBJECT_RADIUS = 64;
|
||||||
|
|
||||||
private float x;
|
// This value is after XOffset applied.
|
||||||
|
public readonly Bindable<float> XBindable = new Bindable<float>();
|
||||||
|
|
||||||
|
// This value is before XOffset applied.
|
||||||
|
private float originalX;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The horizontal position of the fruit between 0 and <see cref="CatchPlayfield.WIDTH"/>.
|
/// The horizontal position of the fruit between 0 and <see cref="CatchPlayfield.WIDTH"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float X
|
public float X
|
||||||
{
|
{
|
||||||
get => x + XOffset;
|
// TODO: I don't like this asymmetry.
|
||||||
set => x = value;
|
get => XBindable.Value;
|
||||||
|
// originalX is set by `XBindable.BindValueChanged`
|
||||||
|
set => XBindable.Value = value + xOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private float xOffset;
|
||||||
/// Whether this object can be placed on the catcher's plate.
|
|
||||||
/// </summary>
|
|
||||||
public virtual bool CanBePlated => false;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A random offset applied to <see cref="X"/>, set by the <see cref="CatchBeatmapProcessor"/>.
|
/// A random offset applied to <see cref="X"/>, set by the <see cref="CatchBeatmapProcessor"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal float XOffset { get; set; }
|
internal float XOffset
|
||||||
|
{
|
||||||
|
get => xOffset;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
xOffset = value;
|
||||||
|
XBindable.Value = originalX + xOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public double TimePreempt = 1000;
|
public double TimePreempt = 1000;
|
||||||
|
|
||||||
public int IndexInBeatmap { get; set; }
|
public readonly Bindable<int> IndexInBeatmapBindable = new Bindable<int>();
|
||||||
|
|
||||||
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4);
|
public int IndexInBeatmap
|
||||||
|
{
|
||||||
|
get => IndexInBeatmapBindable.Value;
|
||||||
|
set => IndexInBeatmapBindable.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
public virtual bool NewCombo { get; set; }
|
public virtual bool NewCombo { get; set; }
|
||||||
|
|
||||||
@ -63,13 +78,6 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
set => ComboIndexBindable.Value = value;
|
set => ComboIndexBindable.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Difference between the distance to the next object
|
|
||||||
/// and the distance that would have triggered a hyper dash.
|
|
||||||
/// A value close to 0 indicates a difficult jump (for difficulty calculation).
|
|
||||||
/// </summary>
|
|
||||||
public float DistanceToHyperDash { get; set; }
|
|
||||||
|
|
||||||
public Bindable<bool> LastInComboBindable { get; } = new Bindable<bool>();
|
public Bindable<bool> LastInComboBindable { get; } = new Bindable<bool>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -81,17 +89,13 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
set => LastInComboBindable.Value = value;
|
set => LastInComboBindable.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float Scale { get; set; } = 1;
|
public readonly Bindable<float> ScaleBindable = new Bindable<float>(1);
|
||||||
|
|
||||||
/// <summary>
|
public float Scale
|
||||||
/// Whether this fruit can initiate a hyperdash.
|
{
|
||||||
/// </summary>
|
get => ScaleBindable.Value;
|
||||||
public bool HyperDash => HyperDashTarget != null;
|
set => ScaleBindable.Value = value;
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// The target fruit if we are to initiate a hyperdash.
|
|
||||||
/// </summary>
|
|
||||||
public CatchHitObject HyperDashTarget;
|
|
||||||
|
|
||||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||||
{
|
{
|
||||||
@ -103,22 +107,10 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
protected CatchHitObject()
|
||||||
/// Represents a single object that can be caught by the catcher.
|
{
|
||||||
/// </summary>
|
XBindable.BindValueChanged(x => originalX = x.NewValue - xOffset);
|
||||||
public abstract class PalpableCatchHitObject : CatchHitObject
|
}
|
||||||
{
|
|
||||||
public override bool CanBePlated => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum FruitVisualRepresentation
|
|
||||||
{
|
|
||||||
Pear,
|
|
||||||
Grape,
|
|
||||||
Pineapple,
|
|
||||||
Raspberry,
|
|
||||||
Banana // banananananannaanana
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,24 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableBanana : DrawableFruit
|
public class DrawableBanana : DrawableFruit
|
||||||
{
|
{
|
||||||
public DrawableBanana(Banana h)
|
protected override FruitVisualRepresentation GetVisualRepresentation(int indexInBeatmap) => FruitVisualRepresentation.Banana;
|
||||||
: base(h)
|
|
||||||
|
public DrawableBanana()
|
||||||
|
: this(null)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4? colour;
|
public DrawableBanana([CanBeNull] Banana h)
|
||||||
|
: base(h)
|
||||||
protected override Color4 GetComboColour(IReadOnlyList<Color4> comboColours)
|
|
||||||
{
|
{
|
||||||
// override any external colour changes with banananana
|
|
||||||
return colour ??= getBananaColour();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateInitialTransforms()
|
protected override void UpdateInitialTransforms()
|
||||||
@ -46,20 +44,5 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
if (Samples != null)
|
if (Samples != null)
|
||||||
Samples.Frequency.Value = 0.77f + ((Banana)HitObject).BananaIndex * 0.006f;
|
Samples.Frequency.Value = 0.77f + ((Banana)HitObject).BananaIndex * 0.006f;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4 getBananaColour()
|
|
||||||
{
|
|
||||||
switch (RNG.Next(0, 3))
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
return new Color4(255, 240, 0, 255);
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
return new Color4(255, 192, 0, 255);
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
return new Color4(214, 221, 28, 255);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,27 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableBananaShower : DrawableCatchHitObject<BananaShower>
|
public class DrawableBananaShower : DrawableCatchHitObject
|
||||||
{
|
{
|
||||||
private readonly Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation;
|
|
||||||
private readonly Container bananaContainer;
|
private readonly Container bananaContainer;
|
||||||
|
|
||||||
public DrawableBananaShower(BananaShower s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation = null)
|
public DrawableBananaShower()
|
||||||
|
: this(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DrawableBananaShower([CanBeNull] BananaShower s)
|
||||||
: base(s)
|
: base(s)
|
||||||
{
|
{
|
||||||
this.createDrawableRepresentation = createDrawableRepresentation;
|
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Origin = Anchor.BottomLeft;
|
Origin = Anchor.BottomLeft;
|
||||||
X = 0;
|
|
||||||
|
|
||||||
AddInternal(bananaContainer = new Container { RelativeSizeAxes = Axes.Both });
|
AddInternal(bananaContainer = new Container { RelativeSizeAxes = Axes.Both });
|
||||||
}
|
}
|
||||||
@ -34,18 +35,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
protected override void ClearNestedHitObjects()
|
protected override void ClearNestedHitObjects()
|
||||||
{
|
{
|
||||||
base.ClearNestedHitObjects();
|
base.ClearNestedHitObjects();
|
||||||
bananaContainer.Clear();
|
bananaContainer.Clear(false);
|
||||||
}
|
|
||||||
|
|
||||||
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)
|
|
||||||
{
|
|
||||||
switch (hitObject)
|
|
||||||
{
|
|
||||||
case Banana banana:
|
|
||||||
return createDrawableRepresentation?.Invoke(banana)?.With(o => ((DrawableCatchHitObject)o).CheckPosition = p => CheckPosition?.Invoke(p) ?? false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.CreateNestedHitObject(hitObject);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,77 +2,42 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||||
{
|
{
|
||||||
public abstract class PalpableDrawableCatchHitObject<TObject> : DrawableCatchHitObject<TObject>
|
|
||||||
where TObject : PalpableCatchHitObject
|
|
||||||
{
|
|
||||||
protected Container ScaleContainer { get; private set; }
|
|
||||||
|
|
||||||
protected PalpableDrawableCatchHitObject(TObject hitObject)
|
|
||||||
: base(hitObject)
|
|
||||||
{
|
|
||||||
Origin = Anchor.Centre;
|
|
||||||
Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2);
|
|
||||||
Masking = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
AddRangeInternal(new Drawable[]
|
|
||||||
{
|
|
||||||
ScaleContainer = new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ScaleContainer.Scale = new Vector2(HitObject.Scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Color4 GetComboColour(IReadOnlyList<Color4> comboColours) =>
|
|
||||||
comboColours[(HitObject.IndexInBeatmap + 1) % comboColours.Count];
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class DrawableCatchHitObject<TObject> : DrawableCatchHitObject
|
|
||||||
where TObject : CatchHitObject
|
|
||||||
{
|
|
||||||
public new TObject HitObject;
|
|
||||||
|
|
||||||
protected DrawableCatchHitObject(TObject hitObject)
|
|
||||||
: base(hitObject)
|
|
||||||
{
|
|
||||||
HitObject = hitObject;
|
|
||||||
Anchor = Anchor.BottomLeft;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class DrawableCatchHitObject : DrawableHitObject<CatchHitObject>
|
public abstract class DrawableCatchHitObject : DrawableHitObject<CatchHitObject>
|
||||||
{
|
{
|
||||||
protected override double InitialLifetimeOffset => HitObject.TimePreempt;
|
public readonly Bindable<float> XBindable = new Bindable<float>();
|
||||||
|
|
||||||
public virtual bool StaysOnPlate => HitObject.CanBePlated;
|
protected override double InitialLifetimeOffset => HitObject.TimePreempt;
|
||||||
|
|
||||||
public float DisplayRadius => DrawSize.X / 2 * Scale.X * HitObject.Scale;
|
public float DisplayRadius => DrawSize.X / 2 * Scale.X * HitObject.Scale;
|
||||||
|
|
||||||
protected override float SamplePlaybackPosition => HitObject.X / CatchPlayfield.WIDTH;
|
protected override float SamplePlaybackPosition => HitObject.X / CatchPlayfield.WIDTH;
|
||||||
|
|
||||||
protected DrawableCatchHitObject(CatchHitObject hitObject)
|
protected DrawableCatchHitObject([CanBeNull] CatchHitObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
X = hitObject.X;
|
Anchor = Anchor.BottomLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnApply()
|
||||||
|
{
|
||||||
|
base.OnApply();
|
||||||
|
|
||||||
|
XBindable.BindTo(HitObject.XBindable);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFree()
|
||||||
|
{
|
||||||
|
base.OnFree();
|
||||||
|
|
||||||
|
XBindable.UnbindFrom(HitObject.XBindable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Func<CatchHitObject, bool> CheckPosition;
|
public Func<CatchHitObject, bool> CheckPosition;
|
||||||
|
@ -1,18 +1,25 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableDroplet : PalpableDrawableCatchHitObject<Droplet>
|
public class DrawableDroplet : DrawablePalpableCatchHitObject
|
||||||
{
|
{
|
||||||
public override bool StaysOnPlate => false;
|
public override bool StaysOnPlate => false;
|
||||||
|
|
||||||
public DrawableDroplet(Droplet h)
|
public DrawableDroplet()
|
||||||
|
: this(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DrawableDroplet([CanBeNull] CatchHitObject h)
|
||||||
: base(h)
|
: base(h)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -20,7 +27,17 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
ScaleContainer.Child = new SkinnableDrawable(new CatchSkinComponent(CatchSkinComponents.Droplet), _ => new DropletPiece());
|
HyperDash.BindValueChanged(_ => updatePiece(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePiece()
|
||||||
|
{
|
||||||
|
ScaleContainer.Child = new SkinnableDrawable(
|
||||||
|
new CatchSkinComponent(CatchSkinComponents.Droplet),
|
||||||
|
_ => new DropletPiece
|
||||||
|
{
|
||||||
|
HyperDash = { BindTarget = HyperDash }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateInitialTransforms()
|
protected override void UpdateInitialTransforms()
|
||||||
|
@ -2,15 +2,27 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableFruit : PalpableDrawableCatchHitObject<Fruit>
|
public class DrawableFruit : DrawablePalpableCatchHitObject
|
||||||
{
|
{
|
||||||
public DrawableFruit(Fruit h)
|
public readonly Bindable<FruitVisualRepresentation> VisualRepresentation = new Bindable<FruitVisualRepresentation>();
|
||||||
|
|
||||||
|
protected virtual FruitVisualRepresentation GetVisualRepresentation(int indexInBeatmap) => (FruitVisualRepresentation)(indexInBeatmap % 4);
|
||||||
|
|
||||||
|
public DrawableFruit()
|
||||||
|
: this(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DrawableFruit([CanBeNull] Fruit h)
|
||||||
: base(h)
|
: base(h)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -18,10 +30,26 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
ScaleContainer.Child = new SkinnableDrawable(
|
|
||||||
new CatchSkinComponent(getComponent(HitObject.VisualRepresentation)), _ => new FruitPiece());
|
|
||||||
|
|
||||||
ScaleContainer.Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
|
ScaleContainer.Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
|
||||||
|
|
||||||
|
IndexInBeatmap.BindValueChanged(change =>
|
||||||
|
{
|
||||||
|
VisualRepresentation.Value = GetVisualRepresentation(change.NewValue);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
VisualRepresentation.BindValueChanged(_ => updatePiece());
|
||||||
|
HyperDash.BindValueChanged(_ => updatePiece(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePiece()
|
||||||
|
{
|
||||||
|
ScaleContainer.Child = new SkinnableDrawable(
|
||||||
|
new CatchSkinComponent(getComponent(VisualRepresentation.Value)),
|
||||||
|
_ => new FruitPiece
|
||||||
|
{
|
||||||
|
VisualRepresentation = { BindTarget = VisualRepresentation },
|
||||||
|
HyperDash = { BindTarget = HyperDash },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private CatchSkinComponents getComponent(FruitVisualRepresentation hitObjectVisualRepresentation)
|
private CatchSkinComponents getComponent(FruitVisualRepresentation hitObjectVisualRepresentation)
|
||||||
@ -48,4 +76,13 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum FruitVisualRepresentation
|
||||||
|
{
|
||||||
|
Pear,
|
||||||
|
Grape,
|
||||||
|
Pineapple,
|
||||||
|
Raspberry,
|
||||||
|
Banana // banananananannaanana
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,33 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableJuiceStream : DrawableCatchHitObject<JuiceStream>
|
public class DrawableJuiceStream : DrawableCatchHitObject
|
||||||
{
|
{
|
||||||
private readonly Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation;
|
|
||||||
private readonly Container dropletContainer;
|
private readonly Container dropletContainer;
|
||||||
|
|
||||||
public override Vector2 OriginPosition => base.OriginPosition - new Vector2(0, CatchHitObject.OBJECT_RADIUS);
|
public DrawableJuiceStream()
|
||||||
|
: this(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public DrawableJuiceStream(JuiceStream s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation = null)
|
public DrawableJuiceStream([CanBeNull] JuiceStream s)
|
||||||
: base(s)
|
: base(s)
|
||||||
{
|
{
|
||||||
this.createDrawableRepresentation = createDrawableRepresentation;
|
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Origin = Anchor.BottomLeft;
|
Origin = Anchor.BottomLeft;
|
||||||
X = 0;
|
|
||||||
|
|
||||||
AddInternal(dropletContainer = new Container { RelativeSizeAxes = Axes.Both, });
|
AddInternal(dropletContainer = new Container { RelativeSizeAxes = Axes.Both, });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AddNestedHitObject(DrawableHitObject hitObject)
|
protected override void AddNestedHitObject(DrawableHitObject hitObject)
|
||||||
{
|
{
|
||||||
hitObject.Origin = Anchor.BottomCentre;
|
|
||||||
|
|
||||||
base.AddNestedHitObject(hitObject);
|
base.AddNestedHitObject(hitObject);
|
||||||
dropletContainer.Add(hitObject);
|
dropletContainer.Add(hitObject);
|
||||||
}
|
}
|
||||||
@ -39,19 +35,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
protected override void ClearNestedHitObjects()
|
protected override void ClearNestedHitObjects()
|
||||||
{
|
{
|
||||||
base.ClearNestedHitObjects();
|
base.ClearNestedHitObjects();
|
||||||
dropletContainer.Clear();
|
dropletContainer.Clear(false);
|
||||||
}
|
|
||||||
|
|
||||||
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)
|
|
||||||
{
|
|
||||||
switch (hitObject)
|
|
||||||
{
|
|
||||||
case CatchHitObject catchObject:
|
|
||||||
return createDrawableRepresentation?.Invoke(catchObject)?.With(o =>
|
|
||||||
((DrawableCatchHitObject)o).CheckPosition = p => CheckPosition?.Invoke(p) ?? false);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ArgumentException($"{nameof(hitObject)} must be of type {nameof(CatchHitObject)}.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||||
|
{
|
||||||
|
public abstract class DrawablePalpableCatchHitObject : DrawableCatchHitObject
|
||||||
|
{
|
||||||
|
public new PalpableCatchHitObject HitObject => (PalpableCatchHitObject)base.HitObject;
|
||||||
|
|
||||||
|
public readonly Bindable<bool> HyperDash = new Bindable<bool>();
|
||||||
|
|
||||||
|
public readonly Bindable<float> ScaleBindable = new Bindable<float>(1);
|
||||||
|
|
||||||
|
public readonly Bindable<int> IndexInBeatmap = new Bindable<int>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The multiplicative factor applied to <see cref="ScaleContainer"/> scale relative to <see cref="HitObject"/> scale.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual float ScaleFactor => 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this hit object should stay on the catcher plate when the object is caught by the catcher.
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool StaysOnPlate => true;
|
||||||
|
|
||||||
|
protected readonly Container ScaleContainer;
|
||||||
|
|
||||||
|
protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h)
|
||||||
|
: base(h)
|
||||||
|
{
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2);
|
||||||
|
|
||||||
|
AddInternal(ScaleContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
XBindable.BindValueChanged(x =>
|
||||||
|
{
|
||||||
|
if (!IsOnPlate) X = x.NewValue;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
ScaleBindable.BindValueChanged(scale =>
|
||||||
|
{
|
||||||
|
ScaleContainer.Scale = new Vector2(scale.NewValue * ScaleFactor);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
IndexInBeatmap.BindValueChanged(_ => UpdateComboColour());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnApply()
|
||||||
|
{
|
||||||
|
base.OnApply();
|
||||||
|
|
||||||
|
HyperDash.BindTo(HitObject.HyperDashBindable);
|
||||||
|
ScaleBindable.BindTo(HitObject.ScaleBindable);
|
||||||
|
IndexInBeatmap.BindTo(HitObject.IndexInBeatmapBindable);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFree()
|
||||||
|
{
|
||||||
|
HyperDash.UnbindFrom(HitObject.HyperDashBindable);
|
||||||
|
ScaleBindable.UnbindFrom(HitObject.ScaleBindable);
|
||||||
|
IndexInBeatmap.UnbindFrom(HitObject.IndexInBeatmapBindable);
|
||||||
|
|
||||||
|
base.OnFree();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +1,22 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableTinyDroplet : DrawableDroplet
|
public class DrawableTinyDroplet : DrawableDroplet
|
||||||
{
|
{
|
||||||
public DrawableTinyDroplet(TinyDroplet h)
|
protected override float ScaleFactor => base.ScaleFactor / 2;
|
||||||
: base(h)
|
|
||||||
|
public DrawableTinyDroplet()
|
||||||
|
: this(null)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
public DrawableTinyDroplet([CanBeNull] TinyDroplet h)
|
||||||
private void load()
|
: base(h)
|
||||||
{
|
{
|
||||||
ScaleContainer.Scale /= 2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
|
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|
||||||
{
|
|
||||||
public class DropletPiece : CompositeDrawable
|
|
||||||
{
|
|
||||||
public DropletPiece()
|
|
||||||
{
|
|
||||||
Size = new Vector2(CatchHitObject.OBJECT_RADIUS / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(DrawableHitObject drawableObject)
|
|
||||||
{
|
|
||||||
DrawableCatchHitObject drawableCatchObject = (DrawableCatchHitObject)drawableObject;
|
|
||||||
var hitObject = drawableCatchObject.HitObject;
|
|
||||||
|
|
||||||
InternalChild = new Pulp
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
AccentColour = { BindTarget = drawableObject.AccentColour }
|
|
||||||
};
|
|
||||||
|
|
||||||
if (hitObject.HyperDash)
|
|
||||||
{
|
|
||||||
AddInternal(new Container
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Size = new Vector2(2f),
|
|
||||||
Depth = 1,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Circle
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
BorderColour = Catcher.DEFAULT_HYPER_DASH_COLOUR,
|
|
||||||
BorderThickness = 6,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
AlwaysPresent = true,
|
|
||||||
Alpha = 0.3f,
|
|
||||||
Blending = BlendingParameters.Additive,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,111 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|
||||||
{
|
|
||||||
internal class FruitPiece : CompositeDrawable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Because we're adding a border around the fruit, we need to scale down some.
|
|
||||||
/// </summary>
|
|
||||||
public const float RADIUS_ADJUST = 1.1f;
|
|
||||||
|
|
||||||
private Circle border;
|
|
||||||
private CatchHitObject hitObject;
|
|
||||||
|
|
||||||
public FruitPiece()
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(DrawableHitObject drawableObject)
|
|
||||||
{
|
|
||||||
DrawableCatchHitObject drawableCatchObject = (DrawableCatchHitObject)drawableObject;
|
|
||||||
hitObject = drawableCatchObject.HitObject;
|
|
||||||
|
|
||||||
AddRangeInternal(new[]
|
|
||||||
{
|
|
||||||
getFruitFor(drawableCatchObject.HitObject.VisualRepresentation),
|
|
||||||
border = new Circle
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
BorderColour = Color4.White,
|
|
||||||
BorderThickness = 6f * RADIUS_ADJUST,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
AlwaysPresent = true,
|
|
||||||
Alpha = 0,
|
|
||||||
RelativeSizeAxes = Axes.Both
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (hitObject.HyperDash)
|
|
||||||
{
|
|
||||||
AddInternal(new Circle
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
BorderColour = Catcher.DEFAULT_HYPER_DASH_COLOUR,
|
|
||||||
BorderThickness = 12f * RADIUS_ADJUST,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
AlwaysPresent = true,
|
|
||||||
Alpha = 0.3f,
|
|
||||||
Blending = BlendingParameters.Additive,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
border.Alpha = (float)Math.Clamp((hitObject.StartTime - Time.Current) / 500, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Drawable getFruitFor(FruitVisualRepresentation representation)
|
|
||||||
{
|
|
||||||
switch (representation)
|
|
||||||
{
|
|
||||||
case FruitVisualRepresentation.Pear:
|
|
||||||
return new PearPiece();
|
|
||||||
|
|
||||||
case FruitVisualRepresentation.Grape:
|
|
||||||
return new GrapePiece();
|
|
||||||
|
|
||||||
case FruitVisualRepresentation.Pineapple:
|
|
||||||
return new PineapplePiece();
|
|
||||||
|
|
||||||
case FruitVisualRepresentation.Banana:
|
|
||||||
return new BananaPiece();
|
|
||||||
|
|
||||||
case FruitVisualRepresentation.Raspberry:
|
|
||||||
return new RaspberryPiece();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,10 +2,9 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
|
||||||
{
|
{
|
||||||
public class BananaPiece : PulpFormation
|
public class BananaPiece : PulpFormation
|
||||||
{
|
{
|
@ -0,0 +1,31 @@
|
|||||||
|
// 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.Shapes;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
|
||||||
|
{
|
||||||
|
public class BorderPiece : Circle
|
||||||
|
{
|
||||||
|
public BorderPiece()
|
||||||
|
{
|
||||||
|
Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2);
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
BorderColour = Color4.White;
|
||||||
|
BorderThickness = 6f * FruitPiece.RADIUS_ADJUST;
|
||||||
|
|
||||||
|
// Border is drawn only when there is a child drawable.
|
||||||
|
Child = new Box
|
||||||
|
{
|
||||||
|
AlwaysPresent = true,
|
||||||
|
Alpha = 0,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
|||||||
|
// 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.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
|
||||||
|
{
|
||||||
|
public class DropletPiece : CompositeDrawable
|
||||||
|
{
|
||||||
|
public readonly Bindable<bool> HyperDash = new Bindable<bool>();
|
||||||
|
|
||||||
|
public DropletPiece()
|
||||||
|
{
|
||||||
|
Size = new Vector2(CatchHitObject.OBJECT_RADIUS / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(DrawableHitObject drawableObject)
|
||||||
|
{
|
||||||
|
InternalChild = new Pulp
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
AccentColour = { BindTarget = drawableObject.AccentColour }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (HyperDash.Value)
|
||||||
|
{
|
||||||
|
AddInternal(new HyperDropletBorderPiece());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
// 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 JetBrains.Annotations;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
|
||||||
|
{
|
||||||
|
internal class FruitPiece : CompositeDrawable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Because we're adding a border around the fruit, we need to scale down some.
|
||||||
|
/// </summary>
|
||||||
|
public const float RADIUS_ADJUST = 1.1f;
|
||||||
|
|
||||||
|
public readonly Bindable<FruitVisualRepresentation> VisualRepresentation = new Bindable<FruitVisualRepresentation>();
|
||||||
|
public readonly Bindable<bool> HyperDash = new Bindable<bool>();
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
private DrawableCatchHitObject drawableHitObject;
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
private BorderPiece borderPiece;
|
||||||
|
|
||||||
|
public FruitPiece()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(permitNulls: true)]
|
||||||
|
private void load([CanBeNull] DrawableHitObject drawable)
|
||||||
|
{
|
||||||
|
drawableHitObject = (DrawableCatchHitObject)drawable;
|
||||||
|
|
||||||
|
AddInternal(getFruitFor(VisualRepresentation.Value));
|
||||||
|
|
||||||
|
// if it is not part of a DHO, the border is always invisible.
|
||||||
|
if (drawableHitObject != null)
|
||||||
|
AddInternal(borderPiece = new BorderPiece());
|
||||||
|
|
||||||
|
if (HyperDash.Value)
|
||||||
|
AddInternal(new HyperBorderPiece());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
if (borderPiece != null && drawableHitObject?.HitObject != null)
|
||||||
|
borderPiece.Alpha = (float)Math.Clamp((drawableHitObject.HitObject.StartTime - Time.Current) / 500, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable getFruitFor(FruitVisualRepresentation representation)
|
||||||
|
{
|
||||||
|
switch (representation)
|
||||||
|
{
|
||||||
|
case FruitVisualRepresentation.Pear:
|
||||||
|
return new PearPiece();
|
||||||
|
|
||||||
|
case FruitVisualRepresentation.Grape:
|
||||||
|
return new GrapePiece();
|
||||||
|
|
||||||
|
case FruitVisualRepresentation.Pineapple:
|
||||||
|
return new PineapplePiece();
|
||||||
|
|
||||||
|
case FruitVisualRepresentation.Banana:
|
||||||
|
return new BananaPiece();
|
||||||
|
|
||||||
|
case FruitVisualRepresentation.Raspberry:
|
||||||
|
return new RaspberryPiece();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,9 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
|
||||||
{
|
{
|
||||||
public class GrapePiece : PulpFormation
|
public class GrapePiece : PulpFormation
|
||||||
{
|
{
|
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
|
||||||
|
{
|
||||||
|
public class HyperBorderPiece : BorderPiece
|
||||||
|
{
|
||||||
|
public HyperBorderPiece()
|
||||||
|
{
|
||||||
|
BorderColour = Catcher.DEFAULT_HYPER_DASH_COLOUR;
|
||||||
|
BorderThickness = 12f * FruitPiece.RADIUS_ADJUST;
|
||||||
|
|
||||||
|
Child.Alpha = 0.3f;
|
||||||
|
Child.Blending = BlendingParameters.Additive;
|
||||||
|
Child.Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
|
||||||
|
{
|
||||||
|
public class HyperDropletBorderPiece : HyperBorderPiece
|
||||||
|
{
|
||||||
|
public HyperDropletBorderPiece()
|
||||||
|
{
|
||||||
|
Size /= 2;
|
||||||
|
BorderThickness = 6f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,9 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
|
||||||
{
|
{
|
||||||
public class PearPiece : PulpFormation
|
public class PearPiece : PulpFormation
|
||||||
{
|
{
|
@ -2,10 +2,9 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
|
||||||
{
|
{
|
||||||
public class PineapplePiece : PulpFormation
|
public class PineapplePiece : PulpFormation
|
||||||
{
|
{
|
@ -10,7 +10,7 @@ using osu.Game.Rulesets.Objects.Drawables;
|
|||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
|
||||||
{
|
{
|
||||||
public abstract class PulpFormation : CompositeDrawable
|
public abstract class PulpFormation : CompositeDrawable
|
||||||
{
|
{
|
@ -2,10 +2,9 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
|
||||||
{
|
{
|
||||||
public class RaspberryPiece : PulpFormation
|
public class RaspberryPiece : PulpFormation
|
||||||
{
|
{
|
@ -50,12 +50,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
{
|
{
|
||||||
base.CreateNestedHitObjects(cancellationToken);
|
base.CreateNestedHitObjects(cancellationToken);
|
||||||
|
|
||||||
var dropletSamples = Samples.Select(s => new HitSampleInfo
|
var dropletSamples = Samples.Select(s => s.With(@"slidertick")).ToList();
|
||||||
{
|
|
||||||
Bank = s.Bank,
|
|
||||||
Name = @"slidertick",
|
|
||||||
Volume = s.Volume
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
int nodeIndex = 0;
|
int nodeIndex = 0;
|
||||||
SliderEventDescriptor? lastEvent = null;
|
SliderEventDescriptor? lastEvent = null;
|
||||||
|
48
osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs
Normal file
48
osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a single object that can be caught by the catcher.
|
||||||
|
/// This includes normal fruits, droplets, and bananas but excludes objects that act only as a container of nested hit objects.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class PalpableCatchHitObject : CatchHitObject, IHasComboInformation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Difference between the distance to the next object
|
||||||
|
/// and the distance that would have triggered a hyper dash.
|
||||||
|
/// A value close to 0 indicates a difficult jump (for difficulty calculation).
|
||||||
|
/// </summary>
|
||||||
|
public float DistanceToHyperDash { get; set; }
|
||||||
|
|
||||||
|
public readonly Bindable<bool> HyperDashBindable = new Bindable<bool>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this fruit can initiate a hyperdash.
|
||||||
|
/// </summary>
|
||||||
|
public bool HyperDash => HyperDashBindable.Value;
|
||||||
|
|
||||||
|
private CatchHitObject hyperDashTarget;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The target fruit if we are to initiate a hyperdash.
|
||||||
|
/// </summary>
|
||||||
|
public CatchHitObject HyperDashTarget
|
||||||
|
{
|
||||||
|
get => hyperDashTarget;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
hyperDashTarget = value;
|
||||||
|
HyperDashBindable.Value = value != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Color4 IHasComboInformation.GetComboColour(IReadOnlyList<Color4> comboColours) => comboColours[(IndexInBeatmap + 1) % comboColours.Count];
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
float lastPosition = CatchPlayfield.CENTER_X;
|
float lastPosition = CatchPlayfield.CENTER_X;
|
||||||
double lastTime = 0;
|
double lastTime = 0;
|
||||||
|
|
||||||
void moveToNext(CatchHitObject h)
|
void moveToNext(PalpableCatchHitObject h)
|
||||||
{
|
{
|
||||||
float positionChange = Math.Abs(lastPosition - h.X);
|
float positionChange = Math.Abs(lastPosition - h.X);
|
||||||
double timeAvailable = h.StartTime - lastTime;
|
double timeAvailable = h.StartTime - lastTime;
|
||||||
@ -101,23 +101,16 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
|
|
||||||
foreach (var obj in Beatmap.HitObjects)
|
foreach (var obj in Beatmap.HitObjects)
|
||||||
{
|
{
|
||||||
switch (obj)
|
if (obj is PalpableCatchHitObject palpableObject)
|
||||||
{
|
{
|
||||||
case Fruit _:
|
moveToNext(palpableObject);
|
||||||
moveToNext(obj);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var nestedObj in obj.NestedHitObjects.Cast<CatchHitObject>())
|
foreach (var nestedObj in obj.NestedHitObjects.Cast<CatchHitObject>())
|
||||||
{
|
{
|
||||||
switch (nestedObj)
|
if (nestedObj is PalpableCatchHitObject palpableNestedObject)
|
||||||
{
|
{
|
||||||
case Banana _:
|
moveToNext(palpableNestedObject);
|
||||||
case TinyDroplet _:
|
|
||||||
case Droplet _:
|
|
||||||
case Fruit _:
|
|
||||||
moveToNext(nestedObj);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,8 @@ namespace osu.Game.Rulesets.Catch.Skinning
|
|||||||
{
|
{
|
||||||
private readonly string lookupName;
|
private readonly string lookupName;
|
||||||
|
|
||||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
||||||
|
private readonly Bindable<bool> hyperDash = new Bindable<bool>();
|
||||||
private Sprite colouredSprite;
|
private Sprite colouredSprite;
|
||||||
|
|
||||||
public LegacyFruitPiece(string lookupName)
|
public LegacyFruitPiece(string lookupName)
|
||||||
@ -31,9 +32,10 @@ namespace osu.Game.Rulesets.Catch.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(DrawableHitObject drawableObject, ISkinSource skin)
|
private void load(DrawableHitObject drawableObject, ISkinSource skin)
|
||||||
{
|
{
|
||||||
DrawableCatchHitObject drawableCatchObject = (DrawableCatchHitObject)drawableObject;
|
var drawableCatchObject = (DrawablePalpableCatchHitObject)drawableObject;
|
||||||
|
|
||||||
accentColour.BindTo(drawableCatchObject.AccentColour);
|
accentColour.BindTo(drawableCatchObject.AccentColour);
|
||||||
|
hyperDash.BindTo(drawableCatchObject.HyperDash);
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -51,9 +53,9 @@ namespace osu.Game.Rulesets.Catch.Skinning
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (drawableCatchObject.HitObject.HyperDash)
|
if (hyperDash.Value)
|
||||||
{
|
{
|
||||||
var hyperDash = new Sprite
|
var hyperDashOverlay = new Sprite
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -67,7 +69,7 @@ namespace osu.Game.Rulesets.Catch.Skinning
|
|||||||
Catcher.DEFAULT_HYPER_DASH_COLOUR,
|
Catcher.DEFAULT_HYPER_DASH_COLOUR,
|
||||||
};
|
};
|
||||||
|
|
||||||
AddInternal(hyperDash);
|
AddInternal(hyperDashOverlay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -42,7 +43,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
CatcherArea = new CatcherArea(difficulty)
|
CatcherArea = new CatcherArea(difficulty)
|
||||||
{
|
{
|
||||||
CreateDrawableRepresentation = createDrawableRepresentation,
|
|
||||||
ExplodingFruitTarget = explodingFruitContainer,
|
ExplodingFruitTarget = explodingFruitContainer,
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.TopLeft,
|
Origin = Anchor.TopLeft,
|
||||||
@ -57,19 +57,33 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CheckIfWeCanCatch(CatchHitObject obj) => CatcherArea.AttemptCatch(obj);
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
public override void Add(DrawableHitObject h)
|
|
||||||
{
|
{
|
||||||
h.OnNewResult += onNewResult;
|
RegisterPool<Droplet, DrawableDroplet>(50);
|
||||||
h.OnRevertResult += onRevertResult;
|
RegisterPool<TinyDroplet, DrawableTinyDroplet>(50);
|
||||||
|
RegisterPool<Fruit, DrawableFruit>(100);
|
||||||
base.Add(h);
|
RegisterPool<Banana, DrawableBanana>(100);
|
||||||
|
RegisterPool<JuiceStream, DrawableJuiceStream>(10);
|
||||||
var fruit = (DrawableCatchHitObject)h;
|
RegisterPool<BananaShower, DrawableBananaShower>(2);
|
||||||
fruit.CheckPosition = CheckIfWeCanCatch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
// these subscriptions need to be done post constructor to ensure externally bound components have a chance to populate required fields (ScoreProcessor / ComboAtJudgement in this case).
|
||||||
|
NewResult += onNewResult;
|
||||||
|
RevertResult += onRevertResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnNewDrawableHitObject(DrawableHitObject d)
|
||||||
|
{
|
||||||
|
((DrawableCatchHitObject)d).CheckPosition = checkIfWeCanCatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool checkIfWeCanCatch(CatchHitObject obj) => CatcherArea.AttemptCatch(obj);
|
||||||
|
|
||||||
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||||
=> CatcherArea.OnNewResult((DrawableCatchHitObject)judgedObject, result);
|
=> CatcherArea.OnNewResult((DrawableCatchHitObject)judgedObject, result);
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Animations;
|
using osu.Framework.Graphics.Animations;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Pooling;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -107,6 +108,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
private float hyperDashTargetPosition;
|
private float hyperDashTargetPosition;
|
||||||
private Bindable<bool> hitLighting;
|
private Bindable<bool> hitLighting;
|
||||||
|
|
||||||
|
private DrawablePool<HitExplosion> hitExplosionPool;
|
||||||
|
private Container<HitExplosion> hitExplosionContainer;
|
||||||
|
|
||||||
public Catcher([NotNull] Container trailsTarget, BeatmapDifficulty difficulty = null)
|
public Catcher([NotNull] Container trailsTarget, BeatmapDifficulty difficulty = null)
|
||||||
{
|
{
|
||||||
this.trailsTarget = trailsTarget;
|
this.trailsTarget = trailsTarget;
|
||||||
@ -127,6 +131,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
|
hitExplosionPool = new DrawablePool<HitExplosion>(10),
|
||||||
caughtFruitContainer,
|
caughtFruitContainer,
|
||||||
catcherIdle = new CatcherSprite(CatcherAnimationState.Idle)
|
catcherIdle = new CatcherSprite(CatcherAnimationState.Idle)
|
||||||
{
|
{
|
||||||
@ -142,7 +147,12 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
}
|
},
|
||||||
|
hitExplosionContainer = new Container<HitExplosion>
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
trails = new CatcherTrailDisplay(this);
|
trails = new CatcherTrailDisplay(this);
|
||||||
@ -209,22 +219,22 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
if (hitLighting.Value)
|
if (hitLighting.Value)
|
||||||
{
|
{
|
||||||
AddInternal(new HitExplosion(fruit)
|
HitExplosion hitExplosion = hitExplosionPool.Get();
|
||||||
{
|
hitExplosion.X = fruit.X;
|
||||||
X = fruit.X,
|
hitExplosion.Scale = new Vector2(fruit.HitObject.Scale);
|
||||||
Scale = new Vector2(fruit.HitObject.Scale)
|
hitExplosion.ObjectColour = fruit.AccentColour.Value;
|
||||||
});
|
hitExplosionContainer.Add(hitExplosion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Let the catcher attempt to catch a fruit.
|
/// Let the catcher attempt to catch a fruit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fruit">The fruit to catch.</param>
|
/// <param name="hitObject">The fruit to catch.</param>
|
||||||
/// <returns>Whether the catch is possible.</returns>
|
/// <returns>Whether the catch is possible.</returns>
|
||||||
public bool AttemptCatch(CatchHitObject fruit)
|
public bool AttemptCatch(CatchHitObject hitObject)
|
||||||
{
|
{
|
||||||
if (!fruit.CanBePlated)
|
if (!(hitObject is PalpableCatchHitObject fruit))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var halfCatchWidth = catchWidth * 0.5f;
|
var halfCatchWidth = catchWidth * 0.5f;
|
||||||
|
@ -10,7 +10,6 @@ using osu.Game.Rulesets.Catch.Objects;
|
|||||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Catch.Replays;
|
using osu.Game.Rulesets.Catch.Replays;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -21,8 +20,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
{
|
{
|
||||||
public const float CATCHER_SIZE = 106.75f;
|
public const float CATCHER_SIZE = 106.75f;
|
||||||
|
|
||||||
public Func<CatchHitObject, DrawableHitObject<CatchHitObject>> CreateDrawableRepresentation;
|
|
||||||
|
|
||||||
public readonly Catcher MovableCatcher;
|
public readonly Catcher MovableCatcher;
|
||||||
private readonly CatchComboDisplay comboDisplay;
|
private readonly CatchComboDisplay comboDisplay;
|
||||||
|
|
||||||
@ -51,7 +48,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnNewResult(DrawableCatchHitObject fruit, JudgementResult result)
|
public void OnNewResult(DrawableCatchHitObject hitObject, JudgementResult result)
|
||||||
{
|
{
|
||||||
if (!result.Type.IsScorable())
|
if (!result.Type.IsScorable())
|
||||||
return;
|
return;
|
||||||
@ -69,15 +66,15 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
lastPlateableFruit.OnLoadComplete += _ => action();
|
lastPlateableFruit.OnLoadComplete += _ => action();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.IsHit && fruit.HitObject.CanBePlated)
|
if (result.IsHit && hitObject is DrawablePalpableCatchHitObject fruit)
|
||||||
{
|
{
|
||||||
// create a new (cloned) fruit to stay on the plate. the original is faded out immediately.
|
// create a new (cloned) fruit to stay on the plate. the original is faded out immediately.
|
||||||
var caughtFruit = (DrawableCatchHitObject)CreateDrawableRepresentation?.Invoke(fruit.HitObject);
|
var caughtFruit = createCaughtFruit(fruit);
|
||||||
|
|
||||||
if (caughtFruit == null) return;
|
if (caughtFruit == null) return;
|
||||||
|
|
||||||
caughtFruit.RelativePositionAxes = Axes.None;
|
caughtFruit.RelativePositionAxes = Axes.None;
|
||||||
caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0);
|
caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(hitObject.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0);
|
||||||
caughtFruit.IsOnPlate = true;
|
caughtFruit.IsOnPlate = true;
|
||||||
|
|
||||||
caughtFruit.Anchor = Anchor.TopCentre;
|
caughtFruit.Anchor = Anchor.TopCentre;
|
||||||
@ -93,7 +90,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
runAfterLoaded(() => MovableCatcher.Explode(caughtFruit));
|
runAfterLoaded(() => MovableCatcher.Explode(caughtFruit));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fruit.HitObject.LastInCombo)
|
if (hitObject.HitObject.LastInCombo)
|
||||||
{
|
{
|
||||||
if (result.Judgement is CatchJudgement catchJudgement && catchJudgement.ShouldExplodeFor(result))
|
if (result.Judgement is CatchJudgement catchJudgement && catchJudgement.ShouldExplodeFor(result))
|
||||||
runAfterLoaded(() => MovableCatcher.Explode());
|
runAfterLoaded(() => MovableCatcher.Explode());
|
||||||
@ -101,7 +98,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
MovableCatcher.Drop();
|
MovableCatcher.Drop();
|
||||||
}
|
}
|
||||||
|
|
||||||
comboDisplay.OnNewResult(fruit, result);
|
comboDisplay.OnNewResult(hitObject, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnRevertResult(DrawableCatchHitObject fruit, JudgementResult result)
|
public void OnRevertResult(DrawableCatchHitObject fruit, JudgementResult result)
|
||||||
@ -127,5 +124,26 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
comboDisplay.X = MovableCatcher.X;
|
comboDisplay.X = MovableCatcher.X;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DrawableCatchHitObject createCaughtFruit(DrawablePalpableCatchHitObject hitObject)
|
||||||
|
{
|
||||||
|
switch (hitObject.HitObject)
|
||||||
|
{
|
||||||
|
case Banana banana:
|
||||||
|
return new DrawableBanana(banana);
|
||||||
|
|
||||||
|
case Fruit fruit:
|
||||||
|
return new DrawableFruit(fruit);
|
||||||
|
|
||||||
|
case TinyDroplet tiny:
|
||||||
|
return new DrawableTinyDroplet(tiny);
|
||||||
|
|
||||||
|
case Droplet droplet:
|
||||||
|
return new DrawableDroplet(droplet);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ using osu.Game.Configuration;
|
|||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Catch.Replays;
|
using osu.Game.Rulesets.Catch.Replays;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -40,30 +39,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
protected override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
|
protected override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
|
||||||
|
|
||||||
public override DrawableHitObject<CatchHitObject> CreateDrawableRepresentation(CatchHitObject h)
|
public override DrawableHitObject<CatchHitObject> CreateDrawableRepresentation(CatchHitObject h) => null;
|
||||||
{
|
|
||||||
switch (h)
|
|
||||||
{
|
|
||||||
case Banana banana:
|
|
||||||
return new DrawableBanana(banana);
|
|
||||||
|
|
||||||
case Fruit fruit:
|
|
||||||
return new DrawableFruit(fruit);
|
|
||||||
|
|
||||||
case JuiceStream stream:
|
|
||||||
return new DrawableJuiceStream(stream, CreateDrawableRepresentation);
|
|
||||||
|
|
||||||
case BananaShower shower:
|
|
||||||
return new DrawableBananaShower(shower, CreateDrawableRepresentation);
|
|
||||||
|
|
||||||
case TinyDroplet tiny:
|
|
||||||
return new DrawableTinyDroplet(tiny);
|
|
||||||
|
|
||||||
case Droplet droplet:
|
|
||||||
return new DrawableDroplet(droplet);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,35 +5,43 @@ using osu.Framework.Extensions.Color4Extensions;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
|
using osu.Framework.Graphics.Pooling;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.UI
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
{
|
{
|
||||||
public class HitExplosion : CompositeDrawable
|
public class HitExplosion : PoolableDrawable
|
||||||
{
|
{
|
||||||
private readonly CircularContainer largeFaint;
|
private Color4 objectColour;
|
||||||
|
|
||||||
public HitExplosion(DrawableCatchHitObject fruit)
|
public Color4 ObjectColour
|
||||||
|
{
|
||||||
|
get => objectColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (objectColour == value) return;
|
||||||
|
|
||||||
|
objectColour = value;
|
||||||
|
onColourChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly CircularContainer largeFaint;
|
||||||
|
private readonly CircularContainer smallFaint;
|
||||||
|
private readonly CircularContainer directionalGlow1;
|
||||||
|
private readonly CircularContainer directionalGlow2;
|
||||||
|
|
||||||
|
public HitExplosion()
|
||||||
{
|
{
|
||||||
Size = new Vector2(20);
|
Size = new Vector2(20);
|
||||||
Anchor = Anchor.TopCentre;
|
Anchor = Anchor.TopCentre;
|
||||||
Origin = Anchor.BottomCentre;
|
Origin = Anchor.BottomCentre;
|
||||||
|
|
||||||
Color4 objectColour = fruit.AccentColour.Value;
|
|
||||||
|
|
||||||
// scale roughly in-line with visual appearance of notes
|
// scale roughly in-line with visual appearance of notes
|
||||||
|
|
||||||
const float angle_variangle = 15; // should be less than 45
|
|
||||||
|
|
||||||
const float roundness = 100;
|
|
||||||
|
|
||||||
const float initial_height = 10;
|
const float initial_height = 10;
|
||||||
|
|
||||||
var colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1);
|
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
largeFaint = new CircularContainer
|
largeFaint = new CircularContainer
|
||||||
@ -42,33 +50,17 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
// we want our size to be very small so the glow dominates it.
|
|
||||||
Size = new Vector2(0.8f),
|
|
||||||
Blending = BlendingParameters.Additive,
|
Blending = BlendingParameters.Additive,
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Type = EdgeEffectType.Glow,
|
|
||||||
Colour = Interpolation.ValueAt(0.1f, objectColour, Color4.White, 0, 1).Opacity(0.3f),
|
|
||||||
Roundness = 160,
|
|
||||||
Radius = 200,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
new CircularContainer
|
smallFaint = new CircularContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
Blending = BlendingParameters.Additive,
|
Blending = BlendingParameters.Additive,
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Type = EdgeEffectType.Glow,
|
|
||||||
Colour = Interpolation.ValueAt(0.6f, objectColour, Color4.White, 0, 1),
|
|
||||||
Roundness = 20,
|
|
||||||
Radius = 50,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
new CircularContainer
|
directionalGlow1 = new CircularContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -76,16 +68,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
Masking = true,
|
Masking = true,
|
||||||
Size = new Vector2(0.01f, initial_height),
|
Size = new Vector2(0.01f, initial_height),
|
||||||
Blending = BlendingParameters.Additive,
|
Blending = BlendingParameters.Additive,
|
||||||
Rotation = RNG.NextSingle(-angle_variangle, angle_variangle),
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Type = EdgeEffectType.Glow,
|
|
||||||
Colour = colour,
|
|
||||||
Roundness = roundness,
|
|
||||||
Radius = 40,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
new CircularContainer
|
directionalGlow2 = new CircularContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -93,30 +77,57 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
Masking = true,
|
Masking = true,
|
||||||
Size = new Vector2(0.01f, initial_height),
|
Size = new Vector2(0.01f, initial_height),
|
||||||
Blending = BlendingParameters.Additive,
|
Blending = BlendingParameters.Additive,
|
||||||
Rotation = RNG.NextSingle(-angle_variangle, angle_variangle),
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Type = EdgeEffectType.Glow,
|
|
||||||
Colour = colour,
|
|
||||||
Roundness = roundness,
|
|
||||||
Radius = 40,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void PrepareForUse()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.PrepareForUse();
|
||||||
|
|
||||||
const double duration = 400;
|
const double duration = 400;
|
||||||
|
|
||||||
|
// we want our size to be very small so the glow dominates it.
|
||||||
|
largeFaint.Size = new Vector2(0.8f);
|
||||||
largeFaint
|
largeFaint
|
||||||
.ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint)
|
.ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint)
|
||||||
.FadeOut(duration * 2);
|
.FadeOut(duration * 2);
|
||||||
|
|
||||||
|
const float angle_variangle = 15; // should be less than 45
|
||||||
|
directionalGlow1.Rotation = RNG.NextSingle(-angle_variangle, angle_variangle);
|
||||||
|
directionalGlow2.Rotation = RNG.NextSingle(-angle_variangle, angle_variangle);
|
||||||
|
|
||||||
this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out);
|
this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out);
|
||||||
Expire(true);
|
Expire(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onColourChanged()
|
||||||
|
{
|
||||||
|
const float roundness = 100;
|
||||||
|
|
||||||
|
largeFaint.EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Glow,
|
||||||
|
Colour = Interpolation.ValueAt(0.1f, objectColour, Color4.White, 0, 1).Opacity(0.3f),
|
||||||
|
Roundness = 160,
|
||||||
|
Radius = 200,
|
||||||
|
};
|
||||||
|
|
||||||
|
smallFaint.EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Glow,
|
||||||
|
Colour = Interpolation.ValueAt(0.6f, objectColour, Color4.White, 0, 1),
|
||||||
|
Roundness = 20,
|
||||||
|
Radius = 50,
|
||||||
|
};
|
||||||
|
|
||||||
|
directionalGlow1.EdgeEffect = directionalGlow2.EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Glow,
|
||||||
|
Colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1),
|
||||||
|
Roundness = roundness,
|
||||||
|
Radius = 40,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,6 +96,11 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
|||||||
throw new System.NotImplementedException();
|
throw new System.NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public override float GetBeatSnapDistanceAt(double referenceTime)
|
public override float GetBeatSnapDistanceAt(double referenceTime)
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException();
|
throw new System.NotImplementedException();
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -78,9 +78,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
|
|
||||||
private double originalStartTime;
|
private double originalStartTime;
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
base.UpdatePosition(result);
|
base.UpdateTimeAndPosition(result);
|
||||||
|
|
||||||
if (PlacementActive)
|
if (PlacementActive)
|
||||||
{
|
{
|
||||||
|
@ -48,9 +48,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
base.UpdatePosition(result);
|
base.UpdateTimeAndPosition(result);
|
||||||
|
|
||||||
if (!PlacementActive)
|
if (!PlacementActive)
|
||||||
Column = result.Playfield as Column;
|
Column = result.Playfield as Column;
|
||||||
|
@ -22,9 +22,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
InternalChild = piece = new EditNotePiece { Origin = Anchor.Centre };
|
InternalChild = piece = new EditNotePiece { Origin = Anchor.Centre };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
base.UpdatePosition(result);
|
base.UpdateTimeAndPosition(result);
|
||||||
|
|
||||||
if (result.Playfield != null)
|
if (result.Playfield != null)
|
||||||
{
|
{
|
||||||
|
@ -5,7 +5,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModEasy : ModEasy
|
public class ManiaModEasy : ModEasyWithExtraLives
|
||||||
{
|
{
|
||||||
public override string Description => @"More forgiving HP drain, less accuracy required, and three lives!";
|
public override string Description => @"More forgiving HP drain, less accuracy required, and three lives!";
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
|
|
||||||
public void UpdateResult() => base.UpdateResult(true);
|
public void UpdateResult() => base.UpdateResult(true);
|
||||||
|
|
||||||
|
protected override double MaximumJudgementOffset => base.MaximumJudgementOffset * release_window_lenience;
|
||||||
|
|
||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
Debug.Assert(HitObject.HitWindows != null);
|
Debug.Assert(HitObject.HitWindows != null);
|
||||||
|
@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
DrawableManiaHitObject maniaObject = (DrawableManiaHitObject)hitObject;
|
DrawableManiaHitObject maniaObject = (DrawableManiaHitObject)hitObject;
|
||||||
maniaObject.CheckHittable = hitPolicy.IsHittable;
|
maniaObject.CheckHittable = hitPolicy.IsHittable;
|
||||||
|
|
||||||
HitObjectContainer.Add(hitObject);
|
base.Add(hitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Remove(DrawableHitObject h)
|
public override bool Remove(DrawableHitObject h)
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneObjectBeatSnap : TestSceneOsuEditor
|
||||||
|
{
|
||||||
|
private OsuPlayfield playfield;
|
||||||
|
|
||||||
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(Ruleset.Value, false);
|
||||||
|
|
||||||
|
public override void SetUpSteps()
|
||||||
|
{
|
||||||
|
base.SetUpSteps();
|
||||||
|
AddStep("get playfield", () => playfield = Editor.ChildrenOfType<OsuPlayfield>().First());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBeatSnapHitCircle()
|
||||||
|
{
|
||||||
|
double firstTimingPointTime() => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.First().Time;
|
||||||
|
|
||||||
|
AddStep("seek some milliseconds forward", () => EditorClock.Seek(firstTimingPointTime() + 10));
|
||||||
|
|
||||||
|
AddStep("move mouse to centre", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre));
|
||||||
|
AddStep("enter placement mode", () => InputManager.Key(Key.Number2));
|
||||||
|
AddStep("place first object", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddAssert("ensure object snapped back to correct time", () => EditorBeatmap.HitObjects.First().StartTime == firstTimingPointTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
{
|
{
|
||||||
base.SetUpSteps();
|
base.SetUpSteps();
|
||||||
AddStep("get playfield", () => playfield = Editor.ChildrenOfType<OsuPlayfield>().First());
|
AddStep("get playfield", () => playfield = Editor.ChildrenOfType<OsuPlayfield>().First());
|
||||||
|
AddStep("seek to first control point", () => EditorClock.Seek(Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.First().Time));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
@ -51,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
var first = (OsuHitObject)objects.First();
|
var first = (OsuHitObject)objects.First();
|
||||||
var second = (OsuHitObject)objects.Last();
|
var second = (OsuHitObject)objects.Last();
|
||||||
|
|
||||||
return first.Position == second.Position;
|
return Precision.AlmostEquals(first.EndPosition, second.Position);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,13 +67,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
AddStep("start slider placement", () => InputManager.Click(MouseButton.Left));
|
AddStep("start slider placement", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
AddStep("move to place end", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.185f, 0)));
|
AddStep("move to place end", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.225f, 0)));
|
||||||
|
|
||||||
AddStep("end slider placement", () => InputManager.Click(MouseButton.Right));
|
AddStep("end slider placement", () => InputManager.Click(MouseButton.Right));
|
||||||
|
|
||||||
AddStep("enter circle placement mode", () => InputManager.Key(Key.Number2));
|
AddStep("enter circle placement mode", () => InputManager.Key(Key.Number2));
|
||||||
|
|
||||||
AddStep("move mouse slightly", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.20f, 0)));
|
AddStep("move mouse slightly", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.235f, 0)));
|
||||||
|
|
||||||
AddStep("place second object", () => InputManager.Click(MouseButton.Left));
|
AddStep("place second object", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
@ -86,5 +87,64 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
return Precision.AlmostEquals(first.EndPosition, second.Position);
|
return Precision.AlmostEquals(first.EndPosition, second.Position);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSecondCircleInSelectionAlsoSnaps()
|
||||||
|
{
|
||||||
|
AddStep("move mouse to centre", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre));
|
||||||
|
|
||||||
|
AddStep("disable distance snap", () => InputManager.Key(Key.Q));
|
||||||
|
|
||||||
|
AddStep("enter placement mode", () => InputManager.Key(Key.Number2));
|
||||||
|
|
||||||
|
AddStep("place first object", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddStep("increment time", () => EditorClock.SeekForward(true));
|
||||||
|
|
||||||
|
AddStep("move mouse right", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.2f, 0)));
|
||||||
|
AddStep("place second object", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddStep("increment time", () => EditorClock.SeekForward(true));
|
||||||
|
|
||||||
|
AddStep("move mouse down", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(0, playfield.ScreenSpaceDrawQuad.Width * 0.2f)));
|
||||||
|
AddStep("place third object", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddStep("enter selection mode", () => InputManager.Key(Key.Number1));
|
||||||
|
|
||||||
|
AddStep("select objects 2 and 3", () =>
|
||||||
|
{
|
||||||
|
// add selection backwards to test non-sequential time ordering
|
||||||
|
EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[2]);
|
||||||
|
EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("begin drag", () => InputManager.PressButton(MouseButton.Left));
|
||||||
|
|
||||||
|
AddStep("move mouse slightly off centre", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.02f, 0)));
|
||||||
|
|
||||||
|
AddAssert("object 3 snapped to 1", () =>
|
||||||
|
{
|
||||||
|
var objects = EditorBeatmap.HitObjects;
|
||||||
|
|
||||||
|
var first = (OsuHitObject)objects.First();
|
||||||
|
var third = (OsuHitObject)objects.Last();
|
||||||
|
|
||||||
|
return Precision.AlmostEquals(first.EndPosition, third.Position);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("move mouse slightly off centre", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * -0.22f, playfield.ScreenSpaceDrawQuad.Width * 0.21f)));
|
||||||
|
|
||||||
|
AddAssert("object 2 snapped to 1", () =>
|
||||||
|
{
|
||||||
|
var objects = EditorBeatmap.HitObjects;
|
||||||
|
|
||||||
|
var first = (OsuHitObject)objects.First();
|
||||||
|
var second = (OsuHitObject)objects.ElementAt(1);
|
||||||
|
|
||||||
|
return Precision.AlmostEquals(first.EndPosition, second.Position);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("end drag", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,6 +174,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
private class SnapProvider : IPositionSnapProvider
|
private class SnapProvider : IPositionSnapProvider
|
||||||
{
|
{
|
||||||
|
public SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) =>
|
||||||
|
new SnapResult(screenSpacePosition, null);
|
||||||
|
|
||||||
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
|
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
|
||||||
|
|
||||||
public float GetBeatSnapDistanceAt(double referenceTime) => (float)beat_length;
|
public float GetBeatSnapDistanceAt(double referenceTime) => (float)beat_length;
|
||||||
|
@ -5,7 +5,7 @@ using osu.Game.Tests.Visual;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||||
{
|
{
|
||||||
public class OsuModTestScene : ModTestScene
|
public abstract class OsuModTestScene : ModTestScene
|
||||||
{
|
{
|
||||||
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||||
}
|
}
|
||||||
|
@ -108,8 +108,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
AddStep("change samples", () => slider.HitObject.Samples = new[]
|
AddStep("change samples", () => slider.HitObject.Samples = new[]
|
||||||
{
|
{
|
||||||
new HitSampleInfo { Name = HitSampleInfo.HIT_CLAP },
|
new HitSampleInfo(HitSampleInfo.HIT_CLAP),
|
||||||
new HitSampleInfo { Name = HitSampleInfo.HIT_WHISTLE },
|
new HitSampleInfo(HitSampleInfo.HIT_WHISTLE),
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("head samples updated", () => assertSamples(slider.HitObject.HeadCircle));
|
AddAssert("head samples updated", () => assertSamples(slider.HitObject.HeadCircle));
|
||||||
@ -136,15 +136,15 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
slider = (DrawableSlider)createSlider(repeats: 1);
|
slider = (DrawableSlider)createSlider(repeats: 1);
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
slider.HitObject.NodeSamples.Add(new List<HitSampleInfo> { new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH } });
|
slider.HitObject.NodeSamples.Add(new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_FINISH) });
|
||||||
|
|
||||||
Add(slider);
|
Add(slider);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("change samples", () => slider.HitObject.Samples = new[]
|
AddStep("change samples", () => slider.HitObject.Samples = new[]
|
||||||
{
|
{
|
||||||
new HitSampleInfo { Name = HitSampleInfo.HIT_CLAP },
|
new HitSampleInfo(HitSampleInfo.HIT_CLAP),
|
||||||
new HitSampleInfo { Name = HitSampleInfo.HIT_WHISTLE },
|
new HitSampleInfo(HitSampleInfo.HIT_WHISTLE),
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("head samples not updated", () => assertSamples(slider.HitObject.HeadCircle));
|
AddAssert("head samples not updated", () => assertSamples(slider.HitObject.HeadCircle));
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -45,9 +45,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
|
|||||||
return base.OnMouseDown(e);
|
return base.OnMouseDown(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
base.UpdatePosition(result);
|
base.UpdateTimeAndPosition(result);
|
||||||
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
private OsuColour colours { get; set; }
|
private OsuColour colours { get; set; }
|
||||||
|
|
||||||
private IBindable<Vector2> sliderPosition;
|
private IBindable<Vector2> sliderPosition;
|
||||||
|
private IBindable<float> sliderScale;
|
||||||
private IBindable<Vector2> controlPointPosition;
|
private IBindable<Vector2> controlPointPosition;
|
||||||
|
|
||||||
public PathControlPointPiece(Slider slider, PathControlPoint controlPoint)
|
public PathControlPointPiece(Slider slider, PathControlPoint controlPoint)
|
||||||
@ -69,13 +70,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Size = new Vector2(10),
|
Size = new Vector2(20),
|
||||||
},
|
},
|
||||||
markerRing = new CircularContainer
|
markerRing = new CircularContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Size = new Vector2(14),
|
Size = new Vector2(28),
|
||||||
Masking = true,
|
Masking = true,
|
||||||
BorderThickness = 2,
|
BorderThickness = 2,
|
||||||
BorderColour = Color4.White,
|
BorderColour = Color4.White,
|
||||||
@ -102,6 +103,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
controlPointPosition = ControlPoint.Position.GetBoundCopy();
|
controlPointPosition = ControlPoint.Position.GetBoundCopy();
|
||||||
controlPointPosition.BindValueChanged(_ => updateMarkerDisplay());
|
controlPointPosition.BindValueChanged(_ => updateMarkerDisplay());
|
||||||
|
|
||||||
|
sliderScale = slider.ScaleBindable.GetBoundCopy();
|
||||||
|
sliderScale.BindValueChanged(_ => updateMarkerDisplay());
|
||||||
|
|
||||||
IsSelected.BindValueChanged(_ => updateMarkerDisplay());
|
IsSelected.BindValueChanged(_ => updateMarkerDisplay());
|
||||||
|
|
||||||
updateMarkerDisplay();
|
updateMarkerDisplay();
|
||||||
@ -143,6 +147,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
protected override bool OnClick(ClickEvent e) => RequestSelection != null;
|
protected override bool OnClick(ClickEvent e) => RequestSelection != null;
|
||||||
|
|
||||||
|
private Vector2 dragStartPosition;
|
||||||
|
|
||||||
protected override bool OnDragStart(DragStartEvent e)
|
protected override bool OnDragStart(DragStartEvent e)
|
||||||
{
|
{
|
||||||
if (RequestSelection == null)
|
if (RequestSelection == null)
|
||||||
@ -150,6 +156,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
if (e.Button == MouseButton.Left)
|
if (e.Button == MouseButton.Left)
|
||||||
{
|
{
|
||||||
|
dragStartPosition = ControlPoint.Position.Value;
|
||||||
changeHandler?.BeginChange();
|
changeHandler?.BeginChange();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -174,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
slider.Path.ControlPoints[i].Position.Value -= movementDelta;
|
slider.Path.ControlPoints[i].Position.Value -= movementDelta;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ControlPoint.Position.Value += e.Delta;
|
ControlPoint.Position.Value = dragStartPosition + (e.MousePosition - e.MouseDownPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDragEnd(DragEndEvent e) => changeHandler?.EndChange();
|
protected override void OnDragEnd(DragEndEvent e) => changeHandler?.EndChange();
|
||||||
@ -194,6 +201,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
colour = colour.Lighten(1);
|
colour = colour.Lighten(1);
|
||||||
|
|
||||||
marker.Colour = colour;
|
marker.Colour = colour;
|
||||||
|
marker.Scale = new Vector2(slider.Scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,9 +67,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
inputManager = GetContainingInputManager();
|
inputManager = GetContainingInputManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
base.UpdatePosition(result);
|
base.UpdateTimeAndPosition(result);
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
|
@ -44,6 +44,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private IEditorChangeHandler changeHandler { get; set; }
|
private IEditorChangeHandler changeHandler { get; set; }
|
||||||
|
|
||||||
|
private readonly BindableList<PathControlPoint> controlPoints = new BindableList<PathControlPoint>();
|
||||||
|
private readonly IBindable<int> pathVersion = new Bindable<int>();
|
||||||
|
|
||||||
public SliderSelectionBlueprint(DrawableSlider slider)
|
public SliderSelectionBlueprint(DrawableSlider slider)
|
||||||
: base(slider)
|
: base(slider)
|
||||||
{
|
{
|
||||||
@ -61,13 +64,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private IBindable<int> pathVersion;
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
pathVersion = HitObject.Path.Version.GetBoundCopy();
|
controlPoints.BindTo(HitObject.Path.ControlPoints);
|
||||||
|
|
||||||
|
pathVersion.BindTo(HitObject.Path.Version);
|
||||||
pathVersion.BindValueChanged(_ => updatePath());
|
pathVersion.BindValueChanged(_ => updatePath());
|
||||||
|
|
||||||
BodyPiece.UpdateFrom(HitObject);
|
BodyPiece.UpdateFrom(HitObject);
|
||||||
@ -164,8 +167,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BindableList<PathControlPoint> controlPoints => HitObject.Path.ControlPoints;
|
|
||||||
|
|
||||||
private int addControlPoint(Vector2 position)
|
private int addControlPoint(Vector2 position)
|
||||||
{
|
{
|
||||||
position -= HitObject.Position;
|
position -= HitObject.Position;
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit
|
|
||||||
{
|
|
||||||
public class DrawableOsuEditPool<T> : DrawableOsuPool<T>
|
|
||||||
where T : DrawableHitObject, new()
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay.
|
|
||||||
/// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points.
|
|
||||||
/// </summary>
|
|
||||||
private const double editor_hit_object_fade_out_extension = 700;
|
|
||||||
|
|
||||||
public DrawableOsuEditPool(Func<DrawableHitObject, double, bool> checkHittable, Action<Drawable> onLoaded, int initialSize, int? maximumSize = null)
|
|
||||||
: base(checkHittable, onLoaded, initialSize, maximumSize)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override T CreateNewDrawable() => base.CreateNewDrawable().With(d => d.ApplyCustomUpdateState += updateState);
|
|
||||||
|
|
||||||
private void updateState(DrawableHitObject hitObject, ArmedState state)
|
|
||||||
{
|
|
||||||
if (state == ArmedState.Idle)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// adjust the visuals of certain object types to make them stay on screen for longer than usual.
|
|
||||||
switch (hitObject)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
// there are quite a few drawable hit types we don't want to extend (spinners, ticks etc.)
|
|
||||||
return;
|
|
||||||
|
|
||||||
case DrawableSlider _:
|
|
||||||
// no specifics to sliders but let them fade slower below.
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DrawableHitCircle circle: // also handles slider heads
|
|
||||||
circle.ApproachCircle
|
|
||||||
.FadeOutFromOne(editor_hit_object_fade_out_extension)
|
|
||||||
.Expire();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the existing fade out transform
|
|
||||||
var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha));
|
|
||||||
|
|
||||||
if (existing == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
hitObject.RemoveTransform(existing);
|
|
||||||
|
|
||||||
using (hitObject.BeginAbsoluteSequence(existing.StartTime))
|
|
||||||
hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,9 +2,12 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Graphics.Pooling;
|
using System.Linq;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -26,8 +29,51 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
protected override GameplayCursorContainer CreateCursor() => null;
|
protected override GameplayCursorContainer CreateCursor() => null;
|
||||||
|
|
||||||
protected override DrawablePool<TDrawable> CreatePool<TDrawable>(int initialSize, int? maximumSize = null)
|
protected override void OnNewDrawableHitObject(DrawableHitObject d)
|
||||||
=> new DrawableOsuEditPool<TDrawable>(CheckHittable, OnHitObjectLoaded, initialSize, maximumSize);
|
{
|
||||||
|
d.ApplyCustomUpdateState += updateState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay.
|
||||||
|
/// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points.
|
||||||
|
/// </summary>
|
||||||
|
private const double editor_hit_object_fade_out_extension = 700;
|
||||||
|
|
||||||
|
private void updateState(DrawableHitObject hitObject, ArmedState state)
|
||||||
|
{
|
||||||
|
if (state == ArmedState.Idle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// adjust the visuals of certain object types to make them stay on screen for longer than usual.
|
||||||
|
switch (hitObject)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
// there are quite a few drawable hit types we don't want to extend (spinners, ticks etc.)
|
||||||
|
return;
|
||||||
|
|
||||||
|
case DrawableSlider _:
|
||||||
|
// no specifics to sliders but let them fade slower below.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DrawableHitCircle circle: // also handles slider heads
|
||||||
|
circle.ApproachCircle
|
||||||
|
.FadeOutFromOne(editor_hit_object_fade_out_extension)
|
||||||
|
.Expire();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the existing fade out transform
|
||||||
|
var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha));
|
||||||
|
|
||||||
|
if (existing == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hitObject.RemoveTransform(existing);
|
||||||
|
|
||||||
|
using (hitObject.BeginAbsoluteSequence(existing.StartTime))
|
||||||
|
hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,11 +105,20 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
|
public override SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition)
|
||||||
{
|
{
|
||||||
if (snapToVisibleBlueprints(screenSpacePosition, out var snapResult))
|
if (snapToVisibleBlueprints(screenSpacePosition, out var snapResult))
|
||||||
return snapResult;
|
return snapResult;
|
||||||
|
|
||||||
|
return new SnapResult(screenSpacePosition, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
|
||||||
|
{
|
||||||
|
var positionSnap = SnapScreenSpacePositionToValidPosition(screenSpacePosition);
|
||||||
|
if (positionSnap.ScreenSpacePosition != screenSpacePosition)
|
||||||
|
return positionSnap;
|
||||||
|
|
||||||
// will be null if distance snap is disabled or not feasible for the current time value.
|
// will be null if distance snap is disabled or not feasible for the current time value.
|
||||||
if (distanceSnapGrid == null)
|
if (distanceSnapGrid == null)
|
||||||
return base.SnapScreenSpacePositionToValidTime(screenSpacePosition);
|
return base.SnapScreenSpacePositionToValidTime(screenSpacePosition);
|
||||||
|
@ -5,7 +5,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
public class OsuModEasy : ModEasy
|
public class OsuModEasy : ModEasyWithExtraLives
|
||||||
{
|
{
|
||||||
public override string Description => @"Larger circles, more forgiving HP drain, less accuracy required, and three lives!";
|
public override string Description => @"Larger circles, more forgiving HP drain, less accuracy required, and three lives!";
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
@ -47,6 +48,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public double AnimationStartTime { get; set; }
|
public Bindable<double> AnimationStartTime { get; } = new BindableDouble();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
fp.Alpha = 0;
|
fp.Alpha = 0;
|
||||||
fp.Scale = new Vector2(1.5f * end.Scale);
|
fp.Scale = new Vector2(1.5f * end.Scale);
|
||||||
|
|
||||||
fp.AnimationStartTime = fadeInTime;
|
fp.AnimationStartTime.Value = fadeInTime;
|
||||||
|
|
||||||
using (fp.BeginAbsoluteSequence(fadeInTime))
|
using (fp.BeginAbsoluteSequence(fadeInTime))
|
||||||
{
|
{
|
||||||
|
@ -19,7 +19,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach
|
public class DrawableHitCircle : DrawableOsuHitObject
|
||||||
{
|
{
|
||||||
public OsuAction? HitAction => HitArea.HitAction;
|
public OsuAction? HitAction => HitArea.HitAction;
|
||||||
protected virtual OsuSkinComponents CirclePieceComponent => OsuSkinComponents.HitCircle;
|
protected virtual OsuSkinComponents CirclePieceComponent => OsuSkinComponents.HitCircle;
|
||||||
|
@ -9,9 +9,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
@ -53,26 +51,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnApply(HitObject hitObject)
|
protected override void OnApply()
|
||||||
{
|
{
|
||||||
base.OnApply(hitObject);
|
base.OnApply();
|
||||||
|
|
||||||
IndexInCurrentComboBindable.BindTo(HitObject.IndexInCurrentComboBindable);
|
IndexInCurrentComboBindable.BindTo(HitObject.IndexInCurrentComboBindable);
|
||||||
PositionBindable.BindTo(HitObject.PositionBindable);
|
PositionBindable.BindTo(HitObject.PositionBindable);
|
||||||
StackHeightBindable.BindTo(HitObject.StackHeightBindable);
|
StackHeightBindable.BindTo(HitObject.StackHeightBindable);
|
||||||
ScaleBindable.BindTo(HitObject.ScaleBindable);
|
ScaleBindable.BindTo(HitObject.ScaleBindable);
|
||||||
|
|
||||||
// Manually set to reduce the number of future alive objects to a bare minimum.
|
|
||||||
LifetimeStart = HitObject.StartTime - HitObject.TimePreempt;
|
|
||||||
|
|
||||||
// Arbitrary lifetime end to prevent past objects in idle states remaining alive in non-frame-stable contexts.
|
|
||||||
// An extra 1000ms is added to always overestimate the true lifetime, and a more exact value is set by hit transforms and the following expiry.
|
|
||||||
LifetimeEnd = HitObject.GetEndTime() + HitObject.HitWindows.WindowFor(HitResult.Miss) + 1000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnFree(HitObject hitObject)
|
protected override void OnFree()
|
||||||
{
|
{
|
||||||
base.OnFree(hitObject);
|
base.OnFree();
|
||||||
|
|
||||||
IndexInCurrentComboBindable.UnbindFrom(HitObject.IndexInCurrentComboBindable);
|
IndexInCurrentComboBindable.UnbindFrom(HitObject.IndexInCurrentComboBindable);
|
||||||
PositionBindable.UnbindFrom(HitObject.PositionBindable);
|
PositionBindable.UnbindFrom(HitObject.PositionBindable);
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Pooling;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|
||||||
{
|
|
||||||
public class DrawableOsuPool<T> : DrawablePool<T>
|
|
||||||
where T : DrawableHitObject, new()
|
|
||||||
{
|
|
||||||
private readonly Func<DrawableHitObject, double, bool> checkHittable;
|
|
||||||
private readonly Action<Drawable> onLoaded;
|
|
||||||
|
|
||||||
public DrawableOsuPool(Func<DrawableHitObject, double, bool> checkHittable, Action<Drawable> onLoaded, int initialSize, int? maximumSize = null)
|
|
||||||
: base(initialSize, maximumSize)
|
|
||||||
{
|
|
||||||
this.checkHittable = checkHittable;
|
|
||||||
this.onLoaded = onLoaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override T CreateNewDrawable() => base.CreateNewDrawable().With(o =>
|
|
||||||
{
|
|
||||||
var osuObject = (DrawableOsuHitObject)(object)o;
|
|
||||||
|
|
||||||
osuObject.CheckHittable = checkHittable;
|
|
||||||
osuObject.OnLoadComplete += onLoaded;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,7 +19,7 @@ using osu.Game.Skinning;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableSlider : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach
|
public class DrawableSlider : DrawableOsuHitObject
|
||||||
{
|
{
|
||||||
public new Slider HitObject => (Slider)base.HitObject;
|
public new Slider HitObject => (Slider)base.HitObject;
|
||||||
|
|
||||||
@ -86,18 +86,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
Tracking.BindValueChanged(updateSlidingSample);
|
Tracking.BindValueChanged(updateSlidingSample);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnApply(HitObject hitObject)
|
protected override void OnApply()
|
||||||
{
|
{
|
||||||
base.OnApply(hitObject);
|
base.OnApply();
|
||||||
|
|
||||||
// Ensure that the version will change after the upcoming BindTo().
|
// Ensure that the version will change after the upcoming BindTo().
|
||||||
pathVersion.Value = int.MaxValue;
|
pathVersion.Value = int.MaxValue;
|
||||||
PathVersion.BindTo(HitObject.Path.Version);
|
PathVersion.BindTo(HitObject.Path.Version);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnFree(HitObject hitObject)
|
protected override void OnFree()
|
||||||
{
|
{
|
||||||
base.OnFree(hitObject);
|
base.OnFree();
|
||||||
|
|
||||||
PathVersion.UnbindFrom(HitObject.Path.Version);
|
PathVersion.UnbindFrom(HitObject.Path.Version);
|
||||||
}
|
}
|
||||||
@ -115,8 +115,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
if (firstSample != null)
|
if (firstSample != null)
|
||||||
{
|
{
|
||||||
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample);
|
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample).With("sliderslide");
|
||||||
clone.Name = "sliderslide";
|
|
||||||
|
|
||||||
samplesContainer.Add(slidingSample = new PausableSkinnableSound(clone)
|
samplesContainer.Add(slidingSample = new PausableSkinnableSound(clone)
|
||||||
{
|
{
|
||||||
@ -255,7 +254,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
if (userTriggered || Time.Current < HitObject.EndTime)
|
if (userTriggered || Time.Current < HitObject.EndTime)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
ApplyResult(r => r.Type = NestedHitObjects.Any(h => h.Result.IsHit) ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PlaySamples()
|
public override void PlaySamples()
|
||||||
@ -294,14 +293,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
Ball.ScaleTo(HitObject.Scale * 1.4f, fade_out_time, Easing.Out);
|
Ball.ScaleTo(HitObject.Scale * 1.4f, fade_out_time, Easing.Out);
|
||||||
|
if (sliderBody?.SnakingOut.Value == true)
|
||||||
|
Body.FadeOut(40); // short fade to allow for any body colour to smoothly disappear.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.FadeOut(fade_out_time, Easing.OutQuint).Expire();
|
this.FadeOut(fade_out_time, Easing.OutQuint).Expire();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Drawable ProxiedLayer => HeadCircle.ProxiedLayer;
|
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => sliderBody?.ReceivePositionalInputAt(screenSpacePos) ?? base.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => sliderBody?.ReceivePositionalInputAt(screenSpacePos) ?? base.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
private class DefaultSliderBody : PlaySliderBody
|
private class DefaultSliderBody : PlaySliderBody
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
@ -36,9 +35,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
pathVersion.BindValueChanged(_ => updatePosition());
|
pathVersion.BindValueChanged(_ => updatePosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnFree(HitObject hitObject)
|
protected override void OnFree()
|
||||||
{
|
{
|
||||||
base.OnFree(hitObject);
|
base.OnFree();
|
||||||
|
|
||||||
pathVersion.UnbindFrom(drawableSlider.PathVersion);
|
pathVersion.UnbindFrom(drawableSlider.PathVersion);
|
||||||
}
|
}
|
||||||
|
@ -110,8 +110,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
if (firstSample != null)
|
if (firstSample != null)
|
||||||
{
|
{
|
||||||
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample);
|
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample).With("spinnerspin");
|
||||||
clone.Name = "spinnerspin";
|
|
||||||
|
|
||||||
samplesContainer.Add(spinningSample = new PausableSkinnableSound(clone)
|
samplesContainer.Add(spinningSample = new PausableSkinnableSound(clone)
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableSpinnerTick : DrawableOsuHitObject
|
public class DrawableSpinnerTick : DrawableOsuHitObject
|
||||||
@ -17,6 +19,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DrawableSpinner drawableSpinner;
|
||||||
|
|
||||||
|
protected override void OnParentReceived(DrawableHitObject parent)
|
||||||
|
{
|
||||||
|
base.OnParentReceived(parent);
|
||||||
|
drawableSpinner = (DrawableSpinner)parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override double MaximumJudgementOffset => drawableSpinner.HitObject.Duration;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Apply a judgement result.
|
/// Apply a judgement result.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -38,7 +38,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IBindable<ArmedState> state = new Bindable<ArmedState>();
|
|
||||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||||
|
|
||||||
@ -50,7 +49,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
{
|
{
|
||||||
var drawableOsuObject = (DrawableOsuHitObject)drawableObject;
|
var drawableOsuObject = (DrawableOsuHitObject)drawableObject;
|
||||||
|
|
||||||
state.BindTo(drawableObject.State);
|
|
||||||
accentColour.BindTo(drawableObject.AccentColour);
|
accentColour.BindTo(drawableObject.AccentColour);
|
||||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||||
}
|
}
|
||||||
@ -59,7 +57,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
state.BindValueChanged(updateState, true);
|
|
||||||
accentColour.BindValueChanged(colour =>
|
accentColour.BindValueChanged(colour =>
|
||||||
{
|
{
|
||||||
explode.Colour = colour.NewValue;
|
explode.Colour = colour.NewValue;
|
||||||
@ -68,15 +65,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true);
|
indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true);
|
||||||
|
|
||||||
|
drawableObject.ApplyCustomUpdateState += updateState;
|
||||||
|
updateState(drawableObject, drawableObject.State.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateState(ValueChangedEvent<ArmedState> state)
|
private void updateState(DrawableHitObject drawableObject, ArmedState state)
|
||||||
{
|
{
|
||||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true))
|
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true))
|
||||||
{
|
{
|
||||||
glow.FadeOut(400);
|
glow.FadeOut(400);
|
||||||
|
|
||||||
switch (state.NewValue)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
const double flash_in = 40;
|
const double flash_in = 40;
|
||||||
|
@ -248,7 +248,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void trackingChanged(ValueChangedEvent<bool> tracking) =>
|
private void trackingChanged(ValueChangedEvent<bool> tracking) =>
|
||||||
box.FadeTo(tracking.NewValue ? 0.6f : 0.05f, 200, Easing.OutQuint);
|
box.FadeTo(tracking.NewValue ? 0.3f : 0.05f, 200, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,14 +221,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
var sampleList = new List<HitSampleInfo>();
|
var sampleList = new List<HitSampleInfo>();
|
||||||
|
|
||||||
if (firstSample != null)
|
if (firstSample != null)
|
||||||
{
|
sampleList.Add(firstSample.With("slidertick"));
|
||||||
sampleList.Add(new HitSampleInfo
|
|
||||||
{
|
|
||||||
Bank = firstSample.Bank,
|
|
||||||
Volume = firstSample.Volume,
|
|
||||||
Name = @"slidertick",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var tick in NestedHitObjects.OfType<SliderTick>())
|
foreach (var tick in NestedHitObjects.OfType<SliderTick>())
|
||||||
tick.Samples = sampleList;
|
tick.Samples = sampleList;
|
||||||
|
@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
{
|
{
|
||||||
public SpinnerBonusTick()
|
public SpinnerBonusTick()
|
||||||
{
|
{
|
||||||
Samples.Add(new HitSampleInfo { Name = "spinnerbonus" });
|
Samples.Add(new HitSampleInfo("spinnerbonus"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new OsuSpinnerBonusTickJudgement();
|
public override Judgement CreateJudgement() => new OsuSpinnerBonusTickJudgement();
|
||||||
|
@ -38,7 +38,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
private SkinnableSpriteText hitCircleText;
|
private SkinnableSpriteText hitCircleText;
|
||||||
|
|
||||||
private readonly IBindable<ArmedState> state = new Bindable<ArmedState>();
|
|
||||||
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
||||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||||
|
|
||||||
@ -113,7 +112,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
if (overlayAboveNumber)
|
if (overlayAboveNumber)
|
||||||
AddInternal(hitCircleOverlay.CreateProxy());
|
AddInternal(hitCircleOverlay.CreateProxy());
|
||||||
|
|
||||||
state.BindTo(drawableObject.State);
|
|
||||||
accentColour.BindTo(drawableObject.AccentColour);
|
accentColour.BindTo(drawableObject.AccentColour);
|
||||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||||
|
|
||||||
@ -137,19 +135,21 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
state.BindValueChanged(updateState, true);
|
|
||||||
accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
||||||
if (hasNumber)
|
if (hasNumber)
|
||||||
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
||||||
|
|
||||||
|
drawableObject.ApplyCustomUpdateState += updateState;
|
||||||
|
updateState(drawableObject, drawableObject.State.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateState(ValueChangedEvent<ArmedState> state)
|
private void updateState(DrawableHitObject drawableObject, ArmedState state)
|
||||||
{
|
{
|
||||||
const double legacy_fade_duration = 240;
|
const double legacy_fade_duration = 240;
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true))
|
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true))
|
||||||
{
|
{
|
||||||
switch (state.NewValue)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
|
circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -25,8 +26,6 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
{
|
{
|
||||||
public class OsuPlayfield : Playfield
|
public class OsuPlayfield : Playfield
|
||||||
{
|
{
|
||||||
public readonly Func<DrawableHitObject, double, bool> CheckHittable;
|
|
||||||
|
|
||||||
private readonly PlayfieldBorder playfieldBorder;
|
private readonly PlayfieldBorder playfieldBorder;
|
||||||
private readonly ProxyContainer approachCircles;
|
private readonly ProxyContainer approachCircles;
|
||||||
private readonly ProxyContainer spinnerProxies;
|
private readonly ProxyContainer spinnerProxies;
|
||||||
@ -56,7 +55,6 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
};
|
};
|
||||||
|
|
||||||
hitPolicy = new OrderedHitPolicy(HitObjectContainer);
|
hitPolicy = new OrderedHitPolicy(HitObjectContainer);
|
||||||
CheckHittable = hitPolicy.IsHittable;
|
|
||||||
|
|
||||||
var hitWindows = new OsuHitWindows();
|
var hitWindows = new OsuHitWindows();
|
||||||
|
|
||||||
@ -68,6 +66,29 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
NewResult += onNewResult;
|
NewResult += onNewResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnNewDrawableHitObject(DrawableHitObject drawable)
|
||||||
|
{
|
||||||
|
((DrawableOsuHitObject)drawable).CheckHittable = hitPolicy.IsHittable;
|
||||||
|
|
||||||
|
Debug.Assert(!drawable.IsLoaded, $"Already loaded {nameof(DrawableHitObject)} is added to {nameof(OsuPlayfield)}");
|
||||||
|
drawable.OnLoadComplete += onDrawableHitObjectLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDrawableHitObjectLoaded(Drawable drawable)
|
||||||
|
{
|
||||||
|
// note: `Slider`'s `ProxiedLayer` is added when its nested `DrawableHitCircle` is loaded.
|
||||||
|
switch (drawable)
|
||||||
|
{
|
||||||
|
case DrawableSpinner _:
|
||||||
|
spinnerProxies.Add(drawable.CreateProxy());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DrawableHitCircle hitCircle:
|
||||||
|
approachCircles.Add(hitCircle.ProxiedLayer.CreateProxy());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void onJudgmentLoaded(DrawableOsuJudgement judgement)
|
private void onJudgmentLoaded(DrawableOsuJudgement judgement)
|
||||||
{
|
{
|
||||||
judgementAboveHitObjectLayer.Add(judgement.GetProxyAboveHitObjectsContent());
|
judgementAboveHitObjectLayer.Add(judgement.GetProxyAboveHitObjectsContent());
|
||||||
@ -78,28 +99,19 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
{
|
{
|
||||||
config?.BindWith(OsuRulesetSetting.PlayfieldBorderStyle, playfieldBorder.PlayfieldBorderStyle);
|
config?.BindWith(OsuRulesetSetting.PlayfieldBorderStyle, playfieldBorder.PlayfieldBorderStyle);
|
||||||
|
|
||||||
registerPool<HitCircle, DrawableHitCircle>(10, 100);
|
RegisterPool<HitCircle, DrawableHitCircle>(10, 100);
|
||||||
|
|
||||||
registerPool<Slider, DrawableSlider>(10, 100);
|
RegisterPool<Slider, DrawableSlider>(10, 100);
|
||||||
registerPool<SliderHeadCircle, DrawableSliderHead>(10, 100);
|
RegisterPool<SliderHeadCircle, DrawableSliderHead>(10, 100);
|
||||||
registerPool<SliderTailCircle, DrawableSliderTail>(10, 100);
|
RegisterPool<SliderTailCircle, DrawableSliderTail>(10, 100);
|
||||||
registerPool<SliderTick, DrawableSliderTick>(10, 100);
|
RegisterPool<SliderTick, DrawableSliderTick>(10, 100);
|
||||||
registerPool<SliderRepeat, DrawableSliderRepeat>(5, 50);
|
RegisterPool<SliderRepeat, DrawableSliderRepeat>(5, 50);
|
||||||
|
|
||||||
registerPool<Spinner, DrawableSpinner>(2, 20);
|
RegisterPool<Spinner, DrawableSpinner>(2, 20);
|
||||||
registerPool<SpinnerTick, DrawableSpinnerTick>(10, 100);
|
RegisterPool<SpinnerTick, DrawableSpinnerTick>(10, 100);
|
||||||
registerPool<SpinnerBonusTick, DrawableSpinnerBonusTick>(10, 100);
|
RegisterPool<SpinnerBonusTick, DrawableSpinnerBonusTick>(10, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerPool<TObject, TDrawable>(int initialSize, int? maximumSize = null)
|
|
||||||
where TObject : HitObject
|
|
||||||
where TDrawable : DrawableHitObject, new()
|
|
||||||
=> RegisterPool<TObject, TDrawable>(CreatePool<TDrawable>(initialSize, maximumSize));
|
|
||||||
|
|
||||||
protected virtual DrawablePool<TDrawable> CreatePool<TDrawable>(int initialSize, int? maximumSize = null)
|
|
||||||
where TDrawable : DrawableHitObject, new()
|
|
||||||
=> new DrawableOsuPool<TDrawable>(CheckHittable, OnHitObjectLoaded, initialSize, maximumSize);
|
|
||||||
|
|
||||||
protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new OsuHitObjectLifetimeEntry(hitObject);
|
protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new OsuHitObjectLifetimeEntry(hitObject);
|
||||||
|
|
||||||
protected override void OnHitObjectAdded(HitObject hitObject)
|
protected override void OnHitObjectAdded(HitObject hitObject)
|
||||||
@ -114,27 +126,6 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
followPoints.RemoveFollowPoints((OsuHitObject)hitObject);
|
followPoints.RemoveFollowPoints((OsuHitObject)hitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnHitObjectLoaded(Drawable drawable)
|
|
||||||
{
|
|
||||||
switch (drawable)
|
|
||||||
{
|
|
||||||
case DrawableSliderHead _:
|
|
||||||
case DrawableSliderTail _:
|
|
||||||
case DrawableSliderTick _:
|
|
||||||
case DrawableSliderRepeat _:
|
|
||||||
case DrawableSpinnerTick _:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DrawableSpinner _:
|
|
||||||
spinnerProxies.Add(drawable.CreateProxy());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IDrawableHitObjectWithProxiedApproach approach:
|
|
||||||
approachCircles.Add(approach.ProxiedLayer.CreateProxy());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||||
{
|
{
|
||||||
// Hitobjects that block future hits should miss previous hitobjects if they're hit out-of-order.
|
// Hitobjects that block future hits should miss previous hitobjects if they're hit out-of-order.
|
||||||
@ -185,6 +176,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
public OsuHitObjectLifetimeEntry(HitObject hitObject)
|
public OsuHitObjectLifetimeEntry(HitObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
|
// Prevent past objects in idles states from remaining alive as their end times are skipped in non-frame-stable contexts.
|
||||||
|
LifetimeEnd = HitObject.GetEndTime() + HitObject.HitWindows.WindowFor(HitResult.Miss);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override double InitialLifetimeOffset => ((OsuHitObject)HitObject).TimePreempt;
|
protected override double InitialLifetimeOffset => ((OsuHitObject)HitObject).TimePreempt;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -43,10 +43,10 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
piece.Position = ToLocalSpace(result.ScreenSpacePosition);
|
piece.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||||
base.UpdatePosition(result);
|
base.UpdateTimeAndPosition(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,9 +68,9 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints
|
|||||||
EndPlacement(true);
|
EndPlacement(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
base.UpdatePosition(result);
|
base.UpdateTimeAndPosition(result);
|
||||||
|
|
||||||
if (PlacementActive)
|
if (PlacementActive)
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModEasy : ModEasy
|
public class TaikoModEasy : ModEasy
|
||||||
{
|
{
|
||||||
public override string Description => @"Beats move slower, less accuracy required, and three lives!";
|
public override string Description => @"Beats move slower, and less accuracy required!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
Filled = HitObject.FirstTick
|
Filled = HitObject.FirstTick
|
||||||
});
|
});
|
||||||
|
|
||||||
|
protected override double MaximumJudgementOffset => HitObject.HitWindow;
|
||||||
|
|
||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
if (!userTriggered)
|
if (!userTriggered)
|
||||||
|
@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
if (isRimType != rimSamples.Any())
|
if (isRimType != rimSamples.Any())
|
||||||
{
|
{
|
||||||
if (isRimType)
|
if (isRimType)
|
||||||
HitObject.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_CLAP });
|
HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (var sample in rimSamples)
|
foreach (var sample in rimSamples)
|
||||||
@ -125,9 +125,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
if (s.Name != HitSampleInfo.HIT_FINISH)
|
if (s.Name != HitSampleInfo.HIT_FINISH)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var sClone = s.Clone();
|
corrected[i] = s.With(HitSampleInfo.HIT_WHISTLE);
|
||||||
sClone.Name = HitSampleInfo.HIT_WHISTLE;
|
|
||||||
corrected[i] = sClone;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return corrected;
|
return corrected;
|
||||||
|
@ -169,7 +169,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
if (isStrong.Value != strongSamples.Any())
|
if (isStrong.Value != strongSamples.Any())
|
||||||
{
|
{
|
||||||
if (isStrong.Value)
|
if (isStrong.Value)
|
||||||
HitObject.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH });
|
HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_FINISH));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (var sample in strongSamples)
|
foreach (var sample in strongSamples)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -37,7 +38,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
private SkinnableDrawable mascot;
|
private SkinnableDrawable mascot;
|
||||||
|
|
||||||
private ProxyContainer topLevelHitContainer;
|
private ProxyContainer topLevelHitContainer;
|
||||||
private ProxyContainer barlineContainer;
|
private ScrollingHitObjectContainer barlineContainer;
|
||||||
private Container rightArea;
|
private Container rightArea;
|
||||||
private Container leftArea;
|
private Container leftArea;
|
||||||
|
|
||||||
@ -83,10 +84,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
barlineContainer = new ProxyContainer
|
barlineContainer = new ScrollingHitObjectContainer(),
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Name = "Hit objects",
|
Name = "Hit objects",
|
||||||
@ -159,18 +157,37 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
|
|
||||||
public override void Add(DrawableHitObject h)
|
public override void Add(DrawableHitObject h)
|
||||||
{
|
{
|
||||||
h.OnNewResult += OnNewResult;
|
|
||||||
base.Add(h);
|
|
||||||
|
|
||||||
switch (h)
|
switch (h)
|
||||||
{
|
{
|
||||||
case DrawableBarLine barline:
|
case DrawableBarLine barline:
|
||||||
barlineContainer.Add(barline.CreateProxy());
|
barlineContainer.Add(barline);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DrawableTaikoHitObject taikoObject:
|
case DrawableTaikoHitObject taikoObject:
|
||||||
|
h.OnNewResult += OnNewResult;
|
||||||
topLevelHitContainer.Add(taikoObject.CreateProxiedContent());
|
topLevelHitContainer.Add(taikoObject.CreateProxiedContent());
|
||||||
|
base.Add(h);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentException($"Unsupported {nameof(DrawableHitObject)} type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Remove(DrawableHitObject h)
|
||||||
|
{
|
||||||
|
switch (h)
|
||||||
|
{
|
||||||
|
case DrawableBarLine barline:
|
||||||
|
return barlineContainer.Remove(barline);
|
||||||
|
|
||||||
|
case DrawableTaikoHitObject _:
|
||||||
|
h.OnNewResult -= OnNewResult;
|
||||||
|
// todo: consider tidying of proxied content if required.
|
||||||
|
return base.Remove(h);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentException($"Unsupported {nameof(DrawableHitObject)} type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
@ -44,6 +43,36 @@ namespace osu.Game.Tests.Editing
|
|||||||
Assert.That(stateChangedFired, Is.EqualTo(2));
|
Assert.That(stateChangedFired, Is.EqualTo(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestApplyThenUndoThenApplySameChange()
|
||||||
|
{
|
||||||
|
var (handler, beatmap) = createChangeHandler();
|
||||||
|
|
||||||
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
|
string originalHash = handler.CurrentStateHash;
|
||||||
|
|
||||||
|
addArbitraryChange(beatmap);
|
||||||
|
handler.SaveState();
|
||||||
|
|
||||||
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(1));
|
||||||
|
|
||||||
|
string hash = handler.CurrentStateHash;
|
||||||
|
|
||||||
|
// undo a change without saving
|
||||||
|
handler.RestoreState(-1);
|
||||||
|
|
||||||
|
Assert.That(originalHash, Is.EqualTo(handler.CurrentStateHash));
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(2));
|
||||||
|
|
||||||
|
addArbitraryChange(beatmap);
|
||||||
|
handler.SaveState();
|
||||||
|
Assert.That(hash, Is.EqualTo(handler.CurrentStateHash));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSaveSameStateDoesNotSave()
|
public void TestSaveSameStateDoesNotSave()
|
||||||
{
|
{
|
||||||
@ -139,7 +168,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
|
|
||||||
private void addArbitraryChange(EditorBeatmap beatmap)
|
private void addArbitraryChange(EditorBeatmap beatmap)
|
||||||
{
|
{
|
||||||
beatmap.Add(new HitCircle { StartTime = RNG.Next(0, 100000) });
|
beatmap.Add(new HitCircle { StartTime = 2760 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
HitObjects =
|
HitObjects =
|
||||||
{
|
{
|
||||||
(OsuHitObject)current.HitObjects[0],
|
(OsuHitObject)current.HitObjects[0],
|
||||||
new HitCircle { StartTime = 2000, Samples = { new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH } } },
|
new HitCircle { StartTime = 2000, Samples = { new HitSampleInfo(HitSampleInfo.HIT_FINISH) } },
|
||||||
(OsuHitObject)current.HitObjects[2],
|
(OsuHitObject)current.HitObjects[2],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -268,12 +268,12 @@ namespace osu.Game.Tests.Editing
|
|||||||
HitObjects =
|
HitObjects =
|
||||||
{
|
{
|
||||||
(OsuHitObject)current.HitObjects[0],
|
(OsuHitObject)current.HitObjects[0],
|
||||||
new HitCircle { StartTime = 1000, Samples = { new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH } } },
|
new HitCircle { StartTime = 1000, Samples = { new HitSampleInfo(HitSampleInfo.HIT_FINISH) } },
|
||||||
(OsuHitObject)current.HitObjects[2],
|
(OsuHitObject)current.HitObjects[2],
|
||||||
(OsuHitObject)current.HitObjects[3],
|
(OsuHitObject)current.HitObjects[3],
|
||||||
new HitCircle { StartTime = 2250, Samples = { new HitSampleInfo { Name = HitSampleInfo.HIT_WHISTLE } } },
|
new HitCircle { StartTime = 2250, Samples = { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE) } },
|
||||||
(OsuHitObject)current.HitObjects[5],
|
(OsuHitObject)current.HitObjects[5],
|
||||||
new HitCircle { StartTime = 3000, Samples = { new HitSampleInfo { Name = HitSampleInfo.HIT_CLAP } } },
|
new HitCircle { StartTime = 3000, Samples = { new HitSampleInfo(HitSampleInfo.HIT_CLAP) } },
|
||||||
(OsuHitObject)current.HitObjects[7],
|
(OsuHitObject)current.HitObjects[7],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
79
osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs
Normal file
79
osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Animations;
|
||||||
|
using osu.Framework.Graphics.OpenGL.Textures;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.NonVisual.Skinning
|
||||||
|
{
|
||||||
|
[HeadlessTest]
|
||||||
|
public class LegacySkinAnimationTest : OsuTestScene
|
||||||
|
{
|
||||||
|
private const string animation_name = "animation";
|
||||||
|
private const int frame_count = 6;
|
||||||
|
|
||||||
|
[Cached(typeof(IAnimationTimeReference))]
|
||||||
|
private TestAnimationTimeReference animationTimeReference = new TestAnimationTimeReference();
|
||||||
|
|
||||||
|
private TextureAnimation animation;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAnimationTimeReferenceChange()
|
||||||
|
{
|
||||||
|
ISkin skin = new TestSkin();
|
||||||
|
|
||||||
|
AddStep("get animation", () => Add(animation = (TextureAnimation)skin.GetAnimation(animation_name, true, false)));
|
||||||
|
AddAssert("frame count correct", () => animation.FrameCount == frame_count);
|
||||||
|
assertPlaybackPosition(0);
|
||||||
|
|
||||||
|
AddStep("set start time to 1000", () => animationTimeReference.AnimationStartTime.Value = 1000);
|
||||||
|
assertPlaybackPosition(-1000);
|
||||||
|
|
||||||
|
AddStep("set current time to 500", () => animationTimeReference.ManualClock.CurrentTime = 500);
|
||||||
|
assertPlaybackPosition(-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertPlaybackPosition(double expectedPosition)
|
||||||
|
=> AddAssert($"playback position is {expectedPosition}", () => animation.PlaybackPosition == expectedPosition);
|
||||||
|
|
||||||
|
private class TestSkin : ISkin
|
||||||
|
{
|
||||||
|
private static readonly string[] lookup_names = Enumerable.Range(0, frame_count).Select(frame => $"{animation_name}-{frame}").ToArray();
|
||||||
|
|
||||||
|
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT)
|
||||||
|
{
|
||||||
|
return lookup_names.Contains(componentName) ? Texture.WhitePixel : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotSupportedException();
|
||||||
|
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotSupportedException();
|
||||||
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestAnimationTimeReference : IAnimationTimeReference
|
||||||
|
{
|
||||||
|
public ManualClock ManualClock { get; }
|
||||||
|
public IFrameBasedClock Clock { get; }
|
||||||
|
public Bindable<double> AnimationStartTime { get; } = new BindableDouble();
|
||||||
|
|
||||||
|
public TestAnimationTimeReference()
|
||||||
|
{
|
||||||
|
ManualClock = new ManualClock();
|
||||||
|
Clock = new FramedClock(ManualClock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -318,7 +318,7 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
private class FadeAccessibleResults : ResultsScreen
|
private class FadeAccessibleResults : ResultsScreen
|
||||||
{
|
{
|
||||||
public FadeAccessibleResults(ScoreInfo score)
|
public FadeAccessibleResults(ScoreInfo score)
|
||||||
: base(score)
|
: base(score, true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,6 +153,9 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
private class SnapProvider : IPositionSnapProvider
|
private class SnapProvider : IPositionSnapProvider
|
||||||
{
|
{
|
||||||
|
public SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) =>
|
||||||
|
new SnapResult(screenSpacePosition, null);
|
||||||
|
|
||||||
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
|
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
|
||||||
|
|
||||||
public float GetBeatSnapDistanceAt(double referenceTime) => 10;
|
public float GetBeatSnapDistanceAt(double referenceTime) => 10;
|
||||||
|
@ -21,13 +21,13 @@ using osu.Game.Rulesets.Difficulty;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Legacy;
|
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
@ -46,6 +46,50 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup() => Schedule(() => testClock.CurrentTime = 0);
|
public void Setup() => Schedule(() => testClock.CurrentTime = 0);
|
||||||
|
|
||||||
|
[TestCase("pooled")]
|
||||||
|
[TestCase("non-pooled")]
|
||||||
|
public void TestHitObjectLifetime(string pooled)
|
||||||
|
{
|
||||||
|
var beatmap = createBeatmap(_ => pooled == "pooled" ? new TestPooledHitObject() : new TestHitObject());
|
||||||
|
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range });
|
||||||
|
createTest(beatmap);
|
||||||
|
|
||||||
|
assertPosition(0, 0f);
|
||||||
|
assertDead(3);
|
||||||
|
|
||||||
|
setTime(3 * time_range);
|
||||||
|
assertPosition(3, 0f);
|
||||||
|
assertDead(0);
|
||||||
|
|
||||||
|
setTime(0 * time_range);
|
||||||
|
assertPosition(0, 0f);
|
||||||
|
assertDead(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("pooled")]
|
||||||
|
[TestCase("non-pooled")]
|
||||||
|
public void TestNestedHitObject(string pooled)
|
||||||
|
{
|
||||||
|
var beatmap = createBeatmap(i =>
|
||||||
|
{
|
||||||
|
var h = pooled == "pooled" ? new TestPooledParentHitObject() : new TestParentHitObject();
|
||||||
|
h.Duration = 300;
|
||||||
|
h.ChildTimeOffset = i % 3 * 100;
|
||||||
|
return h;
|
||||||
|
});
|
||||||
|
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range });
|
||||||
|
createTest(beatmap);
|
||||||
|
|
||||||
|
assertPosition(0, 0f);
|
||||||
|
assertHeight(0);
|
||||||
|
assertChildPosition(0);
|
||||||
|
|
||||||
|
setTime(5 * time_range);
|
||||||
|
assertPosition(5, 0f);
|
||||||
|
assertHeight(5);
|
||||||
|
assertChildPosition(5);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestRelativeBeatLengthScaleSingleTimingPoint()
|
public void TestRelativeBeatLengthScaleSingleTimingPoint()
|
||||||
{
|
{
|
||||||
@ -147,8 +191,37 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
assertPosition(1, 1);
|
assertPosition(1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a <see cref="DrawableTestHitObject" /> corresponding to the <paramref name="index"/>'th <see cref="TestHitObject"/>.
|
||||||
|
/// When the hit object is not alive, `null` is returned.
|
||||||
|
/// </summary>
|
||||||
|
[CanBeNull]
|
||||||
|
private DrawableTestHitObject getDrawableHitObject(int index)
|
||||||
|
{
|
||||||
|
var hitObject = drawableRuleset.Beatmap.HitObjects.ElementAt(index);
|
||||||
|
return (DrawableTestHitObject)drawableRuleset.Playfield.HitObjectContainer.AliveObjects.FirstOrDefault(obj => obj.HitObject == hitObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float yScale => drawableRuleset.Playfield.HitObjectContainer.DrawHeight;
|
||||||
|
|
||||||
|
private void assertDead(int index) => AddAssert($"hitobject {index} is dead", () => getDrawableHitObject(index) == null);
|
||||||
|
|
||||||
|
private void assertHeight(int index) => AddAssert($"hitobject {index} height", () =>
|
||||||
|
{
|
||||||
|
var d = getDrawableHitObject(index);
|
||||||
|
return d != null && Precision.AlmostEquals(d.DrawHeight, yScale * (float)(d.HitObject.Duration / time_range), 0.1f);
|
||||||
|
});
|
||||||
|
|
||||||
|
private void assertChildPosition(int index) => AddAssert($"hitobject {index} child position", () =>
|
||||||
|
{
|
||||||
|
var d = getDrawableHitObject(index);
|
||||||
|
return d is DrawableTestParentHitObject && Precision.AlmostEquals(
|
||||||
|
d.NestedHitObjects.First().DrawPosition.Y,
|
||||||
|
yScale * (float)((TestParentHitObject)d.HitObject).ChildTimeOffset / time_range, 0.1f);
|
||||||
|
});
|
||||||
|
|
||||||
private void assertPosition(int index, float relativeY) => AddAssert($"hitobject {index} at {relativeY}",
|
private void assertPosition(int index, float relativeY) => AddAssert($"hitobject {index} at {relativeY}",
|
||||||
() => Precision.AlmostEquals(drawableRuleset.Playfield.AllHitObjects.ElementAt(index).DrawPosition.Y, drawableRuleset.Playfield.HitObjectContainer.DrawHeight * relativeY));
|
() => Precision.AlmostEquals(getDrawableHitObject(index)?.DrawPosition.Y ?? -1, yScale * relativeY));
|
||||||
|
|
||||||
private void setTime(double time)
|
private void setTime(double time)
|
||||||
{
|
{
|
||||||
@ -160,12 +233,16 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
/// The hitobjects are spaced <see cref="time_range"/> milliseconds apart.
|
/// The hitobjects are spaced <see cref="time_range"/> milliseconds apart.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The <see cref="IBeatmap"/>.</returns>
|
/// <returns>The <see cref="IBeatmap"/>.</returns>
|
||||||
private IBeatmap createBeatmap()
|
private IBeatmap createBeatmap(Func<int, TestHitObject> createAction = null)
|
||||||
{
|
{
|
||||||
var beatmap = new Beatmap<HitObject> { BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo } };
|
var beatmap = new Beatmap<TestHitObject> { BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo } };
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
beatmap.HitObjects.Add(new HitObject { StartTime = i * time_range });
|
{
|
||||||
|
var h = createAction?.Invoke(i) ?? new TestHitObject();
|
||||||
|
h.StartTime = i * time_range;
|
||||||
|
beatmap.HitObjects.Add(h);
|
||||||
|
}
|
||||||
|
|
||||||
return beatmap;
|
return beatmap;
|
||||||
}
|
}
|
||||||
@ -225,7 +302,21 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
TimeRange.Value = time_range;
|
TimeRange.Value = time_range;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override DrawableHitObject<TestHitObject> CreateDrawableRepresentation(TestHitObject h) => new DrawableTestHitObject(h);
|
public override DrawableHitObject<TestHitObject> CreateDrawableRepresentation(TestHitObject h)
|
||||||
|
{
|
||||||
|
switch (h)
|
||||||
|
{
|
||||||
|
case TestPooledHitObject _:
|
||||||
|
case TestPooledParentHitObject _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case TestParentHitObject p:
|
||||||
|
return new DrawableTestParentHitObject(p);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return new DrawableTestHitObject(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override PassThroughInputManager CreateInputManager() => new PassThroughInputManager();
|
protected override PassThroughInputManager CreateInputManager() => new PassThroughInputManager();
|
||||||
|
|
||||||
@ -265,6 +356,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
RegisterPool<TestPooledHitObject, DrawableTestPooledHitObject>(1);
|
||||||
|
RegisterPool<TestPooledParentHitObject, DrawableTestPooledParentHitObject>(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,30 +371,46 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
public override bool CanConvert() => true;
|
public override bool CanConvert() => true;
|
||||||
|
|
||||||
protected override IEnumerable<TestHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap, CancellationToken cancellationToken)
|
protected override IEnumerable<TestHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap, CancellationToken cancellationToken) =>
|
||||||
{
|
throw new NotImplementedException();
|
||||||
yield return new TestHitObject
|
|
||||||
{
|
|
||||||
StartTime = original.StartTime,
|
|
||||||
Duration = (original as IHasDuration)?.Duration ?? 100
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region HitObject
|
#region HitObject
|
||||||
|
|
||||||
private class TestHitObject : ConvertHitObject, IHasDuration
|
private class TestHitObject : HitObject, IHasDuration
|
||||||
{
|
{
|
||||||
public double EndTime => StartTime + Duration;
|
public double EndTime => StartTime + Duration;
|
||||||
|
|
||||||
public double Duration { get; set; }
|
public double Duration { get; set; } = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestPooledHitObject : TestHitObject
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestParentHitObject : TestHitObject
|
||||||
|
{
|
||||||
|
public double ChildTimeOffset;
|
||||||
|
|
||||||
|
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
AddNested(new TestHitObject { StartTime = StartTime + ChildTimeOffset });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestPooledParentHitObject : TestParentHitObject
|
||||||
|
{
|
||||||
|
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
AddNested(new TestPooledHitObject { StartTime = StartTime + ChildTimeOffset });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DrawableTestHitObject : DrawableHitObject<TestHitObject>
|
private class DrawableTestHitObject : DrawableHitObject<TestHitObject>
|
||||||
{
|
{
|
||||||
public DrawableTestHitObject(TestHitObject hitObject)
|
public DrawableTestHitObject([CanBeNull] TestHitObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre;
|
Anchor = Anchor.TopCentre;
|
||||||
@ -324,6 +434,52 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Update() => LifetimeEnd = HitObject.EndTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DrawableTestPooledHitObject : DrawableTestHitObject
|
||||||
|
{
|
||||||
|
public DrawableTestPooledHitObject()
|
||||||
|
: base(null)
|
||||||
|
{
|
||||||
|
InternalChildren[0].Colour = Color4.LightSkyBlue;
|
||||||
|
InternalChildren[1].Colour = Color4.Blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DrawableTestParentHitObject : DrawableTestHitObject
|
||||||
|
{
|
||||||
|
private readonly Container<DrawableHitObject> container;
|
||||||
|
|
||||||
|
public DrawableTestParentHitObject([CanBeNull] TestHitObject hitObject)
|
||||||
|
: base(hitObject)
|
||||||
|
{
|
||||||
|
InternalChildren[0].Colour = Color4.LightYellow;
|
||||||
|
InternalChildren[1].Colour = Color4.Yellow;
|
||||||
|
|
||||||
|
AddInternal(container = new Container<DrawableHitObject>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) =>
|
||||||
|
new DrawableTestHitObject((TestHitObject)hitObject);
|
||||||
|
|
||||||
|
protected override void AddNestedHitObject(DrawableHitObject hitObject) => container.Add(hitObject);
|
||||||
|
|
||||||
|
protected override void ClearNestedHitObjects() => container.Clear(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DrawableTestPooledParentHitObject : DrawableTestParentHitObject
|
||||||
|
{
|
||||||
|
public DrawableTestPooledParentHitObject()
|
||||||
|
: base(null)
|
||||||
|
{
|
||||||
|
InternalChildren[0].Colour = Color4.LightSeaGreen;
|
||||||
|
InternalChildren[1].Colour = Color4.Green;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user