mirror of
https://github.com/ppy/osu.git
synced 2024-12-05 09:42:54 +08:00
Merge branch 'master' into update-loader-animation
This commit is contained in:
commit
514e72bffb
@ -93,7 +93,7 @@ JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it
|
||||
|
||||
We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention of having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time to ensure no effort is wasted.
|
||||
|
||||
If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues) (especially those with the ["good first issue"](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) label).
|
||||
If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues) (especially those with the ["good first issue"](https://github.com/ppy/osu/issues?q=is%3Aopen+label%3Agood-first-issue+sort%3Aupdated-desc) label).
|
||||
|
||||
Before starting, please make sure you are familiar with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. New component development, and where possible, bug fixing and debugging existing components **should always be done under VisualTests**.
|
||||
|
||||
|
@ -51,7 +51,7 @@
|
||||
<Reference Include="Java.Interop" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.304.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.310.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.317.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -18,7 +18,9 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(BananaShower),
|
||||
typeof(Banana),
|
||||
typeof(DrawableBananaShower),
|
||||
typeof(DrawableBanana),
|
||||
|
||||
typeof(CatchRuleset),
|
||||
typeof(DrawableCatchRuleset),
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(() => new CatcherArea.Catcher
|
||||
SetContents(() => new Catcher
|
||||
{
|
||||
RelativePositionAxes = Axes.None,
|
||||
Anchor = Anchor.Centre,
|
||||
|
@ -1,8 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
@ -10,9 +8,15 @@ using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
@ -22,11 +26,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
private RulesetInfo catchRuleset;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(CatcherArea),
|
||||
};
|
||||
|
||||
public TestSceneCatcherArea()
|
||||
{
|
||||
AddSliderStep<float>("CircleSize", 0, 8, 5, createCatcher);
|
||||
@ -34,9 +33,41 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
CreatedDrawables.OfType<CatchInputManager>().Select(i => i.Child)
|
||||
.OfType<TestCatcherArea>().ForEach(c => c.ToggleHyperDash(t)));
|
||||
|
||||
AddRepeatStep("catch fruit", () =>
|
||||
this.ChildrenOfType<CatcherArea>().ForEach(area =>
|
||||
area.MovableCatcher.PlaceOnPlate(new DrawableFruit(new TestSceneFruitObjects.TestCatchFruit(FruitVisualRepresentation.Grape)))), 20);
|
||||
AddRepeatStep("catch fruit", () => catchFruit(new TestFruit(false)
|
||||
{
|
||||
X = this.ChildrenOfType<CatcherArea>().First().MovableCatcher.X
|
||||
}), 20);
|
||||
AddRepeatStep("catch fruit last in combo", () => catchFruit(new TestFruit(false)
|
||||
{
|
||||
X = this.ChildrenOfType<CatcherArea>().First().MovableCatcher.X,
|
||||
LastInCombo = true,
|
||||
}), 20);
|
||||
AddRepeatStep("catch kiai fruit", () => catchFruit(new TestFruit(true)
|
||||
{
|
||||
X = this.ChildrenOfType<CatcherArea>().First().MovableCatcher.X,
|
||||
}), 20);
|
||||
AddRepeatStep("miss fruit", () => catchFruit(new Fruit
|
||||
{
|
||||
X = this.ChildrenOfType<CatcherArea>().First().MovableCatcher.X + 100,
|
||||
LastInCombo = true,
|
||||
}, true), 20);
|
||||
}
|
||||
|
||||
private void catchFruit(Fruit fruit, bool miss = false)
|
||||
{
|
||||
this.ChildrenOfType<CatcherArea>().ForEach(area =>
|
||||
{
|
||||
DrawableFruit drawable = new DrawableFruit(fruit);
|
||||
area.Add(drawable);
|
||||
|
||||
Schedule(() =>
|
||||
{
|
||||
area.AttemptCatch(fruit);
|
||||
area.OnResult(drawable, new JudgementResult(fruit, new CatchJudgement()) { Type = miss ? HitResult.Miss : HitResult.Great });
|
||||
|
||||
drawable.Expire();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void createCatcher(float size)
|
||||
@ -47,7 +78,8 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
Child = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size })
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.TopLeft
|
||||
Origin = Anchor.TopLeft,
|
||||
CreateDrawableRepresentation = ((DrawableRuleset<CatchHitObject>)catchRuleset.CreateInstance().CreateDrawableRulesetWith(new CatchBeatmap())).CreateDrawableRepresentation
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -58,6 +90,17 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
catchRuleset = rulesets.GetRuleset(2);
|
||||
}
|
||||
|
||||
public class TestFruit : Fruit
|
||||
{
|
||||
public TestFruit(bool kiai)
|
||||
{
|
||||
var kiaiCpi = new ControlPointInfo();
|
||||
kiaiCpi.Add(0, new EffectControlPoint { KiaiMode = kiai });
|
||||
|
||||
ApplyDefaultsToSelf(kiaiCpi, new BeatmapDifficulty());
|
||||
}
|
||||
}
|
||||
|
||||
private class TestCatcherArea : CatcherArea
|
||||
{
|
||||
public TestCatcherArea(BeatmapDifficulty beatmapDifficulty)
|
||||
|
@ -4,7 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(CatcherArea.Catcher),
|
||||
typeof(Catcher),
|
||||
typeof(DrawableCatchRuleset),
|
||||
typeof(DrawableFruit),
|
||||
typeof(DrawableJuiceStream),
|
||||
@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
private DrawableCatchRuleset drawableRuleset;
|
||||
private double playfieldTime => drawableRuleset.Playfield.Time.Current;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
{
|
||||
var controlPointInfo = new ControlPointInfo();
|
||||
controlPointInfo.Add(0, new TimingControlPoint());
|
||||
@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
ControlPointInfo = controlPointInfo
|
||||
});
|
||||
|
||||
Add(new Container
|
||||
Child = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -66,16 +66,49 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
drawableRuleset = new DrawableCatchRuleset(new CatchRuleset(), beatmap.GetPlayableBeatmap(new CatchRuleset().RulesetInfo))
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestFruits()
|
||||
{
|
||||
AddStep("hit fruits", () => spawnFruits(true));
|
||||
AddUntilStep("wait for completion", () => playfieldIsEmpty);
|
||||
AddAssert("catcher state is idle", () => catcherState == CatcherAnimationState.Idle);
|
||||
|
||||
AddStep("miss fruits", () => spawnFruits());
|
||||
AddStep("hit fruits", () => spawnFruits(true));
|
||||
AddStep("miss juicestream", () => spawnJuiceStream());
|
||||
AddStep("hit juicestream", () => spawnJuiceStream(true));
|
||||
AddStep("miss bananas", () => spawnBananas());
|
||||
AddStep("hit bananas", () => spawnBananas(true));
|
||||
AddUntilStep("wait for completion", () => playfieldIsEmpty);
|
||||
AddAssert("catcher state is failed", () => catcherState == CatcherAnimationState.Fail);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestJuicestream()
|
||||
{
|
||||
AddStep("hit juicestream", () => spawnJuiceStream(true));
|
||||
AddUntilStep("wait for completion", () => playfieldIsEmpty);
|
||||
AddAssert("catcher state is idle", () => catcherState == CatcherAnimationState.Idle);
|
||||
|
||||
AddStep("miss juicestream", () => spawnJuiceStream());
|
||||
AddUntilStep("wait for completion", () => playfieldIsEmpty);
|
||||
AddAssert("catcher state is failed", () => catcherState == CatcherAnimationState.Fail);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBananas()
|
||||
{
|
||||
AddStep("hit bananas", () => spawnBananas(true));
|
||||
AddUntilStep("wait for completion", () => playfieldIsEmpty);
|
||||
AddAssert("catcher state is idle", () => catcherState == CatcherAnimationState.Idle);
|
||||
|
||||
AddStep("miss bananas", () => spawnBananas());
|
||||
AddUntilStep("wait for completion", () => playfieldIsEmpty);
|
||||
AddAssert("catcher state is idle", () => catcherState == CatcherAnimationState.Idle);
|
||||
}
|
||||
|
||||
private bool playfieldIsEmpty => !((CatchPlayfield)drawableRuleset.Playfield).AllHitObjects.Any(h => h.IsAlive);
|
||||
|
||||
private CatcherAnimationState catcherState => ((CatchPlayfield)drawableRuleset.Playfield).CatcherArea.MovableCatcher.CurrentState;
|
||||
|
||||
private void spawnFruits(bool hit = false)
|
||||
{
|
||||
for (int i = 1; i <= 4; i++)
|
||||
|
@ -2,19 +2,27 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneHyperDash : PlayerTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(CatcherArea),
|
||||
};
|
||||
|
||||
public TestSceneHyperDash()
|
||||
: base(new CatchRuleset())
|
||||
{
|
||||
@ -26,16 +34,18 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
public void TestHyperDash()
|
||||
{
|
||||
AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash);
|
||||
AddUntilStep("wait for left hyperdash", () => getCatcher().Scale.X < 0 && getCatcher().HyperDashing);
|
||||
AddUntilStep("wait for right movement", () => getCatcher().Scale.X > 0); // don't check hyperdashing as it happens too fast.
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
AddUntilStep("wait for left movement", () => getCatcher().Scale.X < 0);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
AddUntilStep("wait for right hyperdash", () => getCatcher().Scale.X > 0 && getCatcher().HyperDashing);
|
||||
AddUntilStep("wait for left hyperdash", () => getCatcher().Scale.X < 0 && getCatcher().HyperDashing);
|
||||
}
|
||||
}
|
||||
|
||||
private CatcherArea.Catcher getCatcher() => Player.ChildrenOfType<CatcherArea>().First().MovableCatcher;
|
||||
private Catcher getCatcher() => Player.ChildrenOfType<CatcherArea>().First().MovableCatcher;
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||
{
|
||||
@ -49,39 +59,51 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
};
|
||||
|
||||
// Should produce a hyper-dash (edge case test)
|
||||
beatmap.HitObjects.Add(new Fruit { StartTime = 1816, X = 308 / 512f, NewCombo = true });
|
||||
beatmap.HitObjects.Add(new JuiceStream { StartTime = 2008, X = 56 / 512f, });
|
||||
beatmap.HitObjects.Add(new Fruit { StartTime = 1816, X = 56 / 512f, NewCombo = true });
|
||||
beatmap.HitObjects.Add(new Fruit { StartTime = 2008, X = 308 / 512f, NewCombo = true });
|
||||
|
||||
double startTime = 3000;
|
||||
|
||||
const float left_x = 0.02f;
|
||||
const float right_x = 0.98f;
|
||||
|
||||
createObjects(() => new Fruit(), left_x);
|
||||
createObjects(() => new JuiceStream(), right_x);
|
||||
createObjects(() => new JuiceStream(), left_x);
|
||||
createObjects(() => new Fruit(), right_x);
|
||||
createObjects(() => new Fruit(), left_x);
|
||||
createObjects(() => new Fruit(), right_x);
|
||||
createObjects(() => new JuiceStream(), left_x);
|
||||
createObjects(() => new Fruit { X = left_x });
|
||||
createObjects(() => new TestJuiceStream(right_x), 1);
|
||||
createObjects(() => new TestJuiceStream(left_x), 1);
|
||||
createObjects(() => new Fruit { X = right_x });
|
||||
createObjects(() => new Fruit { X = left_x });
|
||||
createObjects(() => new Fruit { X = right_x });
|
||||
createObjects(() => new TestJuiceStream(left_x), 1);
|
||||
|
||||
return beatmap;
|
||||
|
||||
void createObjects(Func<CatchHitObject> createObject, float x)
|
||||
void createObjects(Func<CatchHitObject> createObject, int count = 3)
|
||||
{
|
||||
const float spacing = 140;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var hitObject = createObject();
|
||||
hitObject.X = x;
|
||||
hitObject.StartTime = startTime + i * spacing;
|
||||
|
||||
beatmap.HitObjects.Add(hitObject);
|
||||
}
|
||||
|
||||
startTime += 700;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestJuiceStream : JuiceStream
|
||||
{
|
||||
public TestJuiceStream(float x)
|
||||
{
|
||||
X = x;
|
||||
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(Vector2.Zero),
|
||||
new PathControlPoint(new Vector2(30, 0)),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,8 +28,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
|
||||
ApplyPositionOffsets(Beatmap);
|
||||
|
||||
initialiseHyperDash((List<CatchHitObject>)Beatmap.HitObjects);
|
||||
|
||||
int index = 0;
|
||||
|
||||
foreach (var obj in Beatmap.HitObjects.OfType<CatchHitObject>())
|
||||
@ -76,6 +74,12 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
break;
|
||||
|
||||
case JuiceStream juiceStream:
|
||||
// Todo: BUG!! Stable used the last control point as the final position of the path, but it should use the computed path instead.
|
||||
lastPosition = juiceStream.X + juiceStream.Path.ControlPoints[^1].Position.Value.X / CatchPlayfield.BASE_WIDTH;
|
||||
|
||||
// Todo: BUG!! Stable attempted to use the end time of the stream, but referenced it too early in execution and used the start time instead.
|
||||
lastStartTime = juiceStream.StartTime;
|
||||
|
||||
foreach (var nested in juiceStream.NestedHitObjects)
|
||||
{
|
||||
var catchObject = (CatchHitObject)nested;
|
||||
@ -90,20 +94,12 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
initialiseHyperDash(beatmap);
|
||||
}
|
||||
|
||||
private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng)
|
||||
{
|
||||
if (hitObject is JuiceStream stream)
|
||||
{
|
||||
lastPosition = stream.EndX;
|
||||
lastStartTime = stream.EndTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(hitObject is Fruit))
|
||||
return;
|
||||
|
||||
float offsetPosition = hitObject.X;
|
||||
double startTime = hitObject.StartTime;
|
||||
|
||||
@ -116,7 +112,9 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
}
|
||||
|
||||
float positionDiff = offsetPosition - lastPosition.Value;
|
||||
double timeDiff = startTime - lastStartTime;
|
||||
|
||||
// Todo: BUG!! Stable calculated time deltas as ints, which affects randomisation. This should be changed to a double.
|
||||
int timeDiff = (int)(startTime - lastStartTime);
|
||||
|
||||
if (timeDiff > 1000)
|
||||
{
|
||||
@ -132,7 +130,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3d)
|
||||
// ReSharper disable once PossibleLossOfFraction
|
||||
if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3)
|
||||
applyOffset(ref offsetPosition, positionDiff);
|
||||
|
||||
hitObject.XOffset = offsetPosition - hitObject.X;
|
||||
@ -191,14 +190,14 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
private void initialiseHyperDash(List<CatchHitObject> objects)
|
||||
private static void initialiseHyperDash(IBeatmap beatmap)
|
||||
{
|
||||
List<CatchHitObject> objectWithDroplets = new List<CatchHitObject>();
|
||||
|
||||
foreach (var currentObject in objects)
|
||||
foreach (var currentObject in beatmap.HitObjects)
|
||||
{
|
||||
if (currentObject is Fruit)
|
||||
objectWithDroplets.Add(currentObject);
|
||||
if (currentObject is Fruit fruitObject)
|
||||
objectWithDroplets.Add(fruitObject);
|
||||
|
||||
if (currentObject is JuiceStream)
|
||||
{
|
||||
@ -212,7 +211,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
|
||||
objectWithDroplets.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime));
|
||||
|
||||
double halfCatcherWidth = CatcherArea.GetCatcherSize(Beatmap.BeatmapInfo.BaseDifficulty) / 2;
|
||||
double halfCatcherWidth = CatcherArea.GetCatcherSize(beatmap.BeatmapInfo.BaseDifficulty) / 2;
|
||||
int lastDirection = 0;
|
||||
double lastExcess = halfCatcherWidth;
|
||||
|
||||
@ -221,10 +220,14 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
CatchHitObject currentObject = objectWithDroplets[i];
|
||||
CatchHitObject nextObject = objectWithDroplets[i + 1];
|
||||
|
||||
// Reset variables in-case values have changed (e.g. after applying HR)
|
||||
currentObject.HyperDashTarget = null;
|
||||
currentObject.DistanceToHyperDash = 0;
|
||||
|
||||
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
|
||||
double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable
|
||||
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
|
||||
float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext);
|
||||
float distanceToHyper = (float)(timeToNext * Catcher.BASE_SPEED - distanceToNext);
|
||||
|
||||
if (distanceToHyper < 0)
|
||||
{
|
||||
|
@ -11,6 +11,8 @@ namespace osu.Game.Rulesets.Catch
|
||||
FruitOrange,
|
||||
FruitPear,
|
||||
Droplet,
|
||||
CatcherIdle
|
||||
CatcherIdle,
|
||||
CatcherFail,
|
||||
CatcherKiai
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
|
||||
protected override int SectionLength => 750;
|
||||
|
||||
private float halfCatcherWidth;
|
||||
|
||||
public CatchDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
{
|
||||
@ -48,14 +50,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
|
||||
{
|
||||
float halfCatchWidth;
|
||||
|
||||
using (var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty))
|
||||
{
|
||||
halfCatchWidth = catcher.CatchWidth * 0.5f;
|
||||
halfCatchWidth *= 0.8f; // We're only using 80% of the catcher's width to simulate imperfect gameplay.
|
||||
}
|
||||
|
||||
CatchHitObject lastObject = null;
|
||||
|
||||
// In 2B beatmaps, it is possible that a normal Fruit is placed in the middle of a JuiceStream.
|
||||
@ -69,16 +63,25 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
continue;
|
||||
|
||||
if (lastObject != null)
|
||||
yield return new CatchDifficultyHitObject(hitObject, lastObject, clockRate, halfCatchWidth);
|
||||
yield return new CatchDifficultyHitObject(hitObject, lastObject, clockRate, halfCatcherWidth);
|
||||
|
||||
lastObject = hitObject;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[]
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap)
|
||||
{
|
||||
new Movement(),
|
||||
};
|
||||
using (var catcher = new Catcher(beatmap.BeatmapInfo.BaseDifficulty))
|
||||
{
|
||||
halfCatcherWidth = catcher.CatchWidth * 0.5f;
|
||||
halfCatcherWidth *= 0.8f; // We're only using 80% of the catcher's width to simulate imperfect gameplay.
|
||||
}
|
||||
|
||||
return new Skill[]
|
||||
{
|
||||
new Movement(halfCatcherWidth),
|
||||
};
|
||||
}
|
||||
|
||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||
{
|
||||
|
@ -20,9 +20,16 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||
|
||||
protected override double DecayWeight => 0.94;
|
||||
|
||||
protected readonly float HalfCatcherWidth;
|
||||
|
||||
private float? lastPlayerPosition;
|
||||
private float lastDistanceMoved;
|
||||
|
||||
public Movement(float halfCatcherWidth)
|
||||
{
|
||||
HalfCatcherWidth = halfCatcherWidth;
|
||||
}
|
||||
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
{
|
||||
var catchCurrent = (CatchDifficultyHitObject)current;
|
||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
|
||||
private class MouseInputHelper : Drawable, IKeyBindingHandler<CatchAction>, IRequireHighFrequencyMousePosition
|
||||
{
|
||||
private readonly CatcherArea.Catcher catcher;
|
||||
private readonly Catcher catcher;
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Utils;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -22,6 +23,23 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
return colour ??= getBananaColour();
|
||||
}
|
||||
|
||||
protected override void UpdateInitialTransforms()
|
||||
{
|
||||
base.UpdateInitialTransforms();
|
||||
|
||||
const float end_scale = 0.6f;
|
||||
const float random_scale_range = 1.6f;
|
||||
|
||||
ScaleContainer.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RNG.NextSingle()))
|
||||
.Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt);
|
||||
|
||||
ScaleContainer.RotateTo(getRandomAngle())
|
||||
.Then()
|
||||
.RotateTo(getRandomAngle(), HitObject.TimePreempt);
|
||||
|
||||
float getRandomAngle() => 180 * (RNG.NextSingle() * 2 - 1);
|
||||
}
|
||||
|
||||
private Color4 getBananaColour()
|
||||
{
|
||||
switch (RNG.Next(0, 3))
|
||||
|
@ -91,10 +91,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss);
|
||||
}
|
||||
|
||||
protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt;
|
||||
|
||||
protected override void UpdateInitialTransforms() => this.FadeInFromZero(200);
|
||||
|
||||
protected override void UpdateStateTransforms(ArmedState state)
|
||||
{
|
||||
var endTime = HitObject.GetEndTime();
|
||||
|
@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
float startRotation = RNG.NextSingle() * 20;
|
||||
double duration = HitObject.TimePreempt + 2000;
|
||||
|
||||
this.RotateTo(startRotation).RotateTo(startRotation + 720, duration);
|
||||
ScaleContainer.RotateTo(startRotation).RotateTo(startRotation + 720, duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
public DrawableFruit(Fruit h)
|
||||
: base(h)
|
||||
{
|
||||
Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -21,6 +20,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
{
|
||||
ScaleContainer.Child = new SkinnableDrawable(
|
||||
new CatchSkinComponent(getComponent(HitObject.VisualRepresentation)), _ => new FruitPiece());
|
||||
|
||||
ScaleContainer.Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
|
||||
}
|
||||
|
||||
private CatchSkinComponents getComponent(FruitVisualRepresentation hitObjectVisualRepresentation)
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
{
|
||||
@ -14,11 +15,13 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
private readonly Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation;
|
||||
private readonly Container dropletContainer;
|
||||
|
||||
public override Vector2 OriginPosition => base.OriginPosition - new Vector2(0, CatchHitObject.OBJECT_RADIUS);
|
||||
|
||||
public DrawableJuiceStream(JuiceStream s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation = null)
|
||||
: base(s)
|
||||
{
|
||||
this.createDrawableRepresentation = createDrawableRepresentation;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Origin = Anchor.BottomLeft;
|
||||
X = 0;
|
||||
|
||||
@ -27,6 +30,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
|
||||
protected override void AddNestedHitObject(DrawableHitObject hitObject)
|
||||
{
|
||||
hitObject.Origin = Anchor.BottomCentre;
|
||||
|
||||
base.AddNestedHitObject(hitObject);
|
||||
dropletContainer.Add(hitObject);
|
||||
}
|
||||
|
@ -24,8 +24,8 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
public int RepeatCount { get; set; }
|
||||
|
||||
public double Velocity;
|
||||
public double TickDistance;
|
||||
public double Velocity { get; private set; }
|
||||
public double TickDistance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The length of one span of this <see cref="JuiceStream"/>.
|
||||
@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
base.CreateNestedHitObjects();
|
||||
|
||||
var tickSamples = Samples.Select(s => new HitSampleInfo
|
||||
var dropletSamples = Samples.Select(s => new HitSampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
@ -75,7 +75,6 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
AddNested(new TinyDroplet
|
||||
{
|
||||
Samples = tickSamples,
|
||||
StartTime = t + lastEvent.Value.Time,
|
||||
X = X + Path.PositionAt(
|
||||
lastEvent.Value.PathProgress + (t / sinceLastTick) * (e.PathProgress - lastEvent.Value.PathProgress)).X / CatchPlayfield.BASE_WIDTH,
|
||||
@ -93,7 +92,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
case SliderEventType.Tick:
|
||||
AddNested(new Droplet
|
||||
{
|
||||
Samples = tickSamples,
|
||||
Samples = dropletSamples,
|
||||
StartTime = e.Time,
|
||||
X = X + Path.PositionAt(e.PathProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
});
|
||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
public override Replay Generate()
|
||||
{
|
||||
// todo: add support for HT DT
|
||||
const double dash_speed = CatcherArea.Catcher.BASE_SPEED;
|
||||
const double dash_speed = Catcher.BASE_SPEED;
|
||||
const double movement_speed = dash_speed / 2;
|
||||
float lastPosition = 0.5f;
|
||||
double lastTime = 0;
|
||||
|
@ -48,6 +48,14 @@ namespace osu.Game.Rulesets.Catch.Skinning
|
||||
case CatchSkinComponents.CatcherIdle:
|
||||
return this.GetAnimation("fruit-catcher-idle", true, true, true) ??
|
||||
this.GetAnimation("fruit-ryuuta", true, true, true);
|
||||
|
||||
case CatchSkinComponents.CatcherFail:
|
||||
return this.GetAnimation("fruit-catcher-fail", true, true, true) ??
|
||||
this.GetAnimation("fruit-ryuuta", true, true, true);
|
||||
|
||||
case CatchSkinComponents.CatcherKiai:
|
||||
return this.GetAnimation("fruit-catcher-kiai", true, true, true) ??
|
||||
this.GetAnimation("fruit-ryuuta", true, true, true);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
458
osu.Game.Rulesets.Catch/UI/Catcher.cs
Normal file
458
osu.Game.Rulesets.Catch/UI/Catcher.cs
Normal file
@ -0,0 +1,458 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Animations;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class Catcher : Container, IKeyBindingHandler<CatchAction>
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether we are hyper-dashing or not.
|
||||
/// </summary>
|
||||
public bool HyperDashing => hyperDashModifier != 1;
|
||||
|
||||
/// <summary>
|
||||
/// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable.
|
||||
/// </summary>
|
||||
public const double BASE_SPEED = 1.0 / 512;
|
||||
|
||||
public Container ExplodingFruitTarget;
|
||||
|
||||
public Container AdditiveTarget;
|
||||
|
||||
public CatcherAnimationState CurrentState { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Width of the area that can be used to attempt catches during gameplay.
|
||||
/// </summary>
|
||||
internal float CatchWidth => CatcherArea.CATCHER_SIZE * Math.Abs(Scale.X);
|
||||
|
||||
protected bool Dashing
|
||||
{
|
||||
get => dashing;
|
||||
set
|
||||
{
|
||||
if (value == dashing) return;
|
||||
|
||||
dashing = value;
|
||||
|
||||
Trail |= dashing;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activate or deactivate the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met.
|
||||
/// </summary>
|
||||
protected bool Trail
|
||||
{
|
||||
get => trail;
|
||||
set
|
||||
{
|
||||
if (value == trail || AdditiveTarget == null) return;
|
||||
|
||||
trail = value;
|
||||
|
||||
if (Trail)
|
||||
beginTrail();
|
||||
}
|
||||
}
|
||||
|
||||
private Container<DrawableHitObject> caughtFruit;
|
||||
|
||||
private CatcherSprite catcherIdle;
|
||||
private CatcherSprite catcherKiai;
|
||||
private CatcherSprite catcherFail;
|
||||
|
||||
private CatcherSprite currentCatcher;
|
||||
|
||||
private int currentDirection;
|
||||
|
||||
private bool dashing;
|
||||
|
||||
private bool trail;
|
||||
|
||||
private double hyperDashModifier = 1;
|
||||
private int hyperDashDirection;
|
||||
private float hyperDashTargetPosition;
|
||||
|
||||
public Catcher(BeatmapDifficulty difficulty = null)
|
||||
{
|
||||
RelativePositionAxes = Axes.X;
|
||||
X = 0.5f;
|
||||
|
||||
Origin = Anchor.TopCentre;
|
||||
|
||||
Size = new Vector2(CatcherArea.CATCHER_SIZE);
|
||||
if (difficulty != null)
|
||||
Scale = new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
caughtFruit = new Container<DrawableHitObject>
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
},
|
||||
catcherIdle = new CatcherSprite(CatcherAnimationState.Idle)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Alpha = 0,
|
||||
},
|
||||
catcherKiai = new CatcherSprite(CatcherAnimationState.Kiai)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Alpha = 0,
|
||||
},
|
||||
catcherFail = new CatcherSprite(CatcherAnimationState.Fail)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Alpha = 0,
|
||||
}
|
||||
};
|
||||
|
||||
updateCatcher();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a caught fruit to the catcher's stack.
|
||||
/// </summary>
|
||||
/// <param name="fruit">The fruit that was caught.</param>
|
||||
public void PlaceOnPlate(DrawableCatchHitObject fruit)
|
||||
{
|
||||
var ourRadius = fruit.DisplayRadius;
|
||||
float theirRadius = 0;
|
||||
|
||||
const float allowance = 6;
|
||||
|
||||
while (caughtFruit.Any(f =>
|
||||
f.LifetimeEnd == double.MaxValue &&
|
||||
Vector2Extensions.Distance(f.Position, fruit.Position) < (ourRadius + (theirRadius = f.DrawSize.X / 2 * f.Scale.X)) / (allowance / 2)))
|
||||
{
|
||||
var diff = (ourRadius + theirRadius) / allowance;
|
||||
fruit.X += (RNG.NextSingle() - 0.5f) * 2 * diff;
|
||||
fruit.Y -= RNG.NextSingle() * diff;
|
||||
}
|
||||
|
||||
fruit.X = Math.Clamp(fruit.X, -CatcherArea.CATCHER_SIZE / 2, CatcherArea.CATCHER_SIZE / 2);
|
||||
|
||||
caughtFruit.Add(fruit);
|
||||
|
||||
Add(new HitExplosion(fruit)
|
||||
{
|
||||
X = fruit.X,
|
||||
Scale = new Vector2(fruit.HitObject.Scale)
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Let the catcher attempt to catch a fruit.
|
||||
/// </summary>
|
||||
/// <param name="fruit">The fruit to catch.</param>
|
||||
/// <returns>Whether the catch is possible.</returns>
|
||||
public bool AttemptCatch(CatchHitObject fruit)
|
||||
{
|
||||
var halfCatchWidth = CatchWidth * 0.5f;
|
||||
|
||||
// this stuff wil disappear once we move fruit to non-relative coordinate space in the future.
|
||||
var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH;
|
||||
var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH;
|
||||
|
||||
var validCatch =
|
||||
catchObjectPosition >= catcherPosition - halfCatchWidth &&
|
||||
catchObjectPosition <= catcherPosition + halfCatchWidth;
|
||||
|
||||
// only update hyperdash state if we are catching a fruit.
|
||||
// exceptions are Droplets and JuiceStreams.
|
||||
if (!(fruit is Fruit)) return validCatch;
|
||||
|
||||
if (validCatch && fruit.HyperDash)
|
||||
{
|
||||
var target = fruit.HyperDashTarget;
|
||||
var timeDifference = target.StartTime - fruit.StartTime;
|
||||
double positionDifference = target.X * CatchPlayfield.BASE_WIDTH - catcherPosition;
|
||||
var velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0);
|
||||
|
||||
SetHyperDashState(Math.Abs(velocity), target.X);
|
||||
}
|
||||
else
|
||||
SetHyperDashState();
|
||||
|
||||
if (validCatch)
|
||||
updateState(fruit.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle);
|
||||
else if (!(fruit is Banana))
|
||||
updateState(CatcherAnimationState.Fail);
|
||||
|
||||
return validCatch;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set hyper-dash state.
|
||||
/// </summary>
|
||||
/// <param name="modifier">The speed multiplier. If this is less or equals to 1, this catcher will be non-hyper-dashing state.</param>
|
||||
/// <param name="targetPosition">When this catcher crosses this position, this catcher ends hyper-dashing.</param>
|
||||
public void SetHyperDashState(double modifier = 1, float targetPosition = -1)
|
||||
{
|
||||
const float hyper_dash_transition_length = 180;
|
||||
|
||||
var wasHyperDashing = HyperDashing;
|
||||
|
||||
if (modifier <= 1 || X == targetPosition)
|
||||
{
|
||||
hyperDashModifier = 1;
|
||||
hyperDashDirection = 0;
|
||||
|
||||
if (wasHyperDashing)
|
||||
{
|
||||
this.FadeColour(Color4.White, hyper_dash_transition_length, Easing.OutQuint);
|
||||
this.FadeTo(1, hyper_dash_transition_length, Easing.OutQuint);
|
||||
Trail &= Dashing;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hyperDashModifier = modifier;
|
||||
hyperDashDirection = Math.Sign(targetPosition - X);
|
||||
hyperDashTargetPosition = targetPosition;
|
||||
|
||||
if (!wasHyperDashing)
|
||||
{
|
||||
this.FadeColour(Color4.OrangeRed, hyper_dash_transition_length, Easing.OutQuint);
|
||||
this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint);
|
||||
Trail = true;
|
||||
|
||||
var hyperDashEndGlow = createAdditiveSprite();
|
||||
|
||||
hyperDashEndGlow.MoveToOffset(new Vector2(0, -10), 1200, Easing.In);
|
||||
hyperDashEndGlow.ScaleTo(hyperDashEndGlow.Scale * 0.95f).ScaleTo(hyperDashEndGlow.Scale * 1.2f, 1200, Easing.In);
|
||||
hyperDashEndGlow.FadeOut(1200);
|
||||
hyperDashEndGlow.Expire(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection--;
|
||||
return true;
|
||||
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection++;
|
||||
return true;
|
||||
|
||||
case CatchAction.Dash:
|
||||
Dashing = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection++;
|
||||
break;
|
||||
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection--;
|
||||
break;
|
||||
|
||||
case CatchAction.Dash:
|
||||
Dashing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePosition(float position)
|
||||
{
|
||||
position = Math.Clamp(position, 0, 1);
|
||||
|
||||
if (position == X)
|
||||
return;
|
||||
|
||||
Scale = new Vector2(Math.Abs(Scale.X) * (position > X ? 1 : -1), Scale.Y);
|
||||
X = position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drop any fruit off the plate.
|
||||
/// </summary>
|
||||
public void Drop()
|
||||
{
|
||||
foreach (var f in caughtFruit.ToArray())
|
||||
Drop(f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explode any fruit off the plate.
|
||||
/// </summary>
|
||||
public void Explode()
|
||||
{
|
||||
foreach (var f in caughtFruit.ToArray())
|
||||
Explode(f);
|
||||
}
|
||||
|
||||
public void Drop(DrawableHitObject fruit)
|
||||
{
|
||||
removeFromPlateWithTransform(fruit, f =>
|
||||
{
|
||||
f.MoveToY(f.Y + 75, 750, Easing.InSine);
|
||||
f.FadeOut(750);
|
||||
});
|
||||
}
|
||||
|
||||
public void Explode(DrawableHitObject fruit)
|
||||
{
|
||||
var originalX = fruit.X * Scale.X;
|
||||
|
||||
removeFromPlateWithTransform(fruit, f =>
|
||||
{
|
||||
f.MoveToY(f.Y - 50, 250, Easing.OutSine).Then().MoveToY(f.Y + 50, 500, Easing.InSine);
|
||||
f.MoveToX(f.X + originalX * 6, 1000);
|
||||
f.FadeOut(750);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (currentDirection == 0) return;
|
||||
|
||||
var direction = Math.Sign(currentDirection);
|
||||
|
||||
var dashModifier = Dashing ? 1 : 0.5;
|
||||
var speed = BASE_SPEED * dashModifier * hyperDashModifier;
|
||||
|
||||
UpdatePosition((float)(X + direction * Clock.ElapsedFrameTime * speed));
|
||||
|
||||
// Correct overshooting.
|
||||
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
||||
(hyperDashDirection < 0 && hyperDashTargetPosition > X))
|
||||
{
|
||||
X = hyperDashTargetPosition;
|
||||
SetHyperDashState();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCatcher()
|
||||
{
|
||||
currentCatcher?.Hide();
|
||||
|
||||
switch (CurrentState)
|
||||
{
|
||||
default:
|
||||
currentCatcher = catcherIdle;
|
||||
break;
|
||||
|
||||
case CatcherAnimationState.Fail:
|
||||
currentCatcher = catcherFail;
|
||||
break;
|
||||
|
||||
case CatcherAnimationState.Kiai:
|
||||
currentCatcher = catcherKiai;
|
||||
break;
|
||||
}
|
||||
|
||||
currentCatcher.Show();
|
||||
(currentCatcher.Drawable as IAnimation)?.GotoFrame(0);
|
||||
}
|
||||
|
||||
private void beginTrail()
|
||||
{
|
||||
if (!dashing && !HyperDashing)
|
||||
{
|
||||
Trail = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var additive = createAdditiveSprite();
|
||||
|
||||
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint);
|
||||
additive.Expire(true);
|
||||
|
||||
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
|
||||
}
|
||||
|
||||
private void updateState(CatcherAnimationState state)
|
||||
{
|
||||
if (CurrentState == state)
|
||||
return;
|
||||
|
||||
CurrentState = state;
|
||||
updateCatcher();
|
||||
}
|
||||
|
||||
private CatcherTrailSprite createAdditiveSprite()
|
||||
{
|
||||
var tex = (currentCatcher.Drawable as TextureAnimation)?.CurrentFrame ?? ((Sprite)currentCatcher.Drawable).Texture;
|
||||
|
||||
var sprite = new CatcherTrailSprite(tex)
|
||||
{
|
||||
Anchor = Anchor,
|
||||
Scale = Scale,
|
||||
Colour = HyperDashing ? Color4.Red : Color4.White,
|
||||
Blending = BlendingParameters.Additive,
|
||||
RelativePositionAxes = RelativePositionAxes,
|
||||
Position = Position
|
||||
};
|
||||
|
||||
AdditiveTarget?.Add(sprite);
|
||||
|
||||
return sprite;
|
||||
}
|
||||
|
||||
private void removeFromPlateWithTransform(DrawableHitObject fruit, Action<DrawableHitObject> action)
|
||||
{
|
||||
if (ExplodingFruitTarget != null)
|
||||
{
|
||||
fruit.Anchor = Anchor.TopLeft;
|
||||
fruit.Position = caughtFruit.ToSpaceOfOtherDrawable(fruit.DrawPosition, ExplodingFruitTarget);
|
||||
|
||||
if (!caughtFruit.Remove(fruit))
|
||||
// we may have already been removed by a previous operation (due to the weird OnLoadComplete scheduling).
|
||||
// this avoids a crash on potentially attempting to Add a fruit to ExplodingFruitTarget twice.
|
||||
return;
|
||||
|
||||
ExplodingFruitTarget.Add(fruit);
|
||||
}
|
||||
|
||||
var actionTime = Clock.CurrentTime;
|
||||
|
||||
fruit.ApplyCustomUpdateState += onFruitOnApplyCustomUpdateState;
|
||||
onFruitOnApplyCustomUpdateState(fruit, fruit.State.Value);
|
||||
|
||||
void onFruitOnApplyCustomUpdateState(DrawableHitObject o, ArmedState state)
|
||||
{
|
||||
using (fruit.BeginAbsoluteSequence(actionTime))
|
||||
action(fruit);
|
||||
|
||||
fruit.Expire();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
osu.Game.Rulesets.Catch/UI/CatcherAnimationState.cs
Normal file
12
osu.Game.Rulesets.Catch/UI/CatcherAnimationState.cs
Normal file
@ -0,0 +1,12 @@
|
||||
// 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.UI
|
||||
{
|
||||
public enum CatcherAnimationState
|
||||
{
|
||||
Idle,
|
||||
Fail,
|
||||
Kiai
|
||||
}
|
||||
}
|
@ -2,14 +2,8 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
@ -19,7 +13,6 @@ using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
@ -27,8 +20,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public const float CATCHER_SIZE = 106.75f;
|
||||
|
||||
protected internal readonly Catcher MovableCatcher;
|
||||
|
||||
public Func<CatchHitObject, DrawableHitObject<CatchHitObject>> CreateDrawableRepresentation;
|
||||
|
||||
public Container ExplodingFruitTarget
|
||||
@ -36,6 +27,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
set => MovableCatcher.ExplodingFruitTarget = value;
|
||||
}
|
||||
|
||||
private DrawableCatchHitObject lastPlateableFruit;
|
||||
|
||||
public CatcherArea(BeatmapDifficulty difficulty = null)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
@ -46,7 +39,10 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
};
|
||||
}
|
||||
|
||||
private DrawableCatchHitObject lastPlateableFruit;
|
||||
public static float GetCatcherSize(BeatmapDifficulty difficulty)
|
||||
{
|
||||
return CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
|
||||
}
|
||||
|
||||
public void OnResult(DrawableCatchHitObject fruit, JudgementResult result)
|
||||
{
|
||||
@ -99,6 +95,15 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReleased(CatchAction action)
|
||||
{
|
||||
}
|
||||
|
||||
public bool AttemptCatch(CatchHitObject obj)
|
||||
{
|
||||
return MovableCatcher.AttemptCatch(obj);
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
@ -109,485 +114,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
MovableCatcher.X = state.CatcherX.Value;
|
||||
}
|
||||
|
||||
public void OnReleased(CatchAction action)
|
||||
{
|
||||
}
|
||||
|
||||
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
|
||||
|
||||
public static float GetCatcherSize(BeatmapDifficulty difficulty)
|
||||
{
|
||||
return CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
|
||||
}
|
||||
|
||||
public class Catcher : Container, IKeyBindingHandler<CatchAction>
|
||||
{
|
||||
/// <summary>
|
||||
/// Width of the area that can be used to attempt catches during gameplay.
|
||||
/// </summary>
|
||||
internal float CatchWidth => CATCHER_SIZE * Math.Abs(Scale.X);
|
||||
|
||||
private Container<DrawableHitObject> caughtFruit;
|
||||
|
||||
public Container ExplodingFruitTarget;
|
||||
|
||||
public Container AdditiveTarget;
|
||||
|
||||
public Catcher(BeatmapDifficulty difficulty = null)
|
||||
{
|
||||
RelativePositionAxes = Axes.X;
|
||||
X = 0.5f;
|
||||
|
||||
Origin = Anchor.TopCentre;
|
||||
|
||||
Size = new Vector2(CATCHER_SIZE);
|
||||
if (difficulty != null)
|
||||
Scale = new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new[]
|
||||
{
|
||||
caughtFruit = new Container<DrawableHitObject>
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
},
|
||||
createCatcherSprite().With(c =>
|
||||
{
|
||||
c.Anchor = Anchor.TopCentre;
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
private int currentDirection;
|
||||
|
||||
private bool dashing;
|
||||
|
||||
protected bool Dashing
|
||||
{
|
||||
get => dashing;
|
||||
set
|
||||
{
|
||||
if (value == dashing) return;
|
||||
|
||||
dashing = value;
|
||||
|
||||
Trail |= dashing;
|
||||
}
|
||||
}
|
||||
|
||||
private bool trail;
|
||||
|
||||
/// <summary>
|
||||
/// Activate or deactive the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met.
|
||||
/// </summary>
|
||||
protected bool Trail
|
||||
{
|
||||
get => trail;
|
||||
set
|
||||
{
|
||||
if (value == trail) return;
|
||||
|
||||
trail = value;
|
||||
|
||||
if (Trail)
|
||||
beginTrail();
|
||||
}
|
||||
}
|
||||
|
||||
private void beginTrail()
|
||||
{
|
||||
Trail &= dashing || HyperDashing;
|
||||
Trail &= AdditiveTarget != null;
|
||||
|
||||
if (!Trail) return;
|
||||
|
||||
var additive = createCatcherSprite();
|
||||
|
||||
additive.Anchor = Anchor;
|
||||
additive.Scale = Scale;
|
||||
additive.Colour = HyperDashing ? Color4.Red : Color4.White;
|
||||
additive.Blending = BlendingParameters.Additive;
|
||||
additive.RelativePositionAxes = RelativePositionAxes;
|
||||
additive.Position = Position;
|
||||
|
||||
AdditiveTarget.Add(additive);
|
||||
|
||||
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint);
|
||||
additive.Expire(true);
|
||||
|
||||
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
|
||||
}
|
||||
|
||||
private Drawable createCatcherSprite() => new CatcherSprite();
|
||||
|
||||
/// <summary>
|
||||
/// Add a caught fruit to the catcher's stack.
|
||||
/// </summary>
|
||||
/// <param name="fruit">The fruit that was caught.</param>
|
||||
public void PlaceOnPlate(DrawableCatchHitObject fruit)
|
||||
{
|
||||
float ourRadius = fruit.DisplayRadius;
|
||||
float theirRadius = 0;
|
||||
|
||||
const float allowance = 6;
|
||||
|
||||
while (caughtFruit.Any(f =>
|
||||
f.LifetimeEnd == double.MaxValue &&
|
||||
Vector2Extensions.Distance(f.Position, fruit.Position) < (ourRadius + (theirRadius = f.DrawSize.X / 2 * f.Scale.X)) / (allowance / 2)))
|
||||
{
|
||||
float diff = (ourRadius + theirRadius) / allowance;
|
||||
fruit.X += (RNG.NextSingle() - 0.5f) * 2 * diff;
|
||||
fruit.Y -= RNG.NextSingle() * diff;
|
||||
}
|
||||
|
||||
fruit.X = Math.Clamp(fruit.X, -CATCHER_SIZE / 2, CATCHER_SIZE / 2);
|
||||
|
||||
caughtFruit.Add(fruit);
|
||||
|
||||
Add(new HitExplosion(fruit)
|
||||
{
|
||||
X = fruit.X,
|
||||
Scale = new Vector2(fruit.HitObject.Scale)
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Let the catcher attempt to catch a fruit.
|
||||
/// </summary>
|
||||
/// <param name="fruit">The fruit to catch.</param>
|
||||
/// <returns>Whether the catch is possible.</returns>
|
||||
public bool AttemptCatch(CatchHitObject fruit)
|
||||
{
|
||||
float halfCatchWidth = CatchWidth * 0.5f;
|
||||
|
||||
// this stuff wil disappear once we move fruit to non-relative coordinate space in the future.
|
||||
var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH;
|
||||
var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH;
|
||||
|
||||
var validCatch =
|
||||
catchObjectPosition >= catcherPosition - halfCatchWidth &&
|
||||
catchObjectPosition <= catcherPosition + halfCatchWidth;
|
||||
|
||||
// only update hyperdash state if we are catching a fruit.
|
||||
// exceptions are Droplets and JuiceStreams.
|
||||
if (!(fruit is Fruit)) return validCatch;
|
||||
|
||||
if (validCatch && fruit.HyperDash)
|
||||
{
|
||||
var target = fruit.HyperDashTarget;
|
||||
double timeDifference = target.StartTime - fruit.StartTime;
|
||||
double positionDifference = target.X * CatchPlayfield.BASE_WIDTH - catcherPosition;
|
||||
double velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0);
|
||||
|
||||
SetHyperDashState(Math.Abs(velocity), target.X);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetHyperDashState();
|
||||
}
|
||||
|
||||
return validCatch;
|
||||
}
|
||||
|
||||
private double hyperDashModifier = 1;
|
||||
private int hyperDashDirection;
|
||||
private float hyperDashTargetPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Whether we are hyper-dashing or not.
|
||||
/// </summary>
|
||||
public bool HyperDashing => hyperDashModifier != 1;
|
||||
|
||||
/// <summary>
|
||||
/// Set hyper-dash state.
|
||||
/// </summary>
|
||||
/// <param name="modifier">The speed multiplier. If this is less or equals to 1, this catcher will be non-hyper-dashing state.</param>
|
||||
/// <param name="targetPosition">When this catcher crosses this position, this catcher ends hyper-dashing.</param>
|
||||
public void SetHyperDashState(double modifier = 1, float targetPosition = -1)
|
||||
{
|
||||
const float hyper_dash_transition_length = 180;
|
||||
|
||||
bool previouslyHyperDashing = HyperDashing;
|
||||
|
||||
if (modifier <= 1 || X == targetPosition)
|
||||
{
|
||||
hyperDashModifier = 1;
|
||||
hyperDashDirection = 0;
|
||||
|
||||
if (previouslyHyperDashing)
|
||||
{
|
||||
this.FadeColour(Color4.White, hyper_dash_transition_length, Easing.OutQuint);
|
||||
this.FadeTo(1, hyper_dash_transition_length, Easing.OutQuint);
|
||||
Trail &= Dashing;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hyperDashModifier = modifier;
|
||||
hyperDashDirection = Math.Sign(targetPosition - X);
|
||||
hyperDashTargetPosition = targetPosition;
|
||||
|
||||
if (!previouslyHyperDashing)
|
||||
{
|
||||
this.FadeColour(Color4.OrangeRed, hyper_dash_transition_length, Easing.OutQuint);
|
||||
this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint);
|
||||
Trail = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection--;
|
||||
return true;
|
||||
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection++;
|
||||
return true;
|
||||
|
||||
case CatchAction.Dash:
|
||||
Dashing = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection++;
|
||||
break;
|
||||
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection--;
|
||||
break;
|
||||
|
||||
case CatchAction.Dash:
|
||||
Dashing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable.
|
||||
/// </summary>
|
||||
public const double BASE_SPEED = 1.0 / 512;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (currentDirection == 0) return;
|
||||
|
||||
var direction = Math.Sign(currentDirection);
|
||||
|
||||
double dashModifier = Dashing ? 1 : 0.5;
|
||||
double speed = BASE_SPEED * dashModifier * hyperDashModifier;
|
||||
|
||||
UpdatePosition((float)(X + direction * Clock.ElapsedFrameTime * speed));
|
||||
|
||||
// Correct overshooting.
|
||||
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
||||
(hyperDashDirection < 0 && hyperDashTargetPosition > X))
|
||||
{
|
||||
X = hyperDashTargetPosition;
|
||||
SetHyperDashState();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePosition(float position)
|
||||
{
|
||||
position = Math.Clamp(position, 0, 1);
|
||||
|
||||
if (position == X)
|
||||
return;
|
||||
|
||||
Scale = new Vector2(Math.Abs(Scale.X) * (position > X ? 1 : -1), Scale.Y);
|
||||
X = position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drop any fruit off the plate.
|
||||
/// </summary>
|
||||
public void Drop()
|
||||
{
|
||||
foreach (var f in caughtFruit.ToArray())
|
||||
Drop(f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explode any fruit off the plate.
|
||||
/// </summary>
|
||||
public void Explode()
|
||||
{
|
||||
foreach (var f in caughtFruit.ToArray())
|
||||
Explode(f);
|
||||
}
|
||||
|
||||
public void Drop(DrawableHitObject fruit) => removeFromPlateWithTransform(fruit, f =>
|
||||
{
|
||||
f.MoveToY(f.Y + 75, 750, Easing.InSine);
|
||||
f.FadeOut(750);
|
||||
});
|
||||
|
||||
public void Explode(DrawableHitObject fruit)
|
||||
{
|
||||
var originalX = fruit.X * Scale.X;
|
||||
|
||||
removeFromPlateWithTransform(fruit, f =>
|
||||
{
|
||||
f.MoveToY(f.Y - 50, 250, Easing.OutSine).Then().MoveToY(f.Y + 50, 500, Easing.InSine);
|
||||
f.MoveToX(f.X + originalX * 6, 1000);
|
||||
f.FadeOut(750);
|
||||
});
|
||||
}
|
||||
|
||||
private void removeFromPlateWithTransform(DrawableHitObject fruit, Action<DrawableHitObject> action)
|
||||
{
|
||||
if (ExplodingFruitTarget != null)
|
||||
{
|
||||
fruit.Anchor = Anchor.TopLeft;
|
||||
fruit.Position = caughtFruit.ToSpaceOfOtherDrawable(fruit.DrawPosition, ExplodingFruitTarget);
|
||||
|
||||
if (!caughtFruit.Remove(fruit))
|
||||
// we may have already been removed by a previous operation (due to the weird OnLoadComplete scheduling).
|
||||
// this avoids a crash on potentially attempting to Add a fruit to ExplodingFruitTarget twice.
|
||||
return;
|
||||
|
||||
ExplodingFruitTarget.Add(fruit);
|
||||
}
|
||||
|
||||
double actionTime = Clock.CurrentTime;
|
||||
|
||||
fruit.ApplyCustomUpdateState += onFruitOnApplyCustomUpdateState;
|
||||
onFruitOnApplyCustomUpdateState(fruit, fruit.State.Value);
|
||||
|
||||
void onFruitOnApplyCustomUpdateState(DrawableHitObject o, ArmedState state)
|
||||
{
|
||||
using (fruit.BeginAbsoluteSequence(actionTime))
|
||||
action(fruit);
|
||||
|
||||
fruit.Expire();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class HitExplosion : CompositeDrawable
|
||||
{
|
||||
private readonly CircularContainer largeFaint;
|
||||
|
||||
public HitExplosion(DrawableCatchHitObject fruit)
|
||||
{
|
||||
Size = new Vector2(20);
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.BottomCentre;
|
||||
|
||||
Color4 objectColour = fruit.AccentColour.Value;
|
||||
|
||||
// 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;
|
||||
|
||||
var colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1);
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
largeFaint = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
// we want our size to be very small so the glow dominates it.
|
||||
Size = new Vector2(0.8f),
|
||||
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
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
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
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Size = new Vector2(0.01f, initial_height),
|
||||
Blending = BlendingParameters.Additive,
|
||||
Rotation = RNG.NextSingle(-angle_variangle, angle_variangle),
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = colour,
|
||||
Roundness = roundness,
|
||||
Radius = 40,
|
||||
},
|
||||
},
|
||||
new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Size = new Vector2(0.01f, initial_height),
|
||||
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()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
const double duration = 400;
|
||||
|
||||
largeFaint
|
||||
.ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint)
|
||||
.FadeOut(duration * 2);
|
||||
|
||||
this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out);
|
||||
Expire(true);
|
||||
}
|
||||
protected internal readonly Catcher MovableCatcher;
|
||||
}
|
||||
}
|
||||
|
@ -14,9 +14,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
protected override bool ApplySizeRestrictionsToDefault => true;
|
||||
|
||||
public CatcherSprite()
|
||||
: base(new CatchSkinComponent(CatchSkinComponents.CatcherIdle), _ =>
|
||||
new DefaultCatcherSprite(), confineMode: ConfineMode.ScaleDownToFit)
|
||||
public CatcherSprite(CatcherAnimationState state)
|
||||
: base(new CatchSkinComponent(componentFromState(state)), _ =>
|
||||
new DefaultCatcherSprite(state), confineMode: ConfineMode.ScaleDownToFit)
|
||||
{
|
||||
RelativeSizeAxes = Axes.None;
|
||||
Size = new Vector2(CatcherArea.CATCHER_SIZE);
|
||||
@ -25,12 +25,34 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE;
|
||||
}
|
||||
|
||||
private static CatchSkinComponents componentFromState(CatcherAnimationState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case CatcherAnimationState.Fail:
|
||||
return CatchSkinComponents.CatcherFail;
|
||||
|
||||
case CatcherAnimationState.Kiai:
|
||||
return CatchSkinComponents.CatcherKiai;
|
||||
|
||||
default:
|
||||
return CatchSkinComponents.CatcherIdle;
|
||||
}
|
||||
}
|
||||
|
||||
private class DefaultCatcherSprite : Sprite
|
||||
{
|
||||
private readonly CatcherAnimationState state;
|
||||
|
||||
public DefaultCatcherSprite(CatcherAnimationState state)
|
||||
{
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
Texture = textures.Get("Gameplay/catch/fruit-catcher-idle");
|
||||
Texture = textures.Get($"Gameplay/catch/fruit-catcher-{state.ToString().ToLower()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
22
osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs
Normal file
22
osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs
Normal file
@ -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.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class CatcherTrailSprite : Sprite
|
||||
{
|
||||
public CatcherTrailSprite(Texture texture)
|
||||
{
|
||||
Texture = texture;
|
||||
|
||||
Size = new Vector2(CatcherArea.CATCHER_SIZE);
|
||||
|
||||
// Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling.
|
||||
OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
122
osu.Game.Rulesets.Catch/UI/HitExplosion.cs
Normal file
122
osu.Game.Rulesets.Catch/UI/HitExplosion.cs
Normal file
@ -0,0 +1,122 @@
|
||||
// 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.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class HitExplosion : CompositeDrawable
|
||||
{
|
||||
private readonly CircularContainer largeFaint;
|
||||
|
||||
public HitExplosion(DrawableCatchHitObject fruit)
|
||||
{
|
||||
Size = new Vector2(20);
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.BottomCentre;
|
||||
|
||||
Color4 objectColour = fruit.AccentColour.Value;
|
||||
|
||||
// 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;
|
||||
|
||||
var colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1);
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
largeFaint = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
// we want our size to be very small so the glow dominates it.
|
||||
Size = new Vector2(0.8f),
|
||||
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
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
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
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Size = new Vector2(0.01f, initial_height),
|
||||
Blending = BlendingParameters.Additive,
|
||||
Rotation = RNG.NextSingle(-angle_variangle, angle_variangle),
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = colour,
|
||||
Roundness = roundness,
|
||||
Radius = 40,
|
||||
},
|
||||
},
|
||||
new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Size = new Vector2(0.01f, initial_height),
|
||||
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()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
const double duration = 400;
|
||||
|
||||
largeFaint
|
||||
.ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint)
|
||||
.FadeOut(duration * 2);
|
||||
|
||||
this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out);
|
||||
Expire(true);
|
||||
}
|
||||
}
|
||||
}
|
@ -36,8 +36,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
private readonly SpriteIcon symbol;
|
||||
|
||||
private readonly Color4 baseColour = OsuColour.FromHex(@"002c3c");
|
||||
private readonly Color4 fillColour = OsuColour.FromHex(@"005b7c");
|
||||
private readonly Color4 baseColour = Color4Extensions.FromHex(@"002c3c");
|
||||
private readonly Color4 fillColour = Color4Extensions.FromHex(@"005b7c");
|
||||
|
||||
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
|
||||
|
||||
|
@ -16,16 +16,24 @@ using osu.Game.Rulesets.Osu.Skinning;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
public class SliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition
|
||||
public class SliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition, IHasAccentColour
|
||||
{
|
||||
public Func<OsuAction?> GetInitialHitAction;
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get => ball.Colour;
|
||||
set => ball.Colour = value;
|
||||
}
|
||||
|
||||
private readonly Slider slider;
|
||||
private readonly Drawable followCircle;
|
||||
private readonly DrawableSlider drawableSlider;
|
||||
private readonly CircularContainer ball;
|
||||
|
||||
public SliderBall(Slider slider, DrawableSlider drawableSlider = null)
|
||||
{
|
||||
@ -47,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
Alpha = 0,
|
||||
Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderFollowCircle), _ => new DefaultFollowCircle()),
|
||||
},
|
||||
new CircularContainer
|
||||
ball = new CircularContainer
|
||||
{
|
||||
Masking = true,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
|
@ -417,7 +417,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
[Test]
|
||||
public async Task TestImportWithDuplicateHashes()
|
||||
{
|
||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportNestedStructure)))
|
||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWithDuplicateHashes)))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -27,6 +27,7 @@ using osu.Game.Screens;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.PlayerSettings;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Users;
|
||||
@ -203,7 +204,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the visual settings container removes user dim when suspending <see cref="Player"/> for <see cref="SoloResults"/>
|
||||
/// Check if the visual settings container removes user dim when suspending <see cref="Player"/> for <see cref="ResultsScreen"/>
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TransitionTest()
|
||||
@ -335,7 +336,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
public bool IsBackgroundCurrent() => ((FadeAccessibleBackground)Background).IsCurrentScreen();
|
||||
}
|
||||
|
||||
private class FadeAccessibleResults : SoloResults
|
||||
private class FadeAccessibleResults : ResultsScreen
|
||||
{
|
||||
public FadeAccessibleResults(ScoreInfo score)
|
||||
: base(score)
|
||||
|
@ -11,7 +11,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Ranking.Pages;
|
||||
using osu.Game.Screens.Ranking;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
API.LocalUser.Value = new User
|
||||
{
|
||||
Username = API.LocalUser.Value.Username,
|
||||
Id = API.LocalUser.Value.Id,
|
||||
Id = API.LocalUser.Value.Id + 1,
|
||||
IsSupporter = !API.LocalUser.Value.IsSupporter,
|
||||
};
|
||||
});
|
||||
|
@ -1,106 +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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Multi.Match.Components;
|
||||
using osu.Game.Screens.Multi.Ranking;
|
||||
using osu.Game.Screens.Multi.Ranking.Pages;
|
||||
using osu.Game.Screens.Multi.Ranking.Types;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMatchResults : MultiplayerTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(MatchResults),
|
||||
typeof(RoomLeaderboardPageInfo),
|
||||
typeof(RoomLeaderboardPage)
|
||||
};
|
||||
|
||||
[Resolved]
|
||||
private BeatmapManager beatmaps { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetID == 0);
|
||||
if (beatmapInfo != null)
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
|
||||
|
||||
Room.RoomID.Value = 1;
|
||||
Room.Name.Value = "an awesome room";
|
||||
|
||||
LoadScreen(new TestMatchResults(new ScoreInfo
|
||||
{
|
||||
User = new User { Id = 10 },
|
||||
}));
|
||||
}
|
||||
|
||||
private class TestMatchResults : MatchResults
|
||||
{
|
||||
public TestMatchResults(ScoreInfo score)
|
||||
: base(score)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<IResultPageInfo> CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap.Value) };
|
||||
}
|
||||
|
||||
private class TestRoomLeaderboardPageInfo : RoomLeaderboardPageInfo
|
||||
{
|
||||
private readonly ScoreInfo score;
|
||||
private readonly WorkingBeatmap beatmap;
|
||||
|
||||
public TestRoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap)
|
||||
: base(score, beatmap)
|
||||
{
|
||||
this.score = score;
|
||||
this.beatmap = beatmap;
|
||||
}
|
||||
|
||||
public override ResultsPage CreatePage() => new TestRoomLeaderboardPage(score, beatmap);
|
||||
}
|
||||
|
||||
private class TestRoomLeaderboardPage : RoomLeaderboardPage
|
||||
{
|
||||
public TestRoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap)
|
||||
: base(score, beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
protected override MatchLeaderboard CreateLeaderboard() => new TestMatchLeaderboard();
|
||||
}
|
||||
|
||||
private class TestMatchLeaderboard : RoomLeaderboardPage.ResultsMatchLeaderboard
|
||||
{
|
||||
protected override APIRequest FetchScores(Action<IEnumerable<APIUserScoreAggregate>> scoresCallback)
|
||||
{
|
||||
var scores = Enumerable.Range(0, 50).Select(createRoomScore).ToArray();
|
||||
|
||||
scoresCallback?.Invoke(scores);
|
||||
ScoresLoaded?.Invoke(scores);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private APIUserScoreAggregate createRoomScore(int id) => new APIUserScoreAggregate
|
||||
{
|
||||
User = new User { Id = id, Username = $"User {id}" },
|
||||
Accuracy = 0.98,
|
||||
TotalScore = 987654,
|
||||
TotalAttempts = 13,
|
||||
CompletedBeatmaps = 5
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
API.Logout();
|
||||
|
||||
localUser = API.LocalUser.GetBoundCopy();
|
||||
localUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true);
|
||||
localUser.BindValueChanged(user => { userPanelArea.Child = new UserGridPanel(user.NewValue) { Width = 200 }; }, true);
|
||||
|
||||
AddStep("logout", API.Logout);
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(UpdateStreamBadgeArea),
|
||||
typeof(UpdateStreamBadge),
|
||||
typeof(ChangelogUpdateStreamControl),
|
||||
typeof(ChangelogUpdateStreamItem),
|
||||
typeof(ChangelogHeader),
|
||||
typeof(ChangelogContent),
|
||||
typeof(ChangelogListing),
|
||||
|
@ -7,11 +7,11 @@ using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Chat;
|
||||
@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
|
||||
AddAssert($"msg #{index} has {linkAmount} link(s)", () => newLine.Message.Links.Count == linkAmount);
|
||||
AddAssert($"msg #{index} has the right action", hasExpectedActions);
|
||||
AddAssert($"msg #{index} is " + (isAction ? "italic" : "not italic"), () => newLine.ContentFlow.Any() && isAction == isItalic());
|
||||
//AddAssert($"msg #{index} is " + (isAction ? "italic" : "not italic"), () => newLine.ContentFlow.Any() && isAction == isItalic());
|
||||
AddAssert($"msg #{index} shows {linkAmount} link(s)", isShowingLinks);
|
||||
|
||||
bool hasExpectedActions()
|
||||
@ -96,13 +96,13 @@ namespace osu.Game.Tests.Visual.Online
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast<OsuSpriteText>().All(sprite => sprite.Font.Italics);
|
||||
//bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast<OsuSpriteText>().All(sprite => sprite.Font.Italics);
|
||||
|
||||
bool isShowingLinks()
|
||||
{
|
||||
bool hasBackground = !string.IsNullOrEmpty(newLine.Message.Sender.Colour);
|
||||
|
||||
Color4 textColour = isAction && hasBackground ? OsuColour.FromHex(newLine.Message.Sender.Colour) : Color4.White;
|
||||
Color4 textColour = isAction && hasBackground ? Color4Extensions.FromHex(newLine.Message.Sender.Colour) : Color4.White;
|
||||
|
||||
var linkCompilers = newLine.ContentFlow.Where(d => d is DrawableLinkCompiler).ToList();
|
||||
var linkSprites = linkCompilers.SelectMany(comp => ((DrawableLinkCompiler)comp).Parts);
|
||||
|
@ -38,8 +38,13 @@ namespace osu.Game.Tests.Visual.Online
|
||||
private TestChatOverlay chatOverlay;
|
||||
private ChannelManager channelManager;
|
||||
|
||||
private IEnumerable<Channel> visibleChannels => chatOverlay.ChannelTabControl.VisibleItems.Where(channel => channel.Name != "+");
|
||||
private IEnumerable<Channel> joinedChannels => chatOverlay.ChannelTabControl.Items.Where(channel => channel.Name != "+");
|
||||
private readonly List<Channel> channels;
|
||||
|
||||
private Channel currentChannel => channelManager.CurrentChannel.Value;
|
||||
private Channel nextChannel => joinedChannels.ElementAt(joinedChannels.ToList().IndexOf(currentChannel) + 1);
|
||||
private Channel previousChannel => joinedChannels.ElementAt(joinedChannels.ToList().IndexOf(currentChannel) - 1);
|
||||
private Channel channel1 => channels[0];
|
||||
private Channel channel2 => channels[1];
|
||||
|
||||
@ -91,7 +96,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
AddStep("Join channel 1", () => channelManager.JoinChannel(channel1));
|
||||
AddStep("Switch to channel 1", () => clickDrawable(chatOverlay.TabMap[channel1]));
|
||||
|
||||
AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1);
|
||||
AddAssert("Current channel is channel 1", () => currentChannel == channel1);
|
||||
AddAssert("Channel selector was closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
|
||||
}
|
||||
|
||||
@ -102,12 +107,12 @@ namespace osu.Game.Tests.Visual.Online
|
||||
AddStep("Join channel 2", () => channelManager.JoinChannel(channel2));
|
||||
|
||||
AddStep("Switch to channel 2", () => clickDrawable(chatOverlay.TabMap[channel2]));
|
||||
AddStep("Close channel 2", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child));
|
||||
AddStep("Close channel 2", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child));
|
||||
|
||||
AddAssert("Selector remained closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
|
||||
AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1);
|
||||
AddAssert("Current channel is channel 1", () => currentChannel == channel1);
|
||||
|
||||
AddStep("Close channel 1", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child));
|
||||
AddStep("Close channel 1", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child));
|
||||
|
||||
AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
|
||||
}
|
||||
@ -140,10 +145,67 @@ namespace osu.Game.Tests.Visual.Online
|
||||
var targetNumberKey = oneBasedIndex % 10;
|
||||
var targetChannel = channels[zeroBasedIndex];
|
||||
AddStep($"press Alt+{targetNumberKey}", () => pressChannelHotkey(targetNumberKey));
|
||||
AddAssert($"channel #{oneBasedIndex} is selected", () => channelManager.CurrentChannel.Value == targetChannel);
|
||||
AddAssert($"channel #{oneBasedIndex} is selected", () => currentChannel == targetChannel);
|
||||
}
|
||||
}
|
||||
|
||||
private Channel expectedChannel;
|
||||
|
||||
[Test]
|
||||
public void TestCloseChannelWhileActive()
|
||||
{
|
||||
AddUntilStep("Join until dropdown has channels", () =>
|
||||
{
|
||||
if (visibleChannels.Count() < joinedChannels.Count())
|
||||
return true;
|
||||
|
||||
// Using temporary channels because they don't hide their names when not active
|
||||
Channel toAdd = new Channel { Name = $"test channel {joinedChannels.Count()}", Type = ChannelType.Temporary };
|
||||
channelManager.JoinChannel(toAdd);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
AddStep("Switch to last tab", () => clickDrawable(chatOverlay.TabMap[visibleChannels.Last()]));
|
||||
AddAssert("Last visible selected", () => currentChannel == visibleChannels.Last());
|
||||
|
||||
// Closing the last channel before dropdown
|
||||
AddStep("Close current channel", () =>
|
||||
{
|
||||
expectedChannel = nextChannel;
|
||||
chatOverlay.ChannelTabControl.RemoveChannel(currentChannel);
|
||||
});
|
||||
AddAssert("Next channel selected", () => currentChannel == expectedChannel);
|
||||
|
||||
// Depending on the window size, one more channel might need to be closed for the selectorTab to appear
|
||||
AddUntilStep("Close channels until selector visible", () =>
|
||||
{
|
||||
if (chatOverlay.ChannelTabControl.VisibleItems.Last().Name == "+")
|
||||
return true;
|
||||
|
||||
chatOverlay.ChannelTabControl.RemoveChannel(visibleChannels.Last());
|
||||
return false;
|
||||
});
|
||||
AddAssert("Last visible selected", () => currentChannel == visibleChannels.Last());
|
||||
|
||||
// Closing the last channel with dropdown no longer present
|
||||
AddStep("Close last when selector next", () =>
|
||||
{
|
||||
expectedChannel = previousChannel;
|
||||
chatOverlay.ChannelTabControl.RemoveChannel(currentChannel);
|
||||
});
|
||||
AddAssert("Channel changed to previous", () => currentChannel == expectedChannel);
|
||||
|
||||
// Standard channel closing
|
||||
AddStep("Switch to previous channel", () => chatOverlay.ChannelTabControl.SwitchTab(-1));
|
||||
AddStep("Close current channel", () =>
|
||||
{
|
||||
expectedChannel = nextChannel;
|
||||
chatOverlay.ChannelTabControl.RemoveChannel(currentChannel);
|
||||
});
|
||||
AddAssert("Channel changed to next", () => currentChannel == expectedChannel);
|
||||
}
|
||||
|
||||
private void pressChannelHotkey(int number)
|
||||
{
|
||||
var channelKey = Key.Number0 + number;
|
||||
@ -187,6 +249,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public Visibility SelectionOverlayState => ChannelSelectionOverlay.State.Value;
|
||||
|
||||
public new ChannelTabControl ChannelTabControl => base.ChannelTabControl;
|
||||
|
||||
public new ChannelSelectionOverlay ChannelSelectionOverlay => base.ChannelSelectionOverlay;
|
||||
|
||||
protected override ChannelTabControl CreateChannelTabControl() => new TestTabControl();
|
||||
@ -196,12 +260,22 @@ namespace osu.Game.Tests.Visual.Online
|
||||
|
||||
private class TestTabControl : ChannelTabControl
|
||||
{
|
||||
protected override TabItem<Channel> CreateTabItem(Channel value) => new TestChannelTabItem(value);
|
||||
protected override TabItem<Channel> CreateTabItem(Channel value)
|
||||
{
|
||||
switch (value.Type)
|
||||
{
|
||||
case ChannelType.PM:
|
||||
return new TestPrivateChannelTabItem(value);
|
||||
|
||||
default:
|
||||
return new TestChannelTabItem(value);
|
||||
}
|
||||
}
|
||||
|
||||
public new IReadOnlyDictionary<Channel, TabItem<Channel>> TabMap => base.TabMap;
|
||||
}
|
||||
|
||||
private class TestChannelTabItem : PrivateChannelTabItem
|
||||
private class TestChannelTabItem : ChannelTabItem
|
||||
{
|
||||
public TestChannelTabItem(Channel channel)
|
||||
: base(channel)
|
||||
@ -210,5 +284,15 @@ namespace osu.Game.Tests.Visual.Online
|
||||
|
||||
public new ClickableContainer CloseButton => base.CloseButton;
|
||||
}
|
||||
|
||||
private class TestPrivateChannelTabItem : PrivateChannelTabItem
|
||||
{
|
||||
public TestPrivateChannelTabItem(Channel channel)
|
||||
: base(channel)
|
||||
{
|
||||
}
|
||||
|
||||
public new ClickableContainer CloseButton => base.CloseButton;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,9 @@ namespace osu.Game.Tests.Visual.Online
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(UserPanel),
|
||||
typeof(SocialPanel),
|
||||
typeof(FilterControl),
|
||||
typeof(SocialGridPanel),
|
||||
typeof(SocialListPanel)
|
||||
typeof(UserGridPanel),
|
||||
typeof(UserListPanel)
|
||||
};
|
||||
|
||||
public TestSceneSocialOverlay()
|
||||
|
@ -1,6 +1,8 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -15,9 +17,18 @@ namespace osu.Game.Tests.Visual.Online
|
||||
[TestFixture]
|
||||
public class TestSceneUserPanel : OsuTestScene
|
||||
{
|
||||
private readonly Bindable<UserActivity> activity = new Bindable<UserActivity>();
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(UserPanel),
|
||||
typeof(UserListPanel),
|
||||
typeof(UserGridPanel),
|
||||
};
|
||||
|
||||
private UserPanel peppy;
|
||||
private readonly Bindable<UserActivity> activity = new Bindable<UserActivity>();
|
||||
private readonly Bindable<UserStatus> status = new Bindable<UserStatus>();
|
||||
|
||||
private UserGridPanel peppy;
|
||||
private UserListPanel evast;
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesetStore { get; set; }
|
||||
@ -25,24 +36,25 @@ namespace osu.Game.Tests.Visual.Online
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
UserPanel flyte;
|
||||
UserGridPanel flyte;
|
||||
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Spacing = new Vector2(10f),
|
||||
Children = new[]
|
||||
Children = new Drawable[]
|
||||
{
|
||||
flyte = new UserPanel(new User
|
||||
flyte = new UserGridPanel(new User
|
||||
{
|
||||
Username = @"flyte",
|
||||
Id = 3103765,
|
||||
Country = new Country { FlagName = @"JP" },
|
||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
|
||||
}) { Width = 300 },
|
||||
peppy = new UserPanel(new User
|
||||
peppy = new UserGridPanel(new User
|
||||
{
|
||||
Username = @"peppy",
|
||||
Id = 2,
|
||||
@ -51,27 +63,40 @@ namespace osu.Game.Tests.Visual.Online
|
||||
IsSupporter = true,
|
||||
SupportLevel = 3,
|
||||
}) { Width = 300 },
|
||||
evast = new UserListPanel(new User
|
||||
{
|
||||
Username = @"Evast",
|
||||
Id = 8195163,
|
||||
Country = new Country { FlagName = @"BY" },
|
||||
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg",
|
||||
IsOnline = false,
|
||||
LastVisit = DateTimeOffset.Now
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
flyte.Status.Value = new UserStatusOnline();
|
||||
peppy.Status.Value = null;
|
||||
|
||||
peppy.Status.BindTo(status);
|
||||
peppy.Activity.BindTo(activity);
|
||||
|
||||
evast.Status.BindTo(status);
|
||||
evast.Activity.BindTo(activity);
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestUserStatus()
|
||||
{
|
||||
AddStep("online", () => peppy.Status.Value = new UserStatusOnline());
|
||||
AddStep("do not disturb", () => peppy.Status.Value = new UserStatusDoNotDisturb());
|
||||
AddStep("offline", () => peppy.Status.Value = new UserStatusOffline());
|
||||
AddStep("null status", () => peppy.Status.Value = null);
|
||||
AddStep("online", () => status.Value = new UserStatusOnline());
|
||||
AddStep("do not disturb", () => status.Value = new UserStatusDoNotDisturb());
|
||||
AddStep("offline", () => status.Value = new UserStatusOffline());
|
||||
AddStep("null status", () => status.Value = null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserActivity()
|
||||
{
|
||||
AddStep("set online status", () => peppy.Status.Value = new UserStatusOnline());
|
||||
AddStep("set online status", () => peppy.Status.Value = evast.Status.Value = new UserStatusOnline());
|
||||
|
||||
AddStep("idle", () => activity.Value = null);
|
||||
AddStep("spectating", () => activity.Value = new UserActivity.Spectating());
|
||||
|
155
osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs
Normal file
155
osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs
Normal file
@ -0,0 +1,155 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Ranking.Expanded.Accuracy;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Ranking
|
||||
{
|
||||
public class TestSceneAccuracyCircle : OsuTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(AccuracyCircle),
|
||||
typeof(RankBadge),
|
||||
typeof(RankNotch),
|
||||
typeof(RankText),
|
||||
typeof(SmoothCircularProgress)
|
||||
};
|
||||
|
||||
[Test]
|
||||
public void TestDRank()
|
||||
{
|
||||
var score = createScore();
|
||||
score.Accuracy = 0.5;
|
||||
score.Rank = ScoreRank.D;
|
||||
|
||||
addCircleStep(score);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCRank()
|
||||
{
|
||||
var score = createScore();
|
||||
score.Accuracy = 0.75;
|
||||
score.Rank = ScoreRank.C;
|
||||
|
||||
addCircleStep(score);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBRank()
|
||||
{
|
||||
var score = createScore();
|
||||
score.Accuracy = 0.85;
|
||||
score.Rank = ScoreRank.B;
|
||||
|
||||
addCircleStep(score);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestARank()
|
||||
{
|
||||
var score = createScore();
|
||||
score.Accuracy = 0.925;
|
||||
score.Rank = ScoreRank.A;
|
||||
|
||||
addCircleStep(score);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSRank()
|
||||
{
|
||||
var score = createScore();
|
||||
score.Accuracy = 0.975;
|
||||
score.Rank = ScoreRank.S;
|
||||
|
||||
addCircleStep(score);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAlmostSSRank()
|
||||
{
|
||||
var score = createScore();
|
||||
score.Accuracy = 0.9999;
|
||||
score.Rank = ScoreRank.S;
|
||||
|
||||
addCircleStep(score);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSSRank()
|
||||
{
|
||||
var score = createScore();
|
||||
score.Accuracy = 1;
|
||||
score.Rank = ScoreRank.X;
|
||||
|
||||
addCircleStep(score);
|
||||
}
|
||||
|
||||
private void addCircleStep(ScoreInfo score) => AddStep("add panel", () =>
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(500, 700),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#555"), Color4Extensions.FromHex("#333"))
|
||||
}
|
||||
}
|
||||
},
|
||||
new AccuracyCircle(score)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(230)
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
private ScoreInfo createScore() => new ScoreInfo
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
Id = 2,
|
||||
Username = "peppy",
|
||||
},
|
||||
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
|
||||
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
|
||||
TotalScore = 2845370,
|
||||
Accuracy = 0.95,
|
||||
MaxCombo = 999,
|
||||
Rank = ScoreRank.S,
|
||||
Date = DateTimeOffset.Now,
|
||||
Statistics =
|
||||
{
|
||||
{ HitResult.Miss, 1 },
|
||||
{ HitResult.Meh, 50 },
|
||||
{ HitResult.Good, 100 },
|
||||
{ HitResult.Great, 300 },
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Ranking.Expanded;
|
||||
using osu.Game.Screens.Ranking.Expanded.Accuracy;
|
||||
using osu.Game.Screens.Ranking.Expanded.Statistics;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Ranking
|
||||
{
|
||||
public class TestSceneExpandedPanelMiddleContent : OsuTestScene
|
||||
{
|
||||
[Resolved]
|
||||
private RulesetStore rulesetStore { get; set; }
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(ExpandedPanelMiddleContent),
|
||||
typeof(AccuracyCircle),
|
||||
typeof(AccuracyStatistic),
|
||||
typeof(ComboStatistic),
|
||||
typeof(CounterStatistic),
|
||||
typeof(StarRatingDisplay),
|
||||
typeof(StatisticDisplay),
|
||||
typeof(TotalScoreCounter)
|
||||
};
|
||||
|
||||
[Test]
|
||||
public void TestMapWithKnownMapper()
|
||||
{
|
||||
var author = new User { Username = "mapper_name" };
|
||||
|
||||
AddStep("show example score", () => showPanel(createTestBeatmap(author), createTestScore()));
|
||||
|
||||
AddAssert("mapper name present", () => this.ChildrenOfType<OsuSpriteText>().Any(spriteText => spriteText.Text == "mapper_name"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMapWithUnknownMapper()
|
||||
{
|
||||
AddStep("show example score", () => showPanel(createTestBeatmap(null), createTestScore()));
|
||||
|
||||
AddAssert("mapped by text not present", () =>
|
||||
this.ChildrenOfType<OsuSpriteText>().All(spriteText => !containsAny(spriteText.Text, "mapped", "by")));
|
||||
}
|
||||
|
||||
private void showPanel(WorkingBeatmap workingBeatmap, ScoreInfo score)
|
||||
{
|
||||
Child = new ExpandedPanelMiddleContentContainer(workingBeatmap, score);
|
||||
}
|
||||
|
||||
private WorkingBeatmap createTestBeatmap(User author)
|
||||
{
|
||||
var beatmap = new TestBeatmap(rulesetStore.GetRuleset(0));
|
||||
beatmap.Metadata.Author = author;
|
||||
|
||||
return new TestWorkingBeatmap(beatmap);
|
||||
}
|
||||
|
||||
private ScoreInfo createTestScore() => new ScoreInfo
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
Id = 2,
|
||||
Username = "peppy",
|
||||
},
|
||||
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
|
||||
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
|
||||
TotalScore = 999999,
|
||||
Accuracy = 0.95,
|
||||
MaxCombo = 999,
|
||||
Rank = ScoreRank.S,
|
||||
Date = DateTimeOffset.Now,
|
||||
Statistics =
|
||||
{
|
||||
{ HitResult.Miss, 1 },
|
||||
{ HitResult.Meh, 50 },
|
||||
{ HitResult.Good, 100 },
|
||||
{ HitResult.Great, 300 },
|
||||
}
|
||||
};
|
||||
|
||||
private bool containsAny(string text, params string[] stringsToMatch) => stringsToMatch.Any(text.Contains);
|
||||
|
||||
private class ExpandedPanelMiddleContentContainer : Container
|
||||
{
|
||||
[Cached]
|
||||
private Bindable<WorkingBeatmap> workingBeatmap { get; set; }
|
||||
|
||||
public ExpandedPanelMiddleContentContainer(WorkingBeatmap beatmap, ScoreInfo score)
|
||||
{
|
||||
workingBeatmap = new Bindable<WorkingBeatmap>(beatmap);
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
Size = new Vector2(500, 700);
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4Extensions.FromHex("#444"),
|
||||
},
|
||||
new ExpandedPanelMiddleContent(score)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
// 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.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Screens.Ranking.Expanded;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Ranking
|
||||
{
|
||||
public class TestSceneExpandedPanelTopContent : OsuTestScene
|
||||
{
|
||||
public TestSceneExpandedPanelTopContent()
|
||||
{
|
||||
Child = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(500, 200),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4Extensions.FromHex("#444"),
|
||||
},
|
||||
new ExpandedPanelTopContent(new User { Id = 2, Username = "peppy" }),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -10,29 +10,27 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Screens.Ranking.Pages;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
namespace osu.Game.Tests.Visual.Ranking
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneResults : ScreenTestScene
|
||||
public class TestSceneResultsScreen : ScreenTestScene
|
||||
{
|
||||
private BeatmapManager beatmaps;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(Results),
|
||||
typeof(ResultsPage),
|
||||
typeof(ScoreResultsPage),
|
||||
typeof(ResultsScreen),
|
||||
typeof(RetryButton),
|
||||
typeof(ReplayDownloadButton),
|
||||
typeof(LocalLeaderboardPage),
|
||||
typeof(TestPlayer)
|
||||
};
|
||||
|
||||
@ -65,6 +63,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{ HitResult.Meh, 50 },
|
||||
{ HitResult.Miss, 1 }
|
||||
},
|
||||
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
|
||||
User = new User
|
||||
{
|
||||
Username = "peppy",
|
||||
@ -119,7 +118,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
}
|
||||
|
||||
private class TestSoloResults : SoloResults
|
||||
private class TestSoloResults : ResultsScreen
|
||||
{
|
||||
public HotkeyRetryOverlay RetryOverlay;
|
||||
|
143
osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs
Normal file
143
osu.Game.Tests/Visual/Ranking/TestSceneScorePanel.cs
Normal file
@ -0,0 +1,143 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Screens.Ranking.Expanded;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Ranking
|
||||
{
|
||||
public class TestSceneScorePanel : OsuTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(ScorePanel),
|
||||
typeof(PanelState),
|
||||
typeof(ExpandedPanelMiddleContent),
|
||||
typeof(ExpandedPanelTopContent),
|
||||
};
|
||||
|
||||
[Test]
|
||||
public void TestDRank()
|
||||
{
|
||||
var score = createScore();
|
||||
score.Accuracy = 0.5;
|
||||
score.Rank = ScoreRank.D;
|
||||
|
||||
addPanelStep(score);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCRank()
|
||||
{
|
||||
var score = createScore();
|
||||
score.Accuracy = 0.75;
|
||||
score.Rank = ScoreRank.C;
|
||||
|
||||
addPanelStep(score);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBRank()
|
||||
{
|
||||
var score = createScore();
|
||||
score.Accuracy = 0.85;
|
||||
score.Rank = ScoreRank.B;
|
||||
|
||||
addPanelStep(score);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestARank()
|
||||
{
|
||||
var score = createScore();
|
||||
score.Accuracy = 0.925;
|
||||
score.Rank = ScoreRank.A;
|
||||
|
||||
addPanelStep(score);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSRank()
|
||||
{
|
||||
var score = createScore();
|
||||
score.Accuracy = 0.975;
|
||||
score.Rank = ScoreRank.S;
|
||||
|
||||
addPanelStep(score);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAlmostSSRank()
|
||||
{
|
||||
var score = createScore();
|
||||
score.Accuracy = 0.9999;
|
||||
score.Rank = ScoreRank.S;
|
||||
|
||||
addPanelStep(score);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSSRank()
|
||||
{
|
||||
var score = createScore();
|
||||
score.Accuracy = 1;
|
||||
score.Rank = ScoreRank.X;
|
||||
|
||||
addPanelStep(score);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAllHitResults()
|
||||
{
|
||||
var score = createScore();
|
||||
score.Statistics[HitResult.Perfect] = 350;
|
||||
score.Statistics[HitResult.Ok] = 200;
|
||||
|
||||
addPanelStep(score);
|
||||
}
|
||||
|
||||
private void addPanelStep(ScoreInfo score) => AddStep("add panel", () =>
|
||||
{
|
||||
Child = new ScorePanel(score)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
State = PanelState.Expanded
|
||||
};
|
||||
});
|
||||
|
||||
private ScoreInfo createScore() => new ScoreInfo
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
Id = 2,
|
||||
Username = "peppy",
|
||||
},
|
||||
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
|
||||
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
|
||||
TotalScore = 2845370,
|
||||
Accuracy = 0.95,
|
||||
MaxCombo = 999,
|
||||
Rank = ScoreRank.S,
|
||||
Date = DateTimeOffset.Now,
|
||||
Statistics =
|
||||
{
|
||||
{ HitResult.Miss, 1 },
|
||||
{ HitResult.Meh, 50 },
|
||||
{ HitResult.Good, 100 },
|
||||
{ HitResult.Great, 300 },
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
32
osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs
Normal file
32
osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.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.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Screens.Ranking.Expanded;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Ranking
|
||||
{
|
||||
public class TestSceneStarRatingDisplay : OsuTestScene
|
||||
{
|
||||
public TestSceneStarRatingDisplay()
|
||||
{
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 1.23 }),
|
||||
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 2.34 }),
|
||||
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 3.45 }),
|
||||
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 4.56 }),
|
||||
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 5.67 }),
|
||||
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 6.78 }),
|
||||
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 10.11 }),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -399,7 +399,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddStep("filter to ruleset 0", () =>
|
||||
carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) }, false));
|
||||
AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testMixed.Beatmaps[1], false));
|
||||
AddAssert("unfiltered beatmap selected", () => carousel.SelectedBeatmap.Equals(testMixed.Beatmaps[0]));
|
||||
AddAssert("unfiltered beatmap not selected", () => carousel.SelectedBeatmap == null);
|
||||
|
||||
AddStep("remove mixed set", () =>
|
||||
{
|
||||
@ -497,7 +497,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
AddStep($"Load {beatmapSets.Count} Beatmaps", () =>
|
||||
AddStep($"Load {(beatmapSets.Count > 0 ? beatmapSets.Count.ToString() : "some")} beatmaps", () =>
|
||||
{
|
||||
carousel.Filter(new FilterCriteria());
|
||||
carousel.BeatmapSetsChanged = () => changed = true;
|
||||
@ -697,6 +697,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
public new List<DrawableCarouselItem> Items => base.Items;
|
||||
|
||||
public bool PendingFilterTask => PendingFilter != null;
|
||||
|
||||
protected override IEnumerable<BeatmapSetInfo> GetLoadableBeatmaps() => Enumerable.Empty<BeatmapSetInfo>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -283,7 +283,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddStep("import multi-ruleset map", () =>
|
||||
{
|
||||
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
|
||||
manager.Import(createTestBeatmapSet(0, usableRulesets)).Wait();
|
||||
manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
|
||||
});
|
||||
}
|
||||
else
|
||||
@ -436,6 +436,58 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
changeRuleset(0);
|
||||
|
||||
// used for filter check below
|
||||
AddStep("allow convert display", () => config.Set(OsuSetting.ShowConvertedBeatmaps, true));
|
||||
|
||||
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null);
|
||||
|
||||
AddStep("set filter text", () => songSelect.FilterControl.ChildrenOfType<SearchTextBox>().First().Text = "nonono");
|
||||
|
||||
AddUntilStep("dummy selected", () => Beatmap.Value is DummyWorkingBeatmap);
|
||||
|
||||
AddUntilStep("has no selection", () => songSelect.Carousel.SelectedBeatmap == null);
|
||||
|
||||
BeatmapInfo target = null;
|
||||
|
||||
int targetRuleset = differentRuleset ? 1 : 0;
|
||||
|
||||
AddStep("select beatmap externally", () =>
|
||||
{
|
||||
target = manager.GetAllUsableBeatmapSets()
|
||||
.Where(b => b.Beatmaps.Any(bi => bi.RulesetID == targetRuleset))
|
||||
.ElementAt(5).Beatmaps.First(bi => bi.RulesetID == targetRuleset);
|
||||
|
||||
Beatmap.Value = manager.GetWorkingBeatmap(target);
|
||||
});
|
||||
|
||||
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null);
|
||||
|
||||
AddAssert("selected only shows expected ruleset (plus converts)", () =>
|
||||
{
|
||||
var selectedPanel = songSelect.Carousel.ChildrenOfType<DrawableCarouselBeatmapSet>().First(s => s.Item.State.Value == CarouselItemState.Selected);
|
||||
|
||||
// special case for converts checked here.
|
||||
return selectedPanel.ChildrenOfType<DrawableCarouselBeatmapSet.FilterableDifficultyIcon>().All(i =>
|
||||
i.IsFiltered || i.Item.Beatmap.Ruleset.ID == targetRuleset || i.Item.Beatmap.Ruleset.ID == 0);
|
||||
});
|
||||
|
||||
AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmap?.OnlineBeatmapID == target.OnlineBeatmapID);
|
||||
AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID);
|
||||
|
||||
AddStep("reset filter text", () => songSelect.FilterControl.ChildrenOfType<SearchTextBox>().First().Text = string.Empty);
|
||||
|
||||
AddAssert("game still correct", () => Beatmap.Value?.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID);
|
||||
AddAssert("carousel still correct", () => songSelect.Carousel.SelectedBeatmap.OnlineBeatmapID == target.OnlineBeatmapID);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExternalBeatmapChangeWhileFilteredThenRefilter()
|
||||
{
|
||||
createSongSelect();
|
||||
addManyTestMaps();
|
||||
|
||||
changeRuleset(0);
|
||||
|
||||
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null);
|
||||
|
||||
AddStep("set filter text", () => songSelect.FilterControl.ChildrenOfType<SearchTextBox>().First().Text = "nonono");
|
||||
@ -448,7 +500,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
AddStep("select beatmap externally", () =>
|
||||
{
|
||||
target = manager.GetAllUsableBeatmapSets().Where(b => b.Beatmaps.Any(bi => bi.RulesetID == (differentRuleset ? 1 : 0)))
|
||||
target = manager.GetAllUsableBeatmapSets().Where(b => b.Beatmaps.Any(bi => bi.RulesetID == 1))
|
||||
.ElementAt(5).Beatmaps.First();
|
||||
|
||||
Beatmap.Value = manager.GetWorkingBeatmap(target);
|
||||
@ -459,10 +511,10 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmap?.OnlineBeatmapID == target.OnlineBeatmapID);
|
||||
AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID);
|
||||
|
||||
AddStep("reset filter text", () => songSelect.FilterControl.ChildrenOfType<SearchTextBox>().First().Text = string.Empty);
|
||||
AddStep("set filter text", () => songSelect.FilterControl.ChildrenOfType<SearchTextBox>().First().Text = "nononoo");
|
||||
|
||||
AddAssert("game still correct", () => Beatmap.Value?.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID);
|
||||
AddAssert("carousel still correct", () => songSelect.Carousel.SelectedBeatmap.OnlineBeatmapID == target.OnlineBeatmapID);
|
||||
AddUntilStep("game lost selection", () => Beatmap.Value is DummyWorkingBeatmap);
|
||||
AddAssert("carousel lost selection", () => songSelect.Carousel.SelectedBeatmap == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -520,6 +572,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
difficultyIcon = set.ChildrenOfType<DrawableCarouselBeatmapSet.FilterableDifficultyIcon>()
|
||||
.First(icon => getDifficultyIconIndex(set, icon) != getCurrentBeatmapIndex());
|
||||
});
|
||||
|
||||
AddStep("Click on a difficulty", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(difficultyIcon);
|
||||
@ -527,6 +580,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
InputManager.PressButton(MouseButton.Left);
|
||||
InputManager.ReleaseButton(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddAssert("Selected beatmap correct", () => getCurrentBeatmapIndex() == getDifficultyIconIndex(set, difficultyIcon));
|
||||
|
||||
double? maxBPM = null;
|
||||
@ -539,16 +593,16 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
}
|
||||
}));
|
||||
|
||||
BeatmapInfo filteredBeatmap = null;
|
||||
DrawableCarouselBeatmapSet.FilterableDifficultyIcon filteredIcon = null;
|
||||
|
||||
AddStep("Get filtered icon", () =>
|
||||
{
|
||||
var filteredBeatmap = songSelect.Carousel.SelectedBeatmapSet.Beatmaps.Find(b => b.BPM < maxBPM);
|
||||
filteredBeatmap = songSelect.Carousel.SelectedBeatmapSet.Beatmaps.First(b => b.BPM < maxBPM);
|
||||
int filteredBeatmapIndex = getBeatmapIndex(filteredBeatmap.BeatmapSet, filteredBeatmap);
|
||||
filteredIcon = set.ChildrenOfType<DrawableCarouselBeatmapSet.FilterableDifficultyIcon>().ElementAt(filteredBeatmapIndex);
|
||||
});
|
||||
|
||||
int? previousID = null;
|
||||
AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmap.ID);
|
||||
AddStep("Click on a filtered difficulty", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(filteredIcon);
|
||||
@ -556,7 +610,101 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
InputManager.PressButton(MouseButton.Left);
|
||||
InputManager.ReleaseButton(MouseButton.Left);
|
||||
});
|
||||
AddAssert("Selected beatmap has not changed", () => songSelect.Carousel.SelectedBeatmap.ID == previousID);
|
||||
|
||||
AddAssert("Selected beatmap correct", () => songSelect.Carousel.SelectedBeatmap == filteredBeatmap);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDifficultyIconSelectingForDifferentRuleset()
|
||||
{
|
||||
changeRuleset(0);
|
||||
|
||||
createSongSelect();
|
||||
|
||||
AddStep("import multi-ruleset map", () =>
|
||||
{
|
||||
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
|
||||
manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
|
||||
});
|
||||
|
||||
DrawableCarouselBeatmapSet set = null;
|
||||
AddUntilStep("Find the DrawableCarouselBeatmapSet", () =>
|
||||
{
|
||||
set = songSelect.Carousel.ChildrenOfType<DrawableCarouselBeatmapSet>().FirstOrDefault();
|
||||
return set != null;
|
||||
});
|
||||
|
||||
DrawableCarouselBeatmapSet.FilterableDifficultyIcon difficultyIcon = null;
|
||||
AddStep("Find an icon for different ruleset", () =>
|
||||
{
|
||||
difficultyIcon = set.ChildrenOfType<DrawableCarouselBeatmapSet.FilterableDifficultyIcon>()
|
||||
.First(icon => icon.Item.Beatmap.Ruleset.ID == 3);
|
||||
});
|
||||
|
||||
AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0);
|
||||
|
||||
int previousSetID = 0;
|
||||
|
||||
AddStep("record set ID", () => previousSetID = Beatmap.Value.BeatmapSetInfo.ID);
|
||||
|
||||
AddStep("Click on a difficulty", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(difficultyIcon);
|
||||
|
||||
InputManager.PressButton(MouseButton.Left);
|
||||
InputManager.ReleaseButton(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3);
|
||||
|
||||
AddAssert("Selected beatmap still same set", () => songSelect.Carousel.SelectedBeatmap.BeatmapSet.ID == previousSetID);
|
||||
AddAssert("Selected beatmap is mania", () => Beatmap.Value.BeatmapInfo.Ruleset.ID == 3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGroupedDifficultyIconSelecting()
|
||||
{
|
||||
changeRuleset(0);
|
||||
|
||||
createSongSelect();
|
||||
|
||||
BeatmapSetInfo imported = null;
|
||||
|
||||
AddStep("import huge difficulty count map", () =>
|
||||
{
|
||||
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
|
||||
imported = manager.Import(createTestBeatmapSet(usableRulesets, 50)).Result;
|
||||
});
|
||||
|
||||
AddStep("select the first beatmap of import", () => Beatmap.Value = manager.GetWorkingBeatmap(imported.Beatmaps.First()));
|
||||
|
||||
DrawableCarouselBeatmapSet set = null;
|
||||
AddUntilStep("Find the DrawableCarouselBeatmapSet", () =>
|
||||
{
|
||||
set = songSelect.Carousel.ChildrenOfType<DrawableCarouselBeatmapSet>().FirstOrDefault();
|
||||
return set != null;
|
||||
});
|
||||
|
||||
DrawableCarouselBeatmapSet.FilterableGroupedDifficultyIcon groupIcon = null;
|
||||
AddStep("Find group icon for different ruleset", () =>
|
||||
{
|
||||
groupIcon = set.ChildrenOfType<DrawableCarouselBeatmapSet.FilterableGroupedDifficultyIcon>()
|
||||
.First(icon => icon.Items.First().Beatmap.Ruleset.ID == 3);
|
||||
});
|
||||
|
||||
AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0);
|
||||
|
||||
AddStep("Click on group", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(groupIcon);
|
||||
|
||||
InputManager.PressButton(MouseButton.Left);
|
||||
InputManager.ReleaseButton(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3);
|
||||
|
||||
AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo == groupIcon.Items.First().Beatmap);
|
||||
}
|
||||
|
||||
private int getBeatmapIndex(BeatmapSetInfo set, BeatmapInfo info) => set.Beatmaps.FindIndex(b => b == info);
|
||||
@ -570,7 +718,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
private void addRulesetImportStep(int id) => AddStep($"import test map for ruleset {id}", () => importForRuleset(id));
|
||||
|
||||
private void importForRuleset(int id) => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait();
|
||||
private void importForRuleset(int id) => manager.Import(createTestBeatmapSet(rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait();
|
||||
|
||||
private static int importId;
|
||||
|
||||
@ -596,20 +744,22 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
|
||||
|
||||
for (int i = 0; i < 100; i += 10)
|
||||
manager.Import(createTestBeatmapSet(i, usableRulesets)).Wait();
|
||||
manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
|
||||
});
|
||||
}
|
||||
|
||||
private BeatmapSetInfo createTestBeatmapSet(int setId, RulesetInfo[] rulesets)
|
||||
private BeatmapSetInfo createTestBeatmapSet(RulesetInfo[] rulesets, int countPerRuleset = 6)
|
||||
{
|
||||
int j = 0;
|
||||
RulesetInfo getRuleset() => rulesets[j++ % rulesets.Length];
|
||||
|
||||
int setId = getImportId();
|
||||
|
||||
var beatmaps = new List<BeatmapInfo>();
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
for (int i = 0; i < countPerRuleset; i++)
|
||||
{
|
||||
int beatmapId = setId * 10 + i;
|
||||
int beatmapId = setId * 1000 + i;
|
||||
|
||||
int length = RNG.Next(30000, 200000);
|
||||
double bpm = RNG.NextSingle(80, 200);
|
||||
|
@ -0,0 +1,63 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Home.Friends;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public class TestSceneFriendsOnlineStatusControl : OsuTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(FriendsOnlineStatusControl),
|
||||
typeof(FriendsOnlineStatusItem),
|
||||
typeof(OverlayStreamControl<>),
|
||||
typeof(OverlayStreamItem<>),
|
||||
typeof(FriendsBundle)
|
||||
};
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
private FriendsOnlineStatusControl control;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() => Child = control = new FriendsOnlineStatusControl
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void Populate()
|
||||
{
|
||||
AddStep("Populate", () => control.Populate(new List<User>
|
||||
{
|
||||
new User
|
||||
{
|
||||
IsOnline = true
|
||||
},
|
||||
new User
|
||||
{
|
||||
IsOnline = false
|
||||
},
|
||||
new User
|
||||
{
|
||||
IsOnline = false
|
||||
}
|
||||
}));
|
||||
|
||||
AddAssert("3 users", () => control.Items.FirstOrDefault(item => item.Status == FriendsOnlineStatus.All)?.Count == 3);
|
||||
AddAssert("1 online user", () => control.Items.FirstOrDefault(item => item.Status == FriendsOnlineStatus.Online)?.Count == 1);
|
||||
AddAssert("2 offline users", () => control.Items.FirstOrDefault(item => item.Status == FriendsOnlineStatus.Offline)?.Count == 2);
|
||||
}
|
||||
}
|
||||
}
|
42
osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs
Normal file
42
osu.Game.Tournament.Tests/Components/TestSceneMatchHeader.cs
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Tournament.Components;
|
||||
using osu.Game.Tournament.Screens.Gameplay.Components;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Components
|
||||
{
|
||||
public class TestSceneMatchHeader : TournamentTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(DrawableTournamentHeaderText),
|
||||
typeof(DrawableTournamentHeaderLogo),
|
||||
};
|
||||
|
||||
public TestSceneMatchHeader()
|
||||
{
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(50),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new TournamentSpriteText { Text = "with logo", Font = OsuFont.Torus.With(size: 30) },
|
||||
new MatchHeader(),
|
||||
new TournamentSpriteText { Text = "without logo", Font = OsuFont.Torus.With(size: 30) },
|
||||
new MatchHeader { ShowLogo = false },
|
||||
new TournamentSpriteText { Text = "without scores", Font = OsuFont.Torus.With(size: 30) },
|
||||
new MatchHeader { ShowScores = false },
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Tournament.Components;
|
||||
using osu.Game.Tournament.Models;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Components
|
||||
{
|
||||
public class TestSceneRoundDisplay : TournamentTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(DrawableTournamentHeaderText),
|
||||
typeof(DrawableTournamentHeaderLogo),
|
||||
};
|
||||
|
||||
public TestSceneRoundDisplay()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new RoundDisplay(new TournamentMatch
|
||||
{
|
||||
Round =
|
||||
{
|
||||
Value = new TournamentRound
|
||||
{
|
||||
Name = { Value = "Test Round" }
|
||||
}
|
||||
}
|
||||
})
|
||||
{
|
||||
Margin = new MarginPadding(20)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -22,9 +22,9 @@ namespace osu.Game.Tournament.Components
|
||||
|
||||
public ControlPanel()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
AlwaysPresent = true;
|
||||
Width = 0.15f;
|
||||
Width = TournamentSceneManager.CONTROL_AREA_WIDTH;
|
||||
Anchor = Anchor.TopRight;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
@ -47,8 +47,8 @@ namespace osu.Game.Tournament.Components
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Width = 0.75f,
|
||||
Position = new Vector2(0, 35f),
|
||||
Padding = new MarginPadding(5),
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 5f),
|
||||
},
|
||||
|
@ -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.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
|
||||
namespace osu.Game.Tournament.Components
|
||||
{
|
||||
public class DrawableTournamentHeaderLogo : CompositeDrawable
|
||||
{
|
||||
public DrawableTournamentHeaderLogo()
|
||||
{
|
||||
InternalChild = new LogoSprite();
|
||||
|
||||
Height = 82;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
}
|
||||
|
||||
private class LogoSprite : Sprite
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
FillMode = FillMode.Fit;
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Texture = textures.Get("header-logo");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
|
||||
namespace osu.Game.Tournament.Components
|
||||
{
|
||||
public class DrawableTournamentHeaderText : CompositeDrawable
|
||||
{
|
||||
public DrawableTournamentHeaderText(bool center = true)
|
||||
{
|
||||
InternalChild = new TextSprite
|
||||
{
|
||||
Anchor = center ? Anchor.Centre : Anchor.TopLeft,
|
||||
Origin = center ? Anchor.Centre : Anchor.TopLeft,
|
||||
};
|
||||
|
||||
Height = 22;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
}
|
||||
|
||||
private class TextSprite : Sprite
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
FillMode = FillMode.Fit;
|
||||
|
||||
Texture = textures.Get("header-text");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +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.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Tournament.Components
|
||||
{
|
||||
public class DrawableTournamentTitleText : TournamentSpriteText
|
||||
{
|
||||
public DrawableTournamentTitleText()
|
||||
{
|
||||
Text = "osu!taiko world cup 2020";
|
||||
Font = OsuFont.Torus.With(size: 26, weight: FontWeight.SemiBold);
|
||||
}
|
||||
}
|
||||
}
|
@ -12,19 +12,27 @@ namespace osu.Game.Tournament.Components
|
||||
{
|
||||
public RoundDisplay(TournamentMatch match)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DrawableTournamentTitleText(),
|
||||
new DrawableTournamentHeaderText(false)
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
},
|
||||
new TournamentSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Text = match.Round.Value?.Name.Value ?? "Unknown Round",
|
||||
Font = OsuFont.Torus.With(size: 26, weight: FontWeight.SemiBold)
|
||||
},
|
||||
|
@ -16,7 +16,6 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Tournament.Models;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tournament.Components
|
||||
@ -125,13 +124,21 @@ namespace osu.Game.Tournament.Components
|
||||
|
||||
if (!string.IsNullOrEmpty(mods))
|
||||
{
|
||||
AddInternal(new Sprite
|
||||
AddInternal(new Container
|
||||
{
|
||||
Texture = textures.Get($"mods/{mods}"),
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 60,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Margin = new MarginPadding(10),
|
||||
Scale = new Vector2(0.8f)
|
||||
Child = new Sprite
|
||||
{
|
||||
FillMode = FillMode.Fit,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Texture = textures.Get($"mods/{mods}"),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ namespace osu.Game.Tournament.Components
|
||||
// else if (info.CurrentMatch.Value.Team2.Value.Players.Any(u => u.Id == Message.Sender.Id))
|
||||
// SenderText.Colour = TournamentGame.COLOUR_BLUE;
|
||||
// else if (Message.Sender.Colour != null)
|
||||
// SenderText.Colour = ColourBox.Colour = OsuColour.FromHex(Message.Sender.Colour);
|
||||
// SenderText.Colour = ColourBox.Colour = Color4Extensions.FromHex(Message.Sender.Colour);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Video;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
@ -28,13 +27,13 @@ namespace osu.Game.Tournament.Components
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(Storage storage)
|
||||
private void load(TournamentStorage storage)
|
||||
{
|
||||
var stream = storage.GetStream($@"videos/{filename}.m4v");
|
||||
var stream = storage.GetStream($@"videos/{filename}");
|
||||
|
||||
if (stream != null)
|
||||
{
|
||||
InternalChild = video = new VideoSprite(stream)
|
||||
InternalChild = video = new VideoSprite(stream, false)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fit,
|
||||
|
@ -163,12 +163,7 @@ namespace osu.Game.Tournament.IPC
|
||||
{
|
||||
try
|
||||
{
|
||||
stableInstallPath = "G:\\My Drive\\Main\\osu!tourney";
|
||||
|
||||
if (checkExists(stableInstallPath))
|
||||
return stableInstallPath;
|
||||
|
||||
stableInstallPath = "G:\\My Drive\\Main\\osu!mappool";
|
||||
stableInstallPath = Environment.GetEnvironmentVariable("OSU_STABLE_PATH");
|
||||
|
||||
if (checkExists(stableInstallPath))
|
||||
return stableInstallPath;
|
||||
|
@ -296,7 +296,7 @@ namespace osu.Game.Tournament.Screens.Editors
|
||||
|
||||
private void updatePanel()
|
||||
{
|
||||
drawableContainer.Child = new UserPanel(user) { Width = 300 };
|
||||
drawableContainer.Child = new UserGridPanel(user) { Width = 300 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,14 +2,11 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Tournament.Components;
|
||||
using osu.Game.Tournament.Models;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
{
|
||||
@ -17,13 +14,39 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
{
|
||||
private TeamScoreDisplay teamDisplay1;
|
||||
private TeamScoreDisplay teamDisplay2;
|
||||
private DrawableTournamentHeaderLogo logo;
|
||||
|
||||
private bool showScores = true;
|
||||
|
||||
public bool ShowScores
|
||||
{
|
||||
get => showScores;
|
||||
set
|
||||
{
|
||||
teamDisplay1.ShowScore = value;
|
||||
teamDisplay2.ShowScore = value;
|
||||
if (value == showScores)
|
||||
return;
|
||||
|
||||
showScores = value;
|
||||
|
||||
if (IsLoaded)
|
||||
updateDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
private bool showLogo = true;
|
||||
|
||||
public bool ShowLogo
|
||||
{
|
||||
get => showLogo;
|
||||
set
|
||||
{
|
||||
if (value == showLogo)
|
||||
return;
|
||||
|
||||
showLogo = value;
|
||||
|
||||
if (IsLoaded)
|
||||
updateDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,19 +61,25 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Padding = new MarginPadding(20),
|
||||
Spacing = new Vector2(5),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DrawableTournamentTitleText
|
||||
logo = new DrawableTournamentHeaderLogo
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(1.2f)
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Alpha = showLogo ? 1 : 0
|
||||
},
|
||||
new RoundDisplay
|
||||
new DrawableTournamentHeaderText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
},
|
||||
new MatchRoundDisplay
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Scale = new Vector2(0.4f)
|
||||
},
|
||||
}
|
||||
@ -66,76 +95,16 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
Origin = Anchor.TopRight,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class TeamScoreDisplay : CompositeDrawable
|
||||
{
|
||||
private readonly TeamColour teamColour;
|
||||
|
||||
private readonly Bindable<TournamentMatch> currentMatch = new Bindable<TournamentMatch>();
|
||||
private readonly Bindable<TournamentTeam> currentTeam = new Bindable<TournamentTeam>();
|
||||
private readonly Bindable<int?> currentTeamScore = new Bindable<int?>();
|
||||
|
||||
private TeamDisplay teamDisplay;
|
||||
|
||||
public bool ShowScore { set => teamDisplay.ShowScore = value; }
|
||||
|
||||
public TeamScoreDisplay(TeamColour teamColour)
|
||||
{
|
||||
this.teamColour = teamColour;
|
||||
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
AutoSizeAxes = Axes.X;
|
||||
updateDisplay();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(LadderInfo ladder)
|
||||
private void updateDisplay()
|
||||
{
|
||||
currentMatch.BindTo(ladder.CurrentMatch);
|
||||
currentMatch.BindValueChanged(matchChanged, true);
|
||||
}
|
||||
teamDisplay1.ShowScore = showScores;
|
||||
teamDisplay2.ShowScore = showScores;
|
||||
|
||||
private void matchChanged(ValueChangedEvent<TournamentMatch> match)
|
||||
{
|
||||
currentTeamScore.UnbindBindings();
|
||||
currentTeam.UnbindBindings();
|
||||
|
||||
if (match.NewValue != null)
|
||||
{
|
||||
currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1Score : match.NewValue.Team2Score);
|
||||
currentTeam.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1 : match.NewValue.Team2);
|
||||
}
|
||||
|
||||
// team may change to same team, which means score is not in a good state.
|
||||
// thus we handle this manually.
|
||||
teamChanged(currentTeam.Value);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
switch (e.Button)
|
||||
{
|
||||
case MouseButton.Left:
|
||||
if (currentTeamScore.Value < currentMatch.Value.PointsToWin)
|
||||
currentTeamScore.Value++;
|
||||
return true;
|
||||
|
||||
case MouseButton.Right:
|
||||
if (currentTeamScore.Value > 0)
|
||||
currentTeamScore.Value--;
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseDown(e);
|
||||
}
|
||||
|
||||
private void teamChanged(TournamentTeam team)
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
teamDisplay = new TeamDisplay(team, teamColour, currentTeamScore, currentMatch.Value?.PointsToWin ?? 0),
|
||||
};
|
||||
logo.Alpha = showLogo ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ using osu.Game.Tournament.Models;
|
||||
|
||||
namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
{
|
||||
public class RoundDisplay : TournamentSpriteTextWithBackground
|
||||
public class MatchRoundDisplay : TournamentSpriteTextWithBackground
|
||||
{
|
||||
private readonly Bindable<TournamentMatch> currentMatch = new Bindable<TournamentMatch>();
|
||||
|
@ -11,6 +11,7 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Tournament.IPC;
|
||||
using osu.Game.Tournament.Models;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
{
|
||||
@ -131,13 +132,15 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
Margin = new MarginPadding { Top = bar_height, Horizontal = 10 };
|
||||
|
||||
Winning = false;
|
||||
|
||||
DisplayedCountSpriteText.Spacing = new Vector2(-6);
|
||||
}
|
||||
|
||||
public bool Winning
|
||||
{
|
||||
set => DisplayedCountSpriteText.Font = value
|
||||
? OsuFont.Torus.With(weight: FontWeight.Bold, size: 50)
|
||||
: OsuFont.Torus.With(weight: FontWeight.Regular, size: 40);
|
||||
? OsuFont.Torus.With(weight: FontWeight.Bold, size: 50, fixedWidth: true)
|
||||
: OsuFont.Torus.With(weight: FontWeight.Regular, size: 40, fixedWidth: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
},
|
||||
box = new Box
|
||||
{
|
||||
Colour = OsuColour.FromHex("#FFE8AD"),
|
||||
Colour = Color4Extensions.FromHex("#FFE8AD"),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
};
|
||||
@ -85,7 +85,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = OsuColour.FromHex("#FFE8AD").Opacity(0.1f),
|
||||
Colour = Color4Extensions.FromHex("#FFE8AD").Opacity(0.1f),
|
||||
Hollow = true,
|
||||
Radius = 20,
|
||||
Roundness = 10,
|
||||
|
@ -0,0 +1,94 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Tournament.Models;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
{
|
||||
public class TeamScoreDisplay : CompositeDrawable
|
||||
{
|
||||
private readonly TeamColour teamColour;
|
||||
|
||||
private readonly Bindable<TournamentMatch> currentMatch = new Bindable<TournamentMatch>();
|
||||
private readonly Bindable<TournamentTeam> currentTeam = new Bindable<TournamentTeam>();
|
||||
private readonly Bindable<int?> currentTeamScore = new Bindable<int?>();
|
||||
|
||||
private TeamDisplay teamDisplay;
|
||||
|
||||
public bool ShowScore { set => teamDisplay.ShowScore = value; }
|
||||
|
||||
public TeamScoreDisplay(TeamColour teamColour)
|
||||
{
|
||||
this.teamColour = teamColour;
|
||||
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
AutoSizeAxes = Axes.X;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(LadderInfo ladder)
|
||||
{
|
||||
currentMatch.BindTo(ladder.CurrentMatch);
|
||||
currentMatch.BindValueChanged(matchChanged);
|
||||
|
||||
updateMatch();
|
||||
}
|
||||
|
||||
private void matchChanged(ValueChangedEvent<TournamentMatch> match)
|
||||
{
|
||||
currentTeamScore.UnbindBindings();
|
||||
currentTeam.UnbindBindings();
|
||||
|
||||
Scheduler.AddOnce(updateMatch);
|
||||
}
|
||||
|
||||
private void updateMatch()
|
||||
{
|
||||
var match = currentMatch.Value;
|
||||
|
||||
if (match != null)
|
||||
{
|
||||
match.StartMatch();
|
||||
|
||||
currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.Team1Score : match.Team2Score);
|
||||
currentTeam.BindTo(teamColour == TeamColour.Red ? match.Team1 : match.Team2);
|
||||
}
|
||||
|
||||
// team may change to same team, which means score is not in a good state.
|
||||
// thus we handle this manually.
|
||||
teamChanged(currentTeam.Value);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
switch (e.Button)
|
||||
{
|
||||
case MouseButton.Left:
|
||||
if (currentTeamScore.Value < currentMatch.Value.PointsToWin)
|
||||
currentTeamScore.Value++;
|
||||
return true;
|
||||
|
||||
case MouseButton.Right:
|
||||
if (currentTeamScore.Value > 0)
|
||||
currentTeamScore.Value--;
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseDown(e);
|
||||
}
|
||||
|
||||
private void teamChanged(TournamentTeam team)
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
teamDisplay = new TeamDisplay(team, teamColour, currentTeamScore, currentMatch.Value?.PointsToWin ?? 0),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -47,7 +47,10 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
||||
Loop = true,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
header = new MatchHeader(),
|
||||
header = new MatchHeader
|
||||
{
|
||||
ShowLogo = false
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
@ -85,8 +86,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
this.ladderEditor = ladderEditor;
|
||||
|
||||
colourWinner = losers
|
||||
? OsuColour.FromHex("#8E7F48")
|
||||
: OsuColour.FromHex("#1462AA");
|
||||
? Color4Extensions.FromHex("#8E7F48")
|
||||
: Color4Extensions.FromHex("#1462AA");
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
@ -180,8 +181,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
{
|
||||
bool winner = completed.Value && isWinner?.Invoke() == true;
|
||||
|
||||
background.FadeColour(winner ? Color4.White : OsuColour.FromHex("#444"), winner ? 500 : 0, Easing.OutQuint);
|
||||
backgroundRight.FadeColour(winner ? colourWinner : OsuColour.FromHex("#333"), winner ? 500 : 0, Easing.OutQuint);
|
||||
background.FadeColour(winner ? Color4.White : Color4Extensions.FromHex("#444"), winner ? 500 : 0, Easing.OutQuint);
|
||||
backgroundRight.FadeColour(winner ? colourWinner : Color4Extensions.FromHex("#333"), winner ? 500 : 0, Easing.OutQuint);
|
||||
|
||||
AcronymText.Colour = winner ? Color4.Black : Color4.White;
|
||||
|
||||
|
@ -32,8 +32,8 @@ namespace osu.Game.Tournament.Screens.Ladder
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, Storage storage)
|
||||
{
|
||||
normalPathColour = OsuColour.FromHex("#66D1FF");
|
||||
losersPathColour = OsuColour.FromHex("#FFC700");
|
||||
normalPathColour = Color4Extensions.FromHex("#66D1FF");
|
||||
losersPathColour = Color4Extensions.FromHex("#FFC700");
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
@ -47,7 +47,7 @@ namespace osu.Game.Tournament.Screens.Ladder
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Loop = true,
|
||||
},
|
||||
new DrawableTournamentTitleText
|
||||
new DrawableTournamentHeaderText
|
||||
{
|
||||
Y = 100,
|
||||
Anchor = Anchor.TopCentre,
|
||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Tournament.Screens.MapPool
|
||||
new MatchHeader(),
|
||||
mapFlows = new FillFlowContainer<FillFlowContainer<TournamentBeatmapPanel>>
|
||||
{
|
||||
Y = 100,
|
||||
Y = 140,
|
||||
Spacing = new Vector2(10, 10),
|
||||
Padding = new MarginPadding(25),
|
||||
Direction = FillDirection.Vertical,
|
||||
@ -235,6 +235,7 @@ namespace osu.Game.Tournament.Screens.MapPool
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Height = 42,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ namespace osu.Game.Tournament.Screens.Schedule
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DrawableTournamentTitleText(),
|
||||
new DrawableTournamentHeaderText(),
|
||||
new Container
|
||||
{
|
||||
Margin = new MarginPadding { Top = 40 },
|
||||
|
@ -3,7 +3,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
@ -22,6 +25,7 @@ namespace osu.Game.Tournament.Screens
|
||||
private FillFlowContainer fillFlow;
|
||||
|
||||
private LoginOverlay loginOverlay;
|
||||
private ActionableInfo resolution;
|
||||
|
||||
[Resolved]
|
||||
private MatchIPCInfo ipc { get; set; }
|
||||
@ -32,9 +36,13 @@ namespace osu.Game.Tournament.Screens
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; }
|
||||
|
||||
private Bindable<Size> windowSize;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(FrameworkConfigManager frameworkConfig)
|
||||
{
|
||||
windowSize = frameworkConfig.GetBindable<Size>(FrameworkSetting.WindowedSize);
|
||||
|
||||
InternalChild = fillFlow = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
@ -48,6 +56,9 @@ namespace osu.Game.Tournament.Screens
|
||||
reload();
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private Framework.Game game { get; set; }
|
||||
|
||||
private void reload()
|
||||
{
|
||||
var fileBasedIpc = ipc as FileBasedIPC;
|
||||
@ -97,9 +108,25 @@ namespace osu.Game.Tournament.Screens
|
||||
Items = rulesets.AvailableRulesets,
|
||||
Current = LadderInfo.Ruleset,
|
||||
},
|
||||
resolution = new ActionableInfo
|
||||
{
|
||||
Label = "Stream area resolution",
|
||||
ButtonText = "Set to 1080p",
|
||||
Action = () =>
|
||||
{
|
||||
windowSize.Value = new Size((int)(1920 / TournamentSceneManager.STREAM_AREA_WIDTH * TournamentSceneManager.REQUIRED_WIDTH), 1080);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
resolution.Value = $"{ScreenSpaceDrawQuad.Width:N0}x{ScreenSpaceDrawQuad.Height:N0}";
|
||||
}
|
||||
|
||||
public class LabelledDropdown<T> : LabelledComponent<OsuDropdown<T>, T>
|
||||
{
|
||||
public LabelledDropdown()
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Tournament.Screens.Showcase
|
||||
Origin = Anchor.TopCentre,
|
||||
FillMode = FillMode.Fit,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Texture = textures.Get("game-screen-logo"),
|
||||
Texture = textures.Get("header-logo"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ using osu.Game.Tournament.Components;
|
||||
using osu.Game.Tournament.Models;
|
||||
using osu.Game.Tournament.Screens.Ladder.Components;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tournament.Screens.TeamIntro
|
||||
{
|
||||
@ -140,9 +139,9 @@ namespace osu.Game.Tournament.Screens.TeamIntro
|
||||
Spacing = new Vector2(5),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new TournamentSpriteText { Text = beatmap.BeatmapInfo.Metadata.Title, Colour = Color4.Black, },
|
||||
new TournamentSpriteText { Text = "by", Colour = Color4.Black, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
|
||||
new TournamentSpriteText { Text = beatmap.BeatmapInfo.Metadata.Artist, Colour = Color4.Black, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
|
||||
new TournamentSpriteText { Text = beatmap.BeatmapInfo.Metadata.Title, Colour = TournamentGame.TEXT_COLOUR, },
|
||||
new TournamentSpriteText { Text = "by", Colour = TournamentGame.TEXT_COLOUR, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
|
||||
new TournamentSpriteText { Text = beatmap.BeatmapInfo.Metadata.Artist, Colour = TournamentGame.TEXT_COLOUR, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
|
||||
}
|
||||
},
|
||||
new FillFlowContainer
|
||||
@ -154,8 +153,8 @@ namespace osu.Game.Tournament.Screens.TeamIntro
|
||||
Spacing = new Vector2(40),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new TournamentSpriteText { Text = beatmap.Score.ToString("#,0"), Colour = Color4.Black, Width = 80 },
|
||||
new TournamentSpriteText { Text = "#" + beatmap.Seed.Value.ToString("#,0"), Colour = Color4.Black, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
|
||||
new TournamentSpriteText { Text = beatmap.Score.ToString("#,0"), Colour = TournamentGame.TEXT_COLOUR, Width = 80 },
|
||||
new TournamentSpriteText { Text = "#" + beatmap.Seed.Value.ToString("#,0"), Colour = TournamentGame.TEXT_COLOUR, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -204,7 +203,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Colour = TournamentGame.TEXT_COLOUR,
|
||||
},
|
||||
new TournamentSpriteText
|
||||
{
|
||||
@ -260,20 +259,18 @@ namespace osu.Game.Tournament.Screens.TeamIntro
|
||||
AutoSizeAxes = Axes.Y;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
var colour = OsuColour.Gray(0.3f);
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new TournamentSpriteText
|
||||
{
|
||||
Text = left,
|
||||
Colour = colour,
|
||||
Font = OsuFont.Torus.With(size: 22),
|
||||
Colour = TournamentGame.TEXT_COLOUR,
|
||||
Font = OsuFont.Torus.With(size: 22, weight: FontWeight.SemiBold),
|
||||
},
|
||||
new TournamentSpriteText
|
||||
{
|
||||
Text = right,
|
||||
Colour = colour,
|
||||
Colour = TournamentGame.TEXT_COLOUR,
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopLeft,
|
||||
Font = OsuFont.Torus.With(size: 22, weight: FontWeight.Regular),
|
||||
@ -305,7 +302,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
|
||||
{
|
||||
Text = team?.FullName.Value ?? "???",
|
||||
Font = OsuFont.Torus.With(size: 32, weight: FontWeight.SemiBold),
|
||||
Colour = Color4.Black,
|
||||
Colour = TournamentGame.TEXT_COLOUR,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -1,9 +1,9 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Tournament.Models;
|
||||
using osuTK.Graphics;
|
||||
@ -14,13 +14,13 @@ namespace osu.Game.Tournament
|
||||
{
|
||||
public static ColourInfo GetTeamColour(TeamColour teamColour) => teamColour == TeamColour.Red ? COLOUR_RED : COLOUR_BLUE;
|
||||
|
||||
public static readonly Color4 COLOUR_RED = OsuColour.FromHex("#AA1414");
|
||||
public static readonly Color4 COLOUR_BLUE = OsuColour.FromHex("#1462AA");
|
||||
public static readonly Color4 COLOUR_RED = Color4Extensions.FromHex("#AA1414");
|
||||
public static readonly Color4 COLOUR_BLUE = Color4Extensions.FromHex("#1462AA");
|
||||
|
||||
public static readonly Color4 ELEMENT_BACKGROUND_COLOUR = OsuColour.FromHex("#fff");
|
||||
public static readonly Color4 ELEMENT_FOREGROUND_COLOUR = OsuColour.FromHex("#000");
|
||||
public static readonly Color4 ELEMENT_BACKGROUND_COLOUR = Color4Extensions.FromHex("#fff");
|
||||
public static readonly Color4 ELEMENT_FOREGROUND_COLOUR = Color4Extensions.FromHex("#000");
|
||||
|
||||
public static readonly Color4 TEXT_COLOUR = OsuColour.FromHex("#fff");
|
||||
public static readonly Color4 TEXT_COLOUR = Color4Extensions.FromHex("#fff");
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
|
@ -37,6 +37,8 @@ namespace osu.Game.Tournament
|
||||
|
||||
private Storage storage;
|
||||
|
||||
private TournamentStorage tournamentStorage;
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
private Bindable<Size> windowSize;
|
||||
@ -54,14 +56,16 @@ namespace osu.Game.Tournament
|
||||
{
|
||||
Resources.AddStore(new DllResourceStore(typeof(TournamentGameBase).Assembly));
|
||||
|
||||
Textures.AddStore(new TextureLoaderStore(new ResourceStore<byte[]>(new StorageBackedResourceStore(storage))));
|
||||
dependencies.CacheAs(tournamentStorage = new TournamentStorage(storage));
|
||||
|
||||
Textures.AddStore(new TextureLoaderStore(tournamentStorage));
|
||||
|
||||
this.storage = storage;
|
||||
|
||||
windowSize = frameworkConfig.GetBindable<Size>(FrameworkSetting.WindowedSize);
|
||||
windowSize.BindValueChanged(size => ScheduleAfterChildren(() =>
|
||||
{
|
||||
var minWidth = (int)(size.NewValue.Height / 9f * 16 + 400);
|
||||
var minWidth = (int)(size.NewValue.Height / 768f * TournamentSceneManager.REQUIRED_WIDTH) - 1;
|
||||
|
||||
heightWarning.Alpha = size.NewValue.Width < minWidth ? 1 : 0;
|
||||
}), true);
|
||||
|
@ -33,6 +33,12 @@ namespace osu.Game.Tournament
|
||||
private Container screens;
|
||||
private TourneyVideo video;
|
||||
|
||||
public const float CONTROL_AREA_WIDTH = 160;
|
||||
|
||||
public const float STREAM_AREA_WIDTH = 1366;
|
||||
|
||||
public const double REQUIRED_WIDTH = TournamentSceneManager.CONTROL_AREA_WIDTH * 2 + TournamentSceneManager.STREAM_AREA_WIDTH;
|
||||
|
||||
[Cached]
|
||||
private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay();
|
||||
|
||||
@ -51,13 +57,13 @@ namespace osu.Game.Tournament
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
X = 200,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
X = CONTROL_AREA_WIDTH,
|
||||
FillMode = FillMode.Fit,
|
||||
FillAspectRatio = 16 / 9f,
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Size = new Vector2(0.8f, 1),
|
||||
Width = STREAM_AREA_WIDTH,
|
||||
//Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -96,7 +102,7 @@ namespace osu.Game.Tournament
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 200,
|
||||
Width = CONTROL_AREA_WIDTH,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
@ -108,8 +114,8 @@ namespace osu.Game.Tournament
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(2),
|
||||
Padding = new MarginPadding(2),
|
||||
Spacing = new Vector2(5),
|
||||
Padding = new MarginPadding(5),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new ScreenButton(typeof(SetupScreen)) { Text = "Setup", RequestSelection = SetScreen },
|
||||
|
19
osu.Game.Tournament/TournamentStorage.cs
Normal file
19
osu.Game.Tournament/TournamentStorage.cs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Platform;
|
||||
|
||||
namespace osu.Game.Tournament
|
||||
{
|
||||
internal class TournamentStorage : NamespacedResourceStore<byte[]>
|
||||
{
|
||||
public TournamentStorage(Storage storage)
|
||||
: base(new StorageBackedResourceStore(storage), "tournament")
|
||||
{
|
||||
AddExtension("m4v");
|
||||
AddExtension("avi");
|
||||
AddExtension("mp4");
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
|
||||
namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
@ -26,17 +25,5 @@ namespace osu.Game.Beatmaps.Formats
|
||||
AddDecoder<Beatmap>(@"osu file format v", m => new LegacyDifficultyCalculatorBeatmapDecoder(int.Parse(m.Split('v').Last())));
|
||||
SetFallbackDecoder<Beatmap>(() => new LegacyDifficultyCalculatorBeatmapDecoder());
|
||||
}
|
||||
|
||||
protected override TimingControlPoint CreateTimingControlPoint()
|
||||
=> new LegacyDifficultyCalculatorTimingControlPoint();
|
||||
|
||||
private class LegacyDifficultyCalculatorTimingControlPoint : TimingControlPoint
|
||||
{
|
||||
public LegacyDifficultyCalculatorTimingControlPoint()
|
||||
{
|
||||
BeatLengthBindable.MinValue = double.MinValue;
|
||||
BeatLengthBindable.MaxValue = double.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
@ -60,8 +61,9 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
/// <param name="ruleset">The <see cref="RulesetInfo"/> to create a playable <see cref="IBeatmap"/> for.</param>
|
||||
/// <param name="mods">The <see cref="Mod"/>s to apply to the <see cref="IBeatmap"/>.</param>
|
||||
/// <param name="timeout">The maximum length in milliseconds to wait for load to complete. Defaults to 10,000ms.</param>
|
||||
/// <returns>The converted <see cref="IBeatmap"/>.</returns>
|
||||
/// <exception cref="BeatmapInvalidForRulesetException">If <see cref="Beatmap"/> could not be converted to <paramref name="ruleset"/>.</exception>
|
||||
IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null);
|
||||
IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null, TimeSpan? timeout = null);
|
||||
}
|
||||
}
|
||||
|
@ -83,55 +83,81 @@ namespace osu.Game.Beatmaps
|
||||
/// <returns>The applicable <see cref="IBeatmapConverter"/>.</returns>
|
||||
protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap);
|
||||
|
||||
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null)
|
||||
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null, TimeSpan? timeout = null)
|
||||
{
|
||||
mods ??= Array.Empty<Mod>();
|
||||
|
||||
var rulesetInstance = ruleset.CreateInstance();
|
||||
|
||||
IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance);
|
||||
|
||||
// Check if the beatmap can be converted
|
||||
if (Beatmap.HitObjects.Count > 0 && !converter.CanConvert())
|
||||
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter}).");
|
||||
|
||||
// Apply conversion mods
|
||||
foreach (var mod in mods.OfType<IApplicableToBeatmapConverter>())
|
||||
mod.ApplyToBeatmapConverter(converter);
|
||||
|
||||
// Convert
|
||||
IBeatmap converted = converter.Convert();
|
||||
|
||||
// Apply difficulty mods
|
||||
if (mods.Any(m => m is IApplicableToDifficulty))
|
||||
using (var cancellationSource = new CancellationTokenSource(timeout ?? TimeSpan.FromSeconds(10)))
|
||||
{
|
||||
converted.BeatmapInfo = converted.BeatmapInfo.Clone();
|
||||
converted.BeatmapInfo.BaseDifficulty = converted.BeatmapInfo.BaseDifficulty.Clone();
|
||||
mods ??= Array.Empty<Mod>();
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToDifficulty>())
|
||||
mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty);
|
||||
}
|
||||
var rulesetInstance = ruleset.CreateInstance();
|
||||
|
||||
IBeatmapProcessor processor = rulesetInstance.CreateBeatmapProcessor(converted);
|
||||
IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance);
|
||||
|
||||
processor?.PreProcess();
|
||||
// Check if the beatmap can be converted
|
||||
if (Beatmap.HitObjects.Count > 0 && !converter.CanConvert())
|
||||
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter}).");
|
||||
|
||||
// Compute default values for hitobjects, including creating nested hitobjects in-case they're needed
|
||||
foreach (var obj in converted.HitObjects)
|
||||
obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty);
|
||||
// Apply conversion mods
|
||||
foreach (var mod in mods.OfType<IApplicableToBeatmapConverter>())
|
||||
{
|
||||
if (cancellationSource.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToHitObject>())
|
||||
{
|
||||
mod.ApplyToBeatmapConverter(converter);
|
||||
}
|
||||
|
||||
// Convert
|
||||
IBeatmap converted = converter.Convert();
|
||||
|
||||
// Apply difficulty mods
|
||||
if (mods.Any(m => m is IApplicableToDifficulty))
|
||||
{
|
||||
converted.BeatmapInfo = converted.BeatmapInfo.Clone();
|
||||
converted.BeatmapInfo.BaseDifficulty = converted.BeatmapInfo.BaseDifficulty.Clone();
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToDifficulty>())
|
||||
{
|
||||
if (cancellationSource.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
|
||||
mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty);
|
||||
}
|
||||
}
|
||||
|
||||
IBeatmapProcessor processor = rulesetInstance.CreateBeatmapProcessor(converted);
|
||||
|
||||
processor?.PreProcess();
|
||||
|
||||
// Compute default values for hitobjects, including creating nested hitobjects in-case they're needed
|
||||
foreach (var obj in converted.HitObjects)
|
||||
mod.ApplyToHitObject(obj);
|
||||
{
|
||||
if (cancellationSource.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
|
||||
obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty);
|
||||
}
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToHitObject>())
|
||||
{
|
||||
foreach (var obj in converted.HitObjects)
|
||||
{
|
||||
if (cancellationSource.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
|
||||
mod.ApplyToHitObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
processor?.PostProcess();
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToBeatmap>())
|
||||
{
|
||||
cancellationSource.Token.ThrowIfCancellationRequested();
|
||||
mod.ApplyToBeatmap(converted);
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
processor?.PostProcess();
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToBeatmap>())
|
||||
mod.ApplyToBeatmap(converted);
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
private CancellationTokenSource loadCancellation = new CancellationTokenSource();
|
||||
@ -297,5 +323,13 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
private void recreate() => lazy = new Lazy<T>(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication);
|
||||
}
|
||||
|
||||
private class BeatmapLoadTimeoutException : TimeoutException
|
||||
{
|
||||
public BeatmapLoadTimeoutException(BeatmapInfo beatmapInfo)
|
||||
: base($"Timed out while loading beatmap ({beatmapInfo}).")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,11 +15,13 @@ namespace osu.Game.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// Fired when a <typeparamref name="TModel"/> download begins.
|
||||
/// This is NOT run on the update thread and should be scheduled.
|
||||
/// </summary>
|
||||
event Action<ArchiveDownloadRequest<TModel>> DownloadBegan;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a <typeparamref name="TModel"/> download is interrupted, either due to user cancellation or failure.
|
||||
/// This is NOT run on the update thread and should be scheduled.
|
||||
/// </summary>
|
||||
event Action<ArchiveDownloadRequest<TModel>> DownloadFailed;
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Scoring;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Graphics
|
||||
@ -13,45 +13,6 @@ namespace osu.Game.Graphics
|
||||
public static Color4 Gray(float amt) => new Color4(amt, amt, amt, 1f);
|
||||
public static Color4 Gray(byte amt) => new Color4(amt, amt, amt, 255);
|
||||
|
||||
public static Color4 FromHex(string hex)
|
||||
{
|
||||
var hexSpan = hex[0] == '#' ? hex.AsSpan().Slice(1) : hex.AsSpan();
|
||||
|
||||
switch (hexSpan.Length)
|
||||
{
|
||||
default:
|
||||
throw new ArgumentException(@"Invalid hex string length!");
|
||||
|
||||
case 3:
|
||||
return new Color4(
|
||||
(byte)(byte.Parse(hexSpan.Slice(0, 1), NumberStyles.HexNumber) * 17),
|
||||
(byte)(byte.Parse(hexSpan.Slice(1, 1), NumberStyles.HexNumber) * 17),
|
||||
(byte)(byte.Parse(hexSpan.Slice(2, 1), NumberStyles.HexNumber) * 17),
|
||||
255);
|
||||
|
||||
case 6:
|
||||
return new Color4(
|
||||
byte.Parse(hexSpan.Slice(0, 2), NumberStyles.HexNumber),
|
||||
byte.Parse(hexSpan.Slice(2, 2), NumberStyles.HexNumber),
|
||||
byte.Parse(hexSpan.Slice(4, 2), NumberStyles.HexNumber),
|
||||
255);
|
||||
|
||||
case 4:
|
||||
return new Color4(
|
||||
(byte)(byte.Parse(hexSpan.Slice(0, 1), NumberStyles.HexNumber) * 17),
|
||||
(byte)(byte.Parse(hexSpan.Slice(1, 1), NumberStyles.HexNumber) * 17),
|
||||
(byte)(byte.Parse(hexSpan.Slice(0, 1), NumberStyles.HexNumber) * 17),
|
||||
(byte)(byte.Parse(hexSpan.Slice(0, 1), NumberStyles.HexNumber) * 17));
|
||||
|
||||
case 8:
|
||||
return new Color4(
|
||||
byte.Parse(hexSpan.Slice(0, 2), NumberStyles.HexNumber),
|
||||
byte.Parse(hexSpan.Slice(2, 2), NumberStyles.HexNumber),
|
||||
byte.Parse(hexSpan.Slice(4, 2), NumberStyles.HexNumber),
|
||||
byte.Parse(hexSpan.Slice(6, 2), NumberStyles.HexNumber));
|
||||
}
|
||||
}
|
||||
|
||||
public Color4 ForDifficultyRating(DifficultyRating difficulty, bool useLighterColour = false)
|
||||
{
|
||||
switch (difficulty)
|
||||
@ -77,106 +38,135 @@ namespace osu.Game.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the colour for a <see cref="ScoreRank"/>.
|
||||
/// </summary>
|
||||
public static Color4 ForRank(ScoreRank rank)
|
||||
{
|
||||
switch (rank)
|
||||
{
|
||||
case ScoreRank.XH:
|
||||
case ScoreRank.X:
|
||||
return Color4Extensions.FromHex(@"ce1c9d");
|
||||
|
||||
case ScoreRank.SH:
|
||||
case ScoreRank.S:
|
||||
return Color4Extensions.FromHex(@"00a8b5");
|
||||
|
||||
case ScoreRank.A:
|
||||
return Color4Extensions.FromHex(@"7cce14");
|
||||
|
||||
case ScoreRank.B:
|
||||
return Color4Extensions.FromHex(@"e3b130");
|
||||
|
||||
case ScoreRank.C:
|
||||
return Color4Extensions.FromHex(@"f18252");
|
||||
|
||||
default:
|
||||
return Color4Extensions.FromHex(@"e95353");
|
||||
}
|
||||
}
|
||||
|
||||
// See https://github.com/ppy/osu-web/blob/master/resources/assets/less/colors.less
|
||||
public readonly Color4 PurpleLighter = FromHex(@"eeeeff");
|
||||
public readonly Color4 PurpleLight = FromHex(@"aa88ff");
|
||||
public readonly Color4 PurpleLightAlternative = FromHex(@"cba4da");
|
||||
public readonly Color4 Purple = FromHex(@"8866ee");
|
||||
public readonly Color4 PurpleDark = FromHex(@"6644cc");
|
||||
public readonly Color4 PurpleDarkAlternative = FromHex(@"312436");
|
||||
public readonly Color4 PurpleDarker = FromHex(@"441188");
|
||||
public readonly Color4 PurpleLighter = Color4Extensions.FromHex(@"eeeeff");
|
||||
public readonly Color4 PurpleLight = Color4Extensions.FromHex(@"aa88ff");
|
||||
public readonly Color4 PurpleLightAlternative = Color4Extensions.FromHex(@"cba4da");
|
||||
public readonly Color4 Purple = Color4Extensions.FromHex(@"8866ee");
|
||||
public readonly Color4 PurpleDark = Color4Extensions.FromHex(@"6644cc");
|
||||
public readonly Color4 PurpleDarkAlternative = Color4Extensions.FromHex(@"312436");
|
||||
public readonly Color4 PurpleDarker = Color4Extensions.FromHex(@"441188");
|
||||
|
||||
public readonly Color4 PinkLighter = FromHex(@"ffddee");
|
||||
public readonly Color4 PinkLight = FromHex(@"ff99cc");
|
||||
public readonly Color4 Pink = FromHex(@"ff66aa");
|
||||
public readonly Color4 PinkDark = FromHex(@"cc5288");
|
||||
public readonly Color4 PinkDarker = FromHex(@"bb1177");
|
||||
public readonly Color4 PinkLighter = Color4Extensions.FromHex(@"ffddee");
|
||||
public readonly Color4 PinkLight = Color4Extensions.FromHex(@"ff99cc");
|
||||
public readonly Color4 Pink = Color4Extensions.FromHex(@"ff66aa");
|
||||
public readonly Color4 PinkDark = Color4Extensions.FromHex(@"cc5288");
|
||||
public readonly Color4 PinkDarker = Color4Extensions.FromHex(@"bb1177");
|
||||
|
||||
public readonly Color4 BlueLighter = FromHex(@"ddffff");
|
||||
public readonly Color4 BlueLight = FromHex(@"99eeff");
|
||||
public readonly Color4 Blue = FromHex(@"66ccff");
|
||||
public readonly Color4 BlueDark = FromHex(@"44aadd");
|
||||
public readonly Color4 BlueDarker = FromHex(@"2299bb");
|
||||
public readonly Color4 BlueLighter = Color4Extensions.FromHex(@"ddffff");
|
||||
public readonly Color4 BlueLight = Color4Extensions.FromHex(@"99eeff");
|
||||
public readonly Color4 Blue = Color4Extensions.FromHex(@"66ccff");
|
||||
public readonly Color4 BlueDark = Color4Extensions.FromHex(@"44aadd");
|
||||
public readonly Color4 BlueDarker = Color4Extensions.FromHex(@"2299bb");
|
||||
|
||||
public readonly Color4 YellowLighter = FromHex(@"ffffdd");
|
||||
public readonly Color4 YellowLight = FromHex(@"ffdd55");
|
||||
public readonly Color4 Yellow = FromHex(@"ffcc22");
|
||||
public readonly Color4 YellowDark = FromHex(@"eeaa00");
|
||||
public readonly Color4 YellowDarker = FromHex(@"cc6600");
|
||||
public readonly Color4 YellowLighter = Color4Extensions.FromHex(@"ffffdd");
|
||||
public readonly Color4 YellowLight = Color4Extensions.FromHex(@"ffdd55");
|
||||
public readonly Color4 Yellow = Color4Extensions.FromHex(@"ffcc22");
|
||||
public readonly Color4 YellowDark = Color4Extensions.FromHex(@"eeaa00");
|
||||
public readonly Color4 YellowDarker = Color4Extensions.FromHex(@"cc6600");
|
||||
|
||||
public readonly Color4 GreenLighter = FromHex(@"eeffcc");
|
||||
public readonly Color4 GreenLight = FromHex(@"b3d944");
|
||||
public readonly Color4 Green = FromHex(@"88b300");
|
||||
public readonly Color4 GreenDark = FromHex(@"668800");
|
||||
public readonly Color4 GreenDarker = FromHex(@"445500");
|
||||
public readonly Color4 GreenLighter = Color4Extensions.FromHex(@"eeffcc");
|
||||
public readonly Color4 GreenLight = Color4Extensions.FromHex(@"b3d944");
|
||||
public readonly Color4 Green = Color4Extensions.FromHex(@"88b300");
|
||||
public readonly Color4 GreenDark = Color4Extensions.FromHex(@"668800");
|
||||
public readonly Color4 GreenDarker = Color4Extensions.FromHex(@"445500");
|
||||
|
||||
public readonly Color4 Sky = FromHex(@"6bb5ff");
|
||||
public readonly Color4 GreySkyLighter = FromHex(@"c6e3f4");
|
||||
public readonly Color4 GreySkyLight = FromHex(@"8ab3cc");
|
||||
public readonly Color4 GreySky = FromHex(@"405461");
|
||||
public readonly Color4 GreySkyDark = FromHex(@"303d47");
|
||||
public readonly Color4 GreySkyDarker = FromHex(@"21272c");
|
||||
public readonly Color4 Sky = Color4Extensions.FromHex(@"6bb5ff");
|
||||
public readonly Color4 GreySkyLighter = Color4Extensions.FromHex(@"c6e3f4");
|
||||
public readonly Color4 GreySkyLight = Color4Extensions.FromHex(@"8ab3cc");
|
||||
public readonly Color4 GreySky = Color4Extensions.FromHex(@"405461");
|
||||
public readonly Color4 GreySkyDark = Color4Extensions.FromHex(@"303d47");
|
||||
public readonly Color4 GreySkyDarker = Color4Extensions.FromHex(@"21272c");
|
||||
|
||||
public readonly Color4 Seafoam = FromHex(@"05ffa2");
|
||||
public readonly Color4 GreySeafoamLighter = FromHex(@"9ebab1");
|
||||
public readonly Color4 GreySeafoamLight = FromHex(@"4d7365");
|
||||
public readonly Color4 GreySeafoam = FromHex(@"33413c");
|
||||
public readonly Color4 GreySeafoamDark = FromHex(@"2c3532");
|
||||
public readonly Color4 GreySeafoamDarker = FromHex(@"1e2422");
|
||||
public readonly Color4 Seafoam = Color4Extensions.FromHex(@"05ffa2");
|
||||
public readonly Color4 GreySeafoamLighter = Color4Extensions.FromHex(@"9ebab1");
|
||||
public readonly Color4 GreySeafoamLight = Color4Extensions.FromHex(@"4d7365");
|
||||
public readonly Color4 GreySeafoam = Color4Extensions.FromHex(@"33413c");
|
||||
public readonly Color4 GreySeafoamDark = Color4Extensions.FromHex(@"2c3532");
|
||||
public readonly Color4 GreySeafoamDarker = Color4Extensions.FromHex(@"1e2422");
|
||||
|
||||
public readonly Color4 Cyan = FromHex(@"05f4fd");
|
||||
public readonly Color4 GreyCyanLighter = FromHex(@"77b1b3");
|
||||
public readonly Color4 GreyCyanLight = FromHex(@"436d6f");
|
||||
public readonly Color4 GreyCyan = FromHex(@"293d3e");
|
||||
public readonly Color4 GreyCyanDark = FromHex(@"243536");
|
||||
public readonly Color4 GreyCyanDarker = FromHex(@"1e2929");
|
||||
public readonly Color4 Cyan = Color4Extensions.FromHex(@"05f4fd");
|
||||
public readonly Color4 GreyCyanLighter = Color4Extensions.FromHex(@"77b1b3");
|
||||
public readonly Color4 GreyCyanLight = Color4Extensions.FromHex(@"436d6f");
|
||||
public readonly Color4 GreyCyan = Color4Extensions.FromHex(@"293d3e");
|
||||
public readonly Color4 GreyCyanDark = Color4Extensions.FromHex(@"243536");
|
||||
public readonly Color4 GreyCyanDarker = Color4Extensions.FromHex(@"1e2929");
|
||||
|
||||
public readonly Color4 Lime = FromHex(@"82ff05");
|
||||
public readonly Color4 GreyLimeLighter = FromHex(@"deff87");
|
||||
public readonly Color4 GreyLimeLight = FromHex(@"657259");
|
||||
public readonly Color4 GreyLime = FromHex(@"3f443a");
|
||||
public readonly Color4 GreyLimeDark = FromHex(@"32352e");
|
||||
public readonly Color4 GreyLimeDarker = FromHex(@"2e302b");
|
||||
public readonly Color4 Lime = Color4Extensions.FromHex(@"82ff05");
|
||||
public readonly Color4 GreyLimeLighter = Color4Extensions.FromHex(@"deff87");
|
||||
public readonly Color4 GreyLimeLight = Color4Extensions.FromHex(@"657259");
|
||||
public readonly Color4 GreyLime = Color4Extensions.FromHex(@"3f443a");
|
||||
public readonly Color4 GreyLimeDark = Color4Extensions.FromHex(@"32352e");
|
||||
public readonly Color4 GreyLimeDarker = Color4Extensions.FromHex(@"2e302b");
|
||||
|
||||
public readonly Color4 Violet = FromHex(@"bf04ff");
|
||||
public readonly Color4 GreyVioletLighter = FromHex(@"ebb8fe");
|
||||
public readonly Color4 GreyVioletLight = FromHex(@"685370");
|
||||
public readonly Color4 GreyViolet = FromHex(@"46334d");
|
||||
public readonly Color4 GreyVioletDark = FromHex(@"2c2230");
|
||||
public readonly Color4 GreyVioletDarker = FromHex(@"201823");
|
||||
public readonly Color4 Violet = Color4Extensions.FromHex(@"bf04ff");
|
||||
public readonly Color4 GreyVioletLighter = Color4Extensions.FromHex(@"ebb8fe");
|
||||
public readonly Color4 GreyVioletLight = Color4Extensions.FromHex(@"685370");
|
||||
public readonly Color4 GreyViolet = Color4Extensions.FromHex(@"46334d");
|
||||
public readonly Color4 GreyVioletDark = Color4Extensions.FromHex(@"2c2230");
|
||||
public readonly Color4 GreyVioletDarker = Color4Extensions.FromHex(@"201823");
|
||||
|
||||
public readonly Color4 Carmine = FromHex(@"ff0542");
|
||||
public readonly Color4 GreyCarmineLighter = FromHex(@"deaab4");
|
||||
public readonly Color4 GreyCarmineLight = FromHex(@"644f53");
|
||||
public readonly Color4 GreyCarmine = FromHex(@"342b2d");
|
||||
public readonly Color4 GreyCarmineDark = FromHex(@"302a2b");
|
||||
public readonly Color4 GreyCarmineDarker = FromHex(@"241d1e");
|
||||
public readonly Color4 Carmine = Color4Extensions.FromHex(@"ff0542");
|
||||
public readonly Color4 GreyCarmineLighter = Color4Extensions.FromHex(@"deaab4");
|
||||
public readonly Color4 GreyCarmineLight = Color4Extensions.FromHex(@"644f53");
|
||||
public readonly Color4 GreyCarmine = Color4Extensions.FromHex(@"342b2d");
|
||||
public readonly Color4 GreyCarmineDark = Color4Extensions.FromHex(@"302a2b");
|
||||
public readonly Color4 GreyCarmineDarker = Color4Extensions.FromHex(@"241d1e");
|
||||
|
||||
public readonly Color4 Gray0 = FromHex(@"000");
|
||||
public readonly Color4 Gray1 = FromHex(@"111");
|
||||
public readonly Color4 Gray2 = FromHex(@"222");
|
||||
public readonly Color4 Gray3 = FromHex(@"333");
|
||||
public readonly Color4 Gray4 = FromHex(@"444");
|
||||
public readonly Color4 Gray5 = FromHex(@"555");
|
||||
public readonly Color4 Gray6 = FromHex(@"666");
|
||||
public readonly Color4 Gray7 = FromHex(@"777");
|
||||
public readonly Color4 Gray8 = FromHex(@"888");
|
||||
public readonly Color4 Gray9 = FromHex(@"999");
|
||||
public readonly Color4 GrayA = FromHex(@"aaa");
|
||||
public readonly Color4 GrayB = FromHex(@"bbb");
|
||||
public readonly Color4 GrayC = FromHex(@"ccc");
|
||||
public readonly Color4 GrayD = FromHex(@"ddd");
|
||||
public readonly Color4 GrayE = FromHex(@"eee");
|
||||
public readonly Color4 GrayF = FromHex(@"fff");
|
||||
public readonly Color4 Gray0 = Color4Extensions.FromHex(@"000");
|
||||
public readonly Color4 Gray1 = Color4Extensions.FromHex(@"111");
|
||||
public readonly Color4 Gray2 = Color4Extensions.FromHex(@"222");
|
||||
public readonly Color4 Gray3 = Color4Extensions.FromHex(@"333");
|
||||
public readonly Color4 Gray4 = Color4Extensions.FromHex(@"444");
|
||||
public readonly Color4 Gray5 = Color4Extensions.FromHex(@"555");
|
||||
public readonly Color4 Gray6 = Color4Extensions.FromHex(@"666");
|
||||
public readonly Color4 Gray7 = Color4Extensions.FromHex(@"777");
|
||||
public readonly Color4 Gray8 = Color4Extensions.FromHex(@"888");
|
||||
public readonly Color4 Gray9 = Color4Extensions.FromHex(@"999");
|
||||
public readonly Color4 GrayA = Color4Extensions.FromHex(@"aaa");
|
||||
public readonly Color4 GrayB = Color4Extensions.FromHex(@"bbb");
|
||||
public readonly Color4 GrayC = Color4Extensions.FromHex(@"ccc");
|
||||
public readonly Color4 GrayD = Color4Extensions.FromHex(@"ddd");
|
||||
public readonly Color4 GrayE = Color4Extensions.FromHex(@"eee");
|
||||
public readonly Color4 GrayF = Color4Extensions.FromHex(@"fff");
|
||||
|
||||
public readonly Color4 RedLighter = FromHex(@"ffeded");
|
||||
public readonly Color4 RedLight = FromHex(@"ed7787");
|
||||
public readonly Color4 Red = FromHex(@"ed1121");
|
||||
public readonly Color4 RedDark = FromHex(@"ba0011");
|
||||
public readonly Color4 RedDarker = FromHex(@"870000");
|
||||
public readonly Color4 RedLighter = Color4Extensions.FromHex(@"ffeded");
|
||||
public readonly Color4 RedLight = Color4Extensions.FromHex(@"ed7787");
|
||||
public readonly Color4 Red = Color4Extensions.FromHex(@"ed1121");
|
||||
public readonly Color4 RedDark = Color4Extensions.FromHex(@"ba0011");
|
||||
public readonly Color4 RedDarker = Color4Extensions.FromHex(@"870000");
|
||||
|
||||
public readonly Color4 ChatBlue = FromHex(@"17292e");
|
||||
public readonly Color4 ChatBlue = Color4Extensions.FromHex(@"17292e");
|
||||
|
||||
public readonly Color4 ContextMenuGray = FromHex(@"223034");
|
||||
public readonly Color4 ContextMenuGray = Color4Extensions.FromHex(@"223034");
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,15 @@ namespace osu.Game.Graphics
|
||||
/// <param name="italics">Whether the font is italic.</param>
|
||||
/// <param name="fixedWidth">Whether all characters should be spaced the same distance apart.</param>
|
||||
/// <returns>The <see cref="FontUsage"/>.</returns>
|
||||
public static FontUsage GetFont(Typeface typeface = Typeface.Exo, float size = DEFAULT_FONT_SIZE, FontWeight weight = FontWeight.Medium, bool italics = false, bool fixedWidth = false)
|
||||
=> new FontUsage(GetFamilyString(typeface), size, GetWeightString(typeface, weight), italics, fixedWidth);
|
||||
public static FontUsage GetFont(Typeface typeface = Typeface.Torus, float size = DEFAULT_FONT_SIZE, FontWeight weight = FontWeight.Medium, bool italics = false, bool fixedWidth = false)
|
||||
=> new FontUsage(GetFamilyString(typeface), size, GetWeightString(typeface, weight), getItalics(italics), fixedWidth);
|
||||
|
||||
private static bool getItalics(in bool italicsRequested)
|
||||
{
|
||||
// right now none of our fonts support italics.
|
||||
// should add exceptions to this rule if they come up.
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the string representation of a <see cref="Typeface"/>.
|
||||
@ -42,9 +49,6 @@ namespace osu.Game.Graphics
|
||||
{
|
||||
switch (typeface)
|
||||
{
|
||||
case Typeface.Exo:
|
||||
return "Exo2.0";
|
||||
|
||||
case Typeface.Venera:
|
||||
return "Venera";
|
||||
|
||||
@ -62,7 +66,13 @@ namespace osu.Game.Graphics
|
||||
/// <param name="weight">The <see cref="FontWeight"/>.</param>
|
||||
/// <returns>The string representation of <paramref name="weight"/> in the specified <paramref name="typeface"/>.</returns>
|
||||
public static string GetWeightString(Typeface typeface, FontWeight weight)
|
||||
=> GetWeightString(GetFamilyString(typeface), weight);
|
||||
{
|
||||
if (typeface == Typeface.Torus && weight == FontWeight.Medium)
|
||||
// torus doesn't have a medium; fallback to regular.
|
||||
weight = FontWeight.Regular;
|
||||
|
||||
return GetWeightString(GetFamilyString(typeface), weight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the string representation of a <see cref="FontWeight"/>.
|
||||
@ -96,7 +106,6 @@ namespace osu.Game.Graphics
|
||||
|
||||
public enum Typeface
|
||||
{
|
||||
Exo,
|
||||
Venera,
|
||||
Torus
|
||||
}
|
||||
|
@ -43,6 +43,18 @@ namespace osu.Game.Graphics.Sprites
|
||||
set => blurredText.Colour = value;
|
||||
}
|
||||
|
||||
public Vector2 Spacing
|
||||
{
|
||||
get => spriteText.Spacing;
|
||||
set => spriteText.Spacing = blurredText.Spacing = value;
|
||||
}
|
||||
|
||||
public bool UseFullGlyphHeight
|
||||
{
|
||||
get => spriteText.UseFullGlyphHeight;
|
||||
set => spriteText.UseFullGlyphHeight = blurredText.UseFullGlyphHeight = value;
|
||||
}
|
||||
|
||||
public GlowingSpriteText()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
@ -4,6 +4,7 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
@ -38,7 +39,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
sampleClick = audio.Samples.Get(@"UI/generic-select");
|
||||
|
||||
BackgroundColour = Color4.Transparent;
|
||||
BackgroundColourHover = OsuColour.FromHex(@"172023");
|
||||
BackgroundColourHover = Color4Extensions.FromHex(@"172023");
|
||||
|
||||
updateTextColour();
|
||||
}
|
||||
@ -57,7 +58,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
break;
|
||||
|
||||
case MenuItemType.Highlighted:
|
||||
text.Colour = OsuColour.FromHex(@"ffcc22");
|
||||
text.Colour = Color4Extensions.FromHex(@"ffcc22");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
new HoverClickSounds()
|
||||
};
|
||||
|
||||
Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Exo, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true);
|
||||
Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Torus, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true);
|
||||
}
|
||||
|
||||
protected override void OnActivated() => fadeActive();
|
||||
|
@ -78,7 +78,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
new HoverClickSounds()
|
||||
};
|
||||
|
||||
Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Exo, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true);
|
||||
Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Torus, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true);
|
||||
}
|
||||
|
||||
protected virtual string CreateText() => (Value as Enum)?.GetDescription() ?? Value.ToString();
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@ -42,7 +43,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.FromHex("1c2125"),
|
||||
Colour = Color4Extensions.FromHex("1c2125"),
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
|
@ -12,5 +12,6 @@ namespace osu.Game.Online.Chat
|
||||
Temporary,
|
||||
PM,
|
||||
Group,
|
||||
System,
|
||||
}
|
||||
}
|
||||
|
@ -53,17 +53,17 @@ namespace osu.Game.Online
|
||||
manager.ItemRemoved += itemRemoved;
|
||||
}
|
||||
|
||||
private void downloadBegan(ArchiveDownloadRequest<TModel> request)
|
||||
private void downloadBegan(ArchiveDownloadRequest<TModel> request) => Schedule(() =>
|
||||
{
|
||||
if (request.Model.Equals(Model.Value))
|
||||
attachDownload(request);
|
||||
}
|
||||
});
|
||||
|
||||
private void downloadFailed(ArchiveDownloadRequest<TModel> request)
|
||||
private void downloadFailed(ArchiveDownloadRequest<TModel> request) => Schedule(() =>
|
||||
{
|
||||
if (request.Model.Equals(Model.Value))
|
||||
attachDownload(null);
|
||||
}
|
||||
});
|
||||
|
||||
private ArchiveDownloadRequest<TModel> attachedRequest;
|
||||
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
FillMode = FillMode.Fit;
|
||||
FillAspectRatio = 2;
|
||||
|
||||
var rankColour = getRankColour();
|
||||
var rankColour = OsuColour.ForRank(rank);
|
||||
InternalChild = new DrawSizePreservingFillContainer
|
||||
{
|
||||
TargetDrawSize = new Vector2(64, 32),
|
||||
@ -59,7 +59,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
Padding = new MarginPadding { Top = 5 },
|
||||
Colour = getRankNameColour(),
|
||||
Font = OsuFont.Numeric.With(size: 25),
|
||||
Text = getRankName(),
|
||||
Text = GetRankName(rank),
|
||||
ShadowColour = Color4.Black.Opacity(0.3f),
|
||||
ShadowOffset = new Vector2(0, 0.08f),
|
||||
Shadow = true,
|
||||
@ -69,36 +69,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
};
|
||||
}
|
||||
|
||||
private string getRankName() => rank.GetDescription().TrimEnd('+');
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the grade background colour.
|
||||
/// </summary>
|
||||
private Color4 getRankColour()
|
||||
{
|
||||
switch (rank)
|
||||
{
|
||||
case ScoreRank.XH:
|
||||
case ScoreRank.X:
|
||||
return OsuColour.FromHex(@"ce1c9d");
|
||||
|
||||
case ScoreRank.SH:
|
||||
case ScoreRank.S:
|
||||
return OsuColour.FromHex(@"00a8b5");
|
||||
|
||||
case ScoreRank.A:
|
||||
return OsuColour.FromHex(@"7cce14");
|
||||
|
||||
case ScoreRank.B:
|
||||
return OsuColour.FromHex(@"e3b130");
|
||||
|
||||
case ScoreRank.C:
|
||||
return OsuColour.FromHex(@"f18252");
|
||||
|
||||
default:
|
||||
return OsuColour.FromHex(@"e95353");
|
||||
}
|
||||
}
|
||||
public static string GetRankName(ScoreRank rank) => rank.GetDescription().TrimEnd('+');
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the grade text colour.
|
||||
@ -109,23 +80,23 @@ namespace osu.Game.Online.Leaderboards
|
||||
{
|
||||
case ScoreRank.XH:
|
||||
case ScoreRank.SH:
|
||||
return ColourInfo.GradientVertical(Color4.White, OsuColour.FromHex("afdff0"));
|
||||
return ColourInfo.GradientVertical(Color4.White, Color4Extensions.FromHex("afdff0"));
|
||||
|
||||
case ScoreRank.X:
|
||||
case ScoreRank.S:
|
||||
return ColourInfo.GradientVertical(OsuColour.FromHex(@"ffe7a8"), OsuColour.FromHex(@"ffb800"));
|
||||
return ColourInfo.GradientVertical(Color4Extensions.FromHex(@"ffe7a8"), Color4Extensions.FromHex(@"ffb800"));
|
||||
|
||||
case ScoreRank.A:
|
||||
return OsuColour.FromHex(@"275227");
|
||||
return Color4Extensions.FromHex(@"275227");
|
||||
|
||||
case ScoreRank.B:
|
||||
return OsuColour.FromHex(@"553a2b");
|
||||
return Color4Extensions.FromHex(@"553a2b");
|
||||
|
||||
case ScoreRank.C:
|
||||
return OsuColour.FromHex(@"473625");
|
||||
return Color4Extensions.FromHex(@"473625");
|
||||
|
||||
default:
|
||||
return OsuColour.FromHex(@"512525");
|
||||
return Color4Extensions.FromHex(@"512525");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
scoreLabel = new GlowingSpriteText
|
||||
{
|
||||
TextColour = Color4.White,
|
||||
GlowColour = OsuColour.FromHex(@"83ccfa"),
|
||||
GlowColour = Color4Extensions.FromHex(@"83ccfa"),
|
||||
Text = score.TotalScore.ToString(@"N0"),
|
||||
Font = OsuFont.Numeric.With(size: 23),
|
||||
},
|
||||
@ -325,7 +325,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(icon_size),
|
||||
Rotation = 45,
|
||||
Colour = OsuColour.FromHex(@"3087ac"),
|
||||
Colour = Color4Extensions.FromHex(@"3087ac"),
|
||||
Icon = FontAwesome.Solid.Square,
|
||||
Shadow = true,
|
||||
},
|
||||
@ -334,7 +334,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(icon_size - 6),
|
||||
Colour = OsuColour.FromHex(@"a4edff"),
|
||||
Colour = Color4Extensions.FromHex(@"a4edff"),
|
||||
Icon = statistic.Icon,
|
||||
},
|
||||
},
|
||||
@ -344,7 +344,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
TextColour = Color4.White,
|
||||
GlowColour = OsuColour.FromHex(@"83ccfa"),
|
||||
GlowColour = Color4Extensions.FromHex(@"83ccfa"),
|
||||
Text = statistic.Value,
|
||||
Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold),
|
||||
},
|
||||
|
@ -138,30 +138,17 @@ namespace osu.Game
|
||||
dependencies.Cache(LocalConfig);
|
||||
|
||||
AddFont(Resources, @"Fonts/osuFont");
|
||||
AddFont(Resources, @"Fonts/Exo2.0-Medium");
|
||||
AddFont(Resources, @"Fonts/Exo2.0-MediumItalic");
|
||||
|
||||
AddFont(Resources, @"Fonts/Torus-Regular");
|
||||
AddFont(Resources, @"Fonts/Torus-Light");
|
||||
AddFont(Resources, @"Fonts/Torus-SemiBold");
|
||||
AddFont(Resources, @"Fonts/Torus-Bold");
|
||||
|
||||
AddFont(Resources, @"Fonts/Noto-Basic");
|
||||
AddFont(Resources, @"Fonts/Noto-Hangul");
|
||||
AddFont(Resources, @"Fonts/Noto-CJK-Basic");
|
||||
AddFont(Resources, @"Fonts/Noto-CJK-Compatibility");
|
||||
|
||||
AddFont(Resources, @"Fonts/Exo2.0-Regular");
|
||||
AddFont(Resources, @"Fonts/Exo2.0-RegularItalic");
|
||||
AddFont(Resources, @"Fonts/Exo2.0-SemiBold");
|
||||
AddFont(Resources, @"Fonts/Exo2.0-SemiBoldItalic");
|
||||
AddFont(Resources, @"Fonts/Exo2.0-Bold");
|
||||
AddFont(Resources, @"Fonts/Exo2.0-BoldItalic");
|
||||
AddFont(Resources, @"Fonts/Exo2.0-Light");
|
||||
AddFont(Resources, @"Fonts/Exo2.0-LightItalic");
|
||||
AddFont(Resources, @"Fonts/Exo2.0-Black");
|
||||
AddFont(Resources, @"Fonts/Exo2.0-BlackItalic");
|
||||
|
||||
AddFont(Resources, @"Fonts/Torus-SemiBold");
|
||||
AddFont(Resources, @"Fonts/Torus-Bold");
|
||||
AddFont(Resources, @"Fonts/Torus-Regular");
|
||||
AddFont(Resources, @"Fonts/Torus-Light");
|
||||
|
||||
AddFont(Resources, @"Fonts/Venera-Light");
|
||||
AddFont(Resources, @"Fonts/Venera-Bold");
|
||||
AddFont(Resources, @"Fonts/Venera-Black");
|
||||
|
@ -132,7 +132,7 @@ namespace osu.Game.Overlays.AccountCreation
|
||||
usernameDescription.AddText("This will be your public presence. No profanity, no impersonation. Avoid exposing your own personal details, too!");
|
||||
|
||||
emailAddressDescription.AddText("Will be used for notifications, account verification and in the case you forget your password. No spam, ever.");
|
||||
emailAddressDescription.AddText(" Make sure to get it right!", cp => cp.Font = cp.Font.With(Typeface.Exo, weight: FontWeight.Bold));
|
||||
emailAddressDescription.AddText(" Make sure to get it right!", cp => cp.Font = cp.Font.With(Typeface.Torus, weight: FontWeight.Bold));
|
||||
|
||||
passwordDescription.AddText("At least ");
|
||||
characterCheckText = passwordDescription.AddText("8 characters long");
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
@ -124,7 +125,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
Icon = FontAwesome.Solid.Square,
|
||||
Size = new Vector2(12),
|
||||
Rotation = 45,
|
||||
Colour = OsuColour.FromHex(@"441288"),
|
||||
Colour = Color4Extensions.FromHex(@"441288"),
|
||||
},
|
||||
new SpriteIcon
|
||||
{
|
||||
@ -132,7 +133,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
Origin = Anchor.Centre,
|
||||
Icon = icon,
|
||||
Size = new Vector2(12),
|
||||
Colour = OsuColour.FromHex(@"f7dd55"),
|
||||
Colour = Color4Extensions.FromHex(@"f7dd55"),
|
||||
Scale = new Vector2(0.8f),
|
||||
},
|
||||
value = new OsuSpriteText
|
||||
|
@ -2,8 +2,8 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet.Buttons
|
||||
@ -19,9 +19,9 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
BackgroundColour = OsuColour.FromHex(@"094c5f");
|
||||
Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b");
|
||||
Triangles.ColourDark = OsuColour.FromHex(@"094c5f");
|
||||
BackgroundColour = Color4Extensions.FromHex(@"094c5f");
|
||||
Triangles.ColourLight = Color4Extensions.FromHex(@"0f7c9b");
|
||||
Triangles.ColourDark = Color4Extensions.FromHex(@"094c5f");
|
||||
Triangles.TriangleScale = 1.5f;
|
||||
}
|
||||
}
|
||||
|
@ -52,22 +52,28 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
highAccuracyColour = colours.GreenLight;
|
||||
}
|
||||
|
||||
public IReadOnlyList<ScoreInfo> Scores
|
||||
private bool showPerformancePoints;
|
||||
|
||||
public void DisplayScores(IReadOnlyList<ScoreInfo> scores, bool showPerformanceColumn)
|
||||
{
|
||||
set
|
||||
{
|
||||
Content = null;
|
||||
backgroundFlow.Clear();
|
||||
ClearScores();
|
||||
|
||||
if (value?.Any() != true)
|
||||
return;
|
||||
if (!scores.Any())
|
||||
return;
|
||||
|
||||
for (int i = 0; i < value.Count; i++)
|
||||
backgroundFlow.Add(new ScoreTableRowBackground(i, value[i], row_height));
|
||||
showPerformancePoints = showPerformanceColumn;
|
||||
|
||||
Columns = createHeaders(value[0]);
|
||||
Content = value.Select((s, i) => createContent(i, s)).ToArray().ToRectangular();
|
||||
}
|
||||
for (int i = 0; i < scores.Count; i++)
|
||||
backgroundFlow.Add(new ScoreTableRowBackground(i, scores[i], row_height));
|
||||
|
||||
Columns = createHeaders(scores.FirstOrDefault());
|
||||
Content = scores.Select((s, i) => createContent(i, s)).ToArray().ToRectangular();
|
||||
}
|
||||
|
||||
public void ClearScores()
|
||||
{
|
||||
Content = null;
|
||||
backgroundFlow.Clear();
|
||||
}
|
||||
|
||||
private TableColumn[] createHeaders(ScoreInfo score)
|
||||
@ -88,11 +94,10 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
|
||||
columns.Add(new TableColumn(score.SortedStatistics.LastOrDefault().Key.GetDescription(), Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 45, maxSize: 95)));
|
||||
|
||||
columns.AddRange(new[]
|
||||
{
|
||||
new TableColumn("pp", Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, 30)),
|
||||
new TableColumn("mods", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)),
|
||||
});
|
||||
if (showPerformancePoints)
|
||||
columns.Add(new TableColumn("pp", Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, 30)));
|
||||
|
||||
columns.Add(new TableColumn("mods", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)));
|
||||
|
||||
return columns.ToArray();
|
||||
}
|
||||
@ -150,24 +155,25 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
});
|
||||
}
|
||||
|
||||
content.AddRange(new Drawable[]
|
||||
if (showPerformancePoints)
|
||||
{
|
||||
new OsuSpriteText
|
||||
content.Add(new OsuSpriteText
|
||||
{
|
||||
Text = $@"{score.PP:N0}",
|
||||
Font = OsuFont.GetFont(size: text_size)
|
||||
},
|
||||
new FillFlowContainer
|
||||
});
|
||||
}
|
||||
|
||||
content.Add(new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Spacing = new Vector2(1),
|
||||
ChildrenEnumerable = score.Mods.Select(m => new ModIcon(m)
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Spacing = new Vector2(1),
|
||||
ChildrenEnumerable = score.Mods.Select(m => new ModIcon(m)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(0.3f)
|
||||
})
|
||||
},
|
||||
Scale = new Vector2(0.3f)
|
||||
})
|
||||
});
|
||||
|
||||
return content.ToArray();
|
||||
|
@ -52,17 +52,17 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
|
||||
if (value?.Scores.Any() != true)
|
||||
{
|
||||
scoreTable.Scores = null;
|
||||
scoreTable.ClearScores();
|
||||
scoreTable.Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
var scoreInfos = value.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToList();
|
||||
var topScore = scoreInfos.First();
|
||||
|
||||
scoreTable.Scores = scoreInfos;
|
||||
scoreTable.DisplayScores(scoreInfos, topScore.Beatmap?.Status == BeatmapSetOnlineStatus.Ranked);
|
||||
scoreTable.Show();
|
||||
|
||||
var topScore = scoreInfos.First();
|
||||
var userScore = value.UserScore;
|
||||
var userScoreInfo = userScore?.Score.CreateScoreInfo(rulesets);
|
||||
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -96,6 +97,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
totalScoreColumn.Text = $@"{value.TotalScore:N0}";
|
||||
accuracyColumn.Text = value.DisplayAccuracy;
|
||||
maxComboColumn.Text = $@"{value.MaxCombo:N0}x";
|
||||
ppColumn.Alpha = value.Beatmap?.Status == BeatmapSetOnlineStatus.Ranked ? 1 : 0;
|
||||
ppColumn.Text = $@"{value.PP:N0}";
|
||||
|
||||
statisticsColumns.ChildrenEnumerable = value.SortedStatistics.Select(kvp => createStatisticsColumn(kvp.Key, kvp.Value));
|
||||
|
@ -93,6 +93,7 @@ namespace osu.Game.Overlays.Changelog
|
||||
Direction = FillDirection.Full,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
TextAnchor = Anchor.BottomLeft,
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -125,7 +126,7 @@ namespace osu.Game.Overlays.Changelog
|
||||
|
||||
title.AddText("by ", t =>
|
||||
{
|
||||
t.Font = fontMedium.With(italics: true);
|
||||
t.Font = fontMedium;
|
||||
t.Colour = entryColour;
|
||||
t.Padding = new MarginPadding { Left = 10 };
|
||||
});
|
||||
@ -138,7 +139,7 @@ namespace osu.Game.Overlays.Changelog
|
||||
Id = entry.GithubUser.UserId.Value
|
||||
}, t =>
|
||||
{
|
||||
t.Font = fontMedium.With(italics: true);
|
||||
t.Font = fontMedium;
|
||||
t.Colour = entryColour;
|
||||
});
|
||||
}
|
||||
@ -146,7 +147,7 @@ namespace osu.Game.Overlays.Changelog
|
||||
{
|
||||
title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, t =>
|
||||
{
|
||||
t.Font = fontMedium.With(italics: true);
|
||||
t.Font = fontMedium;
|
||||
t.Colour = entryColour;
|
||||
});
|
||||
}
|
||||
@ -154,7 +155,7 @@ namespace osu.Game.Overlays.Changelog
|
||||
{
|
||||
title.AddText(entry.GithubUser.DisplayName, t =>
|
||||
{
|
||||
t.Font = fontMedium.With(italics: true);
|
||||
t.Font = fontMedium;
|
||||
t.Colour = entryColour;
|
||||
});
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user