mirror of
https://github.com/ppy/osu.git
synced 2026-05-15 06:22:34 +08:00
Compare commits
186 Commits
0.7.1v
...
v2018.324.1
+1
-1
Submodule osu-framework updated: 41e2a0a430...d8d4f55e10
+1
-1
Submodule osu-resources updated: 7bb0782200...6e145ed502
@@ -16,11 +16,9 @@
|
||||
<language>en-AU</language>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="*.exe" target="lib\net45\" exclude="**vshost**"/>
|
||||
<file src="*.dll" target="lib\net45\"/>
|
||||
<file src="*.config" target="lib\net45\"/>
|
||||
<file src="x86\*.dll" target="lib\net45\x86\"/>
|
||||
<file src="x64\*.dll" target="lib\net45\x64\"/>
|
||||
<file src="**.exe" target="lib\net45\" exclude="**vshost**"/>
|
||||
<file src="**.dll" target="lib\net45\"/>
|
||||
<file src="**.config" target="lib\net45\"/>
|
||||
</files>
|
||||
</package>
|
||||
|
||||
|
||||
@@ -16,29 +16,13 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
{
|
||||
public override void PostProcess(Beatmap<CatchHitObject> beatmap)
|
||||
{
|
||||
if (beatmap.ComboColours.Count == 0)
|
||||
return;
|
||||
|
||||
int index = 0;
|
||||
int colourIndex = 0;
|
||||
|
||||
CatchHitObject lastObj = null;
|
||||
|
||||
initialiseHyperDash(beatmap.HitObjects);
|
||||
|
||||
base.PostProcess(beatmap);
|
||||
|
||||
int index = 0;
|
||||
foreach (var obj in beatmap.HitObjects)
|
||||
{
|
||||
if (obj.NewCombo)
|
||||
{
|
||||
if (lastObj != null) lastObj.LastInCombo = true;
|
||||
colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count;
|
||||
}
|
||||
|
||||
obj.IndexInBeatmap = index++;
|
||||
obj.ComboColour = beatmap.ComboColours[colourIndex];
|
||||
|
||||
lastObj = obj;
|
||||
}
|
||||
}
|
||||
|
||||
private void initialiseHyperDash(List<CatchHitObject> objects)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
@@ -32,25 +31,11 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
AddNested(new Banana
|
||||
{
|
||||
Samples = Samples,
|
||||
ComboColour = getNextComboColour(),
|
||||
StartTime = i,
|
||||
X = RNG.NextSingle()
|
||||
});
|
||||
}
|
||||
|
||||
private Color4 getNextComboColour()
|
||||
{
|
||||
switch (RNG.Next(0, 3))
|
||||
{
|
||||
default:
|
||||
return new Color4(255, 240, 0, 255);
|
||||
case 1:
|
||||
return new Color4(255, 192, 0, 255);
|
||||
case 2:
|
||||
return new Color4(214, 221, 28, 255);
|
||||
}
|
||||
}
|
||||
|
||||
public double EndTime => StartTime + Duration;
|
||||
|
||||
public double Duration { get; set; }
|
||||
|
||||
@@ -5,24 +5,25 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasCombo
|
||||
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation
|
||||
{
|
||||
public const double OBJECT_RADIUS = 44;
|
||||
|
||||
public float X { get; set; }
|
||||
|
||||
public Color4 ComboColour { get; set; }
|
||||
|
||||
public int IndexInBeatmap { get; set; }
|
||||
|
||||
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4);
|
||||
|
||||
public virtual bool NewCombo { get; set; }
|
||||
|
||||
public int IndexInCurrentCombo { get; set; }
|
||||
|
||||
public int ComboIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The next fruit starts a new combo. Used for explodey.
|
||||
/// </summary>
|
||||
|
||||
@@ -8,6 +8,8 @@ using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
@@ -57,6 +59,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss });
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
|
||||
if (HitObject is IHasComboInformation combo)
|
||||
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
|
||||
}
|
||||
|
||||
private const float preempt = 1000;
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
|
||||
@@ -5,28 +5,39 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
public class DrawableDroplet : PalpableCatchHitObject<Droplet>
|
||||
{
|
||||
private Pulp pulp;
|
||||
|
||||
public DrawableDroplet(Droplet h)
|
||||
: base(h)
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4;
|
||||
AccentColour = h.ComboColour;
|
||||
Masking = false;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChild = new Pulp
|
||||
InternalChild = pulp = new Pulp
|
||||
{
|
||||
AccentColour = AccentColour,
|
||||
Size = Size
|
||||
};
|
||||
}
|
||||
|
||||
public override Color4 AccentColour
|
||||
{
|
||||
get { return base.AccentColour; }
|
||||
set
|
||||
{
|
||||
base.AccentColour = value;
|
||||
pulp.AccentColour = AccentColour;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS);
|
||||
AccentColour = HitObject.ComboColour;
|
||||
Masking = false;
|
||||
|
||||
Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
|
||||
@@ -33,6 +32,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
// todo: this should come from the skin.
|
||||
AccentColour = colourForRrepesentation(HitObject.VisualRepresentation);
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
createPulp(HitObject.VisualRepresentation),
|
||||
@@ -273,5 +275,31 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
|
||||
border.Alpha = (float)MathHelper.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1);
|
||||
}
|
||||
|
||||
private Color4 colourForRrepesentation(FruitVisualRepresentation representation)
|
||||
{
|
||||
switch (representation)
|
||||
{
|
||||
default:
|
||||
case FruitVisualRepresentation.Pear:
|
||||
return new Color4(17, 136, 170, 255);
|
||||
case FruitVisualRepresentation.Grape:
|
||||
return new Color4(204, 102, 0, 255);
|
||||
case FruitVisualRepresentation.Raspberry:
|
||||
return new Color4(121, 9, 13, 255);
|
||||
case FruitVisualRepresentation.Pineapple:
|
||||
return new Color4(102, 136, 0, 255);
|
||||
case FruitVisualRepresentation.Banana:
|
||||
switch (RNG.Next(0, 3))
|
||||
{
|
||||
default:
|
||||
return new Color4(255, 240, 0, 255);
|
||||
case 1:
|
||||
return new Color4(255, 192, 0, 255);
|
||||
case 2:
|
||||
return new Color4(214, 221, 28, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
var catchObject = (DrawableCatchHitObject)h;
|
||||
|
||||
catchObject.CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
|
||||
catchObject.AccentColour = HitObject.ComboColour;
|
||||
|
||||
dropletContainer.Add(h);
|
||||
base.AddNested(h);
|
||||
|
||||
@@ -60,65 +60,66 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
AddNested(new Fruit
|
||||
{
|
||||
Samples = Samples,
|
||||
ComboColour = ComboColour,
|
||||
StartTime = StartTime,
|
||||
X = X
|
||||
});
|
||||
|
||||
for (var span = 0; span < this.SpanCount(); span++)
|
||||
double lastDropletTime = StartTime;
|
||||
|
||||
for (int span = 0; span < this.SpanCount(); span++)
|
||||
{
|
||||
var spanStartTime = StartTime + span * spanDuration;
|
||||
var reversed = span % 2 == 1;
|
||||
|
||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||
for (double d = 0; d <= length; d += tickDistance)
|
||||
{
|
||||
if (d > length - minDistanceFromEnd)
|
||||
break;
|
||||
|
||||
var timeProgress = d / length;
|
||||
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
|
||||
|
||||
var lastTickTime = spanStartTime + timeProgress * spanDuration;
|
||||
AddNested(new Droplet
|
||||
double time = spanStartTime + timeProgress * spanDuration;
|
||||
|
||||
double tinyTickInterval = time - lastDropletTime;
|
||||
while (tinyTickInterval > 100)
|
||||
tinyTickInterval /= 2;
|
||||
|
||||
for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval)
|
||||
{
|
||||
StartTime = lastTickTime,
|
||||
ComboColour = ComboColour,
|
||||
X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration;
|
||||
|
||||
AddNested(new TinyDroplet
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
StartTime = t,
|
||||
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
double tinyTickInterval = tickDistance / length * spanDuration;
|
||||
while (tinyTickInterval > 100)
|
||||
tinyTickInterval /= 2;
|
||||
|
||||
for (double t = 0; t < spanDuration; t += tinyTickInterval)
|
||||
{
|
||||
double progress = reversed ? 1 - t / spanDuration : t / spanDuration;
|
||||
|
||||
AddNested(new TinyDroplet
|
||||
if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd)
|
||||
{
|
||||
StartTime = spanStartTime + t,
|
||||
ComboColour = ComboColour,
|
||||
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
AddNested(new Droplet
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
StartTime = time,
|
||||
X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
lastDropletTime = time;
|
||||
}
|
||||
|
||||
AddNested(new Fruit
|
||||
{
|
||||
Samples = Samples,
|
||||
ComboColour = ComboColour,
|
||||
StartTime = spanStartTime + spanDuration,
|
||||
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
||||
|
||||
[TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2149")]
|
||||
[TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2232")]
|
||||
public new void Test(string name)
|
||||
{
|
||||
base.Test(name);
|
||||
|
||||
@@ -6,13 +6,11 @@ using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||
using osu.Game.Tests.Visual;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
@@ -62,8 +60,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
Scale = 1.5f,
|
||||
};
|
||||
|
||||
fruit.ComboColour = colourForRrepesentation(fruit.VisualRepresentation);
|
||||
|
||||
return new DrawableFruit(fruit)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
@@ -74,31 +70,5 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
LifetimeEnd = double.PositiveInfinity,
|
||||
};
|
||||
}
|
||||
|
||||
private Color4 colourForRrepesentation(FruitVisualRepresentation representation)
|
||||
{
|
||||
switch (representation)
|
||||
{
|
||||
default:
|
||||
case FruitVisualRepresentation.Pear:
|
||||
return new Color4(17, 136, 170, 255);
|
||||
case FruitVisualRepresentation.Grape:
|
||||
return new Color4(204, 102, 0, 255);
|
||||
case FruitVisualRepresentation.Raspberry:
|
||||
return new Color4(121, 9, 13, 255);
|
||||
case FruitVisualRepresentation.Pineapple:
|
||||
return new Color4(102, 136, 0, 255);
|
||||
case FruitVisualRepresentation.Banana:
|
||||
switch (RNG.Next(0, 3))
|
||||
{
|
||||
default:
|
||||
return new Color4(255, 240, 0, 255);
|
||||
case 1:
|
||||
return new Color4(255, 192, 0, 255);
|
||||
case 2:
|
||||
return new Color4(214, 221, 28, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
if (caughtFruit == null) return;
|
||||
|
||||
caughtFruit.AccentColour = fruit.AccentColour;
|
||||
caughtFruit.RelativePositionAxes = Axes.None;
|
||||
caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0);
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
@@ -24,7 +23,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
private readonly GlowPiece glowPiece;
|
||||
private readonly BodyPiece bodyPiece;
|
||||
private readonly Container<DrawableHoldNoteTick> tickContainer;
|
||||
private readonly Container fullHeightContainer;
|
||||
|
||||
/// <summary>
|
||||
@@ -40,6 +38,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
public DrawableHoldNote(HoldNote hitObject, ManiaAction action)
|
||||
: base(hitObject, action)
|
||||
{
|
||||
Container<DrawableHoldNoteTick> tickContainer;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
@@ -57,7 +56,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
},
|
||||
tickContainer = new Container<DrawableHoldNoteTick> { RelativeSizeAxes = Axes.Both },
|
||||
tickContainer = new Container<DrawableHoldNoteTick>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ChildrenEnumerable = HitObject.NestedHitObjects.OfType<HoldNoteTick>().Select(tick => new DrawableHoldNoteTick(tick)
|
||||
{
|
||||
HoldStartTime = () => holdStartTime
|
||||
})
|
||||
},
|
||||
head = new DrawableHeadNote(this, action)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
@@ -70,16 +76,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
}
|
||||
};
|
||||
|
||||
foreach (var tick in HitObject.NestedHitObjects.OfType<HoldNoteTick>())
|
||||
{
|
||||
var drawableTick = new DrawableHoldNoteTick(tick)
|
||||
{
|
||||
HoldStartTime = () => holdStartTime
|
||||
};
|
||||
|
||||
tickContainer.Add(drawableTick);
|
||||
AddNested(drawableTick);
|
||||
}
|
||||
foreach (var tick in tickContainer)
|
||||
AddNested(tick);
|
||||
|
||||
AddNested(head);
|
||||
AddNested(tail);
|
||||
@@ -90,12 +88,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
get { return base.AccentColour; }
|
||||
set
|
||||
{
|
||||
if (base.AccentColour == value)
|
||||
return;
|
||||
base.AccentColour = value;
|
||||
|
||||
tickContainer.Children.ForEach(t => t.AccentColour = value);
|
||||
|
||||
glowPiece.AccentColour = value;
|
||||
bodyPiece.AccentColour = value;
|
||||
head.AccentColour = value;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
@@ -28,16 +27,5 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
if (action != null)
|
||||
Action = action.Value;
|
||||
}
|
||||
|
||||
public override Color4 AccentColour
|
||||
{
|
||||
get { return base.AccentColour; }
|
||||
set
|
||||
{
|
||||
if (base.AccentColour == value)
|
||||
return;
|
||||
base.AccentColour = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,13 +48,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
get { return base.AccentColour; }
|
||||
set
|
||||
{
|
||||
if (base.AccentColour == value)
|
||||
return;
|
||||
base.AccentColour = value;
|
||||
|
||||
laneGlowPiece.AccentColour = value;
|
||||
GlowPiece.AccentColour = value;
|
||||
headPiece.AccentColour = value;
|
||||
laneGlowPiece.AccentColour = AccentColour;
|
||||
GlowPiece.AccentColour = AccentColour;
|
||||
headPiece.AccentColour = AccentColour;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,24 +13,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
public override void PostProcess(Beatmap<OsuHitObject> beatmap)
|
||||
{
|
||||
applyStacking(beatmap);
|
||||
|
||||
if (beatmap.ComboColours.Count == 0)
|
||||
return;
|
||||
|
||||
int comboIndex = 0;
|
||||
int colourIndex = 0;
|
||||
|
||||
foreach (var obj in beatmap.HitObjects)
|
||||
{
|
||||
if (obj.NewCombo)
|
||||
{
|
||||
comboIndex = 0;
|
||||
colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count;
|
||||
}
|
||||
|
||||
obj.IndexInCurrentCombo = comboIndex++;
|
||||
obj.ComboColour = beatmap.ComboColours[colourIndex];
|
||||
}
|
||||
base.PostProcess(beatmap);
|
||||
}
|
||||
|
||||
private void applyStacking(Beatmap<OsuHitObject> beatmap)
|
||||
|
||||
@@ -8,6 +8,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
@@ -21,7 +22,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
private readonly NumberPiece number;
|
||||
private readonly GlowPiece glow;
|
||||
|
||||
public DrawableHitCircle(HitCircle h) : base(h)
|
||||
public DrawableHitCircle(HitCircle h)
|
||||
: base(h)
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
@@ -30,13 +32,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
glow = new GlowPiece
|
||||
{
|
||||
Colour = AccentColour
|
||||
},
|
||||
glow = new GlowPiece(),
|
||||
circle = new CirclePiece
|
||||
{
|
||||
Colour = AccentColour,
|
||||
Hit = () =>
|
||||
{
|
||||
if (AllJudged)
|
||||
@@ -52,15 +50,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
},
|
||||
ring = new RingPiece(),
|
||||
flash = new FlashPiece(),
|
||||
explode = new ExplodePiece
|
||||
{
|
||||
Colour = AccentColour,
|
||||
},
|
||||
explode = new ExplodePiece(),
|
||||
ApproachCircle = new ApproachCircle
|
||||
{
|
||||
Alpha = 0,
|
||||
Scale = new Vector2(4),
|
||||
Colour = AccentColour,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -70,6 +64,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
|
||||
}
|
||||
|
||||
public override Color4 AccentColour
|
||||
{
|
||||
get { return base.AccentColour; }
|
||||
set
|
||||
{
|
||||
base.AccentColour = value;
|
||||
explode.Colour = AccentColour;
|
||||
glow.Colour = AccentColour;
|
||||
circle.Colour = AccentColour;
|
||||
ApproachCircle.Colour = AccentColour;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (!userTriggered)
|
||||
|
||||
@@ -5,6 +5,9 @@ using System.ComponentModel;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Framework.Graphics;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Skinning;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
@@ -15,7 +18,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
protected DrawableOsuHitObject(OsuHitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
AccentColour = HitObject.ComboColour;
|
||||
Alpha = 0;
|
||||
}
|
||||
|
||||
@@ -35,6 +37,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
|
||||
if (HitObject is IHasComboInformation combo)
|
||||
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
|
||||
}
|
||||
|
||||
protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadein);
|
||||
|
||||
protected virtual void UpdateCurrentState(ArmedState state)
|
||||
|
||||
@@ -13,6 +13,7 @@ using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
@@ -41,7 +42,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
Body = new SliderBody(s)
|
||||
{
|
||||
AccentColour = AccentColour,
|
||||
PathWidth = s.Scale * 64,
|
||||
},
|
||||
ticks = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
|
||||
@@ -50,7 +50,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
BypassAutoSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(s.Scale),
|
||||
AccentColour = AccentColour,
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0
|
||||
},
|
||||
@@ -87,6 +86,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
|
||||
}
|
||||
|
||||
public override Color4 AccentColour
|
||||
{
|
||||
get { return base.AccentColour; }
|
||||
set
|
||||
{
|
||||
base.AccentColour = value;
|
||||
Body.AccentColour = AccentColour;
|
||||
Ball.AccentColour = AccentColour;
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
Blending = BlendingMode.Additive,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.2f,
|
||||
}, false);
|
||||
}, s => s.GetTexture("Play/osu/hitcircle") == null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
}, false);
|
||||
}, s => s.GetTexture("Play/osu/hitcircle") == null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
Texture = textures.Get(name),
|
||||
Blending = BlendingMode.Additive,
|
||||
Alpha = 0.5f
|
||||
}, false);
|
||||
}, s => s.GetTexture("Play/osu/hitcircle") == null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
Colour = Color4.White.Opacity(0.5f),
|
||||
},
|
||||
Child = new Box()
|
||||
}, false),
|
||||
}, s => s.GetTexture("Play/osu/hitcircle") == null),
|
||||
number = new OsuSpriteText
|
||||
{
|
||||
Text = @"1",
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
public double? SnakedStart { get; private set; }
|
||||
public double? SnakedEnd { get; private set; }
|
||||
|
||||
private Color4 accentColour;
|
||||
private Color4 accentColour = Color4.White;
|
||||
/// <summary>
|
||||
/// Used to colour the path.
|
||||
/// </summary>
|
||||
@@ -173,6 +173,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
texture.SetData(upload);
|
||||
path.Texture = texture;
|
||||
|
||||
container.ForceRedraw();
|
||||
}
|
||||
|
||||
private void computeSize()
|
||||
|
||||
@@ -6,13 +6,11 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Edit.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
public abstract class OsuHitObject : HitObject, IHasCombo, IHasEditablePosition
|
||||
public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition
|
||||
{
|
||||
public const double OBJECT_RADIUS = 64;
|
||||
|
||||
@@ -53,10 +51,14 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
public float Scale { get; set; } = 1;
|
||||
|
||||
public Color4 ComboColour { get; set; } = Color4.Gray;
|
||||
public virtual bool NewCombo { get; set; }
|
||||
|
||||
public int IndexInCurrentCombo { get; set; }
|
||||
|
||||
public int ComboIndex { get; set; }
|
||||
|
||||
public bool LastInCombo { get; set; }
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
@@ -99,10 +99,10 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
StartTime = StartTime,
|
||||
Position = Position,
|
||||
IndexInCurrentCombo = IndexInCurrentCombo,
|
||||
ComboColour = ComboColour,
|
||||
Samples = Samples,
|
||||
SampleControlPoint = SampleControlPoint
|
||||
SampleControlPoint = SampleControlPoint,
|
||||
IndexInCurrentCombo = IndexInCurrentCombo,
|
||||
ComboIndex = ComboIndex,
|
||||
};
|
||||
|
||||
TailCircle = new SliderCircle(this)
|
||||
@@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
StartTime = EndTime,
|
||||
Position = EndPosition,
|
||||
IndexInCurrentCombo = IndexInCurrentCombo,
|
||||
ComboColour = ComboColour
|
||||
ComboIndex = ComboIndex,
|
||||
};
|
||||
|
||||
AddNested(HeadCircle);
|
||||
@@ -160,7 +160,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
Position = Position + Curve.PositionAt(distanceProgress),
|
||||
StackHeight = StackHeight,
|
||||
Scale = Scale,
|
||||
ComboColour = ComboColour,
|
||||
Samples = sampleList
|
||||
});
|
||||
}
|
||||
@@ -179,7 +178,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
Position = Position + Curve.PositionAt(repeat % 2),
|
||||
StackHeight = StackHeight,
|
||||
Scale = Scale,
|
||||
ComboColour = ComboColour,
|
||||
Samples = new List<SampleInfo>(RepeatSamples[repeatIndex])
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Tests.Visual;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
@@ -61,7 +60,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = Time.Current + 1000 + timeOffset,
|
||||
Position = positionOffset.Value,
|
||||
ComboColour = Color4.LightSeaGreen
|
||||
};
|
||||
|
||||
circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize });
|
||||
|
||||
@@ -117,7 +117,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = Time.Current + 1000,
|
||||
Position = new Vector2(-(distance / 2), 0),
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
Vector2.Zero,
|
||||
@@ -138,7 +137,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = Time.Current + 1000,
|
||||
Position = new Vector2(-200, 0),
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
Vector2.Zero,
|
||||
@@ -162,7 +160,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
CurveType = CurveType.Linear,
|
||||
StartTime = Time.Current + 1000,
|
||||
Position = new Vector2(-200, 0),
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
Vector2.Zero,
|
||||
@@ -189,7 +186,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
CurveType = CurveType.Bezier,
|
||||
StartTime = Time.Current + 1000,
|
||||
Position = new Vector2(-200, 0),
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
Vector2.Zero,
|
||||
@@ -215,7 +211,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
CurveType = CurveType.Linear,
|
||||
StartTime = Time.Current + 1000,
|
||||
Position = new Vector2(0, 0),
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
Vector2.Zero,
|
||||
@@ -245,7 +240,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
StartTime = Time.Current + 1000,
|
||||
Position = new Vector2(-100, 0),
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
CurveType = CurveType.Catmull,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Skinning;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
@@ -82,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
|
||||
public class OsuCursor : Container
|
||||
{
|
||||
private Container cursorContainer;
|
||||
private Drawable cursorContainer;
|
||||
|
||||
private Bindable<double> cursorScale;
|
||||
private Bindable<bool> autoCursorScale;
|
||||
@@ -97,66 +98,66 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config, OsuGameBase game)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
Child = cursorContainer = new SkinnableDrawable("cursor", _ => new CircularContainer
|
||||
{
|
||||
cursorContainer = new CircularContainer
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
BorderThickness = Size.X / 6,
|
||||
BorderColour = Color4.White,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
BorderThickness = Size.X / 6,
|
||||
BorderColour = Color4.White,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = Color4.Pink.Opacity(0.5f),
|
||||
Radius = 5,
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
},
|
||||
new CircularContainer
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
BorderThickness = Size.X / 3,
|
||||
BorderColour = Color4.White.Opacity(0.5f),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
new CircularContainer
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(0.1f),
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.White,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = Color4.Pink.Opacity(0.5f),
|
||||
Radius = 5,
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
},
|
||||
new CircularContainer
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
BorderThickness = Size.X / 3,
|
||||
BorderColour = Color4.White.Opacity(0.5f),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
new CircularContainer
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(0.1f),
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.White,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}, restrictSize: false)
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
};
|
||||
|
||||
beatmap = game.Beatmap.GetBoundCopy();
|
||||
|
||||
@@ -20,11 +20,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
public class DrawableDrumRoll : DrawableTaikoHitObject<DrumRoll>
|
||||
{
|
||||
/// <summary>
|
||||
/// Number of rolling hits required to reach the dark/final accent colour.
|
||||
/// Number of rolling hits required to reach the dark/final colour.
|
||||
/// </summary>
|
||||
private const int rolling_hits_for_dark_accent = 5;
|
||||
|
||||
private Color4 accentDarkColour;
|
||||
private const int rolling_hits_for_engaged_colour = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Rolling number of tick hits. This increases for hits and decreases for misses.
|
||||
@@ -53,11 +51,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
|
||||
public override bool OnPressed(TaikoAction action) => false;
|
||||
|
||||
private Color4 colourIdle;
|
||||
private Color4 colourEngaged;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
MainPiece.AccentColour = AccentColour = colours.YellowDark;
|
||||
accentDarkColour = colours.YellowDarker;
|
||||
MainPiece.AccentColour = colourIdle = colours.YellowDark;
|
||||
colourEngaged = colours.YellowDarker;
|
||||
}
|
||||
|
||||
private void onTickJudgement(DrawableHitObject obj, Judgement judgement)
|
||||
@@ -67,10 +68,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
else
|
||||
rollingHits--;
|
||||
|
||||
rollingHits = MathHelper.Clamp(rollingHits, 0, rolling_hits_for_dark_accent);
|
||||
rollingHits = MathHelper.Clamp(rollingHits, 0, rolling_hits_for_engaged_colour);
|
||||
|
||||
Color4 newAccent = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_dark_accent, AccentColour, accentDarkColour, 0, 1);
|
||||
MainPiece.FadeAccent(newAccent, 100);
|
||||
Color4 newColour = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_engaged_colour, colourIdle, colourEngaged, 0, 1);
|
||||
MainPiece.FadeAccent(newColour, 100);
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
|
||||
@@ -11,6 +11,7 @@ using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.Formats
|
||||
{
|
||||
@@ -163,7 +164,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
[Test]
|
||||
public void TestDecodeBeatmapColors()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder();
|
||||
var decoder = new LegacySkinDecoder();
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
|
||||
@@ -12,7 +12,6 @@ using osu.Game.IO.Serialization;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Tests.Resources;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.Formats
|
||||
{
|
||||
@@ -89,24 +88,6 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.AreEqual(2, difficulty.SliderTickRate);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeColors()
|
||||
{
|
||||
var beatmap = decodeAsJson(normal);
|
||||
Color4[] expected =
|
||||
{
|
||||
new Color4(142, 199, 255, 255),
|
||||
new Color4(255, 128, 128, 255),
|
||||
new Color4(128, 255, 255, 255),
|
||||
new Color4(128, 255, 128, 255),
|
||||
new Color4(255, 187, 255, 255),
|
||||
new Color4(255, 177, 140, 255),
|
||||
};
|
||||
Assert.AreEqual(expected.Length, beatmap.ComboColours.Count);
|
||||
for (int i = 0; i < expected.Length; i++)
|
||||
Assert.AreEqual(expected[i], beatmap.ComboColours[i]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeHitObjects()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Screens.Edit.Screens.Compose;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCaseBeatDivisorControl : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(BindableBeatDivisor) };
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Child = new BeatDivisorControl(new BindableBeatDivisor())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(90, 90)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,47 +2,39 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Edit.Screens.Compose;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCaseEditorCompose : OsuTestCase
|
||||
{
|
||||
private readonly Random random;
|
||||
private readonly Compose compose;
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(Compose) };
|
||||
|
||||
public TestCaseEditorCompose()
|
||||
{
|
||||
random = new Random(1337);
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
Add(compose = new Compose());
|
||||
AddStep("Next beatmap", nextBeatmap);
|
||||
}
|
||||
|
||||
private OsuGameBase osuGame;
|
||||
private BeatmapManager beatmaps;
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(parent);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase osuGame, BeatmapManager beatmaps)
|
||||
private void load(OsuGameBase osuGame)
|
||||
{
|
||||
this.osuGame = osuGame;
|
||||
this.beatmaps = beatmaps;
|
||||
osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
|
||||
var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
dependencies.CacheAs<IAdjustableClock>(clock);
|
||||
dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||
|
||||
var compose = new Compose();
|
||||
compose.Beatmap.BindTo(osuGame.Beatmap);
|
||||
}
|
||||
|
||||
private void nextBeatmap()
|
||||
{
|
||||
var sets = beatmaps.GetAllUsableBeatmapSets();
|
||||
if (sets.Count == 0)
|
||||
return;
|
||||
|
||||
var b = sets[random.Next(0, sets.Count)].Beatmaps[0];
|
||||
osuGame.Beatmap.Value = beatmaps.GetWorkingBeatmap(b);
|
||||
Child = compose;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets;
|
||||
@@ -17,6 +18,7 @@ using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Screens.Edit.Screens.Compose;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
@@ -30,9 +32,23 @@ namespace osu.Game.Tests.Visual
|
||||
private Track track;
|
||||
private HitObjectComposer composer;
|
||||
|
||||
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(4);
|
||||
|
||||
private DecoupleableInterpolatingFramedClock clock;
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(parent);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase osuGame)
|
||||
{
|
||||
clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
dependencies.CacheAs<IAdjustableClock>(clock);
|
||||
dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||
dependencies.Cache(beatDivisor);
|
||||
|
||||
var testBeatmap = new Beatmap
|
||||
{
|
||||
ControlPointInfo = new ControlPointInfo
|
||||
@@ -63,7 +79,7 @@ namespace osu.Game.Tests.Visual
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[] { composer = new TestHitObjectComposer(new OsuRuleset()) },
|
||||
new Drawable[] { new TimingPointVisualiser(testBeatmap, track) },
|
||||
new Drawable[] { new TimingPointVisualiser(testBeatmap, track) { Clock = clock } },
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
@@ -92,17 +108,17 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
// Forwards
|
||||
AddStep("Seek(0)", () => composer.SeekTo(0));
|
||||
AddAssert("Time = 0", () => track.CurrentTime == 0);
|
||||
AddAssert("Time = 0", () => clock.CurrentTime == 0);
|
||||
AddStep("Seek(33)", () => composer.SeekTo(33));
|
||||
AddAssert("Time = 33", () => track.CurrentTime == 33);
|
||||
AddAssert("Time = 33", () => clock.CurrentTime == 33);
|
||||
AddStep("Seek(89)", () => composer.SeekTo(89));
|
||||
AddAssert("Time = 89", () => track.CurrentTime == 89);
|
||||
AddAssert("Time = 89", () => clock.CurrentTime == 89);
|
||||
|
||||
// Backwards
|
||||
AddStep("Seek(25)", () => composer.SeekTo(25));
|
||||
AddAssert("Time = 25", () => track.CurrentTime == 25);
|
||||
AddAssert("Time = 25", () => clock.CurrentTime == 25);
|
||||
AddStep("Seek(0)", () => composer.SeekTo(0));
|
||||
AddAssert("Time = 0", () => track.CurrentTime == 0);
|
||||
AddAssert("Time = 0", () => clock.CurrentTime == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -114,19 +130,19 @@ namespace osu.Game.Tests.Visual
|
||||
reset();
|
||||
|
||||
AddStep("Seek(0), Snap", () => composer.SeekTo(0, true));
|
||||
AddAssert("Time = 0", () => track.CurrentTime == 0);
|
||||
AddAssert("Time = 0", () => clock.CurrentTime == 0);
|
||||
AddStep("Seek(50), Snap", () => composer.SeekTo(50, true));
|
||||
AddAssert("Time = 50", () => track.CurrentTime == 50);
|
||||
AddAssert("Time = 50", () => clock.CurrentTime == 50);
|
||||
AddStep("Seek(100), Snap", () => composer.SeekTo(100, true));
|
||||
AddAssert("Time = 100", () => track.CurrentTime == 100);
|
||||
AddAssert("Time = 100", () => clock.CurrentTime == 100);
|
||||
AddStep("Seek(175), Snap", () => composer.SeekTo(175, true));
|
||||
AddAssert("Time = 175", () => track.CurrentTime == 175);
|
||||
AddAssert("Time = 175", () => clock.CurrentTime == 175);
|
||||
AddStep("Seek(350), Snap", () => composer.SeekTo(350, true));
|
||||
AddAssert("Time = 350", () => track.CurrentTime == 350);
|
||||
AddAssert("Time = 350", () => clock.CurrentTime == 350);
|
||||
AddStep("Seek(400), Snap", () => composer.SeekTo(400, true));
|
||||
AddAssert("Time = 400", () => track.CurrentTime == 400);
|
||||
AddAssert("Time = 400", () => clock.CurrentTime == 400);
|
||||
AddStep("Seek(450), Snap", () => composer.SeekTo(450, true));
|
||||
AddAssert("Time = 450", () => track.CurrentTime == 450);
|
||||
AddAssert("Time = 450", () => clock.CurrentTime == 450);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -139,17 +155,17 @@ namespace osu.Game.Tests.Visual
|
||||
reset();
|
||||
|
||||
AddStep("Seek(24), Snap", () => composer.SeekTo(24, true));
|
||||
AddAssert("Time = 0", () => track.CurrentTime == 0);
|
||||
AddAssert("Time = 0", () => clock.CurrentTime == 0);
|
||||
AddStep("Seek(26), Snap", () => composer.SeekTo(26, true));
|
||||
AddAssert("Time = 50", () => track.CurrentTime == 50);
|
||||
AddAssert("Time = 50", () => clock.CurrentTime == 50);
|
||||
AddStep("Seek(150), Snap", () => composer.SeekTo(150, true));
|
||||
AddAssert("Time = 100", () => track.CurrentTime == 100);
|
||||
AddAssert("Time = 100", () => clock.CurrentTime == 100);
|
||||
AddStep("Seek(170), Snap", () => composer.SeekTo(170, true));
|
||||
AddAssert("Time = 175", () => track.CurrentTime == 175);
|
||||
AddAssert("Time = 175", () => clock.CurrentTime == 175);
|
||||
AddStep("Seek(274), Snap", () => composer.SeekTo(274, true));
|
||||
AddAssert("Time = 175", () => track.CurrentTime == 175);
|
||||
AddAssert("Time = 175", () => clock.CurrentTime == 175);
|
||||
AddStep("Seek(276), Snap", () => composer.SeekTo(276, true));
|
||||
AddAssert("Time = 350", () => track.CurrentTime == 350);
|
||||
AddAssert("Time = 350", () => clock.CurrentTime == 350);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -160,15 +176,15 @@ namespace osu.Game.Tests.Visual
|
||||
reset();
|
||||
|
||||
AddStep("SeekForward", () => composer.SeekForward());
|
||||
AddAssert("Time = 50", () => track.CurrentTime == 50);
|
||||
AddAssert("Time = 50", () => clock.CurrentTime == 50);
|
||||
AddStep("SeekForward", () => composer.SeekForward());
|
||||
AddAssert("Time = 100", () => track.CurrentTime == 100);
|
||||
AddAssert("Time = 100", () => clock.CurrentTime == 100);
|
||||
AddStep("SeekForward", () => composer.SeekForward());
|
||||
AddAssert("Time = 200", () => track.CurrentTime == 200);
|
||||
AddAssert("Time = 200", () => clock.CurrentTime == 200);
|
||||
AddStep("SeekForward", () => composer.SeekForward());
|
||||
AddAssert("Time = 400", () => track.CurrentTime == 400);
|
||||
AddAssert("Time = 400", () => clock.CurrentTime == 400);
|
||||
AddStep("SeekForward", () => composer.SeekForward());
|
||||
AddAssert("Time = 450", () => track.CurrentTime == 450);
|
||||
AddAssert("Time = 450", () => clock.CurrentTime == 450);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -179,17 +195,17 @@ namespace osu.Game.Tests.Visual
|
||||
reset();
|
||||
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 50", () => track.CurrentTime == 50);
|
||||
AddAssert("Time = 50", () => clock.CurrentTime == 50);
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 100", () => track.CurrentTime == 100);
|
||||
AddAssert("Time = 100", () => clock.CurrentTime == 100);
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 175", () => track.CurrentTime == 175);
|
||||
AddAssert("Time = 175", () => clock.CurrentTime == 175);
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 350", () => track.CurrentTime == 350);
|
||||
AddAssert("Time = 350", () => clock.CurrentTime == 350);
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 400", () => track.CurrentTime == 400);
|
||||
AddAssert("Time = 400", () => clock.CurrentTime == 400);
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 450", () => track.CurrentTime == 450);
|
||||
AddAssert("Time = 450", () => clock.CurrentTime == 450);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -202,28 +218,28 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
AddStep("Seek(49)", () => composer.SeekTo(49));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 50", () => track.CurrentTime == 50);
|
||||
AddAssert("Time = 50", () => clock.CurrentTime == 50);
|
||||
AddStep("Seek(49.999)", () => composer.SeekTo(49.999));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 50", () => track.CurrentTime == 50);
|
||||
AddAssert("Time = 50", () => clock.CurrentTime == 50);
|
||||
AddStep("Seek(99)", () => composer.SeekTo(99));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 100", () => track.CurrentTime == 100);
|
||||
AddAssert("Time = 100", () => clock.CurrentTime == 100);
|
||||
AddStep("Seek(99.999)", () => composer.SeekTo(99.999));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 100", () => track.CurrentTime == 100);
|
||||
AddAssert("Time = 100", () => clock.CurrentTime == 100);
|
||||
AddStep("Seek(174)", () => composer.SeekTo(174));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 175", () => track.CurrentTime == 175);
|
||||
AddAssert("Time = 175", () => clock.CurrentTime == 175);
|
||||
AddStep("Seek(349)", () => composer.SeekTo(349));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 350", () => track.CurrentTime == 350);
|
||||
AddAssert("Time = 350", () => clock.CurrentTime == 350);
|
||||
AddStep("Seek(399)", () => composer.SeekTo(399));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 400", () => track.CurrentTime == 400);
|
||||
AddAssert("Time = 400", () => clock.CurrentTime == 400);
|
||||
AddStep("Seek(449)", () => composer.SeekTo(449));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 450", () => track.CurrentTime == 450);
|
||||
AddAssert("Time = 450", () => clock.CurrentTime == 450);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -235,17 +251,17 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
AddStep("Seek(450)", () => composer.SeekTo(450));
|
||||
AddStep("SeekBackward", () => composer.SeekBackward());
|
||||
AddAssert("Time = 425", () => track.CurrentTime == 425);
|
||||
AddAssert("Time = 425", () => clock.CurrentTime == 425);
|
||||
AddStep("SeekBackward", () => composer.SeekBackward());
|
||||
AddAssert("Time = 375", () => track.CurrentTime == 375);
|
||||
AddAssert("Time = 375", () => clock.CurrentTime == 375);
|
||||
AddStep("SeekBackward", () => composer.SeekBackward());
|
||||
AddAssert("Time = 325", () => track.CurrentTime == 325);
|
||||
AddAssert("Time = 325", () => clock.CurrentTime == 325);
|
||||
AddStep("SeekBackward", () => composer.SeekBackward());
|
||||
AddAssert("Time = 125", () => track.CurrentTime == 125);
|
||||
AddAssert("Time = 125", () => clock.CurrentTime == 125);
|
||||
AddStep("SeekBackward", () => composer.SeekBackward());
|
||||
AddAssert("Time = 25", () => track.CurrentTime == 25);
|
||||
AddAssert("Time = 25", () => clock.CurrentTime == 25);
|
||||
AddStep("SeekBackward", () => composer.SeekBackward());
|
||||
AddAssert("Time = 0", () => track.CurrentTime == 0);
|
||||
AddAssert("Time = 0", () => clock.CurrentTime == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -257,17 +273,17 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
AddStep("Seek(450)", () => composer.SeekTo(450));
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 400", () => track.CurrentTime == 400);
|
||||
AddAssert("Time = 400", () => clock.CurrentTime == 400);
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 350", () => track.CurrentTime == 350);
|
||||
AddAssert("Time = 350", () => clock.CurrentTime == 350);
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 175", () => track.CurrentTime == 175);
|
||||
AddAssert("Time = 175", () => clock.CurrentTime == 175);
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 100", () => track.CurrentTime == 100);
|
||||
AddAssert("Time = 100", () => clock.CurrentTime == 100);
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 50", () => track.CurrentTime == 50);
|
||||
AddAssert("Time = 50", () => clock.CurrentTime == 50);
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 0", () => track.CurrentTime == 0);
|
||||
AddAssert("Time = 0", () => clock.CurrentTime == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -280,16 +296,16 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
AddStep("Seek(451)", () => composer.SeekTo(451));
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 450", () => track.CurrentTime == 450);
|
||||
AddAssert("Time = 450", () => clock.CurrentTime == 450);
|
||||
AddStep("Seek(450.999)", () => composer.SeekTo(450.999));
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 450", () => track.CurrentTime == 450);
|
||||
AddAssert("Time = 450", () => clock.CurrentTime == 450);
|
||||
AddStep("Seek(401)", () => composer.SeekTo(401));
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 400", () => track.CurrentTime == 400);
|
||||
AddAssert("Time = 400", () => clock.CurrentTime == 400);
|
||||
AddStep("Seek(401.999)", () => composer.SeekTo(401.999));
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 400", () => track.CurrentTime == 400);
|
||||
AddAssert("Time = 400", () => clock.CurrentTime == 400);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -307,23 +323,23 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
AddStep("SeekForward, Snap", () =>
|
||||
{
|
||||
lastTime = track.CurrentTime;
|
||||
lastTime = clock.CurrentTime;
|
||||
composer.SeekForward(true);
|
||||
});
|
||||
AddAssert("Time > lastTime", () => track.CurrentTime > lastTime);
|
||||
AddAssert("Time > lastTime", () => clock.CurrentTime > lastTime);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
AddStep("SeekBackward, Snap", () =>
|
||||
{
|
||||
lastTime = track.CurrentTime;
|
||||
lastTime = clock.CurrentTime;
|
||||
composer.SeekBackward(true);
|
||||
});
|
||||
AddAssert("Time < lastTime", () => track.CurrentTime < lastTime);
|
||||
AddAssert("Time < lastTime", () => clock.CurrentTime < lastTime);
|
||||
}
|
||||
|
||||
AddAssert("Time = 0", () => track.CurrentTime == 0);
|
||||
AddAssert("Time = 0", () => clock.CurrentTime == 0);
|
||||
}
|
||||
|
||||
private void reset()
|
||||
@@ -409,7 +425,7 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
base.Update();
|
||||
|
||||
tracker.X = (float)(track.CurrentTime / track.Length);
|
||||
tracker.X = (float)(Time.Current / track.Length);
|
||||
}
|
||||
|
||||
private class TimingPointTimeline : CompositeDrawable
|
||||
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Timing;
|
||||
using OpenTK;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
@@ -34,6 +35,11 @@ namespace osu.Game.Tests.Visual
|
||||
typeof(SliderCircleMask)
|
||||
};
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(parent);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase osuGame)
|
||||
{
|
||||
@@ -59,6 +65,10 @@ namespace osu.Game.Tests.Visual
|
||||
},
|
||||
});
|
||||
|
||||
var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
dependencies.CacheAs<IAdjustableClock>(clock);
|
||||
dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||
|
||||
Child = new OsuHitObjectComposer(new OsuRuleset());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,30 +4,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using OpenTK;
|
||||
using osu.Game.Screens.Edit.Components.Timelines.Summary;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCaseEditorSummaryTimeline : OsuTestCase
|
||||
{
|
||||
private const int length = 60000;
|
||||
private readonly Random random;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(SummaryTimeline) };
|
||||
|
||||
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
||||
|
||||
public TestCaseEditorSummaryTimeline()
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(parent);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
random = new Random(1337);
|
||||
beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
|
||||
var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
dependencies.CacheAs<IAdjustableClock>(clock);
|
||||
dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||
|
||||
SummaryTimeline summaryTimeline;
|
||||
Add(summaryTimeline = new SummaryTimeline
|
||||
@@ -38,58 +46,6 @@ namespace osu.Game.Tests.Visual
|
||||
});
|
||||
|
||||
summaryTimeline.Beatmap.BindTo(beatmap);
|
||||
|
||||
AddStep("New beatmap", newBeatmap);
|
||||
|
||||
newBeatmap();
|
||||
}
|
||||
|
||||
private void newBeatmap()
|
||||
{
|
||||
var b = new Beatmap();
|
||||
|
||||
for (int i = 0; i < random.Next(1, 10); i++)
|
||||
b.ControlPointInfo.TimingPoints.Add(new TimingControlPoint { Time = random.Next(0, length) });
|
||||
|
||||
for (int i = 0; i < random.Next(1, 5); i++)
|
||||
b.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint { Time = random.Next(0, length) });
|
||||
|
||||
for (int i = 0; i < random.Next(1, 5); i++)
|
||||
b.ControlPointInfo.EffectPoints.Add(new EffectControlPoint { Time = random.Next(0, length) });
|
||||
|
||||
for (int i = 0; i < random.Next(1, 5); i++)
|
||||
b.ControlPointInfo.SamplePoints.Add(new SampleControlPoint { Time = random.Next(0, length) });
|
||||
|
||||
b.BeatmapInfo.Bookmarks = new int[random.Next(10, 30)];
|
||||
for (int i = 0; i < b.BeatmapInfo.Bookmarks.Length; i++)
|
||||
b.BeatmapInfo.Bookmarks[i] = random.Next(0, length);
|
||||
|
||||
beatmap.Value = new TestWorkingBeatmap(b);
|
||||
}
|
||||
|
||||
private class TestWorkingBeatmap : WorkingBeatmap
|
||||
{
|
||||
private readonly Beatmap beatmap;
|
||||
|
||||
public TestWorkingBeatmap(Beatmap beatmap)
|
||||
: base(beatmap.BeatmapInfo)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
}
|
||||
|
||||
protected override Texture GetBackground() => null;
|
||||
|
||||
protected override Beatmap GetBeatmap() => beatmap;
|
||||
|
||||
protected override Track GetTrack() => new TestTrack();
|
||||
|
||||
private class TestTrack : TrackVirtual
|
||||
{
|
||||
public TestTrack()
|
||||
{
|
||||
Length = length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual
|
||||
IDatabaseContextFactory factory = new SingletonContextFactory(new OsuDbContext());
|
||||
|
||||
dependencies.Cache(rulesets = new RulesetStore(factory));
|
||||
dependencies.Cache(manager = new BeatmapManager(storage, factory, rulesets, null)
|
||||
dependencies.Cache(manager = new BeatmapManager(storage, factory, rulesets, null, null)
|
||||
{
|
||||
DefaultBeatmap = defaultBeatmap = game.Beatmap.Default
|
||||
});
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Screens.Edit.Components;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
@@ -13,17 +15,28 @@ namespace osu.Game.Tests.Visual
|
||||
[TestFixture]
|
||||
public class TestCasePlaybackControl : OsuTestCase
|
||||
{
|
||||
public TestCasePlaybackControl()
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(parent);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
dependencies.CacheAs<IAdjustableClock>(clock);
|
||||
dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||
|
||||
var playback = new PlaybackControl
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200,100)
|
||||
};
|
||||
|
||||
playback.Beatmap.Value = new TestWorkingBeatmap(new Beatmap());
|
||||
|
||||
Add(playback);
|
||||
Child = playback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
<Compile Include="Visual\TestCaseBeatmapOptionsOverlay.cs" />
|
||||
<Compile Include="Visual\TestCaseBeatmapScoresContainer.cs" />
|
||||
<Compile Include="Visual\TestCaseBeatmapSetOverlay.cs" />
|
||||
<Compile Include="Visual\TestCaseBeatDivisorControl.cs" />
|
||||
<Compile Include="Visual\TestCaseBeatSyncedContainer.cs" />
|
||||
<Compile Include="Visual\TestCaseBreadcrumbs.cs" />
|
||||
<Compile Include="Visual\TestCaseBreakOverlay.cs" />
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using System.Collections.Generic;
|
||||
@@ -9,7 +8,6 @@ using System.Linq;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.IO.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.IO.Serialization.Converters;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
@@ -17,21 +15,13 @@ namespace osu.Game.Beatmaps
|
||||
/// <summary>
|
||||
/// A Beatmap containing converted HitObjects.
|
||||
/// </summary>
|
||||
public class Beatmap<T> : IJsonSerializable, IHasComboColours
|
||||
public class Beatmap<T> : IJsonSerializable
|
||||
where T : HitObject
|
||||
{
|
||||
public BeatmapInfo BeatmapInfo = new BeatmapInfo();
|
||||
public ControlPointInfo ControlPointInfo = new ControlPointInfo();
|
||||
public List<BreakPeriod> Breaks = new List<BreakPeriod>();
|
||||
|
||||
public List<Color4> ComboColours { get; set; } = new List<Color4>
|
||||
{
|
||||
new Color4(17, 136, 170, 255),
|
||||
new Color4(102, 136, 0, 255),
|
||||
new Color4(204, 102, 0, 255),
|
||||
new Color4(121, 9, 13, 255)
|
||||
};
|
||||
|
||||
[JsonIgnore]
|
||||
public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata;
|
||||
|
||||
@@ -56,7 +46,6 @@ namespace osu.Game.Beatmaps
|
||||
BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo;
|
||||
ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo;
|
||||
Breaks = original?.Breaks ?? Breaks;
|
||||
ComboColours = original?.ComboColours ?? ComboColours;
|
||||
HitObjects = original?.HitObjects ?? HitObjects;
|
||||
|
||||
if (original == null && Metadata == null)
|
||||
|
||||
@@ -57,7 +57,6 @@ namespace osu.Game.Beatmaps
|
||||
beatmap.ControlPointInfo = original.ControlPointInfo;
|
||||
beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList();
|
||||
beatmap.Breaks = original.Breaks;
|
||||
beatmap.ComboColours = original.ComboColours;
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
@@ -55,6 +56,8 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
private readonly APIAccess api;
|
||||
|
||||
private readonly AudioManager audioManager;
|
||||
|
||||
private readonly List<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>();
|
||||
|
||||
/// <summary>
|
||||
@@ -62,7 +65,7 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
public Func<Storage> GetStableStorage { private get; set; }
|
||||
|
||||
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null)
|
||||
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, AudioManager audioManager, IIpcHost importHost = null)
|
||||
: base(storage, contextFactory, new BeatmapStore(contextFactory), importHost)
|
||||
{
|
||||
beatmaps = (BeatmapStore)ModelStore;
|
||||
@@ -71,6 +74,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
this.rulesets = rulesets;
|
||||
this.api = api;
|
||||
this.audioManager = audioManager;
|
||||
}
|
||||
|
||||
protected override void Populate(BeatmapSetInfo model, ArchiveReader archive)
|
||||
@@ -217,7 +221,7 @@ namespace osu.Game.Beatmaps
|
||||
if (beatmapInfo.Metadata == null)
|
||||
beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata;
|
||||
|
||||
WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, beatmapInfo);
|
||||
WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, beatmapInfo, audioManager);
|
||||
|
||||
previous?.TransferTo(working);
|
||||
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Graphics.Textures;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Storyboards;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
@@ -19,11 +21,13 @@ namespace osu.Game.Beatmaps
|
||||
protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap
|
||||
{
|
||||
private readonly IResourceStore<byte[]> store;
|
||||
private readonly AudioManager audioManager;
|
||||
|
||||
public BeatmapManagerWorkingBeatmap(IResourceStore<byte[]> store, BeatmapInfo beatmapInfo)
|
||||
public BeatmapManagerWorkingBeatmap(IResourceStore<byte[]> store, BeatmapInfo beatmapInfo, AudioManager audioManager)
|
||||
: base(beatmapInfo)
|
||||
{
|
||||
this.store = store;
|
||||
this.audioManager = audioManager;
|
||||
}
|
||||
|
||||
protected override Beatmap GetBeatmap()
|
||||
@@ -100,6 +104,22 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
return storyboard;
|
||||
}
|
||||
|
||||
protected override Skin GetSkin()
|
||||
{
|
||||
Skin skin;
|
||||
try
|
||||
{
|
||||
skin = new LegacyBeatmapSkin(BeatmapInfo, store, audioManager);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error(e, "Skin failed to load");
|
||||
skin = new DefaultSkin();
|
||||
}
|
||||
|
||||
return skin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
@@ -19,6 +21,29 @@ namespace osu.Game.Beatmaps
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The Beatmap to process.</param>
|
||||
public virtual void PostProcess(Beatmap<TObject> beatmap) { }
|
||||
public virtual void PostProcess(Beatmap<TObject> beatmap)
|
||||
{
|
||||
IHasComboInformation lastObj = null;
|
||||
|
||||
foreach (var obj in beatmap.HitObjects.OfType<IHasComboInformation>())
|
||||
{
|
||||
if (obj.NewCombo)
|
||||
{
|
||||
obj.IndexInCurrentCombo = 0;
|
||||
if (lastObj != null)
|
||||
{
|
||||
lastObj.LastInCombo = true;
|
||||
obj.ComboIndex = lastObj.ComboIndex + 1;
|
||||
}
|
||||
}
|
||||
else if (lastObj != null)
|
||||
{
|
||||
obj.IndexInCurrentCombo = lastObj.IndexInCurrentCombo + 1;
|
||||
obj.ComboIndex = lastObj.ComboIndex;
|
||||
}
|
||||
|
||||
lastObj = obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private void handleGeneral(string line)
|
||||
{
|
||||
var pair = SplitKeyVal(line, ':');
|
||||
var pair = SplitKeyVal(line);
|
||||
|
||||
var metadata = beatmap.BeatmapInfo.Metadata;
|
||||
switch (pair.Key)
|
||||
@@ -155,7 +155,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private void handleEditor(string line)
|
||||
{
|
||||
var pair = SplitKeyVal(line, ':');
|
||||
var pair = SplitKeyVal(line);
|
||||
|
||||
switch (pair.Key)
|
||||
{
|
||||
@@ -179,7 +179,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private void handleMetadata(string line)
|
||||
{
|
||||
var pair = SplitKeyVal(line, ':');
|
||||
var pair = SplitKeyVal(line);
|
||||
|
||||
var metadata = beatmap.BeatmapInfo.Metadata;
|
||||
switch (pair.Key)
|
||||
@@ -220,7 +220,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private void handleDifficulty(string line)
|
||||
{
|
||||
var pair = SplitKeyVal(line, ':');
|
||||
var pair = SplitKeyVal(line);
|
||||
|
||||
var difficulty = beatmap.BeatmapInfo.BaseDifficulty;
|
||||
switch (pair.Key)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using osu.Framework.Logging;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Beatmaps.Formats
|
||||
@@ -31,7 +32,11 @@ namespace osu.Game.Beatmaps.Formats
|
||||
if (line.StartsWith(@"[") && line.EndsWith(@"]"))
|
||||
{
|
||||
if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section))
|
||||
throw new InvalidDataException($@"Unknown osu section {line}");
|
||||
{
|
||||
Logger.Log($"Unknown section \"{line}\" in {beatmap}");
|
||||
section = Section.None;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -55,7 +60,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private void handleColours(T output, string line)
|
||||
{
|
||||
var pair = SplitKeyVal(line, ':');
|
||||
var pair = SplitKeyVal(line);
|
||||
|
||||
bool isCombo = pair.Key.StartsWith(@"Combo");
|
||||
|
||||
@@ -89,7 +94,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
}
|
||||
}
|
||||
|
||||
protected KeyValuePair<string, string> SplitKeyVal(string line, char separator)
|
||||
protected KeyValuePair<string, string> SplitKeyVal(string line, char separator = ':')
|
||||
{
|
||||
var split = line.Trim().Split(new[] { separator }, 2);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ using osu.Framework.IO.File;
|
||||
using System.IO;
|
||||
using osu.Game.IO.Serialization;
|
||||
using System.Diagnostics;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
@@ -40,6 +41,7 @@ namespace osu.Game.Beatmaps
|
||||
track = new AsyncLazy<Track>(populateTrack);
|
||||
waveform = new AsyncLazy<Waveform>(populateWaveform);
|
||||
storyboard = new AsyncLazy<Storyboard>(populateStoryboard);
|
||||
skin = new AsyncLazy<Skin>(populateSkin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -56,6 +58,7 @@ namespace osu.Game.Beatmaps
|
||||
protected abstract Beatmap GetBeatmap();
|
||||
protected abstract Texture GetBackground();
|
||||
protected abstract Track GetTrack();
|
||||
protected virtual Skin GetSkin() => new DefaultSkin();
|
||||
protected virtual Waveform GetWaveform() => new Waveform();
|
||||
protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo };
|
||||
|
||||
@@ -109,6 +112,13 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
private Storyboard populateStoryboard() => GetStoryboard();
|
||||
|
||||
public bool SkinLoaded => skin.IsResultAvailable;
|
||||
public Skin Skin => skin.Value.Result;
|
||||
public async Task<Skin> GetSkinAsync() => await skin.Value;
|
||||
private readonly AsyncLazy<Skin> skin;
|
||||
|
||||
private Skin populateSkin() => GetSkin();
|
||||
|
||||
public void TransferTo(WorkingBeatmap other)
|
||||
{
|
||||
if (track.IsResultAvailable && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
|
||||
@@ -123,6 +133,7 @@ namespace osu.Game.Beatmaps
|
||||
if (BackgroundLoaded) Background?.Dispose();
|
||||
if (WaveformLoaded) Waveform?.Dispose();
|
||||
if (StoryboardLoaded) Storyboard?.Dispose();
|
||||
if (SkinLoaded) Skin?.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -82,6 +82,8 @@ namespace osu.Game.Configuration
|
||||
Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
|
||||
|
||||
Set(OsuSetting.Version, string.Empty);
|
||||
|
||||
Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg);
|
||||
}
|
||||
|
||||
public OsuConfigManager(Storage storage) : base(storage)
|
||||
@@ -125,6 +127,7 @@ namespace osu.Game.Configuration
|
||||
Version,
|
||||
ShowConvertedBeatmaps,
|
||||
SpeedChangeVisualisation,
|
||||
Skin
|
||||
Skin,
|
||||
ScreenshotFormat
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ namespace osu.Game.Configuration
|
||||
{
|
||||
public enum ScreenshotFormat
|
||||
{
|
||||
Bmp = 0, // TODO: Figure out the best way to hide this from the dropdown
|
||||
[Description("JPG (web-friendly)")]
|
||||
Jpg = 1,
|
||||
[Description("PNG (lossless)")]
|
||||
|
||||
@@ -13,6 +13,7 @@ using osu.Game.IO;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.IPC;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using SharpCompress.Common;
|
||||
using FileInfo = osu.Game.IO.FileInfo;
|
||||
|
||||
namespace osu.Game.Database
|
||||
@@ -79,7 +80,6 @@ namespace osu.Game.Database
|
||||
var notification = new ProgressNotification
|
||||
{
|
||||
Text = "Import is initialising...",
|
||||
CompletionText = "Import successful!",
|
||||
Progress = 0,
|
||||
State = ProgressNotificationState.Active,
|
||||
};
|
||||
@@ -88,7 +88,8 @@ namespace osu.Game.Database
|
||||
|
||||
List<TModel> imported = new List<TModel>();
|
||||
|
||||
int i = 0;
|
||||
int current = 0;
|
||||
int errors = 0;
|
||||
foreach (string path in paths)
|
||||
{
|
||||
if (notification.State == ProgressNotificationState.Cancelled)
|
||||
@@ -97,11 +98,11 @@ namespace osu.Game.Database
|
||||
|
||||
try
|
||||
{
|
||||
notification.Text = $"Importing ({i} of {paths.Length})\n{Path.GetFileName(path)}";
|
||||
notification.Text = $"Importing ({++current} of {paths.Length})\n{Path.GetFileName(path)}";
|
||||
using (ArchiveReader reader = getReaderFrom(path))
|
||||
imported.Add(Import(reader));
|
||||
|
||||
notification.Progress = (float)++i / paths.Length;
|
||||
notification.Progress = (float)current / paths.Length;
|
||||
|
||||
// We may or may not want to delete the file depending on where it is stored.
|
||||
// e.g. reconstructing/repairing database with items from default storage.
|
||||
@@ -121,9 +122,11 @@ namespace osu.Game.Database
|
||||
{
|
||||
e = e.InnerException ?? e;
|
||||
Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})");
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
notification.Text = errors > 0 ? $"Import complete with {errors} errors" : "Import successful!";
|
||||
notification.State = ProgressNotificationState.Completed;
|
||||
}
|
||||
|
||||
@@ -218,9 +221,11 @@ namespace osu.Game.Database
|
||||
// user requested abort
|
||||
return;
|
||||
|
||||
notification.Text = $"Deleting ({i} of {items.Count})";
|
||||
notification.Progress = (float)++i / items.Count;
|
||||
notification.Text = $"Deleting ({++i} of {items.Count})";
|
||||
|
||||
Delete(b);
|
||||
|
||||
notification.Progress = (float)i / items.Count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,9 +259,11 @@ namespace osu.Game.Database
|
||||
// user requested abort
|
||||
return;
|
||||
|
||||
notification.Text = $"Restoring ({i} of {items.Count})";
|
||||
notification.Progress = (float)++i / items.Count;
|
||||
notification.Text = $"Restoring ({++i} of {items.Count})";
|
||||
|
||||
Undelete(item);
|
||||
|
||||
notification.Progress = (float)i / items.Count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,7 +338,9 @@ namespace osu.Game.Database
|
||||
{
|
||||
if (ZipFile.IsZipFile(path))
|
||||
return new ZipArchiveReader(Files.Storage.GetStream(path), Path.GetFileName(path));
|
||||
return new LegacyFilesystemReader(path);
|
||||
if (Directory.Exists(path))
|
||||
return new LegacyFilesystemReader(path);
|
||||
throw new InvalidFormatException($"{path} is not a valid archive");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace osu.Game.Database
|
||||
/// </summary>
|
||||
/// <typeparam name="TFile">The model representing a file.</typeparam>
|
||||
public interface IHasFiles<TFile>
|
||||
where TFile : INamedFileInfo
|
||||
|
||||
{
|
||||
List<TFile> Files { get; set; }
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
triangle,
|
||||
colourInfo,
|
||||
null,
|
||||
Shared.VertexBatch.Add,
|
||||
Shared.VertexBatch.AddAction,
|
||||
Vector2.Divide(localInflationAmount, size));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
|
||||
namespace osu.Game.Graphics
|
||||
{
|
||||
public class ScreenshotManager : Container, IKeyBindingHandler<GlobalAction>, IHandleGlobalInput
|
||||
{
|
||||
private Bindable<ScreenshotFormat> screenshotFormat;
|
||||
private GameHost host;
|
||||
private Storage storage;
|
||||
private NotificationOverlay notificationOverlay;
|
||||
|
||||
private SampleChannel shutter;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, OsuConfigManager config, Storage storage, NotificationOverlay notificationOverlay, AudioManager audio)
|
||||
{
|
||||
this.host = host;
|
||||
this.storage = storage.GetStorageForDirectory(@"screenshots");
|
||||
this.notificationOverlay = notificationOverlay;
|
||||
|
||||
screenshotFormat = config.GetBindable<ScreenshotFormat>(OsuSetting.ScreenshotFormat);
|
||||
|
||||
shutter = audio.Sample.Get("UI/shutter");
|
||||
}
|
||||
|
||||
public bool OnPressed(GlobalAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case GlobalAction.TakeScreenshot:
|
||||
shutter.Play();
|
||||
TakeScreenshotAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(GlobalAction action) => false;
|
||||
|
||||
public async void TakeScreenshotAsync()
|
||||
{
|
||||
using (var bitmap = await host.TakeScreenshotAsync())
|
||||
{
|
||||
var fileName = getFileName();
|
||||
if (fileName == null) return;
|
||||
|
||||
var stream = storage.GetStream(fileName, FileAccess.Write);
|
||||
|
||||
switch (screenshotFormat.Value)
|
||||
{
|
||||
case ScreenshotFormat.Png:
|
||||
bitmap.Save(stream, ImageFormat.Png);
|
||||
break;
|
||||
case ScreenshotFormat.Jpg:
|
||||
bitmap.Save(stream, ImageFormat.Jpeg);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(screenshotFormat));
|
||||
}
|
||||
|
||||
notificationOverlay.Post(new SimpleNotification
|
||||
{
|
||||
Text = $"{fileName} saved!",
|
||||
Activated = () =>
|
||||
{
|
||||
storage.OpenInNativeExplorer();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private string getFileName()
|
||||
{
|
||||
var dt = DateTime.Now;
|
||||
var fileExt = screenshotFormat.ToString().ToLower();
|
||||
|
||||
var withoutIndex = $"osu_{dt:yyyy-MM-dd_HH-mm-ss}.{fileExt}";
|
||||
if (!storage.Exists(withoutIndex))
|
||||
return withoutIndex;
|
||||
|
||||
for (ulong i = 1; i < ulong.MaxValue; i++)
|
||||
{
|
||||
var indexedName = $"osu_{dt:yyyy-MM-dd_HH-mm-ss}-{i}.{fileExt}";
|
||||
if (!storage.Exists(indexedName))
|
||||
return indexedName;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -988,7 +988,7 @@ namespace osu.Game.Graphics
|
||||
fa_osu_expert_mania = 0xe028,
|
||||
|
||||
// mod icons
|
||||
fa_osu_mod_perfect = 0xe02d,
|
||||
fa_osu_mod_perfect = 0xe049,
|
||||
fa_osu_mod_autopilot = 0xe03a,
|
||||
fa_osu_mod_auto = 0xe03b,
|
||||
fa_osu_mod_cinema = 0xe03c,
|
||||
|
||||
@@ -30,6 +30,9 @@ namespace osu.Game.Graphics.UserInterface
|
||||
}
|
||||
}
|
||||
|
||||
// We may not be focused yet, but we need to handle keyboard input to be able to request focus
|
||||
public override bool HandleKeyboardInput => HoldFocus || base.HandleKeyboardInput;
|
||||
|
||||
protected override void OnFocus(InputState state)
|
||||
{
|
||||
base.OnFocus(state);
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace osu.Game.Input.Bindings
|
||||
{
|
||||
new KeyBinding(InputKey.F8, GlobalAction.ToggleChat),
|
||||
new KeyBinding(InputKey.F9, GlobalAction.ToggleSocial),
|
||||
new KeyBinding(InputKey.F12,GlobalAction.TakeScreenshot),
|
||||
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings),
|
||||
@@ -72,5 +74,8 @@ namespace osu.Game.Input.Bindings
|
||||
SkipCutscene,
|
||||
[Description("Quick Retry (Hold)")]
|
||||
QuickRetry,
|
||||
|
||||
[Description("Take screenshot")]
|
||||
TakeScreenshot
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
@@ -44,8 +45,7 @@ namespace osu.Game.Online.API
|
||||
|
||||
protected bool HasLogin => Token != null || !string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password);
|
||||
|
||||
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable (should dispose of this or at very least keep a reference).
|
||||
private readonly Thread thread;
|
||||
private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource();
|
||||
|
||||
private readonly Logger log;
|
||||
|
||||
@@ -59,8 +59,7 @@ namespace osu.Game.Online.API
|
||||
ProvidedUsername = config.Get<string>(OsuSetting.Username);
|
||||
Token = config.Get<string>(OsuSetting.Token);
|
||||
|
||||
thread = new Thread(run) { IsBackground = true };
|
||||
thread.Start();
|
||||
Task.Factory.StartNew(run, cancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||
}
|
||||
|
||||
private readonly List<IOnlineComponent> components = new List<IOnlineComponent>();
|
||||
@@ -93,7 +92,7 @@ namespace osu.Game.Online.API
|
||||
|
||||
private void run()
|
||||
{
|
||||
while (thread.IsAlive)
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
switch (State)
|
||||
{
|
||||
@@ -267,10 +266,7 @@ namespace osu.Game.Online.API
|
||||
|
||||
public bool IsLoggedIn => LocalUser.Value.Id > 1;
|
||||
|
||||
public void Queue(APIRequest request)
|
||||
{
|
||||
queue.Enqueue(request);
|
||||
}
|
||||
public void Queue(APIRequest request) => queue.Enqueue(request);
|
||||
|
||||
public event StateChangeDelegate OnStateChange;
|
||||
|
||||
@@ -312,6 +308,9 @@ namespace osu.Game.Online.API
|
||||
|
||||
config.Set(OsuSetting.Token, config.Get<bool>(OsuSetting.SavePassword) ? Token : string.Empty);
|
||||
config.Save();
|
||||
|
||||
flushQueue();
|
||||
cancellationToken.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-1
@@ -133,7 +133,6 @@ namespace osu.Game
|
||||
|
||||
// bind config int to database SkinInfo
|
||||
configSkin = LocalConfig.GetBindable<int>(OsuSetting.Skin);
|
||||
|
||||
SkinManager.CurrentSkinInfo.ValueChanged += s => configSkin.Value = s.ID;
|
||||
configSkin.ValueChanged += id => SkinManager.CurrentSkinInfo.Value = SkinManager.Query(s => s.ID == id) ?? SkinInfo.Default;
|
||||
configSkin.TriggerChange();
|
||||
@@ -240,6 +239,7 @@ namespace osu.Game
|
||||
|
||||
loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add);
|
||||
loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add);
|
||||
loadComponentSingleFile(new ScreenshotManager(), Add);
|
||||
|
||||
//overlay elements
|
||||
loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add);
|
||||
@@ -302,6 +302,21 @@ namespace osu.Game
|
||||
};
|
||||
}
|
||||
|
||||
var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications };
|
||||
foreach (var overlay in singleDisplaySideOverlays)
|
||||
{
|
||||
overlay.StateChanged += state =>
|
||||
{
|
||||
if (state == Visibility.Hidden) return;
|
||||
|
||||
foreach (var c in singleDisplaySideOverlays)
|
||||
{
|
||||
if (c == overlay) continue;
|
||||
c.State = Visibility.Hidden;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// eventually informational overlays should be displayed in a stack, but for now let's only allow one to stay open at a time.
|
||||
var informationalOverlays = new OverlayContainer[] { beatmapSetOverlay, userProfile };
|
||||
foreach (var overlay in informationalOverlays)
|
||||
|
||||
@@ -105,6 +105,7 @@ namespace osu.Game
|
||||
runMigrations();
|
||||
|
||||
dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio));
|
||||
dependencies.CacheAs<ISkinSource>(SkinManager);
|
||||
|
||||
var api = new APIAccess(LocalConfig);
|
||||
|
||||
@@ -113,7 +114,7 @@ namespace osu.Game
|
||||
|
||||
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
|
||||
dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage));
|
||||
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Host));
|
||||
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host));
|
||||
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory, Host, BeatmapManager, RulesetStore));
|
||||
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore));
|
||||
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory));
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
@@ -30,7 +27,6 @@ namespace osu.Game.Overlays.Mods
|
||||
private ModIcon backgroundIcon;
|
||||
private readonly SpriteText text;
|
||||
private readonly Container<ModIcon> iconsContainer;
|
||||
private SampleChannel sampleOn, sampleOff;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when the selection changes.
|
||||
@@ -100,7 +96,6 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
foregroundIcon.Highlighted = Selected;
|
||||
|
||||
(selectedIndex == -1 ? sampleOff : sampleOn).Play();
|
||||
SelectionChanged?.Invoke(SelectedMod);
|
||||
return true;
|
||||
}
|
||||
@@ -152,13 +147,6 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
public virtual Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
sampleOn = audio.Sample.Get(@"UI/check-on");
|
||||
sampleOff = audio.Sample.Get(@"UI/check-off");
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
switch (args.Button)
|
||||
|
||||
@@ -15,6 +15,8 @@ using osu.Game.Rulesets.Mods;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
@@ -49,7 +51,7 @@ namespace osu.Game.Overlays.Mods
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets)
|
||||
private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets, AudioManager audio)
|
||||
{
|
||||
SelectedMods.ValueChanged += selectedModsChanged;
|
||||
|
||||
@@ -63,6 +65,9 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
Ruleset.ValueChanged += rulesetChanged;
|
||||
Ruleset.TriggerChange();
|
||||
|
||||
sampleOn = audio.Sample.Get(@"UI/check-on");
|
||||
sampleOff = audio.Sample.Get(@"UI/check-off");
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
@@ -154,10 +159,21 @@ namespace osu.Game.Overlays.Mods
|
||||
section.DeselectTypes(modTypes, immediate);
|
||||
}
|
||||
|
||||
|
||||
private SampleChannel sampleOn, sampleOff;
|
||||
|
||||
private void modButtonPressed(Mod selectedMod)
|
||||
{
|
||||
if (selectedMod != null)
|
||||
{
|
||||
if (State == Visibility.Visible) sampleOn?.Play();
|
||||
DeselectTypes(selectedMod.IncompatibleMods, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (State == Visibility.Visible) sampleOff?.Play();
|
||||
}
|
||||
|
||||
refreshSelectedMods();
|
||||
}
|
||||
|
||||
|
||||
@@ -110,17 +110,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
private int runningDepth;
|
||||
|
||||
private void notificationClosed()
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
// hide ourselves if all notifications have been dismissed.
|
||||
if (totalCount == 0)
|
||||
State = Visibility.Hidden;
|
||||
});
|
||||
|
||||
updateCounts();
|
||||
}
|
||||
private void notificationClosed() => updateCounts();
|
||||
|
||||
private readonly Scheduler postScheduler = new Scheduler();
|
||||
|
||||
@@ -141,6 +131,8 @@ namespace osu.Game.Overlays
|
||||
var section = sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType)));
|
||||
section?.Add(notification, notification.DisplayOnTop ? -runningDepth : runningDepth);
|
||||
|
||||
State = Visibility.Visible;
|
||||
|
||||
updateCounts();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Configuration;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
@@ -12,7 +14,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
Children = new[]
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SettingsCheckbox
|
||||
{
|
||||
@@ -24,6 +26,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
LabelText = "Rotate cursor when dragging",
|
||||
Bindable = config.GetBindable<bool>(OsuSetting.CursorRotation)
|
||||
},
|
||||
new SettingsEnumDropdown<ScreenshotFormat>
|
||||
{
|
||||
LabelText = "Screenshot format",
|
||||
Bindable = config.GetBindable<ScreenshotFormat>(OsuSetting.ScreenshotFormat)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
},
|
||||
};
|
||||
|
||||
void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair<string, int>(s.Name, s.ID));
|
||||
void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair<string, int>(s.ToString(), s.ID));
|
||||
skins.ItemAdded += _ => reloadSkins();
|
||||
skins.ItemRemoved += _ => reloadSkins();
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Input.Bindings;
|
||||
@@ -93,19 +94,25 @@ namespace osu.Game.Overlays.Volume
|
||||
Colour = colours.Gray2,
|
||||
Size = new Vector2(0.8f)
|
||||
},
|
||||
(volumeCircle = new CircularProgress
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
InnerRadius = 0.05f,
|
||||
Rotation = 180,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(0.8f)
|
||||
}).WithEffect(new GlowEffect
|
||||
{
|
||||
Colour = meterColour,
|
||||
Strength = 2
|
||||
}),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Size = new Vector2(0.8f),
|
||||
Padding = new MarginPadding(-Blur.KernelSize(5)),
|
||||
Rotation = 180,
|
||||
Child = (volumeCircle = new CircularProgress
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
InnerRadius = 0.05f,
|
||||
}).WithEffect(new GlowEffect
|
||||
{
|
||||
Colour = meterColour,
|
||||
Strength = 2,
|
||||
PadExtent = true
|
||||
}),
|
||||
},
|
||||
maxGlow = (text = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -16,6 +17,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Edit.Screens.Compose;
|
||||
using osu.Game.Screens.Edit.Screens.Compose.Layers;
|
||||
using osu.Game.Screens.Edit.Screens.Compose.RadioButtons;
|
||||
|
||||
@@ -31,32 +33,31 @@ namespace osu.Game.Rulesets.Edit
|
||||
private readonly List<Container> layerContainers = new List<Container>();
|
||||
|
||||
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
||||
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
||||
|
||||
private IAdjustableClock sourceClock;
|
||||
private DecoupleableInterpolatingFramedClock adjustableClock;
|
||||
private IAdjustableClock adjustableClock;
|
||||
|
||||
protected HitObjectComposer(Ruleset ruleset)
|
||||
{
|
||||
this.ruleset = ruleset;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase osuGame)
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load([NotNull] OsuGameBase osuGame, [NotNull] IAdjustableClock adjustableClock, [NotNull] IFrameBasedClock framedClock, [CanBeNull] BindableBeatDivisor beatDivisor)
|
||||
{
|
||||
this.adjustableClock = adjustableClock;
|
||||
|
||||
if (beatDivisor != null)
|
||||
this.beatDivisor.BindTo(beatDivisor);
|
||||
|
||||
beatmap.BindTo(osuGame.Beatmap);
|
||||
|
||||
try
|
||||
{
|
||||
rulesetContainer = CreateRulesetContainer(ruleset, beatmap.Value);
|
||||
|
||||
// TODO: should probably be done at a RulesetContainer level to share logic with Player.
|
||||
sourceClock = (IAdjustableClock)beatmap.Value.Track ?? new StopwatchClock();
|
||||
adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
adjustableClock.ChangeSource(sourceClock);
|
||||
|
||||
rulesetContainer.Clock = adjustableClock;
|
||||
|
||||
rulesetContainer.Clock = framedClock;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -172,9 +173,6 @@ namespace osu.Game.Rulesets.Edit
|
||||
|
||||
private void seek(int direction, bool snapped)
|
||||
{
|
||||
// Todo: This should not be a constant, but feels good for now
|
||||
const int beat_snap_divisor = 4;
|
||||
|
||||
var cpi = beatmap.Value.Beatmap.ControlPointInfo;
|
||||
|
||||
var timingPoint = cpi.TimingPointAt(adjustableClock.CurrentTime);
|
||||
@@ -186,7 +184,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
timingPoint = cpi.TimingPoints[--activeIndex];
|
||||
}
|
||||
|
||||
double seekAmount = timingPoint.BeatLength / beat_snap_divisor;
|
||||
double seekAmount = timingPoint.BeatLength / beatDivisor;
|
||||
double seekTime = adjustableClock.CurrentTime + seekAmount * direction;
|
||||
|
||||
if (!snapped || cpi.TimingPoints.Count == 0)
|
||||
@@ -227,9 +225,6 @@ namespace osu.Game.Rulesets.Edit
|
||||
|
||||
public void SeekTo(double seekTime, bool snapped = false)
|
||||
{
|
||||
// Todo: This should not be a constant, but feels good for now
|
||||
const int beat_snap_divisor = 4;
|
||||
|
||||
if (!snapped)
|
||||
{
|
||||
adjustableClock.Seek(seekTime);
|
||||
@@ -237,7 +232,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
}
|
||||
|
||||
var timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(seekTime);
|
||||
double beatSnapLength = timingPoint.BeatLength / beat_snap_divisor;
|
||||
double beatSnapLength = timingPoint.BeatLength / beatDivisor;
|
||||
|
||||
// We will be snapping to beats within the timing point
|
||||
seekTime -= timingPoint.Time;
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public override string Name => "Perfect";
|
||||
public override string ShortenedName => "PF";
|
||||
public override FontAwesome Icon => FontAwesome.fa_question;
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_mod_perfect;
|
||||
public override string Description => "SS or quit.";
|
||||
|
||||
protected override bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Accuracy.Value != 1;
|
||||
|
||||
@@ -6,7 +6,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Graphics;
|
||||
@@ -19,7 +18,7 @@ using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Drawables
|
||||
{
|
||||
public abstract class DrawableHitObject : CompositeDrawable, IHasAccentColour
|
||||
public abstract class DrawableHitObject : SkinReloadableDrawable, IHasAccentColour
|
||||
{
|
||||
public readonly HitObject HitObject;
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// A HitObject that is part of a combo and has extended information about its position relative to other combo objects.
|
||||
/// </summary>
|
||||
public interface IHasComboIndex : IHasCombo
|
||||
{
|
||||
/// <summary>
|
||||
/// The offset of this hitobject in the current combo.
|
||||
/// </summary>
|
||||
int IndexInCurrentCombo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The offset of this hitobject in the current combo.
|
||||
/// </summary>
|
||||
int ComboIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this is the last object in the current combo.
|
||||
/// </summary>
|
||||
bool LastInCombo { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// A HitObject that is part of a combo and has extended information about its position relative to other combo objects.
|
||||
/// </summary>
|
||||
public interface IHasComboInformation : IHasCombo
|
||||
{
|
||||
/// <summary>
|
||||
/// The offset of this hitobject in the current combo.
|
||||
/// </summary>
|
||||
int IndexInCurrentCombo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The offset of this combo in relation to the beatmap.
|
||||
/// </summary>
|
||||
int ComboIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this is the last object in the current combo.
|
||||
/// </summary>
|
||||
bool LastInCombo { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
@@ -17,10 +18,15 @@ namespace osu.Game.Screens.Edit.Components
|
||||
{
|
||||
public class PlaybackControl : BottomBarContainer
|
||||
{
|
||||
private readonly IconButton playButton;
|
||||
private IconButton playButton;
|
||||
|
||||
public PlaybackControl()
|
||||
private IAdjustableClock adjustableClock;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAdjustableClock adjustableClock)
|
||||
{
|
||||
this.adjustableClock = adjustableClock;
|
||||
|
||||
PlaybackTabControl tabs;
|
||||
|
||||
Children = new Drawable[]
|
||||
@@ -54,22 +60,22 @@ namespace osu.Game.Screens.Edit.Components
|
||||
}
|
||||
};
|
||||
|
||||
tabs.Current.ValueChanged += newValue => Track.Tempo.Value = newValue;
|
||||
tabs.Current.ValueChanged += newValue => Beatmap.Value.Track.Tempo.Value = newValue;
|
||||
}
|
||||
|
||||
private void togglePause()
|
||||
{
|
||||
if (Track.IsRunning)
|
||||
Track.Stop();
|
||||
if (adjustableClock.IsRunning)
|
||||
adjustableClock.Stop();
|
||||
else
|
||||
Track.Start();
|
||||
adjustableClock.Start();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
playButton.Icon = Track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o;
|
||||
playButton.Icon = adjustableClock.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o;
|
||||
}
|
||||
|
||||
private class PlaybackTabControl : OsuTabControl<double>
|
||||
|
||||
@@ -4,17 +4,20 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Timing;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Components
|
||||
{
|
||||
public class TimeInfoContainer : BottomBarContainer
|
||||
{
|
||||
private const int count_duration = 150;
|
||||
|
||||
private readonly OsuSpriteText trackTimer;
|
||||
|
||||
private IAdjustableClock adjustableClock;
|
||||
|
||||
public TimeInfoContainer()
|
||||
{
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
trackTimer = new OsuSpriteText
|
||||
@@ -28,11 +31,17 @@ namespace osu.Game.Screens.Edit.Components
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAdjustableClock adjustableClock)
|
||||
{
|
||||
this.adjustableClock = adjustableClock;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
trackTimer.Text = TimeSpan.FromMilliseconds(Track.CurrentTime).ToString(@"mm\:ss\:fff");
|
||||
trackTimer.Text = TimeSpan.FromMilliseconds(adjustableClock.CurrentTime).ToString(@"mm\:ss\:fff");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
@@ -19,8 +20,12 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||
{
|
||||
private readonly Drawable marker;
|
||||
|
||||
public MarkerPart()
|
||||
private readonly IAdjustableClock adjustableClock;
|
||||
|
||||
public MarkerPart(IAdjustableClock adjustableClock)
|
||||
{
|
||||
this.adjustableClock = adjustableClock;
|
||||
|
||||
Add(marker = new MarkerVisualisation());
|
||||
}
|
||||
|
||||
@@ -53,12 +58,12 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||
seekTo(markerPos / DrawWidth * Beatmap.Value.Track.Length);
|
||||
}
|
||||
|
||||
private void seekTo(double time) => Beatmap.Value?.Track.Seek(time);
|
||||
private void seekTo(double time) => adjustableClock.Seek(time);
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
marker.X = (float)(Beatmap.Value?.Track.CurrentTime ?? 0);
|
||||
marker.X = (float)adjustableClock.CurrentTime;
|
||||
}
|
||||
|
||||
protected override void LoadBeatmap(WorkingBeatmap beatmap)
|
||||
|
||||
@@ -6,6 +6,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
|
||||
|
||||
@@ -16,15 +17,14 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
|
||||
/// </summary>
|
||||
public class SummaryTimeline : BottomBarContainer
|
||||
{
|
||||
private readonly Drawable timelineBar;
|
||||
|
||||
public SummaryTimeline()
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, IAdjustableClock adjustableClock)
|
||||
{
|
||||
TimelinePart markerPart, controlPointPart, bookmarkPart, breakPart;
|
||||
|
||||
Children = new[]
|
||||
Children = new Drawable[]
|
||||
{
|
||||
markerPart = new MarkerPart { RelativeSizeAxes = Axes.Both },
|
||||
markerPart = new MarkerPart(adjustableClock) { RelativeSizeAxes = Axes.Both },
|
||||
controlPointPart = new ControlPointPart
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
@@ -39,9 +39,10 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.35f
|
||||
},
|
||||
timelineBar = new Container
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colours.Gray5,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Circle
|
||||
@@ -80,11 +81,5 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
|
||||
bookmarkPart.Beatmap.BindTo(Beatmap);
|
||||
breakPart.Beatmap.BindTo(Beatmap);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
timelineBar.Colour = colours.Gray5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ using osu.Game.Screens.Edit.Menus;
|
||||
using osu.Game.Screens.Edit.Components.Timelines.Summary;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Screens.Edit.Screens;
|
||||
using osu.Game.Screens.Edit.Screens.Compose;
|
||||
@@ -26,13 +27,27 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
public override bool ShowOverlaysOnEnter => false;
|
||||
|
||||
private readonly Box bottomBackground;
|
||||
private readonly Container screenContainer;
|
||||
private Box bottomBackground;
|
||||
private Container screenContainer;
|
||||
|
||||
private EditorScreen currentScreen;
|
||||
|
||||
public Editor()
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(parent);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
// TODO: should probably be done at a RulesetContainer level to share logic with Player.
|
||||
var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock();
|
||||
var adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
adjustableClock.ChangeSource(sourceClock);
|
||||
|
||||
dependencies.CacheAs<IAdjustableClock>(adjustableClock);
|
||||
dependencies.CacheAs<IFrameBasedClock>(adjustableClock);
|
||||
|
||||
EditorMenuBar menuBar;
|
||||
TimeInfoContainer timeInfo;
|
||||
SummaryTimeline timeline;
|
||||
@@ -130,12 +145,9 @@ namespace osu.Game.Screens.Edit
|
||||
timeline.Beatmap.BindTo(Beatmap);
|
||||
playback.Beatmap.BindTo(Beatmap);
|
||||
menuBar.Mode.ValueChanged += onModeChanged;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
bottomBackground.Colour = colours.Gray2;
|
||||
|
||||
}
|
||||
|
||||
private void exportBeatmap()
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Screens.Compose
|
||||
{
|
||||
public class BeatDivisorControl : CompositeDrawable
|
||||
{
|
||||
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
||||
private int currentDivisorIndex;
|
||||
|
||||
public BeatDivisorControl(BindableBeatDivisor beatDivisor)
|
||||
{
|
||||
this.beatDivisor.BindTo(beatDivisor);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Masking = true;
|
||||
CornerRadius = 5;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Name = "Gray Background",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colours.Gray4
|
||||
},
|
||||
new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Name = "Black Background",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black
|
||||
},
|
||||
new TickSliderBar(beatDivisor, BindableBeatDivisor.VALID_DIVISORS)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colours.Gray4
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Horizontal = 5 },
|
||||
Child = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new DivisorButton
|
||||
{
|
||||
Icon = FontAwesome.fa_chevron_left,
|
||||
Action = beatDivisor.Previous
|
||||
},
|
||||
new DivisorText(beatDivisor),
|
||||
new DivisorButton
|
||||
{
|
||||
Icon = FontAwesome.fa_chevron_right,
|
||||
Action = beatDivisor.Next
|
||||
}
|
||||
},
|
||||
},
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.Absolute, 20),
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.Absolute, 20)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
new TextFlowContainer(s => s.TextSize = 14)
|
||||
{
|
||||
Padding = new MarginPadding { Horizontal = 15 },
|
||||
Text = "beat snap divisor",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
TextAnchor = Anchor.TopCentre
|
||||
},
|
||||
}
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.Absolute, 30),
|
||||
new Dimension(GridSizeMode.Absolute, 25),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class DivisorText : SpriteText
|
||||
{
|
||||
private readonly Bindable<int> beatDivisor = new Bindable<int>();
|
||||
|
||||
public DivisorText(BindableBeatDivisor beatDivisor)
|
||||
{
|
||||
this.beatDivisor.BindTo(beatDivisor);
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Colour = colours.BlueLighter;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
beatDivisor.ValueChanged += v => updateText();
|
||||
updateText();
|
||||
}
|
||||
|
||||
private void updateText() => Text = $"1/{beatDivisor.Value}";
|
||||
}
|
||||
|
||||
private class DivisorButton : IconButton
|
||||
{
|
||||
public DivisorButton()
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
// Small offset to look a bit better centered along with the divisor text
|
||||
Y = 1;
|
||||
|
||||
ButtonSize = new Vector2(20);
|
||||
IconScale = new Vector2(0.6f);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
IconColour = Color4.Black;
|
||||
HoverColour = colours.Gray7;
|
||||
FlashColour = colours.Gray9;
|
||||
}
|
||||
}
|
||||
|
||||
private class TickSliderBar : SliderBar<int>
|
||||
{
|
||||
private Marker marker;
|
||||
|
||||
private readonly BindableBeatDivisor beatDivisor;
|
||||
private readonly int[] availableDivisors;
|
||||
|
||||
public TickSliderBar(BindableBeatDivisor beatDivisor, params int[] divisors)
|
||||
{
|
||||
CurrentNumber.BindTo(this.beatDivisor = beatDivisor);
|
||||
availableDivisors = divisors;
|
||||
|
||||
Padding = new MarginPadding { Horizontal = 5 };
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
foreach (var t in availableDivisors)
|
||||
{
|
||||
AddInternal(new Tick(t)
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = getMappedPosition(t)
|
||||
});
|
||||
}
|
||||
|
||||
AddInternal(marker = new Marker());
|
||||
|
||||
CurrentNumber.ValueChanged += v =>
|
||||
{
|
||||
marker.MoveToX(getMappedPosition(v), 100, Easing.OutQuint);
|
||||
marker.Flash();
|
||||
};
|
||||
}
|
||||
|
||||
protected override void UpdateValue(float value)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool HandleKeyboardInput => IsHovered && !CurrentNumber.Disabled;
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
switch (args.Key)
|
||||
{
|
||||
case Key.Right:
|
||||
beatDivisor.Next();
|
||||
OnUserChange();
|
||||
return true;
|
||||
case Key.Left:
|
||||
beatDivisor.Previous();
|
||||
OnUserChange();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
marker.Active = true;
|
||||
return base.OnMouseDown(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
marker.Active = false;
|
||||
return base.OnMouseUp(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
handleMouseInput(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnDrag(InputState state)
|
||||
{
|
||||
handleMouseInput(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleMouseInput(InputState state)
|
||||
{
|
||||
// copied from SliderBar so we can do custom spacing logic.
|
||||
var xPosition = (ToLocalSpace(state?.Mouse.NativeState.Position ?? Vector2.Zero).X - RangePadding) / UsableWidth;
|
||||
|
||||
CurrentNumber.Value = availableDivisors.OrderBy(d => Math.Abs(getMappedPosition(d) - xPosition)).First();
|
||||
OnUserChange();
|
||||
}
|
||||
|
||||
private float getMappedPosition(float divisor) => (float)Math.Pow((divisor - 1) / (availableDivisors.Last() - 1), 0.90f);
|
||||
|
||||
private class Tick : CompositeDrawable
|
||||
{
|
||||
private readonly int divisor;
|
||||
|
||||
public Tick(int divisor)
|
||||
{
|
||||
this.divisor = divisor;
|
||||
Size = new Vector2(2.5f, 10);
|
||||
|
||||
InternalChild = new Box { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
CornerRadius = 0.5f;
|
||||
Masking = true;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Colour = getColourForDivisor(divisor, colours);
|
||||
}
|
||||
|
||||
private ColourInfo getColourForDivisor(int divisor, OsuColour colours)
|
||||
{
|
||||
switch (divisor)
|
||||
{
|
||||
case 2:
|
||||
return colours.BlueLight;
|
||||
case 4:
|
||||
return colours.Blue;
|
||||
case 8:
|
||||
return colours.BlueDarker;
|
||||
case 16:
|
||||
return colours.PurpleDark;
|
||||
case 3:
|
||||
return colours.YellowLight;
|
||||
case 6:
|
||||
return colours.Yellow;
|
||||
case 12:
|
||||
return colours.YellowDarker;
|
||||
default:
|
||||
return Color4.White;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Marker : CompositeDrawable
|
||||
{
|
||||
private Color4 defaultColour;
|
||||
|
||||
private const float size = 7;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Colour = defaultColour = colours.Gray4;
|
||||
Anchor = Anchor.TopLeft;
|
||||
Origin = Anchor.TopCentre;
|
||||
|
||||
Width = size;
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
RelativePositionAxes = Axes.X;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Width = 2,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0.2f), Color4.White),
|
||||
Blending = BlendingMode.Additive,
|
||||
},
|
||||
new EquilateralTriangle
|
||||
{
|
||||
Origin = Anchor.BottomCentre,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Height = size,
|
||||
EdgeSmoothness = new Vector2(1),
|
||||
Colour = Color4.White,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private bool active;
|
||||
|
||||
public bool Active
|
||||
{
|
||||
get => active;
|
||||
set
|
||||
{
|
||||
this.FadeColour(value ? Color4.White : defaultColour, 500, Easing.OutQuint);
|
||||
active = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void Flash()
|
||||
{
|
||||
bool wasActive = active;
|
||||
|
||||
Active = true;
|
||||
|
||||
if (wasActive) return;
|
||||
|
||||
using (BeginDelayedSequence(50))
|
||||
Active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Configuration;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Screens.Compose
|
||||
{
|
||||
public class BindableBeatDivisor : BindableNumber<int>
|
||||
{
|
||||
public static readonly int[] VALID_DIVISORS = { 1, 2, 3, 4, 6, 8, 12, 16 };
|
||||
|
||||
public BindableBeatDivisor(int value = 1)
|
||||
: base(value)
|
||||
{
|
||||
}
|
||||
|
||||
public void Next() => Value = VALID_DIVISORS[Math.Min(VALID_DIVISORS.Length - 1, Array.IndexOf(VALID_DIVISORS, Value) + 1)];
|
||||
|
||||
public void Previous() => Value = VALID_DIVISORS[Math.Max(0, Array.IndexOf(VALID_DIVISORS, Value) - 1)];
|
||||
|
||||
public override int Value
|
||||
{
|
||||
get { return base.Value; }
|
||||
set
|
||||
{
|
||||
if (!VALID_DIVISORS.Contains(value))
|
||||
throw new ArgumentOutOfRangeException($"Provided divisor is not in {nameof(VALID_DIVISORS)}");
|
||||
|
||||
base.Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override int DefaultMinValue => VALID_DIVISORS.First();
|
||||
protected override int DefaultMaxValue => VALID_DIVISORS.Last();
|
||||
protected override int DefaultPrecision => 1;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Screens.Edit.Screens.Compose.Timeline;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Screens.Compose
|
||||
@@ -17,10 +17,20 @@ namespace osu.Game.Screens.Edit.Screens.Compose
|
||||
private const float vertical_margins = 10;
|
||||
private const float horizontal_margins = 20;
|
||||
|
||||
private readonly Container composerContainer;
|
||||
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
||||
|
||||
public Compose()
|
||||
private Container composerContainer;
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(parent);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
dependencies.Cache(beatDivisor);
|
||||
|
||||
ScrollableTimeline timeline;
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@@ -47,15 +57,28 @@ namespace osu.Game.Screens.Edit.Screens.Compose
|
||||
Name = "Timeline content",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins },
|
||||
Children = new Drawable[]
|
||||
Child = new GridContainer
|
||||
{
|
||||
new Container
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Right = 115 },
|
||||
Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both }
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Right = 5 },
|
||||
Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both }
|
||||
},
|
||||
new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both }
|
||||
},
|
||||
},
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.Absolute, 90),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,14 +98,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose
|
||||
};
|
||||
|
||||
timeline.Beatmap.BindTo(Beatmap);
|
||||
Beatmap.ValueChanged += beatmapChanged;
|
||||
}
|
||||
|
||||
private void beatmapChanged(WorkingBeatmap newBeatmap)
|
||||
{
|
||||
composerContainer.Clear();
|
||||
|
||||
var ruleset = newBeatmap.BeatmapInfo.Ruleset?.CreateInstance();
|
||||
var ruleset = Beatmap.Value.BeatmapInfo.Ruleset?.CreateInstance();
|
||||
if (ruleset == null)
|
||||
{
|
||||
Logger.Log("Beatmap doesn't have a ruleset assigned.");
|
||||
|
||||
@@ -51,7 +51,11 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||
|
||||
private SelectionBox currentSelectionBox;
|
||||
|
||||
public void AddSelectionOverlay() => AddInternal(currentSelectionBox = composer.CreateSelectionOverlay(overlayContainer));
|
||||
public void AddSelectionOverlay()
|
||||
{
|
||||
if (overlayContainer.Count > 0)
|
||||
AddInternal(currentSelectionBox = composer.CreateSelectionOverlay(overlayContainer));
|
||||
}
|
||||
|
||||
public void RemoveSelectionOverlay()
|
||||
{
|
||||
|
||||
@@ -211,7 +211,7 @@ namespace osu.Game.Screens.Menu
|
||||
rectangle,
|
||||
colourInfo,
|
||||
null,
|
||||
Shared.VertexBatch.Add,
|
||||
Shared.VertexBatch.AddAction,
|
||||
//barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that.
|
||||
Vector2.Divide(inflation, barSize.Yx));
|
||||
}
|
||||
|
||||
@@ -337,12 +337,10 @@ namespace osu.Game.Screens.Menu
|
||||
}
|
||||
}
|
||||
|
||||
private bool interactive => Action != null && Alpha > 0.2f;
|
||||
public override bool HandleMouseInput => base.HandleMouseInput && Action != null && Alpha > 0.2f;
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
if (!interactive) return false;
|
||||
|
||||
logoBounceContainer.ScaleTo(0.9f, 1000, Easing.Out);
|
||||
return true;
|
||||
}
|
||||
@@ -355,8 +353,6 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
if (!interactive) return false;
|
||||
|
||||
if (Action?.Invoke() ?? true)
|
||||
sampleClick.Play();
|
||||
|
||||
@@ -368,8 +364,6 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
if (!interactive) return false;
|
||||
|
||||
logoHoverContainer.ScaleTo(1.1f, 500, Easing.OutElastic);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Storyboards.Drawables;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
@@ -163,7 +164,11 @@ namespace osu.Game.Screens.Play
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
},
|
||||
RulesetContainer,
|
||||
new LocalSkinOverrideContainer(working.Skin)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = RulesetContainer
|
||||
},
|
||||
new SkipOverlay(firstObjectTime)
|
||||
{
|
||||
Clock = Clock, // skip button doesn't want to use the audio clock directly
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@@ -27,6 +28,8 @@ namespace osu.Game.Screens.Play
|
||||
private bool showOverlays = true;
|
||||
public override bool ShowOverlaysOnEnter => showOverlays;
|
||||
|
||||
private Task loadTask;
|
||||
|
||||
public PlayerLoader(Player player)
|
||||
{
|
||||
this.player = player;
|
||||
@@ -55,7 +58,7 @@ namespace osu.Game.Screens.Play
|
||||
Margin = new MarginPadding(25)
|
||||
});
|
||||
|
||||
LoadComponentAsync(player);
|
||||
loadTask = LoadComponentAsync(player);
|
||||
}
|
||||
|
||||
protected override void OnResuming(Screen last)
|
||||
@@ -65,7 +68,7 @@ namespace osu.Game.Screens.Play
|
||||
contentIn();
|
||||
|
||||
//we will only be resumed if the player has requested a re-run (see ValidForResume setting above)
|
||||
LoadComponentAsync(player = new Player
|
||||
loadTask = LoadComponentAsync(player = new Player
|
||||
{
|
||||
RestartCount = player.RestartCount + 1,
|
||||
RestartRequested = player.RestartRequested,
|
||||
@@ -139,8 +142,7 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
// as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce
|
||||
// if we become unready for push during the delay.
|
||||
pushDebounce?.Cancel();
|
||||
pushDebounce = null;
|
||||
cancelLoad();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -155,6 +157,8 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
if (!IsCurrentScreen) return;
|
||||
|
||||
loadTask = null;
|
||||
|
||||
if (!Push(player))
|
||||
Exit();
|
||||
else
|
||||
@@ -172,14 +176,35 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelLoad()
|
||||
{
|
||||
pushDebounce?.Cancel();
|
||||
pushDebounce = null;
|
||||
}
|
||||
|
||||
protected override void OnSuspending(Screen next)
|
||||
{
|
||||
base.OnSuspending(next);
|
||||
cancelLoad();
|
||||
}
|
||||
|
||||
protected override bool OnExiting(Screen next)
|
||||
{
|
||||
Content.ScaleTo(0.7f, 150, Easing.InQuint);
|
||||
this.FadeOut(150);
|
||||
cancelLoad();
|
||||
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
// if the player never got pushed, we should explicitly dispose it.
|
||||
loadTask?.ContinueWith(_ => player.Dispose());
|
||||
}
|
||||
|
||||
private class BeatmapMetadataDisplay : Container
|
||||
{
|
||||
private class MetadataLine : Container
|
||||
|
||||
@@ -46,6 +46,12 @@ namespace osu.Game.Screens.Play
|
||||
UpdateBackgroundElements();
|
||||
}
|
||||
|
||||
protected override void OnResuming(Screen last)
|
||||
{
|
||||
base.OnResuming(last);
|
||||
UpdateBackgroundElements();
|
||||
}
|
||||
|
||||
protected virtual void UpdateBackgroundElements()
|
||||
{
|
||||
if (!IsCurrentScreen) return;
|
||||
|
||||
@@ -328,7 +328,10 @@ namespace osu.Game.Screens.Select
|
||||
public void FlushPendingFilterOperations()
|
||||
{
|
||||
if (FilterTask?.Completed == false)
|
||||
{
|
||||
applyActiveCriteria(false, false);
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
public void Filter(FilterCriteria newCriteria, bool debounce = true)
|
||||
|
||||
@@ -259,6 +259,8 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private void workingBeatmapChanged(WorkingBeatmap beatmap)
|
||||
{
|
||||
if (beatmap is DummyWorkingBeatmap) return;
|
||||
|
||||
if (IsCurrentScreen && !Carousel.SelectBeatmap(beatmap?.BeatmapInfo, false))
|
||||
// If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch
|
||||
if (beatmap?.BeatmapInfo?.Ruleset != null && beatmap.BeatmapInfo.Ruleset != Ruleset.Value)
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
@@ -11,16 +13,22 @@ namespace osu.Game.Skinning
|
||||
public DefaultSkin()
|
||||
: base(SkinInfo.Default)
|
||||
{
|
||||
Configuration = new SkinConfiguration
|
||||
{
|
||||
ComboColours =
|
||||
{
|
||||
new Color4(17, 136, 170, 255),
|
||||
new Color4(102, 136, 0, 255),
|
||||
new Color4(204, 102, 0, 255),
|
||||
new Color4(121, 9, 13, 255)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override Drawable GetDrawableComponent(string componentName)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public override Drawable GetDrawableComponent(string componentName) => null;
|
||||
|
||||
public override SampleChannel GetSample(string sampleName)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public override Texture GetTexture(string componentName) => null;
|
||||
|
||||
public override SampleChannel GetSample(string sampleName) => null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to skinnable elements.
|
||||
/// </summary>
|
||||
public interface ISkinSource
|
||||
{
|
||||
event Action SourceChanged;
|
||||
|
||||
Drawable GetDrawableComponent(string componentName);
|
||||
|
||||
Texture GetTexture(string componentName);
|
||||
|
||||
SampleChannel GetSample(string sampleName);
|
||||
|
||||
TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class;
|
||||
|
||||
TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public class LegacyBeatmapSkin : LegacySkin
|
||||
{
|
||||
public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore<byte[]> storage, AudioManager audioManager)
|
||||
: base(createSkinInfo(beatmap), new LegacySkinResourceStore<BeatmapSetFileInfo>(beatmap.BeatmapSet, storage), audioManager, beatmap.Path)
|
||||
{
|
||||
}
|
||||
|
||||
private static SkinInfo createSkinInfo(BeatmapInfo beatmap) =>
|
||||
new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() };
|
||||
}
|
||||
}
|
||||
@@ -10,21 +10,33 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Database;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public class LegacySkin : Skin
|
||||
{
|
||||
private readonly TextureStore textures;
|
||||
protected TextureStore Textures;
|
||||
|
||||
private readonly SampleManager samples;
|
||||
protected SampleManager Samples;
|
||||
|
||||
public LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager)
|
||||
: base(skin)
|
||||
: this(skin, new LegacySkinResourceStore<SkinFileInfo>(skin, storage), audioManager, "skin.ini")
|
||||
{
|
||||
storage = new LegacySkinResourceStore(skin, storage);
|
||||
samples = audioManager.GetSampleManager(storage);
|
||||
textures = new TextureStore(new RawTextureLoaderStore(storage));
|
||||
}
|
||||
|
||||
protected LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager, string filename) : base(skin)
|
||||
{
|
||||
Stream stream = storage.GetStream(filename);
|
||||
if (stream != null)
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
Configuration = new LegacySkinDecoder().Decode(reader);
|
||||
else
|
||||
Configuration = new SkinConfiguration();
|
||||
|
||||
Samples = audioManager.GetSampleManager(storage);
|
||||
Textures = new TextureStore(new RawTextureLoaderStore(storage));
|
||||
}
|
||||
|
||||
public override Drawable GetDrawableComponent(string componentName)
|
||||
@@ -45,37 +57,62 @@ namespace osu.Game.Skinning
|
||||
break;
|
||||
}
|
||||
|
||||
var texture = textures.Get(componentName);
|
||||
float ratio = 0.72f; // brings sizing roughly in-line with stable
|
||||
|
||||
var texture = GetTexture($"{componentName}@2x");
|
||||
if (texture == null)
|
||||
{
|
||||
ratio *= 2;
|
||||
GetTexture(componentName);
|
||||
}
|
||||
|
||||
if (texture == null) return null;
|
||||
|
||||
return new Sprite { Texture = texture };
|
||||
return new Sprite
|
||||
{
|
||||
Texture = texture,
|
||||
Scale = new Vector2(ratio),
|
||||
};
|
||||
}
|
||||
|
||||
public override SampleChannel GetSample(string sampleName) => samples.Get(sampleName);
|
||||
public override Texture GetTexture(string componentName) => Textures.Get(componentName);
|
||||
|
||||
private class LegacySkinResourceStore : IResourceStore<byte[]>
|
||||
public override SampleChannel GetSample(string sampleName) => Samples.Get(sampleName);
|
||||
|
||||
protected class LegacySkinResourceStore<T> : IResourceStore<byte[]>
|
||||
where T : INamedFileInfo
|
||||
{
|
||||
private readonly SkinInfo skin;
|
||||
private readonly IHasFiles<T> source;
|
||||
private readonly IResourceStore<byte[]> underlyingStore;
|
||||
|
||||
private string getPathForFile(string filename)
|
||||
{
|
||||
bool hasExtension = filename.Contains('.');
|
||||
|
||||
string lastPiece = filename.Split('/').Last();
|
||||
|
||||
var file = skin.Files.FirstOrDefault(f =>
|
||||
string.Equals(Path.GetFileNameWithoutExtension(f.Filename), lastPiece, StringComparison.InvariantCultureIgnoreCase));
|
||||
var file = source.Files.FirstOrDefault(f =>
|
||||
string.Equals(hasExtension ? f.Filename : Path.GetFileNameWithoutExtension(f.Filename), lastPiece, StringComparison.InvariantCultureIgnoreCase));
|
||||
return file?.FileInfo.StoragePath;
|
||||
}
|
||||
|
||||
public LegacySkinResourceStore(SkinInfo skin, IResourceStore<byte[]> underlyingStore)
|
||||
public LegacySkinResourceStore(IHasFiles<T> source, IResourceStore<byte[]> underlyingStore)
|
||||
{
|
||||
this.skin = skin;
|
||||
this.source = source;
|
||||
this.underlyingStore = underlyingStore;
|
||||
}
|
||||
|
||||
public Stream GetStream(string name) => underlyingStore.GetStream(getPathForFile(name));
|
||||
public Stream GetStream(string name)
|
||||
{
|
||||
string path = getPathForFile(name);
|
||||
return path == null ? null : underlyingStore.GetStream(path);
|
||||
}
|
||||
|
||||
byte[] IResourceStore<byte[]>.Get(string name) => underlyingStore.Get(getPathForFile(name));
|
||||
byte[] IResourceStore<byte[]>.Get(string name)
|
||||
{
|
||||
string path = getPathForFile(name);
|
||||
return path == null ? null : underlyingStore.Get(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public class LegacySkinDecoder : LegacyDecoder<SkinConfiguration>
|
||||
{
|
||||
public LegacySkinDecoder()
|
||||
: base(1)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void ParseLine(SkinConfiguration output, Section section, string line)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case Section.General:
|
||||
var pair = SplitKeyVal(line);
|
||||
|
||||
switch (pair.Key)
|
||||
{
|
||||
case @"Name":
|
||||
output.SkinInfo.Name = pair.Value;
|
||||
break;
|
||||
case @"Author":
|
||||
output.SkinInfo.Creator = pair.Value;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
base.ParseLine(output, section, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public class LocalSkinOverrideContainer : Container, ISkinSource
|
||||
{
|
||||
public event Action SourceChanged;
|
||||
|
||||
public Drawable GetDrawableComponent(string componentName) => source.GetDrawableComponent(componentName) ?? fallbackSource?.GetDrawableComponent(componentName);
|
||||
|
||||
public Texture GetTexture(string componentName) => source.GetTexture(componentName) ?? fallbackSource.GetTexture(componentName);
|
||||
|
||||
public SampleChannel GetSample(string sampleName) => source.GetSample(sampleName) ?? fallbackSource?.GetSample(sampleName);
|
||||
|
||||
public TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct
|
||||
{
|
||||
TValue? val = null;
|
||||
var conf = (source as Skin)?.Configuration as TConfiguration;
|
||||
if (conf != null)
|
||||
val = query?.Invoke(conf);
|
||||
|
||||
return val ?? fallbackSource?.GetValue(query);
|
||||
}
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class
|
||||
{
|
||||
TValue val = null;
|
||||
var conf = (source as Skin)?.Configuration as TConfiguration;
|
||||
if (conf != null)
|
||||
val = query?.Invoke(conf);
|
||||
|
||||
return val ?? fallbackSource?.GetValue(query);
|
||||
}
|
||||
|
||||
private readonly ISkinSource source;
|
||||
private ISkinSource fallbackSource;
|
||||
|
||||
public LocalSkinOverrideContainer(ISkinSource source)
|
||||
{
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
private void onSourceChanged() => SourceChanged?.Invoke();
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
|
||||
fallbackSource = dependencies.Get<ISkinSource>();
|
||||
if (fallbackSource != null)
|
||||
fallbackSource.SourceChanged += onSourceChanged;
|
||||
|
||||
dependencies.CacheAs<ISkinSource>(this);
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (fallbackSource != null)
|
||||
fallbackSource.SourceChanged -= onSourceChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,60 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public abstract class Skin
|
||||
public abstract class Skin : IDisposable, ISkinSource
|
||||
{
|
||||
public readonly SkinInfo SkinInfo;
|
||||
|
||||
public virtual SkinConfiguration Configuration { get; protected set; }
|
||||
|
||||
public event Action SourceChanged;
|
||||
|
||||
public abstract Drawable GetDrawableComponent(string componentName);
|
||||
|
||||
public abstract SampleChannel GetSample(string sampleName);
|
||||
|
||||
public abstract Texture GetTexture(string componentName);
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class
|
||||
=> Configuration is TConfiguration conf ? query?.Invoke(conf) : null;
|
||||
|
||||
public TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct
|
||||
=> Configuration is TConfiguration conf ? query?.Invoke(conf) : null;
|
||||
|
||||
protected Skin(SkinInfo skin)
|
||||
{
|
||||
SkinInfo = skin;
|
||||
}
|
||||
|
||||
#region Disposal
|
||||
|
||||
~Skin()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private bool isDisposed;
|
||||
|
||||
protected virtual void Dispose(bool isDisposing)
|
||||
{
|
||||
if (isDisposed)
|
||||
return;
|
||||
isDisposed = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public class SkinConfiguration : IHasComboColours, IHasCustomColours
|
||||
{
|
||||
public readonly SkinInfo SkinInfo = new SkinInfo();
|
||||
|
||||
public List<Color4> ComboColours { get; set; } = new List<Color4>();
|
||||
|
||||
public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>();
|
||||
}
|
||||
}
|
||||
@@ -24,5 +24,7 @@ namespace osu.Game.Skinning
|
||||
public static SkinInfo Default { get; } = new SkinInfo { Name = "osu!lazer", Creator = "team osu!" };
|
||||
|
||||
public bool Equals(SkinInfo other) => other != null && ID == other.ID;
|
||||
|
||||
public override string ToString() => $"\"{Name}\" by {Creator}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,17 @@ using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.IO.Archives;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public class SkinManager : ArchiveModelManager<SkinInfo, SkinFileInfo>
|
||||
public class SkinManager : ArchiveModelManager<SkinInfo, SkinFileInfo>, ISkinSource
|
||||
{
|
||||
private readonly AudioManager audio;
|
||||
|
||||
@@ -39,6 +42,31 @@ namespace osu.Game.Skinning
|
||||
Name = archive.Name
|
||||
};
|
||||
|
||||
protected override void Populate(SkinInfo model, ArchiveReader archive)
|
||||
{
|
||||
base.Populate(model, archive);
|
||||
populate(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate a <see cref="SkinInfo"/> from its <see cref="SkinConfiguration"/> (if possible).
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
private void populate(SkinInfo model)
|
||||
{
|
||||
Skin reference = GetSkin(model);
|
||||
if (!string.IsNullOrEmpty(reference.Configuration.SkinInfo.Name))
|
||||
{
|
||||
model.Name = reference.Configuration.SkinInfo.Name;
|
||||
model.Creator = reference.Configuration.SkinInfo.Creator;
|
||||
}
|
||||
else
|
||||
{
|
||||
model.Name = model.Name.Replace(".osk", "");
|
||||
model.Creator = "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a <see cref="Skin"/> instance for the provided <see cref="SkinInfo"/>
|
||||
/// </summary>
|
||||
@@ -64,7 +92,19 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
if (skin.SkinInfo != CurrentSkinInfo.Value)
|
||||
throw new InvalidOperationException($"Setting {nameof(CurrentSkin)}'s value directly is not supported. Use {nameof(CurrentSkinInfo)} instead.");
|
||||
|
||||
SourceChanged?.Invoke();
|
||||
};
|
||||
|
||||
// migrate older imports which didn't have access to skin.ini
|
||||
using (ContextFactory.GetForWrite())
|
||||
{
|
||||
foreach (var skinInfo in ModelStore.ConsumableItems.Where(s => s.Name.EndsWith(".osk")))
|
||||
{
|
||||
populate(skinInfo);
|
||||
Update(skinInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -73,5 +113,17 @@ namespace osu.Game.Skinning
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>The first result for the provided query, or null if no results were found.</returns>
|
||||
public SkinInfo Query(Expression<Func<SkinInfo, bool>> query) => ModelStore.ConsumableItems.AsNoTracking().FirstOrDefault(query);
|
||||
|
||||
public event Action SourceChanged;
|
||||
|
||||
public Drawable GetDrawableComponent(string componentName) => CurrentSkin.Value.GetDrawableComponent(componentName);
|
||||
|
||||
public Texture GetTexture(string componentName) => CurrentSkin.Value.GetTexture(componentName);
|
||||
|
||||
public SampleChannel GetSample(string sampleName) => CurrentSkin.Value.GetSample(sampleName);
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class => CurrentSkin.Value.GetValue(query);
|
||||
|
||||
public TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct => CurrentSkin.Value.GetValue(query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
@@ -12,33 +12,36 @@ namespace osu.Game.Skinning
|
||||
/// </summary>
|
||||
public abstract class SkinReloadableDrawable : CompositeDrawable
|
||||
{
|
||||
private Bindable<Skin> skin;
|
||||
private readonly Func<ISkinSource, bool> allowFallback;
|
||||
private ISkinSource skin;
|
||||
|
||||
/// <summary>
|
||||
/// Whether fallback to default skin should be allowed if the custom skin is missing this resource.
|
||||
/// </summary>
|
||||
private readonly bool allowDefaultFallback;
|
||||
private bool allowDefaultFallback => allowFallback == null || allowFallback.Invoke(skin);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="SkinReloadableDrawable"/>
|
||||
/// </summary>
|
||||
/// <param name="fallback">Whether fallback to default skin should be allowed if the custom skin is missing this resource.</param>
|
||||
protected SkinReloadableDrawable(bool fallback = true)
|
||||
protected SkinReloadableDrawable(Func<ISkinSource, bool> allowFallback = null)
|
||||
{
|
||||
allowDefaultFallback = fallback;
|
||||
this.allowFallback = allowFallback;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(SkinManager skinManager)
|
||||
private void load(ISkinSource source)
|
||||
{
|
||||
skin = skinManager.CurrentSkin.GetBoundCopy();
|
||||
skin.ValueChanged += skin => SkinChanged(skin, allowDefaultFallback || skin.SkinInfo == SkinInfo.Default);
|
||||
skin = source;
|
||||
skin.SourceChanged += onChange;
|
||||
}
|
||||
|
||||
private void onChange() => SkinChanged(skin, allowDefaultFallback);
|
||||
|
||||
protected override void LoadAsyncComplete()
|
||||
{
|
||||
base.LoadAsyncComplete();
|
||||
skin.TriggerChange();
|
||||
onChange();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -46,7 +49,7 @@ namespace osu.Game.Skinning
|
||||
/// </summary>
|
||||
/// <param name="skin">The new skin.</param>
|
||||
/// <param name="allowFallback">Whether fallback to default skin should be allowed if the custom skin is missing this resource.</param>
|
||||
protected virtual void SkinChanged(Skin skin, bool allowFallback)
|
||||
protected virtual void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user